Hopefully fixes #79835
[mono.git] / mcs / class / System.Drawing / System.Drawing.Printing / PrintingServicesUnix.cs
index e2164ca3bbfb71bda9e1ea0141a9bc306edfd036..afcf9b29c8d22f5a95a02ccc738791d3c72a7671 100644 (file)
@@ -31,29 +31,63 @@ using System.Drawing.Printing;
 using System.ComponentModel;
 using System.Drawing.Imaging;
 using System.Text;
+using System.IO;
 
 namespace System.Drawing.Printing
 {
        internal class PrintingServicesUnix : PrintingServices
        {
                private Hashtable doc_info = new Hashtable ();
+               private bool cups_installed;
+               private string printer_name;
+               private bool is_printer_valid;
 
                internal PrintingServicesUnix ()
                {
+                       CheckCupsInstalled ();
+               }
+
+               private void CheckCupsInstalled ()
+               {
+                       try {
+                               cupsGetDefault ();
+                       }
+                       catch (DllNotFoundException) {
+                               Console.WriteLine("libcups not found. To have printing support, you need cups installed");
+                               cups_installed = false;
+                               return;
+                       }
 
+                       cups_installed = true;
+               }
+               
+               internal override bool IsPrinterValid(string printer, bool force)
+               {
+                       if (!cups_installed || printer == null | printer == String.Empty)
+                               return false;
+
+                       if (!force && this.printer_name != null && String.Intern(this.printer_name).Equals(printer))
+                               return is_printer_valid;
+
+                       IntPtr ptr = cupsGetPPD (printer);
+                       string ppd_filename = Marshal.PtrToStringAnsi (ptr);
+                       is_printer_valid = ppd_filename != null;
+                       this.printer_name = printer; 
+                       return is_printer_valid;
                }
 
                // Methods
                internal override void LoadPrinterSettings (string printer, PrinterSettings settings)
                {
-                       IntPtr ptr, ppd_handle;
+                       IntPtr ptr, ppd_handle, ptr_opt, ptr_choice;
                        string ppd_filename;
                        PPD_FILE ppd;
+                       PPD_OPTION option;
+                       PPD_CHOICE choice;                      
 
-                       if ((printer == null) || (printer == String.Empty)) {
+                       if (cups_installed == false || (printer == null) || (printer == String.Empty))
                                return;
-                       }
-
+               
                        ptr = cupsGetPPD (printer);
                        ppd_filename = Marshal.PtrToStringAnsi (ptr);
                        ppd_handle = ppdOpenFile (ppd_filename);
@@ -62,6 +96,47 @@ namespace System.Drawing.Printing
                        ppd = (PPD_FILE) Marshal.PtrToStructure (ppd_handle, typeof (PPD_FILE));
                        settings.landscape_angle = ppd.landscape;
                        settings.supports_color = (ppd.color_device == 0) ? false : true;
+
+                       // Default paper source
+                       ptr_opt = ppdFindOption (ppd_handle, "InputSlot"); 
+                       if (ptr_opt != IntPtr.Zero) {
+                               option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
+                               ptr_choice = option.choices;
+                               for (int c = 0; c < option.num_choices; c++) {
+                                       choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
+                                       ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
+                                       if (choice.choice == option.defchoice) {
+                                               foreach (PaperSource paper_source in settings.PaperSources) {
+                                                       if (paper_source.SourceName ==  choice.text) {
+                                                               settings.DefaultPageSettings.PaperSource = paper_source;
+                                                               break;
+                                                       }
+                                               }
+                                               break;
+                                       }
+                               }
+                       }
+                       
+                       // Default paper size
+                       ptr_opt = ppdFindOption (ppd_handle, "PageSize"); 
+                       if (ptr_opt != IntPtr.Zero) {
+                               option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
+                               ptr_choice = option.choices;
+                               for (int c = 0; c < option.num_choices; c++) {
+                                       choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
+                                       ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
+                                       if (choice.choice == option.defchoice) {
+                                               foreach (PaperSize paper_size in settings.PaperSizes) {
+                                                       if (paper_size.PaperName == choice.text) {
+                                                               settings.DefaultPageSettings.PaperSize = paper_size;
+                                                               break;
+                                                       }
+                                               }
+                                               break;
+                                       }
+                               }
+                       }
+                       
                        ppdClose (ppd_handle);
                }
 
@@ -78,7 +153,6 @@ namespace System.Drawing.Printing
                        PPD_FILE ppd;
                        PPD_SIZE size;
                        PaperSize ps;
-                       PaperKind kind = PaperKind.Custom;
 
                        settings.PaperSizes.Clear ();
 
@@ -91,17 +165,49 @@ namespace System.Drawing.Printing
                        float w, h;
                        for (int i = 0; i < ppd.num_sizes; i++) {
                                size = (PPD_SIZE) Marshal.PtrToStructure (ptr, typeof (PPD_SIZE));
-                               real_name = GetPaperSizeName (ppd, size.name);
+                               real_name = GetPaperSizeName (ppd_handle, size.name);
                                ptr = new IntPtr (ptr.ToInt64 () + Marshal.SizeOf (size));
-
                                w = size.width * 100 / 72;
                                h = size.length * 100 / 72;
                                ps = new PaperSize (real_name, (int) w, (int) h);
-                               // TODO: Convert from name to paper kind enum
-                               ps.SetKind (kind);
+                               ps.SetKind (GetPaperKind ((int) w, (int) h));
                                settings.PaperSizes.Add (ps);
                        }
 
+                       ppdClose (ppd_handle);
+               }               
+               
+               internal override void LoadPrinterPaperSources (string printer, PrinterSettings settings)
+               {
+                       IntPtr ptr, ppd_handle, ptr_opt, ptr_choice;
+                       string ppd_filename;
+                       PPD_OPTION option;
+                       PPD_CHOICE choice;
+
+                       if (cups_installed == false || (printer == null) || (printer == String.Empty))
+                               return;
+
+                       ptr = cupsGetPPD (printer);
+                       ppd_filename = Marshal.PtrToStringAnsi (ptr);
+                       ppd_handle = ppdOpenFile (ppd_filename);
+
+                       ptr_opt = ppdFindOption (ppd_handle, "InputSlot"); 
+
+                       if (ptr_opt == IntPtr.Zero) {
+                               ppdClose (ppd_handle);
+                               return; 
+                       }
+                       
+                       option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
+                       //Console.WriteLine (" OPTION  key:{0} def:{1} text: {2}", option.keyword, option.defchoice, option.text);
+
+                       ptr_choice = option.choices;
+                       for (int c = 0; c < option.num_choices; c++) {
+                               choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
+                               ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
+                               //Console.WriteLine ("       choice:{0} - text: {1}", choice.choice, choice.text);
+                               settings.PaperSources.Add (new PaperSource (choice.text, PaperSourceKind.Custom));                                      
+                       }
                        ppdClose (ppd_handle);
                }
 
@@ -116,14 +222,24 @@ namespace System.Drawing.Printing
                        return true;
                }
 
+               string tmpfile;
+
                internal override bool EndDoc (GraphicsPrinter gr)
                {
                        DOCINFO doc = (DOCINFO) doc_info[gr.Hdc];
 
                        gr.Graphics.Dispose (); // Dispose object to force surface finish
-                       cupsPrintFile (doc.settings.PrinterName, doc.filename, doc.title, 0, IntPtr.Zero);
+
+                       IntPtr options;
+                       int options_count = GetCupsOptions (doc.settings, doc.default_page_settings, out options);
+
+                       cupsPrintFile (doc.settings.PrinterName, doc.filename, doc.title, options_count, options);
+                       cupsFreeOptions (options_count, options);
                        doc_info.Remove (gr.Hdc);
-                       //TODO: Delete temporary file created
+                       if (tmpfile != null) {
+                               try { File.Delete (tmpfile); }
+                               catch { }
+                       }
                        return true;
                }
 
@@ -134,46 +250,77 @@ namespace System.Drawing.Printing
                        return true;
                }
 
-               internal override IntPtr CreateGraphicsContext (PrinterSettings settings)
+               // Unfortunately, PrinterSettings and PageSettings couldn't be referencing each other,
+               // thus we need to pass them separately
+               internal override IntPtr CreateGraphicsContext (PrinterSettings settings, PageSettings default_page_settings)
                {
                        IntPtr graphics = IntPtr.Zero;
-                       StringBuilder name = new StringBuilder (1024);
-                       int length = name.Capacity;
-                       cupsTempFile (name, length);
-               
-                       GdipGetPostScriptGraphicsContext (name.ToString(),
-                               settings.DefaultPageSettings.PaperSize.Width / 100 * 72,
-                               settings.DefaultPageSettings.PaperSize.Height / 100 * 72, 
+                       string name;
+                       if (!settings.PrintToFile) {
+                               StringBuilder sb = new StringBuilder (1024);
+                               int length = sb.Capacity;
+                               cupsTempFile (sb, length);
+                               name = sb.ToString ();
+                               tmpfile = name;
+                       }
+                       else
+                               name = settings.PrintFileName;
+
+                       GdipGetPostScriptGraphicsContext (name,
+                               default_page_settings.PaperSize.Width / 100 * 72,
+                               default_page_settings.PaperSize.Height / 100 * 72, 
                                // Harcoded dpy's
                                300, 300, ref graphics);
 
                        DOCINFO doc = new DOCINFO ();
                        doc.filename = name.ToString();
                        doc.settings = settings;
+                       doc.default_page_settings = default_page_settings;
                        doc_info.Add (graphics, doc);
 
                        return graphics;
                }
 
+               internal int GetCupsOptions (PrinterSettings printer_settings, PageSettings page_settings, out IntPtr options)
+               {
+                       options = IntPtr.Zero;
+
+                       PaperSize size = page_settings.PaperSize;
+                       int width = size.Width * 72 / 100;
+                       int height = size.Height * 72 / 100;
+
+                       string options_string =
+                               "copies=" + printer_settings.Copies + " " + 
+                               "Collate=" + printer_settings.Collate + " " +
+                               "ColorModel=" + (page_settings.Color ? "Color" : "Black") + " " +
+                               "PageSize=" + String.Format ("Custom.{0}x{1}", width, height);
+
+                       return cupsParseOptions (options_string, 0, ref options);
+               }
+
                // Properties
 
                internal override PrinterSettings.StringCollection InstalledPrinters {
                        get {
                                int n_printers;
-                               IntPtr printers = IntPtr.Zero, ptr_printers, ptr_printer;
+                               IntPtr dests = IntPtr.Zero, ptr_printers, ptr_printer;
                                string str;
                                PrinterSettings.StringCollection col = new PrinterSettings.StringCollection (new string[] {});
 
-                               n_printers = cupsGetPrinters (ref printers);
+                               if (cups_installed == false)
+                                       return col;
+
+                               n_printers = cupsGetDests (ref dests);
 
-                               ptr_printers = printers;
+                               ptr_printers = dests;
                                for (int i = 0; i < n_printers; i++) {
                                        ptr_printer = (IntPtr) Marshal.ReadInt32 (ptr_printers);
                                        str = Marshal.PtrToStringAnsi (ptr_printer);
-                                       ptr_printers = new IntPtr (ptr_printers.ToInt64 () + 4);
+                                       Marshal.FreeHGlobal (ptr_printer);
+                                       ptr_printers = new IntPtr (ptr_printers.ToInt64 () + 20 /*size of CUPS_DEST*/);
                                        col.Add (str);
                                }
-                               Marshal.FreeHGlobal (printers);
+                               Marshal.FreeHGlobal (dests);
                                return col;
                        }
                }
@@ -181,59 +328,184 @@ namespace System.Drawing.Printing
                internal override string DefaultPrinter {
                        get {
                                IntPtr str;
+
+                               if (cups_installed == false)
+                                       return string.Empty;
+
                                str = cupsGetDefault ();
-                               return Marshal.PtrToStringAnsi (str);
+                               if (str == IntPtr.Zero)
+                                       return Marshal.PtrToStringAnsi (str);
+                               return String.Empty;
                        }
                }
 
                // Private functions
 
-               private string GetPaperSizeName (PPD_FILE ppd, string name)
+               private string GetPaperSizeName (IntPtr ppd, string name)
                {
                        string rslt = name;
-                       PPD_GROUP group;
                        PPD_OPTION option;
                        PPD_CHOICE choice;
-                       IntPtr ptr, ptr_opt, ptr_choice;
-
-                       ptr = ppd.groups;
-                       for (int i = 0; i < ppd.num_groups; i++) {
-                               group = (PPD_GROUP) Marshal.PtrToStructure (ptr, typeof (PPD_GROUP));
-                               //Console.WriteLine ("Size text:{0} name:{1} opts {2}", group.text, group.name, group.num_options);
-                               ptr = new IntPtr (ptr.ToInt64 () + Marshal.SizeOf (group));
-
-                               ptr_opt = group.options;
-                               for (int n = 0; n < group.num_options; n++) {
-                                       option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
-                                       ptr_opt = new IntPtr (ptr_opt.ToInt64 () + Marshal.SizeOf (option));
-                                       //Console.WriteLine ("   key:{0} def:{1} text: {2}", option.keyword, option.defchoice, option.text);
-
-                                       if (!option.keyword.Equals ("PageSize"))
-                                               continue;
-
-                                       ptr_choice = option.choices;
-                                       for (int c = 0; c < option.num_choices; c++) {
-                                               choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
-                                               ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
-                                               //Console.WriteLine ("       choice:{0} - text: {1}", choice.choice, choice.text);
-                                               if (name.Equals (choice.choice)) {
-                                                       rslt = choice.text;
-                                                       break;
-                                               }
-                                       }
+                       IntPtr ptr_opt, ptr_choice;
+                       
+                       ptr_opt = ppdFindOption (ppd, "PageSize"); 
+
+                       if (ptr_opt == IntPtr.Zero) {
+                               return rslt;    
+                       }
+                       
+                       option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
+
+                       ptr_choice = option.choices;
+                       for (int c = 0; c < option.num_choices; c++) {
+                               choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
+                               ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
+                               if (name.Equals (choice.choice)) {
+                                       // Special case for custom size (cups returns NULL for it)
+                                       if (name == "Custom" && choice.text == null)
+                                               rslt = "Custom";
+                                       else
+                                               rslt = choice.text;
+
+                                       break;
                                }
                        }
-
                        return rslt;
                }
 
+               private PaperKind GetPaperKind (int width, int height)
+               {
+                       if (width == 827 && height == 1169)
+                               return PaperKind.A4;
+                       if (width == 583 && height == 827)
+                               return PaperKind.A5;
+                       if (width == 717 && height == 1012)
+                               return PaperKind.B5;
+                       if (width == 693 && height == 984)
+                               return PaperKind.B5Envelope;
+                       if (width == 638 && height == 902)
+                               return PaperKind.C5Envelope;
+                       if (width == 449 && height == 638)
+                               return PaperKind.C6Envelope;
+                       if (width == 1700 && height == 2200)
+                               return PaperKind.CSheet;
+                       if (width == 433 && height == 866)
+                               return PaperKind.DLEnvelope;
+                       if (width == 2200 && height == 3400)
+                               return PaperKind.DSheet;
+                       if (width == 3400 && height == 4400)
+                               return PaperKind.ESheet;
+                       if (width == 725 && height == 1050)
+                               return PaperKind.Executive;
+                       if (width == 850 && height == 1300)
+                               return PaperKind.Folio;
+                       if (width == 850 && height == 1200)
+                               return PaperKind.GermanStandardFanfold;
+                       if (width == 1700 && height == 1100)
+                               return PaperKind.Ledger;
+                       if (width == 850 && height == 1400)
+                               return PaperKind.Legal;
+                       if (width == 927 && height == 1500)
+                               return PaperKind.LegalExtra;
+                       if (width == 850 && height == 1100)
+                               return PaperKind.Letter;
+                       if (width == 927 && height == 1200)
+                               return PaperKind.LetterExtra;
+                       if (width == 850 && height == 1269)
+                               return PaperKind.LetterPlus;
+                       if (width == 387 && height == 750)
+                               return PaperKind.MonarchEnvelope;
+                       if (width == 387 && height == 887)
+                               return PaperKind.Number9Envelope;
+                       if (width == 413 && height == 950)
+                               return PaperKind.Number10Envelope;
+                       if (width == 450 && height == 1037)
+                               return PaperKind.Number11Envelope;
+                       if (width == 475 && height == 1100)
+                               return PaperKind.Number12Envelope;
+                       if (width == 500 && height == 1150)
+                               return PaperKind.Number14Envelope;
+                       if (width == 363 && height == 650)
+                               return PaperKind.PersonalEnvelope;
+                       if (width == 1000 && height == 1100)
+                               return PaperKind.Standard10x11;
+                       if (width == 1000 && height == 1400)
+                               return PaperKind.Standard10x14;
+                       if (width == 1100 && height == 1700)
+                               return PaperKind.Standard11x17;
+                       if (width == 1200 && height == 1100)
+                               return PaperKind.Standard12x11;
+                       if (width == 1500 && height == 1100)
+                               return PaperKind.Standard15x11;
+                       if (width == 900 && height == 1100)
+                               return PaperKind.Standard9x11;
+                       if (width == 550 && height == 850)
+                               return PaperKind.Statement;
+                       if (width == 1100 && height == 1700)
+                               return PaperKind.Tabloid;
+                       if (width == 1487 && height == 1100)
+                               return PaperKind.USStandardFanfold;
+
+                       return PaperKind.Custom;
+               }
+
+               internal override void GetPrintDialogInfo (string printer, ref string port, ref string type, ref string status, ref string comment)
+               {                       
+                       int printers, state = -1;
+                       CUPS_DESTS cups_dests;
+                       CUPS_OPTIONS options;
+                       string str;
+                       IntPtr dests = IntPtr.Zero, ptr_printers, ptr_printer, ptr_options;
+                       
+                       if (cups_installed == false)
+                               return;
+
+                       printers = cupsGetDests (ref dests);
+
+                       ptr_printers = dests;
+                       for (int i = 0; i < printers; i++) {
+                               cups_dests = (CUPS_DESTS) Marshal.PtrToStructure (ptr_printers, typeof (CUPS_DESTS));
+                               str = Marshal.PtrToStringAnsi (cups_dests.name);
+                               ptr_printers = new IntPtr (ptr_printers.ToInt64 () + 20 /*size of CUPS_DEST*/);
+                               if (str != printer)
+                                       continue;
+
+                               ptr_options = cups_dests.options;
+                               for (int o = 0; o < cups_dests.num_options; o ++) {
+                                       options = (CUPS_OPTIONS) Marshal.PtrToStructure (ptr_options, typeof (CUPS_OPTIONS));
+                                       str = Marshal.PtrToStringAnsi (options.name);                                   
+                                       if (str == "printer-state") {
+                                               state = Int32.Parse (Marshal.PtrToStringAnsi (options.val));
+                                       } else {
+                                               if (str == "printer-info")
+                                                       comment = Marshal.PtrToStringAnsi (options.val);
+                                       }
+                                       ptr_options = new IntPtr (ptr_options.ToInt64 () + 8 /*size of CUPS_DEST*/);
+                               }
+                               
+                       }
+
+                       Marshal.FreeHGlobal (dests);
+
+                       if (state == 4) {
+                               status = "Printing";
+                       }
+                       else {
+                               if (state == 5) {
+                                       status = "Stopped";
+                               }
+                               else {
+                                       status =  "Ready";
+                               }
+                       }                       
+               }
 
                //
                // DllImports
                //
 
                [DllImport("libcups", CharSet=CharSet.Ansi)]
-               static extern int cupsGetPrinters (ref IntPtr printers);
+               static extern int cupsGetDests (ref IntPtr dests);
 
                [DllImport("libcups", CharSet=CharSet.Ansi)]
                static extern IntPtr cupsTempFile (StringBuilder sb, int len);
@@ -250,13 +522,22 @@ namespace System.Drawing.Printing
                [DllImport("libcups", CharSet=CharSet.Ansi)]
                static extern IntPtr ppdOpenFile (string filename);
 
+               [DllImport("libcups", CharSet=CharSet.Ansi)]
+               static extern IntPtr ppdFindOption (IntPtr ppd_file, string keyword);
+
                [DllImport("libcups")]
                static extern void ppdClose (IntPtr ppd);
 
-               [DllImport("libgdiplus", CharSet=CharSet.Ansi)]
+               [DllImport ("libcups", CharSet=CharSet.Ansi)]
+               static extern int cupsParseOptions (string arg, int number_of_options, ref IntPtr options);
+
+               [DllImport("libcups")]
+               static extern void cupsFreeOptions (int number_options, IntPtr options);
+
+               [DllImport("gdiplus.dll", CharSet=CharSet.Ansi)]
                static extern int GdipGetPostScriptGraphicsContext (string filename, int with, int height, double dpix, double dpiy, ref IntPtr graphics);
 
-               [DllImport("libgdiplus")]
+               [DllImport("gdiplus.dll")]
                static extern int GdipGetPostScriptSavePage (IntPtr graphics);
 
 
@@ -264,6 +545,7 @@ namespace System.Drawing.Printing
                public struct DOCINFO
                {
                        public PrinterSettings settings;
+                       public PageSettings default_page_settings;
                        public string title;
                        public string filename;
                }
@@ -353,6 +635,22 @@ namespace System.Drawing.Printing
 
                        /* There is more data after this that we are not using*/
                }
+
+
+               public struct CUPS_OPTIONS
+               {
+                       public IntPtr name;
+                       public IntPtr val;
+               }
+               
+               public struct CUPS_DESTS
+               {
+                       public IntPtr   name;
+                       public IntPtr   instance;
+                       public int      is_default;
+                       public int      num_options;
+                       public IntPtr   options;
+               }
        }
 }