1 /*
2 * PostScript filter for CUPS.
3 *
4 * Copyright © 2007-2018 by Apple Inc.
5 * Copyright © 1993-2007 by Easy Software Products.
6 *
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
9 */
10
11 /*
12 * Include necessary headers...
13 */
14
15 #include "common.h"
16 #include <limits.h>
17 #include <math.h>
18 #include <cups/file.h>
19 #include <cups/array.h>
20 #include <cups/language-private.h>
21 #include <signal.h>
22
23
24 /*
25 * Constants...
26 */
27
28 #define PSTOPS_BORDERNONE 0 /* No border or hairline border */
29 #define PSTOPS_BORDERTHICK 1 /* Think border */
30 #define PSTOPS_BORDERSINGLE 2 /* Single-line hairline border */
31 #define PSTOPS_BORDERSINGLE2 3 /* Single-line thick border */
32 #define PSTOPS_BORDERDOUBLE 4 /* Double-line hairline border */
33 #define PSTOPS_BORDERDOUBLE2 5 /* Double-line thick border */
34
35 #define PSTOPS_LAYOUT_LRBT 0 /* Left to right, bottom to top */
36 #define PSTOPS_LAYOUT_LRTB 1 /* Left to right, top to bottom */
37 #define PSTOPS_LAYOUT_RLBT 2 /* Right to left, bottom to top */
38 #define PSTOPS_LAYOUT_RLTB 3 /* Right to left, top to bottom */
39 #define PSTOPS_LAYOUT_BTLR 4 /* Bottom to top, left to right */
40 #define PSTOPS_LAYOUT_TBLR 5 /* Top to bottom, left to right */
41 #define PSTOPS_LAYOUT_BTRL 6 /* Bottom to top, right to left */
42 #define PSTOPS_LAYOUT_TBRL 7 /* Top to bottom, right to left */
43
44 #define PSTOPS_LAYOUT_NEGATEY 1 /* The bits for the layout */
45 #define PSTOPS_LAYOUT_NEGATEX 2 /* definitions above... */
46 #define PSTOPS_LAYOUT_VERTICAL 4
47
48
49 /*
50 * Types...
51 */
52
53 typedef struct /**** Page information ****/
54 {
55 char *label; /* Page label */
56 int bounding_box[4]; /* PageBoundingBox */
57 off_t offset; /* Offset to start of page */
58 ssize_t length; /* Number of bytes for page */
59 int num_options; /* Number of options for this page */
60 cups_option_t *options; /* Options for this page */
61 } pstops_page_t;
62
63 typedef struct /**** Document information ****/
64 {
65 int page; /* Current page */
66 int bounding_box[4]; /* BoundingBox from header */
67 int new_bounding_box[4]; /* New composite bounding box */
68 int num_options; /* Number of document-wide options */
69 cups_option_t *options; /* Document-wide options */
70 int normal_landscape, /* Normal rotation for landscape? */
71 saw_eof, /* Saw the %%EOF comment? */
72 slow_collate, /* Collate copies by hand? */
73 slow_duplex, /* Duplex pages slowly? */
74 slow_order, /* Reverse pages slowly? */
75 use_ESPshowpage; /* Use ESPshowpage? */
76 cups_array_t *pages; /* Pages in document */
77 cups_file_t *temp; /* Temporary file, if any */
78 char tempfile[1024]; /* Temporary filename */
79 int job_id; /* Job ID */
80 const char *user, /* User name */
81 *title; /* Job name */
82 int copies; /* Number of copies */
83 const char *ap_input_slot, /* AP_FIRSTPAGE_InputSlot value */
84 *ap_manual_feed, /* AP_FIRSTPAGE_ManualFeed value */
85 *ap_media_color, /* AP_FIRSTPAGE_MediaColor value */
86 *ap_media_type, /* AP_FIRSTPAGE_MediaType value */
87 *ap_page_region, /* AP_FIRSTPAGE_PageRegion value */
88 *ap_page_size; /* AP_FIRSTPAGE_PageSize value */
89 int collate, /* Collate copies? */
90 emit_jcl, /* Emit JCL commands? */
91 fit_to_page; /* Fit pages to media */
92 const char *input_slot, /* InputSlot value */
93 *manual_feed, /* ManualFeed value */
94 *media_color, /* MediaColor value */
95 *media_type, /* MediaType value */
96 *page_region, /* PageRegion value */
97 *page_size; /* PageSize value */
98 int mirror, /* doc->mirror/mirror pages */
99 number_up, /* Number of pages on each sheet */
100 number_up_layout, /* doc->number_up_layout of N-up pages */
101 output_order, /* Requested reverse output order? */
102 page_border; /* doc->page_border around pages */
103 const char *page_label, /* page-label option, if any */
104 *page_ranges, /* page-ranges option, if any */
105 *page_set; /* page-set option, if any */
106 } pstops_doc_t;
107
108
109 /*
110 * Convenience macros...
111 */
112
113 #define is_first_page(p) (doc->number_up == 1 || \
114 ((p) % doc->number_up) == 1)
115 #define is_last_page(p) (doc->number_up == 1 || \
116 ((p) % doc->number_up) == 0)
117 #define is_not_last_page(p) (doc->number_up > 1 && \
118 ((p) % doc->number_up) != 0)
119
120
121 /*
122 * Local globals...
123 */
124
125 static int JobCanceled = 0;/* Set to 1 on SIGTERM */
126
127
128 /*
129 * Local functions...
130 */
131
132 static pstops_page_t *add_page(pstops_doc_t *doc, const char *label);
133 static void cancel_job(int sig);
134 static int check_range(pstops_doc_t *doc, int page);
135 static void copy_bytes(cups_file_t *fp, off_t offset,
136 size_t length);
137 static ssize_t copy_comments(cups_file_t *fp, pstops_doc_t *doc,
138 ppd_file_t *ppd, char *line,
139 ssize_t linelen, size_t linesize);
140 static void copy_dsc(cups_file_t *fp, pstops_doc_t *doc,
141 ppd_file_t *ppd, char *line, ssize_t linelen,
142 size_t linesize);
143 static void copy_non_dsc(cups_file_t *fp, pstops_doc_t *doc,
144 ppd_file_t *ppd, char *line,
145 ssize_t linelen, size_t linesize);
146 static ssize_t copy_page(cups_file_t *fp, pstops_doc_t *doc,
147 ppd_file_t *ppd, int number, char *line,
148 ssize_t linelen, size_t linesize);
149 static ssize_t copy_prolog(cups_file_t *fp, pstops_doc_t *doc,
150 ppd_file_t *ppd, char *line,
151 ssize_t linelen, size_t linesize);
152 static ssize_t copy_setup(cups_file_t *fp, pstops_doc_t *doc,
153 ppd_file_t *ppd, char *line,
154 ssize_t linelen, size_t linesize);
155 static ssize_t copy_trailer(cups_file_t *fp, pstops_doc_t *doc,
156 ppd_file_t *ppd, int number, char *line,
157 ssize_t linelen, size_t linesize);
158 static void do_prolog(pstops_doc_t *doc, ppd_file_t *ppd);
159 static void do_setup(pstops_doc_t *doc, ppd_file_t *ppd);
160 static void doc_printf(pstops_doc_t *doc, const char *format, ...) _CUPS_FORMAT(2, 3);
161 static void doc_puts(pstops_doc_t *doc, const char *s);
162 static void doc_write(pstops_doc_t *doc, const char *s, size_t len);
163 static void end_nup(pstops_doc_t *doc, int number);
164 static int include_feature(ppd_file_t *ppd, const char *line,
165 int num_options,
166 cups_option_t **options);
167 static char *parse_text(const char *start, char **end, char *buffer,
168 size_t bufsize);
169 static void set_pstops_options(pstops_doc_t *doc, ppd_file_t *ppd,
170 char *argv[], int num_options,
171 cups_option_t *options);
172 static ssize_t skip_page(cups_file_t *fp, char *line, ssize_t linelen,
173 size_t linesize);
174 static void start_nup(pstops_doc_t *doc, int number,
175 int show_border, const int *bounding_box);
176 static void write_label_prolog(pstops_doc_t *doc, const char *label,
177 float bottom, float top,
178 float width);
179 static void write_labels(pstops_doc_t *doc, int orient);
180 static void write_options(pstops_doc_t *doc, ppd_file_t *ppd,
181 int num_options, cups_option_t *options);
182
183
184 /*
185 * 'main()' - Main entry.
186 */
187
188 int /* O - Exit status */
main(int argc,char * argv[])189 main(int argc, /* I - Number of command-line args */
190 char *argv[]) /* I - Command-line arguments */
191 {
192 pstops_doc_t doc; /* Document information */
193 cups_file_t *fp; /* Print file */
194 ppd_file_t *ppd; /* PPD file */
195 int num_options; /* Number of print options */
196 cups_option_t *options; /* Print options */
197 char line[8192]; /* Line buffer */
198 ssize_t len; /* Length of line buffer */
199 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
200 struct sigaction action; /* Actions for POSIX signals */
201 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
202
203
204 /*
205 * Make sure status messages are not buffered...
206 */
207
208 setbuf(stderr, NULL);
209
210 /*
211 * Ignore broken pipe signals...
212 */
213
214 signal(SIGPIPE, SIG_IGN);
215
216 /*
217 * Check command-line...
218 */
219
220 if (argc < 6 || argc > 7)
221 {
222 _cupsLangPrintf(stderr,
223 _("Usage: %s job-id user title copies options [file]"),
224 argv[0]);
225 return (1);
226 }
227
228 /*
229 * Register a signal handler to cleanly cancel a job.
230 */
231
232 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
233 sigset(SIGTERM, cancel_job);
234 #elif defined(HAVE_SIGACTION)
235 memset(&action, 0, sizeof(action));
236
237 sigemptyset(&action.sa_mask);
238 action.sa_handler = cancel_job;
239 sigaction(SIGTERM, &action, NULL);
240 #else
241 signal(SIGTERM, cancel_job);
242 #endif /* HAVE_SIGSET */
243
244 /*
245 * If we have 7 arguments, print the file named on the command-line.
246 * Otherwise, send stdin instead...
247 */
248
249 if (argc == 6)
250 fp = cupsFileStdin();
251 else
252 {
253 /*
254 * Try to open the print file...
255 */
256
257 if ((fp = cupsFileOpen(argv[6], "r")) == NULL)
258 {
259 if (!JobCanceled)
260 {
261 fprintf(stderr, "DEBUG: Unable to open \"%s\".\n", argv[6]);
262 _cupsLangPrintError("ERROR", _("Unable to open print file"));
263 }
264
265 return (1);
266 }
267 }
268
269 /*
270 * Read the first line to see if we have DSC comments...
271 */
272
273 if ((len = (ssize_t)cupsFileGetLine(fp, line, sizeof(line))) == 0)
274 {
275 fputs("DEBUG: The print file is empty.\n", stderr);
276 return (1);
277 }
278
279 /*
280 * Process command-line options...
281 */
282
283 options = NULL;
284 num_options = cupsParseOptions(argv[5], 0, &options);
285 ppd = SetCommonOptions(num_options, options, 1);
286
287 set_pstops_options(&doc, ppd, argv, num_options, options);
288
289 /*
290 * Write any "exit server" options that have been selected...
291 */
292
293 ppdEmit(ppd, stdout, PPD_ORDER_EXIT);
294
295 /*
296 * Write any JCL commands that are needed to print PostScript code...
297 */
298
299 if (doc.emit_jcl)
300 ppdEmitJCL(ppd, stdout, doc.job_id, doc.user, doc.title);
301
302 /*
303 * Start with a DSC header...
304 */
305
306 puts("%!PS-Adobe-3.0");
307
308 /*
309 * Skip leading PJL in the document...
310 */
311
312 while (!strncmp(line, "\033%-12345X", 9) || !strncmp(line, "@PJL ", 5))
313 {
314 /*
315 * Yup, we have leading PJL fun, so skip it until we hit the line
316 * with "ENTER LANGUAGE"...
317 */
318
319 fputs("DEBUG: Skipping PJL header...\n", stderr);
320
321 while (strstr(line, "ENTER LANGUAGE") == NULL && strncmp(line, "%!", 2))
322 if ((len = (ssize_t)cupsFileGetLine(fp, line, sizeof(line))) == 0)
323 break;
324
325 if (!strncmp(line, "%!", 2))
326 break;
327
328 if ((len = (ssize_t)cupsFileGetLine(fp, line, sizeof(line))) == 0)
329 break;
330 }
331
332 /*
333 * Now see if the document conforms to the Adobe Document Structuring
334 * Conventions...
335 */
336
337 if (!strncmp(line, "%!PS-Adobe-", 11))
338 {
339 /*
340 * Yes, filter the document...
341 */
342
343 copy_dsc(fp, &doc, ppd, line, len, sizeof(line));
344 }
345 else
346 {
347 /*
348 * No, display an error message and treat the file as if it contains
349 * a single page...
350 */
351
352 copy_non_dsc(fp, &doc, ppd, line, len, sizeof(line));
353 }
354
355 /*
356 * Send %%EOF as needed...
357 */
358
359 if (!doc.saw_eof)
360 puts("%%EOF");
361
362 /*
363 * End the job with the appropriate JCL command or CTRL-D...
364 */
365
366 if (doc.emit_jcl)
367 {
368 if (ppd && ppd->jcl_end)
369 ppdEmitJCLEnd(ppd, stdout);
370 else
371 putchar(0x04);
372 }
373
374 /*
375 * Close files and remove the temporary file if needed...
376 */
377
378 if (doc.temp)
379 {
380 cupsFileClose(doc.temp);
381 unlink(doc.tempfile);
382 }
383
384 ppdClose(ppd);
385 cupsFreeOptions(num_options, options);
386
387 cupsFileClose(fp);
388
389 return (0);
390 }
391
392
393 /*
394 * 'add_page()' - Add a page to the pages array.
395 */
396
397 static pstops_page_t * /* O - New page info object */
add_page(pstops_doc_t * doc,const char * label)398 add_page(pstops_doc_t *doc, /* I - Document information */
399 const char *label) /* I - Page label */
400 {
401 pstops_page_t *pageinfo; /* New page info object */
402
403
404 if (!doc->pages)
405 doc->pages = cupsArrayNew(NULL, NULL);
406
407 if (!doc->pages)
408 {
409 _cupsLangPrintError("EMERG", _("Unable to allocate memory for pages array"));
410 exit(1);
411 }
412
413 if ((pageinfo = calloc(1, sizeof(pstops_page_t))) == NULL)
414 {
415 _cupsLangPrintError("EMERG", _("Unable to allocate memory for page info"));
416 exit(1);
417 }
418
419 pageinfo->label = strdup(label);
420 pageinfo->offset = cupsFileTell(doc->temp);
421
422 cupsArrayAdd(doc->pages, pageinfo);
423
424 doc->page ++;
425
426 return (pageinfo);
427 }
428
429
430 /*
431 * 'cancel_job()' - Flag the job as canceled.
432 */
433
434 static void
cancel_job(int sig)435 cancel_job(int sig) /* I - Signal number (unused) */
436 {
437 (void)sig;
438
439 JobCanceled = 1;
440 }
441
442
443 /*
444 * 'check_range()' - Check to see if the current page is selected for
445 * printing.
446 */
447
448 static int /* O - 1 if selected, 0 otherwise */
check_range(pstops_doc_t * doc,int page)449 check_range(pstops_doc_t *doc, /* I - Document information */
450 int page) /* I - Page number */
451 {
452 const char *range; /* Pointer into range string */
453 int lower, upper; /* Lower and upper page numbers */
454
455
456 if (doc->page_set)
457 {
458 /*
459 * See if we only print even or odd pages...
460 */
461
462 if (!_cups_strcasecmp(doc->page_set, "even") && (page & 1))
463 return (0);
464
465 if (!_cups_strcasecmp(doc->page_set, "odd") && !(page & 1))
466 return (0);
467 }
468
469 if (!doc->page_ranges)
470 return (1); /* No range, print all pages... */
471
472 for (range = doc->page_ranges; *range != '\0';)
473 {
474 if (*range == '-')
475 {
476 lower = 1;
477 range ++;
478 upper = (int)strtol(range, (char **)&range, 10);
479 }
480 else
481 {
482 lower = (int)strtol(range, (char **)&range, 10);
483
484 if (*range == '-')
485 {
486 range ++;
487 if (!isdigit(*range & 255))
488 upper = 65535;
489 else
490 upper = (int)strtol(range, (char **)&range, 10);
491 }
492 else
493 upper = lower;
494 }
495
496 if (page >= lower && page <= upper)
497 return (1);
498
499 if (*range == ',')
500 range ++;
501 else
502 break;
503 }
504
505 return (0);
506 }
507
508
509 /*
510 * 'copy_bytes()' - Copy bytes from the input file to stdout.
511 */
512
513 static void
copy_bytes(cups_file_t * fp,off_t offset,size_t length)514 copy_bytes(cups_file_t *fp, /* I - File to read from */
515 off_t offset, /* I - Offset to page data */
516 size_t length) /* I - Length of page data */
517 {
518 char buffer[8192]; /* Data buffer */
519 ssize_t nbytes; /* Number of bytes read */
520 size_t nleft; /* Number of bytes left/remaining */
521
522
523 nleft = length;
524
525 if (cupsFileSeek(fp, offset) < 0)
526 {
527 _cupsLangPrintError("ERROR", _("Unable to see in file"));
528 return;
529 }
530
531 while (nleft > 0 || length == 0)
532 {
533 if (nleft > sizeof(buffer) || length == 0)
534 nbytes = sizeof(buffer);
535 else
536 nbytes = (ssize_t)nleft;
537
538 if ((nbytes = cupsFileRead(fp, buffer, (size_t)nbytes)) < 1)
539 return;
540
541 nleft -= (size_t)nbytes;
542
543 fwrite(buffer, 1, (size_t)nbytes, stdout);
544 }
545 }
546
547
548 /*
549 * 'copy_comments()' - Copy all of the comments section.
550 *
551 * This function expects "line" to be filled with a comment line.
552 * On return, "line" will contain the next line in the file, if any.
553 */
554
555 static ssize_t /* O - Length of next line */
copy_comments(cups_file_t * fp,pstops_doc_t * doc,ppd_file_t * ppd,char * line,ssize_t linelen,size_t linesize)556 copy_comments(cups_file_t *fp, /* I - File to read from */
557 pstops_doc_t *doc, /* I - Document info */
558 ppd_file_t *ppd, /* I - PPD file */
559 char *line, /* I - Line buffer */
560 ssize_t linelen, /* I - Length of initial line */
561 size_t linesize) /* I - Size of line buffer */
562 {
563 int saw_bounding_box, /* Saw %%BoundingBox: comment? */
564 saw_for, /* Saw %%For: comment? */
565 saw_pages, /* Saw %%Pages: comment? */
566 saw_title; /* Saw %%Title: comment? */
567
568
569 /*
570 * Loop until we see %%EndComments or a non-comment line...
571 */
572
573 saw_bounding_box = 0;
574 saw_for = 0;
575 saw_pages = 0;
576 saw_title = 0;
577
578 while (line[0] == '%')
579 {
580 /*
581 * Strip trailing whitespace...
582 */
583
584 while (linelen > 0)
585 {
586 linelen --;
587
588 if (!isspace(line[linelen] & 255))
589 break;
590 else
591 line[linelen] = '\0';
592 }
593
594 /*
595 * Log the header...
596 */
597
598 fprintf(stderr, "DEBUG: %s\n", line);
599
600 /*
601 * Pull the headers out...
602 */
603
604 if (!strncmp(line, "%%Pages:", 8))
605 {
606 int pages; /* Number of pages */
607
608 if (saw_pages)
609 fputs("DEBUG: A duplicate %%Pages: comment was seen.\n", stderr);
610
611 saw_pages = 1;
612
613 if (Duplex && (pages = atoi(line + 8)) > 0 && pages <= doc->number_up)
614 {
615 /*
616 * Since we will only be printing on a single page, disable duplexing.
617 */
618
619 Duplex = 0;
620 doc->slow_duplex = 0;
621
622 if (cupsGetOption("sides", doc->num_options, doc->options))
623 doc->num_options = cupsAddOption("sides", "one-sided",
624 doc->num_options, &(doc->options));
625
626 if (cupsGetOption("Duplex", doc->num_options, doc->options))
627 doc->num_options = cupsAddOption("Duplex", "None",
628 doc->num_options, &(doc->options));
629
630 if (cupsGetOption("EFDuplex", doc->num_options, doc->options))
631 doc->num_options = cupsAddOption("EFDuplex", "None",
632 doc->num_options, &(doc->options));
633
634 if (cupsGetOption("EFDuplexing", doc->num_options, doc->options))
635 doc->num_options = cupsAddOption("EFDuplexing", "False",
636 doc->num_options, &(doc->options));
637
638 if (cupsGetOption("KD03Duplex", doc->num_options, doc->options))
639 doc->num_options = cupsAddOption("KD03Duplex", "None",
640 doc->num_options, &(doc->options));
641
642 if (cupsGetOption("JCLDuplex", doc->num_options, doc->options))
643 doc->num_options = cupsAddOption("JCLDuplex", "None",
644 doc->num_options, &(doc->options));
645
646 ppdMarkOption(ppd, "Duplex", "None");
647 ppdMarkOption(ppd, "EFDuplex", "None");
648 ppdMarkOption(ppd, "EFDuplexing", "False");
649 ppdMarkOption(ppd, "KD03Duplex", "None");
650 ppdMarkOption(ppd, "JCLDuplex", "None");
651 }
652 }
653 else if (!strncmp(line, "%%BoundingBox:", 14))
654 {
655 if (saw_bounding_box)
656 fputs("DEBUG: A duplicate %%BoundingBox: comment was seen.\n", stderr);
657 else if (strstr(line + 14, "(atend)"))
658 {
659 /*
660 * Do nothing for now but use the default imageable area...
661 */
662 }
663 else if (sscanf(line + 14, "%d%d%d%d", doc->bounding_box + 0,
664 doc->bounding_box + 1, doc->bounding_box + 2,
665 doc->bounding_box + 3) != 4)
666 {
667 fputs("DEBUG: A bad %%BoundingBox: comment was seen.\n", stderr);
668
669 doc->bounding_box[0] = (int)PageLeft;
670 doc->bounding_box[1] = (int)PageBottom;
671 doc->bounding_box[2] = (int)PageRight;
672 doc->bounding_box[3] = (int)PageTop;
673 }
674
675 saw_bounding_box = 1;
676 }
677 else if (!strncmp(line, "%%For:", 6))
678 {
679 saw_for = 1;
680 doc_printf(doc, "%s\n", line);
681 }
682 else if (!strncmp(line, "%%Title:", 8))
683 {
684 saw_title = 1;
685 doc_printf(doc, "%s\n", line);
686 }
687 else if (!strncmp(line, "%cupsRotation:", 14))
688 {
689 /*
690 * Reset orientation of document?
691 */
692
693 int orient = (atoi(line + 14) / 90) & 3;
694
695 if (orient != Orientation)
696 {
697 /*
698 * Yes, update things so that the pages come out right...
699 */
700
701 Orientation = (4 - Orientation + orient) & 3;
702 UpdatePageVars();
703 Orientation = orient;
704 }
705 }
706 else if (!strcmp(line, "%%EndComments"))
707 {
708 linelen = (ssize_t)cupsFileGetLine(fp, line, linesize);
709 break;
710 }
711 else if (strncmp(line, "%!", 2) && strncmp(line, "%cups", 5))
712 doc_printf(doc, "%s\n", line);
713
714 if ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) == 0)
715 break;
716 }
717
718 if (!saw_bounding_box)
719 fputs("DEBUG: There wasn't a %%BoundingBox: comment in the header.\n",
720 stderr);
721
722 if (!saw_pages)
723 fputs("DEBUG: There wasn't a %%Pages: comment in the header.\n", stderr);
724
725 if (!saw_for)
726 WriteTextComment("For", doc->user);
727
728 if (!saw_title)
729 WriteTextComment("Title", doc->title);
730
731 if (doc->copies != 1 && (!doc->collate || !doc->slow_collate))
732 {
733 /*
734 * Tell the document processor the copy and duplex options
735 * that are required...
736 */
737
738 doc_printf(doc, "%%%%Requirements: numcopies(%d)%s%s\n", doc->copies,
739 doc->collate ? " collate" : "",
740 Duplex ? " duplex" : "");
741
742 /*
743 * Apple uses RBI comments for various non-PPD options...
744 */
745
746 doc_printf(doc, "%%RBINumCopies: %d\n", doc->copies);
747 }
748 else
749 {
750 /*
751 * Tell the document processor the duplex option that is required...
752 */
753
754 if (Duplex)
755 doc_puts(doc, "%%Requirements: duplex\n");
756
757 /*
758 * Apple uses RBI comments for various non-PPD options...
759 */
760
761 doc_puts(doc, "%RBINumCopies: 1\n");
762 }
763
764 doc_puts(doc, "%%Pages: (atend)\n");
765 doc_puts(doc, "%%BoundingBox: (atend)\n");
766 doc_puts(doc, "%%EndComments\n");
767
768 return (linelen);
769 }
770
771
772 /*
773 * 'copy_dsc()' - Copy a DSC-conforming document.
774 *
775 * This function expects "line" to be filled with the %!PS-Adobe comment line.
776 */
777
778 static void
copy_dsc(cups_file_t * fp,pstops_doc_t * doc,ppd_file_t * ppd,char * line,ssize_t linelen,size_t linesize)779 copy_dsc(cups_file_t *fp, /* I - File to read from */
780 pstops_doc_t *doc, /* I - Document info */
781 ppd_file_t *ppd, /* I - PPD file */
782 char *line, /* I - Line buffer */
783 ssize_t linelen, /* I - Length of initial line */
784 size_t linesize) /* I - Size of line buffer */
785 {
786 int number; /* Page number */
787 pstops_page_t *pageinfo; /* Page information */
788
789
790 /*
791 * Make sure we use ESPshowpage for EPS files...
792 */
793
794 if (strstr(line, "EPSF"))
795 {
796 doc->use_ESPshowpage = 1;
797 doc->number_up = 1;
798 }
799
800 /*
801 * Start sending the document with any commands needed...
802 */
803
804 fprintf(stderr, "DEBUG: Before copy_comments - %s", line);
805 linelen = copy_comments(fp, doc, ppd, line, linelen, linesize);
806
807 /*
808 * Now find the prolog section, if any...
809 */
810
811 fprintf(stderr, "DEBUG: Before copy_prolog - %s", line);
812 linelen = copy_prolog(fp, doc, ppd, line, linelen, linesize);
813
814 /*
815 * Then the document setup section...
816 */
817
818 fprintf(stderr, "DEBUG: Before copy_setup - %s", line);
819 linelen = copy_setup(fp, doc, ppd, line, linelen, linesize);
820
821 /*
822 * Copy until we see %%Page:...
823 */
824
825 while (strncmp(line, "%%Page:", 7) && strncmp(line, "%%Trailer", 9))
826 {
827 doc_write(doc, line, (size_t)linelen);
828
829 if ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) == 0)
830 break;
831 }
832
833 /*
834 * Then process pages until we have no more...
835 */
836
837 number = 0;
838
839 fprintf(stderr, "DEBUG: Before page loop - %s", line);
840 while (!strncmp(line, "%%Page:", 7))
841 {
842 if (JobCanceled)
843 break;
844
845 number ++;
846
847 if (check_range(doc, (number - 1) / doc->number_up + 1))
848 {
849 fprintf(stderr, "DEBUG: Copying page %d...\n", number);
850 linelen = copy_page(fp, doc, ppd, number, line, linelen, linesize);
851 }
852 else
853 {
854 fprintf(stderr, "DEBUG: Skipping page %d...\n", number);
855 linelen = skip_page(fp, line, linelen, linesize);
856 }
857 }
858
859 /*
860 * Finish up the last page(s)...
861 */
862
863 if (number && is_not_last_page(number) && cupsArrayLast(doc->pages) &&
864 check_range(doc, (number - 1) / doc->number_up + 1))
865 {
866 pageinfo = (pstops_page_t *)cupsArrayLast(doc->pages);
867
868 start_nup(doc, doc->number_up, 0, doc->bounding_box);
869 doc_puts(doc, "showpage\n");
870 end_nup(doc, doc->number_up);
871
872 pageinfo->length = (ssize_t)(cupsFileTell(doc->temp) - pageinfo->offset);
873 }
874
875 if (doc->slow_duplex && (doc->page & 1))
876 {
877 /*
878 * Make sure we have an even number of pages...
879 */
880
881 pageinfo = add_page(doc, "(filler)");
882
883 if (!doc->slow_order)
884 {
885 if (!ppd || !ppd->num_filters)
886 fprintf(stderr, "PAGE: %d %d\n", doc->page,
887 doc->slow_collate ? 1 : doc->copies);
888
889 printf("%%%%Page: (filler) %d\n", doc->page);
890 }
891
892 start_nup(doc, doc->number_up, 0, doc->bounding_box);
893 doc_puts(doc, "showpage\n");
894 end_nup(doc, doc->number_up);
895
896 pageinfo->length = (ssize_t)(cupsFileTell(doc->temp) - pageinfo->offset);
897 }
898
899 /*
900 * Make additional copies as necessary...
901 */
902
903 number = doc->slow_order ? 0 : doc->page;
904
905 if (doc->temp && !JobCanceled && cupsArrayCount(doc->pages) > 0)
906 {
907 int copy; /* Current copy */
908
909
910 /*
911 * Reopen the temporary file for reading...
912 */
913
914 cupsFileClose(doc->temp);
915
916 doc->temp = cupsFileOpen(doc->tempfile, "r");
917
918 /*
919 * Make the copies...
920 */
921
922 if (doc->slow_collate)
923 copy = !doc->slow_order;
924 else
925 copy = doc->copies - 1;
926
927 for (; copy < doc->copies; copy ++)
928 {
929 if (JobCanceled)
930 break;
931
932 /*
933 * Send end-of-job stuff followed by any start-of-job stuff required
934 * for the JCL options...
935 */
936
937 if (number && doc->emit_jcl && ppd && ppd->jcl_end)
938 {
939 /*
940 * Send the trailer...
941 */
942
943 puts("%%Trailer");
944 printf("%%%%Pages: %d\n", cupsArrayCount(doc->pages));
945 if (doc->number_up > 1 || doc->fit_to_page)
946 printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
947 PageLeft, PageBottom, PageRight, PageTop);
948 else
949 printf("%%%%BoundingBox: %d %d %d %d\n",
950 doc->new_bounding_box[0], doc->new_bounding_box[1],
951 doc->new_bounding_box[2], doc->new_bounding_box[3]);
952 puts("%%EOF");
953
954 /*
955 * Start a new document...
956 */
957
958 ppdEmitJCLEnd(ppd, stdout);
959 ppdEmitJCL(ppd, stdout, doc->job_id, doc->user, doc->title);
960
961 puts("%!PS-Adobe-3.0");
962
963 number = 0;
964 }
965
966 /*
967 * Copy the prolog as needed...
968 */
969
970 if (!number)
971 {
972 pageinfo = (pstops_page_t *)cupsArrayFirst(doc->pages);
973 copy_bytes(doc->temp, 0, (size_t)pageinfo->offset);
974 }
975
976 /*
977 * Then copy all of the pages...
978 */
979
980 pageinfo = doc->slow_order ? (pstops_page_t *)cupsArrayLast(doc->pages) :
981 (pstops_page_t *)cupsArrayFirst(doc->pages);
982
983 while (pageinfo)
984 {
985 if (JobCanceled)
986 break;
987
988 number ++;
989
990 if (!ppd || !ppd->num_filters)
991 fprintf(stderr, "PAGE: %d %d\n", number,
992 doc->slow_collate ? 1 : doc->copies);
993
994 if (doc->number_up > 1)
995 {
996 printf("%%%%Page: (%d) %d\n", number, number);
997 printf("%%%%PageBoundingBox: %.0f %.0f %.0f %.0f\n",
998 PageLeft, PageBottom, PageRight, PageTop);
999 }
1000 else
1001 {
1002 printf("%%%%Page: %s %d\n", pageinfo->label, number);
1003 printf("%%%%PageBoundingBox: %d %d %d %d\n",
1004 pageinfo->bounding_box[0], pageinfo->bounding_box[1],
1005 pageinfo->bounding_box[2], pageinfo->bounding_box[3]);
1006 }
1007
1008 copy_bytes(doc->temp, pageinfo->offset, (size_t)pageinfo->length);
1009
1010 pageinfo = doc->slow_order ? (pstops_page_t *)cupsArrayPrev(doc->pages) :
1011 (pstops_page_t *)cupsArrayNext(doc->pages);
1012 }
1013 }
1014 }
1015
1016 /*
1017 * Restore the old showpage operator as needed...
1018 */
1019
1020 if (doc->use_ESPshowpage)
1021 puts("userdict/showpage/ESPshowpage load put\n");
1022
1023 /*
1024 * Write/copy the trailer...
1025 */
1026
1027 if (!JobCanceled)
1028 copy_trailer(fp, doc, ppd, number, line, linelen, linesize);
1029 }
1030
1031
1032 /*
1033 * 'copy_non_dsc()' - Copy a document that does not conform to the DSC.
1034 *
1035 * This function expects "line" to be filled with the %! comment line.
1036 */
1037
1038 static void
copy_non_dsc(cups_file_t * fp,pstops_doc_t * doc,ppd_file_t * ppd,char * line,ssize_t linelen,size_t linesize)1039 copy_non_dsc(cups_file_t *fp, /* I - File to read from */
1040 pstops_doc_t *doc, /* I - Document info */
1041 ppd_file_t *ppd, /* I - PPD file */
1042 char *line, /* I - Line buffer */
1043 ssize_t linelen, /* I - Length of initial line */
1044 size_t linesize) /* I - Size of line buffer */
1045 {
1046 int copy; /* Current copy */
1047 char buffer[8192]; /* Copy buffer */
1048 ssize_t bytes; /* Number of bytes copied */
1049
1050
1051 (void)linesize;
1052
1053 /*
1054 * First let the user know that they are attempting to print a file
1055 * that may not print correctly...
1056 */
1057
1058 fputs("DEBUG: This document does not conform to the Adobe Document "
1059 "Structuring Conventions and may not print correctly.\n", stderr);
1060
1061 /*
1062 * Then write a standard DSC comment section...
1063 */
1064
1065 printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n", PageLeft, PageBottom,
1066 PageRight, PageTop);
1067
1068 if (doc->slow_collate && doc->copies > 1)
1069 printf("%%%%Pages: %d\n", doc->copies);
1070 else
1071 puts("%%Pages: 1");
1072
1073 WriteTextComment("For", doc->user);
1074 WriteTextComment("Title", doc->title);
1075
1076 if (doc->copies != 1 && (!doc->collate || !doc->slow_collate))
1077 {
1078 /*
1079 * Tell the document processor the copy and duplex options
1080 * that are required...
1081 */
1082
1083 printf("%%%%Requirements: numcopies(%d)%s%s\n", doc->copies,
1084 doc->collate ? " collate" : "",
1085 Duplex ? " duplex" : "");
1086
1087 /*
1088 * Apple uses RBI comments for various non-PPD options...
1089 */
1090
1091 printf("%%RBINumCopies: %d\n", doc->copies);
1092 }
1093 else
1094 {
1095 /*
1096 * Tell the document processor the duplex option that is required...
1097 */
1098
1099 if (Duplex)
1100 puts("%%Requirements: duplex");
1101
1102 /*
1103 * Apple uses RBI comments for various non-PPD options...
1104 */
1105
1106 puts("%RBINumCopies: 1");
1107 }
1108
1109 puts("%%EndComments");
1110
1111 /*
1112 * Then the prolog...
1113 */
1114
1115 puts("%%BeginProlog");
1116
1117 do_prolog(doc, ppd);
1118
1119 puts("%%EndProlog");
1120
1121 /*
1122 * Then the setup section...
1123 */
1124
1125 puts("%%BeginSetup");
1126
1127 do_setup(doc, ppd);
1128
1129 puts("%%EndSetup");
1130
1131 /*
1132 * Finally, embed a copy of the file inside a %%Page...
1133 */
1134
1135 if (!ppd || !ppd->num_filters)
1136 fprintf(stderr, "PAGE: 1 %d\n", doc->temp ? 1 : doc->copies);
1137
1138 puts("%%Page: 1 1");
1139 puts("%%BeginPageSetup");
1140 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
1141 puts("%%EndPageSetup");
1142 puts("%%BeginDocument: nondsc");
1143
1144 fwrite(line, (size_t)linelen, 1, stdout);
1145
1146 if (doc->temp)
1147 cupsFileWrite(doc->temp, line, (size_t)linelen);
1148
1149 while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
1150 {
1151 fwrite(buffer, 1, (size_t)bytes, stdout);
1152
1153 if (doc->temp)
1154 cupsFileWrite(doc->temp, buffer, (size_t)bytes);
1155 }
1156
1157 puts("%%EndDocument");
1158
1159 if (doc->use_ESPshowpage)
1160 {
1161 WriteLabels(Orientation);
1162 puts("ESPshowpage");
1163 }
1164
1165 if (doc->temp && !JobCanceled)
1166 {
1167 /*
1168 * Reopen the temporary file for reading...
1169 */
1170
1171 cupsFileClose(doc->temp);
1172
1173 doc->temp = cupsFileOpen(doc->tempfile, "r");
1174
1175 /*
1176 * Make the additional copies as needed...
1177 */
1178
1179 for (copy = 1; copy < doc->copies; copy ++)
1180 {
1181 if (JobCanceled)
1182 break;
1183
1184 if (!ppd || !ppd->num_filters)
1185 fputs("PAGE: 1 1\n", stderr);
1186
1187 printf("%%%%Page: %d %d\n", copy + 1, copy + 1);
1188 puts("%%BeginPageSetup");
1189 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
1190 puts("%%EndPageSetup");
1191 puts("%%BeginDocument: nondsc");
1192
1193 copy_bytes(doc->temp, 0, 0);
1194
1195 puts("%%EndDocument");
1196
1197 if (doc->use_ESPshowpage)
1198 {
1199 WriteLabels(Orientation);
1200 puts("ESPshowpage");
1201 }
1202 }
1203 }
1204
1205 /*
1206 * Restore the old showpage operator as needed...
1207 */
1208
1209 if (doc->use_ESPshowpage)
1210 puts("userdict/showpage/ESPshowpage load put\n");
1211 }
1212
1213
1214 /*
1215 * 'copy_page()' - Copy a page description.
1216 *
1217 * This function expects "line" to be filled with a %%Page comment line.
1218 * On return, "line" will contain the next line in the file, if any.
1219 */
1220
1221 static ssize_t /* O - Length of next line */
copy_page(cups_file_t * fp,pstops_doc_t * doc,ppd_file_t * ppd,int number,char * line,ssize_t linelen,size_t linesize)1222 copy_page(cups_file_t *fp, /* I - File to read from */
1223 pstops_doc_t *doc, /* I - Document info */
1224 ppd_file_t *ppd, /* I - PPD file */
1225 int number, /* I - Current page number */
1226 char *line, /* I - Line buffer */
1227 ssize_t linelen, /* I - Length of initial line */
1228 size_t linesize) /* I - Size of line buffer */
1229 {
1230 char label[256], /* Page label string */
1231 *ptr; /* Pointer into line */
1232 int level; /* Embedded document level */
1233 pstops_page_t *pageinfo; /* Page information */
1234 int first_page; /* First page on N-up output? */
1235 int has_page_setup = 0; /* Does the page have %%Begin/EndPageSetup? */
1236 int bounding_box[4]; /* PageBoundingBox */
1237
1238
1239 /*
1240 * Get the page label for this page...
1241 */
1242
1243 first_page = is_first_page(number);
1244
1245 if (!parse_text(line + 7, &ptr, label, sizeof(label)))
1246 {
1247 fputs("DEBUG: There was a bad %%Page: comment in the file.\n", stderr);
1248 label[0] = '\0';
1249 number = doc->page;
1250 }
1251 else if (strtol(ptr, &ptr, 10) == LONG_MAX || !isspace(*ptr & 255))
1252 {
1253 fputs("DEBUG: There was a bad %%Page: comment in the file.\n", stderr);
1254 number = doc->page;
1255 }
1256
1257 /*
1258 * Create or update the current output page...
1259 */
1260
1261 if (first_page)
1262 pageinfo = add_page(doc, label);
1263 else
1264 pageinfo = (pstops_page_t *)cupsArrayLast(doc->pages);
1265
1266 /*
1267 * Handle first page override...
1268 */
1269
1270 if (doc->ap_input_slot || doc->ap_manual_feed)
1271 {
1272 if ((doc->page == 1 && (!doc->slow_order || !Duplex)) ||
1273 (doc->page == 2 && doc->slow_order && Duplex))
1274 {
1275 /*
1276 * First page/sheet gets AP_FIRSTPAGE_* options...
1277 */
1278
1279 pageinfo->num_options = cupsAddOption("InputSlot", doc->ap_input_slot,
1280 pageinfo->num_options,
1281 &(pageinfo->options));
1282 pageinfo->num_options = cupsAddOption("ManualFeed",
1283 doc->ap_input_slot ? "False" :
1284 doc->ap_manual_feed,
1285 pageinfo->num_options,
1286 &(pageinfo->options));
1287 pageinfo->num_options = cupsAddOption("MediaColor", doc->ap_media_color,
1288 pageinfo->num_options,
1289 &(pageinfo->options));
1290 pageinfo->num_options = cupsAddOption("MediaType", doc->ap_media_type,
1291 pageinfo->num_options,
1292 &(pageinfo->options));
1293 pageinfo->num_options = cupsAddOption("PageRegion", doc->ap_page_region,
1294 pageinfo->num_options,
1295 &(pageinfo->options));
1296 pageinfo->num_options = cupsAddOption("PageSize", doc->ap_page_size,
1297 pageinfo->num_options,
1298 &(pageinfo->options));
1299 }
1300 else if (doc->page == (Duplex + 2))
1301 {
1302 /*
1303 * Second page/sheet gets default options...
1304 */
1305
1306 pageinfo->num_options = cupsAddOption("InputSlot", doc->input_slot,
1307 pageinfo->num_options,
1308 &(pageinfo->options));
1309 pageinfo->num_options = cupsAddOption("ManualFeed",
1310 doc->input_slot ? "False" :
1311 doc->manual_feed,
1312 pageinfo->num_options,
1313 &(pageinfo->options));
1314 pageinfo->num_options = cupsAddOption("MediaColor", doc->media_color,
1315 pageinfo->num_options,
1316 &(pageinfo->options));
1317 pageinfo->num_options = cupsAddOption("MediaType", doc->media_type,
1318 pageinfo->num_options,
1319 &(pageinfo->options));
1320 pageinfo->num_options = cupsAddOption("PageRegion", doc->page_region,
1321 pageinfo->num_options,
1322 &(pageinfo->options));
1323 pageinfo->num_options = cupsAddOption("PageSize", doc->page_size,
1324 pageinfo->num_options,
1325 &(pageinfo->options));
1326 }
1327 }
1328
1329 /*
1330 * Scan comments until we see something other than %%Page*: or
1331 * %%Include*...
1332 */
1333
1334 memcpy(bounding_box, doc->bounding_box, sizeof(bounding_box));
1335
1336 while ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) > 0)
1337 {
1338 if (!strncmp(line, "%%PageBoundingBox:", 18))
1339 {
1340 /*
1341 * %%PageBoundingBox: llx lly urx ury
1342 */
1343
1344 if (sscanf(line + 18, "%d%d%d%d", bounding_box + 0,
1345 bounding_box + 1, bounding_box + 2,
1346 bounding_box + 3) != 4)
1347 {
1348 fputs("DEBUG: There was a bad %%PageBoundingBox: comment in the file.\n", stderr);
1349 memcpy(bounding_box, doc->bounding_box,
1350 sizeof(bounding_box));
1351 }
1352 else if (doc->number_up == 1 && !doc->fit_to_page && Orientation)
1353 {
1354 int temp_bbox[4]; /* Temporary bounding box */
1355
1356
1357 memcpy(temp_bbox, bounding_box, sizeof(temp_bbox));
1358
1359 fprintf(stderr, "DEBUG: Orientation = %d\n", Orientation);
1360 fprintf(stderr, "DEBUG: original bounding_box = [ %d %d %d %d ]\n",
1361 bounding_box[0], bounding_box[1],
1362 bounding_box[2], bounding_box[3]);
1363 fprintf(stderr, "DEBUG: PageWidth = %.1f, PageLength = %.1f\n",
1364 PageWidth, PageLength);
1365
1366 switch (Orientation)
1367 {
1368 case 1 : /* Landscape */
1369 bounding_box[0] = (int)(PageLength - temp_bbox[3]);
1370 bounding_box[1] = temp_bbox[0];
1371 bounding_box[2] = (int)(PageLength - temp_bbox[1]);
1372 bounding_box[3] = temp_bbox[2];
1373 break;
1374
1375 case 2 : /* Reverse Portrait */
1376 bounding_box[0] = (int)(PageWidth - temp_bbox[2]);
1377 bounding_box[1] = (int)(PageLength - temp_bbox[3]);
1378 bounding_box[2] = (int)(PageWidth - temp_bbox[0]);
1379 bounding_box[3] = (int)(PageLength - temp_bbox[1]);
1380 break;
1381
1382 case 3 : /* Reverse Landscape */
1383 bounding_box[0] = temp_bbox[1];
1384 bounding_box[1] = (int)(PageWidth - temp_bbox[2]);
1385 bounding_box[2] = temp_bbox[3];
1386 bounding_box[3] = (int)(PageWidth - temp_bbox[0]);
1387 break;
1388 }
1389
1390 fprintf(stderr, "DEBUG: updated bounding_box = [ %d %d %d %d ]\n",
1391 bounding_box[0], bounding_box[1],
1392 bounding_box[2], bounding_box[3]);
1393 }
1394 }
1395 #if 0
1396 else if (!strncmp(line, "%%PageCustomColors:", 19) ||
1397 !strncmp(line, "%%PageMedia:", 12) ||
1398 !strncmp(line, "%%PageOrientation:", 18) ||
1399 !strncmp(line, "%%PageProcessColors:", 20) ||
1400 !strncmp(line, "%%PageRequirements:", 18) ||
1401 !strncmp(line, "%%PageResources:", 16))
1402 {
1403 /*
1404 * Copy literal...
1405 */
1406 }
1407 #endif /* 0 */
1408 else if (!strncmp(line, "%%PageCustomColors:", 19))
1409 {
1410 /*
1411 * %%PageCustomColors: ...
1412 */
1413 }
1414 else if (!strncmp(line, "%%PageMedia:", 12))
1415 {
1416 /*
1417 * %%PageMedia: ...
1418 */
1419 }
1420 else if (!strncmp(line, "%%PageOrientation:", 18))
1421 {
1422 /*
1423 * %%PageOrientation: ...
1424 */
1425 }
1426 else if (!strncmp(line, "%%PageProcessColors:", 20))
1427 {
1428 /*
1429 * %%PageProcessColors: ...
1430 */
1431 }
1432 else if (!strncmp(line, "%%PageRequirements:", 18))
1433 {
1434 /*
1435 * %%PageRequirements: ...
1436 */
1437 }
1438 else if (!strncmp(line, "%%PageResources:", 16))
1439 {
1440 /*
1441 * %%PageResources: ...
1442 */
1443 }
1444 else if (!strncmp(line, "%%IncludeFeature:", 17))
1445 {
1446 /*
1447 * %%IncludeFeature: *MainKeyword OptionKeyword
1448 */
1449
1450 if (doc->number_up == 1 &&!doc->fit_to_page)
1451 pageinfo->num_options = include_feature(ppd, line,
1452 pageinfo->num_options,
1453 &(pageinfo->options));
1454 }
1455 else if (!strncmp(line, "%%BeginPageSetup", 16))
1456 {
1457 has_page_setup = 1;
1458 break;
1459 }
1460 else
1461 break;
1462 }
1463
1464 if (doc->number_up == 1)
1465 {
1466 /*
1467 * Update the document's composite and page bounding box...
1468 */
1469
1470 memcpy(pageinfo->bounding_box, bounding_box,
1471 sizeof(pageinfo->bounding_box));
1472
1473 if (bounding_box[0] < doc->new_bounding_box[0])
1474 doc->new_bounding_box[0] = bounding_box[0];
1475 if (bounding_box[1] < doc->new_bounding_box[1])
1476 doc->new_bounding_box[1] = bounding_box[1];
1477 if (bounding_box[2] > doc->new_bounding_box[2])
1478 doc->new_bounding_box[2] = bounding_box[2];
1479 if (bounding_box[3] > doc->new_bounding_box[3])
1480 doc->new_bounding_box[3] = bounding_box[3];
1481 }
1482
1483 /*
1484 * Output the page header as needed...
1485 */
1486
1487 if (!doc->slow_order && first_page)
1488 {
1489 if (!ppd || !ppd->num_filters)
1490 fprintf(stderr, "PAGE: %d %d\n", doc->page,
1491 doc->slow_collate ? 1 : doc->copies);
1492
1493 if (doc->number_up > 1)
1494 {
1495 printf("%%%%Page: (%d) %d\n", doc->page, doc->page);
1496 printf("%%%%PageBoundingBox: %.0f %.0f %.0f %.0f\n",
1497 PageLeft, PageBottom, PageRight, PageTop);
1498 }
1499 else
1500 {
1501 printf("%%%%Page: %s %d\n", pageinfo->label, doc->page);
1502 printf("%%%%PageBoundingBox: %d %d %d %d\n",
1503 pageinfo->bounding_box[0], pageinfo->bounding_box[1],
1504 pageinfo->bounding_box[2], pageinfo->bounding_box[3]);
1505 }
1506 }
1507
1508 /*
1509 * Copy any page setup commands...
1510 */
1511
1512 if (first_page)
1513 doc_puts(doc, "%%BeginPageSetup\n");
1514
1515 if (has_page_setup)
1516 {
1517 int feature = 0; /* In a Begin/EndFeature block? */
1518
1519 while ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) > 0)
1520 {
1521 if (!strncmp(line, "%%EndPageSetup", 14))
1522 break;
1523 else if (!strncmp(line, "%%BeginFeature:", 15))
1524 {
1525 feature = 1;
1526
1527 if (doc->number_up > 1 || doc->fit_to_page)
1528 continue;
1529 }
1530 else if (!strncmp(line, "%%EndFeature", 12))
1531 {
1532 feature = 0;
1533
1534 if (doc->number_up > 1 || doc->fit_to_page)
1535 continue;
1536 }
1537 else if (!strncmp(line, "%%IncludeFeature:", 17))
1538 {
1539 pageinfo->num_options = include_feature(ppd, line,
1540 pageinfo->num_options,
1541 &(pageinfo->options));
1542 continue;
1543 }
1544 else if (!strncmp(line, "%%Include", 9))
1545 continue;
1546
1547 if (line[0] != '%' && !feature)
1548 break;
1549
1550 if (!feature || (doc->number_up == 1 && !doc->fit_to_page))
1551 doc_write(doc, line, (size_t)linelen);
1552 }
1553
1554 /*
1555 * Skip %%EndPageSetup...
1556 */
1557
1558 if (linelen > 0 && !strncmp(line, "%%EndPageSetup", 14))
1559 linelen = (ssize_t)cupsFileGetLine(fp, line, linesize);
1560 }
1561
1562 if (first_page)
1563 {
1564 char *page_setup; /* PageSetup commands to send */
1565
1566
1567 if (pageinfo->num_options > 0)
1568 write_options(doc, ppd, pageinfo->num_options, pageinfo->options);
1569
1570 /*
1571 * Output commands for the current page...
1572 */
1573
1574 page_setup = ppdEmitString(ppd, PPD_ORDER_PAGE, 0);
1575
1576 if (page_setup)
1577 {
1578 doc_puts(doc, page_setup);
1579 free(page_setup);
1580 }
1581 }
1582
1583 /*
1584 * Prep for the start of the page description...
1585 */
1586
1587 start_nup(doc, number, 1, bounding_box);
1588
1589 if (first_page)
1590 doc_puts(doc, "%%EndPageSetup\n");
1591
1592 /*
1593 * Read the rest of the page description...
1594 */
1595
1596 level = 0;
1597
1598 do
1599 {
1600 if (level == 0 &&
1601 (!strncmp(line, "%%Page:", 7) ||
1602 !strncmp(line, "%%Trailer", 9) ||
1603 !strncmp(line, "%%EOF", 5)))
1604 break;
1605 else if (!strncmp(line, "%%BeginDocument", 15) ||
1606 !strncmp(line, "%ADO_BeginApplication", 21))
1607 {
1608 doc_write(doc, line, (size_t)linelen);
1609
1610 level ++;
1611 }
1612 else if ((!strncmp(line, "%%EndDocument", 13) ||
1613 !strncmp(line, "%ADO_EndApplication", 19)) && level > 0)
1614 {
1615 doc_write(doc, line, (size_t)linelen);
1616
1617 level --;
1618 }
1619 else if (!strncmp(line, "%%BeginBinary:", 14) ||
1620 (!strncmp(line, "%%BeginData:", 12) &&
1621 !strstr(line, "ASCII") && !strstr(line, "Hex")))
1622 {
1623 /*
1624 * Copy binary data...
1625 */
1626
1627 int bytes; /* Bytes of data */
1628
1629
1630 doc_write(doc, line, (size_t)linelen);
1631
1632 bytes = atoi(strchr(line, ':') + 1);
1633
1634 while (bytes > 0)
1635 {
1636 if ((size_t)bytes > linesize)
1637 linelen = cupsFileRead(fp, line, linesize);
1638 else
1639 linelen = cupsFileRead(fp, line, (size_t)bytes);
1640
1641 if (linelen < 1)
1642 {
1643 line[0] = '\0';
1644 perror("ERROR: Early end-of-file while reading binary data");
1645 return (0);
1646 }
1647
1648 doc_write(doc, line, (size_t)linelen);
1649
1650 bytes -= linelen;
1651 }
1652 }
1653 else
1654 doc_write(doc, line, (size_t)linelen);
1655 }
1656 while ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) > 0);
1657
1658 /*
1659 * Finish up this page and return...
1660 */
1661
1662 end_nup(doc, number);
1663
1664 pageinfo->length = (ssize_t)(cupsFileTell(doc->temp) - pageinfo->offset);
1665
1666 return (linelen);
1667 }
1668
1669
1670 /*
1671 * 'copy_prolog()' - Copy the document prolog section.
1672 *
1673 * This function expects "line" to be filled with a %%BeginProlog comment line.
1674 * On return, "line" will contain the next line in the file, if any.
1675 */
1676
1677 static ssize_t /* O - Length of next line */
copy_prolog(cups_file_t * fp,pstops_doc_t * doc,ppd_file_t * ppd,char * line,ssize_t linelen,size_t linesize)1678 copy_prolog(cups_file_t *fp, /* I - File to read from */
1679 pstops_doc_t *doc, /* I - Document info */
1680 ppd_file_t *ppd, /* I - PPD file */
1681 char *line, /* I - Line buffer */
1682 ssize_t linelen, /* I - Length of initial line */
1683 size_t linesize) /* I - Size of line buffer */
1684 {
1685 while (strncmp(line, "%%BeginProlog", 13))
1686 {
1687 if (!strncmp(line, "%%BeginSetup", 12) || !strncmp(line, "%%Page:", 7))
1688 break;
1689
1690 doc_write(doc, line, (size_t)linelen);
1691
1692 if ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) == 0)
1693 break;
1694 }
1695
1696 doc_puts(doc, "%%BeginProlog\n");
1697
1698 do_prolog(doc, ppd);
1699
1700 if (!strncmp(line, "%%BeginProlog", 13))
1701 {
1702 while ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) > 0)
1703 {
1704 if (!strncmp(line, "%%EndProlog", 11) ||
1705 !strncmp(line, "%%BeginSetup", 12) ||
1706 !strncmp(line, "%%Page:", 7))
1707 break;
1708
1709 doc_write(doc, line, (size_t)linelen);
1710 }
1711
1712 if (!strncmp(line, "%%EndProlog", 11))
1713 linelen = (ssize_t)cupsFileGetLine(fp, line, linesize);
1714 else
1715 fputs("DEBUG: The %%EndProlog comment is missing.\n", stderr);
1716 }
1717
1718 doc_puts(doc, "%%EndProlog\n");
1719
1720 return (linelen);
1721 }
1722
1723
1724 /*
1725 * 'copy_setup()' - Copy the document setup section.
1726 *
1727 * This function expects "line" to be filled with a %%BeginSetup comment line.
1728 * On return, "line" will contain the next line in the file, if any.
1729 */
1730
1731 static ssize_t /* O - Length of next line */
copy_setup(cups_file_t * fp,pstops_doc_t * doc,ppd_file_t * ppd,char * line,ssize_t linelen,size_t linesize)1732 copy_setup(cups_file_t *fp, /* I - File to read from */
1733 pstops_doc_t *doc, /* I - Document info */
1734 ppd_file_t *ppd, /* I - PPD file */
1735 char *line, /* I - Line buffer */
1736 ssize_t linelen, /* I - Length of initial line */
1737 size_t linesize) /* I - Size of line buffer */
1738 {
1739 int num_options; /* Number of options */
1740 cups_option_t *options; /* Options */
1741
1742
1743 while (strncmp(line, "%%BeginSetup", 12))
1744 {
1745 if (!strncmp(line, "%%Page:", 7))
1746 break;
1747
1748 doc_write(doc, line, (size_t)linelen);
1749
1750 if ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) == 0)
1751 break;
1752 }
1753
1754 doc_puts(doc, "%%BeginSetup\n");
1755
1756 do_setup(doc, ppd);
1757
1758 num_options = 0;
1759 options = NULL;
1760
1761 if (!strncmp(line, "%%BeginSetup", 12))
1762 {
1763 while (strncmp(line, "%%EndSetup", 10))
1764 {
1765 if (!strncmp(line, "%%Page:", 7))
1766 break;
1767 else if (!strncmp(line, "%%IncludeFeature:", 17))
1768 {
1769 /*
1770 * %%IncludeFeature: *MainKeyword OptionKeyword
1771 */
1772
1773 if (doc->number_up == 1 && !doc->fit_to_page)
1774 num_options = include_feature(ppd, line, num_options, &options);
1775 }
1776 else if (strncmp(line, "%%BeginSetup", 12))
1777 doc_write(doc, line, (size_t)linelen);
1778
1779 if ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) == 0)
1780 break;
1781 }
1782
1783 if (!strncmp(line, "%%EndSetup", 10))
1784 linelen = (ssize_t)cupsFileGetLine(fp, line, linesize);
1785 else
1786 fputs("DEBUG: The %%EndSetup comment is missing.\n", stderr);
1787 }
1788
1789 if (num_options > 0)
1790 {
1791 write_options(doc, ppd, num_options, options);
1792 cupsFreeOptions(num_options, options);
1793 }
1794
1795 doc_puts(doc, "%%EndSetup\n");
1796
1797 return (linelen);
1798 }
1799
1800
1801 /*
1802 * 'copy_trailer()' - Copy the document trailer.
1803 *
1804 * This function expects "line" to be filled with a %%Trailer comment line.
1805 * On return, "line" will contain the next line in the file, if any.
1806 */
1807
1808 static ssize_t /* O - Length of next line */
copy_trailer(cups_file_t * fp,pstops_doc_t * doc,ppd_file_t * ppd,int number,char * line,ssize_t linelen,size_t linesize)1809 copy_trailer(cups_file_t *fp, /* I - File to read from */
1810 pstops_doc_t *doc, /* I - Document info */
1811 ppd_file_t *ppd, /* I - PPD file */
1812 int number, /* I - Number of pages */
1813 char *line, /* I - Line buffer */
1814 ssize_t linelen, /* I - Length of initial line */
1815 size_t linesize) /* I - Size of line buffer */
1816 {
1817 /*
1818 * Write the trailer comments...
1819 */
1820
1821 (void)ppd;
1822
1823 puts("%%Trailer");
1824
1825 while (linelen > 0)
1826 {
1827 if (!strncmp(line, "%%EOF", 5))
1828 break;
1829 else if (strncmp(line, "%%Trailer", 9) &&
1830 strncmp(line, "%%Pages:", 8) &&
1831 strncmp(line, "%%BoundingBox:", 14))
1832 fwrite(line, 1, (size_t)linelen, stdout);
1833
1834 linelen = (ssize_t)cupsFileGetLine(fp, line, linesize);
1835 }
1836
1837 fprintf(stderr, "DEBUG: Wrote %d pages...\n", number);
1838
1839 printf("%%%%Pages: %d\n", number);
1840 if (doc->number_up > 1 || doc->fit_to_page)
1841 printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
1842 PageLeft, PageBottom, PageRight, PageTop);
1843 else
1844 printf("%%%%BoundingBox: %d %d %d %d\n",
1845 doc->new_bounding_box[0], doc->new_bounding_box[1],
1846 doc->new_bounding_box[2], doc->new_bounding_box[3]);
1847
1848 return (linelen);
1849 }
1850
1851
1852 /*
1853 * 'do_prolog()' - Send the necessary document prolog commands.
1854 */
1855
1856 static void
do_prolog(pstops_doc_t * doc,ppd_file_t * ppd)1857 do_prolog(pstops_doc_t *doc, /* I - Document information */
1858 ppd_file_t *ppd) /* I - PPD file */
1859 {
1860 char *ps; /* PS commands */
1861
1862
1863 /*
1864 * Send the document prolog commands...
1865 */
1866
1867 if (ppd && ppd->patches)
1868 {
1869 doc_puts(doc, "%%BeginFeature: *JobPatchFile 1\n");
1870 doc_puts(doc, ppd->patches);
1871 doc_puts(doc, "\n%%EndFeature\n");
1872 }
1873
1874 if ((ps = ppdEmitString(ppd, PPD_ORDER_PROLOG, 0.0)) != NULL)
1875 {
1876 doc_puts(doc, ps);
1877 free(ps);
1878 }
1879
1880 /*
1881 * Define ESPshowpage here so that applications that define their
1882 * own procedure to do a showpage pick it up...
1883 */
1884
1885 if (doc->use_ESPshowpage)
1886 doc_puts(doc, "userdict/ESPshowpage/showpage load put\n"
1887 "userdict/showpage{}put\n");
1888 }
1889
1890
1891 /*
1892 * 'do_setup()' - Send the necessary document setup commands.
1893 */
1894
1895 static void
do_setup(pstops_doc_t * doc,ppd_file_t * ppd)1896 do_setup(pstops_doc_t *doc, /* I - Document information */
1897 ppd_file_t *ppd) /* I - PPD file */
1898 {
1899 char *ps; /* PS commands */
1900
1901
1902 /*
1903 * Disable CTRL-D so that embedded files don't cause printing
1904 * errors...
1905 */
1906
1907 doc_puts(doc, "% Disable CTRL-D as an end-of-file marker...\n");
1908 doc_puts(doc, "userdict dup(\\004)cvn{}put (\\004\\004)cvn{}put\n");
1909
1910 /*
1911 * Mark job options...
1912 */
1913
1914 cupsMarkOptions(ppd, doc->num_options, doc->options);
1915
1916 /*
1917 * Send all the printer-specific setup commands...
1918 */
1919
1920 if ((ps = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, 0.0)) != NULL)
1921 {
1922 doc_puts(doc, ps);
1923 free(ps);
1924 }
1925
1926 if ((ps = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL)
1927 {
1928 doc_puts(doc, ps);
1929 free(ps);
1930 }
1931
1932 /*
1933 * Set the number of copies for the job...
1934 */
1935
1936 if (doc->copies != 1 && (!doc->collate || !doc->slow_collate))
1937 {
1938 doc_printf(doc, "%%RBIBeginNonPPDFeature: *NumCopies %d\n", doc->copies);
1939 doc_printf(doc,
1940 "%d/languagelevel where{pop languagelevel 2 ge}{false}ifelse\n"
1941 "{1 dict begin/NumCopies exch def currentdict end "
1942 "setpagedevice}\n"
1943 "{userdict/#copies 3 -1 roll put}ifelse\n", doc->copies);
1944 doc_puts(doc, "%RBIEndNonPPDFeature\n");
1945 }
1946
1947 /*
1948 * If we are doing N-up printing, disable setpagedevice...
1949 */
1950
1951 if (doc->number_up > 1)
1952 {
1953 doc_puts(doc, "userdict/CUPSsetpagedevice/setpagedevice load put\n");
1954 doc_puts(doc, "userdict/setpagedevice{pop}bind put\n");
1955 }
1956
1957 /*
1958 * Make sure we have rectclip and rectstroke procedures of some sort...
1959 */
1960
1961 doc_puts(doc,
1962 "% x y w h ESPrc - Clip to a rectangle.\n"
1963 "userdict/ESPrc/rectclip where{pop/rectclip load}\n"
1964 "{{newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
1965 "neg 0 rlineto closepath clip newpath}bind}ifelse put\n");
1966
1967 doc_puts(doc,
1968 "% x y w h ESPrf - Fill a rectangle.\n"
1969 "userdict/ESPrf/rectfill where{pop/rectfill load}\n"
1970 "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
1971 "neg 0 rlineto closepath fill grestore}bind}ifelse put\n");
1972
1973 doc_puts(doc,
1974 "% x y w h ESPrs - Stroke a rectangle.\n"
1975 "userdict/ESPrs/rectstroke where{pop/rectstroke load}\n"
1976 "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
1977 "neg 0 rlineto closepath stroke grestore}bind}ifelse put\n");
1978
1979 /*
1980 * Write the page and label prologs...
1981 */
1982
1983 if (doc->number_up == 2 || doc->number_up == 6)
1984 {
1985 /*
1986 * For 2- and 6-up output, rotate the labels to match the orientation
1987 * of the pages...
1988 */
1989
1990 if (Orientation & 1)
1991 write_label_prolog(doc, doc->page_label, PageBottom,
1992 PageWidth - PageLength + PageTop, PageLength);
1993 else
1994 write_label_prolog(doc, doc->page_label, PageLeft, PageRight,
1995 PageLength);
1996 }
1997 else
1998 write_label_prolog(doc, doc->page_label, PageBottom, PageTop, PageWidth);
1999 }
2000
2001
2002 /*
2003 * 'doc_printf()' - Send a formatted string to stdout and/or the temp file.
2004 *
2005 * This function should be used for all page-level output that is affected
2006 * by ordering, collation, etc.
2007 */
2008
2009 static void
doc_printf(pstops_doc_t * doc,const char * format,...)2010 doc_printf(pstops_doc_t *doc, /* I - Document information */
2011 const char *format, /* I - Printf-style format string */
2012 ...) /* I - Additional arguments as needed */
2013 {
2014 va_list ap; /* Pointer to arguments */
2015 char buffer[1024]; /* Output buffer */
2016 ssize_t bytes; /* Number of bytes to write */
2017
2018
2019 va_start(ap, format);
2020 bytes = vsnprintf(buffer, sizeof(buffer), format, ap);
2021 va_end(ap);
2022
2023 if ((size_t)bytes > sizeof(buffer))
2024 {
2025 _cupsLangPrintFilter(stderr, "ERROR",
2026 _("Buffer overflow detected, aborting."));
2027 exit(1);
2028 }
2029
2030 doc_write(doc, buffer, (size_t)bytes);
2031 }
2032
2033
2034 /*
2035 * 'doc_puts()' - Send a nul-terminated string to stdout and/or the temp file.
2036 *
2037 * This function should be used for all page-level output that is affected
2038 * by ordering, collation, etc.
2039 */
2040
2041 static void
doc_puts(pstops_doc_t * doc,const char * s)2042 doc_puts(pstops_doc_t *doc, /* I - Document information */
2043 const char *s) /* I - String to send */
2044 {
2045 doc_write(doc, s, strlen(s));
2046 }
2047
2048
2049 /*
2050 * 'doc_write()' - Send data to stdout and/or the temp file.
2051 */
2052
2053 static void
doc_write(pstops_doc_t * doc,const char * s,size_t len)2054 doc_write(pstops_doc_t *doc, /* I - Document information */
2055 const char *s, /* I - Data to send */
2056 size_t len) /* I - Number of bytes to send */
2057 {
2058 if (!doc->slow_order)
2059 fwrite(s, 1, len, stdout);
2060
2061 if (doc->temp)
2062 cupsFileWrite(doc->temp, s, len);
2063 }
2064
2065
2066 /*
2067 * 'end_nup()' - End processing for N-up printing.
2068 */
2069
2070 static void
end_nup(pstops_doc_t * doc,int number)2071 end_nup(pstops_doc_t *doc, /* I - Document information */
2072 int number) /* I - Page number */
2073 {
2074 if (doc->number_up > 1)
2075 doc_puts(doc, "userdict/ESPsave get restore\n");
2076
2077 switch (doc->number_up)
2078 {
2079 case 1 :
2080 if (doc->use_ESPshowpage)
2081 {
2082 write_labels(doc, Orientation);
2083 doc_puts(doc, "ESPshowpage\n");
2084 }
2085 break;
2086
2087 case 2 :
2088 case 6 :
2089 if (is_last_page(number) && doc->use_ESPshowpage)
2090 {
2091 if (Orientation & 1)
2092 {
2093 /*
2094 * Rotate the labels back to portrait...
2095 */
2096
2097 write_labels(doc, Orientation - 1);
2098 }
2099 else if (Orientation == 0)
2100 {
2101 /*
2102 * Rotate the labels to landscape...
2103 */
2104
2105 write_labels(doc, doc->normal_landscape ? 1 : 3);
2106 }
2107 else
2108 {
2109 /*
2110 * Rotate the labels to landscape...
2111 */
2112
2113 write_labels(doc, doc->normal_landscape ? 3 : 1);
2114 }
2115
2116 doc_puts(doc, "ESPshowpage\n");
2117 }
2118 break;
2119
2120 default :
2121 if (is_last_page(number) && doc->use_ESPshowpage)
2122 {
2123 write_labels(doc, Orientation);
2124 doc_puts(doc, "ESPshowpage\n");
2125 }
2126 break;
2127 }
2128
2129 fflush(stdout);
2130 }
2131
2132
2133 /*
2134 * 'include_feature()' - Include a printer option/feature command.
2135 */
2136
2137 static int /* O - New number of options */
include_feature(ppd_file_t * ppd,const char * line,int num_options,cups_option_t ** options)2138 include_feature(
2139 ppd_file_t *ppd, /* I - PPD file */
2140 const char *line, /* I - DSC line */
2141 int num_options, /* I - Number of options */
2142 cups_option_t **options) /* IO - Options */
2143 {
2144 char name[255], /* Option name */
2145 value[255]; /* Option value */
2146 ppd_option_t *option; /* Option in file */
2147
2148
2149 /*
2150 * Get the "%%IncludeFeature: *Keyword OptionKeyword" values...
2151 */
2152
2153 if (sscanf(line + 17, "%254s%254s", name, value) != 2)
2154 {
2155 fputs("DEBUG: The %%IncludeFeature: comment is not valid.\n", stderr);
2156 return (num_options);
2157 }
2158
2159 /*
2160 * Find the option and choice...
2161 */
2162
2163 if ((option = ppdFindOption(ppd, name + 1)) == NULL)
2164 {
2165 _cupsLangPrintFilter(stderr, "WARNING", _("Unknown option \"%s\"."),
2166 name + 1);
2167 return (num_options);
2168 }
2169
2170 if (option->section == PPD_ORDER_EXIT ||
2171 option->section == PPD_ORDER_JCL)
2172 {
2173 _cupsLangPrintFilter(stderr, "WARNING",
2174 _("Option \"%s\" cannot be included via "
2175 "%%%%IncludeFeature."), name + 1);
2176 return (num_options);
2177 }
2178
2179 if (!ppdFindChoice(option, value))
2180 {
2181 _cupsLangPrintFilter(stderr, "WARNING",
2182 _("Unknown choice \"%s\" for option \"%s\"."),
2183 value, name + 1);
2184 return (num_options);
2185 }
2186
2187 /*
2188 * Add the option to the option array and return...
2189 */
2190
2191 return (cupsAddOption(name + 1, value, num_options, options));
2192 }
2193
2194
2195 /*
2196 * 'parse_text()' - Parse a text value in a comment.
2197 *
2198 * This function parses a DSC text value as defined on page 36 of the
2199 * DSC specification. Text values are either surrounded by parenthesis
2200 * or whitespace-delimited.
2201 *
2202 * The value returned is the literal characters for the entire text
2203 * string, including any parenthesis and escape characters.
2204 */
2205
2206 static char * /* O - Value or NULL on error */
parse_text(const char * start,char ** end,char * buffer,size_t bufsize)2207 parse_text(const char *start, /* I - Start of text value */
2208 char **end, /* O - End of text value */
2209 char *buffer, /* I - Buffer */
2210 size_t bufsize) /* I - Size of buffer */
2211 {
2212 char *bufptr, /* Pointer in buffer */
2213 *bufend; /* End of buffer */
2214 int level; /* Parenthesis level */
2215
2216
2217 /*
2218 * Skip leading whitespace...
2219 */
2220
2221 while (isspace(*start & 255))
2222 start ++;
2223
2224 /*
2225 * Then copy the value...
2226 */
2227
2228 level = 0;
2229 bufptr = buffer;
2230 bufend = buffer + bufsize - 1;
2231
2232 while (*start && bufptr < bufend)
2233 {
2234 if (isspace(*start & 255) && !level)
2235 break;
2236
2237 *bufptr++ = *start;
2238
2239 if (*start == '(')
2240 level ++;
2241 else if (*start == ')')
2242 {
2243 if (!level)
2244 {
2245 start ++;
2246 break;
2247 }
2248 else
2249 level --;
2250 }
2251 else if (*start == '\\')
2252 {
2253 /*
2254 * Copy escaped character...
2255 */
2256
2257 int i; /* Looping var */
2258
2259
2260 for (i = 1;
2261 i <= 3 && isdigit(start[i] & 255) && bufptr < bufend;
2262 *bufptr++ = start[i], i ++);
2263 }
2264
2265 start ++;
2266 }
2267
2268 *bufptr = '\0';
2269
2270 /*
2271 * Return the value and new pointer into the line...
2272 */
2273
2274 if (end)
2275 *end = (char *)start;
2276
2277 if (bufptr == bufend)
2278 return (NULL);
2279 else
2280 return (buffer);
2281 }
2282
2283
2284 /*
2285 * 'set_pstops_options()' - Set pstops options.
2286 */
2287
2288 static void
set_pstops_options(pstops_doc_t * doc,ppd_file_t * ppd,char * argv[],int num_options,cups_option_t * options)2289 set_pstops_options(
2290 pstops_doc_t *doc, /* I - Document information */
2291 ppd_file_t *ppd, /* I - PPD file */
2292 char *argv[], /* I - Command-line arguments */
2293 int num_options, /* I - Number of options */
2294 cups_option_t *options) /* I - Options */
2295 {
2296 const char *val; /* Option value */
2297 int intval; /* Integer option value */
2298 ppd_attr_t *attr; /* PPD attribute */
2299 ppd_option_t *option; /* PPD option */
2300 ppd_choice_t *choice; /* PPD choice */
2301 const char *content_type; /* Original content type */
2302 int max_copies; /* Maximum number of copies supported */
2303
2304
2305 /*
2306 * Initialize document information structure...
2307 */
2308
2309 memset(doc, 0, sizeof(pstops_doc_t));
2310
2311 doc->job_id = atoi(argv[1]);
2312 doc->user = argv[2];
2313 doc->title = argv[3];
2314 doc->copies = atoi(argv[4]);
2315
2316 if (ppd && ppd->landscape > 0)
2317 doc->normal_landscape = 1;
2318
2319 doc->bounding_box[0] = (int)PageLeft;
2320 doc->bounding_box[1] = (int)PageBottom;
2321 doc->bounding_box[2] = (int)PageRight;
2322 doc->bounding_box[3] = (int)PageTop;
2323
2324 doc->new_bounding_box[0] = INT_MAX;
2325 doc->new_bounding_box[1] = INT_MAX;
2326 doc->new_bounding_box[2] = INT_MIN;
2327 doc->new_bounding_box[3] = INT_MIN;
2328
2329 /*
2330 * AP_FIRSTPAGE_* and the corresponding non-first-page options.
2331 */
2332
2333 doc->ap_input_slot = cupsGetOption("AP_FIRSTPAGE_InputSlot", num_options,
2334 options);
2335 doc->ap_manual_feed = cupsGetOption("AP_FIRSTPAGE_ManualFeed", num_options,
2336 options);
2337 doc->ap_media_color = cupsGetOption("AP_FIRSTPAGE_MediaColor", num_options,
2338 options);
2339 doc->ap_media_type = cupsGetOption("AP_FIRSTPAGE_MediaType", num_options,
2340 options);
2341 doc->ap_page_region = cupsGetOption("AP_FIRSTPAGE_PageRegion", num_options,
2342 options);
2343 doc->ap_page_size = cupsGetOption("AP_FIRSTPAGE_PageSize", num_options,
2344 options);
2345
2346 if ((choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL)
2347 doc->input_slot = choice->choice;
2348 if ((choice = ppdFindMarkedChoice(ppd, "ManualFeed")) != NULL)
2349 doc->manual_feed = choice->choice;
2350 if ((choice = ppdFindMarkedChoice(ppd, "MediaColor")) != NULL)
2351 doc->media_color = choice->choice;
2352 if ((choice = ppdFindMarkedChoice(ppd, "MediaType")) != NULL)
2353 doc->media_type = choice->choice;
2354 if ((choice = ppdFindMarkedChoice(ppd, "PageRegion")) != NULL)
2355 doc->page_region = choice->choice;
2356 if ((choice = ppdFindMarkedChoice(ppd, "PageSize")) != NULL)
2357 doc->page_size = choice->choice;
2358
2359 /*
2360 * collate, multiple-document-handling
2361 */
2362
2363 if ((val = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
2364 {
2365 /*
2366 * This IPP attribute is unnecessarily complicated...
2367 *
2368 * single-document, separate-documents-collated-copies, and
2369 * single-document-new-sheet all require collated copies.
2370 *
2371 * separate-documents-uncollated-copies allows for uncollated copies.
2372 */
2373
2374 doc->collate = _cups_strcasecmp(val, "separate-documents-uncollated-copies") != 0;
2375 }
2376
2377 if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
2378 (!_cups_strcasecmp(val, "true") ||!_cups_strcasecmp(val, "on") ||
2379 !_cups_strcasecmp(val, "yes")))
2380 doc->collate = 1;
2381
2382 /*
2383 * emit-jcl
2384 */
2385
2386 if ((val = cupsGetOption("emit-jcl", num_options, options)) != NULL &&
2387 (!_cups_strcasecmp(val, "false") || !_cups_strcasecmp(val, "off") ||
2388 !_cups_strcasecmp(val, "no") || !strcmp(val, "0")))
2389 doc->emit_jcl = 0;
2390 else
2391 doc->emit_jcl = 1;
2392
2393 /*
2394 * fit-to-page/ipp-attribute-fidelity
2395 *
2396 * (Only for original PostScript content)
2397 */
2398
2399 if ((content_type = getenv("CONTENT_TYPE")) == NULL)
2400 content_type = "application/postscript";
2401
2402 if (!_cups_strcasecmp(content_type, "application/postscript"))
2403 {
2404 if ((val = cupsGetOption("fit-to-page", num_options, options)) != NULL &&
2405 !_cups_strcasecmp(val, "true"))
2406 doc->fit_to_page = 1;
2407 else if ((val = cupsGetOption("ipp-attribute-fidelity", num_options,
2408 options)) != NULL &&
2409 !_cups_strcasecmp(val, "true"))
2410 doc->fit_to_page = 1;
2411 }
2412
2413 /*
2414 * mirror/MirrorPrint
2415 */
2416
2417 if ((choice = ppdFindMarkedChoice(ppd, "MirrorPrint")) != NULL)
2418 {
2419 val = choice->choice;
2420 choice->marked = 0;
2421 }
2422 else
2423 val = cupsGetOption("mirror", num_options, options);
2424
2425 if (val && (!_cups_strcasecmp(val, "true") || !_cups_strcasecmp(val, "on") ||
2426 !_cups_strcasecmp(val, "yes")))
2427 doc->mirror = 1;
2428
2429 /*
2430 * number-up
2431 */
2432
2433 if ((val = cupsGetOption("number-up", num_options, options)) != NULL)
2434 {
2435 switch (intval = atoi(val))
2436 {
2437 case 1 :
2438 case 2 :
2439 case 4 :
2440 case 6 :
2441 case 9 :
2442 case 16 :
2443 doc->number_up = intval;
2444 break;
2445 default :
2446 _cupsLangPrintFilter(stderr, "ERROR",
2447 _("Unsupported number-up value %d, using "
2448 "number-up=1."), intval);
2449 doc->number_up = 1;
2450 break;
2451 }
2452 }
2453 else
2454 doc->number_up = 1;
2455
2456 /*
2457 * number-up-layout
2458 */
2459
2460 if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL)
2461 {
2462 if (!_cups_strcasecmp(val, "lrtb"))
2463 doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
2464 else if (!_cups_strcasecmp(val, "lrbt"))
2465 doc->number_up_layout = PSTOPS_LAYOUT_LRBT;
2466 else if (!_cups_strcasecmp(val, "rltb"))
2467 doc->number_up_layout = PSTOPS_LAYOUT_RLTB;
2468 else if (!_cups_strcasecmp(val, "rlbt"))
2469 doc->number_up_layout = PSTOPS_LAYOUT_RLBT;
2470 else if (!_cups_strcasecmp(val, "tblr"))
2471 doc->number_up_layout = PSTOPS_LAYOUT_TBLR;
2472 else if (!_cups_strcasecmp(val, "tbrl"))
2473 doc->number_up_layout = PSTOPS_LAYOUT_TBRL;
2474 else if (!_cups_strcasecmp(val, "btlr"))
2475 doc->number_up_layout = PSTOPS_LAYOUT_BTLR;
2476 else if (!_cups_strcasecmp(val, "btrl"))
2477 doc->number_up_layout = PSTOPS_LAYOUT_BTRL;
2478 else
2479 {
2480 _cupsLangPrintFilter(stderr, "ERROR",
2481 _("Unsupported number-up-layout value %s, using "
2482 "number-up-layout=lrtb."), val);
2483 doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
2484 }
2485 }
2486 else
2487 doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
2488
2489 /*
2490 * OutputOrder
2491 */
2492
2493 if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL)
2494 {
2495 if (!_cups_strcasecmp(val, "Reverse"))
2496 doc->output_order = 1;
2497 }
2498 else if (ppd)
2499 {
2500 /*
2501 * Figure out the right default output order from the PPD file...
2502 */
2503
2504 if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL &&
2505 (attr = ppdFindAttr(ppd, "PageStackOrder", choice->choice)) != NULL &&
2506 attr->value)
2507 doc->output_order = !_cups_strcasecmp(attr->value, "Reverse");
2508 else if ((attr = ppdFindAttr(ppd, "DefaultOutputOrder", NULL)) != NULL &&
2509 attr->value)
2510 doc->output_order = !_cups_strcasecmp(attr->value, "Reverse");
2511 }
2512
2513 /*
2514 * page-border
2515 */
2516
2517 if ((val = cupsGetOption("page-border", num_options, options)) != NULL)
2518 {
2519 if (!_cups_strcasecmp(val, "none"))
2520 doc->page_border = PSTOPS_BORDERNONE;
2521 else if (!_cups_strcasecmp(val, "single"))
2522 doc->page_border = PSTOPS_BORDERSINGLE;
2523 else if (!_cups_strcasecmp(val, "single-thick"))
2524 doc->page_border = PSTOPS_BORDERSINGLE2;
2525 else if (!_cups_strcasecmp(val, "double"))
2526 doc->page_border = PSTOPS_BORDERDOUBLE;
2527 else if (!_cups_strcasecmp(val, "double-thick"))
2528 doc->page_border = PSTOPS_BORDERDOUBLE2;
2529 else
2530 {
2531 _cupsLangPrintFilter(stderr, "ERROR",
2532 _("Unsupported page-border value %s, using "
2533 "page-border=none."), val);
2534 doc->page_border = PSTOPS_BORDERNONE;
2535 }
2536 }
2537 else
2538 doc->page_border = PSTOPS_BORDERNONE;
2539
2540 /*
2541 * page-label
2542 */
2543
2544 doc->page_label = cupsGetOption("page-label", num_options, options);
2545
2546 /*
2547 * page-ranges
2548 */
2549
2550 doc->page_ranges = cupsGetOption("page-ranges", num_options, options);
2551
2552 /*
2553 * page-set
2554 */
2555
2556 doc->page_set = cupsGetOption("page-set", num_options, options);
2557
2558 /*
2559 * Now figure out if we have to force collated copies, etc.
2560 */
2561
2562 if ((attr = ppdFindAttr(ppd, "cupsMaxCopies", NULL)) != NULL)
2563 max_copies = atoi(attr->value);
2564 else if (ppd && ppd->manual_copies)
2565 max_copies = 1;
2566 else
2567 max_copies = 9999;
2568
2569 if (doc->copies > max_copies)
2570 doc->collate = 1;
2571 else if (ppd && ppd->manual_copies && Duplex && doc->copies > 1)
2572 {
2573 /*
2574 * Force collated copies when printing a duplexed document to
2575 * a non-PS printer that doesn't do hardware copy generation.
2576 * Otherwise the copies will end up on the front/back side of
2577 * each page.
2578 */
2579
2580 doc->collate = 1;
2581 }
2582
2583 /*
2584 * See if we have to filter the fast or slow way...
2585 */
2586
2587 if (doc->collate && doc->copies > 1)
2588 {
2589 /*
2590 * See if we need to manually collate the pages...
2591 */
2592
2593 doc->slow_collate = 1;
2594
2595 if (doc->copies <= max_copies &&
2596 (choice = ppdFindMarkedChoice(ppd, "Collate")) != NULL &&
2597 !_cups_strcasecmp(choice->choice, "True"))
2598 {
2599 /*
2600 * Hardware collate option is selected, see if the option is
2601 * conflicting - if not, collate in hardware. Otherwise,
2602 * turn the hardware collate option off...
2603 */
2604
2605 if ((option = ppdFindOption(ppd, "Collate")) != NULL &&
2606 !option->conflicted)
2607 doc->slow_collate = 0;
2608 else
2609 ppdMarkOption(ppd, "Collate", "False");
2610 }
2611 }
2612 else
2613 doc->slow_collate = 0;
2614
2615 if (!ppdFindOption(ppd, "OutputOrder") && doc->output_order)
2616 doc->slow_order = 1;
2617 else
2618 doc->slow_order = 0;
2619
2620 if (Duplex &&
2621 (doc->slow_collate || doc->slow_order ||
2622 ((attr = ppdFindAttr(ppd, "cupsEvenDuplex", NULL)) != NULL &&
2623 attr->value && !_cups_strcasecmp(attr->value, "true"))))
2624 doc->slow_duplex = 1;
2625 else
2626 doc->slow_duplex = 0;
2627
2628 /*
2629 * Create a temporary file for page data if we need to filter slowly...
2630 */
2631
2632 if (doc->slow_order || doc->slow_collate)
2633 {
2634 if ((doc->temp = cupsTempFile2(doc->tempfile,
2635 sizeof(doc->tempfile))) == NULL)
2636 {
2637 perror("DEBUG: Unable to create temporary file");
2638 exit(1);
2639 }
2640 }
2641
2642 /*
2643 * Figure out if we should use ESPshowpage or not...
2644 */
2645
2646 if (doc->page_label || getenv("CLASSIFICATION") || doc->number_up > 1 ||
2647 doc->page_border)
2648 {
2649 /*
2650 * Yes, use ESPshowpage...
2651 */
2652
2653 doc->use_ESPshowpage = 1;
2654 }
2655
2656 fprintf(stderr, "DEBUG: slow_collate=%d, slow_duplex=%d, slow_order=%d\n",
2657 doc->slow_collate, doc->slow_duplex, doc->slow_order);
2658 }
2659
2660
2661 /*
2662 * 'skip_page()' - Skip past a page that won't be printed.
2663 */
2664
2665 static ssize_t /* O - Length of next line */
skip_page(cups_file_t * fp,char * line,ssize_t linelen,size_t linesize)2666 skip_page(cups_file_t *fp, /* I - File to read from */
2667 char *line, /* I - Line buffer */
2668 ssize_t linelen, /* I - Length of initial line */
2669 size_t linesize) /* I - Size of line buffer */
2670 {
2671 int level; /* Embedded document level */
2672
2673
2674 level = 0;
2675
2676 while ((linelen = (ssize_t)cupsFileGetLine(fp, line, linesize)) > 0)
2677 {
2678 if (level == 0 &&
2679 (!strncmp(line, "%%Page:", 7) || !strncmp(line, "%%Trailer", 9)))
2680 break;
2681 else if (!strncmp(line, "%%BeginDocument", 15) ||
2682 !strncmp(line, "%ADO_BeginApplication", 21))
2683 level ++;
2684 else if ((!strncmp(line, "%%EndDocument", 13) ||
2685 !strncmp(line, "%ADO_EndApplication", 19)) && level > 0)
2686 level --;
2687 else if (!strncmp(line, "%%BeginBinary:", 14) ||
2688 (!strncmp(line, "%%BeginData:", 12) &&
2689 !strstr(line, "ASCII") && !strstr(line, "Hex")))
2690 {
2691 /*
2692 * Skip binary data...
2693 */
2694
2695 ssize_t bytes; /* Bytes of data */
2696
2697 bytes = atoi(strchr(line, ':') + 1);
2698
2699 while (bytes > 0)
2700 {
2701 if ((size_t)bytes > linesize)
2702 linelen = (ssize_t)cupsFileRead(fp, line, linesize);
2703 else
2704 linelen = (ssize_t)cupsFileRead(fp, line, (size_t)bytes);
2705
2706 if (linelen < 1)
2707 {
2708 line[0] = '\0';
2709 perror("ERROR: Early end-of-file while reading binary data");
2710 return (0);
2711 }
2712
2713 bytes -= linelen;
2714 }
2715 }
2716 }
2717
2718 return (linelen);
2719 }
2720
2721
2722 /*
2723 * 'start_nup()' - Start processing for N-up printing.
2724 */
2725
2726 static void
start_nup(pstops_doc_t * doc,int number,int show_border,const int * bounding_box)2727 start_nup(pstops_doc_t *doc, /* I - Document information */
2728 int number, /* I - Page number */
2729 int show_border, /* I - Show the border? */
2730 const int *bounding_box) /* I - BoundingBox value */
2731 {
2732 int pos; /* Position on page */
2733 int x, y; /* Relative position of subpage */
2734 double w, l, /* Width and length of subpage */
2735 tx, ty; /* Translation values for subpage */
2736 double pagew, /* Printable width of page */
2737 pagel; /* Printable height of page */
2738 int bboxx, /* BoundingBox X origin */
2739 bboxy, /* BoundingBox Y origin */
2740 bboxw, /* BoundingBox width */
2741 bboxl; /* BoundingBox height */
2742 double margin = 0; /* Current margin for border */
2743
2744
2745 if (doc->number_up > 1)
2746 doc_puts(doc, "userdict/ESPsave save put\n");
2747
2748 pos = (number - 1) % doc->number_up;
2749 pagew = PageRight - PageLeft;
2750 pagel = PageTop - PageBottom;
2751
2752 if (doc->fit_to_page)
2753 {
2754 bboxx = bounding_box[0];
2755 bboxy = bounding_box[1];
2756 bboxw = bounding_box[2] - bounding_box[0];
2757 bboxl = bounding_box[3] - bounding_box[1];
2758 }
2759 else
2760 {
2761 bboxx = 0;
2762 bboxy = 0;
2763 bboxw = (int)PageWidth;
2764 bboxl = (int)PageLength;
2765 }
2766
2767 fprintf(stderr, "DEBUG: pagew = %.1f, pagel = %.1f\n", pagew, pagel);
2768 fprintf(stderr, "DEBUG: bboxx = %d, bboxy = %d, bboxw = %d, bboxl = %d\n",
2769 bboxx, bboxy, bboxw, bboxl);
2770 fprintf(stderr, "DEBUG: PageLeft = %.1f, PageRight = %.1f\n",
2771 PageLeft, PageRight);
2772 fprintf(stderr, "DEBUG: PageTop = %.1f, PageBottom = %.1f\n",
2773 PageTop, PageBottom);
2774 fprintf(stderr, "DEBUG: PageWidth = %.1f, PageLength = %.1f\n",
2775 PageWidth, PageLength);
2776
2777 switch (Orientation)
2778 {
2779 case 1 : /* Landscape */
2780 doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", PageLength);
2781 break;
2782 case 2 : /* Reverse Portrait */
2783 doc_printf(doc, "%.1f %.1f translate 180 rotate\n", PageWidth,
2784 PageLength);
2785 break;
2786 case 3 : /* Reverse Landscape */
2787 doc_printf(doc, "0.0 %.1f translate -90 rotate\n", PageWidth);
2788 break;
2789 }
2790
2791 /*
2792 * Mirror the page as needed...
2793 */
2794
2795 if (doc->mirror)
2796 doc_printf(doc, "%.1f 0.0 translate -1 1 scale\n", PageWidth);
2797
2798 /*
2799 * Offset and scale as necessary for fit_to_page/fit-to-page/number-up...
2800 */
2801
2802 if (Duplex && doc->number_up > 1 && ((number / doc->number_up) & 1))
2803 doc_printf(doc, "%.1f %.1f translate\n", PageWidth - PageRight, PageBottom);
2804 else if (doc->number_up > 1 || doc->fit_to_page)
2805 doc_printf(doc, "%.1f %.1f translate\n", PageLeft, PageBottom);
2806
2807 switch (doc->number_up)
2808 {
2809 default :
2810 if (doc->fit_to_page)
2811 {
2812 w = pagew;
2813 l = w * bboxl / bboxw;
2814
2815 if (l > pagel)
2816 {
2817 l = pagel;
2818 w = l * bboxw / bboxl;
2819 }
2820
2821 tx = 0.5 * (pagew - w);
2822 ty = 0.5 * (pagel - l);
2823
2824 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n", tx, ty,
2825 w / bboxw, l / bboxl);
2826 }
2827 else
2828 w = PageWidth;
2829 break;
2830
2831 case 2 :
2832 if (Orientation & 1)
2833 {
2834 x = pos & 1;
2835
2836 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
2837 x = 1 - x;
2838
2839 w = pagel;
2840 l = w * bboxl / bboxw;
2841
2842 if (l > (pagew * 0.5))
2843 {
2844 l = pagew * 0.5;
2845 w = l * bboxw / bboxl;
2846 }
2847
2848 tx = 0.5 * (pagew * 0.5 - l);
2849 ty = 0.5 * (pagel - w);
2850
2851 if (doc->normal_landscape)
2852 doc_printf(doc, "0.0 %.1f translate -90 rotate\n", pagel);
2853 else
2854 doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", pagew);
2855
2856 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
2857 ty, tx + pagew * 0.5 * x, w / bboxw, l / bboxl);
2858 }
2859 else
2860 {
2861 x = pos & 1;
2862
2863 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
2864 x = 1 - x;
2865
2866 l = pagew;
2867 w = l * bboxw / bboxl;
2868
2869 if (w > (pagel * 0.5))
2870 {
2871 w = pagel * 0.5;
2872 l = w * bboxl / bboxw;
2873 }
2874
2875 tx = 0.5 * (pagel * 0.5 - w);
2876 ty = 0.5 * (pagew - l);
2877
2878 if (doc->normal_landscape)
2879 doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", pagew);
2880 else
2881 doc_printf(doc, "0.0 %.1f translate -90 rotate\n", pagel);
2882
2883 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
2884 tx + pagel * 0.5 * x, ty, w / bboxw, l / bboxl);
2885 }
2886 break;
2887
2888 case 4 :
2889 if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
2890 {
2891 x = (pos / 2) & 1;
2892 y = pos & 1;
2893 }
2894 else
2895 {
2896 x = pos & 1;
2897 y = (pos / 2) & 1;
2898 }
2899
2900 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
2901 x = 1 - x;
2902
2903 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
2904 y = 1 - y;
2905
2906 w = pagew * 0.5;
2907 l = w * bboxl / bboxw;
2908
2909 if (l > (pagel * 0.5))
2910 {
2911 l = pagel * 0.5;
2912 w = l * bboxw / bboxl;
2913 }
2914
2915 tx = 0.5 * (pagew * 0.5 - w);
2916 ty = 0.5 * (pagel * 0.5 - l);
2917
2918 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
2919 tx + x * pagew * 0.5, ty + y * pagel * 0.5,
2920 w / bboxw, l / bboxl);
2921 break;
2922
2923 case 6 :
2924 if (Orientation & 1)
2925 {
2926 if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
2927 {
2928 x = pos / 3;
2929 y = pos % 3;
2930
2931 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
2932 x = 1 - x;
2933
2934 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
2935 y = 2 - y;
2936 }
2937 else
2938 {
2939 x = pos & 1;
2940 y = pos / 2;
2941
2942 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
2943 x = 1 - x;
2944
2945 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
2946 y = 2 - y;
2947 }
2948
2949 w = pagel * 0.5;
2950 l = w * bboxl / bboxw;
2951
2952 if (l > (pagew * 0.333))
2953 {
2954 l = pagew * 0.333;
2955 w = l * bboxw / bboxl;
2956 }
2957
2958 tx = 0.5 * (pagel - 2 * w);
2959 ty = 0.5 * (pagew - 3 * l);
2960
2961 if (doc->normal_landscape)
2962 doc_printf(doc, "0 %.1f translate -90 rotate\n", pagel);
2963 else
2964 doc_printf(doc, "%.1f 0 translate 90 rotate\n", pagew);
2965
2966 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
2967 tx + x * w, ty + y * l, l / bboxl, w / bboxw);
2968 }
2969 else
2970 {
2971 if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
2972 {
2973 x = pos / 2;
2974 y = pos & 1;
2975
2976 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
2977 x = 2 - x;
2978
2979 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
2980 y = 1 - y;
2981 }
2982 else
2983 {
2984 x = pos % 3;
2985 y = pos / 3;
2986
2987 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
2988 x = 2 - x;
2989
2990 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
2991 y = 1 - y;
2992 }
2993
2994 l = pagew * 0.5;
2995 w = l * bboxw / bboxl;
2996
2997 if (w > (pagel * 0.333))
2998 {
2999 w = pagel * 0.333;
3000 l = w * bboxl / bboxw;
3001 }
3002
3003 tx = 0.5 * (pagel - 3 * w);
3004 ty = 0.5 * (pagew - 2 * l);
3005
3006 if (doc->normal_landscape)
3007 doc_printf(doc, "%.1f 0 translate 90 rotate\n", pagew);
3008 else
3009 doc_printf(doc, "0 %.1f translate -90 rotate\n", pagel);
3010
3011 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
3012 tx + w * x, ty + l * y, w / bboxw, l / bboxl);
3013
3014 }
3015 break;
3016
3017 case 9 :
3018 if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
3019 {
3020 x = (pos / 3) % 3;
3021 y = pos % 3;
3022 }
3023 else
3024 {
3025 x = pos % 3;
3026 y = (pos / 3) % 3;
3027 }
3028
3029 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
3030 x = 2 - x;
3031
3032 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
3033 y = 2 - y;
3034
3035 w = pagew * 0.333;
3036 l = w * bboxl / bboxw;
3037
3038 if (l > (pagel * 0.333))
3039 {
3040 l = pagel * 0.333;
3041 w = l * bboxw / bboxl;
3042 }
3043
3044 tx = 0.5 * (pagew * 0.333 - w);
3045 ty = 0.5 * (pagel * 0.333 - l);
3046
3047 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
3048 tx + x * pagew * 0.333, ty + y * pagel * 0.333,
3049 w / bboxw, l / bboxl);
3050 break;
3051
3052 case 16 :
3053 if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
3054 {
3055 x = (pos / 4) & 3;
3056 y = pos & 3;
3057 }
3058 else
3059 {
3060 x = pos & 3;
3061 y = (pos / 4) & 3;
3062 }
3063
3064 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
3065 x = 3 - x;
3066
3067 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
3068 y = 3 - y;
3069
3070 w = pagew * 0.25;
3071 l = w * bboxl / bboxw;
3072
3073 if (l > (pagel * 0.25))
3074 {
3075 l = pagel * 0.25;
3076 w = l * bboxw / bboxl;
3077 }
3078
3079 tx = 0.5 * (pagew * 0.25 - w);
3080 ty = 0.5 * (pagel * 0.25 - l);
3081
3082 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
3083 tx + x * pagew * 0.25, ty + y * pagel * 0.25,
3084 w / bboxw, l / bboxl);
3085 break;
3086 }
3087
3088 /*
3089 * Draw borders as necessary...
3090 */
3091
3092 if (doc->page_border && show_border)
3093 {
3094 int rects; /* Number of border rectangles */
3095 double fscale; /* Scaling value for points */
3096
3097
3098 rects = (doc->page_border & PSTOPS_BORDERDOUBLE) ? 2 : 1;
3099 fscale = PageWidth / w;
3100 margin = 2.25 * fscale;
3101
3102 /*
3103 * Set the line width and color...
3104 */
3105
3106 doc_puts(doc, "gsave\n");
3107 doc_printf(doc, "%.3f setlinewidth 0 setgray newpath\n",
3108 (doc->page_border & PSTOPS_BORDERTHICK) ? 0.5 * fscale :
3109 0.24 * fscale);
3110
3111 /*
3112 * Draw border boxes...
3113 */
3114
3115 for (; rects > 0; rects --, margin += 2 * fscale)
3116 if (doc->number_up > 1)
3117 doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrs\n",
3118 margin,
3119 margin,
3120 bboxw - 2 * margin,
3121 bboxl - 2 * margin);
3122 else
3123 doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrs\n",
3124 PageLeft + margin,
3125 PageBottom + margin,
3126 PageRight - PageLeft - 2 * margin,
3127 PageTop - PageBottom - 2 * margin);
3128
3129 /*
3130 * Restore pen settings...
3131 */
3132
3133 doc_puts(doc, "grestore\n");
3134 }
3135
3136 if (doc->fit_to_page)
3137 {
3138 /*
3139 * Offset the page by its bounding box...
3140 */
3141
3142 doc_printf(doc, "%d %d translate\n", -bounding_box[0],
3143 -bounding_box[1]);
3144 }
3145
3146 if (doc->fit_to_page || doc->number_up > 1)
3147 {
3148 /*
3149 * Clip the page to the page's bounding box...
3150 */
3151
3152 doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrc\n",
3153 bboxx + margin, bboxy + margin,
3154 bboxw - 2 * margin, bboxl - 2 * margin);
3155 }
3156 }
3157
3158
3159 /*
3160 * 'write_label_prolog()' - Write the prolog with the classification
3161 * and page label.
3162 */
3163
3164 static void
write_label_prolog(pstops_doc_t * doc,const char * label,float bottom,float top,float width)3165 write_label_prolog(pstops_doc_t *doc, /* I - Document info */
3166 const char *label, /* I - Page label */
3167 float bottom, /* I - Bottom position in points */
3168 float top, /* I - Top position in points */
3169 float width) /* I - Width in points */
3170 {
3171 const char *classification; /* CLASSIFICATION environment variable */
3172 const char *ptr; /* Temporary string pointer */
3173
3174
3175 /*
3176 * First get the current classification...
3177 */
3178
3179 if ((classification = getenv("CLASSIFICATION")) == NULL)
3180 classification = "";
3181 if (strcmp(classification, "none") == 0)
3182 classification = "";
3183
3184 /*
3185 * If there is nothing to show, bind an empty 'write labels' procedure
3186 * and return...
3187 */
3188
3189 if (!classification[0] && (label == NULL || !label[0]))
3190 {
3191 doc_puts(doc, "userdict/ESPwl{}bind put\n");
3192 return;
3193 }
3194
3195 /*
3196 * Set the classification + page label string...
3197 */
3198
3199 doc_puts(doc, "userdict");
3200 if (!strcmp(classification, "confidential"))
3201 doc_puts(doc, "/ESPpl(CONFIDENTIAL");
3202 else if (!strcmp(classification, "classified"))
3203 doc_puts(doc, "/ESPpl(CLASSIFIED");
3204 else if (!strcmp(classification, "secret"))
3205 doc_puts(doc, "/ESPpl(SECRET");
3206 else if (!strcmp(classification, "topsecret"))
3207 doc_puts(doc, "/ESPpl(TOP SECRET");
3208 else if (!strcmp(classification, "unclassified"))
3209 doc_puts(doc, "/ESPpl(UNCLASSIFIED");
3210 else
3211 {
3212 doc_puts(doc, "/ESPpl(");
3213
3214 for (ptr = classification; *ptr; ptr ++)
3215 {
3216 if (*ptr < 32 || *ptr > 126)
3217 doc_printf(doc, "\\%03o", *ptr);
3218 else if (*ptr == '_')
3219 doc_puts(doc, " ");
3220 else if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
3221 doc_printf(doc, "\\%c", *ptr);
3222 else
3223 doc_printf(doc, "%c", *ptr);
3224 }
3225 }
3226
3227 if (label)
3228 {
3229 if (classification[0])
3230 doc_puts(doc, " - ");
3231
3232 /*
3233 * Quote the label string as needed...
3234 */
3235
3236 for (ptr = label; *ptr; ptr ++)
3237 {
3238 if (*ptr < 32 || *ptr > 126)
3239 doc_printf(doc, "\\%03o", *ptr);
3240 else if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
3241 doc_printf(doc, "\\%c", *ptr);
3242 else
3243 doc_printf(doc, "%c", *ptr);
3244 }
3245 }
3246
3247 doc_puts(doc, ")put\n");
3248
3249 /*
3250 * Then get a 14 point Helvetica-Bold font...
3251 */
3252
3253 doc_puts(doc, "userdict/ESPpf /Helvetica-Bold findfont 14 scalefont put\n");
3254
3255 /*
3256 * Finally, the procedure to write the labels on the page...
3257 */
3258
3259 doc_puts(doc, "userdict/ESPwl{\n");
3260 doc_puts(doc, " ESPpf setfont\n");
3261 doc_printf(doc, " ESPpl stringwidth pop dup 12 add exch -0.5 mul %.0f add\n",
3262 width * 0.5f);
3263 doc_puts(doc, " 1 setgray\n");
3264 doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrf\n", bottom - 2.0);
3265 doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrf\n", top - 18.0);
3266 doc_puts(doc, " 0 setgray\n");
3267 doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrs\n", bottom - 2.0);
3268 doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrs\n", top - 18.0);
3269 doc_printf(doc, " dup %.0f moveto ESPpl show\n", bottom + 2.0);
3270 doc_printf(doc, " %.0f moveto ESPpl show\n", top - 14.0);
3271 doc_puts(doc, "pop\n");
3272 doc_puts(doc, "}bind put\n");
3273 }
3274
3275
3276 /*
3277 * 'write_labels()' - Write the actual page labels.
3278 *
3279 * This function is a copy of the one in common.c since we need to
3280 * use doc_puts/doc_printf instead of puts/printf...
3281 */
3282
3283 static void
write_labels(pstops_doc_t * doc,int orient)3284 write_labels(pstops_doc_t *doc, /* I - Document information */
3285 int orient) /* I - Orientation of the page */
3286 {
3287 float width, /* Width of page */
3288 length; /* Length of page */
3289
3290
3291 doc_puts(doc, "gsave\n");
3292
3293 if ((orient ^ Orientation) & 1)
3294 {
3295 width = PageLength;
3296 length = PageWidth;
3297 }
3298 else
3299 {
3300 width = PageWidth;
3301 length = PageLength;
3302 }
3303
3304 switch (orient & 3)
3305 {
3306 case 1 : /* Landscape */
3307 doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", length);
3308 break;
3309 case 2 : /* Reverse Portrait */
3310 doc_printf(doc, "%.1f %.1f translate 180 rotate\n", width, length);
3311 break;
3312 case 3 : /* Reverse Landscape */
3313 doc_printf(doc, "0.0 %.1f translate -90 rotate\n", width);
3314 break;
3315 }
3316
3317 doc_puts(doc, "ESPwl\n");
3318 doc_puts(doc, "grestore\n");
3319 }
3320
3321
3322 /*
3323 * 'write_options()' - Write options provided via %%IncludeFeature.
3324 */
3325
3326 static void
write_options(pstops_doc_t * doc,ppd_file_t * ppd,int num_options,cups_option_t * options)3327 write_options(
3328 pstops_doc_t *doc, /* I - Document */
3329 ppd_file_t *ppd, /* I - PPD file */
3330 int num_options, /* I - Number of options */
3331 cups_option_t *options) /* I - Options */
3332 {
3333 int i; /* Looping var */
3334 ppd_option_t *option; /* PPD option */
3335 float min_order; /* Minimum OrderDependency value */
3336 char *doc_setup, /* DocumentSetup commands to send */
3337 *any_setup; /* AnySetup commands to send */
3338
3339
3340 /*
3341 * Figure out the minimum OrderDependency value...
3342 */
3343
3344 if ((option = ppdFindOption(ppd, "PageRegion")) != NULL)
3345 min_order = option->order;
3346 else
3347 min_order = 999.0f;
3348
3349 for (i = 0; i < num_options; i ++)
3350 if ((option = ppdFindOption(ppd, options[i].name)) != NULL &&
3351 option->order < min_order)
3352 min_order = option->order;
3353
3354 /*
3355 * Mark and extract them...
3356 */
3357
3358 cupsMarkOptions(ppd, num_options, options);
3359
3360 doc_setup = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, min_order);
3361 any_setup = ppdEmitString(ppd, PPD_ORDER_ANY, min_order);
3362
3363 /*
3364 * Then send them out...
3365 */
3366
3367 if (doc->number_up > 1)
3368 {
3369 /*
3370 * Temporarily restore setpagedevice so we can set the options...
3371 */
3372
3373 doc_puts(doc, "userdict/setpagedevice/CUPSsetpagedevice load put\n");
3374 }
3375
3376 if (doc_setup)
3377 {
3378 doc_puts(doc, doc_setup);
3379 free(doc_setup);
3380 }
3381
3382 if (any_setup)
3383 {
3384 doc_puts(doc, any_setup);
3385 free(any_setup);
3386 }
3387
3388 if (doc->number_up > 1)
3389 {
3390 /*
3391 * Disable setpagedevice again...
3392 */
3393
3394 doc_puts(doc, "userdict/setpagedevice{pop}bind put\n");
3395 }
3396 }
3397