]> git.ipfire.org Git - thirdparty/cups.git/blame - filter/pdftops.c
Merge changes from CUPS 1.5.1-r9875.
[thirdparty/cups.git] / filter / pdftops.c
CommitLineData
91c84a35
MS
1/*
2 * "$Id$"
3 *
aaf19ab0 4 * PDF to PostScript filter front-end for CUPS.
91c84a35 5 *
eac3a0a0 6 * Copyright 2007-2011 by Apple Inc.
91c84a35
MS
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>
39ff2fe7 26#include <cups/ppd.h>
aaf19ab0
MS
27#include <cups/string-private.h>
28#include <cups/language-private.h>
91c84a35
MS
29#include <signal.h>
30#include <sys/wait.h>
839a51c8 31#include <errno.h>
91c84a35
MS
32
33
34/*
35 * Local functions...
36 */
37
38static void cancel_job(int sig);
39
40
839a51c8
MS
41/*
42 * Local globals...
43 */
44
45static int job_canceled = 0;
46
47
91c84a35
MS
48/*
49 * 'main()' - Main entry for filter...
50 */
51
52int /* O - Exit status */
53main(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 */
dfd5680b
MS
64 int orientation, /* Output orientation */
65 fit; /* Fit output to default page size? */
91c84a35
MS
66 ppd_file_t *ppd; /* PPD file */
67 ppd_size_t *size; /* Current page size */
b0f6947b
MS
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 */
91c84a35
MS
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
eac3a0a0
MS
96 /*
97 * Ignore broken pipe signals...
98 */
99
100 signal(SIGPIPE, SIG_IGN);
101
91c84a35
MS
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,
0837b7e8 109 _("Usage: %s job user title copies options [filename]"),
91c84a35
MS
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 {
c7017ecc 142 perror("DEBUG: Unable to copy PDF file");
91c84a35
MS
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
b0f6947b
MS
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
91c84a35 266 /*
ae71f5de 267 * Build the command-line for the pdftops or gs filter...
91c84a35
MS
268 */
269
ae71f5de 270#ifdef HAVE_PDFTOPS
b0f6947b
MS
271 pdf_argv[0] = (char *)"pdftops";
272 pdf_argc = 1;
ae71f5de 273#else
b0f6947b
MS
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";
eac3a0a0
MS
279# ifdef HAVE_GHOSTSCRIPT_PS2WRITE
280 pdf_argv[5] = (char *)"-sDEVICE=ps2write";
281# else
b0f6947b 282 pdf_argv[5] = (char *)"-sDEVICE=pswrite";
eac3a0a0 283# endif /* HAVE_GHOSTSCRIPT_PS2WRITE */
b0f6947b
MS
284 pdf_argv[6] = (char *)"-sOUTPUTFILE=%stdout";
285 pdf_argc = 7;
ae71f5de 286#endif /* HAVE_PDFTOPS */
91c84a35
MS
287
288 if (ppd)
289 {
290 /*
291 * Set language level and TrueType font handling...
292 */
293
294 if (ppd->language_level == 1)
295 {
ae71f5de 296#ifdef HAVE_PDFTOPS
b0f6947b
MS
297 pdf_argv[pdf_argc++] = (char *)"-level1";
298 pdf_argv[pdf_argc++] = (char *)"-noembtt";
ae71f5de 299#else
b0f6947b 300 pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=1";
ae71f5de 301#endif /* HAVE_PDFTOPS */
91c84a35
MS
302 }
303 else if (ppd->language_level == 2)
304 {
ae71f5de 305#ifdef HAVE_PDFTOPS
b0f6947b 306 pdf_argv[pdf_argc++] = (char *)"-level2";
91c84a35 307 if (!ppd->ttrasterizer)
b0f6947b 308 pdf_argv[pdf_argc++] = (char *)"-noembtt";
ae71f5de 309#else
b0f6947b 310 pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=2";
ae71f5de 311#endif /* HAVE_PDFTOPS */
91c84a35
MS
312 }
313 else
ae71f5de 314#ifdef HAVE_PDFTOPS
b0f6947b 315 pdf_argv[pdf_argc++] = (char *)"-level3";
ae71f5de 316#else
b0f6947b 317 pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=3";
ae71f5de 318#endif /* HAVE_PDFTOPS */
91c84a35 319
dfd5680b
MS
320 if ((val = cupsGetOption("fitplot", num_options, options)) == NULL)
321 val = cupsGetOption("fit-to-page", num_options, options);
322
88f9aafc
MS
323 if (val && _cups_strcasecmp(val, "no") && _cups_strcasecmp(val, "off") &&
324 _cups_strcasecmp(val, "false"))
dfd5680b
MS
325 fit = 1;
326 else
327 fit = 0;
328
91c84a35
MS
329 /*
330 * Set output page size...
331 */
332
333 size = ppdPageSize(ppd, NULL);
dfd5680b 334 if (size && fit)
91c84a35
MS
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 {
88f9aafc
MS
344 if (_cups_strcasecmp(val, "no") != 0 && _cups_strcasecmp(val, "off") != 0 &&
345 _cups_strcasecmp(val, "false") != 0)
91c84a35
MS
346 orientation = 1;
347 }
eac3a0a0
MS
348 else if ((val = cupsGetOption("orientation-requested", num_options,
349 options)) != NULL)
91c84a35
MS
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
ae71f5de 365#ifdef HAVE_PDFTOPS
91c84a35
MS
366 if (orientation & 1)
367 {
b0f6947b
MS
368 snprintf(pdf_width, sizeof(pdf_width), "%.0f", size->length);
369 snprintf(pdf_height, sizeof(pdf_height), "%.0f", size->width);
91c84a35
MS
370 }
371 else
372 {
b0f6947b
MS
373 snprintf(pdf_width, sizeof(pdf_width), "%.0f", size->width);
374 snprintf(pdf_height, sizeof(pdf_height), "%.0f", size->length);
91c84a35
MS
375 }
376
b0f6947b
MS
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";
d1c13e16 382
ae71f5de
MS
383#else
384 if (orientation & 1)
d1c13e16 385 {
b0f6947b 386 snprintf(pdf_width, sizeof(pdf_width), "-dDEVICEWIDTHPOINTS=%.0f",
d1c13e16 387 size->length);
b0f6947b 388 snprintf(pdf_height, sizeof(pdf_height), "-dDEVICEHEIGHTPOINTS=%.0f",
ae71f5de 389 size->width);
d1c13e16 390 }
ae71f5de 391 else
d1c13e16 392 {
b0f6947b 393 snprintf(pdf_width, sizeof(pdf_width), "-dDEVICEWIDTHPOINTS=%.0f",
d1c13e16 394 size->width);
b0f6947b 395 snprintf(pdf_height, sizeof(pdf_height), "-dDEVICEHEIGHTPOINTS=%.0f",
ae71f5de 396 size->length);
d1c13e16 397 }
ae71f5de 398
b0f6947b
MS
399 pdf_argv[pdf_argc++] = pdf_width;
400 pdf_argv[pdf_argc++] = pdf_height;
ae71f5de 401#endif /* HAVE_PDFTOPS */
91c84a35 402 }
eac3a0a0
MS
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 */
91c84a35
MS
414 }
415
ae71f5de 416#ifdef HAVE_PDFTOPS
b0f6947b
MS
417 pdf_argv[pdf_argc++] = filename;
418 pdf_argv[pdf_argc++] = (char *)"-";
ae71f5de 419#else
b0f6947b
MS
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;
ae71f5de
MS
424#endif /* HAVE_PDFTOPS */
425
b0f6947b
MS
426 pdf_argv[pdf_argc] = NULL;
427
428 /*
429 * Execute "pdftops/gs | pstops"...
430 */
431
432 if (pipe(pstops_pipe))
433 {
c7017ecc 434 perror("DEBUG: Unable to create pipe");
b0f6947b
MS
435
436 exit_status = 1;
437 goto error;
438 }
91c84a35 439
b0f6947b 440 if ((pdf_pid = fork()) == 0)
91c84a35
MS
441 {
442 /*
443 * Child comes here...
444 */
445
b0f6947b
MS
446 dup2(pstops_pipe[1], 1);
447 close(pstops_pipe[0]);
448 close(pstops_pipe[1]);
449
ae71f5de 450#ifdef HAVE_PDFTOPS
b0f6947b 451 execv(CUPS_PDFTOPS, pdf_argv);
c7017ecc 452 perror("DEBUG: Unable to execute pdftops program");
ae71f5de 453#else
b0f6947b 454 execv(CUPS_GHOSTSCRIPT, pdf_argv);
c7017ecc 455 perror("DEBUG: Unable to execute gs program");
ae71f5de
MS
456#endif /* HAVE_PDFTOPS */
457
91c84a35
MS
458 exit(1);
459 }
b0f6947b 460 else if (pdf_pid < 0)
91c84a35
MS
461 {
462 /*
463 * Unable to fork!
464 */
465
ae71f5de 466#ifdef HAVE_PDFTOPS
c7017ecc 467 perror("DEBUG: Unable to execute pdftops program");
ae71f5de 468#else
c7017ecc 469 perror("DEBUG: Unable to execute gs program");
ae71f5de
MS
470#endif /* HAVE_PDFTOPS */
471
b0f6947b
MS
472 exit_status = 1;
473 goto error;
91c84a35 474 }
b0f6947b
MS
475
476 fprintf(stderr, "DEBUG: Started filter %s (PID %d)\n", pdf_argv[0], pdf_pid);
477
478 if ((pstops_pid = fork()) == 0)
91c84a35
MS
479 {
480 /*
b0f6947b 481 * Child comes here...
91c84a35
MS
482 */
483
b0f6947b
MS
484 dup2(pstops_pipe[0], 0);
485 close(pstops_pipe[0]);
486 close(pstops_pipe[1]);
487
488 execv(pstops_path, pstops_argv);
c7017ecc 489 perror("DEBUG: Unable to execute pstops program");
b0f6947b
MS
490
491 exit(1);
492 }
493 else if (pstops_pid < 0)
494 {
495 /*
496 * Unable to fork!
497 */
498
c7017ecc 499 perror("DEBUG: Unable to execute pstops program");
b0f6947b
MS
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 */
839a51c8 521
b0f6947b
MS
522 while ((wait_pid = wait(&wait_status)) < 0 && errno == EINTR)
523 {
839a51c8 524 if (job_canceled)
3dfe78b3 525 {
b0f6947b
MS
526 kill(pdf_pid, SIGTERM);
527 kill(pstops_pid, SIGTERM);
528
3dfe78b3
MS
529 job_canceled = 0;
530 }
839a51c8
MS
531 }
532
b0f6947b
MS
533 if (wait_pid < 0)
534 break;
535
536 wait_children --;
537
538 /*
539 * Report child status...
540 */
541
542 if (wait_status)
91c84a35 543 {
b0f6947b 544 if (WIFEXITED(wait_status))
91c84a35 545 {
b0f6947b 546 exit_status = WEXITSTATUS(wait_status);
91c84a35 547
b0f6947b 548 fprintf(stderr, "DEBUG: PID %d (%s) stopped with status %d!\n",
aaf19ab0 549 wait_pid,
b0f6947b
MS
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",
aaf19ab0 561 wait_pid,
b0f6947b
MS
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);
91c84a35
MS
568 }
569 else
570 {
b0f6947b 571 exit_status = WTERMSIG(wait_status);
91c84a35 572
aaf19ab0 573 fprintf(stderr, "DEBUG: PID %d (%s) crashed on signal %d!\n", wait_pid,
b0f6947b
MS
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);
91c84a35
MS
580 }
581 }
b0f6947b
MS
582 else
583 {
aaf19ab0 584 fprintf(stderr, "DEBUG: PID %d (%s) exited with no errors.\n", wait_pid,
b0f6947b
MS
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 }
91c84a35
MS
591 }
592
593 /*
594 * Cleanup and exit...
595 */
596
b0f6947b
MS
597 error:
598
91c84a35
MS
599 if (tempfile[0])
600 unlink(tempfile);
601
b0f6947b 602 return (exit_status);
91c84a35
MS
603}
604
605
606/*
607 * 'cancel_job()' - Flag the job as canceled.
608 */
609
610static void
611cancel_job(int sig) /* I - Signal number (unused) */
612{
613 (void)sig;
839a51c8
MS
614
615 job_canceled = 1;
91c84a35
MS
616}
617
618
619/*
620 * End of "$Id$".
621 */