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