2 // Copyright (C) 2005 Novell, Inc. http://www.novell.com
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:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
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.
25 // Jordi Mas i Hernandez, jordimash@gmail.com
28 using System.Runtime.InteropServices;
29 using System.Collections;
30 using System.Drawing.Printing;
31 using System.ComponentModel;
32 using System.Drawing.Imaging;
36 namespace System.Drawing.Printing
38 internal class PrintingServicesUnix : PrintingServices
40 private Hashtable doc_info = new Hashtable ();
41 private bool cups_installed;
42 private string printer_name;
43 private bool is_printer_valid;
45 internal PrintingServicesUnix ()
47 CheckCupsInstalled ();
50 private void CheckCupsInstalled ()
55 catch (DllNotFoundException) {
56 Console.WriteLine("libcups not found. To have printing support, you need cups installed");
57 cups_installed = false;
61 cups_installed = true;
64 internal override bool IsPrinterValid(string printer, bool force)
66 if (!cups_installed || printer == null | printer == String.Empty)
69 if (!force && this.printer_name != null && String.Intern(this.printer_name).Equals(printer))
70 return is_printer_valid;
72 IntPtr ptr = cupsGetPPD (printer);
73 string ppd_filename = Marshal.PtrToStringAnsi (ptr);
74 is_printer_valid = ppd_filename != null;
75 this.printer_name = printer;
76 return is_printer_valid;
80 internal override void LoadPrinterSettings (string printer, PrinterSettings settings)
82 IntPtr ptr, ppd_handle, ptr_opt, ptr_choice;
88 if (cups_installed == false || (printer == null) || (printer == String.Empty))
91 ptr = cupsGetPPD (printer);
92 ppd_filename = Marshal.PtrToStringAnsi (ptr);
93 ppd_handle = ppdOpenFile (ppd_filename);
94 //Console.WriteLine ("File: {0}", ppd_filename);
96 ppd = (PPD_FILE) Marshal.PtrToStructure (ppd_handle, typeof (PPD_FILE));
97 settings.landscape_angle = ppd.landscape;
98 settings.supports_color = (ppd.color_device == 0) ? false : true;
100 // Default paper source
101 ptr_opt = ppdFindOption (ppd_handle, "InputSlot");
102 if (ptr_opt != IntPtr.Zero) {
103 option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
104 ptr_choice = option.choices;
105 for (int c = 0; c < option.num_choices; c++) {
106 choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
107 ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
108 if (choice.choice == option.defchoice) {
109 foreach (PaperSource paper_source in settings.PaperSources) {
110 if (paper_source.SourceName == choice.text) {
111 settings.DefaultPageSettings.PaperSource = paper_source;
120 // Default paper size
121 ptr_opt = ppdFindOption (ppd_handle, "PageSize");
122 if (ptr_opt != IntPtr.Zero) {
123 option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
124 ptr_choice = option.choices;
125 for (int c = 0; c < option.num_choices; c++) {
126 choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
127 ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
128 if (choice.choice == option.defchoice) {
129 foreach (PaperSize paper_size in settings.PaperSizes) {
130 if (paper_size.PaperName == choice.text) {
131 settings.DefaultPageSettings.PaperSize = paper_size;
140 ppdClose (ppd_handle);
143 internal override void LoadPrinterResolutions (string printer, PrinterSettings settings)
145 settings.PrinterResolutions.Clear ();
146 LoadDefaultResolutions (settings.PrinterResolutions);
149 internal override void LoadPrinterPaperSizes (string printer, PrinterSettings settings)
151 IntPtr ptr, ppd_handle;
152 string ppd_filename, real_name;
157 settings.PaperSizes.Clear ();
159 ptr = cupsGetPPD (printer);
160 ppd_filename = Marshal.PtrToStringAnsi (ptr);
161 ppd_handle = ppdOpenFile (ppd_filename);
163 ppd = (PPD_FILE) Marshal.PtrToStructure (ppd_handle, typeof (PPD_FILE));
166 for (int i = 0; i < ppd.num_sizes; i++) {
167 size = (PPD_SIZE) Marshal.PtrToStructure (ptr, typeof (PPD_SIZE));
168 real_name = GetPaperSizeName (ppd_handle, size.name);
169 ptr = new IntPtr (ptr.ToInt64 () + Marshal.SizeOf (size));
170 w = size.width * 100 / 72;
171 h = size.length * 100 / 72;
172 ps = new PaperSize (real_name, (int) w, (int) h);
173 ps.SetKind (GetPaperKind ((int) w, (int) h));
174 settings.PaperSizes.Add (ps);
177 ppdClose (ppd_handle);
180 internal override void LoadPrinterPaperSources (string printer, PrinterSettings settings)
182 IntPtr ptr, ppd_handle, ptr_opt, ptr_choice;
187 if (cups_installed == false || (printer == null) || (printer == String.Empty))
190 ptr = cupsGetPPD (printer);
191 ppd_filename = Marshal.PtrToStringAnsi (ptr);
192 ppd_handle = ppdOpenFile (ppd_filename);
194 ptr_opt = ppdFindOption (ppd_handle, "InputSlot");
196 if (ptr_opt == IntPtr.Zero) {
197 ppdClose (ppd_handle);
201 option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
202 //Console.WriteLine (" OPTION key:{0} def:{1} text: {2}", option.keyword, option.defchoice, option.text);
204 ptr_choice = option.choices;
205 for (int c = 0; c < option.num_choices; c++) {
206 choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
207 ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
208 //Console.WriteLine (" choice:{0} - text: {1}", choice.choice, choice.text);
209 settings.PaperSources.Add (new PaperSource (choice.text, PaperSourceKind.Custom));
211 ppdClose (ppd_handle);
214 internal override bool StartPage (GraphicsPrinter gr)
219 internal override bool EndPage (GraphicsPrinter gr)
221 GdipGetPostScriptSavePage (gr.Hdc);
227 internal override bool EndDoc (GraphicsPrinter gr)
229 DOCINFO doc = (DOCINFO) doc_info[gr.Hdc];
231 gr.Graphics.Dispose (); // Dispose object to force surface finish
234 int options_count = GetCupsOptions (doc.settings, doc.default_page_settings, out options);
236 cupsPrintFile (doc.settings.PrinterName, doc.filename, doc.title, options_count, options);
237 cupsFreeOptions (options_count, options);
238 doc_info.Remove (gr.Hdc);
239 if (tmpfile != null) {
240 try { File.Delete (tmpfile); }
246 internal override bool StartDoc (GraphicsPrinter gr, string doc_name, string output_file)
248 DOCINFO doc = (DOCINFO) doc_info[gr.Hdc];
249 doc.title = doc_name;
253 // Unfortunately, PrinterSettings and PageSettings couldn't be referencing each other,
254 // thus we need to pass them separately
255 internal override IntPtr CreateGraphicsContext (PrinterSettings settings, PageSettings default_page_settings)
257 IntPtr graphics = IntPtr.Zero;
259 if (!settings.PrintToFile) {
260 StringBuilder sb = new StringBuilder (1024);
261 int length = sb.Capacity;
262 cupsTempFile (sb, length);
263 name = sb.ToString ();
267 name = settings.PrintFileName;
269 GdipGetPostScriptGraphicsContext (name,
270 default_page_settings.PaperSize.Width / 100 * 72,
271 default_page_settings.PaperSize.Height / 100 * 72,
273 300, 300, ref graphics);
275 DOCINFO doc = new DOCINFO ();
276 doc.filename = name.ToString();
277 doc.settings = settings;
278 doc.default_page_settings = default_page_settings;
279 doc_info.Add (graphics, doc);
284 internal int GetCupsOptions (PrinterSettings printer_settings, PageSettings page_settings, out IntPtr options)
286 options = IntPtr.Zero;
288 PaperSize size = page_settings.PaperSize;
289 int width = size.Width * 72 / 100;
290 int height = size.Height * 72 / 100;
292 string options_string =
293 "copies=" + printer_settings.Copies + " " +
294 "Collate=" + printer_settings.Collate + " " +
295 "ColorModel=" + (page_settings.Color ? "Color" : "Black") + " " +
296 "PageSize=" + String.Format ("Custom.{0}x{1}", width, height);
298 return cupsParseOptions (options_string, 0, ref options);
303 internal override PrinterSettings.StringCollection InstalledPrinters {
306 IntPtr dests = IntPtr.Zero, ptr_printers, ptr_printer;
308 PrinterSettings.StringCollection col = new PrinterSettings.StringCollection (new string[] {});
310 if (cups_installed == false)
313 n_printers = cupsGetDests (ref dests);
315 ptr_printers = dests;
316 for (int i = 0; i < n_printers; i++) {
317 ptr_printer = (IntPtr) Marshal.ReadInt32 (ptr_printers);
318 str = Marshal.PtrToStringAnsi (ptr_printer);
319 Marshal.FreeHGlobal (ptr_printer);
320 ptr_printers = new IntPtr (ptr_printers.ToInt64 () + 20 /*size of CUPS_DEST*/);
323 Marshal.FreeHGlobal (dests);
328 internal override string DefaultPrinter {
332 if (cups_installed == false)
335 str = cupsGetDefault ();
336 return Marshal.PtrToStringAnsi (str);
342 private string GetPaperSizeName (IntPtr ppd, string name)
347 IntPtr ptr_opt, ptr_choice;
349 ptr_opt = ppdFindOption (ppd, "PageSize");
351 if (ptr_opt == IntPtr.Zero) {
355 option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
357 ptr_choice = option.choices;
358 for (int c = 0; c < option.num_choices; c++) {
359 choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
360 ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
361 if (name.Equals (choice.choice)) {
362 // Special case for custom size (cups returns NULL for it)
363 if (name == "Custom" && choice.text == null)
374 private PaperKind GetPaperKind (int width, int height)
376 if (width == 827 && height == 1169)
378 if (width == 583 && height == 827)
380 if (width == 717 && height == 1012)
382 if (width == 693 && height == 984)
383 return PaperKind.B5Envelope;
384 if (width == 638 && height == 902)
385 return PaperKind.C5Envelope;
386 if (width == 449 && height == 638)
387 return PaperKind.C6Envelope;
388 if (width == 1700 && height == 2200)
389 return PaperKind.CSheet;
390 if (width == 433 && height == 866)
391 return PaperKind.DLEnvelope;
392 if (width == 2200 && height == 3400)
393 return PaperKind.DSheet;
394 if (width == 3400 && height == 4400)
395 return PaperKind.ESheet;
396 if (width == 725 && height == 1050)
397 return PaperKind.Executive;
398 if (width == 850 && height == 1300)
399 return PaperKind.Folio;
400 if (width == 850 && height == 1200)
401 return PaperKind.GermanStandardFanfold;
402 if (width == 1700 && height == 1100)
403 return PaperKind.Ledger;
404 if (width == 850 && height == 1400)
405 return PaperKind.Legal;
406 if (width == 927 && height == 1500)
407 return PaperKind.LegalExtra;
408 if (width == 850 && height == 1100)
409 return PaperKind.Letter;
410 if (width == 927 && height == 1200)
411 return PaperKind.LetterExtra;
412 if (width == 850 && height == 1269)
413 return PaperKind.LetterPlus;
414 if (width == 387 && height == 750)
415 return PaperKind.MonarchEnvelope;
416 if (width == 387 && height == 887)
417 return PaperKind.Number9Envelope;
418 if (width == 413 && height == 950)
419 return PaperKind.Number10Envelope;
420 if (width == 450 && height == 1037)
421 return PaperKind.Number11Envelope;
422 if (width == 475 && height == 1100)
423 return PaperKind.Number12Envelope;
424 if (width == 500 && height == 1150)
425 return PaperKind.Number14Envelope;
426 if (width == 363 && height == 650)
427 return PaperKind.PersonalEnvelope;
428 if (width == 1000 && height == 1100)
429 return PaperKind.Standard10x11;
430 if (width == 1000 && height == 1400)
431 return PaperKind.Standard10x14;
432 if (width == 1100 && height == 1700)
433 return PaperKind.Standard11x17;
434 if (width == 1200 && height == 1100)
435 return PaperKind.Standard12x11;
436 if (width == 1500 && height == 1100)
437 return PaperKind.Standard15x11;
438 if (width == 900 && height == 1100)
439 return PaperKind.Standard9x11;
440 if (width == 550 && height == 850)
441 return PaperKind.Statement;
442 if (width == 1100 && height == 1700)
443 return PaperKind.Tabloid;
444 if (width == 1487 && height == 1100)
445 return PaperKind.USStandardFanfold;
447 return PaperKind.Custom;
450 internal override void GetPrintDialogInfo (string printer, ref string port, ref string type, ref string status, ref string comment)
452 int printers, state = -1;
453 CUPS_DESTS cups_dests;
454 CUPS_OPTIONS options;
456 IntPtr dests = IntPtr.Zero, ptr_printers, ptr_printer, ptr_options;
458 if (cups_installed == false)
461 printers = cupsGetDests (ref dests);
463 ptr_printers = dests;
464 for (int i = 0; i < printers; i++) {
465 cups_dests = (CUPS_DESTS) Marshal.PtrToStructure (ptr_printers, typeof (CUPS_DESTS));
466 str = Marshal.PtrToStringAnsi (cups_dests.name);
467 ptr_printers = new IntPtr (ptr_printers.ToInt64 () + 20 /*size of CUPS_DEST*/);
471 ptr_options = cups_dests.options;
472 for (int o = 0; o < cups_dests.num_options; o ++) {
473 options = (CUPS_OPTIONS) Marshal.PtrToStructure (ptr_options, typeof (CUPS_OPTIONS));
474 str = Marshal.PtrToStringAnsi (options.name);
475 if (str == "printer-state") {
476 state = Int32.Parse (Marshal.PtrToStringAnsi (options.val));
478 if (str == "printer-info")
479 comment = Marshal.PtrToStringAnsi (options.val);
481 ptr_options = new IntPtr (ptr_options.ToInt64 () + 8 /*size of CUPS_DEST*/);
486 Marshal.FreeHGlobal (dests);
505 [DllImport("libcups", CharSet=CharSet.Ansi)]
506 static extern int cupsGetDests (ref IntPtr dests);
508 [DllImport("libcups", CharSet=CharSet.Ansi)]
509 static extern IntPtr cupsTempFile (StringBuilder sb, int len);
511 [DllImport("libcups", CharSet=CharSet.Ansi)]
512 static extern IntPtr cupsGetDefault ();
514 [DllImport("libcups", CharSet=CharSet.Ansi)]
515 static extern int cupsPrintFile (string printer, string filename, string title, int num_options, IntPtr options);
517 [DllImport("libcups", CharSet=CharSet.Ansi)]
518 static extern IntPtr cupsGetPPD (string printer);
520 [DllImport("libcups", CharSet=CharSet.Ansi)]
521 static extern IntPtr ppdOpenFile (string filename);
523 [DllImport("libcups", CharSet=CharSet.Ansi)]
524 static extern IntPtr ppdFindOption (IntPtr ppd_file, string keyword);
526 [DllImport("libcups")]
527 static extern void ppdClose (IntPtr ppd);
529 [DllImport ("libcups", CharSet=CharSet.Ansi)]
530 static extern int cupsParseOptions (string arg, int number_of_options, ref IntPtr options);
532 [DllImport("libcups")]
533 static extern void cupsFreeOptions (int number_options, IntPtr options);
535 [DllImport("gdiplus.dll", CharSet=CharSet.Ansi)]
536 static extern int GdipGetPostScriptGraphicsContext (string filename, int with, int height, double dpix, double dpiy, ref IntPtr graphics);
538 [DllImport("gdiplus.dll")]
539 static extern int GdipGetPostScriptSavePage (IntPtr graphics);
543 public struct DOCINFO
545 public PrinterSettings settings;
546 public PageSettings default_page_settings;
548 public string filename;
551 public struct PPD_SIZE
554 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=42)]
564 public struct PPD_GROUP
566 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=40)]
568 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=42)]
570 public int num_options;
571 public IntPtr options;
572 public int num_subgroups;
573 public IntPtr subgrups;
576 public struct PPD_OPTION
578 public byte conflicted;
579 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=41)]
580 public string keyword;
581 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=41)]
582 public string defchoice;
583 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=81)]
588 public int num_choices;
589 public IntPtr choices;
592 public struct PPD_CHOICE
595 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=41)]
596 public string choice;
597 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=81)]
600 public IntPtr option;
603 public struct PPD_FILE
605 public int language_level;
606 public int color_device;
607 public int variable_sizes;
608 public int accurate_screens;
609 public int contone_only;
610 public int landscape;
611 public int model_number;
612 public int manual_copies;
613 public int throughput;
614 public int colorspace;
615 public IntPtr patches;
616 public int num_emulations;
617 public IntPtr emulations;
618 public IntPtr jcl_begin;
619 public IntPtr jcl_ps;
620 public IntPtr jcl_end;
621 public IntPtr lang_encoding;
622 public IntPtr lang_version;
623 public IntPtr modelname;
624 public IntPtr ttrasterizer;
625 public IntPtr manufacturer;
626 public IntPtr product;
627 public IntPtr nickname;
628 public IntPtr shortnickname;
629 public int num_groups;
630 public IntPtr groups;
631 public int num_sizes;
634 /* There is more data after this that we are not using*/
638 public struct CUPS_OPTIONS
644 public struct CUPS_DESTS
647 public IntPtr instance;
648 public int is_default;
649 public int num_options;
650 public IntPtr options;