2008-03-28 Ivan N. Zlatev <contact@i-nz.net>
[mono.git] / mcs / class / System.Drawing / System.Drawing.Printing / PrintingServicesUnix.cs
1 //#define PrintDebug
2 //
3 // Copyright (C) 2005, 2007 Novell, Inc. http://www.novell.com
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24 // Author:
25 //
26 //      Jordi Mas i Hernandez, jordimash@gmail.com
27 //
28
29 using System.Runtime.InteropServices;
30 using System.Collections;
31 using System.Collections.Specialized;
32 using System.Drawing.Printing;
33 using System.ComponentModel;
34 using System.Drawing.Imaging;
35 using System.Text;
36 using System.IO;
37
38 namespace System.Drawing.Printing
39 {
40         internal class PrintingServicesUnix : PrintingServices
41         {
42                 #region Private Fields
43                 
44                 private static Hashtable doc_info = new Hashtable ();
45                 private static bool cups_installed;
46                 
47                 private string printer_name;
48                 
49                 private static Hashtable installed_printers;
50                 private static string default_printer = String.Empty;
51                                 
52                 #endregion
53
54                 #region Constructor
55
56                 internal PrintingServicesUnix () {
57                 }
58                 
59                 static PrintingServicesUnix () {
60                         installed_printers = new Hashtable ();
61                         CheckCupsInstalled ();
62                 }
63                 
64                 #endregion
65
66                 #region Properties
67
68                 internal static PrinterSettings.StringCollection InstalledPrinters {
69                         get {
70                                 LoadPrinters();
71                                 PrinterSettings.StringCollection list = new PrinterSettings.StringCollection (new string[] {});
72                                 foreach (object key in installed_printers.Keys) {
73                                         list.Add (key.ToString());
74                                 }
75                                 return list;
76                         }
77                 }
78
79                 internal override string DefaultPrinter {
80                         get {
81                                 if (installed_printers.Count == 0)
82                                         LoadPrinters();
83                                 return default_printer;
84                         }
85                 }
86                 
87                 #endregion
88
89
90                 #region Methods
91
92                 /// <summary>
93                 /// Do a cups call to check if it is installed
94                 /// </summary>
95                 private static void CheckCupsInstalled ()
96                 {
97                         try {
98                                 cupsGetDefault ();
99                         }
100                         catch (DllNotFoundException) {
101                                 Console.WriteLine("libcups not found. To have printing support, you need cups installed");
102                                 cups_installed = false;
103                                 return;
104                         }
105
106                         cups_installed = true;
107                 }
108
109                 /// <summary>
110                 /// Open the printer's PPD file
111                 /// </summary>
112                 /// <param name="printer">Printer name, returned from cupsGetDests</param>
113                 private IntPtr OpenPrinter (string printer)
114                 {
115                         try {
116                                 IntPtr ptr = cupsGetPPD (printer);
117                                 string ppd_filename = Marshal.PtrToStringAnsi (ptr);
118                                 IntPtr ppd_handle = ppdOpenFile (ppd_filename);
119                                 return ppd_handle;
120                         }
121                         catch (Exception ex) {
122                                 Console.WriteLine ("There was an error opening the printer {0}. Please check your cups installation.");
123 #if DEBUG
124                                 Console.WriteLine (ex.Message);
125 #endif
126                         }
127                         return IntPtr.Zero;
128                 }
129
130                 /// <summary>
131                 /// Close the printer file
132                 /// </summary>
133                 /// <param name="handle">PPD handle</param>
134                 private void ClosePrinter (ref IntPtr handle)
135                 {
136                         try {
137                                 if (handle != IntPtr.Zero)
138                                         ppdClose (handle);
139                         }
140                         finally {
141                                 handle = IntPtr.Zero;
142                         }
143                 }
144
145                 private static int OpenDests (ref IntPtr ptr) {
146                         try {
147                                 return cupsGetDests (ref ptr);
148                         }
149                         catch {
150                                 ptr = IntPtr.Zero;
151                         }
152                         return 0;
153                 }
154
155                 private static void CloseDests (ref IntPtr ptr, int count) {
156                         try {
157                                 if (ptr != IntPtr.Zero)
158                                         cupsFreeDests (count, ptr);
159                         }
160                         finally {
161                                 ptr = IntPtr.Zero;
162                         }
163                 }
164
165                 /// <summary>
166                 /// Checks if a printer has a valid PPD file. Caches the result unless force is true
167                 /// </summary>
168                 /// <param name="force">Does the check disregarding the last cached value if true</param>
169                 internal override bool IsPrinterValid(string printer)
170                 {
171                         if (!cups_installed || printer == null | printer == String.Empty)
172                                 return false;
173                                 
174                         return installed_printers.Contains (printer);
175 /*
176                         if (!force && this.printer_name != null && String.Intern(this.printer_name).Equals(printer))
177                                 return is_printer_valid;
178
179                         IntPtr ptr = cupsGetPPD (printer);
180                         string ppd_filename = Marshal.PtrToStringAnsi (ptr);
181                         is_printer_valid = ppd_filename != null;
182                         this.printer_name = printer; 
183                         return is_printer_valid;
184 */                      
185                 }
186         
187                 /// <summary>
188                 /// Loads the printer settings and initializes the PrinterSettings and PageSettings fields
189                 /// </summary>
190                 /// <param name="printer">Printer name</param>
191                 /// <param name="settings">PrinterSettings object to initialize</param>
192                 internal override void LoadPrinterSettings (string printer, PrinterSettings settings)
193                 {
194                         if (cups_installed == false || (printer == null) || (printer == String.Empty))
195                                 return;
196
197                         if (installed_printers.Count == 0)
198                                 LoadPrinters ();
199
200                         if (((SysPrn.Printer)installed_printers[printer]).Settings != null) {
201                                 SysPrn.Printer p = (SysPrn.Printer) installed_printers[printer];
202                                 settings.can_duplex = p.Settings.can_duplex;
203                                 settings.is_plotter = p.Settings.is_plotter;
204                                 settings.landscape_angle = p.Settings.landscape_angle;
205                                 settings.maximum_copies = p.Settings.maximum_copies;
206                                 settings.paper_sizes = p.Settings.paper_sizes;
207                                 settings.paper_sources = p.Settings.paper_sources;
208                                 settings.printer_capabilities = p.Settings.printer_capabilities;
209                                 settings.printer_resolutions = p.Settings.printer_resolutions;
210                                 settings.supports_color = p.Settings.supports_color;
211                                 return;
212                         }
213
214                         settings.PrinterCapabilities.Clear ();
215
216                         IntPtr dests = IntPtr.Zero, ptr = IntPtr.Zero, ptr_printer, ppd_handle = IntPtr.Zero;
217                         string name = String.Empty;
218                         CUPS_DESTS printer_dest;
219                         PPD_FILE ppd;
220                         int ret = 0, cups_dests_size;
221                         NameValueCollection options, paper_names, paper_sources;
222
223                         try {
224                                 ret = OpenDests (ref dests);
225                                 if (ret == 0)
226                                         return;
227
228                                 cups_dests_size = Marshal.SizeOf (typeof(CUPS_DESTS));
229                                 ptr = dests;
230                                 for (int i = 0; i < ret; i++) {
231                                         ptr_printer = (IntPtr) Marshal.ReadInt32 (ptr);
232                                         if (Marshal.PtrToStringAnsi (ptr_printer).Equals(printer)) {
233                                                 name = printer;
234                                                 break;
235                                         }
236                                         ptr = (IntPtr) ((long)ptr + cups_dests_size);
237                                 }
238                         
239                                 if (!name.Equals(printer)) {
240                                         return;
241                                 }
242
243                                 ppd_handle = OpenPrinter (printer);
244                                 if (ppd_handle == IntPtr.Zero)
245                                         return;
246
247                                 printer_dest = (CUPS_DESTS) Marshal.PtrToStructure (ptr, typeof (CUPS_DESTS));
248                                 options = new NameValueCollection();
249                                 paper_names = new NameValueCollection();
250                                 paper_sources = new NameValueCollection();
251                                 LoadPrinterOptions(printer_dest.options, printer_dest.num_options, ppd_handle, options, paper_names, paper_sources);
252                         
253                                 if (settings.paper_sizes == null)
254                                         settings.paper_sizes = new PrinterSettings.PaperSizeCollection (new PaperSize [] {});
255                                 else
256                                         settings.paper_sizes.Clear();
257                         
258                                 if (settings.paper_sources == null)                             
259                                         settings.paper_sources = new PrinterSettings.PaperSourceCollection (new PaperSource [] {});
260                                 else
261                                         settings.paper_sources.Clear();
262                         
263                                 string defsource = options["InputSlot"];
264                                 string defsize = options["PageSize"];
265                         
266                                 settings.DefaultPageSettings.PaperSource = LoadPrinterPaperSources (settings, defsource, paper_sources);
267                                 settings.DefaultPageSettings.PaperSize = LoadPrinterPaperSizes (ppd_handle, settings, defsize, paper_names);
268                         
269                                 ppd = (PPD_FILE) Marshal.PtrToStructure (ppd_handle, typeof (PPD_FILE));
270                                 settings.landscape_angle = ppd.landscape;
271                                 settings.supports_color = (ppd.color_device == 0) ? false : true;
272                                 settings.can_duplex = options["Duplex"] != null;
273                         
274                                 ClosePrinter (ref ppd_handle);
275                         
276                                 ((SysPrn.Printer)installed_printers[printer]).Settings = settings;
277                         }
278                         finally {
279                                 CloseDests (ref dests, ret);
280                         }
281                 }
282                 
283                 /// <summary>
284                 /// Loads the global options of a printer plus the paper types and trays supported.
285                 /// </summary>
286                 /// <param name="options">The options field of a printer's CUPS_DESTS structure</param>
287                 /// <param name="numOptions">The number of options of the printer</param>
288                 /// <param name="ppd">A ppd handle for the printer, returned by ppdOpen</param>
289                 /// <param name="list">The list of options</param>
290                 /// <param name="paper_names">A list of types of paper (PageSize)</param>
291                 /// <param name="paper_sources">A list of trays(InputSlot) </param>
292                 private static void LoadPrinterOptions(IntPtr options, int numOptions, IntPtr ppd, 
293                                                                                  NameValueCollection list, 
294                                                                                  NameValueCollection paper_names,
295                                                                                  NameValueCollection paper_sources)
296                 {
297                         CUPS_OPTIONS cups_options;
298                         string option_name, option_value;
299                         int cups_size = Marshal.SizeOf(typeof(CUPS_OPTIONS));
300                         
301                         for (int j = 0; j < numOptions; j++)
302                         {
303                                 cups_options = (CUPS_OPTIONS) Marshal.PtrToStructure(options, typeof(CUPS_OPTIONS));
304                                 option_name = Marshal.PtrToStringAnsi(cups_options.name);
305                                 option_value = Marshal.PtrToStringAnsi(cups_options.val);
306
307                                 #if PrintDebug
308                                 Console.WriteLine("{0} = {1}", option_name, option_value);
309                                 #endif
310                                 
311                                 list.Add(option_name, option_value);
312
313                                 options = (IntPtr) ((long)options + cups_size);
314                         }
315                         
316                         LoadOptionList (ppd, "PageSize", paper_names);
317                         LoadOptionList (ppd, "InputSlot", paper_sources);
318                 }
319                 
320                 /// <summary>
321                 /// Loads the global options of a printer. 
322                 /// </summary>
323                 /// <param name="options">The options field of a printer's CUPS_DESTS structure</param>
324                 /// <param name="numOptions">The number of options of the printer</param>
325                 private static NameValueCollection LoadPrinterOptions(IntPtr options, int numOptions)
326                 {
327                         CUPS_OPTIONS cups_options;
328                         string option_name, option_value;
329                         int cups_size = Marshal.SizeOf (typeof(CUPS_OPTIONS));
330                         NameValueCollection list = new NameValueCollection ();
331                         for (int j = 0; j < numOptions; j++)
332                         {
333                                 cups_options = (CUPS_OPTIONS) Marshal.PtrToStructure(options, typeof(CUPS_OPTIONS));
334                                 option_name = Marshal.PtrToStringAnsi (cups_options.name);
335                                 option_value = Marshal.PtrToStringAnsi (cups_options.val);
336                                 
337                                 #if PrintDebug
338                                 Console.WriteLine("{0} = {1}", option_name, option_value);
339                                 #endif
340                                 
341                                 list.Add (option_name, option_value);
342
343                                 options = (IntPtr) ((long)options + cups_size);
344                         }
345                         return list;
346                 }
347
348                 /// <summary>
349                 /// Loads a printer's options (selection of paper sizes, paper sources, etc)
350                 /// </summary>
351                 /// <param name="ppd">Printer ppd file handle</param>
352                 /// <param name="option_name">Name of the option group to load</param>
353                 /// <param name="list">List of loaded options</param>
354                 private static void LoadOptionList(IntPtr ppd, string option_name, NameValueCollection list) {
355
356                         IntPtr ptr = IntPtr.Zero;
357                         PPD_OPTION ppd_option;
358                         PPD_CHOICE choice;
359                         int choice_size = Marshal.SizeOf(typeof(PPD_CHOICE)); 
360                         
361                         ptr = ppdFindOption (ppd, option_name);
362                         if (ptr != IntPtr.Zero)
363                         {
364                                 ppd_option = (PPD_OPTION) Marshal.PtrToStructure (ptr, typeof (PPD_OPTION));
365                                 #if PrintDebug
366                                 Console.WriteLine (" OPTION  key:{0} def:{1} text: {2}", ppd_option.keyword, ppd_option.defchoice, ppd_option.text);
367                                 #endif
368
369                                 ptr = ppd_option.choices;
370                                 for (int c = 0; c < ppd_option.num_choices; c++) {
371                                         choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr, typeof (PPD_CHOICE));
372                                         list.Add(choice.choice, choice.text);
373                                         #if PrintDebug
374                                         Console.WriteLine ("       choice:{0} - text: {1}", choice.choice, choice.text);
375                                         #endif
376
377                                         ptr = (IntPtr) ((long)ptr + choice_size);
378                                 }
379                         }               
380                 }
381
382                 /// <summary>
383                 /// Loads a printer's available resolutions
384                 /// </summary>
385                 /// <param name="printer">Printer name</param>
386                 /// <param name="settings">PrinterSettings object to fill</param>
387                 internal override void LoadPrinterResolutions (string printer, PrinterSettings settings)
388                 {
389                         settings.PrinterResolutions.Clear ();
390                         LoadDefaultResolutions (settings.PrinterResolutions);
391                 }
392
393                 /// <summary>
394                 /// Loads a printer's paper sizes. Returns the default PaperSize, and fills a list of paper_names for use in dialogues
395                 /// </summary>
396                 /// <param name="ppd_handle">PPD printer file handle</param>
397                 /// <param name="settings">PrinterSettings object to fill</param>
398                 /// <param name="def_size">Default paper size, from the global options of the printer</param>
399                 /// <param name="paper_names">List of available paper sizes that gets filled</param>
400                 private PaperSize LoadPrinterPaperSizes(IntPtr ppd_handle, PrinterSettings settings, 
401                                                                                                 string def_size, NameValueCollection paper_names)
402                 {
403                         IntPtr ptr;
404                         string real_name;
405                         PPD_FILE ppd;
406                         PPD_SIZE size;
407                         PaperSize ps;
408
409                         PaperSize defsize = null;
410                         ppd = (PPD_FILE) Marshal.PtrToStructure (ppd_handle, typeof (PPD_FILE));
411                         ptr = ppd.sizes;
412                         float w, h;
413                         for (int i = 0; i < ppd.num_sizes; i++) {
414                                 size = (PPD_SIZE) Marshal.PtrToStructure (ptr, typeof (PPD_SIZE));
415                                 real_name = paper_names[size.name];
416                                 w = size.width * 100 / 72;
417                                 h = size.length * 100 / 72;
418                                 ps = new PaperSize (real_name, (int) w, (int) h, GetPaperKind ((int) w, (int) h), def_size == real_name);
419                                 if (def_size == real_name)
420                                         defsize = ps;
421                                 ps.SetKind (GetPaperKind ((int) w, (int) h));
422                                 settings.paper_sizes.Add (ps);
423                                 ptr = (IntPtr) ((long)ptr + Marshal.SizeOf (size));
424                         }
425                         
426                         return defsize;
427
428                 }
429                 
430                 /// <summary>
431                 /// Loads a printer's paper sources (trays). Returns the default PaperSource, and fills a list of paper_sources for use in dialogues
432                 /// </summary>
433                 /// <param name="settings">PrinterSettings object to fill</param>
434                 /// <param name="def_source">Default paper source, from the global options of the printer</param>
435                 /// <param name="paper_sources">List of available paper sizes that gets filled</param>
436                 private PaperSource LoadPrinterPaperSources (PrinterSettings settings, string def_source, 
437                                                                                                         NameValueCollection paper_sources)
438                 {
439                         PaperSourceKind kind;
440                         PaperSource defsource = null;
441                         foreach(string source in paper_sources) {
442                                 switch (source)
443                                 {
444                                         case "Tray":
445                                                 kind = PaperSourceKind.AutomaticFeed;
446                                                 break;
447                                         case "Envelope":
448                                                 kind = PaperSourceKind.Envelope;
449                                                 break;
450                                         case "Manual":
451                                                 kind = PaperSourceKind.Manual;
452                                                 break;
453                                         default:
454                                                 kind = PaperSourceKind.Custom;
455                                                 break;
456                                 }
457                                 settings.paper_sources.Add (new PaperSource (paper_sources[source], kind, def_source == source));
458                                 if (def_source == source)
459                                         defsource = settings.paper_sources[settings.paper_sources.Count-1];
460                         }
461                         
462                         if (defsource == null && settings.paper_sources.Count > 0)
463                                 return settings.paper_sources[0];
464                         return defsource;
465                 }
466
467                 /// <summary>
468                 /// </summary>
469                 /// <param name="load"></param>
470                 /// <param name="def_printer"></param>
471                 private static void LoadPrinters()
472                 {
473                         installed_printers.Clear ();            
474                         if (cups_installed == false)
475                                 return;
476                         
477                         IntPtr dests = IntPtr.Zero, ptr_printers;
478                         CUPS_DESTS printer;
479                         int n_printers = 0;
480                         int cups_dests_size = Marshal.SizeOf(typeof(CUPS_DESTS));
481                         string name, first, type, status, comment;
482                         first = type = status = comment = String.Empty;
483                         int state = 0;
484                         
485                         try {
486                                 n_printers = OpenDests (ref dests);
487
488                                 ptr_printers = dests;
489                                 for (int i = 0; i < n_printers; i++) {
490                                         printer = (CUPS_DESTS) Marshal.PtrToStructure (ptr_printers, typeof (CUPS_DESTS));
491                                         name = Marshal.PtrToStringAnsi (printer.name);
492
493                                         if (printer.is_default == 1)
494                                                 default_printer = name;
495                                 
496                                         if (first.Equals (String.Empty))
497                                                 first = name;
498                         
499                                         NameValueCollection options = LoadPrinterOptions(printer.options, printer.num_options);
500                                 
501                                         if (options["printer-state"] != null)
502                                                 state = Int32.Parse(options["printer-state"]);
503                                 
504                                         if (options["printer-comment"] != null)
505                                                 comment = options["printer-state"];
506
507                                         switch(state) {
508                                                 case 4:
509                                                         status = "Printing";
510                                                         break;
511                                                 case 5:
512                                                         status = "Stopped";
513                                                         break;
514                                                 default:
515                                                         status =  "Ready";
516                                                         break;
517                                         }
518                                 
519                                         installed_printers.Add (name, new SysPrn.Printer (String.Empty, type, status, comment));
520
521                                         ptr_printers = (IntPtr) ((long)ptr_printers + cups_dests_size);
522                                 }
523                         
524                         }
525                         finally {
526                                 CloseDests (ref dests, n_printers);
527                         }
528                         
529                         if (default_printer.Equals (String.Empty))
530                                 default_printer = first;
531                 }
532
533                 /// <summary>
534                 /// Gets a printer's settings for use in the print dialogue
535                 /// </summary>
536                 /// <param name="printer"></param>
537                 /// <param name="port"></param>
538                 /// <param name="type"></param>
539                 /// <param name="status"></param>
540                 /// <param name="comment"></param>
541                 internal override void GetPrintDialogInfo (string printer, ref string port, ref string type, ref string status, ref string comment) {
542                         int count = 0, state = -1;
543                         bool found = false;
544                         CUPS_DESTS cups_dests;
545                         IntPtr dests = IntPtr.Zero, ptr_printers, ptr_printer;
546                         int cups_dests_size = Marshal.SizeOf(typeof(CUPS_DESTS));
547                         
548                         if (cups_installed == false)
549                                 return;
550
551                         try {
552                                 count = OpenDests (ref dests);
553
554                                 if (count == 0)
555                                         return;
556
557                                 ptr_printers = dests;
558
559                                 for (int i = 0; i < count; i++) {
560                                         ptr_printer = (IntPtr) Marshal.ReadInt32 (ptr_printers);
561                                         if (Marshal.PtrToStringAnsi (ptr_printer).Equals(printer)) {
562                                                 found = true;
563                                                 break;
564                                         }
565                                         ptr_printers = (IntPtr) ((long)ptr_printers + cups_dests_size);                         
566                                 }
567                         
568                                 if (!found)
569                                         return;
570                         
571                                 cups_dests = (CUPS_DESTS) Marshal.PtrToStructure (ptr_printers, typeof (CUPS_DESTS));
572                         
573                                 NameValueCollection options = LoadPrinterOptions(cups_dests.options, cups_dests.num_options);
574
575                                 if (options["printer-state"] != null)
576                                         state = Int32.Parse(options["printer-state"]);
577                         
578                                 if (options["printer-comment"] != null)
579                                         comment = options["printer-state"];
580
581                                 switch(state) {
582                                         case 4:
583                                                 status = "Printing";
584                                                 break;
585                                         case 5:
586                                                 status = "Stopped";
587                                                 break;
588                                         default:
589                                                 status =  "Ready";
590                                                 break;
591                                 }
592                         }
593                         finally {
594                                 CloseDests (ref dests, count);
595                         }
596                 }
597
598                 /// <summary>
599                 /// Returns the appropriate PaperKind for the width and height
600                 /// </summary>
601                 /// <param name="width"></param>
602                 /// <param name="height"></param>
603                 private PaperKind GetPaperKind (int width, int height)
604                 {
605                         if (width == 827 && height == 1169)
606                                 return PaperKind.A4;
607                         if (width == 583 && height == 827)
608                                 return PaperKind.A5;
609                         if (width == 717 && height == 1012)
610                                 return PaperKind.B5;
611                         if (width == 693 && height == 984)
612                                 return PaperKind.B5Envelope;
613                         if (width == 638 && height == 902)
614                                 return PaperKind.C5Envelope;
615                         if (width == 449 && height == 638)
616                                 return PaperKind.C6Envelope;
617                         if (width == 1700 && height == 2200)
618                                 return PaperKind.CSheet;
619                         if (width == 433 && height == 866)
620                                 return PaperKind.DLEnvelope;
621                         if (width == 2200 && height == 3400)
622                                 return PaperKind.DSheet;
623                         if (width == 3400 && height == 4400)
624                                 return PaperKind.ESheet;
625                         if (width == 725 && height == 1050)
626                                 return PaperKind.Executive;
627                         if (width == 850 && height == 1300)
628                                 return PaperKind.Folio;
629                         if (width == 850 && height == 1200)
630                                 return PaperKind.GermanStandardFanfold;
631                         if (width == 1700 && height == 1100)
632                                 return PaperKind.Ledger;
633                         if (width == 850 && height == 1400)
634                                 return PaperKind.Legal;
635                         if (width == 927 && height == 1500)
636                                 return PaperKind.LegalExtra;
637                         if (width == 850 && height == 1100)
638                                 return PaperKind.Letter;
639                         if (width == 927 && height == 1200)
640                                 return PaperKind.LetterExtra;
641                         if (width == 850 && height == 1269)
642                                 return PaperKind.LetterPlus;
643                         if (width == 387 && height == 750)
644                                 return PaperKind.MonarchEnvelope;
645                         if (width == 387 && height == 887)
646                                 return PaperKind.Number9Envelope;
647                         if (width == 413 && height == 950)
648                                 return PaperKind.Number10Envelope;
649                         if (width == 450 && height == 1037)
650                                 return PaperKind.Number11Envelope;
651                         if (width == 475 && height == 1100)
652                                 return PaperKind.Number12Envelope;
653                         if (width == 500 && height == 1150)
654                                 return PaperKind.Number14Envelope;
655                         if (width == 363 && height == 650)
656                                 return PaperKind.PersonalEnvelope;
657                         if (width == 1000 && height == 1100)
658                                 return PaperKind.Standard10x11;
659                         if (width == 1000 && height == 1400)
660                                 return PaperKind.Standard10x14;
661                         if (width == 1100 && height == 1700)
662                                 return PaperKind.Standard11x17;
663                         if (width == 1200 && height == 1100)
664                                 return PaperKind.Standard12x11;
665                         if (width == 1500 && height == 1100)
666                                 return PaperKind.Standard15x11;
667                         if (width == 900 && height == 1100)
668                                 return PaperKind.Standard9x11;
669                         if (width == 550 && height == 850)
670                                 return PaperKind.Statement;
671                         if (width == 1100 && height == 1700)
672                                 return PaperKind.Tabloid;
673                         if (width == 1487 && height == 1100)
674                                 return PaperKind.USStandardFanfold;
675
676                         return PaperKind.Custom;
677                 }
678
679                 #endregion
680
681                 #region Print job methods
682
683                 static string tmpfile;
684
685                 /// <summary>
686                 /// Gets a pointer to an options list parsed from the printer's current settings, to use when setting up the printing job
687                 /// </summary>
688                 /// <param name="printer_settings"></param>
689                 /// <param name="page_settings"></param>
690                 /// <param name="options"></param>
691                 internal static int GetCupsOptions (PrinterSettings printer_settings, PageSettings page_settings, out IntPtr options)
692                 {
693                         options = IntPtr.Zero;
694
695                         PaperSize size = page_settings.PaperSize;
696                         int width = size.Width * 72 / 100;
697                         int height = size.Height * 72 / 100;
698
699                         StringBuilder sb = new StringBuilder();
700                         sb.Append(
701                                 "copies=" + printer_settings.Copies + " " + 
702                                 "Collate=" + printer_settings.Collate + " " +
703                                 "ColorModel=" + (page_settings.Color ? "Color" : "Black") + " " +
704                                 "PageSize=" + String.Format ("Custom.{0}x{1}", width, height) + " " +
705                                 "landscape=" + page_settings.Landscape
706                         );
707                         
708                         if (printer_settings.CanDuplex)
709                         {
710                                 if (printer_settings.Duplex == Duplex.Simplex)
711                                         sb.Append(" Duplex=None");
712                                 else
713                                         sb.Append(" Duplex=DuplexNoTumble");                            
714                         }
715
716                         return cupsParseOptions (sb.ToString(), 0, ref options);
717                 }
718
719                 internal static bool StartDoc (GraphicsPrinter gr, string doc_name, string output_file)
720                 {
721                         DOCINFO doc = (DOCINFO) doc_info[gr.Hdc];
722                         doc.title = doc_name;
723                         return true;
724                 }
725
726                 internal static bool EndDoc (GraphicsPrinter gr)
727                 {
728                         DOCINFO doc = (DOCINFO) doc_info[gr.Hdc];
729
730                         gr.Graphics.Dispose (); // Dispose object to force surface finish
731                         
732                         IntPtr options;
733                         int options_count = GetCupsOptions (doc.settings, doc.default_page_settings, out options);
734
735                         cupsPrintFile (doc.settings.PrinterName, doc.filename, doc.title, options_count, options);
736                         cupsFreeOptions (options_count, options);
737                         doc_info.Remove (gr.Hdc);
738                         if (tmpfile != null) {
739                                 try { File.Delete (tmpfile); }
740                                 catch { }
741                         }
742                         return true;
743                 }
744
745                 internal static bool StartPage (GraphicsPrinter gr)
746                 {
747                         return true;
748                 }
749
750                 internal static bool EndPage (GraphicsPrinter gr)
751                 {
752                         GdipGetPostScriptSavePage (gr.Hdc);
753                         return true;
754                 }
755
756                 // Unfortunately, PrinterSettings and PageSettings couldn't be referencing each other,
757                 // thus we need to pass them separately
758                 internal static IntPtr CreateGraphicsContext (PrinterSettings settings, PageSettings default_page_settings)
759                 {
760                         IntPtr graphics = IntPtr.Zero;
761                         string name;
762                         if (!settings.PrintToFile) {
763                                 StringBuilder sb = new StringBuilder (1024);
764                                 int length = sb.Capacity;
765                                 cupsTempFile (sb, length);
766                                 name = sb.ToString ();
767                                 tmpfile = name;
768                         }
769                         else
770                                 name = settings.PrintFileName;
771
772                         PaperSize psize = default_page_settings.PaperSize;
773                         int width, height;
774                         if (default_page_settings.Landscape) { // Swap in case of landscape
775                                 width = psize.Height;
776                                 height = psize.Width;
777                         } else {
778                                 width = psize.Width;
779                                 height = psize.Height;
780                         }
781
782                         GdipGetPostScriptGraphicsContext (name,
783                                 width * 72 / 100,
784                                 height * 72 / 100, 
785                                 default_page_settings.PrinterResolution.X,
786                                 default_page_settings.PrinterResolution.Y, ref graphics);
787
788                         DOCINFO doc = new DOCINFO ();
789                         doc.filename = name;
790                         doc.settings = settings;
791                         doc.default_page_settings = default_page_settings;
792                         doc_info.Add (graphics, doc);
793
794                         return graphics;
795                 }
796
797                 #endregion
798
799                 #region DllImports
800
801                 [DllImport("libcups", CharSet=CharSet.Ansi)]
802                 static extern int cupsGetDests (ref IntPtr dests);
803
804                 [DllImport("libcups", CharSet=CharSet.Ansi)]
805                 static extern void cupsGetDest (string name, string instance, int num_dests, ref IntPtr dests);
806
807                 [DllImport("libcups")]
808                 static extern void cupsFreeDests (int num_dests, IntPtr dests);
809
810                 [DllImport("libcups", CharSet=CharSet.Ansi)]
811                 static extern IntPtr cupsTempFile (StringBuilder sb, int len);
812
813                 [DllImport("libcups", CharSet=CharSet.Ansi)]
814                 static extern IntPtr cupsGetDefault ();
815
816                 [DllImport("libcups", CharSet=CharSet.Ansi)]
817                 static extern int cupsPrintFile (string printer, string filename, string title, int num_options, IntPtr options);
818
819                 [DllImport("libcups", CharSet=CharSet.Ansi)]
820                 static extern IntPtr cupsGetPPD (string printer);
821
822                 [DllImport("libcups", CharSet=CharSet.Ansi)]
823                 static extern IntPtr ppdOpenFile (string filename);
824
825                 [DllImport("libcups", CharSet=CharSet.Ansi)]
826                 static extern IntPtr ppdFindOption (IntPtr ppd_file, string keyword);
827
828                 [DllImport("libcups")]
829                 static extern void ppdClose (IntPtr ppd);
830
831                 [DllImport ("libcups", CharSet=CharSet.Ansi)]
832                 static extern int cupsParseOptions (string arg, int number_of_options, ref IntPtr options);
833
834                 [DllImport("libcups")]
835                 static extern void cupsFreeOptions (int number_options, IntPtr options);
836
837                 [DllImport("gdiplus.dll", CharSet=CharSet.Ansi)]
838                 static extern int GdipGetPostScriptGraphicsContext (string filename, int with, int height, double dpix, double dpiy, ref IntPtr graphics);
839
840                 [DllImport("gdiplus.dll")]
841                 static extern int GdipGetPostScriptSavePage (IntPtr graphics);
842
843                 #endregion
844
845                 #region Struct
846                 public struct DOCINFO
847                 {
848                         public PrinterSettings settings;
849                         public PageSettings default_page_settings;
850                         public string title;
851                         public string filename;
852                 }
853
854                 public struct PPD_SIZE
855                 {
856                         public  int marked;
857                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=42)]
858                         public  string name;
859                         public  float width;
860                         public  float length;
861                         public  float left;
862                         public  float bottom;
863                         public  float right;
864                         public  float top;
865                 }
866
867                 public struct PPD_GROUP
868                 {
869                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=40)]
870                         public string text;
871                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=42)]
872                         public string name;
873                         public int num_options;
874                         public IntPtr options;
875                         public int num_subgroups;
876                         public IntPtr subgrups;
877                 }
878
879                 public struct PPD_OPTION
880                 {
881                         public byte     conflicted;
882                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=41)]
883                         public string   keyword;
884                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=41)]
885                         public string   defchoice;
886                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=81)]
887                         public string   text;
888                         public int      ui;
889                         public int      section;
890                         public float    order;
891                         public int      num_choices;
892                         public IntPtr   choices;
893                 }
894
895                 public struct PPD_CHOICE
896                 {
897                         public byte     marked;
898                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=41)]
899                         public string   choice;
900                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=81)]
901                         public string   text;
902                         public IntPtr   code;
903                         public IntPtr   option;
904                 }
905
906                 public struct PPD_FILE
907                 {
908                         public int      language_level;
909                         public int      color_device;
910                         public int      variable_sizes;
911                         public int      accurate_screens;
912                         public int      contone_only;
913                         public int      landscape;
914                         public int      model_number;
915                         public int      manual_copies;
916                         public int      throughput;
917                         public int      colorspace;
918                         public IntPtr   patches;
919                         public int      num_emulations;
920                         public IntPtr   emulations;
921                         public IntPtr   jcl_begin;
922                         public IntPtr   jcl_ps;
923                         public IntPtr   jcl_end;
924                         public IntPtr   lang_encoding;
925                         public IntPtr   lang_version;
926                         public IntPtr   modelname;
927                         public IntPtr   ttrasterizer;
928                         public IntPtr   manufacturer;
929                         public IntPtr   product;
930                         public IntPtr   nickname;
931                         public IntPtr   shortnickname;
932                         public int      num_groups;
933                         public IntPtr   groups;
934                         public int      num_sizes;
935                         public IntPtr   sizes;
936
937                         /* There is more data after this that we are not using*/
938                 }
939
940
941                 public struct CUPS_OPTIONS
942                 {
943                         public IntPtr name;
944                         public IntPtr val;
945                 }
946                 
947                 public struct CUPS_DESTS
948                 {
949                         public IntPtr   name;
950                         public IntPtr   instance;
951                         public int      is_default;
952                         public int      num_options;
953                         public IntPtr   options;
954                 }
955                 
956                 #endregion
957         }
958
959         class GlobalPrintingServicesUnix : GlobalPrintingServices\r
960         {\r
961                 internal override PrinterSettings.StringCollection InstalledPrinters {\r
962                         get {\r
963                                 return PrintingServicesUnix.InstalledPrinters;\r
964                         }\r
965                 }\r
966 \r
967                 internal override IntPtr CreateGraphicsContext (PrinterSettings settings, PageSettings default_page_settings)\r
968                 {\r
969                         return PrintingServicesUnix.CreateGraphicsContext (settings, default_page_settings);\r
970                 }\r
971 \r
972                 internal override bool StartDoc (GraphicsPrinter gr, string doc_name, string output_file)\r
973                 {\r
974                         return PrintingServicesUnix.StartDoc (gr, doc_name, output_file);\r
975                 }\r
976 \r
977                 internal override bool EndDoc (GraphicsPrinter gr)\r
978                 {\r
979                         return PrintingServicesUnix.EndDoc (gr);\r
980                 }\r
981 \r
982                 internal override bool StartPage (GraphicsPrinter gr)\r
983                 {\r
984                         return PrintingServicesUnix.StartPage (gr);\r
985                 }\r
986 \r
987                 internal override bool EndPage (GraphicsPrinter gr)\r
988                 {\r
989                         return PrintingServicesUnix.EndPage (gr);\r
990                 }\r
991         }
992 }
993