1 | //======================================================================== |
2 | // |
3 | // This file is licensed under the GPLv2 or later |
4 | // |
5 | // Copyright (C) 2014 Rodrigo Rivas Costa <rodrigorivascosta@gmail.com> |
6 | // Copyright (C) 2014, 2017 Adrian Johnson <ajohnson@redneon.com> |
7 | // Copyright (C) 2017, 2018, 2022 Albert Astals Cid <aacid@kde.org> |
8 | // |
9 | // To see a description of the changes please see the Changelog file that |
10 | // came with your tarball or type make ChangeLog if you are building from git |
11 | // |
12 | //======================================================================== |
13 | |
14 | #include <cairo.h> |
15 | #ifdef CAIRO_HAS_WIN32_SURFACE |
16 | |
17 | # include <cairo-win32.h> |
18 | |
19 | # include "parseargs.h" |
20 | # include "pdftocairo-win32.h" |
21 | # include "Win32Console.h" |
22 | |
23 | # include <dlgs.h> |
24 | # include <commctrl.h> |
25 | # include <commdlg.h> |
26 | # include <windowsx.h> |
27 | # include <winspool.h> |
28 | |
29 | static HDC hdc; |
30 | static HGLOBAL hDevmode = nullptr; |
31 | static HGLOBAL hDevnames = nullptr; |
32 | static DEVMODEA *devmode; |
33 | static char *printerName; |
34 | |
35 | struct Win32Option |
36 | { |
37 | const char *name; |
38 | int value; |
39 | }; |
40 | |
41 | static const Win32Option win32PaperSource[] = { { "upper" , DMBIN_UPPER }, { "onlyone" , DMBIN_ONLYONE }, |
42 | { "lower" , DMBIN_LOWER }, { "middle" , DMBIN_MIDDLE }, |
43 | { "manual" , DMBIN_MANUAL }, { "envelope" , DMBIN_ENVELOPE }, |
44 | { "envmanual" , DMBIN_ENVMANUAL }, { "auto" , DMBIN_AUTO }, |
45 | { "tractor" , DMBIN_TRACTOR }, { "smallfmt" , DMBIN_SMALLFMT }, |
46 | { "largefmt" , DMBIN_LARGEFMT }, { "largecapacity" , DMBIN_LARGECAPACITY }, |
47 | { "formsource" , DMBIN_FORMSOURCE }, { nullptr, 0 } }; |
48 | |
49 | static void parseSource(GooString *source) |
50 | { |
51 | const Win32Option *option = win32PaperSource; |
52 | while (option->name) { |
53 | if (source->cmp(option->name) == 0) { |
54 | devmode->dmDefaultSource = option->value; |
55 | devmode->dmFields |= DM_DEFAULTSOURCE; |
56 | return; |
57 | } |
58 | option++; |
59 | } |
60 | fprintf(stderr, "Warning: Unknown paper source \"%s\"\n" , source->c_str()); |
61 | } |
62 | |
63 | static const Win32Option win32DuplexMode[] = { { "off" , DMDUP_SIMPLEX }, { "short" , DMDUP_HORIZONTAL }, { "long" , DMDUP_VERTICAL }, { nullptr, 0 } }; |
64 | |
65 | static void parseDuplex(GooString *mode) |
66 | { |
67 | const Win32Option *option = win32DuplexMode; |
68 | while (option->name) { |
69 | if (mode->cmp(option->name) == 0) { |
70 | devmode->dmDuplex = option->value; |
71 | devmode->dmFields |= DM_DUPLEX; |
72 | return; |
73 | } |
74 | option++; |
75 | } |
76 | fprintf(stderr, "Warning: Unknown duplex mode \"%s\"\n" , mode->c_str()); |
77 | } |
78 | |
79 | static void fillCommonPrinterOptions(bool duplex) |
80 | { |
81 | if (duplex) { |
82 | devmode->dmDuplex = DMDUP_HORIZONTAL; |
83 | devmode->dmFields |= DM_DUPLEX; |
84 | } |
85 | } |
86 | |
87 | static void fillPagePrinterOptions(double w, double h) |
88 | { |
89 | w *= 254.0 / 72.0; // units are 0.1mm |
90 | h *= 254.0 / 72.0; |
91 | if (w > h) { |
92 | devmode->dmOrientation = DMORIENT_LANDSCAPE; |
93 | devmode->dmPaperWidth = static_cast<short>(h); |
94 | devmode->dmPaperLength = static_cast<short>(w); |
95 | } else { |
96 | devmode->dmOrientation = DMORIENT_PORTRAIT; |
97 | devmode->dmPaperWidth = static_cast<short>(w); |
98 | devmode->dmPaperLength = static_cast<short>(h); |
99 | } |
100 | devmode->dmPaperSize = 0; |
101 | devmode->dmFields |= DM_ORIENTATION | DM_PAPERWIDTH | DM_PAPERLENGTH; |
102 | } |
103 | |
104 | static void fillPrinterOptions(bool duplex, GooString *printOpt) |
105 | { |
106 | // printOpt format is: <opt1>=<val1>,<opt2>=<val2>,... |
107 | const char *nextOpt = printOpt->c_str(); |
108 | while (nextOpt && *nextOpt) { |
109 | const char *comma = strchr(nextOpt, ','); |
110 | GooString opt; |
111 | if (comma) { |
112 | opt.Set(nextOpt, static_cast<int>(comma - nextOpt)); |
113 | nextOpt = comma + 1; |
114 | } else { |
115 | opt.Set(nextOpt); |
116 | nextOpt = NULL; |
117 | } |
118 | // here opt is "<optN>=<valN> " |
119 | const char *equal = strchr(opt.c_str(), '='); |
120 | if (!equal) { |
121 | fprintf(stderr, "Warning: unknown printer option \"%s\"\n" , opt.c_str()); |
122 | continue; |
123 | } |
124 | const int iequal = static_cast<int>(equal - opt.c_str()); |
125 | GooString value(&opt, iequal + 1, opt.getLength() - iequal - 1); |
126 | opt.del(iequal, opt.getLength() - iequal); |
127 | // here opt is "<optN>" and value is "<valN>" |
128 | |
129 | if (opt.cmp("source" ) == 0) { |
130 | parseSource(&value); |
131 | } else if (opt.cmp("duplex" ) == 0) { |
132 | if (duplex) |
133 | fprintf(stderr, "Warning: duplex mode is specified both as standalone and printer options\n" ); |
134 | else |
135 | parseDuplex(&value); |
136 | } else { |
137 | fprintf(stderr, "Warning: unknown printer option \"%s\"\n" , opt.c_str()); |
138 | } |
139 | } |
140 | } |
141 | |
142 | static void getLocalPos(HWND wind, HWND dlg, RECT *rect) |
143 | { |
144 | GetClientRect(wind, rect); |
145 | MapWindowPoints(wind, dlg, (LPPOINT)rect, 2); |
146 | } |
147 | |
148 | static HWND createGroupBox(HWND parent, HINSTANCE hInstance, HMENU id, const char *label, RECT *rect) |
149 | { |
150 | 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, id, hInstance, NULL); |
151 | HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); |
152 | if (hFont) |
153 | SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, (LPARAM)0); |
154 | return hwnd; |
155 | } |
156 | |
157 | static HWND createCheckBox(HWND parent, HINSTANCE hInstance, HMENU id, const char *label, RECT *rect) |
158 | { |
159 | 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, id, hInstance, NULL); |
160 | HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); |
161 | if (hFont) |
162 | SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, (LPARAM)0); |
163 | return hwnd; |
164 | } |
165 | |
166 | static HWND createStaticText(HWND parent, HINSTANCE hinstance, HMENU id, const char *text, RECT *rect) |
167 | { |
168 | 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, id, hinstance, NULL); |
169 | HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); |
170 | if (hFont) |
171 | SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, (LPARAM)0); |
172 | return hwnd; |
173 | } |
174 | |
175 | static HWND createPageScaleComboBox(HWND parent, HINSTANCE hinstance, HMENU id, RECT *rect) |
176 | { |
177 | 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, id, hinstance, NULL); |
178 | HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); |
179 | if (hFont) |
180 | SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, (LPARAM)0); |
181 | ComboBox_AddString(hwnd, "None" ); |
182 | ComboBox_AddString(hwnd, "Shrink to Printable Area" ); |
183 | ComboBox_AddString(hwnd, "Fit to Printable Area" ); |
184 | return hwnd; |
185 | } |
186 | |
187 | enum PageScale |
188 | { |
189 | NONE = 0, |
190 | SHRINK = 1, |
191 | FIT = 2 |
192 | }; |
193 | |
194 | // used to set/get option values in printDialogHookProc |
195 | static PageScale pageScale; |
196 | static bool centerPage; |
197 | static bool useOrigPageSize; |
198 | |
199 | // PrintDlg callback to customize the print dialog with additional controls. |
200 | static UINT_PTR CALLBACK printDialogHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) |
201 | { |
202 | if (uiMsg == WM_INITDIALOG) { |
203 | // Get the extant controls. See dlgs.h and prnsetup.dlg for the PrintDlg control ids. |
204 | HWND printerGroupWind = GetDlgItem(hdlg, grp4); |
205 | HWND printerComboWind = GetDlgItem(hdlg, cmb4); |
206 | HWND nameLabelWind = GetDlgItem(hdlg, stc6); |
207 | HWND statusLabelWind = GetDlgItem(hdlg, stc8); |
208 | HWND printRangeGroupWind = GetDlgItem(hdlg, grp1); |
209 | HWND radio1Wind = GetDlgItem(hdlg, rad1); |
210 | HWND radio2Wind = GetDlgItem(hdlg, rad3); |
211 | HWND copiesGroupWind = GetDlgItem(hdlg, grp2); |
212 | HWND okWind = GetDlgItem(hdlg, IDOK); |
213 | HWND cancelWind = GetDlgItem(hdlg, IDCANCEL); |
214 | if (!printerGroupWind || !printerComboWind || !nameLabelWind || !statusLabelWind || !printRangeGroupWind || !radio1Wind || !radio2Wind || !copiesGroupWind || !okWind || !cancelWind) |
215 | return 0; |
216 | |
217 | // Get the size and position of the above controls relative to the |
218 | // print dialog window |
219 | RECT printerGroupRect; |
220 | RECT printerComboRect; |
221 | RECT nameLabelRect; |
222 | RECT statusLabelRect; |
223 | RECT printRangeGroupRect; |
224 | RECT radio1Rect, radio2Rect; |
225 | RECT copiesGroupRect; |
226 | RECT okRect, cancelRect; |
227 | getLocalPos(printerGroupWind, hdlg, &printerGroupRect); |
228 | getLocalPos(printerComboWind, hdlg, &printerComboRect); |
229 | getLocalPos(nameLabelWind, hdlg, &nameLabelRect); |
230 | getLocalPos(statusLabelWind, hdlg, &statusLabelRect); |
231 | getLocalPos(printRangeGroupWind, hdlg, &printRangeGroupRect); |
232 | getLocalPos(radio1Wind, hdlg, &radio1Rect); |
233 | getLocalPos(radio2Wind, hdlg, &radio2Rect); |
234 | getLocalPos(copiesGroupWind, hdlg, &copiesGroupRect); |
235 | getLocalPos(okWind, hdlg, &okRect); |
236 | getLocalPos(cancelWind, hdlg, &cancelRect); |
237 | |
238 | // Calc space required for new group |
239 | int interGroupSpace = printRangeGroupRect.top - printerGroupRect.bottom; |
240 | int groupHeight = statusLabelRect.top - printerGroupRect.top + printRangeGroupRect.bottom - radio1Rect.bottom; |
241 | |
242 | // Increase dialog size |
243 | RECT dlgRect; |
244 | GetWindowRect(hdlg, &dlgRect); |
245 | SetWindowPos(hdlg, nullptr, dlgRect.left, dlgRect.top, dlgRect.right - dlgRect.left, dlgRect.bottom - dlgRect.top + interGroupSpace + groupHeight, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER); |
246 | |
247 | // Add new group and controls |
248 | HINSTANCE hinstance = (HINSTANCE)GetWindowLongPtr(hdlg, GWLP_HINSTANCE); |
249 | RECT pdfGroupBoxRect; |
250 | pdfGroupBoxRect.left = printRangeGroupRect.left; |
251 | pdfGroupBoxRect.right = copiesGroupRect.right; |
252 | pdfGroupBoxRect.top = printRangeGroupRect.bottom + interGroupSpace; |
253 | pdfGroupBoxRect.bottom = pdfGroupBoxRect.top + groupHeight; |
254 | createGroupBox(hdlg, hinstance, (HMENU)grp3, "PDF Print Options" , &pdfGroupBoxRect); |
255 | |
256 | RECT textRect; |
257 | textRect.left = nameLabelRect.left; |
258 | textRect.right = static_cast<LONG>(nameLabelRect.left + 1.8 * (printerComboRect.left - nameLabelRect.left)); |
259 | textRect.top = pdfGroupBoxRect.top + nameLabelRect.top - printerGroupRect.top; |
260 | textRect.bottom = textRect.top + nameLabelRect.bottom - nameLabelRect.top; |
261 | createStaticText(hdlg, hinstance, (HMENU)stc1, "Page Scaling:" , &textRect); |
262 | |
263 | RECT comboBoxRect; |
264 | comboBoxRect.left = textRect.right; |
265 | comboBoxRect.right = comboBoxRect.left + printerComboRect.right - printerComboRect.left; |
266 | ; |
267 | comboBoxRect.top = pdfGroupBoxRect.top + printerComboRect.top - printerGroupRect.top; |
268 | comboBoxRect.bottom = textRect.top + 4 * (printerComboRect.bottom - printerComboRect.top); |
269 | HWND comboBoxWind = createPageScaleComboBox(hdlg, hinstance, (HMENU)cmb1, &comboBoxRect); |
270 | |
271 | RECT checkBox1Rect; |
272 | checkBox1Rect.left = radio1Rect.left; |
273 | checkBox1Rect.right = pdfGroupBoxRect.right - 10; |
274 | checkBox1Rect.top = pdfGroupBoxRect.top + statusLabelRect.top - printerGroupRect.top; |
275 | checkBox1Rect.bottom = checkBox1Rect.top + radio1Rect.bottom - radio1Rect.top; |
276 | HWND checkBox1Wind = createCheckBox(hdlg, hinstance, (HMENU)chx3, "Center" , &checkBox1Rect); |
277 | |
278 | RECT checkBox2Rect; |
279 | checkBox2Rect.left = radio1Rect.left; |
280 | checkBox2Rect.right = pdfGroupBoxRect.right - 10; |
281 | checkBox2Rect.top = checkBox1Rect.top + radio2Rect.top - radio1Rect.top; |
282 | checkBox2Rect.bottom = checkBox2Rect.top + radio1Rect.bottom - radio1Rect.top; |
283 | HWND checkBox2Wind = createCheckBox(hdlg, hinstance, (HMENU)chx4, "Select page size using document page size" , &checkBox2Rect); |
284 | |
285 | // Move OK and Cancel buttons down ensuring they are last in the Z order |
286 | // so that the tab order is correct. |
287 | SetWindowPos(okWind, HWND_BOTTOM, okRect.left, okRect.top + interGroupSpace + groupHeight, 0, 0, |
288 | SWP_NOSIZE); // keep current size |
289 | SetWindowPos(cancelWind, HWND_BOTTOM, cancelRect.left, cancelRect.top + interGroupSpace + groupHeight, 0, 0, |
290 | SWP_NOSIZE); // keep current size |
291 | |
292 | // Initialize control values |
293 | ComboBox_SetCurSel(comboBoxWind, pageScale); |
294 | Button_SetCheck(checkBox1Wind, centerPage ? BST_CHECKED : BST_UNCHECKED); |
295 | Button_SetCheck(checkBox2Wind, useOrigPageSize ? BST_CHECKED : BST_UNCHECKED); |
296 | |
297 | } else if (uiMsg == WM_COMMAND) { |
298 | // Save settings |
299 | UINT id = LOWORD(wParam); |
300 | if (id == cmb1) |
301 | pageScale = (PageScale)ComboBox_GetCurSel(GetDlgItem(hdlg, cmb1)); |
302 | if (id == chx3) |
303 | centerPage = IsDlgButtonChecked(hdlg, chx3); |
304 | if (id == chx4) |
305 | useOrigPageSize = IsDlgButtonChecked(hdlg, chx4); |
306 | } |
307 | return 0; |
308 | } |
309 | |
310 | void win32SetupPrinter(GooString *printer, GooString *printOpt, bool duplex, bool setupdlg) |
311 | { |
312 | if (printer->c_str()[0] == 0) { |
313 | DWORD size = 0; |
314 | GetDefaultPrinterA(nullptr, &size); |
315 | printerName = (char *)gmalloc(size); |
316 | GetDefaultPrinterA(printerName, &size); |
317 | } else { |
318 | printerName = copyString(printer->c_str(), printer->getLength()); |
319 | } |
320 | |
321 | // Query the size of the DEVMODE struct |
322 | LONG szProp = DocumentPropertiesA(nullptr, nullptr, printerName, nullptr, nullptr, 0); |
323 | if (szProp < 0) { |
324 | fprintf(stderr, "Error: Printer \"%s\" not found\n" , printerName); |
325 | exit(99); |
326 | } |
327 | devmode = (DEVMODEA *)gmalloc(szProp); |
328 | memset(devmode, 0, szProp); |
329 | devmode->dmSize = sizeof(DEVMODEA); |
330 | devmode->dmSpecVersion = DM_SPECVERSION; |
331 | // Load the current default configuration for the printer into devmode |
332 | if (DocumentPropertiesA(nullptr, nullptr, printerName, devmode, devmode, DM_OUT_BUFFER) < 0) { |
333 | fprintf(stderr, "Error: Printer \"%s\" not found\n" , printerName); |
334 | exit(99); |
335 | } |
336 | |
337 | // Update devmode with selected print options |
338 | fillCommonPrinterOptions(duplex); |
339 | fillPrinterOptions(duplex, printOpt); |
340 | |
341 | // Call DocumentProperties again so the driver can update its private data |
342 | // with the modified print options. This will also display the printer |
343 | // properties dialog if setupdlg is true. |
344 | int ret; |
345 | DWORD mode = DM_IN_BUFFER | DM_OUT_BUFFER; |
346 | if (setupdlg) |
347 | mode |= DM_IN_PROMPT; |
348 | ret = DocumentPropertiesA(nullptr, nullptr, printerName, devmode, devmode, mode); |
349 | if (ret < 0) { |
350 | fprintf(stderr, "Error: Printer \"%s\" not found\n" , printerName); |
351 | exit(99); |
352 | } |
353 | if (setupdlg && ret == IDCANCEL) |
354 | exit(0); |
355 | |
356 | hdc = CreateDCA(nullptr, printerName, nullptr, devmode); |
357 | if (!hdc) { |
358 | fprintf(stderr, "Error: Printer \"%s\" not found\n" , printerName); |
359 | exit(99); |
360 | } |
361 | } |
362 | |
363 | void win32ShowPrintDialog(bool *expand, bool *noShrink, bool *noCenter, bool *usePDFPageSize, bool *allPages, int *firstPage, int *lastPage, int maxPages) |
364 | { |
365 | PRINTDLG pd; |
366 | memset(&pd, 0, sizeof(PRINTDLG)); |
367 | pd.lStructSize = sizeof(PRINTDLG); |
368 | pd.Flags = PD_NOSELECTION | PD_ENABLEPRINTHOOK | PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC; |
369 | if (*allPages) { |
370 | pd.nFromPage = 1; |
371 | pd.nToPage = maxPages; |
372 | } else { |
373 | pd.Flags |= PD_PAGENUMS; |
374 | pd.nFromPage = *firstPage; |
375 | pd.nToPage = *lastPage; |
376 | } |
377 | pd.nCopies = 1; |
378 | pd.nMinPage = 1; |
379 | pd.nMaxPage = maxPages; |
380 | pd.lpfnPrintHook = printDialogHookProc; |
381 | if (!*expand && *noShrink) |
382 | pageScale = NONE; |
383 | else if (!*expand && !*noShrink) |
384 | pageScale = SHRINK; |
385 | else |
386 | pageScale = FIT; |
387 | centerPage = !*noCenter; |
388 | useOrigPageSize = *usePDFPageSize; |
389 | |
390 | if (PrintDlgA(&pd)) { |
391 | // Ok |
392 | hDevnames = pd.hDevNames; |
393 | DEVNAMES *devnames = (DEVNAMES *)GlobalLock(hDevnames); |
394 | printerName = (char *)devnames + devnames->wDeviceOffset; |
395 | hDevmode = pd.hDevMode; |
396 | devmode = (DEVMODEA *)GlobalLock(hDevmode); |
397 | hdc = pd.hDC; |
398 | if (pd.Flags & PD_PAGENUMS) { |
399 | *allPages = false; |
400 | *firstPage = pd.nFromPage; |
401 | *lastPage = pd.nToPage; |
402 | } else { |
403 | *allPages = true; |
404 | } |
405 | if (pageScale == NONE) { |
406 | *expand = false; |
407 | *noShrink = true; |
408 | } else if (pageScale == SHRINK) { |
409 | *expand = false; |
410 | *noShrink = false; |
411 | } else { |
412 | *expand = true; |
413 | *noShrink = false; |
414 | } |
415 | *noCenter = !centerPage; |
416 | *usePDFPageSize = useOrigPageSize; |
417 | } else { |
418 | // Cancel |
419 | exit(0); |
420 | } |
421 | } |
422 | |
423 | cairo_surface_t *win32BeginDocument(GooString *inputFileName, GooString *outputFileName) |
424 | { |
425 | DOCINFOA docinfo; |
426 | memset(&docinfo, 0, sizeof(docinfo)); |
427 | docinfo.cbSize = sizeof(docinfo); |
428 | if (inputFileName->cmp("fd://0" ) == 0) |
429 | docinfo.lpszDocName = "pdftocairo <stdin>" ; |
430 | else |
431 | docinfo.lpszDocName = inputFileName->c_str(); |
432 | if (outputFileName) |
433 | docinfo.lpszOutput = outputFileName->c_str(); |
434 | if (StartDocA(hdc, &docinfo) <= 0) { |
435 | fprintf(stderr, "Error: StartDoc failed\n" ); |
436 | exit(99); |
437 | } |
438 | |
439 | return cairo_win32_printing_surface_create(hdc); |
440 | } |
441 | |
442 | void win32BeginPage(double *w, double *h, bool changePageSize, bool useFullPage) |
443 | { |
444 | if (changePageSize) |
445 | fillPagePrinterOptions(*w, *h); |
446 | if (DocumentPropertiesA(nullptr, nullptr, printerName, devmode, devmode, DM_IN_BUFFER | DM_OUT_BUFFER) < 0) { |
447 | fprintf(stderr, "Error: Printer \"%s\" not found\n" , printerName); |
448 | exit(99); |
449 | } |
450 | ResetDCA(hdc, devmode); |
451 | |
452 | // Get actual paper size or if useFullPage is false the printable area. |
453 | // Transform the hdc scale to points to be consistent with other cairo backends |
454 | int x_dpi = GetDeviceCaps(hdc, LOGPIXELSX); |
455 | int y_dpi = GetDeviceCaps(hdc, LOGPIXELSY); |
456 | int x_off = GetDeviceCaps(hdc, PHYSICALOFFSETX); |
457 | int y_off = GetDeviceCaps(hdc, PHYSICALOFFSETY); |
458 | if (useFullPage) { |
459 | *w = GetDeviceCaps(hdc, PHYSICALWIDTH) * 72.0 / x_dpi; |
460 | *h = GetDeviceCaps(hdc, PHYSICALHEIGHT) * 72.0 / y_dpi; |
461 | } else { |
462 | *w = GetDeviceCaps(hdc, HORZRES) * 72.0 / x_dpi; |
463 | *h = GetDeviceCaps(hdc, VERTRES) * 72.0 / y_dpi; |
464 | } |
465 | XFORM xform; |
466 | xform.eM11 = x_dpi / 72.0f; |
467 | xform.eM12 = 0; |
468 | xform.eM21 = 0; |
469 | xform.eM22 = y_dpi / 72.0f; |
470 | if (useFullPage) { |
471 | xform.eDx = static_cast<FLOAT>(-x_off); |
472 | xform.eDy = static_cast<FLOAT>(-y_off); |
473 | } else { |
474 | xform.eDx = 0; |
475 | xform.eDy = 0; |
476 | } |
477 | SetGraphicsMode(hdc, GM_ADVANCED); |
478 | SetWorldTransform(hdc, &xform); |
479 | |
480 | StartPage(hdc); |
481 | } |
482 | |
483 | void win32EndPage(GooString *imageFileName) |
484 | { |
485 | EndPage(hdc); |
486 | } |
487 | |
488 | void win32EndDocument() |
489 | { |
490 | EndDoc(hdc); |
491 | DeleteDC(hdc); |
492 | if (hDevmode) { |
493 | GlobalUnlock(hDevmode); |
494 | GlobalFree(hDevmode); |
495 | } else { |
496 | gfree(devmode); |
497 | } |
498 | if (hDevnames) { |
499 | GlobalUnlock(hDevnames); |
500 | GlobalFree(hDevnames); |
501 | } else { |
502 | gfree(printerName); |
503 | } |
504 | } |
505 | |
506 | #endif // CAIRO_HAS_WIN32_SURFACE |
507 | |