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);
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);
}
PPD_FILE ppd;
PPD_SIZE size;
PaperSize ps;
- PaperKind kind = PaperKind.Custom;
settings.PaperSizes.Clear ();
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);
}
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;
}
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;
}
}
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);
[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);
public struct DOCINFO
{
public PrinterSettings settings;
+ public PageSettings default_page_settings;
public string title;
public string filename;
}
/* 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;
+ }
}
}