Tizen 2.0 Release
[platform/upstream/cups-filters.git] / filter / pdftops.c
1 /*
2  * "$Id$"
3  *
4  *   PDF to PostScript filter front-end for CUPS.
5  *
6  *   Copyright 2007-2011 by Apple Inc.
7  *   Copyright 1997-2006 by Easy Software Products.
8  *   Copyright 2011-2012 by Till Kamppeter
9  *
10  *   These coded instructions, statements, and computer programs are the
11  *   property of Apple Inc. and are protected by Federal copyright
12  *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
13  *   which should have been included with this file.  If this file is
14  *   file is missing or damaged, see the license at "http://www.cups.org/".
15  *
16  * Contents:
17  *
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 <cups/cups.h>
27 #include <cups/ppd.h>
28 #include <cups/file.h>
29 #include <signal.h>
30 #include <sys/wait.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <config.h>
36 #include <cupsfilters/image-private.h>
37
38 #define MAX_CHECK_COMMENT_LINES 20
39
40 /*
41  * Type definitions
42  */
43
44 typedef unsigned renderer_t;
45 enum renderer_e {GS = 0, PDFTOPS = 1, ACROREAD = 2};
46
47 /*
48  * Local functions...
49  */
50
51 static void             cancel_job(int sig);
52
53
54 /*
55  * Local globals...
56  */
57
58 static int              job_canceled = 0;
59 int                     pdftopdfapplied = 0;
60 char                    deviceCopies[32] = "1";
61 int                     deviceCollate = 0;
62
63
64 /*
65  * When calling the "pstops" filter we exclude the following options from its
66  * command line as we have applied these options already to the PDF input,
67  * either on the "pdftops"/Ghostscript call in this filter or by use of the
68  * "pdftopdf" filter before this filter.
69  */
70
71 const char *pstops_exclude_general[] = {
72   "fitplot",
73   "fit-to-page",
74   "landscape",
75   "orientation-requested",
76   NULL
77 };
78
79 const char *pstops_exclude_page_management[] = {
80   "brightness",
81   "Collate",
82   "cupsEvenDuplex",
83   "gamma",
84   "hue",
85   "ipp-attribute-fidelity",
86   "MirrorPrint",
87   "mirror",
88   "multiple-document-handling",
89   "natural-scaling",
90   "number-up",
91   "number-up-layout",
92   "OutputOrder",
93   "page-border",
94   "page-bottom",
95   "page-label",
96   "page-left",
97   "page-ranges",
98   "page-right",
99   "page-set",
100   "page-top",
101   "position",
102   "saturation",
103   "scaling",
104   NULL
105 };
106
107
108 /*
109  * Check whether we were called after the "pdftopdf" filter and extract
110  * parameters passed over by "pdftopdf" in the header comments of the PDF
111  * file
112  */
113
114 static void parsePDFTOPDFComment(char *filename)
115 {
116   char buf[4096];
117   int i;
118   FILE *fp;
119
120   if ((fp = fopen(filename,"rb")) == 0) {
121     fprintf(stderr, "ERROR: pdftops - cannot open print file \"%s\"\n",
122             filename);
123     return;
124   }
125
126   /* skip until PDF start header */
127   while (fgets(buf,sizeof(buf),fp) != 0) {
128     if (strncmp(buf,"%PDF",4) == 0) {
129       break;
130     }
131   }
132   for (i = 0;i < MAX_CHECK_COMMENT_LINES;i++) {
133     if (fgets(buf,sizeof(buf),fp) == 0) break;
134     if (strncmp(buf,"%%PDFTOPDFNumCopies",19) == 0) {
135       char *p;
136
137       p = strchr(buf+19,':') + 1;
138       while (*p == ' ' || *p == '\t') p++;
139       strncpy(deviceCopies, p, sizeof(deviceCopies));
140       deviceCopies[sizeof(deviceCopies) - 1] = '\0';
141       p = deviceCopies + strlen(deviceCopies) - 1;
142       while (*p == ' ' || *p == '\t'  || *p == '\r'  || *p == '\n') p--;
143       *(p + 1) = '\0';
144       pdftopdfapplied = 1;
145     } else if (strncmp(buf,"%%PDFTOPDFCollate",17) == 0) {
146       char *p;
147
148       p = strchr(buf+17,':') + 1;
149       while (*p == ' ' || *p == '\t') p++;
150       if (strncasecmp(p,"true",4) == 0) {
151         deviceCollate = 1;
152       } else {
153         deviceCollate = 0;
154       }
155       pdftopdfapplied = 1;
156     } else if (strcmp(buf,"% This file was generated by pdftopdf") == 0) {
157       pdftopdfapplied = 1;
158     }
159   }
160
161   fclose(fp);
162 }
163
164
165 /*
166  * Remove all options in option_list from the string option_str, including
167  * option values after an "=" sign and preceded "no" before boolean options
168  */
169
170 void remove_options(char *options_str, const char **option_list)
171 {
172   const char    **option;               /* Option to be removed now */
173   char          *option_start,          /* Start of option in string */
174                 *option_end;            /* End of option in string */
175
176   for (option = option_list; *option; option ++)
177   {
178     option_start = options_str;
179
180     while ((option_start = strcasestr(option_start, *option)) != NULL)
181     {
182       if (!option_start[strlen(*option)] ||
183           isspace(option_start[strlen(*option)] & 255) ||
184           option_start[strlen(*option)] == '=')
185       {
186         /*
187          * Strip option...
188          */
189
190         option_end = option_start + strlen(*option);
191
192         /* Remove preceding "no" of boolean option */
193         if ((option_start - options_str) >= 2 &&
194             !strncasecmp(option_start - 2, "no", 2))
195           option_start -= 2;
196
197         /* Is match of the searched option name really at the beginning of
198            the name of the option in the command line? */
199         if ((option_start > options_str) &&
200             (!isspace(*(option_start - 1) & 255)))
201         {
202           /* Prevent the same option to be found again. */
203           option_start += 1;
204           /* Skip */
205           continue;
206         }
207
208         /* Remove "=" and value */
209         while (*option_end && !isspace(*option_end & 255))
210           option_end ++;
211
212         /* Remove spaces up to next option */
213         while (*option_end && isspace(*option_end & 255))
214           option_end ++;
215
216         memmove(option_start, option_end, strlen(option_end) + 1);
217       } else {
218         /* Prevent the same option to be found again. */
219         option_start += 1;
220       }
221     }
222   }
223 }
224
225
226 /*
227  * 'main()' - Main entry for filter...
228  */
229
230 int                                     /* O - Exit status */
231 main(int  argc,                         /* I - Number of command-line args */
232      char *argv[])                      /* I - Command-line arguments */
233 {
234   renderer_t    renderer = CUPS_PDFTOPS_RENDERER; /* Renderer: gs or pdftops or acroread */
235   int           fd = 0;                 /* Copy file descriptor */
236   char          *filename,              /* PDF file to convert */
237                 tempfile[1024];         /* Temporary file */
238   char          buffer[8192];           /* Copy buffer */
239   int           bytes;                  /* Bytes copied */
240   int           num_options;            /* Number of options */
241   cups_option_t *options;               /* Options */
242   const char    *val;                   /* Option value */
243   int           orientation,            /* Output orientation */
244                 fit;                    /* Fit output to default page size? */
245   ppd_file_t    *ppd;                   /* PPD file */
246   ppd_size_t    *size;                  /* Current page size */
247   char          resolution[128] = "300";/* Output resolution */
248   int           xres = 0, yres = 0,     /* resolution values */
249                 maxres = CUPS_PDFTOPS_MAX_RESOLUTION,
250                                         /* Maximum image rendering resolution */
251                 numvalues;              /* Number of values actually read */
252   ppd_choice_t  *choice;
253   ppd_attr_t    *attr;
254   cups_page_header2_t header;
255   cups_file_t   *fp;                    /* Post-processing input file */
256   int           pdf_pid,                /* Process ID for pdftops */
257                 pdf_argc,               /* Number of args for pdftops */
258                 pstops_pid,             /* Process ID of pstops filter */
259                 pstops_pipe[2],         /* Pipe to pstops filter */
260                 need_post_proc = 0,     /* Post-processing needed? */
261                 post_proc_pid = 0,      /* Process ID of post-processing */
262                 post_proc_pipe[2],      /* Pipe to post-processing */
263                 wait_children,          /* Number of child processes left */
264                 wait_pid,               /* Process ID from wait() */
265                 wait_status,            /* Status from child */
266                 exit_status = 0;        /* Exit status */
267   char          *pdf_argv[100],         /* Arguments for pdftops/gs */
268                 pdf_width[255],         /* Paper width */
269                 pdf_height[255],        /* Paper height */
270                 pdf_widthxheight[255],  /* Paper width x height */
271                 pstops_path[1024],      /* Path to pstops program */
272                 *pstops_argv[7],        /* Arguments for pstops filter */
273                 *pstops_options,        /* Options for pstops filter */
274                 *pstops_end;            /* End of pstops filter option */
275   const char    *cups_serverbin;        /* CUPS_SERVERBIN environment variable */
276 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
277   struct sigaction action;              /* Actions for POSIX signals */
278 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
279
280
281  /*
282   * Make sure status messages are not buffered...
283   */
284
285   setbuf(stderr, NULL);
286
287  /*
288   * Ignore broken pipe signals...
289   */
290
291   signal(SIGPIPE, SIG_IGN);
292
293  /*
294   * Make sure we have the right number of arguments for CUPS!
295   */
296
297   if (argc < 6 || argc > 7)
298   {
299     fprintf(stderr, "Usage: %s job user title copies options [file]\n",
300             argv[0]);
301     return (1);
302   }
303
304  /*
305   * Register a signal handler to cleanly cancel a job.
306   */
307
308 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
309   sigset(SIGTERM, cancel_job);
310 #elif defined(HAVE_SIGACTION)
311   memset(&action, 0, sizeof(action));
312
313   sigemptyset(&action.sa_mask);
314   action.sa_handler = cancel_job;
315   sigaction(SIGTERM, &action, NULL);
316 #else
317   signal(SIGTERM, cancel_job);
318 #endif /* HAVE_SIGSET */
319
320  /*
321   * Copy stdin if needed...
322   */
323
324   if (argc == 6)
325   {
326    /*
327     * Copy stdin to a temp file...
328     */
329
330     if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
331     {
332       perror("DEBUG: Unable to copy PDF file");
333       return (1);
334     }
335
336     fprintf(stderr, "DEBUG: pdftops - copying to temp print file \"%s\"\n",
337             tempfile);
338
339     while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
340       bytes = write(fd, buffer, bytes);
341
342     close(fd);
343
344     filename = tempfile;
345   }
346   else
347   {
348    /*
349     * Use the filename on the command-line...
350     */
351
352     filename    = argv[6];
353     tempfile[0] = '\0';
354   }
355
356  /*
357   * Read out copy counts and collate setting passed over by pdftopdf
358   */
359
360   parsePDFTOPDFComment(filename);
361
362  /*
363   * Load the PPD file and mark options...
364   */
365
366   ppd         = ppdOpenFile(getenv("PPD"));
367   num_options = cupsParseOptions(argv[5], 0, &options);
368
369   ppdMarkDefaults(ppd);
370   cupsMarkOptions(ppd, num_options, options);
371
372  /*
373   * Select the PDF renderer: Ghostscript (gs) or Poppler (pdftops)
374   */
375
376   if ((val = cupsGetOption("pdftops-renderer", num_options, options)) != NULL)
377   {
378     if (strcasecmp(val, "gs") == 0)
379       renderer = GS;
380     else if (strcasecmp(val, "pdftops") == 0)
381       renderer = PDFTOPS;
382     else if (strcasecmp(val, "acroread") == 0)
383       renderer = ACROREAD;
384     else
385       fprintf(stderr,
386               "WARNING: Invalid value for \"pdftops-renderer\": \"%s\"\n", val);
387   }
388
389  /*
390   * Build the pstops command-line...
391   */
392
393   if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
394     cups_serverbin = CUPS_SERVERBIN;
395
396   snprintf(pstops_path, sizeof(pstops_path), "%s/filter/pstops",
397            cups_serverbin);
398
399   pstops_options = strdup(argv[5]);
400
401   /*
402    * Strip options which "pstops" does not need to apply any more
403    */
404   remove_options(pstops_options, pstops_exclude_general);
405   if (pdftopdfapplied)
406     remove_options(pstops_options, pstops_exclude_page_management);
407
408   if (pdftopdfapplied && deviceCollate)
409   {
410    /*
411     * Add collate option to the pstops call if pdftopdf has found out that the
412     * printer does hardware collate.
413     */
414
415     pstops_options = realloc(pstops_options, strlen(pstops_options) + 9);
416     if (!pstops_options) {
417       fprintf(stderr, "ERROR: Can't allocate pstops_options\n");
418       exit(2);
419     }   
420     pstops_end = pstops_options + strlen(pstops_options);
421     strcpy(pstops_end, " Collate");
422   }
423
424   pstops_argv[0] = argv[0];             /* Printer */
425   pstops_argv[1] = argv[1];             /* Job */
426   pstops_argv[2] = argv[2];             /* User */
427   pstops_argv[3] = argv[3];             /* Title */
428   if (pdftopdfapplied)
429     pstops_argv[4] = deviceCopies;      /* Copies */
430   else
431     pstops_argv[4] = argv[4];           /* Copies */
432   pstops_argv[5] = pstops_options;      /* Options */
433   pstops_argv[6] = NULL;
434
435  /*
436   * Build the command-line for the pdftops or gs filter...
437   */
438
439   if (renderer == PDFTOPS)
440   {
441     pdf_argv[0] = (char *)"pdftops";
442     pdf_argc    = 1;
443   }
444   else if (renderer == GS)
445   {
446     pdf_argv[0] = (char *)"gs";
447     pdf_argv[1] = (char *)"-q";
448     pdf_argv[2] = (char *)"-dNOPAUSE";
449     pdf_argv[3] = (char *)"-dBATCH";
450     pdf_argv[4] = (char *)"-dSAFER";
451 #    ifdef HAVE_GHOSTSCRIPT_PS2WRITE
452     pdf_argv[5] = (char *)"-sDEVICE=ps2write";
453 #    else
454     pdf_argv[5] = (char *)"-sDEVICE=pswrite";
455 #    endif /* HAVE_GHOSTSCRIPT_PS2WRITE */
456     pdf_argv[6] = (char *)"-sOUTPUTFILE=%stdout";
457     pdf_argc    = 7;
458   }
459   else
460   {
461     pdf_argv[0] = (char *)"acroread";
462     pdf_argv[1] = (char *)"-toPostScript";
463     pdf_argc    = 2;
464   }
465
466   if (ppd)
467   {
468    /*
469     * Set language level and TrueType font handling...
470     */
471
472     if (ppd->language_level == 1)
473     {
474       if (renderer == PDFTOPS)
475       {
476         pdf_argv[pdf_argc++] = (char *)"-level1";
477         pdf_argv[pdf_argc++] = (char *)"-noembtt";
478       }
479       else if (renderer == GS)
480         pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=1";
481       else
482         fprintf(stderr, "WARNING: Level 1 PostScript not supported by acroread.");
483     }
484     else if (ppd->language_level == 2)
485     {
486       if (renderer == PDFTOPS)
487       {
488         pdf_argv[pdf_argc++] = (char *)"-level2";
489         if (!ppd->ttrasterizer)
490           pdf_argv[pdf_argc++] = (char *)"-noembtt";
491       }
492       else if (renderer == GS)
493         pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=2";
494       else
495         pdf_argv[pdf_argc++] = (char *)"-level2";
496     }
497     else
498     {
499       if (renderer == PDFTOPS)
500         /* Do not emit PS Level 3 with Poppler, some HP PostScript printers
501            do not like it. See https://bugs.launchpad.net/bugs/277404. */
502         pdf_argv[pdf_argc++] = (char *)"-level2";
503       else if (renderer == GS)
504         pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=3";
505       else
506         pdf_argv[pdf_argc++] = (char *)"-level3";
507     }
508
509     if ((val = cupsGetOption("fitplot", num_options, options)) == NULL)
510       val = cupsGetOption("fit-to-page", num_options, options);
511
512     if (val && strcasecmp(val, "no") && strcasecmp(val, "off") &&
513         strcasecmp(val, "false"))
514       fit = 1;
515     else
516       fit = 0;
517
518    /*
519     * Set output page size...
520     */
521
522     size = ppdPageSize(ppd, NULL);
523     if (size && fit)
524     {
525      /*
526       * Got the size, now get the orientation...
527       */
528
529       orientation = 0;
530
531       if ((val = cupsGetOption("landscape", num_options, options)) != NULL)
532       {
533         if (strcasecmp(val, "no") != 0 && strcasecmp(val, "off") != 0 &&
534             strcasecmp(val, "false") != 0)
535           orientation = 1;
536       }
537       else if ((val = cupsGetOption("orientation-requested", num_options,
538                                     options)) != NULL)
539       {
540        /*
541         * Map IPP orientation values to 0 to 3:
542         *
543         *   3 = 0 degrees   = 0
544         *   4 = 90 degrees  = 1
545         *   5 = -90 degrees = 3
546         *   6 = 180 degrees = 2
547         */
548
549         orientation = atoi(val) - 3;
550         if (orientation >= 2)
551           orientation ^= 1;
552       }
553
554       if (renderer == PDFTOPS)
555       {
556         if (orientation & 1)
557         {
558           snprintf(pdf_width, sizeof(pdf_width), "%.0f", size->length);
559           snprintf(pdf_height, sizeof(pdf_height), "%.0f", size->width);
560         }
561         else
562         {
563           snprintf(pdf_width, sizeof(pdf_width), "%.0f", size->width);
564           snprintf(pdf_height, sizeof(pdf_height), "%.0f", size->length);
565         }
566
567         pdf_argv[pdf_argc++] = (char *)"-paperw";
568         pdf_argv[pdf_argc++] = pdf_width;
569         pdf_argv[pdf_argc++] = (char *)"-paperh";
570         pdf_argv[pdf_argc++] = pdf_height;
571         pdf_argv[pdf_argc++] = (char *)"-expand";
572
573       }
574       else if (renderer == GS)
575       {
576         if (orientation & 1)
577         {
578           snprintf(pdf_width, sizeof(pdf_width), "-dDEVICEWIDTHPOINTS=%.0f",
579                    size->length);
580           snprintf(pdf_height, sizeof(pdf_height), "-dDEVICEHEIGHTPOINTS=%.0f",
581                    size->width);
582         }
583         else
584         {
585           snprintf(pdf_width, sizeof(pdf_width), "-dDEVICEWIDTHPOINTS=%.0f",
586                    size->width);
587           snprintf(pdf_height, sizeof(pdf_height), "-dDEVICEHEIGHTPOINTS=%.0f",
588                    size->length);
589         }
590
591         pdf_argv[pdf_argc++] = pdf_width;
592         pdf_argv[pdf_argc++] = pdf_height;
593       }
594       else
595       {
596         if (orientation & 1)
597           snprintf(pdf_widthxheight, sizeof(pdf_widthxheight), "%.0fx%.0f",
598                    size->length, size->width);
599         else
600           snprintf(pdf_widthxheight, sizeof(pdf_widthxheight), "%.0fx%.0f",
601                    size->width, size->length);
602
603         pdf_argv[pdf_argc++] = (char *)"-size";
604         pdf_argv[pdf_argc++] = pdf_widthxheight;
605       }
606     }
607 #ifdef HAVE_POPPLER_PDFTOPS_WITH_ORIGPAGESIZES
608     else if (renderer == PDFTOPS)
609     {
610      /*
611       *  Use the page sizes of the original PDF document, this way documents
612       *  which contain pages of different sizes can be printed correctly
613       */
614
615       pdf_argv[pdf_argc++] = (char *)"-origpagesizes";
616     }
617 #endif /* HAVE_POPPLER_PDFTOPS_WITH_ORIGPAGESIZES */
618     else if (renderer == ACROREAD)
619     {
620      /*
621       * Use the page sizes of the original PDF document, this way documents
622       * which contain pages of different sizes can be printed correctly
623       */
624      
625       pdf_argv[pdf_argc++] = (char *)"-choosePaperByPDFPageSize";
626     }
627
628    /*
629     * Set output resolution ...
630     */
631
632     /* Ignore error exits of cupsRasterInterpretPPD(), if it found a resolution
633        setting before erroring it is OK for us */
634     cupsRasterInterpretPPD(&header, ppd, num_options, options, NULL);
635     /* 100 dpi is default, this means that if we have 100 dpi here this
636        method failed to find the printing resolution */
637     if (header.HWResolution[0] > 100 && header.HWResolution[1] > 100)
638     {
639         xres = header.HWResolution[0];
640         yres = header.HWResolution[1];
641     }
642     else if ((choice = ppdFindMarkedChoice(ppd, "Resolution")) != NULL)
643       strncpy(resolution, choice->choice, sizeof(resolution));
644     else if ((attr = ppdFindAttr(ppd,"DefaultResolution",NULL)) != NULL)
645       strncpy(resolution, attr->value, sizeof(resolution));
646   }
647
648   resolution[sizeof(resolution)-1] = '\0';
649   if ((xres > 0) || (yres > 0) ||
650       ((numvalues = sscanf(resolution, "%dx%d", &xres, &yres)) > 0))
651   {
652     if ((yres > 0) && (xres > yres)) xres = yres;
653   }
654   else
655     xres = 300;
656
657  /*
658   * Get the ceiling for the image rendering resolution
659   */
660
661   if ((val = cupsGetOption("pdftops-max-image-resolution", num_options, options)) != NULL)
662   {
663     if ((numvalues = sscanf(val, "%d", &yres)) > 0)
664       maxres = yres;
665     else
666       fprintf(stderr,
667               "WARNING: Invalid value for \"pdftops-max-image-resolution\": \"%s\"\n", val);
668   }
669
670  /*
671   * Reduce the image rendering resolution to not exceed a given maximum
672   * to make processing of jobs by the PDF->PS converter and the printer faster
673   *
674   * maxres = 0 means no limit
675   */
676
677   if (maxres)
678     while (xres > maxres)
679       xres = xres / 2;
680
681   if (renderer == PDFTOPS)
682   {
683 #ifdef HAVE_POPPLER_PDFTOPS_WITH_RESOLUTION
684    /*
685     * Set resolution to avoid slow processing by the printer when the
686     * resolution of embedded images does not match the printer's resolution
687     */
688     pdf_argv[pdf_argc++] = (char *)"-r";
689     snprintf(resolution, sizeof(resolution), "%d", xres);
690     pdf_argv[pdf_argc++] = resolution;
691     fprintf(stderr, "DEBUG: Using image rendering resolution %d dpi\n", xres);
692 #endif /* HAVE_POPPLER_PDFTOPS_WITH_RESOLUTION */
693     pdf_argv[pdf_argc++] = filename;
694     pdf_argv[pdf_argc++] = (char *)"-";
695   }
696   else if (renderer == GS)
697   {
698    /*
699     * Set resolution to avoid slow processing by the printer when the
700     * resolution of embedded images does not match the printer's resolution
701     */
702     snprintf(resolution, 127, "-r%d", xres);
703     pdf_argv[pdf_argc++] = resolution;
704     fprintf(stderr, "DEBUG: Using image rendering resolution %d dpi\n", xres);
705    /*
706     * PostScript debug mode: If you send a job with "lpr -o psdebug" Ghostscript
707     * will not compress the pages, so that the PostScript code can get
708     * analysed. This is especially important if a PostScript printer errors or
709     * misbehaves on Ghostscript's output.
710     * On Kyocera printers we always suppress page compression, to avoid slow
711     * processing of raster images.
712     */
713     val = cupsGetOption("psdebug", num_options, options);
714     if ((val && strcasecmp(val, "no") && strcasecmp(val, "off") &&
715          strcasecmp(val, "false")) ||
716         (ppd && ppd->manufacturer &&
717          !strncasecmp(ppd->manufacturer, "Kyocera", 7)))
718     {
719       fprintf(stderr, "DEBUG: Deactivated compression of pages in Ghostscript's PostScript output (\"psdebug\" debug mode or Kyocera printer)\n");
720       pdf_argv[pdf_argc++] = (char *)"-dCompressPages=false";
721     }
722    /*
723     * The PostScript interpreters on many printers have bugs which make
724     * the interpreter crash, error out, or otherwise misbehave on too
725     * heavily compressed input files, especially if code with compressed
726     * elements is compressed again. Therefore we reduce compression here.
727     */
728     pdf_argv[pdf_argc++] = (char *)"-dCompressFonts=false";
729     pdf_argv[pdf_argc++] = (char *)"-dNoT3CCITT";
730     if (ppd && ppd->manufacturer &&
731         !strncasecmp(ppd->manufacturer, "Brother", 7))
732     {
733       fprintf(stderr, "DEBUG: Deactivation of Ghostscript's image compression for Brother printers to workarounmd PS interpreter bug\n");
734       pdf_argv[pdf_argc++] = (char *)"-dEncodeMonoImages=false";
735       pdf_argv[pdf_argc++] = (char *)"-dEncodeColorImages=false";
736     }
737     pdf_argv[pdf_argc++] = (char *)"-c";
738     pdf_argv[pdf_argc++] = (char *)"save pop";
739     pdf_argv[pdf_argc++] = (char *)"-f";
740     pdf_argv[pdf_argc++] = filename;
741   }
742   /* acroread has to read from stdin */
743
744   pdf_argv[pdf_argc] = NULL;
745
746  /*
747   * Do we need post-processing of the PostScript output to work around bugs
748   * of the printer's PostScript interpreter?
749   */
750
751   if (renderer == PDFTOPS)
752     need_post_proc = 0;
753   else if (renderer == GS)
754     need_post_proc =
755       (ppd && ppd->manufacturer &&
756        (!strncasecmp(ppd->manufacturer, "Kyocera", 7) ||
757         !strncasecmp(ppd->manufacturer, "Brother", 7)) ? 1 : 0);
758   else
759     need_post_proc = 1;
760
761  /*
762   * Execute "pdftops/gs | pstops [ | post-processing ]"...
763   */
764
765   if (pipe(pstops_pipe))
766   {
767     perror("DEBUG: Unable to create pipe for pstops");
768
769     exit_status = 1;
770     goto error;
771   }
772
773   if (need_post_proc)
774   {
775     if (pipe(post_proc_pipe))
776     {
777       perror("DEBUG: Unable to create pipe for post-processing");
778
779       exit_status = 1;
780       goto error;
781     }
782   }
783
784   if ((pdf_pid = fork()) == 0)
785   {
786    /*
787     * Child comes here...
788     */
789
790     if (need_post_proc)
791     {
792       dup2(post_proc_pipe[1], 1);
793       close(post_proc_pipe[0]);
794       close(post_proc_pipe[1]);
795     }
796     else
797       dup2(pstops_pipe[1], 1);
798     close(pstops_pipe[0]);
799     close(pstops_pipe[1]);
800
801     if (renderer == PDFTOPS)
802     {
803       execv(CUPS_POPPLER_PDFTOPS, pdf_argv);
804       perror("DEBUG: Unable to execute pdftops program");
805     }
806     else if (renderer == GS)
807     {
808       execv(CUPS_GHOSTSCRIPT, pdf_argv);
809       perror("DEBUG: Unable to execute gs program");
810     }
811     else
812     {
813       /*
814        * use filename as stdin for acroread to force output to stdout
815        */
816
817       if ((fd = open(filename, O_RDONLY)))
818       {
819         dup2(fd, 0);
820         close(fd);
821       }
822      
823       execv(CUPS_ACROREAD, pdf_argv);
824       perror("DEBUG: Unable to execute acroread program");
825     }
826
827     exit(1);
828   }
829   else if (pdf_pid < 0)
830   {
831    /*
832     * Unable to fork!
833     */
834
835     if (renderer == PDFTOPS)
836       perror("DEBUG: Unable to execute pdftops program");
837     else if (renderer == GS)
838       perror("DEBUG: Unable to execute gs program");
839     else
840       perror("DEBUG: Unable to execute acroread program");
841
842     exit_status = 1;
843     goto error;
844   }
845
846   fprintf(stderr, "DEBUG: Started filter %s (PID %d)\n", pdf_argv[0], pdf_pid);
847
848   if (need_post_proc)
849   {
850     if ((post_proc_pid = fork()) == 0)
851     {
852      /*
853       * Child comes here...
854       */
855
856       dup2(post_proc_pipe[0], 0);
857       close(post_proc_pipe[0]);
858       close(post_proc_pipe[1]);
859       dup2(pstops_pipe[1], 1);
860       close(pstops_pipe[0]);
861       close(pstops_pipe[1]);
862
863       fp = cupsFileStdin();
864
865       if (renderer == ACROREAD)
866       {
867        /*
868         * Set %Title and %For from filter arguments since acroread inserts
869         * garbage for these when using -toPostScript
870         */
871
872         while ((bytes = cupsFileGetLine(fp, buffer, sizeof(buffer))) > 0 &&
873                strncmp(buffer, "%%BeginProlog", 13))
874         {
875           if (strncmp(buffer, "%%Title", 7) == 0)
876             printf("%%%%Title: %s\n", argv[3]);
877           else if (strncmp(buffer, "%%For", 5) == 0)
878             printf("%%%%For: %s\n", argv[2]);
879           else
880             printf("%s", buffer);
881         }
882
883        /*
884         * Copy the rest of the file
885         */
886         while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
887           fwrite(buffer, 1, bytes, stdout);
888       }
889       else
890       {
891
892        /*
893         * Copy everything until after initial comments (Prolog section)
894         */
895         while ((bytes = cupsFileGetLine(fp, buffer, sizeof(buffer))) > 0 &&
896                strncmp(buffer, "%%BeginProlog", 13) &&
897                strncmp(buffer, "%%EndProlog", 11) &&
898                strncmp(buffer, "%%BeginSetup", 12) &&
899                strncmp(buffer, "%%Page:", 7))
900           printf("%s", buffer);
901
902         if (bytes > 0)
903         {
904          /*
905           * Insert PostScript interpreter bug fix code in the beginning of
906           * the Prolog section (before the first active PostScript code)
907           */
908           if (strncmp(buffer, "%%BeginProlog", 13))
909           {
910             /* No Prolog section, create one */
911             fprintf(stderr, "DEBUG: Adding Prolog section for workaround PostScript code\n");
912             puts("%%BeginProlog");
913           }
914           else
915             printf("%s", buffer);
916
917           if (renderer == GS && ppd && ppd->manufacturer)
918           {
919
920            /*
921             * Kyocera printers have a bug in their PostScript interpreter
922             * making them crashing on PostScript input data generated by
923             * Ghostscript's "ps2write" output device.
924             *
925             * The problem can be simply worked around by preceding the PostScript
926             * code with some extra bits.
927             *
928             * See https://bugs.launchpad.net/bugs/951627
929             *
930             * In addition, at least some of Kyocera's PostScript printers are
931             * very slow on rendering images which request interpolation. So we
932             * also add some code to eliminate interpolation requests.
933             *
934             * See https://bugs.launchpad.net/bugs/1026974
935             */
936
937             if (!strncasecmp(ppd->manufacturer, "Kyocera", 7))
938             {
939               fprintf(stderr, "DEBUG: Inserted workaround PostScript code for Kyocera printers\n");
940               puts("% ===== Workaround insertion by pdftops CUPS filter =====");
941               puts("% Kyocera's PostScript interpreter crashes on early name binding,");
942               puts("% so eliminate all \"bind\"s by redifining \"bind\" to no-op");
943               puts("/bind {} bind def");
944               puts("% Some Kyocera printers have an unacceptably slow implementation");
945               puts("% of image interpolation.");
946               puts("/image");
947               puts("{");
948               puts("  dup /Interpolate known");
949               puts("  {");
950               puts("    dup /Interpolate undef");
951               puts("  } if");
952               puts("  systemdict /image get exec");
953               puts("} def");
954               puts("% =====");
955             }
956
957            /*
958             * Brother printers have a bug in their PostScript interpreter
959             * making them printing one blank page if PostScript input data
960             * generated by Ghostscript's "ps2write" output device is used.
961             *
962             * The problem can be simply worked around by preceding the PostScript
963             * code with some extra bits.
964             *
965             * See https://bugs.launchpad.net/bugs/950713
966             */
967
968             else if (!strncasecmp(ppd->manufacturer, "Brother", 7))
969             {
970               fprintf(stderr, "DEBUG: Inserted workaround PostScript code for Brother printers\n");
971               puts("% ===== Workaround insertion by pdftops CUPS filter =====");
972               puts("% Brother's PostScript interpreter spits out the current page");
973               puts("% and aborts the job on the \"currenthalftone\" operator, so redefine");
974               puts("% it to null");
975               puts("/currenthalftone {//null} bind def");
976               puts("/orig.sethalftone systemdict /sethalftone get def");
977               puts("/sethalftone {dup //null eq not {//orig.sethalftone}{pop} ifelse} bind def");
978               puts("% =====");
979             }
980           }
981
982           if (strncmp(buffer, "%%BeginProlog", 13))
983           {
984             /* Close newly created Prolog section */
985             if (strncmp(buffer, "%%EndProlog", 11))
986               puts("%%EndProlog");
987             printf("%s", buffer);
988           }
989
990          /*
991           * Copy the rest of the file
992           */
993           while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
994             fwrite(buffer, 1, bytes, stdout);
995         }
996       }
997
998       exit(0);
999     }
1000     else if (post_proc_pid < 0)
1001     {
1002      /*
1003       * Unable to fork!
1004       */
1005
1006       perror("DEBUG: Unable to execute post-processing process");
1007
1008       exit_status = 1;
1009       goto error;
1010     }
1011
1012     fprintf(stderr, "DEBUG: Started post-processing (PID %d)\n", post_proc_pid);
1013   }
1014
1015   if ((pstops_pid = fork()) == 0)
1016   {
1017    /*
1018     * Child comes here...
1019     */
1020
1021     if (need_post_proc)
1022     {
1023       close(post_proc_pipe[0]);
1024       close(post_proc_pipe[1]);
1025     }
1026     dup2(pstops_pipe[0], 0);
1027     close(pstops_pipe[0]);
1028     close(pstops_pipe[1]);
1029
1030     execv(pstops_path, pstops_argv);
1031     perror("DEBUG: Unable to execute pstops program");
1032
1033     exit(1);
1034   }
1035   else if (pstops_pid < 0)
1036   {
1037    /*
1038     * Unable to fork!
1039     */
1040
1041     perror("DEBUG: Unable to execute pstops program");
1042
1043     exit_status = 1;
1044     goto error;
1045   }
1046
1047   fprintf(stderr, "DEBUG: Started filter pstops (PID %d)\n", pstops_pid);
1048
1049   close(pstops_pipe[0]);
1050   close(pstops_pipe[1]);
1051   if (need_post_proc)
1052   {
1053     close(post_proc_pipe[0]);
1054     close(post_proc_pipe[1]);
1055   }
1056
1057  /*
1058   * Wait for the child processes to exit...
1059   */
1060
1061   wait_children = 2 + need_post_proc;
1062
1063   while (wait_children > 0)
1064   {
1065    /*
1066     * Wait until we get a valid process ID or the job is canceled...
1067     */
1068
1069     while ((wait_pid = wait(&wait_status)) < 0 && errno == EINTR)
1070     {
1071       if (job_canceled)
1072       {
1073         kill(pdf_pid, SIGTERM);
1074         if (need_post_proc)
1075           kill(post_proc_pid, SIGTERM);
1076         kill(pstops_pid, SIGTERM);
1077
1078         job_canceled = 0;
1079       }
1080     }
1081
1082     if (wait_pid < 0)
1083       break;
1084
1085     wait_children --;
1086
1087    /*
1088     * Report child status...
1089     */
1090
1091     if (wait_status)
1092     {
1093       if (WIFEXITED(wait_status))
1094       {
1095         exit_status = WEXITSTATUS(wait_status);
1096
1097         fprintf(stderr, "DEBUG: PID %d (%s) stopped with status %d!\n",
1098                 wait_pid,
1099                 wait_pid == pdf_pid ? (renderer == PDFTOPS ? "pdftops" :
1100                 (renderer == GS ? "gs" : "acroread")) :
1101                 (wait_pid == pstops_pid ? "pstops" : "Post-processing"),
1102                 exit_status);
1103       }
1104       else if (WTERMSIG(wait_status) == SIGTERM)
1105       {
1106         fprintf(stderr,
1107                 "DEBUG: PID %d (%s) was terminated normally with signal %d!\n",
1108                 wait_pid,
1109                 wait_pid == pdf_pid ? (renderer == PDFTOPS ? "pdftops" :
1110                 (renderer == GS ? "gs" : "acroread")) :
1111                 (wait_pid == pstops_pid ? "pstops" : "Post-processing"),
1112                 exit_status);
1113       }
1114       else
1115       {
1116         exit_status = WTERMSIG(wait_status);
1117
1118         fprintf(stderr, "DEBUG: PID %d (%s) crashed on signal %d!\n", wait_pid,
1119                 wait_pid == pdf_pid ? (renderer == PDFTOPS ? "pdftops" :
1120                 (renderer == GS ? "gs" : "acroread")) :
1121                 (wait_pid == pstops_pid ? "pstops" : "Post-processing"),
1122                 exit_status);
1123       }
1124     }
1125     else
1126     {
1127       fprintf(stderr, "DEBUG: PID %d (%s) exited with no errors.\n", wait_pid,
1128               wait_pid == pdf_pid ? (renderer == PDFTOPS ? "pdftops" :
1129               (renderer == GS ? "gs" : "acroread")) :
1130               (wait_pid == pstops_pid ? "pstops" : "Post-processing"));
1131     }
1132   }
1133
1134  /*
1135   * Cleanup and exit...
1136   */
1137
1138   error:
1139
1140   if (tempfile[0])
1141     unlink(tempfile);
1142
1143   return (exit_status);
1144 }
1145
1146
1147 /*
1148  * 'cancel_job()' - Flag the job as canceled.
1149  */
1150
1151 static void
1152 cancel_job(int sig)                     /* I - Signal number (unused) */
1153 {
1154   (void)sig;
1155
1156   job_canceled = 1;
1157 }
1158
1159
1160 /*
1161  * End of "$Id$".
1162  */