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