]> git.ipfire.org Git - thirdparty/cups.git/blob - filter/pstops.c
Merge changes from CUPS 1.4svn-r7961.
[thirdparty/cups.git] / filter / pstops.c
1 /*
2 * "$Id: pstops.c 7886 2008-08-29 01:51: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 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
2231
2232 /*
2233 * Get the "%%IncludeFeature: *Keyword OptionKeyword" values...
2234 */
2235
2236 if (sscanf(line + 17, "%254s%254s", name, value) != 2)
2237 {
2238 fputs(_("ERROR: Bad %%IncludeFeature: comment!\n"), stderr);
2239 return (num_options);
2240 }
2241
2242 /*
2243 * Find the option and choice...
2244 */
2245
2246 if ((option = ppdFindOption(ppd, name + 1)) == NULL)
2247 {
2248 fprintf(stderr, _("WARNING: Unknown option \"%s\"!\n"), name + 1);
2249 return (num_options);
2250 }
2251
2252 if (option->section == PPD_ORDER_EXIT ||
2253 option->section == PPD_ORDER_JCL)
2254 {
2255 fprintf(stderr, _("WARNING: Option \"%s\" cannot be included via "
2256 "IncludeFeature!\n"), name + 1);
2257 return (num_options);
2258 }
2259
2260 if (!ppdFindChoice(option, value))
2261 {
2262 fprintf(stderr, _("WARNING: Unknown choice \"%s\" for option \"%s\"!\n"),
2263 value, name + 1);
2264 return (num_options);
2265 }
2266
2267 /*
2268 * Add the option to the option array and return...
2269 */
2270
2271 return (cupsAddOption(name + 1, value, num_options, options));
2272 }
2273
2274
2275 /*
2276 * 'parse_text()' - Parse a text value in a comment...
2277 *
2278 * This function parses a DSC text value as defined on page 36 of the
2279 * DSC specification. Text values are either surrounded by parenthesis
2280 * or whitespace-delimited.
2281 *
2282 * The value returned is the literal characters for the entire text
2283 * string, including any parenthesis and escape characters.
2284 */
2285
2286 static char * /* O - Value or NULL on error */
2287 parse_text(const char *start, /* I - Start of text value */
2288 char **end, /* O - End of text value */
2289 char *buffer, /* I - Buffer */
2290 size_t bufsize) /* I - Size of buffer */
2291 {
2292 char *bufptr, /* Pointer in buffer */
2293 *bufend; /* End of buffer */
2294 int level; /* Parenthesis level */
2295
2296
2297 /*
2298 * Skip leading whitespace...
2299 */
2300
2301 while (isspace(*start & 255))
2302 start ++;
2303
2304 /*
2305 * Then copy the value...
2306 */
2307
2308 level = 0;
2309 bufptr = buffer;
2310 bufend = buffer + bufsize - 1;
2311
2312 while (bufptr < bufend)
2313 {
2314 if (isspace(*start & 255) && !level)
2315 break;
2316
2317 *bufptr++ = *start;
2318
2319 if (*start == '(')
2320 level ++;
2321 else if (*start == ')')
2322 {
2323 if (!level)
2324 {
2325 start ++;
2326 break;
2327 }
2328 else
2329 level --;
2330 }
2331 else if (*start == '\\')
2332 {
2333 /*
2334 * Copy escaped character...
2335 */
2336
2337 int i; /* Looping var */
2338
2339
2340 for (i = 1;
2341 i <= 3 && isdigit(start[i] & 255) && bufptr < bufend;
2342 *bufptr++ = start[i], i ++);
2343 }
2344
2345 start ++;
2346 }
2347
2348 *bufptr = '\0';
2349
2350 /*
2351 * Return the value and new pointer into the line...
2352 */
2353
2354 if (end)
2355 *end = (char *)start;
2356
2357 if (bufptr == bufend)
2358 return (NULL);
2359 else
2360 return (buffer);
2361 }
2362
2363
2364 /*
2365 * 'set_pstops_options()' - Set pstops options...
2366 */
2367
2368 static void
2369 set_pstops_options(
2370 pstops_doc_t *doc, /* I - Document information */
2371 ppd_file_t *ppd, /* I - PPD file */
2372 char *argv[], /* I - Command-line arguments */
2373 int num_options, /* I - Number of options */
2374 cups_option_t *options) /* I - Options */
2375 {
2376 const char *val; /* Option value */
2377 int intval; /* Integer option value */
2378 ppd_attr_t *attr; /* PPD attribute */
2379 ppd_option_t *option; /* PPD option */
2380 ppd_choice_t *choice; /* PPD choice */
2381
2382
2383 /*
2384 * Initialize document information structure...
2385 */
2386
2387 memset(doc, 0, sizeof(pstops_doc_t));
2388
2389 doc->job_id = atoi(argv[1]);
2390 doc->user = argv[2];
2391 doc->title = argv[3];
2392 doc->copies = atoi(argv[4]);
2393
2394 if (ppd && ppd->landscape > 0)
2395 doc->normal_landscape = 1;
2396
2397 doc->bounding_box[0] = (int)PageLeft;
2398 doc->bounding_box[1] = (int)PageBottom;
2399 doc->bounding_box[2] = (int)PageRight;
2400 doc->bounding_box[3] = (int)PageTop;
2401
2402 doc->new_bounding_box[0] = INT_MAX;
2403 doc->new_bounding_box[1] = INT_MAX;
2404 doc->new_bounding_box[2] = INT_MIN;
2405 doc->new_bounding_box[3] = INT_MIN;
2406
2407 /*
2408 * AP_FIRSTPAGE_InputSlot
2409 */
2410
2411 doc->ap_input_slot = cupsGetOption("AP_FIRSTPAGE_InputSlot", num_options,
2412 options);
2413
2414 /*
2415 * AP_FIRSTPAGE_ManualFeed
2416 */
2417
2418 doc->ap_manual_feed = cupsGetOption("AP_FIRSTPAGE_ManualFeed", num_options,
2419 options);
2420
2421 /*
2422 * brightness
2423 */
2424
2425 if ((val = cupsGetOption("brightness", num_options, options)) != NULL)
2426 {
2427 /*
2428 * Get brightness value from 10 to 1000.
2429 */
2430
2431 intval = atoi(val);
2432
2433 if (intval < 10 || intval > 1000)
2434 {
2435 fprintf(stderr, _("ERROR: Unsupported brightness value %s, using "
2436 "brightness=100!\n"), val);
2437 doc->brightness = 1.0f;
2438 }
2439 else
2440 doc->brightness = intval * 0.01f;
2441 }
2442 else
2443 doc->brightness = 1.0f;
2444
2445 /*
2446 * collate, multiple-document-handling
2447 */
2448
2449 if ((val = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
2450 {
2451 /*
2452 * This IPP attribute is unnecessarily complicated...
2453 *
2454 * single-document, separate-documents-collated-copies, and
2455 * single-document-new-sheet all require collated copies.
2456 *
2457 * separate-documents-uncollated-copies allows for uncollated copies.
2458 */
2459
2460 doc->collate = strcasecmp(val, "separate-documents-uncollated-copies") != 0;
2461 }
2462
2463 if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
2464 (!strcasecmp(val, "true") ||!strcasecmp(val, "on") ||
2465 !strcasecmp(val, "yes")))
2466 doc->collate = 1;
2467
2468 /*
2469 * emit-jcl
2470 */
2471
2472 if ((val = cupsGetOption("emit-jcl", num_options, options)) != NULL &&
2473 (!strcasecmp(val, "false") || !strcasecmp(val, "off") ||
2474 !strcasecmp(val, "no") || !strcmp(val, "0")))
2475 doc->emit_jcl = 0;
2476 else
2477 doc->emit_jcl = 1;
2478
2479 /*
2480 * fitplot
2481 */
2482
2483 if ((val = cupsGetOption("fitplot", num_options, options)) != NULL &&
2484 (!strcasecmp(val, "true") || !strcasecmp(val, "on") ||
2485 !strcasecmp(val, "yes")))
2486 doc->fitplot = 1;
2487
2488 /*
2489 * gamma
2490 */
2491
2492 if ((val = cupsGetOption("gamma", num_options, options)) != NULL)
2493 {
2494 /*
2495 * Get gamma value from 1 to 10000...
2496 */
2497
2498 intval = atoi(val);
2499
2500 if (intval < 1 || intval > 10000)
2501 {
2502 fprintf(stderr, _("ERROR: Unsupported gamma value %s, using "
2503 "gamma=1000!\n"), val);
2504 doc->gamma = 1.0f;
2505 }
2506 else
2507 doc->gamma = intval * 0.001f;
2508 }
2509 else
2510 doc->gamma = 1.0f;
2511
2512 /*
2513 * InputSlot
2514 */
2515
2516 if ((choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL)
2517 doc->input_slot = choice->choice;
2518
2519 /*
2520 * ManualFeed
2521 */
2522
2523 if ((choice = ppdFindMarkedChoice(ppd, "ManualFeed")) != NULL)
2524 doc->manual_feed = choice->choice;
2525
2526 if ((choice = ppdFindMarkedChoice(ppd, "MirrorPrint")) != NULL)
2527 {
2528 val = choice->choice;
2529 choice->marked = 0;
2530 }
2531 else
2532 val = cupsGetOption("mirror", num_options, options);
2533
2534 if (val && (!strcasecmp(val, "true") || !strcasecmp(val, "on") ||
2535 !strcasecmp(val, "yes")))
2536 doc->mirror = 1;
2537
2538 /*
2539 * number-up
2540 */
2541
2542 if ((val = cupsGetOption("number-up", num_options, options)) != NULL)
2543 {
2544 switch (intval = atoi(val))
2545 {
2546 case 1 :
2547 case 2 :
2548 case 4 :
2549 case 6 :
2550 case 9 :
2551 case 16 :
2552 doc->number_up = intval;
2553 break;
2554 default :
2555 fprintf(stderr,
2556 _("ERROR: Unsupported number-up value %d, using "
2557 "number-up=1!\n"), intval);
2558 doc->number_up = 1;
2559 break;
2560 }
2561 }
2562 else
2563 doc->number_up = 1;
2564
2565 /*
2566 * number-up-layout
2567 */
2568
2569 if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL)
2570 {
2571 if (!strcasecmp(val, "lrtb"))
2572 doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
2573 else if (!strcasecmp(val, "lrbt"))
2574 doc->number_up_layout = PSTOPS_LAYOUT_LRBT;
2575 else if (!strcasecmp(val, "rltb"))
2576 doc->number_up_layout = PSTOPS_LAYOUT_RLTB;
2577 else if (!strcasecmp(val, "rlbt"))
2578 doc->number_up_layout = PSTOPS_LAYOUT_RLBT;
2579 else if (!strcasecmp(val, "tblr"))
2580 doc->number_up_layout = PSTOPS_LAYOUT_TBLR;
2581 else if (!strcasecmp(val, "tbrl"))
2582 doc->number_up_layout = PSTOPS_LAYOUT_TBRL;
2583 else if (!strcasecmp(val, "btlr"))
2584 doc->number_up_layout = PSTOPS_LAYOUT_BTLR;
2585 else if (!strcasecmp(val, "btrl"))
2586 doc->number_up_layout = PSTOPS_LAYOUT_BTRL;
2587 else
2588 {
2589 fprintf(stderr, _("ERROR: Unsupported number-up-layout value %s, using "
2590 "number-up-layout=lrtb!\n"), val);
2591 doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
2592 }
2593 }
2594 else
2595 doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
2596
2597 /*
2598 * OutputOrder
2599 */
2600
2601 if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL)
2602 {
2603 if (!strcasecmp(val, "Reverse"))
2604 doc->output_order = 1;
2605 }
2606 else if (ppd)
2607 {
2608 /*
2609 * Figure out the right default output order from the PPD file...
2610 */
2611
2612 if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL &&
2613 (attr = ppdFindAttr(ppd, "PageStackOrder", choice->choice)) != NULL &&
2614 attr->value)
2615 doc->output_order = !strcasecmp(attr->value, "Reverse");
2616 else if ((attr = ppdFindAttr(ppd, "DefaultOutputOrder", NULL)) != NULL &&
2617 attr->value)
2618 doc->output_order = !strcasecmp(attr->value, "Reverse");
2619 }
2620
2621 /*
2622 * page-border
2623 */
2624
2625 if ((val = cupsGetOption("page-border", num_options, options)) != NULL)
2626 {
2627 if (!strcasecmp(val, "none"))
2628 doc->page_border = PSTOPS_BORDERNONE;
2629 else if (!strcasecmp(val, "single"))
2630 doc->page_border = PSTOPS_BORDERSINGLE;
2631 else if (!strcasecmp(val, "single-thick"))
2632 doc->page_border = PSTOPS_BORDERSINGLE2;
2633 else if (!strcasecmp(val, "double"))
2634 doc->page_border = PSTOPS_BORDERDOUBLE;
2635 else if (!strcasecmp(val, "double-thick"))
2636 doc->page_border = PSTOPS_BORDERDOUBLE2;
2637 else
2638 {
2639 fprintf(stderr, _("ERROR: Unsupported page-border value %s, using "
2640 "page-border=none!\n"), val);
2641 doc->page_border = PSTOPS_BORDERNONE;
2642 }
2643 }
2644 else
2645 doc->page_border = PSTOPS_BORDERNONE;
2646
2647 /*
2648 * page-label
2649 */
2650
2651 doc->page_label = cupsGetOption("page-label", num_options, options);
2652
2653 /*
2654 * page-ranges
2655 */
2656
2657 doc->page_ranges = cupsGetOption("page-ranges", num_options, options);
2658
2659 /*
2660 * page-set
2661 */
2662
2663 doc->page_set = cupsGetOption("page-set", num_options, options);
2664
2665 /*
2666 * Now figure out if we have to force collated copies, etc.
2667 */
2668
2669 if (ppd && ppd->manual_copies && Duplex && doc->copies > 1)
2670 {
2671 /*
2672 * Force collated copies when printing a duplexed document to
2673 * a non-PS printer that doesn't do hardware copy generation.
2674 * Otherwise the copies will end up on the front/back side of
2675 * each page.
2676 */
2677
2678 doc->collate = 1;
2679 }
2680
2681 /*
2682 * See if we have to filter the fast or slow way...
2683 */
2684
2685 if (doc->collate && doc->copies > 1)
2686 {
2687 /*
2688 * See if we need to manually collate the pages...
2689 */
2690
2691 doc->slow_collate = 1;
2692
2693 if ((choice = ppdFindMarkedChoice(ppd, "Collate")) != NULL &&
2694 !strcasecmp(choice->choice, "True"))
2695 {
2696 /*
2697 * Hardware collate option is selected, see if the option is
2698 * conflicting - if not, collate in hardware. Otherwise,
2699 * turn the hardware collate option off...
2700 */
2701
2702 if ((option = ppdFindOption(ppd, "Collate")) != NULL &&
2703 !option->conflicted)
2704 doc->slow_collate = 0;
2705 else
2706 ppdMarkOption(ppd, "Collate", "False");
2707 }
2708 }
2709 else
2710 doc->slow_collate = 0;
2711
2712 if (!ppdFindOption(ppd, "OutputOrder") && doc->output_order)
2713 doc->slow_order = 1;
2714 else
2715 doc->slow_order = 0;
2716
2717 if (Duplex &&
2718 (doc->slow_collate || doc->slow_order ||
2719 ((attr = ppdFindAttr(ppd, "cupsEvenDuplex", NULL)) != NULL &&
2720 attr->value && !strcasecmp(attr->value, "true"))))
2721 doc->slow_duplex = 1;
2722 else
2723 doc->slow_duplex = 0;
2724
2725 /*
2726 * Create a temporary file for page data if we need to filter slowly...
2727 */
2728
2729 if (doc->slow_order || doc->slow_collate)
2730 {
2731 if ((doc->temp = cupsTempFile2(doc->tempfile,
2732 sizeof(doc->tempfile))) == NULL)
2733 {
2734 fprintf(stderr, _("ERROR: Unable to create temporary file: %s\n"),
2735 strerror(errno));
2736 exit(1);
2737 }
2738 }
2739
2740 /*
2741 * Figure out if we should use ESPshowpage or not...
2742 */
2743
2744 if (doc->page_label || getenv("CLASSIFICATION") || doc->number_up > 1 ||
2745 doc->page_border)
2746 {
2747 /*
2748 * Yes, use ESPshowpage...
2749 */
2750
2751 doc->use_ESPshowpage = 1;
2752 }
2753
2754 fprintf(stderr, "DEBUG: slow_collate=%d, slow_duplex=%d, slow_order=%d\n",
2755 doc->slow_collate, doc->slow_duplex, doc->slow_order);
2756 }
2757
2758
2759 /*
2760 * 'skip_page()' - Skip past a page that won't be printed...
2761 */
2762
2763 static ssize_t /* O - Length of next line */
2764 skip_page(cups_file_t *fp, /* I - File to read from */
2765 char *line, /* I - Line buffer */
2766 ssize_t linelen, /* I - Length of initial line */
2767 size_t linesize) /* I - Size of line buffer */
2768 {
2769 int level; /* Embedded document level */
2770
2771
2772 level = 0;
2773
2774 while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0)
2775 {
2776 if (level == 0 &&
2777 (!strncmp(line, "%%Page:", 7) || !strncmp(line, "%%Trailer", 9)))
2778 break;
2779 else if (!strncmp(line, "%%BeginDocument", 15) ||
2780 !strncmp(line, "%ADO_BeginApplication", 21))
2781 level ++;
2782 else if ((!strncmp(line, "%%EndDocument", 13) ||
2783 !strncmp(line, "%ADO_EndApplication", 19)) && level > 0)
2784 level --;
2785 else if (!strncmp(line, "%%BeginBinary:", 14) ||
2786 (!strncmp(line, "%%BeginData:", 12) &&
2787 !strstr(line, "ASCII") && !strstr(line, "Hex")))
2788 {
2789 /*
2790 * Skip binary data...
2791 */
2792
2793 int bytes; /* Bytes of data */
2794
2795
2796 bytes = atoi(strchr(line, ':') + 1);
2797
2798 while (bytes > 0)
2799 {
2800 if (bytes > linesize)
2801 linelen = cupsFileRead(fp, line, linesize);
2802 else
2803 linelen = cupsFileRead(fp, line, bytes);
2804
2805 if (linelen < 1)
2806 {
2807 line[0] = '\0';
2808 perror("ERROR: Early end-of-file while reading binary data");
2809 return (0);
2810 }
2811
2812 bytes -= linelen;
2813 }
2814 }
2815 }
2816
2817 return (linelen);
2818 }
2819
2820
2821 /*
2822 * 'start_nup()' - Start processing for N-up printing...
2823 */
2824
2825 static void
2826 start_nup(pstops_doc_t *doc, /* I - Document information */
2827 int number, /* I - Page number */
2828 int show_border, /* I - Show the border? */
2829 const int *bounding_box) /* I - BoundingBox value */
2830 {
2831 int pos; /* Position on page */
2832 int x, y; /* Relative position of subpage */
2833 float w, l, /* Width and length of subpage */
2834 tx, ty; /* Translation values for subpage */
2835 float pagew, /* Printable width of page */
2836 pagel; /* Printable height of page */
2837 int bboxx, /* BoundingBox X origin */
2838 bboxy, /* BoundingBox Y origin */
2839 bboxw, /* BoundingBox width */
2840 bboxl; /* BoundingBox height */
2841 float margin = 0; /* Current margin for border */
2842
2843
2844 if (doc->number_up > 1)
2845 doc_puts(doc, "userdict/ESPsave save put\n");
2846
2847 pos = (number - 1) % doc->number_up;
2848 pagew = PageRight - PageLeft;
2849 pagel = PageTop - PageBottom;
2850
2851 if (doc->fitplot)
2852 {
2853 bboxx = bounding_box[0];
2854 bboxy = bounding_box[1];
2855 bboxw = bounding_box[2] - bounding_box[0];
2856 bboxl = bounding_box[3] - bounding_box[1];
2857 }
2858 else
2859 {
2860 bboxx = 0;
2861 bboxy = 0;
2862 bboxw = PageWidth;
2863 bboxl = PageLength;
2864 }
2865
2866 fprintf(stderr, "DEBUG: pagew = %.1f, pagel = %.1f\n", pagew, pagel);
2867 fprintf(stderr, "DEBUG: bboxx = %d, bboxy = %d, bboxw = %d, bboxl = %d\n",
2868 bboxx, bboxy, bboxw, bboxl);
2869 fprintf(stderr, "DEBUG: PageLeft = %.1f, PageRight = %.1f\n",
2870 PageLeft, PageRight);
2871 fprintf(stderr, "DEBUG: PageTop = %.1f, PageBottom = %.1f\n",
2872 PageTop, PageBottom);
2873 fprintf(stderr, "DEBUG: PageWidth = %.1f, PageLength = %.1f\n",
2874 PageWidth, PageLength);
2875
2876 switch (Orientation)
2877 {
2878 case 1 : /* Landscape */
2879 doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", PageLength);
2880 break;
2881 case 2 : /* Reverse Portrait */
2882 doc_printf(doc, "%.1f %.1f translate 180 rotate\n", PageWidth,
2883 PageLength);
2884 break;
2885 case 3 : /* Reverse Landscape */
2886 doc_printf(doc, "0.0 %.1f translate -90 rotate\n", PageWidth);
2887 break;
2888 }
2889
2890 if (Duplex && doc->number_up > 1 && ((number / doc->number_up) & 1))
2891 doc_printf(doc, "%.1f %.1f translate\n", PageWidth - PageRight, PageBottom);
2892 else if (doc->number_up > 1 || doc->fitplot)
2893 doc_printf(doc, "%.1f %.1f translate\n", PageLeft, PageBottom);
2894
2895 if (doc->mirror)
2896 doc_printf(doc, "%.1f 0.0 translate -1 1 scale\n", PageWidth);
2897
2898 switch (doc->number_up)
2899 {
2900 default :
2901 if (doc->fitplot)
2902 {
2903 w = pagew;
2904 l = w * bboxl / bboxw;
2905
2906 if (l > pagel)
2907 {
2908 l = pagel;
2909 w = l * bboxw / bboxl;
2910 }
2911
2912 tx = 0.5 * (pagew - w);
2913 ty = 0.5 * (pagel - l);
2914
2915 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n", tx, ty,
2916 w / bboxw, l / bboxl);
2917 }
2918 else
2919 w = PageWidth;
2920 break;
2921
2922 case 2 :
2923 if (Orientation & 1)
2924 {
2925 x = pos & 1;
2926
2927 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
2928 x = 1 - x;
2929
2930 w = pagel;
2931 l = w * bboxl / bboxw;
2932
2933 if (l > (pagew * 0.5))
2934 {
2935 l = pagew * 0.5;
2936 w = l * bboxw / bboxl;
2937 }
2938
2939 tx = 0.5 * (pagew * 0.5 - l);
2940 ty = 0.5 * (pagel - w);
2941
2942 if (doc->normal_landscape)
2943 doc_printf(doc, "0.0 %.1f translate -90 rotate\n", pagel);
2944 else
2945 doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", pagew);
2946
2947 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
2948 ty, tx + pagew * 0.5 * x, w / bboxw, l / bboxl);
2949 }
2950 else
2951 {
2952 x = pos & 1;
2953
2954 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
2955 x = 1 - x;
2956
2957 l = pagew;
2958 w = l * bboxw / bboxl;
2959
2960 if (w > (pagel * 0.5))
2961 {
2962 w = pagel * 0.5;
2963 l = w * bboxl / bboxw;
2964 }
2965
2966 tx = 0.5 * (pagel * 0.5 - w);
2967 ty = 0.5 * (pagew - l);
2968
2969 if (doc->normal_landscape)
2970 doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", pagew);
2971 else
2972 doc_printf(doc, "0.0 %.1f translate -90 rotate\n", pagel);
2973
2974 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
2975 tx + pagel * 0.5 * x, ty, w / bboxw, l / bboxl);
2976 }
2977 break;
2978
2979 case 4 :
2980 if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
2981 {
2982 x = (pos / 2) & 1;
2983 y = pos & 1;
2984 }
2985 else
2986 {
2987 x = pos & 1;
2988 y = (pos / 2) & 1;
2989 }
2990
2991 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
2992 x = 1 - x;
2993
2994 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
2995 y = 1 - y;
2996
2997 w = pagew * 0.5;
2998 l = w * bboxl / bboxw;
2999
3000 if (l > (pagel * 0.5))
3001 {
3002 l = pagel * 0.5;
3003 w = l * bboxw / bboxl;
3004 }
3005
3006 tx = 0.5 * (pagew * 0.5 - w);
3007 ty = 0.5 * (pagel * 0.5 - l);
3008
3009 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
3010 tx + x * pagew * 0.5, ty + y * pagel * 0.5,
3011 w / bboxw, l / bboxl);
3012 break;
3013
3014 case 6 :
3015 if (Orientation & 1)
3016 {
3017 if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
3018 {
3019 x = pos / 3;
3020 y = pos % 3;
3021
3022 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
3023 x = 1 - x;
3024
3025 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
3026 y = 2 - y;
3027 }
3028 else
3029 {
3030 x = pos & 1;
3031 y = pos / 2;
3032
3033 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
3034 x = 1 - x;
3035
3036 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
3037 y = 2 - y;
3038 }
3039
3040 w = pagel * 0.5;
3041 l = w * bboxl / bboxw;
3042
3043 if (l > (pagew * 0.333))
3044 {
3045 l = pagew * 0.333;
3046 w = l * bboxw / bboxl;
3047 }
3048
3049 tx = 0.5 * (pagel - 2 * w);
3050 ty = 0.5 * (pagew - 3 * l);
3051
3052 if (doc->normal_landscape)
3053 doc_printf(doc, "0 %.1f translate -90 rotate\n", pagel);
3054 else
3055 doc_printf(doc, "%.1f 0 translate 90 rotate\n", pagew);
3056
3057 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
3058 tx + x * w, ty + y * l, l / bboxl, w / bboxw);
3059 }
3060 else
3061 {
3062 if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
3063 {
3064 x = pos / 2;
3065 y = pos & 1;
3066
3067 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
3068 x = 2 - x;
3069
3070 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
3071 y = 1 - y;
3072 }
3073 else
3074 {
3075 x = pos % 3;
3076 y = pos / 3;
3077
3078 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
3079 x = 2 - x;
3080
3081 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
3082 y = 1 - y;
3083 }
3084
3085 l = pagew * 0.5;
3086 w = l * bboxw / bboxl;
3087
3088 if (w > (pagel * 0.333))
3089 {
3090 w = pagel * 0.333;
3091 l = w * bboxl / bboxw;
3092 }
3093
3094 tx = 0.5 * (pagel - 3 * w);
3095 ty = 0.5 * (pagew - 2 * l);
3096
3097 if (doc->normal_landscape)
3098 doc_printf(doc, "%.1f 0 translate 90 rotate\n", pagew);
3099 else
3100 doc_printf(doc, "0 %.1f translate -90 rotate\n", pagel);
3101
3102 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
3103 tx + w * x, ty + l * y, w / bboxw, l / bboxl);
3104
3105 }
3106 break;
3107
3108 case 9 :
3109 if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
3110 {
3111 x = (pos / 3) % 3;
3112 y = pos % 3;
3113 }
3114 else
3115 {
3116 x = pos % 3;
3117 y = (pos / 3) % 3;
3118 }
3119
3120 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
3121 x = 2 - x;
3122
3123 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
3124 y = 2 - y;
3125
3126 w = pagew * 0.333;
3127 l = w * bboxl / bboxw;
3128
3129 if (l > (pagel * 0.333))
3130 {
3131 l = pagel * 0.333;
3132 w = l * bboxw / bboxl;
3133 }
3134
3135 tx = 0.5 * (pagew * 0.333 - w);
3136 ty = 0.5 * (pagel * 0.333 - l);
3137
3138 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
3139 tx + x * pagew * 0.333, ty + y * pagel * 0.333,
3140 w / bboxw, l / bboxl);
3141 break;
3142
3143 case 16 :
3144 if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
3145 {
3146 x = (pos / 4) & 3;
3147 y = pos & 3;
3148 }
3149 else
3150 {
3151 x = pos & 3;
3152 y = (pos / 4) & 3;
3153 }
3154
3155 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
3156 x = 3 - x;
3157
3158 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
3159 y = 3 - y;
3160
3161 w = pagew * 0.25;
3162 l = w * bboxl / bboxw;
3163
3164 if (l > (pagel * 0.25))
3165 {
3166 l = pagel * 0.25;
3167 w = l * bboxw / bboxl;
3168 }
3169
3170 tx = 0.5 * (pagew * 0.25 - w);
3171 ty = 0.5 * (pagel * 0.25 - l);
3172
3173 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
3174 tx + x * pagew * 0.25, ty + y * pagel * 0.25,
3175 w / bboxw, l / bboxl);
3176 break;
3177 }
3178
3179 /*
3180 * Draw borders as necessary...
3181 */
3182
3183 if (doc->page_border && show_border)
3184 {
3185 int rects; /* Number of border rectangles */
3186 float fscale; /* Scaling value for points */
3187
3188
3189 rects = (doc->page_border & PSTOPS_BORDERDOUBLE) ? 2 : 1;
3190 fscale = PageWidth / w;
3191 margin = 2.25 * fscale;
3192
3193 /*
3194 * Set the line width and color...
3195 */
3196
3197 doc_puts(doc, "gsave\n");
3198 doc_printf(doc, "%.3f setlinewidth 0 setgray newpath\n",
3199 (doc->page_border & PSTOPS_BORDERTHICK) ? 0.5 * fscale :
3200 0.24 * fscale);
3201
3202 /*
3203 * Draw border boxes...
3204 */
3205
3206 for (; rects > 0; rects --, margin += 2 * fscale)
3207 if (doc->number_up > 1)
3208 doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrs\n",
3209 margin,
3210 margin,
3211 bboxw - 2 * margin,
3212 bboxl - 2 * margin);
3213 else
3214 doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrs\n",
3215 PageLeft + margin,
3216 PageBottom + margin,
3217 PageRight - PageLeft - 2 * margin,
3218 PageTop - PageBottom - 2 * margin);
3219
3220 /*
3221 * Restore pen settings...
3222 */
3223
3224 doc_puts(doc, "grestore\n");
3225 }
3226
3227 if (doc->fitplot)
3228 {
3229 /*
3230 * Offset the page by its bounding box...
3231 */
3232
3233 doc_printf(doc, "%d %d translate\n", -bounding_box[0],
3234 -bounding_box[1]);
3235 }
3236
3237 if (doc->fitplot || doc->number_up > 1)
3238 {
3239 /*
3240 * Clip the page to the page's bounding box...
3241 */
3242
3243 doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrc\n",
3244 bboxx + margin, bboxy + margin,
3245 bboxw - 2 * margin, bboxl - 2 * margin);
3246 }
3247 }
3248
3249
3250 /*
3251 * 'write_label_prolog()' - Write the prolog with the classification
3252 * and page label.
3253 */
3254
3255 static void
3256 write_label_prolog(pstops_doc_t *doc, /* I - Document info */
3257 const char *label, /* I - Page label */
3258 float bottom, /* I - Bottom position in points */
3259 float top, /* I - Top position in points */
3260 float width) /* I - Width in points */
3261 {
3262 const char *classification; /* CLASSIFICATION environment variable */
3263 const char *ptr; /* Temporary string pointer */
3264
3265
3266 /*
3267 * First get the current classification...
3268 */
3269
3270 if ((classification = getenv("CLASSIFICATION")) == NULL)
3271 classification = "";
3272 if (strcmp(classification, "none") == 0)
3273 classification = "";
3274
3275 /*
3276 * If there is nothing to show, bind an empty 'write labels' procedure
3277 * and return...
3278 */
3279
3280 if (!classification[0] && (label == NULL || !label[0]))
3281 {
3282 doc_puts(doc, "userdict/ESPwl{}bind put\n");
3283 return;
3284 }
3285
3286 /*
3287 * Set the classification + page label string...
3288 */
3289
3290 doc_puts(doc, "userdict");
3291 if (!strcmp(classification, "confidential"))
3292 doc_puts(doc, "/ESPpl(CONFIDENTIAL");
3293 else if (!strcmp(classification, "classified"))
3294 doc_puts(doc, "/ESPpl(CLASSIFIED");
3295 else if (!strcmp(classification, "secret"))
3296 doc_puts(doc, "/ESPpl(SECRET");
3297 else if (!strcmp(classification, "topsecret"))
3298 doc_puts(doc, "/ESPpl(TOP SECRET");
3299 else if (!strcmp(classification, "unclassified"))
3300 doc_puts(doc, "/ESPpl(UNCLASSIFIED");
3301 else
3302 {
3303 doc_puts(doc, "/ESPpl(");
3304
3305 for (ptr = classification; *ptr; ptr ++)
3306 {
3307 if (*ptr < 32 || *ptr > 126)
3308 doc_printf(doc, "\\%03o", *ptr);
3309 else if (*ptr == '_')
3310 doc_puts(doc, " ");
3311 else if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
3312 doc_printf(doc, "\\%c", *ptr);
3313 else
3314 doc_printf(doc, "%c", *ptr);
3315 }
3316 }
3317
3318 if (label)
3319 {
3320 if (classification[0])
3321 doc_puts(doc, " - ");
3322
3323 /*
3324 * Quote the label string as needed...
3325 */
3326
3327 for (ptr = label; *ptr; ptr ++)
3328 {
3329 if (*ptr < 32 || *ptr > 126)
3330 doc_printf(doc, "\\%03o", *ptr);
3331 else if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
3332 doc_printf(doc, "\\%c", *ptr);
3333 else
3334 doc_printf(doc, "%c", *ptr);
3335 }
3336 }
3337
3338 doc_puts(doc, ")put\n");
3339
3340 /*
3341 * Then get a 14 point Helvetica-Bold font...
3342 */
3343
3344 doc_puts(doc, "userdict/ESPpf /Helvetica-Bold findfont 14 scalefont put\n");
3345
3346 /*
3347 * Finally, the procedure to write the labels on the page...
3348 */
3349
3350 doc_puts(doc, "userdict/ESPwl{\n");
3351 doc_puts(doc, " ESPpf setfont\n");
3352 doc_printf(doc, " ESPpl stringwidth pop dup 12 add exch -0.5 mul %.0f add\n",
3353 width * 0.5f);
3354 doc_puts(doc, " 1 setgray\n");
3355 doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrf\n", bottom - 2.0);
3356 doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrf\n", top - 18.0);
3357 doc_puts(doc, " 0 setgray\n");
3358 doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrs\n", bottom - 2.0);
3359 doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrs\n", top - 18.0);
3360 doc_printf(doc, " dup %.0f moveto ESPpl show\n", bottom + 2.0);
3361 doc_printf(doc, " %.0f moveto ESPpl show\n", top - 14.0);
3362 doc_puts(doc, "pop\n");
3363 doc_puts(doc, "}bind put\n");
3364 }
3365
3366
3367 /*
3368 * 'write_labels()' - Write the actual page labels.
3369 *
3370 * This function is a copy of the one in common.c since we need to
3371 * use doc_puts/doc_printf instead of puts/printf...
3372 */
3373
3374 static void
3375 write_labels(pstops_doc_t *doc, /* I - Document information */
3376 int orient) /* I - Orientation of the page */
3377 {
3378 float width, /* Width of page */
3379 length; /* Length of page */
3380
3381
3382 doc_puts(doc, "gsave\n");
3383
3384 if ((orient ^ Orientation) & 1)
3385 {
3386 width = PageLength;
3387 length = PageWidth;
3388 }
3389 else
3390 {
3391 width = PageWidth;
3392 length = PageLength;
3393 }
3394
3395 switch (orient & 3)
3396 {
3397 case 1 : /* Landscape */
3398 doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", length);
3399 break;
3400 case 2 : /* Reverse Portrait */
3401 doc_printf(doc, "%.1f %.1f translate 180 rotate\n", width, length);
3402 break;
3403 case 3 : /* Reverse Landscape */
3404 doc_printf(doc, "0.0 %.1f translate -90 rotate\n", width);
3405 break;
3406 }
3407
3408 doc_puts(doc, "ESPwl\n");
3409 doc_puts(doc, "grestore\n");
3410 }
3411
3412
3413 /*
3414 * End of "$Id: pstops.c 7886 2008-08-29 01:51:57Z mike $".
3415 */