From 578e9e8466197bf6324973529d13dd85566bb56a Mon Sep 17 00:00:00 2001 From: Adrian Johnson Date: Sun, 5 Oct 2014 21:58:37 +1030 Subject: [PATCH 6/6] pdftocairo: add -printdlg output option for win32 when set the win32 print dialog is displayed before printing --- configure.ac | 2 +- utils/pdftocairo-win32.cc | 303 ++++++++++++++++++++++++++++++++++++++++++++-- utils/pdftocairo-win32.h | 14 +-- utils/pdftocairo.1 | 14 ++- utils/pdftocairo.cc | 31 ++++- 5 files changed, 337 insertions(+), 27 deletions(-) diff --git a/configure.ac b/configure.ac index 10a4b39..7a5d747 100644 --- a/configure.ac +++ b/configure.ac @@ -48,7 +48,7 @@ case "$host_os" in ;; mingw*) os_win32=yes - win32_libs="-lgdi32 -lwinspool" + win32_libs="-lgdi32 -lwinspool -lcomdlg32" create_shared_lib="-no-undefined" auto_import_flags="-Wl,--enable-auto-import" diff --git a/utils/pdftocairo-win32.cc b/utils/pdftocairo-win32.cc index de7da57..565d8a1 100644 --- a/utils/pdftocairo-win32.cc +++ b/utils/pdftocairo-win32.cc @@ -6,7 +6,13 @@ #include "parseargs.h" #include "pdftocairo-win32.h" +#include +#include +#include + static HDC hdc; +static HGLOBAL hDevmode = 0; +static HGLOBAL hDevnames = 0; static DEVMODEA *devmode; static char *printerName; @@ -70,7 +76,7 @@ static void parseDuplex(GooString *mode) fprintf(stderr, "Warning: Unknown duplex mode \"%s\"\n", mode->getCString()); } -static void fillCommonPrinterOptions(double w, double h, GBool duplex) +static void fillCommonPrinterOptions(GBool duplex) { if (duplex) { devmode->dmDuplex = DMDUP_HORIZONTAL; @@ -135,12 +141,214 @@ static void fillPrinterOptions(GBool duplex, GooString *printOpt) } } -cairo_surface_t *win32BeginDocument(GooString *inputFileName, GooString *outputFileName, - double w, double h, - GooString *printer, - GooString *printOpt, - GBool setupdlg, - GBool duplex) +static void getLocalPos(HWND wind, HWND dlg, RECT *rect) +{ + GetClientRect(wind, rect); + MapWindowPoints(wind, dlg, (LPPOINT)rect, 2); +} + +static HWND createGroupBox(HWND parent, HINSTANCE hInstance, int id, const char *label, RECT *rect) +{ + HWND hwnd = CreateWindowA(WC_BUTTONA, + label, + WS_CHILD | WS_VISIBLE | BS_GROUPBOX, + rect->left, rect->top, + rect->right - rect->left, + rect->bottom - rect->top, + parent, (HMENU)id, + hInstance, NULL); + HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); + if (hFont) + SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, (LPARAM)0); + return hwnd; +} + +static HWND createCheckBox(HWND parent, HINSTANCE hInstance, int id, const char *label, RECT *rect) +{ + HWND hwnd = CreateWindowA(WC_BUTTONA, + label, + WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | WS_TABSTOP, + rect->left, rect->top, + rect->right - rect->left, + rect->bottom - rect->top, + parent, (HMENU)id, + hInstance, NULL); + HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); + if (hFont) + SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, (LPARAM)0); + return hwnd; +} + +static HWND createStaticText(HWND parent, HINSTANCE hinstance, int id, const char *text, RECT *rect) +{ + HWND hwnd = CreateWindowA(WC_STATICA, + text, + WS_CHILD | WS_VISIBLE | SS_LEFT, + rect->left, rect->top, + rect->right - rect->left, + rect->bottom - rect->top, + parent, (HMENU)id, + hinstance, NULL); + HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); + if (hFont) + SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, (LPARAM)0); + return hwnd; +} + +HWND createPageScaleComboBox(HWND parent, HINSTANCE hinstance, int id, RECT *rect) +{ + HWND hwnd = CreateWindowA(WC_COMBOBOX, + "", + WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP | + CBS_DROPDOWNLIST, + rect->left, rect->top, + rect->right - rect->left, + rect->bottom - rect->top, + parent, (HMENU)id, + hinstance, NULL); + HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); + if (hFont) + SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, (LPARAM)0); + ComboBox_AddString(hwnd, "None"); + ComboBox_AddString(hwnd, "Shrink to Printable Area"); + ComboBox_AddString(hwnd, "Fit to Printable Area"); + return hwnd; +} + +enum PageScale { NONE = 0, SHRINK = 1, FIT = 2 }; + +// used to set/get option values in printDialogHookProc +static PageScale pageScale; +static GBool centerPage; +static GBool useOrigPageSize; + +// PrintDlg callback to customize the print dialog with additional controls. +static UINT_PTR CALLBACK printDialogHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) +{ + if (uiMsg == WM_INITDIALOG) { + // Get the extant controls. See dlgs.h and prnsetup.dlg for the PrintDlg control ids. + HWND printerGroupWind = GetDlgItem(hdlg, grp4); + HWND printerComboWind = GetDlgItem(hdlg, cmb4); + HWND nameLabelWind = GetDlgItem(hdlg, stc6); + HWND statusLabelWind = GetDlgItem(hdlg, stc8); + HWND printRangeGroupWind = GetDlgItem(hdlg, grp1); + HWND radio1Wind = GetDlgItem(hdlg, rad1); + HWND radio2Wind = GetDlgItem(hdlg, rad3); + HWND copiesGroupWind = GetDlgItem(hdlg, grp2); + HWND okWind = GetDlgItem(hdlg, IDOK); + HWND cancelWind = GetDlgItem(hdlg, IDCANCEL); + if (!printerGroupWind || !printerComboWind || !nameLabelWind || !statusLabelWind || + !printRangeGroupWind || !radio1Wind || !radio2Wind || !copiesGroupWind || + !okWind || !cancelWind) + return 0; + + // Get the size and position of the above controls relative to the + // print dialog window + RECT printerGroupRect; + RECT printerComboRect; + RECT nameLabelRect; + RECT statusLabelRect; + RECT printRangeGroupRect; + RECT radio1Rect, radio2Rect; + RECT copiesGroupRect; + RECT okRect, cancelRect; + getLocalPos(printerGroupWind, hdlg, &printerGroupRect); + getLocalPos(printerComboWind, hdlg, &printerComboRect); + getLocalPos(nameLabelWind, hdlg, &nameLabelRect); + getLocalPos(statusLabelWind, hdlg, &statusLabelRect); + getLocalPos(printRangeGroupWind, hdlg, &printRangeGroupRect); + getLocalPos(radio1Wind, hdlg, &radio1Rect); + getLocalPos(radio2Wind, hdlg, &radio2Rect); + getLocalPos(copiesGroupWind, hdlg, &copiesGroupRect); + getLocalPos(okWind, hdlg, &okRect); + getLocalPos(cancelWind, hdlg, &cancelRect); + + // Calc space required for new group + int interGroupSpace = printRangeGroupRect.top - printerGroupRect.bottom; + int groupHeight = statusLabelRect.top - printerGroupRect.top + + printRangeGroupRect.bottom - radio1Rect.bottom; + + // Increase dialog size + RECT dlgRect; + GetWindowRect(hdlg, &dlgRect); + SetWindowPos(hdlg, NULL, + dlgRect.left, dlgRect.top, + dlgRect.right - dlgRect.left, + dlgRect.bottom - dlgRect.top + interGroupSpace + groupHeight, + SWP_NOMOVE|SWP_NOREDRAW|SWP_NOZORDER); + + // Add new group and controls + HINSTANCE hinstance = (HINSTANCE)GetWindowLongPtr(hdlg, GWLP_HINSTANCE); + RECT pdfGroupBoxRect; + pdfGroupBoxRect.left = printRangeGroupRect.left; + pdfGroupBoxRect.right = copiesGroupRect.right; + pdfGroupBoxRect.top = printRangeGroupRect.bottom + interGroupSpace; + pdfGroupBoxRect.bottom = pdfGroupBoxRect.top + groupHeight; + createGroupBox(hdlg, hinstance, grp3, "PDF Print Options", &pdfGroupBoxRect); + + RECT textRect; + textRect.left = nameLabelRect.left; + textRect.right = nameLabelRect.left + 1.8*(printerComboRect.left - nameLabelRect.left); + textRect.top = pdfGroupBoxRect.top + nameLabelRect.top - printerGroupRect.top; + textRect.bottom = textRect.top + nameLabelRect.bottom - nameLabelRect.top; + createStaticText(hdlg, hinstance, stc1, "Page Scaling:", &textRect); + + RECT comboBoxRect; + comboBoxRect.left = textRect.right; + comboBoxRect.right = comboBoxRect.left + printerComboRect.right - printerComboRect.left;; + comboBoxRect.top = pdfGroupBoxRect.top + printerComboRect.top - printerGroupRect.top; + comboBoxRect.bottom = textRect.top + 4*(printerComboRect.bottom - printerComboRect.top); + HWND comboBoxWind = createPageScaleComboBox(hdlg, hinstance, cmb1, &comboBoxRect); + + RECT checkBox1Rect; + checkBox1Rect.left = radio1Rect.left; + checkBox1Rect.right = pdfGroupBoxRect.right - 10; + checkBox1Rect.top = pdfGroupBoxRect.top + statusLabelRect.top - printerGroupRect.top; + checkBox1Rect.bottom = checkBox1Rect.top + radio1Rect.bottom - radio1Rect.top; + HWND checkBox1Wind = createCheckBox(hdlg, hinstance, chx3, "Center", &checkBox1Rect); + + RECT checkBox2Rect; + checkBox2Rect.left = radio1Rect.left; + checkBox2Rect.right = pdfGroupBoxRect.right - 10; + checkBox2Rect.top = checkBox1Rect.top + radio2Rect.top - radio1Rect.top; + checkBox2Rect.bottom = checkBox2Rect.top + radio1Rect.bottom - radio1Rect.top; + HWND checkBox2Wind = createCheckBox(hdlg, hinstance, chx4, "Select page size using document page size", &checkBox2Rect); + + // Move OK and Cancel buttons down ensuring they are last in the Z order + // so that the tab order is correct. + SetWindowPos(okWind, + HWND_BOTTOM, + okRect.left, + okRect.top + interGroupSpace + groupHeight, + 0, 0, + SWP_NOSIZE); // keep current size + SetWindowPos(cancelWind, + HWND_BOTTOM, + cancelRect.left, + cancelRect.top + interGroupSpace + groupHeight, + 0, 0, + SWP_NOSIZE); // keep current size + + // Initialize control values + ComboBox_SetCurSel(comboBoxWind, pageScale); + Button_SetCheck(checkBox1Wind, centerPage ? BST_CHECKED : BST_UNCHECKED); + Button_SetCheck(checkBox2Wind, useOrigPageSize ? BST_CHECKED : BST_UNCHECKED); + + } else if (uiMsg == WM_COMMAND) { + // Save settings + UINT id = LOWORD(wParam); + if (id == cmb1) + pageScale = (PageScale)ComboBox_GetCurSel(GetDlgItem(hdlg, cmb1)); + if (id == chx3) + centerPage = IsDlgButtonChecked(hdlg, chx3); + if (id == chx4) + useOrigPageSize = IsDlgButtonChecked(hdlg, chx4); + } + return 0; +} + +void win32SetupPrinter(GooString *printer, GooString *printOpt, + GBool duplex, GBool setupdlg) { if (printer->getCString()[0] == 0) { DWORD size = 0; @@ -168,7 +376,7 @@ cairo_surface_t *win32BeginDocument(GooString *inputFileName, GooString *outputF } // Update devmode with selected print options - fillCommonPrinterOptions(w, h, duplex); + fillCommonPrinterOptions(duplex); fillPrinterOptions(duplex, printOpt); // Call DocumentProperties again so the driver can update its private data @@ -191,7 +399,72 @@ cairo_surface_t *win32BeginDocument(GooString *inputFileName, GooString *outputF fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); exit(99); } +} + +void win32ShowPrintDialog(GBool *expand, GBool *noShrink, GBool *noCenter, + GBool *usePDFPageSize, GBool *allPages, + int *firstPage, int *lastPage, int maxPages) +{ + PRINTDLG pd; + memset(&pd, 0, sizeof(PRINTDLG)); + pd.lStructSize = sizeof(PRINTDLG); + pd.Flags = PD_NOSELECTION | PD_ENABLEPRINTHOOK | PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC; + if (*allPages) { + pd.nFromPage = 1; + pd.nToPage = maxPages; + } else { + pd.Flags |= PD_PAGENUMS; + pd.nFromPage = *firstPage; + pd.nToPage = *lastPage; + } + pd.nCopies = 1; + pd.nMinPage = 1; + pd.nMaxPage = maxPages; + pd.lpfnPrintHook = printDialogHookProc; + if (!*expand && *noShrink) + pageScale = NONE; + else if (!*expand && !*noShrink) + pageScale = SHRINK; + else + pageScale = FIT; + centerPage = !*noCenter; + useOrigPageSize = *usePDFPageSize; + + if (PrintDlgA(&pd)) { + // Ok + hDevnames = pd.hDevNames; + DEVNAMES *devnames = (DEVNAMES*)GlobalLock(hDevnames); + printerName = (char*)devnames + devnames->wDeviceOffset; + hDevmode = pd.hDevMode; + devmode = (DEVMODEA*)GlobalLock(hDevmode); + hdc = pd.hDC; + if (pd.Flags & PD_PAGENUMS) { + *allPages = gFalse; + *firstPage = pd.nFromPage; + *lastPage = pd.nToPage; + } else { + *allPages = gTrue; + } + if (pageScale == NONE) { + *expand = gFalse; + *noShrink = gTrue; + } else if (pageScale == SHRINK) { + *expand = gFalse; + *noShrink = gFalse; + } else { + *expand = gTrue; + *noShrink = gFalse; + } + *noCenter = !centerPage; + *usePDFPageSize = useOrigPageSize; + } else { + // Cancel + exit(0); + } +} +cairo_surface_t *win32BeginDocument(GooString *inputFileName, GooString *outputFileName) +{ DOCINFOA docinfo; memset(&docinfo, 0, sizeof(docinfo)); docinfo.cbSize = sizeof(docinfo); @@ -259,8 +532,18 @@ void win32EndDocument() { EndDoc(hdc); DeleteDC(hdc); - gfree(devmode); - gfree(printerName); + if (hDevmode) { + GlobalUnlock(hDevmode); + GlobalFree(hDevmode); + } else { + gfree(devmode); + } + if (hDevnames) { + GlobalUnlock(hDevnames); + GlobalFree(hDevnames); + } else { + gfree(printerName); + } } #endif // CAIRO_HAS_WIN32_SURFACE diff --git a/utils/pdftocairo-win32.h b/utils/pdftocairo-win32.h index 3f797b4..fe2cf4d 100644 --- a/utils/pdftocairo-win32.h +++ b/utils/pdftocairo-win32.h @@ -4,18 +4,16 @@ #include "goo/gtypes_p.h" #include "goo/GooString.h" - #ifdef CAIRO_HAS_WIN32_SURFACE #include -void win32GetFitToPageTransform(cairo_matrix_t *m); -cairo_surface_t *win32BeginDocument(GooString *inputFileName, GooString *outputFileName, - double w, double h, - GooString *printer, - GooString *printOpt, - GBool setupdlg, - GBool duplex); +void win32SetupPrinter(GooString *printer, GooString *printOpt, + GBool duplex, GBool setupdlg); +void win32ShowPrintDialog(GBool *expand, GBool *noShrink, GBool *noCenter, + GBool *usePDFPageSize, GBool *allPages, + int *firstPage, int *lastPage, int maxPages); +cairo_surface_t *win32BeginDocument(GooString *inputFileName, GooString *outputFileName); void win32BeginPage(double *w, double *h, GBool changePageSize, GBool useFullPage); void win32EndPage(GooString *imageFileName); void win32EndDocument(); diff --git a/utils/pdftocairo.1 b/utils/pdftocairo.1 index 1045436..7575cab 100644 --- a/utils/pdftocairo.1 +++ b/utils/pdftocairo.1 @@ -82,11 +82,6 @@ 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. - If an output file is not specified, the output will be sent to the printer. - The output file '-' can not be used with this option. -.TP .BI \-ps Generate a PS file .TP @@ -99,6 +94,15 @@ to specify a single page. The page size options (\-origpagesizes, .BI \-svg Generate a SVG (Scalable Vector Graphics) file .TP +.BI \-print +(Windows only) Prints to a system printer. See also \-printer and \-printeropt. + If an output file is not specified, the output will be sent to the printer. + The output file '-' can not be used with this option. +.TP +.BI \-printdlg +(Windows only) Prints to a system printer. Displays the print dialog to allow +the print options to be modified before printing. +.TP .BI \-f " number" Specifies the first page to convert. .TP diff --git a/utils/pdftocairo.cc b/utils/pdftocairo.cc index 0c25b14..d779123 100644 --- a/utils/pdftocairo.cc +++ b/utils/pdftocairo.cc @@ -80,6 +80,7 @@ static GBool ps = gFalse; static GBool eps = gFalse; static GBool pdf = gFalse; static GBool printToWin32 = gFalse; +static GBool printdlg = gFalse; static GBool svg = gFalse; static GBool tiff = gFalse; @@ -160,6 +161,8 @@ static const ArgDesc argDesc[] = { #ifdef CAIRO_HAS_WIN32_SURFACE {"-print", argFlag, &printToWin32, 0, "print to a Windows printer"}, + {"-printdlg", argFlag, &printdlg, 0, + "show Windows print dialog and print"}, {"-printer", argGooString, &printer, 0, "printer name or use default if this option is not specified"}, {"-printopt", argGooString, &printOpt, 0, @@ -541,7 +544,7 @@ static void beginDocument(GooString *inputFileName, GooString *outputFileName, d } #ifdef CAIRO_HAS_WIN32_SURFACE if (printToWin32) - surface = win32BeginDocument(inputFileName, outputFileName, w, h, &printer, &printOpt, setupdlg, duplex); + surface = win32BeginDocument(inputFileName, outputFileName); #endif } } @@ -857,13 +860,14 @@ int main(int argc, char *argv[]) { (eps ? 1 : 0) + (pdf ? 1 : 0) + (printToWin32 ? 1 : 0) + + (printdlg ? 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"); + fprintf(stderr, "Error: one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -print, -printdlg, -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, -print, -svg).\n"); + fprintf(stderr, "Error: use only one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -printdlg, -print, -svg).\n"); exit(99); } if (png || jpeg || tiff) @@ -947,6 +951,9 @@ int main(int argc, char *argv[]) { else usePDFPageSize = gFalse; + if (printdlg) + printToWin32 = gTrue; + globalParams = new GlobalParams(); if (quiet) { globalParams->setErrQuiet(quiet); @@ -1046,6 +1053,24 @@ int main(int argc, char *argv[]) { lastPage = firstPage; } +#ifdef CAIRO_HAS_WIN32_SURFACE + if (printdlg) { + GBool allPages = gFalse; + if (firstPage == 1 && lastPage == doc->getNumPages()) + allPages = gTrue; + win32ShowPrintDialog(&expand, &noShrink, &noCenter, + &usePDFPageSize, &allPages, + &firstPage, &lastPage, doc->getNumPages()); + if (allPages) { + firstPage = 1; + lastPage = doc->getNumPages(); + } + } else if (printToWin32) { + win32SetupPrinter(&printer, &printOpt, + duplex, setupdlg); + } +#endif + // Make sure firstPage is always used so that beginDocument() is called if ((printOnlyEven && firstPage % 2 == 0) || (printOnlyOdd && firstPage % 2 == 1)) firstPage++; -- 2.1.1