1//========================================================================
2//
3// pdftocairo.cc
4//
5// Copyright 2003 Glyph & Cog, LLC
6//
7//========================================================================
8
9//========================================================================
10//
11// Modified under the Poppler project - http://poppler.freedesktop.org
12//
13// All changes made under the Poppler project to this file are licensed
14// under GPL version 2 or later
15//
16// Copyright (C) 2007 Ilmari Heikkinen <ilmari.heikkinen@gmail.com>
17// Copyright (C) 2008 Richard Airlie <richard.airlie@maglabs.net>
18// Copyright (C) 2009 Michael K. Johnson <a1237@danlj.org>
19// Copyright (C) 2009 Shen Liang <shenzhuxi@gmail.com>
20// Copyright (C) 2009 Stefan Thomas <thomas@eload24.com>
21// Copyright (C) 2009, 2010, 2017-2020, 2022 Albert Astals Cid <aacid@kde.org>
22// Copyright (C) 2010, 2011-2017, 2023 Adrian Johnson <ajohnson@redneon.com>
23// Copyright (C) 2010, 2014 Hib Eris <hib@hiberis.nl>
24// Copyright (C) 2010 Jonathan Liu <net147@gmail.com>
25// Copyright (C) 2010 William Bader <williambader@hotmail.com>
26// Copyright (C) 2011 Thomas Freitag <Thomas.Freitag@alfa.de>
27// Copyright (C) 2011, 2015 Carlos Garcia Campos <carlosgc@gnome.org>
28// Copyright (C) 2012 Koji Otani <sho@bbr.jp>
29// Copyright (C) 2013 Lu Wang <coolwanglu@gmail.com>
30// Copyright (C) 2013, 2017 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp>
31// Copyright (C) 2014 Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>
32// Copyright (C) 2016 Jason Crain <jason@aquaticape.us>
33// Copyright (C) 2018 Martin Packman <gzlist@googlemail.com>
34// Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
35// Copyright (C) 2019, 2020 Oliver Sander <oliver.sander@tu-dresden.de>
36// Copyright (C) 2019 Kris Jurka <jurka@ejurka.com>
37// Copyright (C) 2020, 2021 Oliver Sander <oliver.sander@tu-dresden.de>
38// Copyright (C) 2020 Philipp Knechtges <philipp-dev@knechtges.com>
39// Copyright (C) 2020 Salvo Miosi <salvo.ilmiosi@gmail.com>
40// Copyright (C) 2021 Peter Williams <peter@newton.cx>
41// Copyright (C) 2021 Christian Persch <chpe@src.gnome.org>
42// Copyright (C) 2022 James Cloos <cloos@jhcloos.com>
43// Copyright (C) 2023 Anton Thomasson <antonthomasson@gmail.com>
44//
45// To see a description of the changes please see the Changelog file that
46// came with your tarball or type make ChangeLog if you are building from git
47//
48//========================================================================
49
50#include "config.h"
51#include <poppler-config.h>
52#include <cstdint>
53#include <cstdio>
54#include <cmath>
55#include <cstring>
56#include <fcntl.h>
57#if defined(_WIN32) || defined(__CYGWIN__)
58# include <io.h> // for _setmode
59#endif
60#include "parseargs.h"
61#include "goo/gmem.h"
62#include "goo/GooString.h"
63#include "goo/ImgWriter.h"
64#include "goo/JpegWriter.h"
65#include "goo/PNGWriter.h"
66#include "goo/TiffWriter.h"
67#include "GlobalParams.h"
68#include "Object.h"
69#include "PDFDoc.h"
70#include "PDFDocFactory.h"
71#include "CairoOutputDev.h"
72#include "Win32Console.h"
73#include "numberofcharacters.h"
74#ifdef USE_CMS
75# include <lcms2.h>
76#endif
77#include <cairo.h>
78#ifdef CAIRO_HAS_PS_SURFACE
79# include <cairo-ps.h>
80#endif
81#ifdef CAIRO_HAS_PDF_SURFACE
82# include <cairo-pdf.h>
83#endif
84#ifdef CAIRO_HAS_SVG_SURFACE
85# include <cairo-svg.h>
86#endif
87
88#include "pdftocairo-win32.h"
89
90static bool png = false;
91static bool jpeg = false;
92static bool ps = false;
93static bool eps = false;
94static bool pdf = false;
95static bool printToWin32 = false;
96static bool printdlg = false;
97static bool svg = false;
98static bool tiff = false;
99
100static int firstPage = 1;
101static int lastPage = 0;
102static bool printOnlyOdd = false;
103static bool printOnlyEven = false;
104static bool singleFile = false;
105static double resolution = 0.0;
106static double x_resolution = 150.0;
107static double y_resolution = 150.0;
108static int scaleTo = 0;
109static int x_scaleTo = 0;
110static int y_scaleTo = 0;
111static int crop_x = 0;
112static int crop_y = 0;
113static int crop_w = 0;
114static int crop_h = 0;
115static int sz = 0;
116static bool useCropBox = false;
117static bool mono = false;
118static bool gray = false;
119static bool transp = false;
120static GooString antialias;
121static GooString icc;
122
123static bool level2 = false;
124static bool level3 = false;
125static bool origPageSizes = false;
126static char paperSize[15] = "";
127static int paperWidth = -1;
128static int paperHeight = -1;
129static bool noCrop = false;
130static bool expand = false;
131static bool noShrink = false;
132static bool noCenter = false;
133static bool duplex = false;
134static char tiffCompressionStr[16] = "";
135static bool docStruct = false;
136
137static char ownerPassword[33] = "";
138static char userPassword[33] = "";
139static bool quiet = false;
140static bool printVersion = false;
141static bool printHelp = false;
142
143static GooString jpegOpt;
144static int jpegQuality = -1;
145static bool jpegProgressive = false;
146static bool jpegOptimize = false;
147
148static GooString printer;
149static GooString printOpt;
150#ifdef CAIRO_HAS_WIN32_SURFACE
151static bool setupdlg = false;
152#endif
153
154static const ArgDesc argDesc[] = {
155#ifdef ENABLE_LIBPNG
156 { .arg: "-png", .kind: argFlag, .val: &png, .size: 0, .usage: "generate a PNG file" },
157#endif
158#ifdef ENABLE_LIBJPEG
159 { .arg: "-jpeg", .kind: argFlag, .val: &jpeg, .size: 0, .usage: "generate a JPEG file" },
160 { .arg: "-jpegopt", .kind: argGooString, .val: &jpegOpt, .size: 0, .usage: "jpeg options, with format <opt1>=<val1>[,<optN>=<valN>]*" },
161#endif
162#ifdef ENABLE_LIBTIFF
163 { .arg: "-tiff", .kind: argFlag, .val: &tiff, .size: 0, .usage: "generate a TIFF file" },
164 { .arg: "-tiffcompression", .kind: argString, .val: tiffCompressionStr, .size: sizeof(tiffCompressionStr), .usage: "set TIFF compression: none, packbits, jpeg, lzw, deflate" },
165#endif
166#ifdef CAIRO_HAS_PS_SURFACE
167 { .arg: "-ps", .kind: argFlag, .val: &ps, .size: 0, .usage: "generate PostScript file" },
168 { .arg: "-eps", .kind: argFlag, .val: &eps, .size: 0, .usage: "generate Encapsulated PostScript (EPS)" },
169#endif
170#ifdef CAIRO_HAS_PDF_SURFACE
171 { .arg: "-pdf", .kind: argFlag, .val: &pdf, .size: 0, .usage: "generate a PDF file" },
172#endif
173#ifdef CAIRO_HAS_SVG_SURFACE
174 { .arg: "-svg", .kind: argFlag, .val: &svg, .size: 0, .usage: "generate a Scalable Vector Graphics (SVG) file" },
175#endif
176#ifdef CAIRO_HAS_WIN32_SURFACE
177 { "-print", argFlag, &printToWin32, 0, "print to a Windows printer" },
178 { "-printdlg", argFlag, &printdlg, 0, "show Windows print dialog and print" },
179 { "-printer", argGooString, &printer, 0, "printer name or use default if this option is not specified" },
180 { "-printopt", argGooString, &printOpt, 0, "printer options, with format <opt1>=<val1>[,<optN>=<valN>]*" },
181 { "-setupdlg", argFlag, &setupdlg, 0, "show printer setup dialog before printing" },
182#endif
183
184 { .arg: "-f", .kind: argInt, .val: &firstPage, .size: 0, .usage: "first page to print" },
185 { .arg: "-l", .kind: argInt, .val: &lastPage, .size: 0, .usage: "last page to print" },
186 { .arg: "-o", .kind: argFlag, .val: &printOnlyOdd, .size: 0, .usage: "print only odd pages" },
187 { .arg: "-e", .kind: argFlag, .val: &printOnlyEven, .size: 0, .usage: "print only even pages" },
188 { .arg: "-singlefile", .kind: argFlag, .val: &singleFile, .size: 0, .usage: "write only the first page and do not add digits" },
189
190 { .arg: "-r", .kind: argFP, .val: &resolution, .size: 0, .usage: "resolution, in PPI (default is 150)" },
191 { .arg: "-rx", .kind: argFP, .val: &x_resolution, .size: 0, .usage: "X resolution, in PPI (default is 150)" },
192 { .arg: "-ry", .kind: argFP, .val: &y_resolution, .size: 0, .usage: "Y resolution, in PPI (default is 150)" },
193 { .arg: "-scale-to", .kind: argInt, .val: &scaleTo, .size: 0, .usage: "scales each page to fit within scale-to*scale-to pixel box" },
194 { .arg: "-scale-to-x", .kind: argInt, .val: &x_scaleTo, .size: 0, .usage: "scales each page horizontally to fit in scale-to-x pixels" },
195 { .arg: "-scale-to-y", .kind: argInt, .val: &y_scaleTo, .size: 0, .usage: "scales each page vertically to fit in scale-to-y pixels" },
196
197 { .arg: "-x", .kind: argInt, .val: &crop_x, .size: 0, .usage: "x-coordinate of the crop area top left corner" },
198 { .arg: "-y", .kind: argInt, .val: &crop_y, .size: 0, .usage: "y-coordinate of the crop area top left corner" },
199 { .arg: "-W", .kind: argInt, .val: &crop_w, .size: 0, .usage: "width of crop area in pixels (default is 0)" },
200 { .arg: "-H", .kind: argInt, .val: &crop_h, .size: 0, .usage: "height of crop area in pixels (default is 0)" },
201 { .arg: "-sz", .kind: argInt, .val: &sz, .size: 0, .usage: "size of crop square in pixels (sets W and H)" },
202 { .arg: "-cropbox", .kind: argFlag, .val: &useCropBox, .size: 0, .usage: "use the crop box rather than media box" },
203
204 { .arg: "-mono", .kind: argFlag, .val: &mono, .size: 0, .usage: "generate a monochrome image file (PNG, JPEG)" },
205 { .arg: "-gray", .kind: argFlag, .val: &gray, .size: 0, .usage: "generate a grayscale image file (PNG, JPEG)" },
206 { .arg: "-transp", .kind: argFlag, .val: &transp, .size: 0, .usage: "use a transparent background instead of white (PNG)" },
207 { .arg: "-antialias", .kind: argGooString, .val: &antialias, .size: 0, .usage: "set cairo antialias option" },
208#ifdef USE_CMS
209 { "-icc", argGooString, &icc, 0, "ICC color profile to use" },
210#endif
211
212 { .arg: "-level2", .kind: argFlag, .val: &level2, .size: 0, .usage: "generate Level 2 PostScript (PS, EPS)" },
213 { .arg: "-level3", .kind: argFlag, .val: &level3, .size: 0, .usage: "generate Level 3 PostScript (PS, EPS)" },
214 { .arg: "-origpagesizes", .kind: argFlag, .val: &origPageSizes, .size: 0, .usage: "conserve original page sizes (PS, PDF, SVG)" },
215 { .arg: "-paper", .kind: argString, .val: paperSize, .size: sizeof(paperSize), .usage: "paper size (letter, legal, A4, A3, match)" },
216 { .arg: "-paperw", .kind: argInt, .val: &paperWidth, .size: 0, .usage: "paper width, in points" },
217 { .arg: "-paperh", .kind: argInt, .val: &paperHeight, .size: 0, .usage: "paper height, in points" },
218 { .arg: "-nocrop", .kind: argFlag, .val: &noCrop, .size: 0, .usage: "don't crop pages to CropBox" },
219 { .arg: "-expand", .kind: argFlag, .val: &expand, .size: 0, .usage: "expand pages smaller than the paper size" },
220 { .arg: "-noshrink", .kind: argFlag, .val: &noShrink, .size: 0, .usage: "don't shrink pages larger than the paper size" },
221 { .arg: "-nocenter", .kind: argFlag, .val: &noCenter, .size: 0, .usage: "don't center pages smaller than the paper size" },
222 { .arg: "-duplex", .kind: argFlag, .val: &duplex, .size: 0, .usage: "enable duplex printing" },
223
224#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0)
225 { "-struct", argFlag, &docStruct, 0, "enable logical document structure" },
226#endif
227
228 { .arg: "-opw", .kind: argString, .val: ownerPassword, .size: sizeof(ownerPassword), .usage: "owner password (for encrypted files)" },
229 { .arg: "-upw", .kind: argString, .val: userPassword, .size: sizeof(userPassword), .usage: "user password (for encrypted files)" },
230
231 { .arg: "-q", .kind: argFlag, .val: &quiet, .size: 0, .usage: "don't print any messages or errors" },
232 { .arg: "-v", .kind: argFlag, .val: &printVersion, .size: 0, .usage: "print copyright and version info" },
233 { .arg: "-h", .kind: argFlag, .val: &printHelp, .size: 0, .usage: "print usage information" },
234 { .arg: "-help", .kind: argFlag, .val: &printHelp, .size: 0, .usage: "print usage information" },
235 { .arg: "--help", .kind: argFlag, .val: &printHelp, .size: 0, .usage: "print usage information" },
236 { .arg: "-?", .kind: argFlag, .val: &printHelp, .size: 0, .usage: "print usage information" },
237 {}
238};
239
240static cairo_surface_t *surface;
241static bool printing;
242static FILE *output_file;
243static bool usePDFPageSize;
244static cairo_antialias_t antialiasEnum = CAIRO_ANTIALIAS_DEFAULT;
245
246#ifdef USE_CMS
247static unsigned char *icc_data;
248static int icc_data_size;
249static GfxLCMSProfilePtr profile;
250#endif
251
252struct AntialiasOption
253{
254 const char *name;
255 cairo_antialias_t value;
256};
257
258static const AntialiasOption antialiasOptions[] = {
259 { .name: "default", .value: CAIRO_ANTIALIAS_DEFAULT }, { .name: "none", .value: CAIRO_ANTIALIAS_NONE }, { .name: "gray", .value: CAIRO_ANTIALIAS_GRAY }, { .name: "subpixel", .value: CAIRO_ANTIALIAS_SUBPIXEL },
260 { .name: "fast", .value: CAIRO_ANTIALIAS_FAST }, { .name: "good", .value: CAIRO_ANTIALIAS_GOOD }, { .name: "best", .value: CAIRO_ANTIALIAS_BEST }, { .name: nullptr, .value: CAIRO_ANTIALIAS_DEFAULT },
261};
262
263static bool parseAntialiasOption()
264{
265 const AntialiasOption *option = antialiasOptions;
266 while (option->name) {
267 if (antialias.cmp(sA: option->name) == 0) {
268 antialiasEnum = option->value;
269 return true;
270 }
271 option++;
272 }
273
274 fprintf(stderr, format: "Error: Invalid antialias option \"%s\"\n", antialias.c_str());
275 fprintf(stderr, format: "Valid options are:\n");
276 option = antialiasOptions;
277 while (option->name) {
278 fprintf(stderr, format: " %s\n", option->name);
279 option++;
280 }
281 return false;
282}
283
284static bool parseJpegOptions()
285{
286 // jpegOpt format is: <opt1>=<val1>,<opt2>=<val2>,...
287 const char *nextOpt = jpegOpt.c_str();
288 while (nextOpt && *nextOpt) {
289 const char *comma = strchr(s: nextOpt, c: ',');
290 GooString opt;
291 if (comma) {
292 opt.Set(newStr: nextOpt, newLen: static_cast<int>(comma - nextOpt));
293 nextOpt = comma + 1;
294 } else {
295 opt.Set(nextOpt);
296 nextOpt = nullptr;
297 }
298 // here opt is "<optN>=<valN> "
299 const char *equal = strchr(s: opt.c_str(), c: '=');
300 if (!equal) {
301 fprintf(stderr, format: "Unknown jpeg option \"%s\"\n", opt.c_str());
302 return false;
303 }
304 const int iequal = static_cast<int>(equal - opt.c_str());
305 GooString value(&opt, iequal + 1, opt.getLength() - iequal - 1);
306 opt.del(i: iequal, n: opt.getLength() - iequal);
307 // here opt is "<optN>" and value is "<valN>"
308
309 if (opt.cmp(sA: "quality") == 0) {
310 if (!isInt(s: value.c_str())) {
311 fprintf(stderr, format: "Invalid jpeg quality\n");
312 return false;
313 }
314 jpegQuality = atoi(nptr: value.c_str());
315 if (jpegQuality < 0 || jpegQuality > 100) {
316 fprintf(stderr, format: "jpeg quality must be between 0 and 100\n");
317 return false;
318 }
319 } else if (opt.cmp(sA: "progressive") == 0) {
320 jpegProgressive = false;
321 if (value.cmp(sA: "y") == 0) {
322 jpegProgressive = true;
323 } else if (value.cmp(sA: "n") != 0) {
324 fprintf(stderr, format: "jpeg progressive option must be \"y\" or \"n\"\n");
325 return false;
326 }
327 } else if (opt.cmp(sA: "optimize") == 0 || opt.cmp(sA: "optimise") == 0) {
328 jpegOptimize = false;
329 if (value.cmp(sA: "y") == 0) {
330 jpegOptimize = true;
331 } else if (value.cmp(sA: "n") != 0) {
332 fprintf(stderr, format: "jpeg optimize option must be \"y\" or \"n\"\n");
333 return false;
334 }
335 } else {
336 fprintf(stderr, format: "Unknown jpeg option \"%s\"\n", opt.c_str());
337 return false;
338 }
339 }
340 return true;
341}
342
343static void writePageImage(GooString *filename)
344{
345 ImgWriter *writer = nullptr;
346 FILE *file;
347 int height, width, stride;
348 unsigned char *data;
349
350 if (png) {
351#ifdef ENABLE_LIBPNG
352 if (transp) {
353 writer = new PNGWriter(PNGWriter::RGBA);
354 } else if (gray) {
355 writer = new PNGWriter(PNGWriter::GRAY);
356 } else if (mono) {
357 writer = new PNGWriter(PNGWriter::MONOCHROME);
358 } else {
359 writer = new PNGWriter(PNGWriter::RGB);
360 }
361
362# ifdef USE_CMS
363 if (icc_data) {
364 cmsUInt8Number profileID[17];
365 profileID[16] = '\0';
366
367 cmsGetHeaderProfileID(profile.get(), profileID);
368 static_cast<PNGWriter *>(writer)->setICCProfile(reinterpret_cast<char *>(profileID), icc_data, icc_data_size);
369 } else {
370 static_cast<PNGWriter *>(writer)->setSRGBProfile();
371 }
372# endif
373#endif
374
375 } else if (jpeg) {
376#ifdef ENABLE_LIBJPEG
377 if (gray) {
378 writer = new JpegWriter(JpegWriter::GRAY);
379 } else {
380 writer = new JpegWriter(JpegWriter::RGB);
381 }
382
383 static_cast<JpegWriter *>(writer)->setOptimize(jpegOptimize);
384 static_cast<JpegWriter *>(writer)->setProgressive(jpegProgressive);
385 if (jpegQuality >= 0) {
386 static_cast<JpegWriter *>(writer)->setQuality(jpegQuality);
387 }
388#endif
389 } else if (tiff) {
390#ifdef ENABLE_LIBTIFF
391 if (transp) {
392 writer = new TiffWriter(TiffWriter::RGBA_PREMULTIPLIED);
393 } else if (gray) {
394 writer = new TiffWriter(TiffWriter::GRAY);
395 } else if (mono) {
396 writer = new TiffWriter(TiffWriter::MONOCHROME);
397 } else {
398 writer = new TiffWriter(TiffWriter::RGB);
399 }
400 static_cast<TiffWriter *>(writer)->setCompressionString(tiffCompressionStr);
401#endif
402 }
403 if (!writer) {
404 return;
405 }
406
407 if (filename->cmp(sA: "fd://0") == 0) {
408#if defined(_WIN32) || defined(__CYGWIN__)
409 _setmode(fileno(stdout), O_BINARY);
410#endif
411 file = stdout;
412 } else {
413 file = fopen(filename: filename->c_str(), modes: "wb");
414 }
415
416 if (!file) {
417 fprintf(stderr, format: "Error opening output file %s\n", filename->c_str());
418 exit(status: 2);
419 }
420
421 height = cairo_image_surface_get_height(surface);
422 width = cairo_image_surface_get_width(surface);
423 stride = cairo_image_surface_get_stride(surface);
424 cairo_surface_flush(surface);
425 data = cairo_image_surface_get_data(surface);
426
427 if (!writer->init(f: file, width, height, hDPI: x_resolution, vDPI: y_resolution)) {
428 fprintf(stderr, format: "Error writing %s\n", filename->c_str());
429 exit(status: 2);
430 }
431 unsigned char *row = (unsigned char *)gmallocn(count: width, size: 4);
432
433 for (int y = 0; y < height; y++) {
434 uint32_t *pixel = reinterpret_cast<uint32_t *>((data + y * stride));
435 unsigned char *rowp = row;
436 int bit = 7;
437 for (int x = 0; x < width; x++, pixel++) {
438 if (transp) {
439 if (tiff) {
440 // RGBA premultipled format
441 *rowp++ = (*pixel & 0xff0000) >> 16;
442 *rowp++ = (*pixel & 0x00ff00) >> 8;
443 *rowp++ = (*pixel & 0x0000ff) >> 0;
444 *rowp++ = (*pixel & 0xff000000) >> 24;
445 } else {
446 // unpremultiply into RGBA format
447 uint8_t a;
448 a = (*pixel & 0xff000000) >> 24;
449 if (a == 0) {
450 *rowp++ = 0;
451 *rowp++ = 0;
452 *rowp++ = 0;
453 } else {
454 *rowp++ = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a;
455 *rowp++ = (((*pixel & 0x00ff00) >> 8) * 255 + a / 2) / a;
456 *rowp++ = (((*pixel & 0x0000ff) >> 0) * 255 + a / 2) / a;
457 }
458 *rowp++ = a;
459 }
460 } else if (gray || mono) {
461 // convert to gray
462 // The PDF Reference specifies the DeviceRGB to DeviceGray conversion as
463 // gray = 0.3*red + 0.59*green + 0.11*blue
464 const int r = (*pixel & 0x00ff0000) >> 16;
465 const int g = (*pixel & 0x0000ff00) >> 8;
466 const int b = (*pixel & 0x000000ff) >> 0;
467 // an arbitrary integer approximation of .3*r + .59*g + .11*b
468 const int grayValue = (r * 19661 + g * 38666 + b * 7209 + 32829) >> 16;
469 if (mono) {
470 if (bit == 7) {
471 *rowp = 0;
472 }
473 if (grayValue > 127) {
474 *rowp |= (1 << bit);
475 }
476 bit--;
477 if (bit < 0) {
478 bit = 7;
479 rowp++;
480 }
481 } else {
482 *rowp++ = grayValue;
483 }
484 } else {
485 // copy into RGB format
486 *rowp++ = (*pixel & 0x00ff0000) >> 16;
487 *rowp++ = (*pixel & 0x0000ff00) >> 8;
488 *rowp++ = (*pixel & 0x000000ff) >> 0;
489 }
490 }
491 writer->writeRow(row: &row);
492 }
493 gfree(p: row);
494 writer->close();
495 delete writer;
496 if (file == stdout) {
497 fflush(stream: file);
498 } else {
499 fclose(stream: file);
500 }
501}
502
503static void getCropSize(double page_w, double page_h, double *width, double *height)
504{
505 int w = crop_w;
506 int h = crop_h;
507
508 if (w == 0) {
509 w = (int)ceil(x: page_w);
510 }
511
512 if (h == 0) {
513 h = (int)ceil(x: page_h);
514 }
515
516 *width = (crop_x + w > page_w ? (int)ceil(x: page_w - crop_x) : w);
517 *height = (crop_y + h > page_h ? (int)ceil(x: page_h - crop_y) : h);
518}
519
520static void getOutputSize(double page_w, double page_h, double *width, double *height)
521{
522
523 if (printing) {
524 if (usePDFPageSize) {
525 *width = page_w;
526 *height = page_h;
527 } else {
528 if (page_w > page_h) {
529 *width = paperHeight;
530 *height = paperWidth;
531 } else {
532 *width = paperWidth;
533 *height = paperHeight;
534 }
535 }
536 } else {
537 getCropSize(page_w: page_w * x_resolution / 72.0, page_h: page_h * y_resolution / 72.0, width, height);
538 }
539}
540
541static void getFitToPageTransform(double page_w, double page_h, double paper_w, double paper_h, cairo_matrix_t *m)
542{
543 double x_scale, y_scale, scale;
544
545 x_scale = paper_w / page_w;
546 y_scale = paper_h / page_h;
547 if (x_scale < y_scale) {
548 scale = x_scale;
549 } else {
550 scale = y_scale;
551 }
552
553 if (scale > 1.0 && !expand) {
554 scale = 1.0;
555 }
556 if (scale < 1.0 && noShrink) {
557 scale = 1.0;
558 }
559
560 cairo_matrix_init_identity(matrix: m);
561 if (!noCenter) {
562 // centre page
563 cairo_matrix_translate(matrix: m, tx: (paper_w - page_w * scale) / 2, ty: (paper_h - page_h * scale) / 2);
564 } else if (!svg) {
565 // move to PostScript origin
566 cairo_matrix_translate(matrix: m, tx: 0, ty: (paper_h - page_h * scale));
567 }
568 cairo_matrix_scale(matrix: m, sx: scale, sy: scale);
569}
570
571static cairo_status_t writeStream(void *closure, const unsigned char *data, unsigned int length)
572{
573 FILE *file = (FILE *)closure;
574
575 if (fwrite(ptr: data, size: length, n: 1, s: file) == 1) {
576 return CAIRO_STATUS_SUCCESS;
577 } else {
578 return CAIRO_STATUS_WRITE_ERROR;
579 }
580}
581
582static void beginDocument(GooString *inputFileName, GooString *outputFileName, double w, double h)
583{
584 if (printing) {
585 if (printToWin32) {
586 output_file = nullptr;
587 } else {
588 if (outputFileName->cmp(sA: "fd://0") == 0) {
589#if defined(_WIN32) || defined(__CYGWIN__)
590 _setmode(fileno(stdout), O_BINARY);
591#endif
592 output_file = stdout;
593 } else {
594 output_file = fopen(filename: outputFileName->c_str(), modes: "wb");
595 if (!output_file) {
596 fprintf(stderr, format: "Error opening output file %s\n", outputFileName->c_str());
597 exit(status: 2);
598 }
599 }
600 }
601
602 if (ps || eps) {
603#ifdef CAIRO_HAS_PS_SURFACE
604 surface = cairo_ps_surface_create_for_stream(write_func: writeStream, closure: output_file, width_in_points: w, height_in_points: h);
605 if (level2) {
606 cairo_ps_surface_restrict_to_level(surface, level: CAIRO_PS_LEVEL_2);
607 }
608 if (eps) {
609 cairo_ps_surface_set_eps(surface, eps: 1);
610 }
611 if (duplex) {
612 cairo_ps_surface_dsc_comment(surface, comment: "%%Requirements: duplex");
613 cairo_ps_surface_dsc_begin_setup(surface);
614 cairo_ps_surface_dsc_comment(surface, comment: "%%IncludeFeature: *Duplex DuplexNoTumble");
615 }
616 cairo_ps_surface_dsc_begin_page_setup(surface);
617#endif
618 } else if (pdf) {
619#ifdef CAIRO_HAS_PDF_SURFACE
620 surface = cairo_pdf_surface_create_for_stream(write_func: writeStream, closure: output_file, width_in_points: w, height_in_points: h);
621#endif
622 } else if (svg) {
623#ifdef CAIRO_HAS_SVG_SURFACE
624 surface = cairo_svg_surface_create_for_stream(write_func: writeStream, closure: output_file, width_in_points: w, height_in_points: h);
625 cairo_svg_surface_restrict_to_version(surface, version: CAIRO_SVG_VERSION_1_2);
626#endif
627 }
628#ifdef CAIRO_HAS_WIN32_SURFACE
629 if (printToWin32)
630 surface = win32BeginDocument(inputFileName, outputFileName);
631#endif
632 }
633}
634
635static void beginPage(double *w, double *h)
636{
637 if (printing) {
638 if (ps) {
639#ifdef CAIRO_HAS_PS_SURFACE
640 if (*w > *h) {
641 cairo_ps_surface_dsc_comment(surface, comment: "%%PageOrientation: Landscape");
642 cairo_ps_surface_set_size(surface, width_in_points: *h, height_in_points: *w);
643 } else {
644 cairo_ps_surface_dsc_comment(surface, comment: "%%PageOrientation: Portrait");
645 cairo_ps_surface_set_size(surface, width_in_points: *w, height_in_points: *h);
646 }
647#endif
648 }
649
650#ifdef CAIRO_HAS_PDF_SURFACE
651 if (pdf) {
652 cairo_pdf_surface_set_size(surface, width_in_points: *w, height_in_points: *h);
653 }
654#endif
655
656#ifdef CAIRO_HAS_WIN32_SURFACE
657 if (printToWin32) {
658 bool changePageSize = true;
659 if (setupdlg && !origPageSizes)
660 changePageSize = false;
661 win32BeginPage(w, h, changePageSize, noShrink); // w,h will be changed to actual size used
662 }
663#endif
664
665 cairo_surface_set_fallback_resolution(surface, x_pixels_per_inch: x_resolution, y_pixels_per_inch: y_resolution);
666
667 } else {
668 surface = cairo_image_surface_create(format: CAIRO_FORMAT_ARGB32, width: static_cast<int>(ceil(x: *w)), height: static_cast<int>(ceil(x: *h)));
669 }
670}
671
672static void renderPage(PDFDoc *doc, CairoOutputDev *cairoOut, int pg, double page_w, double page_h, double output_w, double output_h)
673{
674 cairo_t *cr;
675 cairo_status_t status;
676 cairo_matrix_t m;
677 cairo_font_options_t *font_options;
678
679 cr = cairo_create(target: surface);
680
681 cairo_set_antialias(cr, antialias: antialiasEnum);
682
683 font_options = cairo_font_options_create();
684 cairo_get_font_options(cr, options: font_options);
685 cairo_font_options_set_antialias(options: font_options, antialias: antialiasEnum);
686 cairo_set_font_options(cr, options: font_options);
687 cairo_font_options_destroy(options: font_options);
688
689 cairoOut->setCairo(cr);
690 cairoOut->setPrinting(printing);
691
692 cairo_save(cr);
693 if (ps && output_w > output_h) {
694 // rotate 90 deg for landscape
695 cairo_translate(cr, tx: 0, ty: output_w);
696 cairo_matrix_init(matrix: &m, xx: 0, yx: -1, xy: 1, yy: 0, x0: 0, y0: 0);
697 cairo_transform(cr, matrix: &m);
698 }
699 cairo_translate(cr, tx: -crop_x, ty: -crop_y);
700 if (printing) {
701 double cropped_w, cropped_h;
702 getCropSize(page_w, page_h, width: &cropped_w, height: &cropped_h);
703 getFitToPageTransform(page_w: cropped_w, page_h: cropped_h, paper_w: output_w, paper_h: output_h, m: &m);
704 cairo_transform(cr, matrix: &m);
705 cairo_rectangle(cr, x: crop_x, y: crop_y, width: cropped_w, height: cropped_h);
706 cairo_clip(cr);
707 } else {
708 cairo_scale(cr, sx: x_resolution / 72.0, sy: y_resolution / 72.0);
709 }
710 doc->displayPageSlice(out: cairoOut, page: pg, hDPI: 72.0, vDPI: 72.0, rotate: 0, /* rotate */
711 useMediaBox: !useCropBox, /* useMediaBox */
712 crop: false, /* Crop */
713 printing, sliceX: -1, sliceY: -1, sliceW: -1, sliceH: -1);
714 cairo_restore(cr);
715 cairoOut->setCairo(nullptr);
716
717 // Blend onto white page
718 if (!printing && !transp) {
719 cairo_save(cr);
720 cairo_set_operator(cr, op: CAIRO_OPERATOR_DEST_OVER);
721 cairo_set_source_rgb(cr, red: 1, green: 1, blue: 1);
722 cairo_paint(cr);
723 cairo_restore(cr);
724 }
725
726 status = cairo_status(cr);
727 if (status) {
728 fprintf(stderr, format: "cairo error: %s\n", cairo_status_to_string(status));
729 }
730 cairo_destroy(cr);
731}
732
733static void endPage(GooString *imageFileName, CairoOutputDev *cairoOut, bool isLastPage)
734{
735 cairo_status_t status;
736 cairo_t *cr;
737
738 if (printing) {
739 if (isLastPage) {
740 cr = cairo_create(target: surface);
741 cairoOut->setCairo(cr);
742 cairoOut->setPrinting(printing);
743 cairoOut->emitStructTree();
744 cairoOut->setCairo(nullptr);
745 status = cairo_status(cr);
746 if (status) {
747 fprintf(stderr, format: "cairo error: %s\n", cairo_status_to_string(status));
748 }
749 cairo_destroy(cr);
750 }
751
752 cairo_surface_show_page(surface);
753
754#ifdef CAIRO_HAS_WIN32_SURFACE
755 if (printToWin32)
756 win32EndPage(imageFileName);
757#endif
758
759 } else {
760 writePageImage(filename: imageFileName);
761 cairo_surface_finish(surface);
762 status = cairo_surface_status(surface);
763 if (status) {
764 fprintf(stderr, format: "cairo error: %s\n", cairo_status_to_string(status));
765 }
766 cairo_surface_destroy(surface);
767 }
768}
769
770static void endDocument()
771{
772 cairo_status_t status;
773
774 if (printing) {
775 cairo_surface_finish(surface);
776 status = cairo_surface_status(surface);
777 if (status) {
778 fprintf(stderr, format: "cairo error: %s\n", cairo_status_to_string(status));
779 }
780 cairo_surface_destroy(surface);
781#ifdef CAIRO_HAS_WIN32_SURFACE
782 if (printToWin32)
783 win32EndDocument();
784#endif
785 if (output_file) {
786 fclose(stream: output_file);
787 }
788 }
789}
790
791static bool setPSPaperSize(char *size, int &psPaperWidth, int &psPaperHeight)
792{
793 if (!strcmp(s1: size, s2: "match")) {
794 psPaperWidth = psPaperHeight = -1;
795 } else if (!strcmp(s1: size, s2: "letter")) {
796 psPaperWidth = 612;
797 psPaperHeight = 792;
798 } else if (!strcmp(s1: size, s2: "legal")) {
799 psPaperWidth = 612;
800 psPaperHeight = 1008;
801 } else if (!strcmp(s1: size, s2: "A4")) {
802 psPaperWidth = 595;
803 psPaperHeight = 842;
804 } else if (!strcmp(s1: size, s2: "A3")) {
805 psPaperWidth = 842;
806 psPaperHeight = 1190;
807 } else {
808 return false;
809 }
810 return true;
811}
812
813static GooString *getImageFileName(GooString *outputFileName, int numDigits, int page)
814{
815 char buf[10];
816 GooString *imageName = new GooString(outputFileName);
817 if (!singleFile) {
818 snprintf(s: buf, maxlen: sizeof(buf), format: "-%0*d", numDigits, page);
819 imageName->append(str: buf);
820 }
821 if (outputFileName->cmp(sA: "fd://0") != 0) {
822 if (png) {
823 imageName->append(str: ".png");
824 } else if (jpeg) {
825 imageName->append(str: ".jpg");
826 } else if (tiff) {
827 imageName->append(str: ".tif");
828 }
829 }
830
831 return imageName;
832}
833
834// If (printing || singleFile) the output file name includes the
835// extension. Otherwise it is the file name base.
836static GooString *getOutputFileName(GooString *fileName, GooString *outputName)
837{
838 GooString *name;
839
840 if (outputName) {
841 if (outputName->cmp(sA: "-") == 0) {
842 if (printToWin32 || (!printing && !singleFile)) {
843 fprintf(stderr, format: "Error: stdout may only be used with the ps, eps, pdf, svg output options or if -singlefile is used.\n");
844 exit(status: 99);
845 }
846 return new GooString("fd://0");
847 }
848 return new GooString(outputName);
849 }
850
851 if (printToWin32) {
852 return nullptr; // No output file means print to printer
853 }
854
855 if (fileName->cmp(sA: "fd://0") == 0) {
856 fprintf(stderr, format: "Error: an output filename or '-' must be supplied when the PDF file is stdin.\n");
857 exit(status: 99);
858 }
859
860 // be careful not to overwrite the input file when the output format is PDF
861 if (pdf && fileName->cmpN(sA: "http://", n: 7) != 0 && fileName->cmpN(sA: "https://", n: 8) != 0) {
862 fprintf(stderr, format: "Error: an output filename or '-' must be supplied when the output format is PDF and input PDF file is a local file.\n");
863 exit(status: 99);
864 }
865
866 // strip everything up to last '/'
867 const char *s = fileName->c_str();
868 const char *p = strrchr(s: s, c: '/');
869 if (p) {
870 p++;
871 if (*p == 0) {
872 fprintf(stderr, format: "Error: invalid output filename.\n");
873 exit(status: 99);
874 }
875 name = new GooString(p);
876 } else {
877 name = new GooString(s);
878 }
879
880 // remove .pdf extension
881 p = strrchr(s: name->c_str(), c: '.');
882 if (p && strcasecmp(s1: p, s2: ".pdf") == 0) {
883 GooString *name2 = new GooString(name->c_str(), name->getLength() - 4);
884 delete name;
885 name = name2;
886 }
887
888 // append new extension
889 if (ps) {
890 name->append(str: ".ps");
891 } else if (eps) {
892 name->append(str: ".eps");
893 } else if (pdf) {
894 name->append(str: ".pdf");
895 } else if (svg) {
896 name->append(str: ".svg");
897 }
898
899 return name;
900}
901
902static void checkInvalidPrintOption(bool option, const char *option_name)
903{
904 if (option) {
905 fprintf(stderr, format: "Error: %s may only be used with the -png, -jpeg, or -tiff output options.\n", option_name);
906 exit(status: 99);
907 }
908}
909
910static void checkInvalidImageOption(bool option, const char *option_name)
911{
912 if (option) {
913 fprintf(stderr, format: "Error: %s may only be used with the -ps, -eps, -pdf, or -svg output options.\n", option_name);
914 exit(status: 99);
915 }
916}
917
918int main(int argc, char *argv[])
919{
920 GooString *fileName = nullptr;
921 GooString *outputName = nullptr;
922 GooString *outputFileName = nullptr;
923 GooString *imageFileName = nullptr;
924 std::optional<GooString> ownerPW, userPW;
925 CairoOutputDev *cairoOut;
926 int pg, pg_num_len;
927 double pg_w, pg_h, tmp, output_w, output_h;
928 int num_outputs;
929
930 // parse args
931 Win32Console win32Console(&argc, &argv);
932 if (!parseArgs(args: argDesc, argc: &argc, argv)) {
933 printUsage(program: "pdftocairo", otherArgs: nullptr, args: argDesc);
934 exit(status: 99);
935 }
936
937 if (resolution != 0.0 && (x_resolution == 150.0 || y_resolution == 150.0)) {
938 x_resolution = resolution;
939 y_resolution = resolution;
940 }
941 if (argc < 2 || argc > 3 || printVersion || printHelp) {
942 fprintf(stderr, format: "pdftocairo version %s\n", PACKAGE_VERSION);
943 fprintf(stderr, format: "%s\n", popplerCopyright);
944 fprintf(stderr, format: "%s\n", xpdfCopyright);
945 if (!printVersion) {
946 printUsage(program: "pdftocairo", otherArgs: "<PDF-file> [<output-file>]", args: argDesc);
947 }
948 if (printVersion || printHelp) {
949 exit(status: 0);
950 } else {
951 exit(status: 99);
952 }
953 }
954
955 num_outputs = (png ? 1 : 0) + (jpeg ? 1 : 0) + (tiff ? 1 : 0) + (ps ? 1 : 0) + (eps ? 1 : 0) + (pdf ? 1 : 0) + (printToWin32 ? 1 : 0) + (printdlg ? 1 : 0) + (svg ? 1 : 0);
956 if (num_outputs == 0) {
957 fprintf(stderr, format: "Error: one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -print, -printdlg, -svg) must be used.\n");
958 exit(status: 99);
959 }
960 if (num_outputs > 1) {
961 fprintf(stderr, format: "Error: use only one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -printdlg, -print, -svg).\n");
962 exit(status: 99);
963 }
964 if (png || jpeg || tiff) {
965 printing = false;
966 } else {
967 printing = true;
968 }
969
970 if (printing) {
971 checkInvalidPrintOption(option: mono, option_name: "-mono");
972 checkInvalidPrintOption(option: gray, option_name: "-gray");
973 checkInvalidPrintOption(option: transp, option_name: "-transp");
974 checkInvalidPrintOption(option: icc.c_str()[0], option_name: "-icc");
975 checkInvalidPrintOption(option: singleFile, option_name: "-singlefile");
976 checkInvalidPrintOption(option: useCropBox, option_name: "-cropbox");
977 checkInvalidPrintOption(option: scaleTo != 0, option_name: "-scale-to");
978 checkInvalidPrintOption(option: x_scaleTo != 0, option_name: "-scale-to-x");
979 checkInvalidPrintOption(option: y_scaleTo != 0, option_name: "-scale-to-y");
980 } else {
981 checkInvalidImageOption(option: level2, option_name: "-level2");
982 checkInvalidImageOption(option: level3, option_name: "-level3");
983 checkInvalidImageOption(option: origPageSizes, option_name: "-origpagesizes");
984 checkInvalidImageOption(option: paperSize[0], option_name: "-paper");
985 checkInvalidImageOption(option: paperWidth > 0, option_name: "-paperw");
986 checkInvalidImageOption(option: paperHeight > 0, option_name: "-paperh");
987 checkInvalidImageOption(option: noCrop, option_name: "-nocrop");
988 checkInvalidImageOption(option: expand, option_name: "-expand");
989 checkInvalidImageOption(option: noShrink, option_name: "-noshrink");
990 checkInvalidImageOption(option: noCenter, option_name: "-nocenter");
991 checkInvalidImageOption(option: duplex, option_name: "-duplex");
992 }
993
994 if (printing) {
995 useCropBox = !noCrop;
996 }
997
998 if (icc.c_str()[0] && !png) {
999 fprintf(stderr, format: "Error: -icc may only be used with png output.\n");
1000 exit(status: 99);
1001 }
1002
1003 if (antialias.getLength() > 0) {
1004 if (!parseAntialiasOption()) {
1005 exit(status: 99);
1006 }
1007 }
1008
1009 if (transp && !(png || tiff)) {
1010 fprintf(stderr, format: "Error: -transp may only be used with png or tiff output.\n");
1011 exit(status: 99);
1012 }
1013
1014 if (mono && gray) {
1015 fprintf(stderr, format: "Error: -mono and -gray may not be used together.\n");
1016 exit(status: 99);
1017 }
1018
1019 if (mono && !(png || tiff)) {
1020 fprintf(stderr, format: "Error: -mono may only be used with png or tiff output.\n");
1021 exit(status: 99);
1022 }
1023
1024 if (jpegOpt.getLength() > 0) {
1025 if (!jpeg) {
1026 fprintf(stderr, format: "Error: -jpegopt may only be used with jpeg output.\n");
1027 exit(status: 99);
1028 }
1029 if (!parseJpegOptions()) {
1030 exit(status: 99);
1031 }
1032 }
1033
1034 if (strlen(s: tiffCompressionStr) > 0 && !tiff) {
1035 fprintf(stderr, format: "Error: -tiffcompression may only be used with tiff output.\n");
1036 exit(status: 99);
1037 }
1038
1039 if (level2 && level3) {
1040 fprintf(stderr, format: "Error: use only one of the 'level' options.\n");
1041 exit(status: 99);
1042 }
1043 if (!level2 && !level3) {
1044 level3 = true;
1045 }
1046
1047 if (docStruct && !pdf) {
1048 fprintf(stderr, format: "Error: -struct may only be used with pdf or output.\n");
1049 exit(status: 99);
1050 }
1051 if (eps && (origPageSizes || paperSize[0] || paperWidth > 0 || paperHeight > 0)) {
1052 fprintf(stderr, format: "Error: page size options may not be used with eps output.\n");
1053 exit(status: 99);
1054 }
1055
1056 if ((paperWidth > 0 && paperHeight <= 0) || (paperWidth <= 0 && paperHeight > 0)) {
1057 fprintf(stderr, format: "Error: both -paperw and -paperh must be specified.\n");
1058 exit(status: 99);
1059 }
1060
1061 if (paperSize[0]) {
1062 if (origPageSizes) {
1063 fprintf(stderr, format: "Error: -origpagesizes and -paper may not be used together.\n");
1064 exit(status: 99);
1065 }
1066 if (!setPSPaperSize(size: paperSize, psPaperWidth&: paperWidth, psPaperHeight&: paperHeight)) {
1067 fprintf(stderr, format: "Invalid paper size\n");
1068 exit(status: 99);
1069 }
1070 }
1071 if (origPageSizes || paperWidth < 0 || paperHeight < 0) {
1072 usePDFPageSize = true;
1073 } else {
1074 usePDFPageSize = false;
1075 }
1076
1077 if (printdlg) {
1078 printToWin32 = true;
1079 }
1080
1081 globalParams = std::make_unique<GlobalParams>();
1082 if (quiet) {
1083 globalParams->setErrQuiet(quiet);
1084 }
1085
1086 // open PDF file
1087 if (ownerPassword[0]) {
1088 ownerPW = GooString(ownerPassword);
1089 }
1090 if (userPassword[0]) {
1091 userPW = GooString(userPassword);
1092 }
1093
1094 fileName = new GooString(argv[1]);
1095 if (fileName->cmp(sA: "-") == 0) {
1096 delete fileName;
1097 fileName = new GooString("fd://0");
1098 }
1099 if (argc == 3) {
1100 outputName = new GooString(argv[2]);
1101 } else {
1102 outputName = nullptr;
1103 }
1104
1105 outputFileName = getOutputFileName(fileName, outputName);
1106
1107#ifdef USE_CMS
1108 icc_data = nullptr;
1109 if (icc.c_str()[0]) {
1110 FILE *file = fopen(icc.c_str(), "rb");
1111 if (!file) {
1112 fprintf(stderr, "Error: unable to open icc profile %s\n", icc.c_str());
1113 exit(4);
1114 }
1115 fseek(file, 0, SEEK_END);
1116 icc_data_size = ftell(file);
1117 fseek(file, 0, SEEK_SET);
1118 icc_data = (unsigned char *)gmalloc(icc_data_size);
1119 if (fread(icc_data, icc_data_size, 1, file) != 1) {
1120 fprintf(stderr, "Error: unable to read icc profile %s\n", icc.c_str());
1121 exit(4);
1122 }
1123 fclose(file);
1124 profile = make_GfxLCMSProfilePtr(cmsOpenProfileFromMem(icc_data, icc_data_size));
1125 if (!profile) {
1126 fprintf(stderr, "Error: lcms error opening profile\n");
1127 exit(4);
1128 }
1129 } else {
1130 profile = make_GfxLCMSProfilePtr(cmsCreate_sRGBProfile());
1131 }
1132#endif
1133
1134 std::unique_ptr<PDFDoc> doc = PDFDocFactory().createPDFDoc(uri: *fileName, ownerPassword: ownerPW, userPassword: userPW);
1135 if (!doc->isOk()) {
1136 fprintf(stderr, format: "Error opening PDF file.\n");
1137 exit(status: 1);
1138 }
1139
1140#ifdef ENFORCE_PERMISSIONS
1141 // check for print permission
1142 if (printing && !doc->okToPrint()) {
1143 fprintf(stderr, "Printing this document is not allowed.\n");
1144 exit(3);
1145 }
1146#endif
1147
1148 // get page range
1149 if (firstPage < 1) {
1150 firstPage = 1;
1151 }
1152 if (singleFile && lastPage < 1) {
1153 lastPage = firstPage;
1154 }
1155 if (lastPage < 1 || lastPage > doc->getNumPages()) {
1156 lastPage = doc->getNumPages();
1157 }
1158
1159 if (lastPage < firstPage) {
1160 fprintf(stderr, format: "Wrong page range given: the first page (%d) can not be after the last page (%d).\n", firstPage, lastPage);
1161 exit(status: 99);
1162 }
1163 if (eps && firstPage != lastPage) {
1164 fprintf(stderr, format: "EPS files can only contain one page.\n");
1165 exit(status: 99);
1166 }
1167
1168 // If our page range selection and document size indicate we're only
1169 // outputting a single page, ensure that even/odd page selection doesn't
1170 // filter out that single page. Also adjust first and last page so there are no pages
1171 // skipped at the start or end of the for loop.
1172 if ((printOnlyEven && firstPage % 2 == 1) || (printOnlyOdd && firstPage % 2 == 0)) {
1173 firstPage++;
1174 }
1175 if ((printOnlyEven && lastPage % 2 == 1) || (printOnlyOdd && lastPage % 2 == 0)) {
1176 lastPage--;
1177 }
1178 if (lastPage < firstPage) {
1179 fprintf(stderr, format: "Invalid even/odd page selection, no pages match criteria.\n");
1180 exit(status: 99);
1181 }
1182
1183 if (singleFile && firstPage < lastPage) {
1184 if (!quiet) {
1185 fprintf(stderr, format: "Warning: Single file will write only the first of the %d pages.\n", lastPage + 1 - firstPage);
1186 }
1187 lastPage = firstPage;
1188 }
1189
1190#ifdef CAIRO_HAS_WIN32_SURFACE
1191 if (printdlg) {
1192 bool allPages = false;
1193 if (firstPage == 1 && lastPage == doc->getNumPages())
1194 allPages = true;
1195 win32ShowPrintDialog(&expand, &noShrink, &noCenter, &usePDFPageSize, &allPages, &firstPage, &lastPage, doc->getNumPages());
1196 if (allPages) {
1197 firstPage = 1;
1198 lastPage = doc->getNumPages();
1199 }
1200 } else if (printToWin32) {
1201 win32SetupPrinter(&printer, &printOpt, duplex, setupdlg);
1202 }
1203#endif
1204
1205 cairoOut = new CairoOutputDev();
1206 cairoOut->setLogicalStructure(docStruct);
1207
1208#ifdef USE_CMS
1209 cairoOut->setDisplayProfile(profile);
1210#endif
1211 cairoOut->startDoc(docA: doc.get());
1212 if (sz != 0) {
1213 crop_w = crop_h = sz;
1214 }
1215 pg_num_len = numberOfCharacters(n: doc->getNumPages());
1216 for (pg = firstPage; pg <= lastPage; ++pg) {
1217 if (printOnlyEven && pg % 2 == 1) {
1218 continue;
1219 }
1220 if (printOnlyOdd && pg % 2 == 0) {
1221 continue;
1222 }
1223 if (useCropBox) {
1224 pg_w = doc->getPageCropWidth(page: pg);
1225 pg_h = doc->getPageCropHeight(page: pg);
1226 } else {
1227 pg_w = doc->getPageMediaWidth(page: pg);
1228 pg_h = doc->getPageMediaHeight(page: pg);
1229 }
1230
1231 if (printing && pg == firstPage) {
1232 if (paperWidth < 0 || paperHeight < 0) {
1233 paperWidth = (int)ceil(x: pg_w);
1234 paperHeight = (int)ceil(x: pg_h);
1235 }
1236 }
1237
1238 if ((doc->getPageRotate(page: pg) == 90) || (doc->getPageRotate(page: pg) == 270)) {
1239 tmp = pg_w;
1240 pg_w = pg_h;
1241 pg_h = tmp;
1242 }
1243 if (scaleTo != 0) {
1244 resolution = (72.0 * scaleTo) / (pg_w > pg_h ? pg_w : pg_h);
1245 x_resolution = y_resolution = resolution;
1246 } else {
1247 if (x_scaleTo > 0) {
1248 x_resolution = (72.0 * x_scaleTo) / pg_w;
1249 if (y_scaleTo == -1) {
1250 y_resolution = x_resolution;
1251 }
1252 }
1253 if (y_scaleTo > 0) {
1254 y_resolution = (72.0 * y_scaleTo) / pg_h;
1255 if (x_scaleTo == -1) {
1256 x_resolution = y_resolution;
1257 }
1258 }
1259 }
1260 if (imageFileName) {
1261 delete imageFileName;
1262 imageFileName = nullptr;
1263 }
1264 if (!printing) {
1265 imageFileName = getImageFileName(outputFileName, numDigits: pg_num_len, page: pg);
1266 }
1267 getOutputSize(page_w: pg_w, page_h: pg_h, width: &output_w, height: &output_h);
1268
1269 if (pg == firstPage) {
1270 beginDocument(inputFileName: fileName, outputFileName, w: output_w, h: output_h);
1271 }
1272 beginPage(w: &output_w, h: &output_h);
1273 renderPage(doc: doc.get(), cairoOut, pg, page_w: pg_w, page_h: pg_h, output_w, output_h);
1274 endPage(imageFileName, cairoOut, isLastPage: pg == lastPage);
1275 }
1276 endDocument();
1277
1278 // clean up
1279 delete cairoOut;
1280 if (fileName) {
1281 delete fileName;
1282 }
1283 if (outputName) {
1284 delete outputName;
1285 }
1286 if (outputFileName) {
1287 delete outputFileName;
1288 }
1289 if (imageFileName) {
1290 delete imageFileName;
1291 }
1292
1293#ifdef USE_CMS
1294 if (icc_data) {
1295 gfree(icc_data);
1296 }
1297#endif
1298
1299 return 0;
1300}
1301

source code of poppler/utils/pdftocairo.cc