diff --git a/configure.ac b/configure.ac index 94eeb5b..0cbf8c0 100644 --- a/configure.ac +++ b/configure.ac @@ -48,7 +48,7 @@ case "$host_os" in ;; mingw*) os_win32=yes - win32_libs="-lgdi32" + win32_libs="-lgdi32 -lwinspool" create_shared_lib="-no-undefined" auto_import_flags="-Wl,--enable-auto-import" diff --git a/utils/pdftocairo-win32.cc b/utils/pdftocairo-win32.cc new file mode 100644 index 0000000..37d95a2 --- /dev/null +++ b/utils/pdftocairo-win32.cc @@ -0,0 +1,220 @@ +#include + +static void win32GetFitToPageTransform(cairo_matrix_t *m) +{ + if (!print) + return; + + HDC hdc = cairo_win32_surface_get_dc(surface); + int logx = GetDeviceCaps(hdc, LOGPIXELSX); + int logy = GetDeviceCaps(hdc, LOGPIXELSY); + cairo_matrix_scale (m, logx / 72.0, logy / 72.0); +} + +struct Win32Option +{ + const char *name; + int value; +}; + +static const Win32Option win32PaperSource[] = +{ + {"upper", DMBIN_UPPER}, + {"onlyone", DMBIN_ONLYONE}, + {"lower", DMBIN_LOWER}, + {"middle", DMBIN_MIDDLE}, + {"manual", DMBIN_MANUAL}, + {"envelope", DMBIN_ENVELOPE}, + {"envmanual", DMBIN_ENVMANUAL}, + {"auto", DMBIN_AUTO}, + {"tractor", DMBIN_TRACTOR}, + {"smallfmt", DMBIN_SMALLFMT}, + {"largefmt", DMBIN_LARGEFMT}, + {"largecapacity", DMBIN_LARGECAPACITY}, + {"formsource", DMBIN_FORMSOURCE}, + {NULL, 0} +}; + +static void parseSource(DEVMODEA *devmode, GooString *source) +{ + int src; + const Win32Option *option = win32PaperSource; + while (option->name) { + if (source->cmp(option->name) == 0) { + src = option->value; + break; + } + } + if (!option->name) { + if (isInt(source->getCString())) { + src = atoi(source->getCString()); + } else { + fprintf(stderr, "Warning: Unknown paper source \"%s\"\n", source->getCString()); + return; + } + } + + devmode->dmDefaultSource = src; + devmode->dmFields |= DM_DEFAULTSOURCE; +} + +static const Win32Option win32DuplexMode[] = +{ + {"simplex", DMDUP_SIMPLEX}, + {"horizontal", DMDUP_HORIZONTAL}, + {"vertical", DMDUP_VERTICAL}, + {NULL, 0} +}; + +static void parseDuplex(DEVMODEA *devmode, GooString *mode) +{ + if (duplex) + fprintf(stderr, "Warning: duplex mode is specified both as standalone and printer options\n"); + + int win32Duplex; + const Win32Option *option = win32DuplexMode; + while (option->name) { + if (mode->cmp(option->name) == 0) { + win32Duplex = option->value; + break; + } + } + if (!option->name) { + fprintf(stderr, "Warning: Unknown duplex mode \"%s\"\n", mode->getCString()); + return; + } + + devmode->dmDuplex = win32Duplex; + devmode->dmFields |= DM_DUPLEX; +} + +static void fillCommonPrinterOptions(DEVMODEA *devmode, double w, double h) +{ + devmode->dmPaperWidth = w * 254.0 / 72.0; + devmode->dmPaperLength = h * 254.0 / 72.0; + printf("PAPER %d, %d\n", devmode->dmPaperWidth, devmode->dmPaperLength); + devmode->dmFields |= DM_PAPERWIDTH | DM_PAPERLENGTH; + if (duplex) { + devmode->dmDuplex = DMDUP_HORIZONTAL; + devmode->dmFields |= DM_DUPLEX; + } +} + +static void fillPrinterOptions(DEVMODEA *devmode) +{ + //printOpt format is: =,=,... + const char *nextOpt = printOpt.getCString(); + while (nextOpt && *nextOpt) + { + const char *comma = strchr(nextOpt, ','); + GooString opt; + if (comma) { + opt.Set(nextOpt, comma - nextOpt); + nextOpt = comma + 1; + } else { + opt.Set(nextOpt); + nextOpt = NULL; + } + //here opt is "= " + const char *equal = strchr(opt.getCString(), '='); + if (!equal) { + fprintf(stderr, "Warning: unknown printer option \"%s\"\n", opt.getCString()); + continue; + } + int iequal = equal - opt.getCString(); + GooString value(&opt, iequal + 1, opt.getLength() - iequal - 1); + opt.del(iequal, opt.getLength() - iequal); + //here opt is "" and value is "" + + if (opt.cmp("source") == 0) { + parseSource(devmode, &value); + } else if (opt.cmp("duplex") == 0) { + parseDuplex(devmode, &value); + } else { + fprintf(stderr, "Warning: unknown printer option \"%s\"\n", opt.getCString()); + } + } +} + +static void win32BeginDocument(GooString *outputFileName, double w, double h) +{ + if (!print) + return; + + if (printer.getCString()[0] == 0) + { + DWORD szName = 0; + GetDefaultPrinterA(NULL, &szName); + char *devname = (char*)gmalloc(szName); + GetDefaultPrinterA(devname, &szName); + printer.Set(devname); + gfree(devname); + } + char *cPrinter = printer.getCString(); + + //Query the size of the DEVMODE struct + LONG szProp = DocumentPropertiesA(NULL, NULL, cPrinter, NULL, NULL, 0); + if (szProp < 0) + { + fprintf(stderr, "Error: Printer \"%s\" not found", cPrinter); + exit(99); + } + DEVMODEA *devmode = (DEVMODEA*)gmalloc(szProp); + memset(devmode, 0, szProp); + devmode->dmSize = sizeof(DEVMODEA); + devmode->dmSpecVersion = DM_SPECVERSION; + //Load the current default configuration for the printer into devmode + if (DocumentPropertiesA(NULL, NULL, cPrinter, devmode, NULL, DM_OUT_BUFFER) < 0) + { + fprintf(stderr, "Error: Printer \"%s\" not found", cPrinter); + exit(99); + } + fillCommonPrinterOptions(devmode, w, h); + fillPrinterOptions(devmode); + HDC hdc = CreateDCA(NULL, cPrinter, NULL, devmode); + gfree(devmode); + if (!hdc) + { + fprintf(stderr, "Error: Printer \"%s\" not found", cPrinter); + exit(99); + } + + DOCINFOA docinfo; + memset(&docinfo, 0, sizeof(docinfo)); + docinfo.cbSize = sizeof(docinfo); + if (outputFileName->cmp("fd://0") == 0) + docinfo.lpszDocName = outputFileName->getCString(); + docinfo.lpszDocName = "pdftocairo "; + else + docinfo.lpszDocName = outputFileName->getCString(); + if (StartDocA(hdc, &docinfo) <=0) { + fprintf(stderr, "Error: StartDoc failed"); + exit(99); + } + + surface = cairo_win32_printing_surface_create(hdc); +} + +static void win32BeginPage(double w, double h) +{ + if (!print) + return; + StartPage(cairo_win32_surface_get_dc(surface)); +} + +static void win32EndPage(GooString *imageFileName) +{ + if (!print) + return; + EndPage(cairo_win32_surface_get_dc(surface)); +} + +static void win32EndDocument() +{ + if (!print) + return; + + HDC hdc = cairo_win32_surface_get_dc(surface); + EndDoc(hdc); + DeleteDC(hdc); +} diff --git a/utils/pdftocairo.1 b/utils/pdftocairo.1 index c903ffa..7747409 100644 --- a/utils/pdftocairo.1 +++ b/utils/pdftocairo.1 @@ -48,7 +48,7 @@ is not used, the output filename will be derived from the .IR PDF-file filename. .PP -Not all options are valid with all output formats. One (and only one) of the output format options (\-png, \-jpeg, \-tiff, \-pdf, \-ps, \-eps, or \-svg) must be used. +Not all options are valid with all output formats. One (and only one) of the output format options (\-png, \-jpeg, \-tiff, \-pdf, \-print, \-ps, \-eps, or \-svg) must be used. .PP The resolution options (\-r, \-rx, \-ry) set the resolution of the image output formats. The image dimensions will depend on the PDF page @@ -80,6 +80,9 @@ Generates a TIFF file(s) .BI \-pdf Generates a PDF file .TP +.BI \-print +(Windows only) Prints to a system printer. See also \-printer and \-printeropt. +.TP .BI \-ps Generate a PS file .TP @@ -205,6 +208,14 @@ lower-left corner of the paper instead (PS,PDF,SVG only). Adds the %%IncludeFeature: *Duplex DuplexNoTumble DSC comment to the PostScript file (PS only). This tells the print manager to enable duplexing. .TP +.BI \-printer " printer-name" +(Windows only). When used with \-print, specifies the name of the printer to be used, instead of the system default. +.TP +.BI \-printopt " printer-options" +(Windows only). When used with \-print, takes a list of options to be used to configure the printer. See +.B WINDOWS PRINTER OPTIONS +for the available options. +.TP .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. @@ -244,6 +255,16 @@ Error related to ICC profile. .TP 99 Other error. +.SH WINDOWS PRINTER OPTIONS +In Windows, you can use the \-print option to print directly to a system printer. Additionally, you can use the \-printopt +option to configure the printer. It takes a string of the form "=[,=]". Currently the available options are: +.TP +.BI source +Selects the source paper tray to be used (bin). The possible values are "upper", "onlyone", "lower", "middle", "manual", "envelope", +"envmanual", "auto", "tractor", "smallfmt", "largefmt", "largecapacity", "formsource", or a numeric value to choose a driver specific source. +.TP +.BI duplex +Sets the duplex mode of the printer. It can be "simplex", "horizontal" or "vertical". General option \-duplex is a synonym of "duplex=horizontal". If both are specified, the \-printopt one has priority. .SH AUTHOR The pdftocairo software and documentation are copyright 1996-2004 Glyph & Cog, LLC and copyright 2005-2011 The Poppler Developers. diff --git a/utils/pdftocairo.cc b/utils/pdftocairo.cc index b3f57b6..79d71d9 100644 --- a/utils/pdftocairo.cc +++ b/utils/pdftocairo.cc @@ -77,6 +77,7 @@ static GBool jpeg = gFalse; static GBool ps = gFalse; static GBool eps = gFalse; static GBool pdf = gFalse; +static GBool print = gFalse; static GBool svg = gFalse; static GBool tiff = gFalse; @@ -121,6 +122,11 @@ static GBool quiet = gFalse; static GBool printVersion = gFalse; static GBool printHelp = gFalse; +#ifdef CAIRO_HAS_WIN32_SURFACE +static GooString printer; +static GooString printOpt; +#endif + static const ArgDesc argDesc[] = { #if ENABLE_LIBPNG {"-png", argFlag, &png, 0, @@ -150,6 +156,14 @@ static const ArgDesc argDesc[] = { {"-svg", argFlag, &svg, 0, "generate a Scalable Vector Graphics (SVG) file"}, #endif +#ifdef CAIRO_HAS_WIN32_SURFACE + {"-print", argFlag, &print, 0, + "print to a Windows printer"}, + {"-printer", argGooString, &printer, 0, + "printer name or use default if this option is not specified"}, + {"-printopt", argGooString, &printOpt, 0, + "printer options, with format =[,=]*"}, +#endif {"-f", argInt, &firstPage, 0, "first page to print"}, @@ -253,6 +267,9 @@ static int icc_data_size; static cmsHPROFILE profile; #endif +#ifdef CAIRO_HAS_WIN32_SURFACE +#include "pdftocairo-win32.cc" +#endif void writePageImage(GooString *filename) { @@ -468,6 +485,9 @@ static void getFitToPageTransform(double page_w, double page_h, // shrink to fit cairo_matrix_scale (m, scale, scale); } +#ifdef CAIRO_HAS_WIN32_SURFACE + win32GetFitToPageTransform(m); +#endif } static cairo_status_t writeStream(void *closure, const unsigned char *data, unsigned int length) @@ -483,14 +503,16 @@ static cairo_status_t writeStream(void *closure, const unsigned char *data, unsi static void beginDocument(GooString *outputFileName, double w, double h) { if (printing) { - if (outputFileName->cmp("fd://0") == 0) - output_file = stdout; - else - { - output_file = fopen(outputFileName->getCString(), "wb"); - if (!output_file) { - fprintf(stderr, "Error opening output file %s\n", outputFileName->getCString()); - exit(2); + if (!print) { + if (outputFileName->cmp("fd://0") == 0) + output_file = stdout; + else + { + output_file = fopen(outputFileName->getCString(), "wb"); + if (!output_file) { + fprintf(stderr, "Error opening output file %s\n", outputFileName->getCString()); + exit(2); + } } } @@ -518,6 +540,9 @@ static void beginDocument(GooString *outputFileName, double w, double h) cairo_svg_surface_restrict_to_version (surface, CAIRO_SVG_VERSION_1_2); #endif } +#ifdef CAIRO_HAS_WIN32_SURFACE + win32BeginDocument(outputFileName, w, h); +#endif } } @@ -540,6 +565,9 @@ static void beginPage(double w, double h) if (pdf) cairo_pdf_surface_set_size (surface, w, h); #endif +#ifdef CAIRO_HAS_WIN32_SURFACE + win32BeginPage(w, h); +#endif cairo_surface_set_fallback_resolution (surface, x_resolution, y_resolution); @@ -610,6 +638,9 @@ static void endPage(GooString *imageFileName) if (printing) { cairo_surface_show_page(surface); +#ifdef CAIRO_HAS_WIN32_SURFACE + win32EndPage(imageFileName); +#endif } else { writePageImage(imageFileName); cairo_surface_finish(surface); @@ -626,12 +657,16 @@ static void endDocument() cairo_status_t status; if (printing) { +#ifdef CAIRO_HAS_WIN32_SURFACE + win32EndDocument(); +#endif cairo_surface_finish(surface); status = cairo_surface_status(surface); if (status) error(errInternal, -1, "cairo error: {0:s}\n", cairo_status_to_string(status)); cairo_surface_destroy(surface); - fclose(output_file); + if (output_file) + fclose(output_file); } } @@ -705,6 +740,9 @@ static GooString *getOutputFileName(GooString *fileName, GooString *outputName) return new GooString(outputName); } + if (print) + return fileName; //it will be used as the job name + if (fileName->cmp("fd://0") == 0) { fprintf(stderr, "Error: an output filename or '-' must be supplied when the PDF file is stdin.\n"); exit(99); @@ -808,13 +846,14 @@ int main(int argc, char *argv[]) { (ps ? 1 : 0) + (eps ? 1 : 0) + (pdf ? 1 : 0) + + (print ? 1 : 0) + (svg ? 1 : 0); if (num_outputs == 0) { - fprintf(stderr, "Error: one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -svg) must be used.\n"); + fprintf(stderr, "Error: one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -print, -svg) must be used.\n"); exit(99); } if (num_outputs > 1) { - fprintf(stderr, "Error: use only one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -svg).\n"); + fprintf(stderr, "Error: use only one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -print, -svg).\n"); exit(99); } if (png || jpeg || tiff)