Revert manifest to default one
[external/cups.git] / filter / pdftops.c
1 /*
2  * "$Id: pdftops.c 9793 2011-05-20 03:49:49Z mike $"
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  *
9  *   These coded instructions, statements, and computer programs are the
10  *   property of Apple Inc. and are protected by Federal copyright
11  *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12  *   which should have been included with this file.  If this file is
13  *   file is missing or damaged, see the license at "http://www.cups.org/".
14  *
15  * Contents:
16  *
17  *   main()       - Main entry for filter...
18  *   cancel_job() - Flag the job as canceled.
19  */
20
21 /*
22  * Include necessary headers...
23  */
24
25 #include <cups/cups.h>
26 #include <cups/ppd.h>
27 #include <cups/string-private.h>
28 #include <cups/language-private.h>
29 #include <signal.h>
30 #include <sys/wait.h>
31 #include <errno.h>
32
33
34 /*
35  * Local functions...
36  */
37
38 static void             cancel_job(int sig);
39
40
41 /*
42  * Local globals...
43  */
44
45 static int              job_canceled = 0;
46
47
48 /*
49  * 'main()' - Main entry for filter...
50  */
51
52 int                                     /* O - Exit status */
53 main(int  argc,                         /* I - Number of command-line args */
54      char *argv[])                      /* I - Command-line arguments */
55 {
56   int           fd;                     /* Copy file descriptor */
57   char          *filename,              /* PDF file to convert */
58                 tempfile[1024];         /* Temporary file */
59   char          buffer[8192];           /* Copy buffer */
60   int           bytes;                  /* Bytes copied */
61   int           num_options;            /* Number of options */
62   cups_option_t *options;               /* Options */
63   const char    *val;                   /* Option value */
64   int           orientation,            /* Output orientation */
65                 fit;                    /* Fit output to default page size? */
66   ppd_file_t    *ppd;                   /* PPD file */
67   ppd_size_t    *size;                  /* Current page size */
68   int           pdf_pid,                /* Process ID for pdftops */
69                 pdf_argc,               /* Number of args for pdftops */
70                 pstops_pid,             /* Process ID of pstops filter */
71                 pstops_pipe[2],         /* Pipe to pstops filter */
72                 wait_children,          /* Number of child processes left */
73                 wait_pid,               /* Process ID from wait() */
74                 wait_status,            /* Status from child */
75                 exit_status = 0;        /* Exit status */
76   char          *pdf_argv[100],         /* Arguments for pdftops/gs */
77                 pdf_width[255],         /* Paper width */
78                 pdf_height[255],        /* Paper height */
79                 pstops_path[1024],      /* Path to pstops program */
80                 *pstops_argv[7],        /* Arguments for pstops filter */
81                 *pstops_options,        /* Options for pstops filter */
82                 *pstops_start,          /* Start of pstops filter option */
83                 *pstops_end;            /* End of pstops filter option */
84   const char    *cups_serverbin;        /* CUPS_SERVERBIN environment variable */
85 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
86   struct sigaction action;              /* Actions for POSIX signals */
87 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
88
89
90  /*
91   * Make sure status messages are not buffered...
92   */
93
94   setbuf(stderr, NULL);
95
96  /*
97   * Ignore broken pipe signals...
98   */
99
100   signal(SIGPIPE, SIG_IGN);
101
102  /*
103   * Make sure we have the right number of arguments for CUPS!
104   */
105
106   if (argc < 6 || argc > 7)
107   {
108     _cupsLangPrintf(stderr,
109                     _("Usage: %s job user title copies options [filename]"),
110                     argv[0]);
111     return (1);
112   }
113
114  /*
115   * Register a signal handler to cleanly cancel a job.
116   */
117
118 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
119   sigset(SIGTERM, cancel_job);
120 #elif defined(HAVE_SIGACTION)
121   memset(&action, 0, sizeof(action));
122
123   sigemptyset(&action.sa_mask);
124   action.sa_handler = cancel_job;
125   sigaction(SIGTERM, &action, NULL);
126 #else
127   signal(SIGTERM, cancel_job);
128 #endif /* HAVE_SIGSET */
129
130  /*
131   * Copy stdin if needed...
132   */
133
134   if (argc == 6)
135   {
136    /*
137     * Copy stdin to a temp file...
138     */
139
140     if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
141     {
142       perror("DEBUG: Unable to copy PDF file");
143       return (1);
144     }
145
146     fprintf(stderr, "DEBUG: pdftops - copying to temp print file \"%s\"\n",
147             tempfile);
148
149     while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
150       write(fd, buffer, bytes);
151
152     close(fd);
153
154     filename = tempfile;
155   }
156   else
157   {
158    /*
159     * Use the filename on the command-line...
160     */
161
162     filename    = argv[6];
163     tempfile[0] = '\0';
164   }
165
166  /*
167   * Load the PPD file and mark options...
168   */
169
170   ppd         = ppdOpenFile(getenv("PPD"));
171   num_options = cupsParseOptions(argv[5], 0, &options);
172
173   ppdMarkDefaults(ppd);
174   cupsMarkOptions(ppd, num_options, options);
175
176  /*
177   * Build the pstops command-line...
178   */
179
180   if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
181     cups_serverbin = CUPS_SERVERBIN;
182
183   snprintf(pstops_path, sizeof(pstops_path), "%s/filter/pstops",
184            cups_serverbin);
185
186   pstops_options = strdup(argv[5]);
187
188   if ((pstops_start = strstr(pstops_options, "fitplot")) != NULL &&
189       (!pstops_start[7] || isspace(pstops_start[7] & 255)))
190   {
191    /*
192     * Strip [no]fitplot option...
193     */
194
195     pstops_end = pstops_start + 7;
196
197     if ((pstops_start - pstops_options) >= 2 &&
198         !strncmp(pstops_start - 2, "no", 2))
199       pstops_start -= 2;
200
201     while (*pstops_end && isspace(*pstops_end & 255))
202       pstops_end ++;
203
204     _cups_strcpy(pstops_start, pstops_end);
205   }
206
207   if ((pstops_start = strstr(pstops_options, "fit-to-page")) != NULL &&
208       (!pstops_start[11] || isspace(pstops_start[11] & 255)))
209   {
210    /*
211     * Strip [no]fit-to-page option...
212     */
213
214     pstops_end = pstops_start + 11;
215
216     if ((pstops_start - pstops_options) >= 2 &&
217         !strncmp(pstops_start - 2, "no", 2))
218       pstops_start -= 2;
219
220     while (*pstops_end && isspace(*pstops_end & 255))
221       pstops_end ++;
222
223     _cups_strcpy(pstops_start, pstops_end);
224   }
225
226   if ((pstops_start = strstr(pstops_options, "landscape")) != NULL &&
227       (!pstops_start[9] || isspace(pstops_start[9] & 255)))
228   {
229    /*
230     * Strip [no]landscape option...
231     */
232
233     pstops_end = pstops_start + 9;
234
235     if ((pstops_start - pstops_options) >= 2 &&
236         !strncmp(pstops_start - 2, "no", 2))
237       pstops_start -= 2;
238
239     while (*pstops_end && isspace(*pstops_end & 255))
240       pstops_end ++;
241
242     _cups_strcpy(pstops_start, pstops_end);
243   }
244
245   if ((pstops_start = strstr(pstops_options, "orientation-requested=")) != NULL)
246   {
247    /*
248     * Strip [no]fitplot option...
249     */
250
251     pstops_end = pstops_start + 22;
252     while (*pstops_end && !isspace(*pstops_end & 255))
253       pstops_end ++;
254
255     _cups_strcpy(pstops_start, pstops_end);
256   }
257
258   pstops_argv[0] = argv[0];             /* Printer */
259   pstops_argv[1] = argv[1];             /* Job */
260   pstops_argv[2] = argv[2];             /* User */
261   pstops_argv[3] = argv[3];             /* Title */
262   pstops_argv[4] = argv[4];             /* Copies */
263   pstops_argv[5] = pstops_options;      /* Options */
264   pstops_argv[6] = NULL;
265
266  /*
267   * Build the command-line for the pdftops or gs filter...
268   */
269
270 #ifdef HAVE_PDFTOPS
271   pdf_argv[0] = (char *)"pdftops";
272   pdf_argc    = 1;
273 #else
274   pdf_argv[0] = (char *)"gs";
275   pdf_argv[1] = (char *)"-q";
276   pdf_argv[2] = (char *)"-dNOPAUSE";
277   pdf_argv[3] = (char *)"-dBATCH";
278   pdf_argv[4] = (char *)"-dSAFER";
279 #  ifdef HAVE_GHOSTSCRIPT_PS2WRITE
280   pdf_argv[5] = (char *)"-sDEVICE=ps2write";
281 #  else
282   pdf_argv[5] = (char *)"-sDEVICE=pswrite";
283 #  endif /* HAVE_GHOSTSCRIPT_PS2WRITE */
284   pdf_argv[6] = (char *)"-sOUTPUTFILE=%stdout";
285   pdf_argc    = 7;
286 #endif /* HAVE_PDFTOPS */
287
288   if (ppd)
289   {
290    /*
291     * Set language level and TrueType font handling...
292     */
293
294     if (ppd->language_level == 1)
295     {
296 #ifdef HAVE_PDFTOPS
297       pdf_argv[pdf_argc++] = (char *)"-level1";
298       pdf_argv[pdf_argc++] = (char *)"-noembtt";
299 #else
300       pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=1";
301 #endif /* HAVE_PDFTOPS */
302     }
303     else if (ppd->language_level == 2)
304     {
305 #ifdef HAVE_PDFTOPS
306       pdf_argv[pdf_argc++] = (char *)"-level2";
307       if (!ppd->ttrasterizer)
308         pdf_argv[pdf_argc++] = (char *)"-noembtt";
309 #else
310       pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=2";
311 #endif /* HAVE_PDFTOPS */
312     }
313     else
314 #ifdef HAVE_PDFTOPS
315       pdf_argv[pdf_argc++] = (char *)"-level3";
316 #else
317       pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=3";
318 #endif /* HAVE_PDFTOPS */
319
320     if ((val = cupsGetOption("fitplot", num_options, options)) == NULL)
321       val = cupsGetOption("fit-to-page", num_options, options);
322
323     if (val && _cups_strcasecmp(val, "no") && _cups_strcasecmp(val, "off") &&
324         _cups_strcasecmp(val, "false"))
325       fit = 1;
326     else
327       fit = 0;
328
329    /*
330     * Set output page size...
331     */
332
333     size = ppdPageSize(ppd, NULL);
334     if (size && fit)
335     {
336      /*
337       * Got the size, now get the orientation...
338       */
339
340       orientation = 0;
341
342       if ((val = cupsGetOption("landscape", num_options, options)) != NULL)
343       {
344         if (_cups_strcasecmp(val, "no") != 0 && _cups_strcasecmp(val, "off") != 0 &&
345             _cups_strcasecmp(val, "false") != 0)
346           orientation = 1;
347       }
348       else if ((val = cupsGetOption("orientation-requested", num_options,
349                                     options)) != NULL)
350       {
351        /*
352         * Map IPP orientation values to 0 to 3:
353         *
354         *   3 = 0 degrees   = 0
355         *   4 = 90 degrees  = 1
356         *   5 = -90 degrees = 3
357         *   6 = 180 degrees = 2
358         */
359
360         orientation = atoi(val) - 3;
361         if (orientation >= 2)
362           orientation ^= 1;
363       }
364
365 #ifdef HAVE_PDFTOPS
366       if (orientation & 1)
367       {
368         snprintf(pdf_width, sizeof(pdf_width), "%.0f", size->length);
369         snprintf(pdf_height, sizeof(pdf_height), "%.0f", size->width);
370       }
371       else
372       {
373         snprintf(pdf_width, sizeof(pdf_width), "%.0f", size->width);
374         snprintf(pdf_height, sizeof(pdf_height), "%.0f", size->length);
375       }
376
377       pdf_argv[pdf_argc++] = (char *)"-paperw";
378       pdf_argv[pdf_argc++] = pdf_width;
379       pdf_argv[pdf_argc++] = (char *)"-paperh";
380       pdf_argv[pdf_argc++] = pdf_height;
381       pdf_argv[pdf_argc++] = (char *)"-expand";
382
383 #else
384       if (orientation & 1)
385       {
386         snprintf(pdf_width, sizeof(pdf_width), "-dDEVICEWIDTHPOINTS=%.0f",
387                  size->length);
388         snprintf(pdf_height, sizeof(pdf_height), "-dDEVICEHEIGHTPOINTS=%.0f",
389                  size->width);
390       }
391       else
392       {
393         snprintf(pdf_width, sizeof(pdf_width), "-dDEVICEWIDTHPOINTS=%.0f",
394                  size->width);
395         snprintf(pdf_height, sizeof(pdf_height), "-dDEVICEHEIGHTPOINTS=%.0f",
396                  size->length);
397       }
398
399       pdf_argv[pdf_argc++] = pdf_width;
400       pdf_argv[pdf_argc++] = pdf_height;
401 #endif /* HAVE_PDFTOPS */
402     }
403 #if defined(HAVE_PDFTOPS) && defined(HAVE_PDFTOPS_WITH_ORIGPAGESIZES)
404     else
405     {
406      /*
407       *  Use the page sizes of the original PDF document, this way documents
408       *  which contain pages of different sizes can be printed correctly
409       */
410
411       pdf_argv[pdf_argc++] = (char *)"-origpagesizes";
412     }
413 #endif /* HAVE_PDFTOPS && HAVE_PDFTOPS_WITH_ORIGPAGESIZES */
414   }
415
416 #ifdef HAVE_PDFTOPS
417   pdf_argv[pdf_argc++] = filename;
418   pdf_argv[pdf_argc++] = (char *)"-";
419 #else
420   pdf_argv[pdf_argc++] = (char *)"-c";
421   pdf_argv[pdf_argc++] = (char *)"save pop";
422   pdf_argv[pdf_argc++] = (char *)"-f";
423   pdf_argv[pdf_argc++] = filename;
424 #endif /* HAVE_PDFTOPS */
425
426   pdf_argv[pdf_argc] = NULL;
427
428  /*
429   * Execute "pdftops/gs | pstops"...
430   */
431
432   if (pipe(pstops_pipe))
433   {
434     perror("DEBUG: Unable to create pipe");
435
436     exit_status = 1;
437     goto error;
438   }
439
440   if ((pdf_pid = fork()) == 0)
441   {
442    /*
443     * Child comes here...
444     */
445
446     dup2(pstops_pipe[1], 1);
447     close(pstops_pipe[0]);
448     close(pstops_pipe[1]);
449
450 #ifdef HAVE_PDFTOPS
451     execv(CUPS_PDFTOPS, pdf_argv);
452     perror("DEBUG: Unable to execute pdftops program");
453 #else
454     execv(CUPS_GHOSTSCRIPT, pdf_argv);
455     perror("DEBUG: Unable to execute gs program");
456 #endif /* HAVE_PDFTOPS */
457
458     exit(1);
459   }
460   else if (pdf_pid < 0)
461   {
462    /*
463     * Unable to fork!
464     */
465
466 #ifdef HAVE_PDFTOPS
467     perror("DEBUG: Unable to execute pdftops program");
468 #else
469     perror("DEBUG: Unable to execute gs program");
470 #endif /* HAVE_PDFTOPS */
471
472     exit_status = 1;
473     goto error;
474   }
475
476   fprintf(stderr, "DEBUG: Started filter %s (PID %d)\n", pdf_argv[0], pdf_pid);
477
478   if ((pstops_pid = fork()) == 0)
479   {
480    /*
481     * Child comes here...
482     */
483
484     dup2(pstops_pipe[0], 0);
485     close(pstops_pipe[0]);
486     close(pstops_pipe[1]);
487
488     execv(pstops_path, pstops_argv);
489     perror("DEBUG: Unable to execute pstops program");
490
491     exit(1);
492   }
493   else if (pstops_pid < 0)
494   {
495    /*
496     * Unable to fork!
497     */
498
499     perror("DEBUG: Unable to execute pstops program");
500
501     exit_status = 1;
502     goto error;
503   }
504
505   fprintf(stderr, "DEBUG: Started filter pstops (PID %d)\n", pstops_pid);
506
507   close(pstops_pipe[0]);
508   close(pstops_pipe[1]);
509
510  /*
511   * Wait for the child processes to exit...
512   */
513
514   wait_children = 2;
515
516   while (wait_children > 0)
517   {
518    /*
519     * Wait until we get a valid process ID or the job is canceled...
520     */
521
522     while ((wait_pid = wait(&wait_status)) < 0 && errno == EINTR)
523     {
524       if (job_canceled)
525       {
526         kill(pdf_pid, SIGTERM);
527         kill(pstops_pid, SIGTERM);
528
529         job_canceled = 0;
530       }
531     }
532
533     if (wait_pid < 0)
534       break;
535
536     wait_children --;
537
538    /*
539     * Report child status...
540     */
541
542     if (wait_status)
543     {
544       if (WIFEXITED(wait_status))
545       {
546         exit_status = WEXITSTATUS(wait_status);
547
548         fprintf(stderr, "DEBUG: PID %d (%s) stopped with status %d!\n",
549                 wait_pid,
550 #ifdef HAVE_PDFTOPS
551                 wait_pid == pdf_pid ? "pdftops" : "pstops",
552 #else
553                 wait_pid == pdf_pid ? "gs" : "pstops",
554 #endif /* HAVE_PDFTOPS */
555                 exit_status);
556       }
557       else if (WTERMSIG(wait_status) == SIGTERM)
558       {
559         fprintf(stderr,
560                 "DEBUG: PID %d (%s) was terminated normally with signal %d!\n",
561                 wait_pid,
562 #ifdef HAVE_PDFTOPS
563                 wait_pid == pdf_pid ? "pdftops" : "pstops",
564 #else
565                 wait_pid == pdf_pid ? "gs" : "pstops",
566 #endif /* HAVE_PDFTOPS */
567                 exit_status);
568       }
569       else
570       {
571         exit_status = WTERMSIG(wait_status);
572
573         fprintf(stderr, "DEBUG: PID %d (%s) crashed on signal %d!\n", wait_pid,
574 #ifdef HAVE_PDFTOPS
575                 wait_pid == pdf_pid ? "pdftops" : "pstops",
576 #else
577                 wait_pid == pdf_pid ? "gs" : "pstops",
578 #endif /* HAVE_PDFTOPS */
579                 exit_status);
580       }
581     }
582     else
583     {
584       fprintf(stderr, "DEBUG: PID %d (%s) exited with no errors.\n", wait_pid,
585 #ifdef HAVE_PDFTOPS
586               wait_pid == pdf_pid ? "pdftops" : "pstops");
587 #else
588               wait_pid == pdf_pid ? "gs" : "pstops");
589 #endif /* HAVE_PDFTOPS */
590     }
591   }
592
593  /*
594   * Cleanup and exit...
595   */
596
597   error:
598
599   if (tempfile[0])
600     unlink(tempfile);
601
602   return (exit_status);
603 }
604
605
606 /*
607  * 'cancel_job()' - Flag the job as canceled.
608  */
609
610 static void
611 cancel_job(int sig)                     /* I - Signal number (unused) */
612 {
613   (void)sig;
614
615   job_canceled = 1;
616 }
617
618
619 /*
620  * End of "$Id: pdftops.c 9793 2011-05-20 03:49:49Z mike $".
621  */