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