]> git.ipfire.org Git - thirdparty/cups.git/blame - filter/pdftops.c
Merge changes from CUPS 1.5svn-r9385.
[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 *
aaf19ab0 6 * Copyright 2007-2010 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
96 /*
97 * Make sure we have the right number of arguments for CUPS!
98 */
99
100 if (argc < 6 || argc > 7)
101 {
102 _cupsLangPrintf(stderr,
0837b7e8 103 _("Usage: %s job user title copies options [filename]"),
91c84a35
MS
104 argv[0]);
105 return (1);
106 }
107
108 /*
109 * Register a signal handler to cleanly cancel a job.
110 */
111
112#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
113 sigset(SIGTERM, cancel_job);
114#elif defined(HAVE_SIGACTION)
115 memset(&action, 0, sizeof(action));
116
117 sigemptyset(&action.sa_mask);
118 action.sa_handler = cancel_job;
119 sigaction(SIGTERM, &action, NULL);
120#else
121 signal(SIGTERM, cancel_job);
122#endif /* HAVE_SIGSET */
123
124 /*
125 * Copy stdin if needed...
126 */
127
128 if (argc == 6)
129 {
130 /*
131 * Copy stdin to a temp file...
132 */
133
134 if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
135 {
c7017ecc 136 perror("DEBUG: Unable to copy PDF file");
91c84a35
MS
137 return (1);
138 }
139
140 fprintf(stderr, "DEBUG: pdftops - copying to temp print file \"%s\"\n",
141 tempfile);
142
143 while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
144 write(fd, buffer, bytes);
145
146 close(fd);
147
148 filename = tempfile;
149 }
150 else
151 {
152 /*
153 * Use the filename on the command-line...
154 */
155
156 filename = argv[6];
157 tempfile[0] = '\0';
158 }
159
160 /*
161 * Load the PPD file and mark options...
162 */
163
164 ppd = ppdOpenFile(getenv("PPD"));
165 num_options = cupsParseOptions(argv[5], 0, &options);
166
167 ppdMarkDefaults(ppd);
168 cupsMarkOptions(ppd, num_options, options);
169
b0f6947b
MS
170 /*
171 * Build the pstops command-line...
172 */
173
174 if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
175 cups_serverbin = CUPS_SERVERBIN;
176
177 snprintf(pstops_path, sizeof(pstops_path), "%s/filter/pstops",
178 cups_serverbin);
179
180 pstops_options = strdup(argv[5]);
181
182 if ((pstops_start = strstr(pstops_options, "fitplot")) != NULL &&
183 (!pstops_start[7] || isspace(pstops_start[7] & 255)))
184 {
185 /*
186 * Strip [no]fitplot option...
187 */
188
189 pstops_end = pstops_start + 7;
190
191 if ((pstops_start - pstops_options) >= 2 &&
192 !strncmp(pstops_start - 2, "no", 2))
193 pstops_start -= 2;
194
195 while (*pstops_end && isspace(*pstops_end & 255))
196 pstops_end ++;
197
198 _cups_strcpy(pstops_start, pstops_end);
199 }
200
201 if ((pstops_start = strstr(pstops_options, "fit-to-page")) != NULL &&
202 (!pstops_start[11] || isspace(pstops_start[11] & 255)))
203 {
204 /*
205 * Strip [no]fit-to-page option...
206 */
207
208 pstops_end = pstops_start + 11;
209
210 if ((pstops_start - pstops_options) >= 2 &&
211 !strncmp(pstops_start - 2, "no", 2))
212 pstops_start -= 2;
213
214 while (*pstops_end && isspace(*pstops_end & 255))
215 pstops_end ++;
216
217 _cups_strcpy(pstops_start, pstops_end);
218 }
219
220 if ((pstops_start = strstr(pstops_options, "landscape")) != NULL &&
221 (!pstops_start[9] || isspace(pstops_start[9] & 255)))
222 {
223 /*
224 * Strip [no]landscape option...
225 */
226
227 pstops_end = pstops_start + 9;
228
229 if ((pstops_start - pstops_options) >= 2 &&
230 !strncmp(pstops_start - 2, "no", 2))
231 pstops_start -= 2;
232
233 while (*pstops_end && isspace(*pstops_end & 255))
234 pstops_end ++;
235
236 _cups_strcpy(pstops_start, pstops_end);
237 }
238
239 if ((pstops_start = strstr(pstops_options, "orientation-requested=")) != NULL)
240 {
241 /*
242 * Strip [no]fitplot option...
243 */
244
245 pstops_end = pstops_start + 22;
246 while (*pstops_end && !isspace(*pstops_end & 255))
247 pstops_end ++;
248
249 _cups_strcpy(pstops_start, pstops_end);
250 }
251
252 pstops_argv[0] = argv[0]; /* Printer */
253 pstops_argv[1] = argv[1]; /* Job */
254 pstops_argv[2] = argv[2]; /* User */
255 pstops_argv[3] = argv[3]; /* Title */
256 pstops_argv[4] = argv[4]; /* Copies */
257 pstops_argv[5] = pstops_options; /* Options */
258 pstops_argv[6] = NULL;
259
91c84a35 260 /*
ae71f5de 261 * Build the command-line for the pdftops or gs filter...
91c84a35
MS
262 */
263
ae71f5de 264#ifdef HAVE_PDFTOPS
b0f6947b
MS
265 pdf_argv[0] = (char *)"pdftops";
266 pdf_argc = 1;
ae71f5de 267#else
b0f6947b
MS
268 pdf_argv[0] = (char *)"gs";
269 pdf_argv[1] = (char *)"-q";
270 pdf_argv[2] = (char *)"-dNOPAUSE";
271 pdf_argv[3] = (char *)"-dBATCH";
272 pdf_argv[4] = (char *)"-dSAFER";
273 pdf_argv[5] = (char *)"-sDEVICE=pswrite";
274 pdf_argv[6] = (char *)"-sOUTPUTFILE=%stdout";
275 pdf_argc = 7;
ae71f5de 276#endif /* HAVE_PDFTOPS */
91c84a35
MS
277
278 if (ppd)
279 {
280 /*
281 * Set language level and TrueType font handling...
282 */
283
284 if (ppd->language_level == 1)
285 {
ae71f5de 286#ifdef HAVE_PDFTOPS
b0f6947b
MS
287 pdf_argv[pdf_argc++] = (char *)"-level1";
288 pdf_argv[pdf_argc++] = (char *)"-noembtt";
ae71f5de 289#else
b0f6947b 290 pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=1";
ae71f5de 291#endif /* HAVE_PDFTOPS */
91c84a35
MS
292 }
293 else if (ppd->language_level == 2)
294 {
ae71f5de 295#ifdef HAVE_PDFTOPS
b0f6947b 296 pdf_argv[pdf_argc++] = (char *)"-level2";
91c84a35 297 if (!ppd->ttrasterizer)
b0f6947b 298 pdf_argv[pdf_argc++] = (char *)"-noembtt";
ae71f5de 299#else
b0f6947b 300 pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=2";
ae71f5de 301#endif /* HAVE_PDFTOPS */
91c84a35
MS
302 }
303 else
ae71f5de 304#ifdef HAVE_PDFTOPS
b0f6947b 305 pdf_argv[pdf_argc++] = (char *)"-level3";
ae71f5de 306#else
b0f6947b 307 pdf_argv[pdf_argc++] = (char *)"-dLanguageLevel=3";
ae71f5de 308#endif /* HAVE_PDFTOPS */
91c84a35 309
dfd5680b
MS
310 if ((val = cupsGetOption("fitplot", num_options, options)) == NULL)
311 val = cupsGetOption("fit-to-page", num_options, options);
312
313 if (val && strcasecmp(val, "no") && strcasecmp(val, "off") &&
314 strcasecmp(val, "false"))
315 fit = 1;
316 else
317 fit = 0;
318
91c84a35
MS
319 /*
320 * Set output page size...
321 */
322
323 size = ppdPageSize(ppd, NULL);
dfd5680b 324 if (size && fit)
91c84a35
MS
325 {
326 /*
327 * Got the size, now get the orientation...
328 */
329
330 orientation = 0;
331
332 if ((val = cupsGetOption("landscape", num_options, options)) != NULL)
333 {
334 if (strcasecmp(val, "no") != 0 && strcasecmp(val, "off") != 0 &&
335 strcasecmp(val, "false") != 0)
336 orientation = 1;
337 }
338 else if ((val = cupsGetOption("orientation-requested", num_options, options)) != NULL)
339 {
340 /*
341 * Map IPP orientation values to 0 to 3:
342 *
343 * 3 = 0 degrees = 0
344 * 4 = 90 degrees = 1
345 * 5 = -90 degrees = 3
346 * 6 = 180 degrees = 2
347 */
348
349 orientation = atoi(val) - 3;
350 if (orientation >= 2)
351 orientation ^= 1;
352 }
353
ae71f5de 354#ifdef HAVE_PDFTOPS
91c84a35
MS
355 if (orientation & 1)
356 {
b0f6947b
MS
357 snprintf(pdf_width, sizeof(pdf_width), "%.0f", size->length);
358 snprintf(pdf_height, sizeof(pdf_height), "%.0f", size->width);
91c84a35
MS
359 }
360 else
361 {
b0f6947b
MS
362 snprintf(pdf_width, sizeof(pdf_width), "%.0f", size->width);
363 snprintf(pdf_height, sizeof(pdf_height), "%.0f", size->length);
91c84a35
MS
364 }
365
b0f6947b
MS
366 pdf_argv[pdf_argc++] = (char *)"-paperw";
367 pdf_argv[pdf_argc++] = pdf_width;
368 pdf_argv[pdf_argc++] = (char *)"-paperh";
369 pdf_argv[pdf_argc++] = pdf_height;
370 pdf_argv[pdf_argc++] = (char *)"-expand";
d1c13e16 371
ae71f5de
MS
372#else
373 if (orientation & 1)
d1c13e16 374 {
b0f6947b 375 snprintf(pdf_width, sizeof(pdf_width), "-dDEVICEWIDTHPOINTS=%.0f",
d1c13e16 376 size->length);
b0f6947b 377 snprintf(pdf_height, sizeof(pdf_height), "-dDEVICEHEIGHTPOINTS=%.0f",
ae71f5de 378 size->width);
d1c13e16 379 }
ae71f5de 380 else
d1c13e16 381 {
b0f6947b 382 snprintf(pdf_width, sizeof(pdf_width), "-dDEVICEWIDTHPOINTS=%.0f",
d1c13e16 383 size->width);
b0f6947b 384 snprintf(pdf_height, sizeof(pdf_height), "-dDEVICEHEIGHTPOINTS=%.0f",
ae71f5de 385 size->length);
d1c13e16 386 }
ae71f5de 387
b0f6947b
MS
388 pdf_argv[pdf_argc++] = pdf_width;
389 pdf_argv[pdf_argc++] = pdf_height;
ae71f5de 390#endif /* HAVE_PDFTOPS */
91c84a35
MS
391 }
392 }
393
ae71f5de 394#ifdef HAVE_PDFTOPS
b0f6947b
MS
395 pdf_argv[pdf_argc++] = filename;
396 pdf_argv[pdf_argc++] = (char *)"-";
ae71f5de 397#else
b0f6947b
MS
398 pdf_argv[pdf_argc++] = (char *)"-c";
399 pdf_argv[pdf_argc++] = (char *)"save pop";
400 pdf_argv[pdf_argc++] = (char *)"-f";
401 pdf_argv[pdf_argc++] = filename;
ae71f5de
MS
402#endif /* HAVE_PDFTOPS */
403
b0f6947b
MS
404 pdf_argv[pdf_argc] = NULL;
405
406 /*
407 * Execute "pdftops/gs | pstops"...
408 */
409
410 if (pipe(pstops_pipe))
411 {
c7017ecc 412 perror("DEBUG: Unable to create pipe");
b0f6947b
MS
413
414 exit_status = 1;
415 goto error;
416 }
91c84a35 417
b0f6947b 418 if ((pdf_pid = fork()) == 0)
91c84a35
MS
419 {
420 /*
421 * Child comes here...
422 */
423
b0f6947b
MS
424 dup2(pstops_pipe[1], 1);
425 close(pstops_pipe[0]);
426 close(pstops_pipe[1]);
427
ae71f5de 428#ifdef HAVE_PDFTOPS
b0f6947b 429 execv(CUPS_PDFTOPS, pdf_argv);
c7017ecc 430 perror("DEBUG: Unable to execute pdftops program");
ae71f5de 431#else
b0f6947b 432 execv(CUPS_GHOSTSCRIPT, pdf_argv);
c7017ecc 433 perror("DEBUG: Unable to execute gs program");
ae71f5de
MS
434#endif /* HAVE_PDFTOPS */
435
91c84a35
MS
436 exit(1);
437 }
b0f6947b 438 else if (pdf_pid < 0)
91c84a35
MS
439 {
440 /*
441 * Unable to fork!
442 */
443
ae71f5de 444#ifdef HAVE_PDFTOPS
c7017ecc 445 perror("DEBUG: Unable to execute pdftops program");
ae71f5de 446#else
c7017ecc 447 perror("DEBUG: Unable to execute gs program");
ae71f5de
MS
448#endif /* HAVE_PDFTOPS */
449
b0f6947b
MS
450 exit_status = 1;
451 goto error;
91c84a35 452 }
b0f6947b
MS
453
454 fprintf(stderr, "DEBUG: Started filter %s (PID %d)\n", pdf_argv[0], pdf_pid);
455
456 if ((pstops_pid = fork()) == 0)
91c84a35
MS
457 {
458 /*
b0f6947b 459 * Child comes here...
91c84a35
MS
460 */
461
b0f6947b
MS
462 dup2(pstops_pipe[0], 0);
463 close(pstops_pipe[0]);
464 close(pstops_pipe[1]);
465
466 execv(pstops_path, pstops_argv);
c7017ecc 467 perror("DEBUG: Unable to execute pstops program");
b0f6947b
MS
468
469 exit(1);
470 }
471 else if (pstops_pid < 0)
472 {
473 /*
474 * Unable to fork!
475 */
476
c7017ecc 477 perror("DEBUG: Unable to execute pstops program");
b0f6947b
MS
478
479 exit_status = 1;
480 goto error;
481 }
482
483 fprintf(stderr, "DEBUG: Started filter pstops (PID %d)\n", pstops_pid);
484
485 close(pstops_pipe[0]);
486 close(pstops_pipe[1]);
487
488 /*
489 * Wait for the child processes to exit...
490 */
491
492 wait_children = 2;
493
494 while (wait_children > 0)
495 {
496 /*
497 * Wait until we get a valid process ID or the job is canceled...
498 */
839a51c8 499
b0f6947b
MS
500 while ((wait_pid = wait(&wait_status)) < 0 && errno == EINTR)
501 {
839a51c8 502 if (job_canceled)
3dfe78b3 503 {
b0f6947b
MS
504 kill(pdf_pid, SIGTERM);
505 kill(pstops_pid, SIGTERM);
506
3dfe78b3
MS
507 job_canceled = 0;
508 }
839a51c8
MS
509 }
510
b0f6947b
MS
511 if (wait_pid < 0)
512 break;
513
514 wait_children --;
515
516 /*
517 * Report child status...
518 */
519
520 if (wait_status)
91c84a35 521 {
b0f6947b 522 if (WIFEXITED(wait_status))
91c84a35 523 {
b0f6947b 524 exit_status = WEXITSTATUS(wait_status);
91c84a35 525
b0f6947b 526 fprintf(stderr, "DEBUG: PID %d (%s) stopped with status %d!\n",
aaf19ab0 527 wait_pid,
b0f6947b
MS
528#ifdef HAVE_PDFTOPS
529 wait_pid == pdf_pid ? "pdftops" : "pstops",
530#else
531 wait_pid == pdf_pid ? "gs" : "pstops",
532#endif /* HAVE_PDFTOPS */
533 exit_status);
534 }
535 else if (WTERMSIG(wait_status) == SIGTERM)
536 {
537 fprintf(stderr,
538 "DEBUG: PID %d (%s) was terminated normally with signal %d!\n",
aaf19ab0 539 wait_pid,
b0f6947b
MS
540#ifdef HAVE_PDFTOPS
541 wait_pid == pdf_pid ? "pdftops" : "pstops",
542#else
543 wait_pid == pdf_pid ? "gs" : "pstops",
544#endif /* HAVE_PDFTOPS */
545 exit_status);
91c84a35
MS
546 }
547 else
548 {
b0f6947b 549 exit_status = WTERMSIG(wait_status);
91c84a35 550
aaf19ab0 551 fprintf(stderr, "DEBUG: PID %d (%s) crashed on signal %d!\n", wait_pid,
b0f6947b
MS
552#ifdef HAVE_PDFTOPS
553 wait_pid == pdf_pid ? "pdftops" : "pstops",
554#else
555 wait_pid == pdf_pid ? "gs" : "pstops",
556#endif /* HAVE_PDFTOPS */
557 exit_status);
91c84a35
MS
558 }
559 }
b0f6947b
MS
560 else
561 {
aaf19ab0 562 fprintf(stderr, "DEBUG: PID %d (%s) exited with no errors.\n", wait_pid,
b0f6947b
MS
563#ifdef HAVE_PDFTOPS
564 wait_pid == pdf_pid ? "pdftops" : "pstops");
565#else
566 wait_pid == pdf_pid ? "gs" : "pstops");
567#endif /* HAVE_PDFTOPS */
568 }
91c84a35
MS
569 }
570
571 /*
572 * Cleanup and exit...
573 */
574
b0f6947b
MS
575 error:
576
91c84a35
MS
577 if (tempfile[0])
578 unlink(tempfile);
579
b0f6947b 580 return (exit_status);
91c84a35
MS
581}
582
583
584/*
585 * 'cancel_job()' - Flag the job as canceled.
586 */
587
588static void
589cancel_job(int sig) /* I - Signal number (unused) */
590{
591 (void)sig;
839a51c8
MS
592
593 job_canceled = 1;
91c84a35
MS
594}
595
596
597/*
598 * End of "$Id$".
599 */