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;
43 internal PrintingServicesUnix ()
45 CheckCupsInstalled ();
48 private void CheckCupsInstalled ()
53 catch (DllNotFoundException) {
54 Console.WriteLine("libcups not found. To have printing support, you need cups installed");
55 cups_installed = false;
59 cups_installed = true;
63 internal override void LoadPrinterSettings (string printer, PrinterSettings settings)
65 IntPtr ptr, ppd_handle, ptr_opt, ptr_choice;
71 if (cups_installed == false || (printer == null) || (printer == String.Empty))
74 ptr = cupsGetPPD (printer);
75 ppd_filename = Marshal.PtrToStringAnsi (ptr);
76 ppd_handle = ppdOpenFile (ppd_filename);
77 //Console.WriteLine ("File: {0}", ppd_filename);
79 ppd = (PPD_FILE) Marshal.PtrToStructure (ppd_handle, typeof (PPD_FILE));
80 settings.landscape_angle = ppd.landscape;
81 settings.supports_color = (ppd.color_device == 0) ? false : true;
83 // Default paper source
84 ptr_opt = ppdFindOption (ppd_handle, "InputSlot");
85 if (ptr_opt != IntPtr.Zero) {
86 option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
87 ptr_choice = option.choices;
88 for (int c = 0; c < option.num_choices; c++) {
89 choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
90 ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
91 if (choice.choice == option.defchoice) {
92 foreach (PaperSource paper_source in settings.PaperSources) {
93 if (paper_source.SourceName == choice.text) {
94 settings.DefaultPageSettings.PaperSource = paper_source;
103 // Default paper size
104 ptr_opt = ppdFindOption (ppd_handle, "PageSize");
105 if (ptr_opt != IntPtr.Zero) {
106 option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
107 ptr_choice = option.choices;
108 for (int c = 0; c < option.num_choices; c++) {
109 choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
110 ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
111 if (choice.choice == option.defchoice) {
112 foreach (PaperSize paper_size in settings.PaperSizes) {
113 if (paper_size.PaperName == choice.text) {
114 settings.DefaultPageSettings.PaperSize = paper_size;
123 ppdClose (ppd_handle);
126 internal override void LoadPrinterResolutions (string printer, PrinterSettings settings)
128 settings.PrinterResolutions.Clear ();
129 LoadDefaultResolutions (settings.PrinterResolutions);
132 internal override void LoadPrinterPaperSizes (string printer, PrinterSettings settings)
134 IntPtr ptr, ppd_handle;
135 string ppd_filename, real_name;
139 PaperKind kind = PaperKind.Custom;
141 settings.PaperSizes.Clear ();
143 ptr = cupsGetPPD (printer);
144 ppd_filename = Marshal.PtrToStringAnsi (ptr);
145 ppd_handle = ppdOpenFile (ppd_filename);
147 ppd = (PPD_FILE) Marshal.PtrToStructure (ppd_handle, typeof (PPD_FILE));
150 for (int i = 0; i < ppd.num_sizes; i++) {
151 size = (PPD_SIZE) Marshal.PtrToStructure (ptr, typeof (PPD_SIZE));
152 real_name = GetPaperSizeName (ppd_handle, size.name);
153 ptr = new IntPtr (ptr.ToInt64 () + Marshal.SizeOf (size));
154 w = size.width * 100 / 72;
155 h = size.length * 100 / 72;
156 ps = new PaperSize (real_name, (int) w, (int) h);
157 // TODO: Convert from name to paper kind enum
159 settings.PaperSizes.Add (ps);
162 ppdClose (ppd_handle);
165 internal override void LoadPrinterPaperSources (string printer, PrinterSettings settings)
167 IntPtr ptr, ppd_handle, ptr_opt, ptr_choice;
172 if (cups_installed == false || (printer == null) || (printer == String.Empty))
175 ptr = cupsGetPPD (printer);
176 ppd_filename = Marshal.PtrToStringAnsi (ptr);
177 ppd_handle = ppdOpenFile (ppd_filename);
179 ptr_opt = ppdFindOption (ppd_handle, "InputSlot");
181 if (ptr_opt == IntPtr.Zero) {
182 ppdClose (ppd_handle);
186 option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
187 //Console.WriteLine (" OPTION key:{0} def:{1} text: {2}", option.keyword, option.defchoice, option.text);
189 ptr_choice = option.choices;
190 for (int c = 0; c < option.num_choices; c++) {
191 choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
192 ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
193 //Console.WriteLine (" choice:{0} - text: {1}", choice.choice, choice.text);
194 settings.PaperSources.Add (new PaperSource (choice.text, PaperSourceKind.Custom));
196 ppdClose (ppd_handle);
199 internal override bool StartPage (GraphicsPrinter gr)
204 internal override bool EndPage (GraphicsPrinter gr)
206 GdipGetPostScriptSavePage (gr.Hdc);
212 internal override bool EndDoc (GraphicsPrinter gr)
214 DOCINFO doc = (DOCINFO) doc_info[gr.Hdc];
216 gr.Graphics.Dispose (); // Dispose object to force surface finish
217 cupsPrintFile (doc.settings.PrinterName, doc.filename, doc.title, 0, IntPtr.Zero);
218 doc_info.Remove (gr.Hdc);
219 if (tmpfile != null) {
220 try { File.Delete (tmpfile); }
226 internal override bool StartDoc (GraphicsPrinter gr, string doc_name, string output_file)
228 DOCINFO doc = (DOCINFO) doc_info[gr.Hdc];
229 doc.title = doc_name;
233 internal override IntPtr CreateGraphicsContext (PrinterSettings settings)
235 IntPtr graphics = IntPtr.Zero;
237 if (!settings.PrintToFile) {
238 StringBuilder sb = new StringBuilder (1024);
239 int length = sb.Capacity;
240 cupsTempFile (sb, length);
241 name = sb.ToString ();
245 name = settings.PrintFileName;
247 GdipGetPostScriptGraphicsContext (name,
248 settings.DefaultPageSettings.PaperSize.Width / 100 * 72,
249 settings.DefaultPageSettings.PaperSize.Height / 100 * 72,
251 300, 300, ref graphics);
253 DOCINFO doc = new DOCINFO ();
254 doc.filename = name.ToString();
255 doc.settings = settings;
256 doc_info.Add (graphics, doc);
263 internal override PrinterSettings.StringCollection InstalledPrinters {
266 IntPtr dests = IntPtr.Zero, ptr_printers, ptr_printer;
268 PrinterSettings.StringCollection col = new PrinterSettings.StringCollection (new string[] {});
270 if (cups_installed == false)
273 n_printers = cupsGetDests (ref dests);
275 ptr_printers = dests;
276 for (int i = 0; i < n_printers; i++) {
277 ptr_printer = (IntPtr) Marshal.ReadInt32 (ptr_printers);
278 str = Marshal.PtrToStringAnsi (ptr_printer);
279 Marshal.FreeHGlobal (ptr_printer);
280 ptr_printers = new IntPtr (ptr_printers.ToInt64 () + 20 /*size of CUPS_DEST*/);
283 Marshal.FreeHGlobal (dests);
288 internal override string DefaultPrinter {
292 if (cups_installed == false)
295 str = cupsGetDefault ();
296 return Marshal.PtrToStringAnsi (str);
302 private string GetPaperSizeName (IntPtr ppd, string name)
307 IntPtr ptr_opt, ptr_choice;
309 ptr_opt = ppdFindOption (ppd, "PageSize");
311 if (ptr_opt == IntPtr.Zero) {
315 option = (PPD_OPTION) Marshal.PtrToStructure (ptr_opt, typeof (PPD_OPTION));
317 ptr_choice = option.choices;
318 for (int c = 0; c < option.num_choices; c++) {
319 choice = (PPD_CHOICE) Marshal.PtrToStructure (ptr_choice, typeof (PPD_CHOICE));
320 ptr_choice = new IntPtr (ptr_choice.ToInt64 () + Marshal.SizeOf (choice));
321 if (name.Equals (choice.choice)) {
330 internal override void GetPrintDialogInfo (string printer, ref string port, ref string type, ref string status, ref string comment)
339 [DllImport("libcups", CharSet=CharSet.Ansi)]
340 static extern int cupsGetDests (ref IntPtr dests);
342 [DllImport("libcups", CharSet=CharSet.Ansi)]
343 static extern IntPtr cupsTempFile (StringBuilder sb, int len);
345 [DllImport("libcups", CharSet=CharSet.Ansi)]
346 static extern IntPtr cupsGetDefault ();
348 [DllImport("libcups", CharSet=CharSet.Ansi)]
349 static extern int cupsPrintFile (string printer, string filename, string title, int num_options, IntPtr options);
351 [DllImport("libcups", CharSet=CharSet.Ansi)]
352 static extern IntPtr cupsGetPPD (string printer);
354 [DllImport("libcups", CharSet=CharSet.Ansi)]
355 static extern IntPtr ppdOpenFile (string filename);
357 [DllImport("libcups", CharSet=CharSet.Ansi)]
358 static extern IntPtr ppdFindOption (IntPtr ppd_file, string keyword);
360 [DllImport("libcups")]
361 static extern void ppdClose (IntPtr ppd);
363 [DllImport("gdiplus.dll", CharSet=CharSet.Ansi)]
364 static extern int GdipGetPostScriptGraphicsContext (string filename, int with, int height, double dpix, double dpiy, ref IntPtr graphics);
366 [DllImport("gdiplus.dll")]
367 static extern int GdipGetPostScriptSavePage (IntPtr graphics);
371 public struct DOCINFO
373 public PrinterSettings settings;
375 public string filename;
378 public struct PPD_SIZE
381 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=42)]
391 public struct PPD_GROUP
393 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=40)]
395 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=42)]
397 public int num_options;
398 public IntPtr options;
399 public int num_subgroups;
400 public IntPtr subgrups;
403 public struct PPD_OPTION
405 public byte conflicted;
406 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=41)]
407 public string keyword;
408 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=41)]
409 public string defchoice;
410 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=81)]
415 public int num_choices;
416 public IntPtr choices;
419 public struct PPD_CHOICE
422 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=41)]
423 public string choice;
424 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=81)]
427 public IntPtr option;
430 public struct PPD_FILE
432 public int language_level;
433 public int color_device;
434 public int variable_sizes;
435 public int accurate_screens;
436 public int contone_only;
437 public int landscape;
438 public int model_number;
439 public int manual_copies;
440 public int throughput;
441 public int colorspace;
442 public IntPtr patches;
443 public int num_emulations;
444 public IntPtr emulations;
445 public IntPtr jcl_begin;
446 public IntPtr jcl_ps;
447 public IntPtr jcl_end;
448 public IntPtr lang_encoding;
449 public IntPtr lang_version;
450 public IntPtr modelname;
451 public IntPtr ttrasterizer;
452 public IntPtr manufacturer;
453 public IntPtr product;
454 public IntPtr nickname;
455 public IntPtr shortnickname;
456 public int num_groups;
457 public IntPtr groups;
458 public int num_sizes;
461 /* There is more data after this that we are not using*/