1 //========================================================================
2 //
3 // XpdfWidgetPrint.cc
4 //
5 // Copyright 2012 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #if XPDFWIDGET_PRINTING
10
11 #include <aconf.h>
12
13 #include <stdlib.h>
14 #include <QPrinter>
15 #include "gfile.h"
16 #include "PDFDoc.h"
17 #include "ErrorCodes.h"
18 #include "XpdfWidget.h"
19
20 #if defined(_WIN32)
21 #elif defined(__APPLE__)
22 # include <CoreFoundation/CoreFoundation.h>
23 # include <ApplicationServices/ApplicationServices.h>
24 #elif defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)
25 # include "PSOutputDev.h"
26 # include <cups/cups.h>
27 #endif
28
29 #include "gmempp.h"
30
31 //------------------------------------------------------------------------
32 // Windows
33 //------------------------------------------------------------------------
34
35 #if defined(_WIN32)
36
37 //------------------------------------------------------------------------
38 // Mac OS X
39 //------------------------------------------------------------------------
40
41 #elif defined(__APPLE__)
42
printPDF(PDFDoc * doc,QPrinter * prt,int hDPI,int vDPI,XpdfWidget * widget)43 XpdfWidget::ErrorCode printPDF(PDFDoc *doc, QPrinter *prt,
44 int hDPI, int vDPI,
45 XpdfWidget *widget) {
46 GString *pdfFileName;
47 CFStringRef s;
48 CFURLRef url;
49 CGPDFDocumentRef pdfDoc;
50 CGPDFPageRef pdfPage;
51 GString *printerName;
52 CFArrayRef printerList, pageFormatList;
53 char prtName[512];
54 PMPrinter printer;
55 PMPrintSession session;
56 PMPageFormat pageFormat;
57 PMPrintSettings printSettings;
58 PMRect paperRect;
59 CGRect paperRect2;
60 CGContextRef ctx;
61 CGAffineTransform pageTransform;
62 QPrinter::ColorMode colorMode;
63 QSizeF paperSize;
64 QPrinter::PaperSource paperSource;
65 QPageLayout::Orientation pageOrientation;
66 FILE *f;
67 GBool deletePDFFile;
68 int startPage, endPage, pg, i;
69
70 //--- get PDF file name
71
72 deletePDFFile = gFalse;
73 if (doc->getFileName()) {
74 pdfFileName = doc->getFileName()->copy();
75 } else {
76 if (!openTempFile(&pdfFileName, &f, "wb", ".pdf")) {
77 goto err0;
78 }
79 fclose(f);
80 deletePDFFile = gTrue;
81 if (!doc->saveAs(pdfFileName)) {
82 goto err1;
83 }
84 }
85
86 //--- load the PDF file
87
88 s = CFStringCreateWithCString(NULL, pdfFileName->getCString(),
89 kCFStringEncodingUTF8);
90 url = CFURLCreateWithFileSystemPath(NULL, s, kCFURLPOSIXPathStyle, false);
91 CFRelease(s);
92 pdfDoc = CGPDFDocumentCreateWithURL(url);
93 CFRelease(url);
94 if (!pdfDoc) {
95 goto err1;
96 }
97
98 //--- get page range
99
100 startPage = prt->fromPage();
101 endPage = prt->toPage();
102 if (startPage == 0) {
103 startPage = 1;
104 }
105 if (endPage == 0) {
106 endPage = doc->getNumPages();
107 }
108 if (startPage > endPage) {
109 CFRelease(pdfDoc);
110 if (deletePDFFile) {
111 unlink(pdfFileName->getCString());
112 }
113 delete pdfFileName;
114 return XpdfWidget::pdfErrBadPageNum;
115 }
116
117 //--- get other parameters
118
119 colorMode = prt->colorMode();
120 paperSize = prt->paperSize(QPrinter::Point);
121 paperSource = prt->paperSource();
122 pageOrientation = prt->pageLayout().orientation();
123
124 //--- create the Session and PrintSettings
125
126 if (PMCreateSession(&session)) {
127 goto err2;
128 }
129 if (PMCreatePrintSettings(&printSettings)) {
130 goto err3;
131 }
132 if (PMSessionDefaultPrintSettings(session, printSettings)) {
133 goto err4;
134 }
135 s = CFStringCreateWithCString(NULL, pdfFileName->getCString(),
136 kCFStringEncodingUTF8);
137 PMPrintSettingsSetJobName(printSettings, s);
138 CFRelease(s);
139
140 //--- set up for print-to-file
141
142 if (!prt->outputFileName().isEmpty()) {
143
144 s = CFStringCreateWithCString(NULL,
145 prt->outputFileName().toUtf8().constData(),
146 kCFStringEncodingUTF8);
147 url = CFURLCreateWithFileSystemPath(NULL, s, kCFURLPOSIXPathStyle, false);
148 CFRelease(s);
149 if (PMSessionSetDestination(session, printSettings, kPMDestinationFile,
150 kPMDocumentFormatPDF, url)) {
151 CFRelease(url);
152 goto err4;
153 }
154 CFRelease(url);
155
156 //--- set the printer
157
158 } else {
159
160 if (PMServerCreatePrinterList(kPMServerLocal, &printerList)) {
161 goto err4;
162 }
163 printer = NULL;
164 printerName = new GString(prt->printerName().toUtf8().constData());
165 for (i = 0; i < CFArrayGetCount(printerList); ++i) {
166 printer = (PMPrinter)CFArrayGetValueAtIndex(printerList, i);
167 #if QT_VERSION >= 0x050000
168 s = PMPrinterGetID(printer);
169 #else
170 s = PMPrinterGetName(printer);
171 #endif
172 if (CFStringGetCString(s, prtName, sizeof(prtName),
173 kCFStringEncodingUTF8)) {
174 if (!strcmp(prtName, printerName->getCString())) {
175 break;
176 }
177 }
178 }
179 delete printerName;
180 if (i >= CFArrayGetCount(printerList)) {
181 CFRelease(printerList);
182 PMRelease(printSettings);
183 PMRelease(session);
184 CFRelease(pdfDoc);
185 return XpdfWidget::pdfErrBadPrinter;
186 }
187 if (PMSessionSetCurrentPMPrinter(session, printer)) {
188 CFRelease(printerList);
189 goto err4;
190 }
191 CFRelease(printerList);
192 }
193
194 //--- set color mode
195
196 #if 0
197 if (colorMode == QPrinter::GrayScale) {
198 // this is deprecated, with no replacement
199 PMSetColorMode(printSettings, kPMGray);
200 }
201 #endif
202
203 //--- set paper size
204
205 if (PMSessionGetCurrentPrinter(session, &printer)) {
206 goto err4;
207 }
208 if (PMSessionCreatePageFormatList(session, printer, &pageFormatList)) {
209 goto err4;
210 }
211 pageFormat = NULL;
212 for (i = 0; i < CFArrayGetCount(pageFormatList); ++i) {
213 pageFormat = (PMPageFormat)CFArrayGetValueAtIndex(pageFormatList, i);
214 PMGetUnadjustedPaperRect(pageFormat, &paperRect);
215 if (fabs((paperRect.right - paperRect.left) - paperSize.width()) < 2 &&
216 fabs((paperRect.bottom - paperRect.top) - paperSize.height()) < 2) {
217 PMRetain(pageFormat);
218 break;
219 }
220 pageFormat = NULL;
221 }
222 CFRelease(pageFormatList);
223 if (!pageFormat) {
224 if (PMCreatePageFormat(&pageFormat)) {
225 goto err4;
226 }
227 if (PMSessionDefaultPageFormat(session, pageFormat)) {
228 goto err5;
229 }
230 }
231
232 //--- set page orientation
233
234 PMGetAdjustedPaperRect(pageFormat, &paperRect);
235 if (pageOrientation == QPageLayout::Landscape) {
236 PMSetOrientation(pageFormat, kPMLandscape, kPMUnlocked);
237 paperRect2 = CGRectMake(paperRect.top,
238 paperRect.left,
239 paperRect.bottom - paperRect.top,
240 paperRect.right - paperRect.left);
241 } else {
242 PMSetOrientation(pageFormat, kPMPortrait, kPMUnlocked);
243 paperRect2 = CGRectMake(paperRect.left,
244 paperRect.top,
245 paperRect.right - paperRect.left,
246 paperRect.bottom - paperRect.top);
247 }
248
249 //--- print
250
251 if (PMSetPageRange(printSettings, startPage, endPage)) {
252 goto err5;
253 }
254 if (PMSessionBeginCGDocumentNoDialog(session, printSettings, pageFormat)) {
255 goto err5;
256 }
257 for (pg = startPage; pg <= endPage; ++pg) {
258 widget->updatePrintStatus(pg, startPage, endPage);
259 if (widget->isPrintCanceled()) {
260 goto err6;
261 }
262 if (PMSessionBeginPageNoDialog(session, pageFormat, NULL)) {
263 goto err6;
264 }
265 if (PMSessionGetCGGraphicsContext(session, &ctx)) {
266 goto err6;
267 }
268 if (!(pdfPage = CGPDFDocumentGetPage(pdfDoc, pg))) {
269 goto err6;
270 }
271 pageTransform = CGPDFPageGetDrawingTransform(pdfPage, kCGPDFMediaBox,
272 paperRect2, 0, true);
273 CGContextSaveGState(ctx);
274 CGContextConcatCTM(ctx, pageTransform);
275 CGContextDrawPDFPage(ctx, pdfPage);
276 CGContextRestoreGState(ctx);
277 if (PMSessionEndPageNoDialog(session)) {
278 goto err6;
279 }
280 }
281 widget->updatePrintStatus(endPage + 1, startPage, endPage);
282 PMSessionEndDocumentNoDialog(session);
283 PMRelease(pageFormat);
284 PMRelease(printSettings);
285 PMRelease(session);
286
287 CFRelease(pdfDoc);
288 if (deletePDFFile) {
289 unlink(pdfFileName->getCString());
290 }
291 delete pdfFileName;
292
293 return XpdfWidget::pdfOk;
294
295 err6:
296 PMSessionEndDocumentNoDialog(session);
297 err5:
298 PMRelease(pageFormat);
299 err4:
300 PMRelease(printSettings);
301 err3:
302 PMRelease(session);
303 err2:
304 CFRelease(pdfDoc);
305 err1:
306 if (deletePDFFile) {
307 unlink(pdfFileName->getCString());
308 }
309 delete pdfFileName;
310 err0:
311 return XpdfWidget::pdfErrPrinting;
312 }
313
314 //------------------------------------------------------------------------
315 // Linux
316 //------------------------------------------------------------------------
317
318 #elif defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)
319
fileOut(void * stream,const char * data,int len)320 static void fileOut(void *stream, const char *data, int len) {
321 fwrite(data, 1, len, (FILE *)stream);
322 }
323
printPDF(PDFDoc * doc,QPrinter * prt,int hDPI,int vDPI,XpdfWidget * widget)324 XpdfWidget::ErrorCode printPDF(PDFDoc *doc, QPrinter *prt,
325 int hDPI, int vDPI,
326 XpdfWidget *widget) {
327 int startPage, endPage;
328 QPrinter::PaperSize paperSize;
329 QSizeF paperSizePts;
330 QPrinter::PaperSource paperSource;
331 QPrinter::DuplexMode duplex;
332 GString *psFileName;
333 FILE *psFile;
334 PSOutputDev *psOut;
335 GString *printerName;
336 cups_dest_t *dests, *dest;
337 cups_option_t *options;
338 const char *paperSizeStr, *paperSourceStr;
339 GString *s;
340 int nDests, nOptions;
341 int pg;
342
343 //--- get page range
344
345 startPage = prt->fromPage();
346 endPage = prt->toPage();
347 if (startPage == 0) {
348 startPage = 1;
349 }
350 if (endPage == 0) {
351 endPage = doc->getNumPages();
352 }
353 if (startPage > endPage) {
354 return XpdfWidget::pdfErrBadPageNum;
355 }
356
357 //--- get other parameters
358
359 paperSize = prt->paperSize();
360 paperSizePts = prt->paperSize(QPrinter::Point);
361 paperSource = prt->paperSource();
362 duplex = prt->duplex();
363
364 //--- print to file
365
366 if (!prt->outputFileName().isEmpty()) {
367 psFileName = NULL; // don't delete the PS file
368 if (!(psFile = fopen(prt->outputFileName().toUtf8().constData(), "wb"))) {
369 goto err0;
370 }
371
372 //--- open temporary PS file
373
374 } else {
375 if (!openTempFile(&psFileName, &psFile, "wb", ".ps")) {
376 goto err0;
377 }
378 }
379
380 //--- generate the PS file
381
382 globalParams->setPSPaperWidth((int)(paperSizePts.width() + 0.5));
383 globalParams->setPSPaperHeight((int)(paperSizePts.height() + 0.5));
384
385 widget->updatePrintStatus(startPage, startPage, endPage);
386 psOut = new PSOutputDev(fileOut, psFile, doc, startPage, endPage,
387 psModePS);
388 if (!psOut->isOk()) {
389 delete psOut;
390 goto err1;
391 }
392 for (pg = startPage; pg <= endPage; ++pg) {
393 if (widget->isPrintCanceled()) {
394 delete psOut;
395 fclose(psFile);
396 goto err1;
397 }
398 doc->displayPage(psOut, pg, 72, 72, 0,
399 !globalParams->getPSUseCropBoxAsPage(),
400 gTrue, gTrue);
401 widget->updatePrintStatus(pg + 1, startPage, endPage);
402 }
403 delete psOut;
404 fclose(psFile);
405
406 //--- print the PS file
407
408 if (psFileName) {
409
410 if (!prt->printerName().isEmpty()) {
411 printerName = new GString(prt->printerName().toLocal8Bit().constData());
412 } else {
413 nDests = cupsGetDests(&dests);
414 if (!(dest = cupsGetDest(NULL, NULL, nDests, dests))) {
415 unlink(psFileName->getCString());
416 delete psFileName;
417 cupsFreeDests(nDests, dests);
418 return XpdfWidget::pdfErrBadPrinter;
419 }
420 printerName = new GString(dest->name);
421 cupsFreeDests(nDests, dests);
422 }
423
424 options = NULL;
425 nOptions = 0;
426
427 switch (paperSize) {
428 case QPrinter::A4: paperSizeStr = "A4"; break;
429 case QPrinter::Comm10E: paperSizeStr = "COM10"; break;
430 case QPrinter::DLE: paperSizeStr = "DL"; break;
431 case QPrinter::Legal: paperSizeStr = "Legal"; break;
432 case QPrinter::Letter: paperSizeStr = "Letter"; break;
433 default: paperSizeStr = NULL; break;
434 }
435 switch (paperSource) {
436 case QPrinter::LargeCapacity: paperSourceStr = "LargeCapacity"; break;
437 case QPrinter::Lower: paperSourceStr = "Lower"; break;
438 default: paperSourceStr = NULL; break;
439 }
440 if (paperSizeStr && paperSourceStr) {
441 s = GString::format("{0:s},{1:s}", paperSizeStr, paperSourceStr);
442 cupsAddOption("media", s->getCString(), nOptions, &options);
443 delete s;
444 ++nOptions;
445 } else if (paperSizeStr) {
446 cupsAddOption("media", paperSizeStr, nOptions, &options);
447 ++nOptions;
448 } else if (paperSourceStr) {
449 cupsAddOption("media", paperSourceStr, nOptions, &options);
450 ++nOptions;
451 }
452
453 switch (duplex) {
454 case QPrinter::DuplexNone:
455 cupsAddOption("sides", "one-sided", nOptions, &options);
456 ++nOptions;
457 break;
458 case QPrinter::DuplexAuto:
459 break;
460 case QPrinter::DuplexLongSide:
461 cupsAddOption("sides", "two-sided-long-edge", nOptions, &options);
462 ++nOptions;
463 break;
464 case QPrinter::DuplexShortSide:
465 cupsAddOption("sides", "two-sided-short-edge", nOptions, &options);
466 ++nOptions;
467 break;
468 }
469
470 if (!cupsPrintFile(printerName->getCString(),
471 psFileName->getCString(),
472 doc->getFileName() ? doc->getFileName()->getCString()
473 : "Xpdf printing",
474 nOptions, options)) {
475 cupsFreeOptions(nOptions, options);
476 delete printerName;
477 goto err1;
478 }
479 cupsFreeOptions(nOptions, options);
480
481 delete printerName;
482
483 unlink(psFileName->getCString());
484 delete psFileName;
485 }
486
487 return XpdfWidget::pdfOk;
488
489 err1:
490 if (psFileName) {
491 unlink(psFileName->getCString());
492 delete psFileName;
493 }
494 err0:
495 return XpdfWidget::pdfErrPrinting;
496 }
497
498 #endif
499
500 #endif // XPDFWIDGET_PRINTING
501