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 	  ((ptr = strcasestr(ptr + 11, "4")) || (ptr = strcasestr(ptr + 11, "12")) ||
500 	   (ptr = strcasestr(ptr + 11, "16"))) &&
501 	  ((ptr = strcasestr(ptr + 2, "600")) || (ptr = strcasestr(ptr + 2, "640")) ||
502 	   (ptr = strcasestr(ptr + 2, "660"))))))
503     {
504       fprintf(stderr, "DEBUG: Switching to Poppler's pdftops instead of Ghostscript for Brother, Minolta, Konica Minolta, Dell, and Apple LaserWriter 16/600, 4/600, 12/640, 12/600, 12/660 to work around bugs in the printer's PS interpreters\n");
505       renderer = PDFTOPS;
506     }
507     else
508       renderer = GS;
509    /*
510     * Use Poppler instead of Ghostscript for old HP LaserJet printers due to
511     * a bug in their PS interpreters. They are very slow with Ghostscript.
512     * A LaserJet is considered old if its model number does not have a letter
513     * in the beginning, like LaserJet 3 or LaserJet 4000, not LaserJet P2015.
514     * See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=742765
515     */
516     if (make_model[0] &&
517 	((!strncasecmp(make_model, "HP", 2) ||
518 	  !strncasecmp(make_model, "Hewlett-Packard", 15) ||
519 	  !strncasecmp(make_model, "Hewlett Packard", 15)) &&
520 	 (ptr = strcasestr(make_model, "LaserJet"))))
521     {
522       for (ptr += 8; *ptr; ptr ++)
523       {
524 	if (isspace(*ptr)) continue;
525 	if (isdigit(*ptr))
526 	{
527 	  fprintf(stderr, "DEBUG: Switching to Poppler's pdftops instead of Ghostscript for old HP LaserJet (\"LaserJet <number>\", no letters before <number>) printers to work around bugs in the printer's PS interpreters\n");
528 	  renderer = PDFTOPS;
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