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