From f75f6fa3c8f75333d4eaf6226774742a4fc99e39 Mon Sep 17 00:00:00 2001 From: Adrian Johnson Date: Sun, 14 Sep 2014 20:43:47 +0930 Subject: [PATCH 4/4] pdftocairo: fix a number of bugs in win32 printing - make origPageSizes work - make landscape pages work - make -noshrink option work - return actual page size in use back to main() so fit page transform works - hdc should be destroyed after cairo surface - improve option parsing and rename duplex values - Add third call to DocumentProperties as discussed in bug 79936 - fix error messages --- utils/pdftocairo-win32.cc | 181 ++++++++++++++++++++++++++-------------------- utils/pdftocairo-win32.h | 2 +- utils/pdftocairo.1 | 5 +- utils/pdftocairo.cc | 49 ++++++------- 4 files changed, 132 insertions(+), 105 deletions(-) diff --git a/utils/pdftocairo-win32.cc b/utils/pdftocairo-win32.cc index 7ad5615..dcabb69 100644 --- a/utils/pdftocairo-win32.cc +++ b/utils/pdftocairo-win32.cc @@ -7,13 +7,8 @@ #include "pdftocairo-win32.h" static HDC hdc; - -void win32GetFitToPageTransform(cairo_matrix_t *m) -{ - int logx = GetDeviceCaps(hdc, LOGPIXELSX); - int logy = GetDeviceCaps(hdc, LOGPIXELSY); - cairo_matrix_scale (m, logx / 72.0, logy / 72.0); -} +static DEVMODEA *devmode; +static char *printerName; struct Win32Option { @@ -39,69 +34,69 @@ static const Win32Option win32PaperSource[] = {NULL, 0} }; -static void parseSource(DEVMODEA *devmode, GooString *source) +static void parseSource(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()); + devmode->dmDefaultSource = option->value; + devmode->dmFields |= DM_DEFAULTSOURCE; return; } + option++; } - - devmode->dmDefaultSource = src; - devmode->dmFields |= DM_DEFAULTSOURCE; + fprintf(stderr, "Warning: Unknown paper source \"%s\"\n", source->getCString()); } static const Win32Option win32DuplexMode[] = { - {"simplex", DMDUP_SIMPLEX}, - {"horizontal", DMDUP_HORIZONTAL}, - {"vertical", DMDUP_VERTICAL}, + {"off", DMDUP_SIMPLEX}, + {"short", DMDUP_HORIZONTAL}, + {"long", DMDUP_VERTICAL}, {NULL, 0} }; -static void parseDuplex(DEVMODEA *devmode, GooString *mode) +static void parseDuplex(GooString *mode) { - int win32Duplex; const Win32Option *option = win32DuplexMode; while (option->name) { if (mode->cmp(option->name) == 0) { - win32Duplex = option->value; - break; + devmode->dmDuplex = option->value; + devmode->dmFields |= DM_DUPLEX; + return; } + option++; } - if (!option->name) { - fprintf(stderr, "Warning: Unknown duplex mode \"%s\"\n", mode->getCString()); - return; - } - - devmode->dmDuplex = win32Duplex; - devmode->dmFields |= DM_DUPLEX; + fprintf(stderr, "Warning: Unknown duplex mode \"%s\"\n", mode->getCString()); } -static void fillCommonPrinterOptions(DEVMODEA *devmode, double w, double h, GBool duplex) +static void fillCommonPrinterOptions(double w, double h, GBool duplex) { - 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, GBool duplex, GooString *printOpt) +static void fillPagePrinterOptions(double w, double h) +{ + w *= 254.0 / 72.0; // units are 0.1mm + h *= 254.0 / 72.0; + if (w > h) { + devmode->dmOrientation = DMORIENT_LANDSCAPE; + devmode->dmPaperWidth = h; + devmode->dmPaperLength = w; + } else { + devmode->dmOrientation = DMORIENT_PORTRAIT; + devmode->dmPaperWidth = w; + devmode->dmPaperLength = h; + } + devmode->dmPaperSize = 0; + devmode->dmFields |= DM_ORIENTATION | DM_PAPERWIDTH | DM_PAPERLENGTH; +} + + +static void fillPrinterOptions(GBool duplex, GooString *printOpt) { //printOpt format is: =,=,... const char *nextOpt = printOpt->getCString(); @@ -128,13 +123,12 @@ static void fillPrinterOptions(DEVMODEA *devmode, GBool duplex, GooString *print //here opt is "" and value is "" if (opt.cmp("source") == 0) { - parseSource(devmode, &value); + parseSource(&value); } else if (opt.cmp("duplex") == 0) { - if (duplex) { + if (duplex) fprintf(stderr, "Warning: duplex mode is specified both as standalone and printer options\n"); - } else { - parseDuplex(devmode, &value); - } + else + parseDuplex( &value); } else { fprintf(stderr, "Warning: unknown printer option \"%s\"\n", opt.getCString()); } @@ -147,65 +141,96 @@ cairo_surface_t *win32BeginDocument(GooString *inputFileName, GooString *outputF GooString *printOpt, GBool duplex) { - if (printer->getCString()[0] == 0) - { - DWORD szName = 0; - GetDefaultPrinterA(NULL, &szName); - char *devname = (char*)gmalloc(szName); - GetDefaultPrinterA(devname, &szName); - printer->Set(devname); - gfree(devname); + if (printer->getCString()[0] == 0) { + DWORD size = 0; + GetDefaultPrinterA(NULL, &size); + printerName = (char*)gmalloc(size); + GetDefaultPrinterA(printerName, &size); + } else { + printerName = gstrndup(printer->getCString(), printer->getLength()); } - 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); + LONG szProp = DocumentPropertiesA(NULL, NULL, printerName, NULL, NULL, 0); + if (szProp < 0) { + fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); exit(99); } - DEVMODEA *devmode = (DEVMODEA*)gmalloc(szProp); + 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); + if (DocumentPropertiesA(NULL, NULL, printerName, devmode, devmode, DM_OUT_BUFFER) < 0) { + fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); exit(99); } - fillCommonPrinterOptions(devmode, w, h, duplex); - fillPrinterOptions(devmode, duplex, printOpt); - hdc = CreateDCA(NULL, cPrinter, NULL, devmode); - gfree(devmode); - if (!hdc) - { - fprintf(stderr, "Error: Printer \"%s\" not found", cPrinter); + fillCommonPrinterOptions(w, h, duplex); + fillPrinterOptions(duplex, printOpt); + if (DocumentPropertiesA(NULL, NULL, printerName, devmode, devmode, DM_IN_BUFFER | DM_OUT_BUFFER) < 0) { + fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); + exit(99); + } + hdc = CreateDCA(NULL, printerName, NULL, devmode); + if (!hdc) { + fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); exit(99); } DOCINFOA docinfo; memset(&docinfo, 0, sizeof(docinfo)); docinfo.cbSize = sizeof(docinfo); - if (inputFileName->cmp("fd://0") == 0) { + if (inputFileName->cmp("fd://0") == 0) docinfo.lpszDocName = "pdftocairo "; - } else { + else docinfo.lpszDocName = inputFileName->getCString(); - } - if (outputFileName) { + if (outputFileName) docinfo.lpszOutput = outputFileName->getCString(); - } if (StartDocA(hdc, &docinfo) <=0) { - fprintf(stderr, "Error: StartDoc failed"); + fprintf(stderr, "Error: StartDoc failed\n"); exit(99); } return cairo_win32_printing_surface_create(hdc); } -void win32BeginPage(double w, double h) +void win32BeginPage(double *w, double *h, GBool useFullPage) { + fillPagePrinterOptions(*w, *h); + if (DocumentPropertiesA(NULL, NULL, printerName, devmode, devmode, DM_IN_BUFFER | DM_OUT_BUFFER) < 0) { + fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); + exit(99); + } + ResetDCA(hdc, devmode); + + // Get actual paper size or if useFullPage is false the printable area. + // Transform the hdc scale to points to be consistent with other cairo backends + int x_dpi = GetDeviceCaps (hdc, LOGPIXELSX); + int y_dpi = GetDeviceCaps (hdc, LOGPIXELSY); + int x_off = GetDeviceCaps (hdc, PHYSICALOFFSETX); + int y_off = GetDeviceCaps (hdc, PHYSICALOFFSETY); + if (useFullPage) { + *w = GetDeviceCaps (hdc, PHYSICALWIDTH)*72.0/x_dpi; + *h = GetDeviceCaps (hdc, PHYSICALHEIGHT)*72.0/y_dpi; + } else { + *w = GetDeviceCaps (hdc, HORZRES)*72.0/x_dpi; + *h = GetDeviceCaps (hdc, VERTRES)*72.0/y_dpi; + } + XFORM xform; + xform.eM11 = x_dpi/72.0; + xform.eM12 = 0; + xform.eM21 = 0; + xform.eM22 = y_dpi/72.0; + if (useFullPage) { + xform.eDx = -x_off; + xform.eDy = -y_off; + } else { + xform.eDx = 0; + xform.eDy = 0; + } + SetGraphicsMode (hdc, GM_ADVANCED); + SetWorldTransform (hdc, &xform); + StartPage(hdc); } @@ -218,6 +243,8 @@ void win32EndDocument() { EndDoc(hdc); DeleteDC(hdc); + gfree(devmode); + gfree(printerName); } #endif // CAIRO_HAS_WIN32_SURFACE diff --git a/utils/pdftocairo-win32.h b/utils/pdftocairo-win32.h index 8ecd84c..07e200c 100644 --- a/utils/pdftocairo-win32.h +++ b/utils/pdftocairo-win32.h @@ -15,7 +15,7 @@ cairo_surface_t *win32BeginDocument(GooString *inputFileName, GooString *outputF GooString *printer, GooString *printOpt, GBool duplex); -void win32BeginPage(double w, double h); +void win32BeginPage(double *w, double *h, GBool useFullPage); void win32EndPage(GooString *imageFileName); void win32EndDocument(); diff --git a/utils/pdftocairo.1 b/utils/pdftocairo.1 index 8725722..00d93c4 100644 --- a/utils/pdftocairo.1 +++ b/utils/pdftocairo.1 @@ -268,7 +268,10 @@ Selects the source paper tray to be used (bin). The possible values are "upper", "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. +Sets the duplex mode of the printer. The possible values are "off", "short" or "long", +indicating no duplexing, short-edge binding, or long-edge binding, respectively. +General option \-duplex is a synonym of "duplex=long". If both options are specified, +\-printopt 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 aa76226..b0a689e 100644 --- a/utils/pdftocairo.cc +++ b/utils/pdftocairo.cc @@ -79,7 +79,7 @@ static GBool jpeg = gFalse; static GBool ps = gFalse; static GBool eps = gFalse; static GBool pdf = gFalse; -static GBool print = gFalse; +static GBool printToWin32 = gFalse; static GBool svg = gFalse; static GBool tiff = gFalse; @@ -159,7 +159,7 @@ static const ArgDesc argDesc[] = { "generate a Scalable Vector Graphics (SVG) file"}, #endif #ifdef CAIRO_HAS_WIN32_SURFACE - {"-print", argFlag, &print, 0, + {"-print", argFlag, &printToWin32, 0, "print to a Windows printer"}, {"-printer", argGooString, &printer, 0, "printer name or use default if this option is not specified"}, @@ -483,10 +483,6 @@ static void getFitToPageTransform(double page_w, double page_h, // shrink to fit cairo_matrix_scale (m, scale, scale); } -#ifdef CAIRO_HAS_WIN32_SURFACE - if (print) - win32GetFitToPageTransform(m); -#endif } static cairo_status_t writeStream(void *closure, const unsigned char *data, unsigned int length) @@ -502,7 +498,9 @@ static cairo_status_t writeStream(void *closure, const unsigned char *data, unsi static void beginDocument(GooString *inputFileName, GooString *outputFileName, double w, double h) { if (printing) { - if (!print) { + if (printToWin32) { + output_file = NULL; + } else { if (outputFileName->cmp("fd://0") == 0) output_file = stdout; else @@ -540,41 +538,41 @@ static void beginDocument(GooString *inputFileName, GooString *outputFileName, d #endif } #ifdef CAIRO_HAS_WIN32_SURFACE - if (print) + if (printToWin32) surface = win32BeginDocument(inputFileName, outputFileName, w, h, &printer, &printOpt, duplex); #endif } } -static void beginPage(double w, double h) +static void beginPage(double *w, double *h) { if (printing) { if (ps || eps) { #if CAIRO_HAS_PS_SURFACE - if (w > h) { + if (*w > *h) { cairo_ps_surface_dsc_comment (surface, "%%PageOrientation: Landscape"); - cairo_ps_surface_set_size (surface, h, w); + cairo_ps_surface_set_size (surface, *h, *w); } else { cairo_ps_surface_dsc_comment (surface, "%%PageOrientation: Portrait"); - cairo_ps_surface_set_size (surface, w, h); + cairo_ps_surface_set_size (surface, *w, *h); } #endif } #if CAIRO_HAS_PDF_SURFACE if (pdf) - cairo_pdf_surface_set_size (surface, w, h); + cairo_pdf_surface_set_size (surface, *w, *h); #endif #ifdef CAIRO_HAS_WIN32_SURFACE - if (print) - win32BeginPage(w, h); + if (printToWin32) + win32BeginPage(w, h, noShrink); // w,h will be changed to actual size used #endif cairo_surface_set_fallback_resolution (surface, x_resolution, y_resolution); } else { - surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ceil(w), ceil(h)); + surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ceil(*w), ceil(*h)); } } @@ -642,7 +640,7 @@ static void endPage(GooString *imageFileName) cairo_surface_show_page(surface); #ifdef CAIRO_HAS_WIN32_SURFACE - if (print) + if (printToWin32) win32EndPage(imageFileName); #endif @@ -662,15 +660,15 @@ static void endDocument() cairo_status_t status; if (printing) { -#ifdef CAIRO_HAS_WIN32_SURFACE - if (print) - 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); +#ifdef CAIRO_HAS_WIN32_SURFACE + if (printToWin32) + win32EndDocument(); +#endif if (output_file) fclose(output_file); } @@ -737,7 +735,7 @@ static GooString *getOutputFileName(GooString *fileName, GooString *outputName) if (outputName) { if (outputName->cmp("-") == 0) { - if (print || (!printing && !singleFile)) { + if (printToWin32 || (!printing && !singleFile)) { fprintf(stderr, "Error: stdout may only be used with the ps, eps, pdf, svg output options or if -singlefile is used.\n"); exit(99); } @@ -746,9 +744,8 @@ static GooString *getOutputFileName(GooString *fileName, GooString *outputName) return new GooString(outputName); } - if (print) { + if (printToWin32) return NULL; // No output file means print to printer - } if (fileName->cmp("fd://0") == 0) { fprintf(stderr, "Error: an output filename or '-' must be supplied when the PDF file is stdin.\n"); @@ -853,7 +850,7 @@ int main(int argc, char *argv[]) { (ps ? 1 : 0) + (eps ? 1 : 0) + (pdf ? 1 : 0) + - (print ? 1 : 0) + + (printToWin32 ? 1 : 0) + (svg ? 1 : 0); if (num_outputs == 0) { fprintf(stderr, "Error: one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -print, -svg) must be used.\n"); @@ -1098,7 +1095,7 @@ int main(int argc, char *argv[]) { if (pg == firstPage) beginDocument(fileName, outputFileName, output_w, output_h); - beginPage(output_w, output_h); + beginPage(&output_w, &output_h); renderPage(doc, cairoOut, pg, pg_w, pg_h, output_w, output_h); endPage(imageFileName); } -- 2.1.0