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