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