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