New test.
[mono.git] / mcs / class / System.Drawing / System.Drawing.Printing / PrintingServicesUnix.cs
1 //
2 // Copyright (C) 2005 Novell, Inc. http://www.novell.com
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 //
23 // Author:
24 //
25 //      Jordi Mas i Hernandez, jordimash@gmail.com
26 //
27
28 using System.Runtime.InteropServices;
29 using System.Collections;
30 using System.Drawing.Printing;
31 using System.ComponentModel;
32 using System.Drawing.Imaging;
33 using System.Text;
34 using System.IO;
35
36 namespace System.Drawing.Printing
37 {
38         internal class PrintingServicesUnix : PrintingServices
39         {
40                 private Hashtable doc_info = new Hashtable ();
41                 private bool cups_installed;
42
43                 internal PrintingServicesUnix ()
44                 {
45                         CheckCupsInstalled ();
46                 }
47
48                 private void CheckCupsInstalled ()
49                 {
50                         try {
51                                 cupsGetDefault ();
52                         }
53                         catch (DllNotFoundException) {
54                                 Console.WriteLine("libcups not found. To have printing support, you need cups installed");
55                                 cups_installed = false;
56                                 return;
57                         }
58
59                         cups_installed = true;
60                 }
61
62                 // Methods
63                 internal override void LoadPrinterSettings (string printer, PrinterSettings settings)
64                 {
65                         IntPtr ptr, ppd_handle, ptr_opt, ptr_choice;
66                         string ppd_filename;
67                         PPD_FILE ppd;
68                         PPD_OPTION option;
69                         PPD_CHOICE choice;                      
70
71                         if (cups_installed == false || (printer == null) || (printer == String.Empty))
72                                 return;
73                 
74                         ptr = cupsGetPPD (printer);
75                         ppd_filename = Marshal.PtrToStringAnsi (ptr);
76                         ppd_handle = ppdOpenFile (ppd_filename);
77                         //Console.WriteLine ("File: {0}", ppd_filename);
78
79                         ppd = (PPD_FILE) Marshal.PtrToStructure (ppd_handle, typeof (PPD_FILE));
80                         settings.landscape_angle = ppd.landscape;
81                         settings.supports_color = (ppd.color_device == 0) ? false : true;
82
83                         // Default paper source
84                         ptr_opt = ppdFindOption (ppd_handle, "InputSlot"); 
85                         if (ptr_opt != IntPtr.Zero) {
86                                 option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
87                                 ptr_choice = option.choices;
88                                 for (int c = 0; c < option.num_choices; c++) {
89                                         choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
90                                         ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
91                                         if (choice.choice == option.defchoice) {
92                                                 foreach (PaperSource paper_source in settings.PaperSources) {
93                                                         if (paper_source.SourceName ==  choice.text) {
94                                                                 settings.DefaultPageSettings.PaperSource = paper_source;
95                                                                 break;
96                                                         }
97                                                 }
98                                                 break;
99                                         }
100                                 }
101                         }
102                         
103                         // Default paper size
104                         ptr_opt = ppdFindOption (ppd_handle, "PageSize"); 
105                         if (ptr_opt != IntPtr.Zero) {
106                                 option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
107                                 ptr_choice = option.choices;
108                                 for (int c = 0; c < option.num_choices; c++) {
109                                         choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
110                                         ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
111                                         if (choice.choice == option.defchoice) {
112                                                 foreach (PaperSize paper_size in settings.PaperSizes) {
113                                                         if (paper_size.PaperName == choice.text) {
114                                                                 settings.DefaultPageSettings.PaperSize = paper_size;
115                                                                 break;
116                                                         }
117                                                 }
118                                                 break;
119                                         }
120                                 }
121                         }
122                         
123                         ppdClose (ppd_handle);
124                 }
125
126                 internal override void LoadPrinterResolutions (string printer, PrinterSettings settings)
127                 {
128                         settings.PrinterResolutions.Clear ();
129                         LoadDefaultResolutions (settings.PrinterResolutions);
130                 }
131
132                 internal override void LoadPrinterPaperSizes (string printer, PrinterSettings settings)
133                 {
134                         IntPtr ptr, ppd_handle;
135                         string ppd_filename, real_name;
136                         PPD_FILE ppd;
137                         PPD_SIZE size;
138                         PaperSize ps;
139                         PaperKind kind = PaperKind.Custom;
140
141                         settings.PaperSizes.Clear ();
142
143                         ptr = cupsGetPPD (printer);
144                         ppd_filename = Marshal.PtrToStringAnsi (ptr);
145                         ppd_handle = ppdOpenFile (ppd_filename);
146
147                         ppd = (PPD_FILE) Marshal.PtrToStructure (ppd_handle, typeof (PPD_FILE));
148                         ptr = ppd.sizes;
149                         float w, h;
150                         for (int i = 0; i < ppd.num_sizes; i++) {
151                                 size = (PPD_SIZE) Marshal.PtrToStructure (ptr, typeof (PPD_SIZE));
152                                 real_name = GetPaperSizeName (ppd_handle, size.name);
153                                 ptr = new IntPtr (ptr.ToInt64 () + Marshal.SizeOf (size));
154                                 w = size.width * 100 / 72;
155                                 h = size.length * 100 / 72;
156                                 ps = new PaperSize (real_name, (int) w, (int) h);
157                                 // TODO: Convert from name to paper kind enum
158                                 ps.SetKind (kind);
159                                 settings.PaperSizes.Add (ps);
160                         }
161
162                         ppdClose (ppd_handle);
163                 }               
164                 
165                 internal override void LoadPrinterPaperSources (string printer, PrinterSettings settings)
166                 {
167                         IntPtr ptr, ppd_handle, ptr_opt, ptr_choice;
168                         string ppd_filename;
169                         PPD_OPTION option;
170                         PPD_CHOICE choice;
171
172                         if (cups_installed == false || (printer == null) || (printer == String.Empty))
173                                 return;
174
175                         ptr = cupsGetPPD (printer);
176                         ppd_filename = Marshal.PtrToStringAnsi (ptr);
177                         ppd_handle = ppdOpenFile (ppd_filename);
178
179                         ptr_opt = ppdFindOption (ppd_handle, "InputSlot"); 
180
181                         if (ptr_opt == IntPtr.Zero) {
182                                 ppdClose (ppd_handle);
183                                 return; 
184                         }
185                         
186                         option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
187                         //Console.WriteLine (" OPTION  key:{0} def:{1} text: {2}", option.keyword, option.defchoice, option.text);
188
189                         ptr_choice = option.choices;
190                         for (int c = 0; c < option.num_choices; c++) {
191                                 choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
192                                 ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
193                                 //Console.WriteLine ("       choice:{0} - text: {1}", choice.choice, choice.text);
194                                 settings.PaperSources.Add (new PaperSource (choice.text, PaperSourceKind.Custom));                                      
195                         }
196                         ppdClose (ppd_handle);
197                 }
198
199                 internal override bool StartPage (GraphicsPrinter gr)
200                 {
201                         return true;
202                 }
203
204                 internal override bool EndPage (GraphicsPrinter gr)
205                 {
206                         GdipGetPostScriptSavePage (gr.Hdc);
207                         return true;
208                 }
209
210                 string tmpfile;
211
212                 internal override bool EndDoc (GraphicsPrinter gr)
213                 {
214                         DOCINFO doc = (DOCINFO) doc_info[gr.Hdc];
215
216                         gr.Graphics.Dispose (); // Dispose object to force surface finish
217                         cupsPrintFile (doc.settings.PrinterName, doc.filename, doc.title, 0, IntPtr.Zero);
218                         doc_info.Remove (gr.Hdc);
219                         if (tmpfile != null) {
220                                 try { File.Delete (tmpfile); }
221                                 catch { }
222                         }
223                         return true;
224                 }
225
226                 internal override bool StartDoc (GraphicsPrinter gr, string doc_name, string output_file)
227                 {
228                         DOCINFO doc = (DOCINFO) doc_info[gr.Hdc];
229                         doc.title = doc_name;
230                         return true;
231                 }
232
233                 internal override IntPtr CreateGraphicsContext (PrinterSettings settings)
234                 {
235                         IntPtr graphics = IntPtr.Zero;
236                         string name;
237                         if (!settings.PrintToFile) {
238                                 StringBuilder sb = new StringBuilder (1024);
239                                 int length = sb.Capacity;
240                                 cupsTempFile (sb, length);
241                                 name = sb.ToString ();
242                                 tmpfile = name;
243                         }
244                         else
245                                 name = settings.PrintFileName;
246
247                         GdipGetPostScriptGraphicsContext (name,
248                                 settings.DefaultPageSettings.PaperSize.Width / 100 * 72,
249                                 settings.DefaultPageSettings.PaperSize.Height / 100 * 72, 
250                                 // Harcoded dpy's
251                                 300, 300, ref graphics);
252
253                         DOCINFO doc = new DOCINFO ();
254                         doc.filename = name.ToString();
255                         doc.settings = settings;
256                         doc_info.Add (graphics, doc);
257
258                         return graphics;
259                 }
260
261                 // Properties
262
263                 internal override PrinterSettings.StringCollection InstalledPrinters {
264                         get {
265                                 int n_printers;
266                                 IntPtr dests = IntPtr.Zero, ptr_printers, ptr_printer;
267                                 string str;
268                                 PrinterSettings.StringCollection col = new PrinterSettings.StringCollection (new string[] {});
269
270                                 if (cups_installed == false)
271                                         return col;
272
273                                 n_printers = cupsGetDests (ref dests);
274
275                                 ptr_printers = dests;
276                                 for (int i = 0; i < n_printers; i++) {
277                                         ptr_printer = (IntPtr) Marshal.ReadInt32 (ptr_printers);
278                                         str = Marshal.PtrToStringAnsi (ptr_printer);
279                                         Marshal.FreeHGlobal (ptr_printer);
280                                         ptr_printers = new IntPtr (ptr_printers.ToInt64 () + 20 /*size of CUPS_DEST*/);
281                                         col.Add (str);
282                                 }
283                                 Marshal.FreeHGlobal (dests);
284                                 return col;
285                         }
286                 }
287
288                 internal override string DefaultPrinter {
289                         get {
290                                 IntPtr str;
291
292                                 if (cups_installed == false)
293                                         return string.Empty;
294
295                                 str = cupsGetDefault ();
296                                 return Marshal.PtrToStringAnsi (str);
297                         }
298                 }
299
300                 // Private functions
301
302                 private string GetPaperSizeName (IntPtr ppd, string name)
303                 {
304                         string rslt = name;
305                         PPD_OPTION option;
306                         PPD_CHOICE choice;
307                         IntPtr ptr_opt, ptr_choice;
308                         
309                         ptr_opt = ppdFindOption (ppd, "PageSize"); 
310
311                         if (ptr_opt == IntPtr.Zero) {
312                                 return rslt;    
313                         }
314                         
315                         option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
316
317                         ptr_choice = option.choices;
318                         for (int c = 0; c < option.num_choices; c++) {
319                                 choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
320                                 ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
321                                 if (name.Equals (choice.choice)) {
322                                         rslt = choice.text;
323                                         break;
324                                 }
325                         }
326                         return rslt;
327                 }
328
329                 // TODO
330                 internal override void GetPrintDialogInfo (string printer, ref string port, ref string type, ref string status, ref string comment)
331                 {                       
332                         status = "Ready";                       
333                 }
334
335                 //
336                 // DllImports
337                 //
338
339                 [DllImport("libcups", CharSet=CharSet.Ansi)]
340                 static extern int cupsGetDests (ref IntPtr dests);
341
342                 [DllImport("libcups", CharSet=CharSet.Ansi)]
343                 static extern IntPtr cupsTempFile (StringBuilder sb, int len);
344
345                 [DllImport("libcups", CharSet=CharSet.Ansi)]
346                 static extern IntPtr cupsGetDefault ();
347
348                 [DllImport("libcups", CharSet=CharSet.Ansi)]
349                 static extern int cupsPrintFile (string printer, string filename, string title, int num_options, IntPtr options);
350
351                 [DllImport("libcups", CharSet=CharSet.Ansi)]
352                 static extern IntPtr cupsGetPPD (string printer);
353
354                 [DllImport("libcups", CharSet=CharSet.Ansi)]
355                 static extern IntPtr ppdOpenFile (string filename);
356
357                 [DllImport("libcups", CharSet=CharSet.Ansi)]
358                 static extern IntPtr ppdFindOption (IntPtr ppd_file, string keyword);
359
360                 [DllImport("libcups")]
361                 static extern void ppdClose (IntPtr ppd);
362
363                 [DllImport("gdiplus.dll", CharSet=CharSet.Ansi)]
364                 static extern int GdipGetPostScriptGraphicsContext (string filename, int with, int height, double dpix, double dpiy, ref IntPtr graphics);
365
366                 [DllImport("gdiplus.dll")]
367                 static extern int GdipGetPostScriptSavePage (IntPtr graphics);
368
369
370                 //Struct
371                 public struct DOCINFO
372                 {
373                         public PrinterSettings settings;
374                         public string title;
375                         public string filename;
376                 }
377
378                 public struct PPD_SIZE
379                 {
380                         public  int marked;
381                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=42)]
382                         public  string name;
383                         public  float width;
384                         public  float length;
385                         public  float left;
386                         public  float bottom;
387                         public  float right;
388                         public  float top;
389                 }
390
391                 public struct PPD_GROUP
392                 {
393                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=40)]
394                         public string text;
395                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=42)]
396                         public string name;
397                         public int num_options;
398                         public IntPtr options;
399                         public int num_subgroups;
400                         public IntPtr subgrups;
401                 }
402
403                 public struct PPD_OPTION
404                 {
405                         public byte     conflicted;
406                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=41)]
407                         public string   keyword;
408                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=41)]
409                         public string   defchoice;
410                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=81)]
411                         public string   text;
412                         public int      ui;
413                         public int      section;
414                         public float    order;
415                         public int      num_choices;
416                         public IntPtr   choices;
417                 }
418
419                 public struct PPD_CHOICE
420                 {
421                         public byte     marked;
422                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=41)]
423                         public string   choice;
424                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=81)]
425                         public string   text;
426                         public IntPtr   code;
427                         public IntPtr   option;
428                 }
429
430                 public struct PPD_FILE
431                 {
432                         public int      language_level;
433                         public int      color_device;
434                         public int      variable_sizes;
435                         public int      accurate_screens;
436                         public int      contone_only;
437                         public int      landscape;
438                         public int      model_number;
439                         public int      manual_copies;
440                         public int      throughput;
441                         public int      colorspace;
442                         public IntPtr   patches;
443                         public int      num_emulations;
444                         public IntPtr   emulations;
445                         public IntPtr   jcl_begin;
446                         public IntPtr   jcl_ps;
447                         public IntPtr   jcl_end;
448                         public IntPtr   lang_encoding;
449                         public IntPtr   lang_version;
450                         public IntPtr   modelname;
451                         public IntPtr   ttrasterizer;
452                         public IntPtr   manufacturer;
453                         public IntPtr   product;
454                         public IntPtr   nickname;
455                         public IntPtr   shortnickname;
456                         public int      num_groups;
457                         public IntPtr   groups;
458                         public int      num_sizes;
459                         public IntPtr   sizes;
460
461                         /* There is more data after this that we are not using*/
462                 }
463         }
464 }
465