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