1 /*
2 * PDF to PostScript filter front-end for CUPS.
3 *
4 * Copyright 2007-2011 by Apple Inc.
5 * Copyright 1997-2006 by Easy Software Products.
6 * Copyright 2011-2013 by Till Kamppeter
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "COPYING"
11 * which should have been included with this file.
12 *
13 * Contents:
14 *
15 * parsePDFTOPDFComment() - Check whether we are executed after pdftopdf
16 * remove_options() - Remove unwished entries from an option list
17 * log_command_line() - Log the command line of a program which we call
18 * main() - Main entry for filter...
19 * cancel_job() - Flag the job as canceled.
20 */
21
22 /*
23 * Include necessary headers...
24 */
25
26 #include "pdf.h"
27 #include <config.h>
28 #include <cups/cups.h>
29 #include <cups/ppd.h>
30 #include <cups/file.h>
31 #include <signal.h>
32 #include <sys/wait.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <cupsfilters/image-private.h>
38
39 #define MAX_CHECK_COMMENT_LINES 20
40
41 /*
42 * Type definitions
43 */
44
45 typedef unsigned renderer_t;
46 enum renderer_e {GS = 0, PDFTOPS = 1, ACROREAD = 2, PDFTOCAIRO = 3, MUPDF = 4, HYBRID = 5};
47
48 /*
49 * Local functions...
50 */
51
52 static void cancel_job(int sig);
53
54
55 /*
56 * Local globals...
57 */
58
59 static int job_canceled = 0;
60 int pdftopdfapplied = 0;
61 char deviceCopies[32] = "1";
62 int deviceCollate = 0;
63 char make_model[128] = "";
64
65
66 /*
67 * When calling the "pstops" filter we exclude the following options from its
68 * command line as we have applied these options already to the PDF input,
69 * either on the "pdftops"/Ghostscript call in this filter or by use of the
70 * "pdftopdf" filter before this filter.
71 */
72
73 const char *pstops_exclude_general[] = {
74 "crop-to-fit",
75 "fill",
76 "fitplot",
77 "fit-to-page",
78 "landscape",
79 "orientation-requested",
80 NULL
81 };
82
83 const char *pstops_exclude_page_management[] = {
84 "brightness",
85 "Collate",
86 "cupsEvenDuplex",
87 "gamma",
88 "hue",
89 "ipp-attribute-fidelity",
90 "MirrorPrint",
91 "mirror",
92 "multiple-document-handling",
93 "natural-scaling",
94 "number-up",
95 "number-up-layout",
96 "OutputOrder",
97 "page-border",
98 "page-bottom",
99 "page-label",
100 "page-left",
101 "page-ranges",
102 "page-right",
103 "page-set",
104 "page-top",
105 "position",
106 "saturation",
107 "scaling",
108 NULL
109 };
110
111
112 /*
113 * Check whether we were called after the "pdftopdf" filter and extract
114 * parameters passed over by "pdftopdf" in the header comments of the PDF
115 * file
116 */
117
parsePDFTOPDFComment(char * filename)118 static void parsePDFTOPDFComment(char *filename)
119 {
120 char buf[4096];
121 int i;
122 FILE *fp;
123
124 if ((fp = fopen(filename,"rb")) == 0) {
125 fprintf(stderr, "ERROR: pdftops - cannot open print file \"%s\"\n",
126 filename);
127 return;
128 }
129
130 /* skip until PDF start header */
131 while (fgets(buf,sizeof(buf),fp) != 0) {
132 if (strncmp(buf,"%PDF",4) == 0) {
133 break;
134 }
135 }
136 for (i = 0;i < MAX_CHECK_COMMENT_LINES;i++) {
137 if (fgets(buf,sizeof(buf),fp) == 0) break;
138 if (strncmp(buf,"%%PDFTOPDFNumCopies",19) == 0) {
139 char *p;
140
141 p = strchr(buf+19,':') + 1;
142 while (*p == ' ' || *p == '\t') p++;
143 strncpy(deviceCopies, p, sizeof(deviceCopies));
144 deviceCopies[sizeof(deviceCopies) - 1] = '\0';
145 p = deviceCopies + strlen(deviceCopies) - 1;
146 while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') p--;
147 *(p + 1) = '\0';
148 pdftopdfapplied = 1;
149 } else if (strncmp(buf,"%%PDFTOPDFCollate",17) == 0) {
150 char *p;
151
152 p = strchr(buf+17,':') + 1;
153 while (*p == ' ' || *p == '\t') p++;
154 if (strncasecmp(p,"true",4) == 0) {
155 deviceCollate = 1;
156 } else {
157 deviceCollate = 0;
158 }
159 pdftopdfapplied = 1;
160 } else if (strcmp(buf,"% This file was generated by pdftopdf") == 0) {
161 pdftopdfapplied = 1;
162 }
163 }
164
165 fclose(fp);
166 }
167
168
169 /*
170 * Remove all options in option_list from the string option_str, including
171 * option values after an "=" sign and preceded "no" before boolean options
172 */
173
remove_options(char * options_str,const char ** option_list)174 void remove_options(char *options_str, const char **option_list)
175 {
176 const char **option; /* Option to be removed now */
177 char *option_start, /* Start of option in string */
178 *option_end; /* End of option in string */
179
180 for (option = option_list; *option; option ++)
181 {
182 option_start = options_str;
183
184 while ((option_start = strcasestr(option_start, *option)) != NULL)
185 {
186 if (!option_start[strlen(*option)] ||
187 isspace(option_start[strlen(*option)] & 255) ||
188 option_start[strlen(*option)] == '=')
189 {
190 /*
191 * Strip option...
192 */
193
194 option_end = option_start + strlen(*option);
195
196 /* Remove preceding "no" of boolean option */
197 if ((option_start - options_str) >= 2 &&
198 !strncasecmp(option_start - 2, "no", 2))
199 option_start -= 2;
200
201 /* Is match of the searched option name really at the beginning of
202 the name of the option in the command line? */
203 if ((option_start > options_str) &&
204 (!isspace(*(option_start - 1) & 255)))
205 {
206 /* Prevent the same option to be found again. */
207 option_start += 1;
208 /* Skip */
209 continue;
210 }
211
212 /* Remove "=" and value */
213 while (*option_end && !isspace(*option_end & 255))
214 option_end ++;
215
216 /* Remove spaces up to next option */
217 while (*option_end && isspace(*option_end & 255))
218 option_end ++;
219
220 memmove(option_start, option_end, strlen(option_end) + 1);
221 } else {
222 /* Prevent the same option to be found again. */
223 option_start += 1;
224 }
225 }
226 }
227 }
228
229
230 /*
231 * Check whether given file is empty
232 */
233
is_empty(char * filename)234 int is_empty(char *filename)
235 {
236 FILE *fp = NULL;
237 fp = fopen(filename, "rb");
238 if (fp == NULL)
239 {
240 fprintf(stderr, "ERROR: pdftops - cannot open print file \"%s\"\n",
241 filename);
242 exit(1);
243 }
244 else
245 {
246 char buf[1];
247 rewind(fp);
248 if (fread(buf, 1, 1, fp) == 0) {
249 fclose(fp);
250 fprintf(stderr, "DEBUG: Input is empty, outputting empty file.\n");
251 return 1;
252 }
253 fclose(fp);
254 int pages = pdf_pages(filename);
255 if (pages == 0) {
256 fprintf(stderr, "DEBUG: No pages left, outputting empty file.\n");
257 return 1;
258 }
259 if (pages > 0)
260 return 0;
261 exit(1);
262 }
263 }
264
265
266 /*
267 * Before calling any command line utility, log its command line in CUPS'
268 * debug mode
269 */
270
271 void
log_command_line(const char * file,char * const argv[])272 log_command_line(const char* file, char *const argv[])
273 {
274 int i;
275 char *apos;
276
277 /* Debug output: Full command line of program to be called */
278 fprintf(stderr, "DEBUG: Running command line for %s:",
279 (file ? file : argv[0]));
280 if (file)
281 fprintf(stderr, " %s", file);
282 for (i = (file ? 1 : 0); argv[i]; i ++) {
283 if ((strchr(argv[i],' ')) || (strchr(argv[i],'\t')))
284 apos = "'";
285 else
286 apos = "";
287 fprintf(stderr, " %s%s%s", apos, argv[i], apos);
288 }
289 fprintf(stderr, "\n");
290 }
291
292
293 /*
294 * 'main()' - Main entry for filter...
295 */
296
297 int /* O - Exit status */
main(int argc,char * argv[])298 main(int argc, /* I - Number of command-line args */
299 char *argv[]) /* I - Command-line arguments */
300 {
301 renderer_t renderer = CUPS_PDFTOPS_RENDERER; /* Renderer: gs or pdftops or acroread or pdftocairo or hybrid */
302 int fd = 0; /* Copy file descriptor */
303 char *filename, /* PDF file to convert */
304 tempfile[1024]; /* Temporary file */
305 char buffer[8192]; /* Copy buffer */
306 int bytes; /* Bytes copied */
307 int num_options; /* Number of options */
308 cups_option_t *options; /* Options */
309 const char *val; /* Option value */
310 ppd_file_t *ppd; /* PPD file */
311 char resolution[128] = ""; /* Output resolution */
312 int xres = 0, yres = 0, /* resolution values */
313 mres, res,
314 maxres = CUPS_PDFTOPS_MAX_RESOLUTION,
315 /* Maximum image rendering resolution */
316 numvalues; /* Number of values actually read */
317 ppd_choice_t *choice;
318 ppd_attr_t *attr;
319 cups_page_header2_t header;
320 cups_file_t *fp; /* Post-processing input file */
321 int pdf_pid, /* Process ID for pdftops/gs */
322 pdf_argc = 0, /* Number of args for pdftops/gs */
323 pstops_pid, /* Process ID of pstops filter */
324 pstops_pipe[2], /* Pipe to pstops filter */
325 need_post_proc = 0, /* Post-processing needed? */
326 post_proc_pid = 0, /* Process ID of post-processing */
327 post_proc_pipe[2], /* Pipe to post-processing */
328 wait_children, /* Number of child processes left */
329 wait_pid, /* Process ID from wait() */
330 wait_status, /* Status from child */
331 exit_status = 0; /* Exit status */
332 int gray_output = 0; /* Checking for monochrome/grayscale PostScript output */
333 char *pdf_argv[100], /* Arguments for pdftops/gs */
334 pstops_path[1024], /* Path to pstops program */
335 *pstops_argv[7], /* Arguments for pstops filter */
336 *pstops_options, /* Options for pstops filter */
337 *pstops_end, /* End of pstops filter option */
338 *ptr; /* Pointer into value */
339 const char *cups_serverbin; /* CUPS_SERVERBIN environment
340 variable */
341 int duplex, tumble; /* Duplex settings for PPD-less
342 printing */
343 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
344 struct sigaction action; /* Actions for POSIX signals */
345 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
346
347
348 /*
349 * Make sure status messages are not buffered...
350 */
351
352 setbuf(stderr, NULL);
353
354 /*
355 * Ignore broken pipe signals...
356 */
357
358 signal(SIGPIPE, SIG_IGN);
359
360 /*
361 * Make sure we have the right number of arguments for CUPS!
362 */
363
364 if (argc < 6 || argc > 7)
365 {
366 fprintf(stderr, "Usage: %s job user title copies options [file]\n",
367 argv[0]);
368 return (1);
369 }
370
371 /*
372 * Register a signal handler to cleanly cancel a job.
373 */
374
375 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
376 sigset(SIGTERM, cancel_job);
377 #elif defined(HAVE_SIGACTION)
378 memset(&action, 0, sizeof(action));
379
380 sigemptyset(&action.sa_mask);
381 action.sa_handler = cancel_job;
382 sigaction(SIGTERM, &action, NULL);
383 #else
384 signal(SIGTERM, cancel_job);
385 #endif /* HAVE_SIGSET */
386
387 /*
388 * Copy stdin if needed...
389 */
390
391 if (argc == 6)
392 {
393 /*
394 * Copy stdin to a temp file...
395 */
396
397 if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
398 {
399 perror("DEBUG: Unable to copy PDF file");
400 return (1);
401 }
402
403 fprintf(stderr, "DEBUG: pdftops - copying to temp print file \"%s\"\n",
404 tempfile);
405
406 while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
407 bytes = write(fd, buffer, bytes);
408
409 close(fd);
410
411 filename = tempfile;
412 }
413 else
414 {
415 /*
416 * Use the filename on the command-line...
417 */
418
419 filename = argv[6];
420 tempfile[0] = '\0';
421 }
422
423 if (is_empty(filename))
424 return 0;
425
426 /*
427 * Read out copy counts and collate setting passed over by pdftopdf
428 */
429
430 parsePDFTOPDFComment(filename);
431
432 /*
433 * Read out the options from the fifth command line argument
434 */
435
436 num_options = cupsParseOptions(argv[5], 0, &options);
437
438 /*
439 * Load the PPD file and mark options...
440 */
441
442 ppd = ppdOpenFile(getenv("PPD"));
443 if (ppd)
444 {
445 ppdMarkDefaults(ppd);
446 cupsMarkOptions(ppd, num_options, options);
447 }
448
449 if ((val = cupsGetOption("make-and-model", num_options, options)) != NULL)
450 {
451 strncpy(make_model, val, sizeof(make_model) - 1);
452 if (strlen(val) > 127)
453 make_model[127] = '\0';
454 for (ptr = make_model; *ptr; ptr ++)
455 if (*ptr == '-') *ptr = ' ';
456 }
457 else if (ppd)
458 {
459 snprintf(make_model, sizeof(make_model), "%s %s", ppd->manufacturer,
460 ppd->product + 1);
461 make_model[strlen(make_model) - 1] = '\0';
462 }
463 fprintf(stderr, "DEBUG: Printer make and model: %s\n", make_model);
464
465 /*
466 * Select the PDF renderer: Ghostscript (gs), Poppler (pdftops),
467 * Adobe Reader (arcoread), Poppler with Cairo (pdftocairo), or
468 * Hybrid (hybrid, Poppler for Brother, Minolta, Konica Minolta, Dell, and
469 * old HP LaserJets and Ghostscript otherwise)
470 */
471
472 if ((val = cupsGetOption("pdftops-renderer", num_options, options)) != NULL)
473 {
474 if (strcasecmp(val, "gs") == 0)
475 renderer = GS;
476 else if (strcasecmp(val, "pdftops") == 0)
477 renderer = PDFTOPS;
478 else if (strcasecmp(val, "acroread") == 0)
479 renderer = ACROREAD;
480 else if (strcasecmp(val, "pdftocairo") == 0)
481 renderer = PDFTOCAIRO;
482 else if (strcasecmp(val, "mupdf") == 0)
483 renderer = MUPDF;
484 else if (strcasecmp(val, "hybrid") == 0)
485 renderer = HYBRID;
486 else
487 fprintf(stderr,
488 "WARNING: Invalid value for \"pdftops-renderer\": \"%s\"\n", val);
489 }
490
491 if (renderer == HYBRID)
492 {
493 if (make_model[0] &&
494 (!strncasecmp(make_model, "Brother", 7) ||
495 !strncasecmp(make_model, "Dell", 4) ||
496 strcasestr(make_model, "Minolta") ||
497 (!strncasecmp(make_model, "Apple", 5) &&
498 (ptr = strcasestr(make_model, "LaserWriter")))))
499 {
500 fprintf(stderr, "DEBUG: Switching to Poppler's pdftops instead of Ghostscript for Brother, Minolta, Konica Minolta, Dell, and Apple LaserWriter printers to work around bugs in the printer's PS interpreters\n");
501 renderer = PDFTOPS;
502 }
503 else
504 renderer = GS;
505 /*
506 * Use Poppler instead of Ghostscript for old HP LaserJet printers due to
507 * a bug in their PS interpreters. They are very slow with Ghostscript.
508 * A LaserJet is considered old if its model number does not have a letter
509 * in the beginning, like LaserJet 3 or LaserJet 4000, not LaserJet P2015.
510 * See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=742765
511 */
512 if (make_model[0] &&
513 ((!strncasecmp(make_model, "HP", 2) ||
514 !strncasecmp(make_model, "Hewlett-Packard", 15) ||
515 !strncasecmp(make_model, "Hewlett Packard", 15)) &&
516 (ptr = strcasestr(make_model, "LaserJet"))))
517 {
518 for (ptr += 8; *ptr; ptr ++)
519 {
520 if (isspace(*ptr)) continue;
521 if (isdigit(*ptr))
522 {
523 while (*ptr && isalnum(*ptr)) ptr ++;
524 if (!*ptr) /* End of string, no further word */
525 {
526 fprintf(stderr, "DEBUG: Switching to Poppler's pdftops instead of Ghostscript for old HP LaserJet (\"LaserJet <number>\", no letters before <number>, no additional words after <number>) printers to work around bugs in the printer's PS interpreters\n");
527 renderer = PDFTOPS;
528 }
529 }
530 break;
531 }
532 }
533 }
534
535 /*
536 * Build the pstops command-line...
537 */
538
539 if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
540 cups_serverbin = CUPS_SERVERBIN;
541
542 snprintf(pstops_path, sizeof(pstops_path), "%s/filter/pstops",
543 cups_serverbin);
544
545 pstops_options = strdup(argv[5]);
546
547 /*
548 * Strip options which "pstops" does not need to apply any more
549 */
550 remove_options(pstops_options, pstops_exclude_general);
551 if (pdftopdfapplied)
552 remove_options(pstops_options, pstops_exclude_page_management);
553
554 if (pdftopdfapplied && deviceCollate)
555 {
556 /*
557 * Add collate option to the pstops call if pdftopdf has found out that the
558 * printer does hardware collate.
559 */
560
561 pstops_options = realloc(pstops_options, strlen(pstops_options) + 9);
562 if (!pstops_options) {
563 fprintf(stderr, "ERROR: Can't allocate pstops_options\n");
564 exit(2);
565 }
566 pstops_end = pstops_options + strlen(pstops_options);
567 strcpy(pstops_end, " Collate");
568 }
569
570 pstops_argv[0] = argv[0]; /* Printer */
571 pstops_argv[1] = argv[1]; /* Job */
572 pstops_argv[2] = argv[2]; /* User */
573 pstops_argv[3] = argv[3]; /* Title */
574 if (pdftopdfapplied)
575 pstops_argv[4] = deviceCopies; /* Copies */
576 else
577 pstops_argv[4] = argv[4]; /* Copies */
578 pstops_argv[5] = pstops_options; /* Options */
579 pstops_argv[6] = NULL;
580
581 log_command_line("pstops", pstops_argv);
582
583 /*
584 * Force monochrome/grayscale PostScript output
585 * if job is to be printed in monochrome/grayscale
586 */
587 if (ppd && ppd->color_device == 0) /* Monochrome printer */
588 gray_output = 1;
589 else /*Color Printer - user option for Grayscale */
590 {
591 if ((val = cupsGetOption("pwg-raster-document-type", num_options,
592 options)) != NULL ||
593 (val = cupsGetOption("PwgRasterDocumentType", num_options,
594 options)) != NULL ||
595 (val = cupsGetOption("print-color-mode", num_options,
596 options)) != NULL ||
597 (val = cupsGetOption("PrintColorMode", num_options,
598 options)) != NULL ||
599 (val = cupsGetOption("color-space", num_options,
600 options)) != NULL ||
601 (val = cupsGetOption("ColorSpace", num_options,
602 options)) != NULL ||
603 (val = cupsGetOption("color-model", num_options,
604 options)) != NULL ||
605 (val = cupsGetOption("ColorModel", num_options,
606 options)) != NULL ||
607 (val = cupsGetOption("output-mode", num_options,
608 options)) != NULL ||
609 (val = cupsGetOption("OutputMode", num_options,
610 options)) != NULL)
611 {
612 if (strcasestr(val, "Black") ||
613 strcasestr(val, "Gray") ||
614 strcasestr(val, "Mono"))
615 gray_output = 1;
616 }
617 }
618
619 /*
620 * Build the command-line for the pdftops, gs, mutool, pdftocairo, or
621 * acroread filter...
622 */
623
624 if (renderer == PDFTOPS)
625 {
626 pdf_argv[0] = (char *)"pdftops";
627 pdf_argc = 1;
628 }
629 else if (renderer == GS)
630 {
631 pdf_argv[0] = (char *)"gs";
632 pdf_argv[1] = (char *)"-q";
633 pdf_argv[2] = (char *)"-dNOPAUSE";
634 pdf_argv[3] = (char *)"-dBATCH";
635 pdf_argv[4] = (char *)"-dSAFER";
636 pdf_argv[5] = (char *)"-dNOMEDIAATTRS";
637 pdf_argv[6] = (char *)"-sstdout=%stderr";
638 # ifdef HAVE_GHOSTSCRIPT_PS2WRITE
639 pdf_argv[7] = (char *)"-sDEVICE=ps2write";
640 # else
641 pdf_argv[7] = (char *)"-sDEVICE=pswrite";
642 # endif /* HAVE_GHOSTSCRIPT_PS2WRITE */
643 pdf_argv[8] = (char *)"-dShowAcroForm";
644 pdf_argv[9] = (char *)"-sOUTPUTFILE=%stdout";
645 if (gray_output == 1) /* Checking for monochrome/grayscale PostScript
646 output */
647 {
648 pdf_argv[10] = (char *)"-sProcessColorModel=DeviceGray";
649 pdf_argv[11] = (char *)"-sColorConversionStrategy=Gray";
650 pdf_argc = 12;
651 }
652 else
653 pdf_argc = 10;
654 }
655 else if (renderer == MUPDF)
656 {
657 pdf_argv[0] = (char *)"mutool";
658 pdf_argv[1] = (char *)"draw";
659 pdf_argv[2] = (char *)"-L";
660 pdf_argv[3] = (char *)"-smtf";
661 pdf_argv[4] = (char *)"-Fps";
662 pdf_argv[5] = (char *)"-o-";
663 if (gray_output == 1) /* Checking for monochrome/grayscale PostScript
664 output */
665 pdf_argv[6] = (char *)"-cgray";
666 else
667 pdf_argv[6] = (char *)"-crgb";
668 pdf_argc = 7;
669 }
670 else if (renderer == PDFTOCAIRO)
671 {
672 pdf_argv[0] = (char *)"pdftocairo";
673 pdf_argv[1] = (char *)"-ps";
674 pdf_argc = 2;
675 }
676 else if (renderer == ACROREAD)
677 {
678 pdf_argv[0] = (char *)"acroread";
679 pdf_argv[1] = (char *)"-toPostScript";
680 pdf_argc = 2;
681 }
682
683 /*
684 * Set language level and TrueType font handling...
685 */
686
687 if (ppd)
688 {
689 if (ppd->language_level == 1)
690 {
691 if (renderer == PDFTOPS)
692 {
693 pdf_argv[pdf_argc++] = (char *)"-level1";
694 pdf_argv[pdf_argc++] = (char *)"-noembtt";
695 }
696 else if (renderer == GS)
697 pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=1";
698 else if (renderer == PDFTOCAIRO)
699 fprintf(stderr, "WARNING: Level 1 PostScript not supported by pdftocairo.\n");
700 else if (renderer == ACROREAD)
701 fprintf(stderr, "WARNING: Level 1 PostScript not supported by acroread.\n");
702 else if (renderer == MUPDF)
703 fprintf(stderr, "WARNING: Level 1 PostScript not supported by mutool.\n");
704 }
705 else if (ppd->language_level == 2)
706 {
707 if (renderer == PDFTOPS)
708 {
709 pdf_argv[pdf_argc++] = (char *)"-level2";
710 if (!ppd->ttrasterizer)
711 pdf_argv[pdf_argc++] = (char *)"-noembtt";
712 }
713 else if (renderer == GS)
714 pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=2";
715 else if (renderer != MUPDF) /* MuPDF is PS level 2 only */
716 /* PDFTOCAIRO, ACROREAD */
717 pdf_argv[pdf_argc++] = (char *)"-level2";
718 }
719 else
720 {
721 if (renderer == PDFTOPS)
722 {
723 /* Do not emit PS Level 3 with Poppler on Brother and HP PostScript
724 laser printers as some do not like it.
725 See https://bugs.launchpad.net/bugs/277404 and
726 https://bugs.launchpad.net/bugs/1306849 comment #42. */
727 if (!make_model[0] ||
728 !strncasecmp(make_model, "Brother", 7) ||
729 ((!strncasecmp(make_model, "HP", 2) ||
730 !strncasecmp(make_model, "Hewlett-Packard", 15) ||
731 !strncasecmp(make_model, "Hewlett Packard", 15)) &&
732 (strcasestr(make_model, "LaserJet"))))
733 pdf_argv[pdf_argc++] = (char *)"-level2";
734 else
735 pdf_argv[pdf_argc++] = (char *)"-level3";
736 }
737 else if (renderer == GS)
738 {
739 /* Do not emit PS Level 3 with Ghostscript on Brother PostScript
740 laser printers as some do not like it.
741 See https://bugs.launchpad.net/bugs/1306849 comment #42. */
742 if (!make_model[0] ||
743 !strncasecmp(make_model, "Brother", 7))
744 pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=2";
745 else
746 pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=3";
747 }
748 else if (renderer == MUPDF)
749 fprintf(stderr, "WARNING: Level 3 PostScript not supported by mutool.\n");
750 else /* PDFTOCAIRO || ACROREAD */
751 pdf_argv[pdf_argc++] = (char *)"-level3";
752 }
753 }
754 else
755 {
756 if (renderer == PDFTOPS)
757 {
758 /* Do not emit PS Level 3 with Poppler on HP PostScript laser printers
759 as some do not like it. See https://bugs.launchpad.net/bugs/277404.*/
760 if (!make_model[0] ||
761 ((!strncasecmp(make_model, "HP", 2) ||
762 !strncasecmp(make_model, "Hewlett-Packard", 15) ||
763 !strncasecmp(make_model, "Hewlett Packard", 15)) &&
764 (strcasestr(make_model, "LaserJet"))))
765 pdf_argv[pdf_argc++] = (char *)"-level2";
766 else
767 pdf_argv[pdf_argc++] = (char *)"-level3";
768 pdf_argv[pdf_argc++] = (char *)"-noembtt";
769 }
770 else if (renderer == GS)
771 pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=3";
772 else if (renderer != MUPDF) /* MuPDF is PS level 2 only */
773 /* PDFTOCAIRO || ACROREAD */
774 pdf_argv[pdf_argc++] = (char *)"-level3";
775 }
776
777 #ifdef HAVE_POPPLER_PDFTOPS_WITH_ORIGPAGESIZES
778 if ((renderer == PDFTOPS) || (renderer == PDFTOCAIRO))
779 {
780 /*
781 * Use the page sizes of the original PDF document, this way documents
782 * which contain pages of different sizes can be printed correctly
783 */
784
785 pdf_argv[pdf_argc++] = (char *)"-origpagesizes";
786 pdf_argv[pdf_argc++] = (char *)"-nocenter";
787 }
788 else
789 #endif /* HAVE_POPPLER_PDFTOPS_WITH_ORIGPAGESIZES */
790 if (renderer == ACROREAD)
791 {
792 /*
793 * Use the page sizes of the original PDF document, this way documents
794 * which contain pages of different sizes can be printed correctly
795 */
796
797 pdf_argv[pdf_argc++] = (char *)"-choosePaperByPDFPageSize";
798 }
799
800 /*
801 * Set output resolution ...
802 */
803
804 if (ppd)
805 {
806 /* Ignore error exits of cupsRasterInterpretPPD(), if it found a resolution
807 setting before erroring it is OK for us */
808 cupsRasterInterpretPPD(&header, ppd, num_options, options, NULL);
809 /* 100 dpi is default, this means that if we have 100 dpi here this
810 method failed to find the printing resolution */
811 if (header.HWResolution[0] > 100 && header.HWResolution[1] > 100)
812 {
813 xres = header.HWResolution[0];
814 yres = header.HWResolution[1];
815 }
816 else if ((choice = ppdFindMarkedChoice(ppd, "Resolution")) != NULL)
817 strncpy(resolution, choice->choice, sizeof(resolution));
818 else if ((attr = ppdFindAttr(ppd,"DefaultResolution",NULL)) != NULL)
819 strncpy(resolution, attr->value, sizeof(resolution));
820 resolution[sizeof(resolution)-1] = '\0';
821 if ((xres == 0) && (yres == 0) &&
822 ((numvalues = sscanf(resolution, "%dx%d", &xres, &yres)) <= 0))
823 fprintf(stderr,
824 "DEBUG: No resolution information found in the PPD file.\n");
825 }
826 if ((xres == 0) && (yres == 0))
827 {
828 if ((val = cupsGetOption("printer-resolution", num_options,
829 options)) != NULL ||
830 (val = cupsGetOption("Resolution", num_options, options)) != NULL)
831 {
832 xres = yres = strtol(val, (char **)&ptr, 10);
833 if (ptr > val && xres > 0)
834 {
835 if (*ptr == 'x')
836 yres = strtol(ptr + 1, (char **)&ptr, 10);
837 }
838
839 if (ptr <= val || xres <= 0 || yres <= 0 || !ptr ||
840 (*ptr != '\0' &&
841 strcasecmp(ptr, "dpi") &&
842 strcasecmp(ptr, "dpc") &&
843 strcasecmp(ptr, "dpcm")))
844 {
845 fprintf(stderr, "DEBUG: Bad resolution value \"%s\".\n", val);
846 }
847 else
848 {
849 if (!strcasecmp(ptr, "dpc") ||
850 !strcasecmp(ptr, "dpcm"))
851 {
852 xres = xres * 254 / 100;
853 yres = yres * 254 / 100;
854 }
855 }
856 }
857 }
858 if ((xres > 0) || (yres > 0))
859 {
860 if (yres == 0) yres = xres;
861 if (xres == 0) xres = yres;
862 if (xres > yres)
863 res = yres;
864 else
865 res = xres;
866 }
867 else
868 res = 300;
869
870 /*
871 * Get the ceiling for the image rendering resolution
872 */
873
874 if ((val = cupsGetOption("pdftops-max-image-resolution", num_options, options)) != NULL)
875 {
876 if ((numvalues = sscanf(val, "%d", &mres)) > 0)
877 maxres = mres;
878 else
879 fprintf(stderr,
880 "WARNING: Invalid value for \"pdftops-max-image-resolution\": \"%s\"\n", val);
881 }
882
883 /*
884 * Reduce the image rendering resolution to not exceed a given maximum
885 * to make processing of jobs by the PDF->PS converter and the printer faster
886 *
887 * maxres = 0 means no limit
888 */
889
890 if (maxres)
891 while (res > maxres)
892 res = res / 2;
893
894 if ((renderer == PDFTOPS) || (renderer == PDFTOCAIRO))
895 {
896 #ifdef HAVE_POPPLER_PDFTOPS_WITH_RESOLUTION
897 /*
898 * Set resolution to avoid slow processing by the printer when the
899 * resolution of embedded images does not match the printer's resolution
900 */
901 pdf_argv[pdf_argc++] = (char *)"-r";
902 snprintf(resolution, sizeof(resolution), "%d", res);
903 pdf_argv[pdf_argc++] = resolution;
904 fprintf(stderr, "DEBUG: Using image rendering resolution %d dpi\n", res);
905 #endif /* HAVE_POPPLER_PDFTOPS_WITH_RESOLUTION */
906 if (gray_output == 1) /* Checking for monochrome/grayscale PostScript output */
907 {
908 /* Poppler does not explicitly support turning colored PDF files into
909 grayscale PostScript. As a workaround, one could let the "pdftops"
910 command line utility generate PostScript level 1 output which is
911 always grayscale, but output files get huge and printing too
912 slow.
913 Recommended solution is to not use Poppler as PDF renderer for
914 printing, especially if one uses a color PostScript printer and
915 wants to have the possibility to print jobs also in grayscale.
916 See the use of the "pdftops-renderer" option in the README file. */
917 /* Example code for PostScript level1 workaround: */
918 /* pdf_argv[1] = (char *)"-level1";
919 pdf_argv[pdf_argc++] = (char *)"-optimizecolorspace"; */
920 /* Issue a warning message when printing a grayscale job with Poppler */
921 fprintf(stderr, "WARNING: Grayscale/monochrome printing requested for this job but Poppler is not able to convert to grayscale/monochrome PostScript.\n");
922 fprintf(stderr, "WARNING: Use \"pdftops-renderer\" option (see cups-filters README file) to use Ghostscript or MuPDF for the PDF -> PostScript conversion.\n");
923 }
924 pdf_argv[pdf_argc++] = filename;
925 pdf_argv[pdf_argc++] = (char *)"-";
926 }
927 else if (renderer == GS)
928 {
929 /*
930 * Set resolution to avoid slow processing by the printer when the
931 * resolution of embedded images does not match the printer's resolution
932 */
933 snprintf(resolution, 127, "-r%d", res);
934 pdf_argv[pdf_argc++] = resolution;
935 fprintf(stderr, "DEBUG: Using image rendering resolution %d dpi\n", res);
936 /*
937 * PostScript debug mode: If you send a job with "lpr -o psdebug" Ghostscript
938 * will not compress the pages, so that the PostScript code can get
939 * analysed. This is especially important if a PostScript printer errors or
940 * misbehaves on Ghostscript's output.
941 * On Kyocera and Utax (uses Kyocera hard- and software) printers we always
942 * suppress page compression, to avoid slow processing of raster images.
943 */
944 val = cupsGetOption("psdebug", num_options, options);
945 if ((val && strcasecmp(val, "no") && strcasecmp(val, "off") &&
946 strcasecmp(val, "false")) ||
947 (make_model[0] &&
948 (!strncasecmp(make_model, "Kyocera", 7) ||
949 !strncasecmp(make_model, "Utax", 4))))
950 {
951 fprintf(stderr, "DEBUG: Deactivated compression of pages in Ghostscript's PostScript output (\"psdebug\" debug mode or Kyocera/Utax printer)\n");
952 pdf_argv[pdf_argc++] = (char *)"-dCompressPages=false";
953 }
954 /*
955 * The PostScript interpreters on many printers have bugs which make
956 * the interpreter crash, error out, or otherwise misbehave on too
957 * heavily compressed input files, especially if code with compressed
958 * elements is compressed again. Therefore we reduce compression here.
959 */
960 pdf_argv[pdf_argc++] = (char *)"-dCompressFonts=false";
961 pdf_argv[pdf_argc++] = (char *)"-dNoT3CCITT";
962 if (make_model[0] &&
963 !strncasecmp(make_model, "Brother", 7))
964 {
965 fprintf(stderr, "DEBUG: Deactivation of Ghostscript's image compression for Brother printers to workarounmd PS interpreter bug\n");
966 pdf_argv[pdf_argc++] = (char *)"-dEncodeMonoImages=false";
967 pdf_argv[pdf_argc++] = (char *)"-dEncodeColorImages=false";
968 }
969 /*
970 * Toshiba's PS interpreters have an issue with how we handle
971 * TrueType/Type42 fonts, therefore we add command line options to turn
972 * the TTF outlines into bitmaps, usually Type 3 PostScript fonts, only
973 * large glyphs into normal image data.
974 * See https://bugs.launchpad.net/bugs/978120
975 */
976 if (make_model[0] &&
977 !strncasecmp(make_model, "Toshiba", 7))
978 {
979 fprintf(stderr, "DEBUG: To work around a bug in Toshiba's PS interpreters turn TTF font glyphs into bitmaps, usually Type 3 PS fonts, or images for large characters\n");
980 pdf_argv[pdf_argc++] = (char *)"-dHaveTrueTypes=false";
981 }
982 pdf_argv[pdf_argc++] = (char *)"-dNOINTERPOLATE";
983 pdf_argv[pdf_argc++] = (char *)"-c";
984 if (make_model[0] &&
985 !strncasecmp(make_model, "Toshiba", 7))
986 pdf_argv[pdf_argc++] = (char *)"<< /MaxFontItem 500000 >> setuserparams";
987 pdf_argv[pdf_argc++] = (char *)"save pop";
988 pdf_argv[pdf_argc++] = (char *)"-f";
989 pdf_argv[pdf_argc++] = filename;
990 }
991 else if (renderer == MUPDF)
992 {
993 /*
994 * Add Resolution option to avoid slow processing by the printer when the
995 * resolution of embedded images does not match the printer's resolution
996 */
997 snprintf(resolution, 127, "-r%dx%d", res, res);
998 pdf_argv[pdf_argc++] = resolution;
999 /*
1000 * Add input file name
1001 */
1002 pdf_argv[pdf_argc++] = filename;
1003 }
1004
1005 pdf_argv[pdf_argc] = NULL;
1006
1007 log_command_line(NULL, pdf_argv);
1008
1009 /*
1010 * Do we need post-processing of the PostScript output to work around bugs
1011 * of the printer's PostScript interpreter?
1012 */
1013
1014 if ((renderer == PDFTOPS) || (renderer == PDFTOCAIRO) ||
1015 (renderer == MUPDF))
1016 need_post_proc = 0;
1017 else if (renderer == GS)
1018 need_post_proc =
1019 (make_model[0] &&
1020 (!strncasecmp(make_model, "Kyocera", 7) ||
1021 !strncasecmp(make_model, "Utax", 4) ||
1022 !strncasecmp(make_model, "Brother", 7)) ? 1 : 0);
1023 else
1024 need_post_proc = 1;
1025
1026 /*
1027 * Do we need post-processing of the PostScript output to apply option
1028 * settings when doing PPD-less printing?
1029 */
1030
1031 if (!ppd)
1032 need_post_proc = 1;
1033
1034 /*
1035 * Execute "pdftops/gs/mutool [ | PS post-processing ] | pstops"...
1036 */
1037
1038
1039 /*
1040 * Create a pipe for each pair of subsequent processes. The variables
1041 * are named by the receiving process.
1042 */
1043
1044 if (pipe(pstops_pipe))
1045 {
1046 perror("DEBUG: Unable to create pipe for pstops");
1047
1048 exit_status = 1;
1049 goto error;
1050 }
1051
1052 if (need_post_proc)
1053 {
1054 if (pipe(post_proc_pipe))
1055 {
1056 perror("DEBUG: Unable to create pipe for post-processing");
1057
1058 exit_status = 1;
1059 goto error;
1060 }
1061 }
1062
1063 if ((pdf_pid = fork()) == 0)
1064 {
1065 /*
1066 * Child comes here...
1067 */
1068
1069 if (need_post_proc)
1070 {
1071 dup2(post_proc_pipe[1], 1);
1072 close(post_proc_pipe[0]);
1073 close(post_proc_pipe[1]);
1074 }
1075 else
1076 dup2(pstops_pipe[1], 1);
1077 close(pstops_pipe[0]);
1078 close(pstops_pipe[1]);
1079
1080 if (renderer == PDFTOPS)
1081 {
1082 execvp(CUPS_POPPLER_PDFTOPS, pdf_argv);
1083 perror("DEBUG: Unable to execute pdftops program");
1084 }
1085 else if (renderer == GS)
1086 {
1087 execvp(CUPS_GHOSTSCRIPT, pdf_argv);
1088 perror("DEBUG: Unable to execute gs program");
1089 }
1090 else if (renderer == PDFTOCAIRO)
1091 {
1092 execvp(CUPS_POPPLER_PDFTOCAIRO, pdf_argv);
1093 perror("DEBUG: Unable to execute pdftocairo program");
1094 }
1095 else if (renderer == ACROREAD)
1096 {
1097 /*
1098 * use filename as stdin for acroread to force output to stdout
1099 */
1100
1101 if ((fd = open(filename, O_RDONLY)))
1102 {
1103 dup2(fd, 0);
1104 close(fd);
1105 }
1106
1107 execvp(CUPS_ACROREAD, pdf_argv);
1108 perror("DEBUG: Unable to execute acroread program");
1109 }
1110 else if (renderer == MUPDF)
1111 {
1112 execvp(CUPS_MUTOOL, pdf_argv);
1113 perror("DEBUG: Unable to execute mutool program");
1114 }
1115
1116 exit(1);
1117 }
1118 else if (pdf_pid < 0)
1119 {
1120 /*
1121 * Unable to fork!
1122 */
1123
1124 if (renderer == PDFTOPS)
1125 perror("DEBUG: Unable to execute pdftops program");
1126 else if (renderer == GS)
1127 perror("DEBUG: Unable to execute gs program");
1128 else if (renderer == PDFTOCAIRO)
1129 perror("DEBUG: Unable to execute pdftocairo program");
1130 else if (renderer == ACROREAD)
1131 perror("DEBUG: Unable to execute acroread program");
1132 else if (renderer == MUPDF)
1133 perror("DEBUG: Unable to execute mutool program");
1134
1135 exit_status = 1;
1136 goto error;
1137 }
1138
1139 fprintf(stderr, "DEBUG: Started filter %s (PID %d)\n", pdf_argv[0], pdf_pid);
1140
1141 if (need_post_proc)
1142 {
1143 if ((post_proc_pid = fork()) == 0)
1144 {
1145 /*
1146 * Child comes here...
1147 */
1148
1149 dup2(post_proc_pipe[0], 0);
1150 close(post_proc_pipe[0]);
1151 close(post_proc_pipe[1]);
1152 dup2(pstops_pipe[1], 1);
1153 close(pstops_pipe[0]);
1154 close(pstops_pipe[1]);
1155
1156 fp = cupsFileStdin();
1157
1158 if (renderer == ACROREAD)
1159 {
1160 /*
1161 * Set %Title and %For from filter arguments since acroread inserts
1162 * garbage for these when using -toPostScript
1163 */
1164
1165 while ((bytes = cupsFileGetLine(fp, buffer, sizeof(buffer))) > 0 &&
1166 strncmp(buffer, "%%BeginProlog", 13))
1167 {
1168 if (strncmp(buffer, "%%Title", 7) == 0)
1169 printf("%%%%Title: %s\n", argv[3]);
1170 else if (strncmp(buffer, "%%For", 5) == 0)
1171 printf("%%%%For: %s\n", argv[2]);
1172 else
1173 printf("%s", buffer);
1174 }
1175
1176 /*
1177 * Copy the rest of the file
1178 */
1179 while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
1180 fwrite(buffer, 1, bytes, stdout);
1181 }
1182 else
1183 {
1184
1185 /*
1186 * Copy everything until after initial comments (Prolog section)
1187 */
1188 while ((bytes = cupsFileGetLine(fp, buffer, sizeof(buffer))) > 0 &&
1189 strncmp(buffer, "%%BeginProlog", 13) &&
1190 strncmp(buffer, "%%EndProlog", 11) &&
1191 strncmp(buffer, "%%BeginSetup", 12) &&
1192 strncmp(buffer, "%%Page:", 7))
1193 printf("%s", buffer);
1194
1195 if (bytes > 0)
1196 {
1197 /*
1198 * Insert PostScript interpreter bug fix code in the beginning of
1199 * the Prolog section (before the first active PostScript code)
1200 */
1201 if (strncmp(buffer, "%%BeginProlog", 13))
1202 {
1203 /* No Prolog section, create one */
1204 fprintf(stderr, "DEBUG: Adding Prolog section for workaround PostScript code\n");
1205 puts("%%BeginProlog");
1206 }
1207 else
1208 printf("%s", buffer);
1209
1210 if (renderer == GS && make_model[0])
1211 {
1212
1213 /*
1214 * Kyocera (and Utax) printers have a bug in their PostScript
1215 * interpreter making them crashing on PostScript input data
1216 * generated by Ghostscript's "ps2write" output device.
1217 *
1218 * The problem can be simply worked around by preceding the
1219 * PostScript code with some extra bits.
1220 *
1221 * See https://bugs.launchpad.net/bugs/951627
1222 *
1223 * In addition, at least some of Kyocera's PostScript printers are
1224 * very slow on rendering images which request interpolation. So we
1225 * also add some code to eliminate interpolation requests.
1226 *
1227 * See https://bugs.launchpad.net/bugs/1026974
1228 */
1229
1230 if (!strncasecmp(make_model, "Kyocera", 7) ||
1231 !strncasecmp(make_model, "Utax", 4))
1232 {
1233 fprintf(stderr, "DEBUG: Inserted workaround PostScript code for Kyocera and Utax printers\n");
1234 puts("% ===== Workaround insertion by pdftops CUPS filter =====");
1235 puts("% Kyocera's/Utax's PostScript interpreter crashes on early name binding,");
1236 puts("% so eliminate all \"bind\"s by redefining \"bind\" to no-op");
1237 puts("/bind {} bind def");
1238 puts("% Some Kyocera and Utax printers have an unacceptably slow implementation");
1239 puts("% of image interpolation.");
1240 puts("/image");
1241 puts("{");
1242 puts(" dup /Interpolate known");
1243 puts(" {");
1244 puts(" dup /Interpolate undef");
1245 puts(" } if");
1246 puts(" systemdict /image get exec");
1247 puts("} def");
1248 puts("% =====");
1249 }
1250
1251 /*
1252 * Brother printers have a bug in their PostScript interpreter
1253 * making them printing one blank page if PostScript input data
1254 * generated by Ghostscript's "ps2write" output device is used.
1255 *
1256 * The problem can be simply worked around by preceding the PostScript
1257 * code with some extra bits.
1258 *
1259 * See https://bugs.launchpad.net/bugs/950713
1260 */
1261
1262 else if (!strncasecmp(make_model, "Brother", 7))
1263 {
1264 fprintf(stderr, "DEBUG: Inserted workaround PostScript code for Brother printers\n");
1265 puts("% ===== Workaround insertion by pdftops CUPS filter =====");
1266 puts("% Brother's PostScript interpreter spits out the current page");
1267 puts("% and aborts the job on the \"currenthalftone\" operator, so redefine");
1268 puts("% it to null");
1269 puts("/currenthalftone {//null} bind def");
1270 puts("/orig.sethalftone systemdict /sethalftone get def");
1271 puts("/sethalftone {dup //null eq not {//orig.sethalftone}{pop} ifelse} bind def");
1272 puts("% =====");
1273 }
1274 }
1275
1276 if (strncmp(buffer, "%%BeginProlog", 13))
1277 {
1278 /* Close newly created Prolog section */
1279 if (strncmp(buffer, "%%EndProlog", 11))
1280 puts("%%EndProlog");
1281 printf("%s", buffer);
1282 }
1283
1284 if (!ppd)
1285 {
1286 /*
1287 * Copy everything until the setup section
1288 */
1289 while (bytes > 0 &&
1290 strncmp(buffer, "%%BeginSetup", 12) &&
1291 strncmp(buffer, "%%EndSetup", 10) &&
1292 strncmp(buffer, "%%Page:", 7))
1293 {
1294 bytes = cupsFileGetLine(fp, buffer, sizeof(buffer));
1295 if (strncmp(buffer, "%%Page:", 7) &&
1296 strncmp(buffer, "%%EndSetup", 10))
1297 printf("%s", buffer);
1298 }
1299
1300 if (bytes > 0)
1301 {
1302 /*
1303 * Insert option PostScript code in Setup section
1304 */
1305 if (strncmp(buffer, "%%BeginSetup", 12))
1306 {
1307 /* No Setup section, create one */
1308 fprintf(stderr, "DEBUG: Adding Setup section for option PostScript code\n");
1309 puts("%%BeginSetup");
1310 }
1311
1312 /*
1313 * Duplex
1314 */
1315 duplex = 0;
1316 tumble = 0;
1317 if ((val = cupsGetOption("sides", num_options, options)) != NULL ||
1318 (val = cupsGetOption("Duplex", num_options, options)) != NULL)
1319 {
1320 if (!strcasecmp(val, "On") ||
1321 !strcasecmp(val, "True") || !strcasecmp(val, "Yes") ||
1322 !strncasecmp(val, "two-sided", 9) ||
1323 !strncasecmp(val, "TwoSided", 8) ||
1324 !strncasecmp(val, "Duplex", 6))
1325 {
1326 duplex = 1;
1327 if (!strncasecmp(val, "DuplexTumble", 12))
1328 tumble = 1;
1329 }
1330 }
1331
1332 if ((val = cupsGetOption("sides", num_options, options)) != NULL ||
1333 (val = cupsGetOption("Tumble", num_options, options)) != NULL)
1334 {
1335 if (!strcasecmp(val, "None") || !strcasecmp(val, "Off") ||
1336 !strcasecmp(val, "False") || !strcasecmp(val, "No") ||
1337 !strcasecmp(val, "one-sided") || !strcasecmp(val, "OneSided") ||
1338 !strcasecmp(val, "two-sided-long-edge") ||
1339 !strcasecmp(val, "TwoSidedLongEdge") ||
1340 !strcasecmp(val, "DuplexNoTumble"))
1341 tumble = 0;
1342 else if (!strcasecmp(val, "On") ||
1343 !strcasecmp(val, "True") || !strcasecmp(val, "Yes") ||
1344 !strcasecmp(val, "two-sided-short-edge") ||
1345 !strcasecmp(val, "TwoSidedShortEdge") ||
1346 !strcasecmp(val, "DuplexTumble"))
1347 tumble = 1;
1348 }
1349
1350 if (duplex)
1351 {
1352 if (tumble)
1353 puts("<</Duplex true /Tumble true>> setpagedevice");
1354 else
1355 puts("<</Duplex true /Tumble false>> setpagedevice");
1356 }
1357 else
1358 puts("<</Duplex false>> setpagedevice");
1359
1360 /*
1361 * Resolution
1362 */
1363 if ((xres > 0) && (yres > 0))
1364 printf("<</HWResolution[%d %d]>> setpagedevice\n", xres, yres);
1365
1366 /*
1367 * InputSlot/MediaSource
1368 */
1369 if ((val = cupsGetOption("media-position", num_options,
1370 options)) != NULL ||
1371 (val = cupsGetOption("MediaPosition", num_options,
1372 options)) != NULL ||
1373 (val = cupsGetOption("media-source", num_options,
1374 options)) != NULL ||
1375 (val = cupsGetOption("MediaSource", num_options,
1376 options)) != NULL ||
1377 (val = cupsGetOption("InputSlot", num_options,
1378 options)) != NULL)
1379 {
1380 if (!strncasecmp(val, "Auto", 4) ||
1381 !strncasecmp(val, "Default", 7))
1382 puts("<</ManualFeed false /MediaPosition 7>> setpagedevice");
1383 else if (!strcasecmp(val, "Main"))
1384 puts("<</MediaPosition 0 /ManualFeed false>> setpagedevice");
1385 else if (!strcasecmp(val, "Alternate"))
1386 puts("<</MediaPosition 1 /ManualFeed false>> setpagedevice");
1387 else if (!strcasecmp(val, "Manual"))
1388 puts("<</MediaPosition 3 /ManualFeed true>> setpagedevice");
1389 else if (!strcasecmp(val, "Top"))
1390 puts("<</MediaPosition 0 /ManualFeed false>> setpagedevice");
1391 else if (!strcasecmp(val, "Bottom"))
1392 puts("<</MediaPosition 1 /ManualFeed false>> setpagedevice");
1393 else if (!strcasecmp(val, "ByPassTray"))
1394 puts("<</MediaPosition 3 /ManualFeed false>> setpagedevice");
1395 else if (!strcasecmp(val, "Tray1"))
1396 puts("<</MediaPosition 3 /ManualFeed false>> setpagedevice");
1397 else if (!strcasecmp(val, "Tray2"))
1398 puts("<</MediaPosition 0 /ManualFeed false>> setpagedevice");
1399 else if (!strcasecmp(val, "Tray3"))
1400 puts("<</MediaPosition 1 /ManualFeed false>> setpagedevice");
1401 }
1402
1403 /*
1404 * ColorModel
1405 */
1406 if ((val = cupsGetOption("pwg-raster-document-type", num_options,
1407 options)) != NULL ||
1408 (val = cupsGetOption("PwgRasterDocumentType", num_options,
1409 options)) != NULL ||
1410 (val = cupsGetOption("print-color-mode", num_options,
1411 options)) != NULL ||
1412 (val = cupsGetOption("PrintColorMode", num_options,
1413 options)) != NULL ||
1414 (val = cupsGetOption("color-space", num_options,
1415 options)) != NULL ||
1416 (val = cupsGetOption("ColorSpace", num_options,
1417 options)) != NULL ||
1418 (val = cupsGetOption("color-model", num_options,
1419 options)) != NULL ||
1420 (val = cupsGetOption("ColorModel", num_options,
1421 options)) != NULL)
1422 {
1423 if (!strncasecmp(val, "Black", 5))
1424 puts("<</ProcessColorModel /DeviceGray>> setpagedevice");
1425 else if (!strncasecmp(val, "Cmyk", 4))
1426 puts("<</ProcessColorModel /DeviceCMYK>> setpagedevice");
1427 else if (!strncasecmp(val, "Cmy", 3))
1428 puts("<</ProcessColorModel /DeviceCMY>> setpagedevice");
1429 else if (!strncasecmp(val, "Rgb", 3))
1430 puts("<</ProcessColorModel /DeviceRGB>> setpagedevice");
1431 else if (!strncasecmp(val, "Gray", 4))
1432 puts("<</ProcessColorModel /DeviceGray>> setpagedevice");
1433 else if (!strncasecmp(val, "Color", 5))
1434 puts("<</ProcessColorModel /DeviceRGB>> setpagedevice");
1435 }
1436
1437 if (strncmp(buffer, "%%BeginSetup", 12))
1438 {
1439 /* Close newly created Setup section */
1440 if (strncmp(buffer, "%%EndSetup", 10))
1441 puts("%%EndSetup");
1442 printf("%s", buffer);
1443 }
1444 }
1445 }
1446
1447 /*
1448 * Copy the rest of the file
1449 */
1450 while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
1451 fwrite(buffer, 1, bytes, stdout);
1452 }
1453 }
1454
1455 exit(0);
1456 }
1457 else if (post_proc_pid < 0)
1458 {
1459 /*
1460 * Unable to fork!
1461 */
1462
1463 perror("DEBUG: Unable to execute post-processing process");
1464
1465 exit_status = 1;
1466 goto error;
1467 }
1468
1469 fprintf(stderr, "DEBUG: Started post-processing (PID %d)\n", post_proc_pid);
1470 }
1471
1472 if ((pstops_pid = fork()) == 0)
1473 {
1474 /*
1475 * Child comes here...
1476 */
1477
1478 dup2(pstops_pipe[0], 0);
1479 close(pstops_pipe[0]);
1480 close(pstops_pipe[1]);
1481 if (need_post_proc)
1482 {
1483 close(post_proc_pipe[0]);
1484 close(post_proc_pipe[1]);
1485 }
1486
1487 execvp(pstops_path, pstops_argv);
1488 perror("DEBUG: Unable to execute pstops program");
1489
1490 exit(1);
1491 }
1492 else if (pstops_pid < 0)
1493 {
1494 /*
1495 * Unable to fork!
1496 */
1497
1498 perror("DEBUG: Unable to execute pstops program");
1499
1500 exit_status = 1;
1501 goto error;
1502 }
1503
1504 fprintf(stderr, "DEBUG: Started filter pstops (PID %d)\n", pstops_pid);
1505
1506 close(pstops_pipe[0]);
1507 close(pstops_pipe[1]);
1508 if (need_post_proc)
1509 {
1510 close(post_proc_pipe[0]);
1511 close(post_proc_pipe[1]);
1512 }
1513
1514 /*
1515 * Wait for the child processes to exit...
1516 */
1517
1518 wait_children = 2 + need_post_proc;
1519
1520 while (wait_children > 0)
1521 {
1522 /*
1523 * Wait until we get a valid process ID or the job is canceled...
1524 */
1525
1526 while ((wait_pid = wait(&wait_status)) < 0 && errno == EINTR)
1527 {
1528 if (job_canceled)
1529 {
1530 kill(pdf_pid, SIGTERM);
1531 if (need_post_proc)
1532 kill(post_proc_pid, SIGTERM);
1533 kill(pstops_pid, SIGTERM);
1534
1535 job_canceled = 0;
1536 }
1537 }
1538
1539 if (wait_pid < 0)
1540 break;
1541
1542 wait_children --;
1543
1544 /*
1545 * Report child status...
1546 */
1547
1548 if (wait_status)
1549 {
1550 if (WIFEXITED(wait_status))
1551 {
1552 exit_status = WEXITSTATUS(wait_status);
1553
1554 fprintf(stderr, "DEBUG: PID %d (%s) stopped with status %d!\n",
1555 wait_pid,
1556 wait_pid == pdf_pid ?
1557 (renderer == PDFTOPS ? "pdftops" :
1558 (renderer == PDFTOCAIRO ? "pdftocairo" :
1559 (renderer == GS ? "gs" :
1560 (renderer == ACROREAD ? "acroread" :
1561 (renderer == MUPDF ? "mutool" :
1562 "Unknown renderer"))))) :
1563 (wait_pid == pstops_pid ? "pstops" :
1564 (wait_pid == post_proc_pid ? "Post-processing" :
1565 "Unknown process")),
1566 exit_status);
1567 }
1568 else if (WTERMSIG(wait_status) == SIGTERM)
1569 {
1570 fprintf(stderr,
1571 "DEBUG: PID %d (%s) was terminated normally with signal %d!\n",
1572 wait_pid,
1573 wait_pid == pdf_pid ?
1574 (renderer == PDFTOPS ? "pdftops" :
1575 (renderer == PDFTOCAIRO ? "pdftocairo" :
1576 (renderer == GS ? "gs" :
1577 (renderer == ACROREAD ? "acroread" :
1578 (renderer == MUPDF ? "mutool" :
1579 "Unknown renderer"))))) :
1580 (wait_pid == pstops_pid ? "pstops" :
1581 (wait_pid == post_proc_pid ? "Post-processing" :
1582 "Unknown process")),
1583 exit_status);
1584 }
1585 else
1586 {
1587 exit_status = WTERMSIG(wait_status);
1588
1589 fprintf(stderr, "DEBUG: PID %d (%s) crashed on signal %d!\n",
1590 wait_pid,
1591 wait_pid == pdf_pid ?
1592 (renderer == PDFTOPS ? "pdftops" :
1593 (renderer == PDFTOCAIRO ? "pdftocairo" :
1594 (renderer == GS ? "gs" :
1595 (renderer == ACROREAD ? "acroread" :
1596 (renderer == MUPDF ? "mutool" :
1597 "Unknown renderer"))))) :
1598 (wait_pid == pstops_pid ? "pstops" :
1599 (wait_pid == post_proc_pid ? "Post-processing" :
1600 "Unknown process")),
1601 exit_status);
1602 }
1603 }
1604 else
1605 {
1606 fprintf(stderr, "DEBUG: PID %d (%s) exited with no errors.\n",
1607 wait_pid,
1608 wait_pid == pdf_pid ?
1609 (renderer == PDFTOPS ? "pdftops" :
1610 (renderer == PDFTOCAIRO ? "pdftocairo" :
1611 (renderer == GS ? "gs" :
1612 (renderer == ACROREAD ? "acroread" :
1613 (renderer == MUPDF ? "mutool" :
1614 "Unknown renderer"))))) :
1615 (wait_pid == pstops_pid ? "pstops" :
1616 (wait_pid == post_proc_pid ? "Post-processing" :
1617 "Unknown process")));
1618 }
1619 }
1620
1621 /*
1622 * Cleanup and exit...
1623 */
1624
1625 error:
1626
1627 if (tempfile[0])
1628 unlink(tempfile);
1629
1630 return (exit_status);
1631 }
1632
1633
1634 /*
1635 * 'cancel_job()' - Flag the job as canceled.
1636 */
1637
1638 static void
cancel_job(int sig)1639 cancel_job(int sig) /* I - Signal number (unused) */
1640 {
1641 (void)sig;
1642
1643 job_canceled = 1;
1644 }
1645
1646