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