Load cups into easysw/current.
[thirdparty/cups.git] / filter / pstops.c
1 /*
2  * "$Id: pstops.c 4672 2005-09-18 04:25:46Z mike $"
3  *
4  *   PostScript filter for the Common UNIX Printing System (CUPS).
5  *
6  *   Copyright 1993-2005 by Easy Software Products.
7  *
8  *   These coded instructions, statements, and computer programs are the
9  *   property of Easy Software Products and are protected by Federal
10  *   copyright law.  Distribution and use rights are outlined in the file
11  *   "LICENSE.txt" which should have been included with this file.  If this
12  *   file is missing or damaged please contact Easy Software Products
13  *   at:
14  *
15  *       Attn: CUPS Licensing Information
16  *       Easy Software Products
17  *       44141 Airport View Drive, Suite 204
18  *       Hollywood, Maryland 20636 USA
19  *
20  *       Voice: (301) 373-9600
21  *       EMail: cups-info@cups.org
22  *         WWW: http://www.cups.org
23  *
24  *   This file is subject to the Apple OS-Developed Software exception.
25  *
26  * Contents:
27  *
28  *   main()            - Main entry...
29  *   check_range()     - Check to see if the current page is selected for
30  *   copy_bytes()      - Copy bytes from the input file to stdout...
31  *   do_prolog()       - Send the necessary document prolog commands...
32  *   do_setup()        - Send the necessary document setup commands...
33  *   end_nup()         - End processing for N-up printing...
34  *   include_feature() - Include a printer option/feature command.
35  *   psbcp()           - Enable the binary communications protocol on the printer.
36  *   psgets()          - Get a line from a file.
37  *   pswrite()         - Write data from a file.
38  *   start_nup()       - Start processing for N-up printing...
39  */
40
41 /*
42  * Include necessary headers...
43  */
44
45 #include "common.h"
46
47
48 /*
49  * Constants...
50  */
51
52 #define MAX_PAGES       10000
53
54 #define BORDER_NONE     0               /* No border or hairline border */
55 #define BORDER_THICK    1               /* Think border */
56 #define BORDER_SINGLE   2               /* Single-line hairline border */
57 #define BORDER_SINGLE2  3               /* Single-line thick border */
58 #define BORDER_DOUBLE   4               /* Double-line hairline border */
59 #define BORDER_DOUBLE2  5               /* Double-line thick border */
60
61 #define LAYOUT_LRBT     0               /* Left to right, bottom to top */
62 #define LAYOUT_LRTB     1               /* Left to right, top to bottom */
63 #define LAYOUT_RLBT     2               /* Right to left, bottom to top */
64 #define LAYOUT_RLTB     3               /* Right to left, top to bottom */
65 #define LAYOUT_BTLR     4               /* Bottom to top, left to right */
66 #define LAYOUT_TBLR     5               /* Top to bottom, left to right */
67 #define LAYOUT_BTRL     6               /* Bottom to top, right to left */
68 #define LAYOUT_TBRL     7               /* Top to bottom, right to left */
69
70 #define LAYOUT_NEGATEY  1               /* The bits for the layout */
71 #define LAYOUT_NEGATEX  2               /* definitions above... */
72 #define LAYOUT_VERTICAL 4
73
74 #define PROT_STANDARD   0               /* Adobe standard protocol */
75 #define PROT_BCP        1               /* Adobe BCP protocol */
76 #define PROT_TBCP       2               /* Adobe TBCP protocol */
77
78
79 /*
80  * Globals...
81  */
82
83 int             NumPages = 0;           /* Number of pages in file */
84 long            Pages[MAX_PAGES];       /* Offsets to each page */
85 const char      *PageRanges = NULL;     /* Range of pages selected */
86 const char      *PageSet = NULL;        /* All, Even, Odd pages */
87 int             Order = 0,              /* 0 = normal, 1 = reverse pages */
88                 Flip = 0,               /* Flip/mirror pages */
89                 NUp = 1,                /* Number of pages on each sheet (1, 2, 4) */
90                 Collate = 0,            /* Collate copies? */
91                 Copies = 1,             /* Number of copies */
92                 UseESPsp = 0,           /* Use ESPshowpage? */
93                 Border = BORDER_NONE,   /* Border around pages */
94                 Layout = LAYOUT_LRTB,   /* Layout of N-up pages */
95                 NormalLandscape = 0,    /* Normal rotation for landscape? */
96                 Protocol = PROT_STANDARD;
97                                         /* Transmission protocol to use */
98
99
100 /*
101  * Local functions...
102  */
103
104 static int      check_range(int page);
105 static void     copy_bytes(FILE *fp, size_t length);
106 static void     do_prolog(ppd_file_t *ppd);
107 static void     do_setup(ppd_file_t *ppd, int copies,  int collate,
108                          int slowcollate, float g, float b);
109 static void     end_nup(int number);
110 static void     include_feature(ppd_file_t *ppd, const char *line, FILE *out);
111 #define         is_first_page(p)        (NUp == 1 || (((p)+1) % NUp) == 1)
112 #define         is_last_page(p)         (NUp > 1 && (((p)+1) % NUp) == 0)
113 #define         is_not_last_page(p)     (NUp > 1 && ((p) % NUp) != 0)
114 static void     psbcp(ppd_file_t *ppd);
115 static char     *psgets(char *buf, size_t *bytes, FILE *fp);
116 static size_t   pswrite(const char *buf, size_t bytes, FILE *fp);
117 static void     start_nup(int number, int show_border);
118
119
120 /*
121  * 'main()' - Main entry...
122  */
123
124 int                             /* O - Exit status */
125 main(int  argc,                 /* I - Number of command-line arguments */
126      char *argv[])              /* I - Command-line arguments */
127 {
128   FILE          *fp;            /* Print file */
129   ppd_file_t    *ppd;           /* PPD file */
130   ppd_attr_t    *attr;          /* Attribute in PPD file */
131   int           num_options;    /* Number of print options */
132   cups_option_t *options;       /* Print options */
133   const char    *val;           /* Option value */
134   char          tempfile[255];  /* Temporary file name */
135   FILE          *temp;          /* Temporary file */
136   int           tempfd;         /* Temporary file descriptor */
137   int           number;         /* Page number */
138   int           slowcollate;    /* 1 if we need to collate manually */
139   int           sloworder;      /* 1 if we need to order manually */
140   int           slowduplex;     /* 1 if we need an even number of pages */
141   char          line[8192];     /* Line buffer */
142   size_t        len;            /* Length of line buffer */
143   float         g;              /* Gamma correction value */
144   float         b;              /* Brightness factor */
145   int           level;          /* Nesting level for embedded files */
146   int           nbytes,         /* Number of bytes read */
147                 tbytes;         /* Total bytes to read for binary data */
148   int           page;           /* Current page sequence number */
149   int           real_page;      /* "Real" page number in document */
150   int           page_count;     /* Page count for NUp */
151   int           basepage;       /* Base page number */
152   int           subpage;        /* Sub-page number */
153   int           copy;           /* Current copy */
154   int           saweof;         /* Did we see a %%EOF tag? */
155   int           sent_espsp,     /* Did we send the ESPshowpage commands? */
156                 sent_prolog,    /* Did we send the prolog commands? */
157                 sent_setup;     /* Did we send the setup commands? */
158
159
160  /*
161   * Make sure status messages are not buffered...
162   */
163
164   setbuf(stderr, NULL);
165
166  /*
167   * Check command-line...
168   */
169
170   if (argc < 6 || argc > 7)
171   {
172     fputs("ERROR: pstops job-id user title copies options [file]\n", stderr);
173     return (1);
174   }
175
176  /*
177   * If we have 7 arguments, print the file named on the command-line.
178   * Otherwise, send stdin instead...
179   */
180
181   if (argc == 6)
182     fp = stdin;
183   else
184   {
185    /*
186     * Try to open the print file...
187     */
188
189     if ((fp = fopen(argv[6], "rb")) == NULL)
190     {
191       fprintf(stderr, "ERROR: unable to open print file \"%s\" - %s\n",
192               argv[6], strerror(errno));
193       return (1);
194     }
195   }
196
197  /*
198   * Process command-line options and write the prolog...
199   */
200
201   g = 1.0;
202   b = 1.0;
203
204   Copies = atoi(argv[4]);
205
206   options     = NULL;
207   num_options = cupsParseOptions(argv[5], 0, &options);
208
209   ppd = SetCommonOptions(num_options, options, 1);
210
211   if (ppd && ppd->landscape > 0)
212     NormalLandscape = 1;
213
214   if ((val = cupsGetOption("page-ranges", num_options, options)) != NULL)
215     PageRanges = val;
216
217   if ((val = cupsGetOption("page-set", num_options, options)) != NULL)
218     PageSet = val;
219
220   if ((val = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
221   {
222    /*
223     * This IPP attribute is unnecessarily complicated...
224     *
225     *   single-document, separate-documents-collated-copies, and
226     *   single-document-new-sheet all require collated copies.
227     *
228     *   separate-documents-uncollated-copies allows for uncollated copies.
229     */
230
231     Collate = strcasecmp(val, "separate-documents-uncollated-copies") != 0;
232   }
233
234   if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
235       (!strcasecmp(val, "true") ||!strcasecmp(val, "on") ||
236        !strcasecmp(val, "yes")))
237     Collate = 1;
238
239   if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL &&
240       !strcasecmp(val, "Reverse"))
241     Order = 1;
242
243   if ((val = cupsGetOption("number-up", num_options, options)) != NULL)
244     NUp = atoi(val);
245
246   if ((val = cupsGetOption("page-border", num_options, options)) != NULL)
247   {
248     if (!strcasecmp(val, "none"))
249       Border = BORDER_NONE;
250     else if (!strcasecmp(val, "single"))
251       Border = BORDER_SINGLE;
252     else if (!strcasecmp(val, "single-thick"))
253       Border = BORDER_SINGLE2;
254     else if (!strcasecmp(val, "double"))
255       Border = BORDER_DOUBLE;
256     else if (!strcasecmp(val, "double-thick"))
257       Border = BORDER_DOUBLE2;
258   }
259
260   if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL)
261   {
262     if (!strcasecmp(val, "lrtb"))
263       Layout = LAYOUT_LRTB;
264     else if (!strcasecmp(val, "lrbt"))
265       Layout = LAYOUT_LRBT;
266     else if (!strcasecmp(val, "rltb"))
267       Layout = LAYOUT_RLTB;
268     else if (!strcasecmp(val, "rlbt"))
269       Layout = LAYOUT_RLBT;
270     else if (!strcasecmp(val, "tblr"))
271       Layout = LAYOUT_TBLR;
272     else if (!strcasecmp(val, "tbrl"))
273       Layout = LAYOUT_TBRL;
274     else if (!strcasecmp(val, "btlr"))
275       Layout = LAYOUT_BTLR;
276     else if (!strcasecmp(val, "btrl"))
277       Layout = LAYOUT_BTRL;
278   }
279
280   if ((val = cupsGetOption("gamma", num_options, options)) != NULL)
281     g = atoi(val) * 0.001f;
282
283   if ((val = cupsGetOption("brightness", num_options, options)) != NULL)
284     b = atoi(val) * 0.01f;
285
286   if ((val = cupsGetOption("mirror", num_options, options)) != NULL &&
287       (!strcasecmp(val, "true") ||!strcasecmp(val, "on") ||
288        !strcasecmp(val, "yes")))
289     Flip = 1;
290
291  /*
292   * See if we have to filter the fast or slow way...
293   */
294
295   if (ppd && ppd->manual_copies && Duplex && Copies > 1)
296   {
297    /*
298     * Force collated copies when printing a duplexed document to
299     * a non-PS printer that doesn't do hardware copy generation.
300     * Otherwise the copies will end up on the front/back side of
301     * each page.  Also, set the "slowduplex" option to make sure
302     * that we output an even number of pages...
303     */
304
305     Collate    = 1;
306     slowduplex = 1;
307   }
308   else
309     slowduplex = 0;
310
311   if (ppdFindOption(ppd, "Collate") == NULL && Collate && Copies > 1)
312     slowcollate = 1;
313   else
314     slowcollate = 0;
315
316   if (ppdFindOption(ppd, "OutputOrder") == NULL && Order)
317     sloworder = 1;
318   else
319     sloworder = 0;
320
321  /*
322   * If we need to filter slowly, then create a temporary file for page data...
323   *
324   * If the temp file can't be created, then we'll ignore the collating/output
325   * order options...
326   */
327
328   if (sloworder || slowcollate)
329   {
330     tempfd = cupsTempFd(tempfile, sizeof(tempfile));
331     if (tempfd < 0)
332     {
333       perror("ERROR: Unable to open temp file");
334       temp = NULL;
335     }
336     else
337       temp = fdopen(tempfd, "wb+");
338
339     if (temp == NULL)
340       slowcollate = sloworder = 0;
341   }
342   else
343     temp = NULL;
344
345  /*
346   * See if we should use a binary transmission protocol...
347   */
348
349   if ((attr = ppdFindAttr(ppd, "cupsProtocol", NULL)) != NULL &&
350       attr->value != NULL)
351   {
352     if (!strcasecmp(attr->value, "TBCP"))
353       Protocol = PROT_TBCP;
354     else if (!strcasecmp(attr->value, "BCP"))
355     {
356       Protocol = PROT_BCP;
357
358       psbcp(ppd);
359     }
360   }
361
362  /*
363   * Write any "exit server" options that have been selected...
364   */
365
366   ppdEmit(ppd, stdout, PPD_ORDER_EXIT);
367
368  /*
369   * Write any JCL commands that are needed to print PostScript code...
370   */
371
372   ppdEmitJCL(ppd, stdout, atoi(argv[1]), argv[2], argv[3]);
373
374  /*
375   * Read the first line to see if we have DSC comments...
376   */
377
378   len = sizeof(line);
379   if (psgets(line, &len, fp) == NULL)
380   {
381     fputs("ERROR: Empty print file!\n", stderr);
382     ppdClose(ppd);
383     return (1);
384   }
385
386  /*
387   * Handle leading PJL fun...
388   */
389
390   while (!strncmp(line, "\033%-12345X", 9) || !strncmp(line, "@PJL ", 5))
391   {
392    /*
393     * Yup, we have leading PJL fun, so skip it until we hit the line
394     * with "ENTER LANGUAGE"...
395     */
396
397     fputs("DEBUG: Skipping PJL header...\n", stderr);
398
399     while (strstr(line, "ENTER LANGUAGE") == NULL)
400     {
401       len = sizeof(line);
402       if (psgets(line, &len, fp) == NULL)
403         break;
404     }
405
406     len = sizeof(line);
407     if (psgets(line, &len, fp) == NULL)
408       break;
409   }
410
411  /*
412   * Switch to TBCP mode as needed...
413   */
414
415   if (Protocol == PROT_TBCP)
416     fputs("\001M", stdout);
417
418  /*
419   * Start sending the document with any commands needed...
420   */
421
422   fwrite(line, 1, len, stdout);
423
424   saweof      = 0;
425   sent_espsp  = 0;
426   sent_prolog = 0;
427   sent_setup  = 0;
428
429   if (Copies != 1 && (!Collate || !slowcollate))
430   {
431    /*
432     * Tell the document processor the copy and duplex options
433     * that are required...
434     */
435
436     printf("%%%%Requirements: numcopies(%d)%s%s\n", Copies,
437            Collate ? " collate" : "",
438            Duplex ? " duplex" : "");
439
440    /*
441     * Apple uses RBI comments for various non-PPD options...
442     */
443
444     printf("%%RBINumCopies: %d\n", Copies);
445   }
446   else
447   {
448    /*
449     * Tell the document processor the duplex option that is required...
450     */
451
452     if (Duplex)
453       puts("%%Requirements: duplex");
454
455    /*
456     * Apple uses RBI comments for various non-PPD options...
457     */
458
459     puts("%RBINumCopies: 1");
460   }
461
462  /*
463   * Figure out if we should use ESPshowpage or not...
464   */
465
466   val = cupsGetOption("page-label", num_options, options);
467
468   if (val != NULL || getenv("CLASSIFICATION") != NULL || NUp > 1 ||
469       Border || strstr(line, "EPS") != NULL)
470   {
471    /*
472     * Yes, use ESPshowpage...
473     */
474
475     UseESPsp = 1;
476   }
477
478   fprintf(stderr, "DEBUG: slowcollate=%d, slowduplex=%d, sloworder=%d\n",
479           slowcollate, slowduplex, sloworder);
480
481   if (!strncmp(line, "%!PS-Adobe-", 11) && !strstr(line, "EPSF"))
482   {
483    /*
484     * OK, we have DSC comments and this isn't an EPS file; read until we
485     * find a %%Page comment...
486     */
487
488     puts("%%Pages: (atend)");
489
490     level = 0;
491
492     while (!feof(fp))
493     {
494       len = sizeof(line);
495       if (psgets(line, &len, fp) == NULL)
496         break;
497
498       if (!strncmp(line, "%%", 2))
499         fprintf(stderr, "DEBUG: %d %s", level, line);
500       else if (line[0] != '%' && line[0] && !sent_espsp && UseESPsp)
501       {
502        /*
503         * Send ESPshowpage stuff...
504         */
505
506         sent_espsp = 1;
507
508         puts("userdict/ESPshowpage/showpage load put\n"
509              "userdict/showpage{}put");
510       }
511
512       if (!strncmp(line, "%%BeginDocument:", 16) ||
513           !strncmp(line, "%%BeginDocument ", 16) ||     /* Adobe Acrobat BUG */
514           !strncmp(line, "%ADO_BeginApplication", 21))
515       {
516         fputs(line, stdout);
517         level ++;
518       }
519       else if ((!strncmp(line, "%%EndDocument", 13) ||
520                 !strncmp(line, "%ADO_EndApplication", 19)) && level > 0)
521       {
522         fputs(line, stdout);
523         level --;
524       }
525       else if (!strncmp(line, "%cupsRotation:", 14) && level == 0)
526       {
527        /*
528         * Reset orientation of document?
529         */
530
531         int orient = (atoi(line + 14) / 90) & 3;
532
533         if (orient != Orientation)
534         {
535           Orientation = (4 - Orientation + orient) & 3;
536           UpdatePageVars();
537           Orientation = orient;
538         }
539       }
540       else if (!strncmp(line, "%%BeginProlog", 13) && level == 0)
541       {
542        /*
543         * Write the existing comment line, and then follow with patches
544         * and prolog commands...
545         */
546
547         fputs(line, stdout);
548
549         if (!sent_prolog)
550         {
551           sent_prolog = 1;
552           do_prolog(ppd);
553         }
554       }
555       else if (!strncmp(line, "%%BeginSetup", 12) && level == 0)
556       {
557        /*
558         * Write the existing comment line, and then follow with document
559         * setup commands...
560         */
561
562         fputs(line, stdout);
563
564         if (!sent_prolog)
565         {
566           sent_prolog = 1;
567           do_prolog(ppd);
568         }
569
570         if (!sent_setup)
571         {
572           sent_setup = 1;
573           do_setup(ppd, Copies, Collate, slowcollate, g, b);
574         }
575       }
576       else if (!strncmp(line, "%%Page:", 7) && level == 0)
577         break;
578       else if (!strncmp(line, "%%IncludeFeature:", 17) && level == 0 && NUp == 1)
579         include_feature(ppd, line, stdout);
580       else if (!strncmp(line, "%%BeginBinary:", 14) ||
581                (!strncmp(line, "%%BeginData:", 12) &&
582                 !strstr(line, "ASCII") && !strstr(line, "Hex")))
583       {
584        /*
585         * Copy binary data...
586         */
587
588         tbytes = atoi(strchr(line, ':') + 1);
589         fputs(line, stdout);
590
591         while (tbytes > 0)
592         {
593           if (tbytes > sizeof(line))
594             nbytes = fread(line, 1, sizeof(line), fp);
595           else
596             nbytes = fread(line, 1, tbytes, fp);
597
598           if (nbytes < 1)
599           {
600             perror("ERROR: Early end-of-file while reading binary data");
601             return (1);
602           }
603
604           pswrite(line, nbytes, stdout);
605           tbytes -= nbytes;
606         }
607       }
608       else if (strncmp(line, "%%Pages:", 8) != 0)
609         pswrite(line, len, stdout);
610     }
611
612    /*
613     * Make sure we have the prolog and setup commands written...
614     */
615
616     if (!sent_prolog)
617     {
618       puts("%%BeginProlog");
619
620       sent_prolog = 1;
621       do_prolog(ppd);
622
623       puts("%%EndProlog");
624     }
625
626     if (!sent_setup)
627     {
628       puts("%%BeginSetup");
629
630       sent_setup = 1;
631       do_setup(ppd, Copies, Collate, slowcollate, g, b);
632
633       puts("%%EndSetup");
634     }
635
636     if (!sent_espsp && UseESPsp)
637     {
638      /*
639       * Send ESPshowpage stuff...
640       */
641
642       sent_espsp = 1;
643
644       puts("userdict/ESPshowpage/showpage load put\n"
645            "userdict/showpage{}put");
646     }
647
648    /*
649     * Write the page and label prologs...
650     */
651
652     if (NUp == 2 || NUp == 6)
653     {
654      /*
655       * For 2- and 6-up output, rotate the labels to match the orientation
656       * of the pages...
657       */
658
659       if (Orientation & 1)
660         WriteLabelProlog(val, PageBottom, PageWidth - PageLength + PageTop,
661                          PageLength);
662       else
663         WriteLabelProlog(val, PageLeft, PageRight, PageLength);
664     }
665     else
666       WriteLabelProlog(val, PageBottom, PageTop, PageWidth);
667
668    /*
669     * Then read all of the pages, filtering as needed...
670     */
671
672     for (page = 1, real_page = 1;;)
673     {
674       if (!strncmp(line, "%%", 2))
675         fprintf(stderr, "DEBUG: %d %s", level, line);
676
677       if (!strncmp(line, "%%BeginDocument:", 16) ||
678           !strncmp(line, "%%BeginDocument ", 16))       /* Adobe Acrobat BUG */
679         level ++;
680       else if (!strncmp(line, "%%EndDocument", 13) && level > 0)
681         level --;
682       else if (!strcmp(line, "\004") && len == 1)
683         break;
684       else if (!strncmp(line, "%%EOF", 5) && level == 0)
685       {
686         fputs("DEBUG: Saw EOF!\n", stderr);
687         saweof = 1;
688         break;
689       }
690       else if (!strncmp(line, "%%Page:", 7) && level == 0)
691       {
692         if (!check_range(real_page))
693         {
694           while (!feof(fp))
695           {
696             len = sizeof(line);
697             if (psgets(line, &len, fp) == NULL)
698               break;
699
700             if (!strncmp(line, "%%", 2))
701               fprintf(stderr, "DEBUG: %d %s", level, line);
702
703             if (!strncmp(line, "%%BeginDocument:", 16) ||
704                 !strncmp(line, "%%BeginDocument ", 16)) /* Adobe Acrobat BUG */
705               level ++;
706             else if (!strncmp(line, "%%EndDocument", 13) && level > 0)
707               level --;
708             else if (!strncmp(line, "%%Page:", 7) && level == 0)
709             {
710               real_page ++;
711               break;
712             }
713             else if (!strncmp(line, "%%BeginBinary:", 14) ||
714                      (!strncmp(line, "%%BeginData:", 12) &&
715                       !strstr(line, "ASCII") && !strstr(line, "Hex")))
716             {
717              /*
718               * Skip binary data...
719               */
720
721               tbytes = atoi(strchr(line, ':') + 1);
722
723               while (tbytes > 0)
724               {
725                 if (tbytes > sizeof(line))
726                   nbytes = fread(line, 1, sizeof(line), fp);
727                 else
728                   nbytes = fread(line, 1, tbytes, fp);
729
730                 if (nbytes < 1)
731                 {
732                   perror("ERROR: Early end-of-file while reading binary data");
733                   return (1);
734                 }
735
736                 tbytes -= nbytes;
737               }
738             }
739           }
740
741           continue;
742         }
743
744         if (!sloworder && NumPages > 0)
745           end_nup(NumPages - 1);
746
747         if (slowcollate || sloworder)
748           Pages[NumPages] = ftell(temp);
749
750         if (!sloworder)
751         {
752           if (is_first_page(NumPages))
753           {
754             if (ppd == NULL || ppd->num_filters == 0)
755               fprintf(stderr, "PAGE: %d %d\n", page, slowcollate ? 1 : Copies);
756
757             printf("%%%%Page: %d %d\n", page, page);
758             page ++;
759             ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
760           }
761
762           start_nup(NumPages, 1);
763         }
764
765         NumPages ++;
766         real_page ++;
767       }
768       else if (!strncmp(line, "%%BeginBinary:", 14) ||
769                (!strncmp(line, "%%BeginData:", 12) &&
770                 !strstr(line, "ASCII") && !strstr(line, "Hex")))
771       {
772        /*
773         * Copy binary data...
774         */
775
776         tbytes = atoi(strchr(line, ':') + 1);
777
778         if (!sloworder)
779           fputs(line, stdout);
780         if (slowcollate || sloworder)
781           fputs(line, temp);
782
783         while (tbytes > 0)
784         {
785           if (tbytes > sizeof(line))
786             nbytes = fread(line, 1, sizeof(line), fp);
787           else
788             nbytes = fread(line, 1, tbytes, fp);
789
790           if (nbytes < 1)
791           {
792             perror("ERROR: Early end-of-file while reading binary data");
793             return (1);
794           }
795
796           if (!sloworder)
797             pswrite(line, nbytes, stdout);
798
799           if (slowcollate || sloworder)
800             fwrite(line, 1, nbytes, temp);
801
802           tbytes -= nbytes;
803         }
804       }
805       else if (!strncmp(line, "%%IncludeFeature:", 17))
806       {
807        /*
808         * Embed printer commands as needed...
809         */
810
811         if (level == 0 && NUp == 1)
812         {
813           include_feature(ppd, line, stdout);
814
815           if (slowcollate || sloworder)
816             include_feature(ppd, line, temp);
817         }
818       }
819       else if (!strncmp(line, "%%BeginFeature:", 15) && NUp > 1)
820       {
821        /*
822         * Strip page options for N-up > 1...
823         */
824
825         do
826         {
827           len = sizeof(line);
828           if (psgets(line, &len, fp) == NULL)
829             break;
830         }
831         while (strncmp(line, "%%EndFeature", 12));
832       }
833       else if (!strncmp(line, "%%Trailer", 9) && level == 0)
834       {
835         fputs("DEBUG: Saw Trailer!\n", stderr);
836         break;
837       }
838       else
839       {
840         if (!sloworder)
841           pswrite(line, len, stdout);
842
843         if (slowcollate || sloworder)
844           fwrite(line, 1, len, temp);
845       }
846
847       len = sizeof(line);
848       if (psgets(line, &len, fp) == NULL)
849         break;
850     }
851
852     if (!sloworder)
853     {
854       end_nup(NumPages - 1);
855
856       if (is_not_last_page(NumPages))
857       {
858         start_nup(NUp - 1, 0);
859         end_nup(NUp - 1);
860       }
861
862       if (Duplex && !(page & 1))
863       {
864        /*
865         * Make sure we have an even number of pages...
866         */
867
868         if (ppd == NULL || ppd->num_filters == 0)
869           fprintf(stderr, "PAGE: %d %d\n", page, slowcollate ? 1 : Copies);
870
871         printf("%%%%Page: %d %d\n", page, page);
872         page ++;
873         ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
874
875         start_nup(NUp - 1, 0);
876         puts("showpage");
877         end_nup(NUp - 1);
878       }
879     }
880
881     if (slowcollate || sloworder)
882     {
883       Pages[NumPages] = ftell(temp);
884
885       if (!sloworder)
886       {
887         while (Copies > 1)
888         {
889           rewind(temp);
890
891           for (number = 0; number < NumPages; number ++)
892           {
893             if (is_first_page(number))
894             {
895               if (ppd == NULL || ppd->num_filters == 0)
896                 fprintf(stderr, "PAGE: %d 1\n", page);
897
898               printf("%%%%Page: %d %d\n", page, page);
899               page ++;
900               ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
901             }
902
903             start_nup(number, 1);
904             copy_bytes(temp, Pages[number + 1] - Pages[number]);
905             end_nup(number);
906           }
907
908           if (is_not_last_page(NumPages))
909           {
910             start_nup(NUp - 1, 0);
911             end_nup(NUp - 1);
912           }
913
914           if (Duplex && !(page & 1))
915           {
916            /*
917             * Make sure we have an even number of pages...
918             */
919
920             if (ppd == NULL || ppd->num_filters == 0)
921               fprintf(stderr, "PAGE: %d 1\n", page);
922
923             printf("%%%%Page: %d %d\n", page, page);
924             page ++;
925             ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
926
927             start_nup(NUp - 1, 0);
928             puts("showpage");
929             end_nup(NUp - 1);
930           }
931
932           Copies --;
933         }
934       }
935       else
936       {
937         page_count = (NumPages + NUp - 1) / NUp;
938         copy       = 0;
939
940         fprintf(stderr, "DEBUG: page_count=%d\n", page_count);
941
942         do
943         {
944           if (Duplex && (page_count & 1))
945             basepage = page_count;
946           else
947             basepage = page_count - 1;
948
949           for (; basepage >= 0; basepage --)
950           {
951             if (ppd == NULL || ppd->num_filters == 0)
952               fprintf(stderr, "PAGE: %d %d\n", page,
953                       slowcollate ? 1 : Copies);
954
955             printf("%%%%Page: %d %d\n", page, page);
956             page ++;
957
958             ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
959
960             if (basepage >= page_count)
961             {
962               start_nup(NUp - 1, 0);
963               puts("showpage");
964               end_nup(NUp - 1);
965             }
966             else
967             {
968               for (subpage = 0, number = basepage * NUp;
969                    subpage < NUp && number < NumPages;
970                    subpage ++, number ++)
971               {
972                 start_nup(number, 1);
973                 fseek(temp, Pages[number], SEEK_SET);
974                 copy_bytes(temp, Pages[number + 1] - Pages[number]);
975                 end_nup(number);
976               }
977
978               if (is_not_last_page(number))
979               {
980                 start_nup(NUp - 1, 0);
981                 end_nup(NUp - 1);
982               }
983             }
984           }
985
986           copy ++;
987         }
988         while (copy < Copies && slowcollate);
989       }
990     }
991
992    /*
993     * Copy the trailer, if any...
994     */
995
996     puts("%%Trailer");
997     printf("%%%%Pages: %d\n", page - 1);
998
999     if (UseESPsp)
1000       puts("userdict/showpage/ESPshowpage load put\n");
1001
1002     while (!feof(fp))
1003     {
1004       len = sizeof(line);
1005       if (psgets(line, &len, fp) == NULL)
1006         break;
1007
1008       if (!(!strcmp(line, "\004") && len == 1) &&
1009           strncmp(line, "%%Pages:", 8) != 0)
1010         pswrite(line, len, stdout);
1011
1012       if (!strncmp(line, "%%EOF", 5))
1013       {
1014         fputs("DEBUG: Saw EOF!\n", stderr);
1015         saweof = 1;
1016         break;
1017       }
1018     }
1019   }
1020   else
1021   {
1022    /*
1023     * No DSC comments - write any page commands and then the rest of the file...
1024     */
1025
1026     if (slowcollate && Copies > 1)
1027       printf("%%%%Pages: %d\n", Copies);
1028     else
1029       puts("%%Pages: 1");
1030
1031     if (UseESPsp)
1032       puts("userdict/ESPshowpage/showpage load put\n"
1033            "userdict/showpage{}put");
1034
1035     puts("%%BeginProlog");
1036     WriteLabelProlog(val, PageBottom, PageTop, PageWidth);
1037     do_prolog(ppd);
1038     puts("%%EndProlog");
1039
1040     puts("%%BeginSetup");
1041     do_setup(ppd, Copies, Collate, slowcollate, g, b);
1042     puts("%%EndSetup");
1043
1044     if (ppd == NULL || ppd->num_filters == 0)
1045       fprintf(stderr, "PAGE: 1 %d\n", slowcollate ? 1 : Copies);
1046
1047     ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
1048
1049     saweof = 1;
1050
1051     while ((nbytes = fread(line, 1, sizeof(line), fp)) > 0)
1052     {
1053       pswrite(line, nbytes, stdout);
1054
1055       if (slowcollate)
1056         fwrite(line, 1, nbytes, temp);
1057     }
1058
1059     if (UseESPsp)
1060     {
1061       WriteLabels(Orientation);
1062       puts("ESPshowpage");
1063     }
1064
1065     if (slowcollate)
1066     {
1067       while (Copies > 1)
1068       {
1069         if (ppd == NULL || ppd->num_filters == 0)
1070           fputs("PAGE: 1 1\n", stderr);
1071
1072         ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
1073         rewind(temp);
1074         copy_bytes(temp, 0);
1075         Copies --;
1076
1077         if (UseESPsp)
1078         {
1079           WriteLabels(Orientation);
1080           puts("ESPshowpage");
1081         }
1082       }
1083     }
1084   }
1085
1086  /*
1087   * Send %%EOF if needed...
1088   */
1089
1090   if (!saweof)
1091     puts("%%EOF");
1092
1093  /*
1094   * End the job with the appropriate JCL command or CTRL-D otherwise.
1095   */
1096
1097   ppdEmitJCLEnd(ppd, stdout);
1098
1099  /*
1100   * Close files and remove the temporary file if needed...
1101   */
1102
1103   if (slowcollate || sloworder)
1104   {
1105     fclose(temp);
1106     unlink(tempfile);
1107   }
1108
1109   ppdClose(ppd);
1110
1111   if (fp != stdin)
1112     fclose(fp);
1113
1114   return (0);
1115 }
1116
1117
1118 /*
1119  * 'check_range()' - Check to see if the current page is selected for
1120  *                   printing.
1121  */
1122
1123 static int              /* O - 1 if selected, 0 otherwise */
1124 check_range(int page)   /* I - Page number */
1125 {
1126   const char    *range;         /* Pointer into range string */
1127   int           lower, upper;   /* Lower and upper page numbers */
1128
1129
1130   if (PageSet != NULL)
1131   {
1132    /*
1133     * See if we only print even or odd pages...
1134     */
1135
1136     if (!strcasecmp(PageSet, "even") && ((page - 1) % (NUp << 1)) <  NUp)
1137       return (0);
1138     if (!strcasecmp(PageSet, "odd") && ((page - 1) % (NUp << 1)) >= NUp)
1139       return (0);
1140   }
1141
1142   if (PageRanges == NULL)
1143     return (1);         /* No range, print all pages... */
1144
1145   for (range = PageRanges; *range != '\0';)
1146   {
1147     if (*range == '-')
1148     {
1149       lower = 1;
1150       range ++;
1151       upper = strtol(range, (char **)&range, 10);
1152     }
1153     else
1154     {
1155       lower = strtol(range, (char **)&range, 10);
1156
1157       if (*range == '-')
1158       {
1159         range ++;
1160         if (!isdigit(*range & 255))
1161           upper = 65535;
1162         else
1163           upper = strtol(range, (char **)&range, 10);
1164       }
1165       else
1166         upper = lower;
1167     }
1168
1169     if (page >= lower && page <= upper)
1170       return (1);
1171
1172     if (*range == ',')
1173       range ++;
1174     else
1175       break;
1176   }
1177
1178   return (0);
1179 }
1180
1181
1182 /*
1183  * 'copy_bytes()' - Copy bytes from the input file to stdout...
1184  */
1185
1186 static void
1187 copy_bytes(FILE   *fp,          /* I - File to read from */
1188            size_t length)       /* I - Length of page data */
1189 {
1190   char          buffer[8192];   /* Data buffer */
1191   size_t        nbytes,         /* Number of bytes read */
1192                 nleft;          /* Number of bytes left/remaining */
1193
1194
1195   nleft = length;
1196
1197   while (nleft > 0 || length == 0)
1198   {
1199     if (nleft > sizeof(buffer) || length == 0)
1200       nbytes = sizeof(buffer);
1201     else
1202       nbytes = nleft;
1203
1204     if ((nbytes = fread(buffer, 1, nbytes, fp)) < 1)
1205       return;
1206
1207     nleft -= nbytes;
1208
1209     pswrite(buffer, nbytes, stdout);
1210   }
1211 }
1212
1213
1214 /*
1215  * 'do_prolog()' - Send the necessary document prolog commands...
1216  */
1217
1218 static void
1219 do_prolog(ppd_file_t *ppd)              /* I - PPD file */
1220 {
1221  /*
1222   * Send the document prolog commands...
1223   */
1224
1225   if (ppd != NULL && ppd->patches != NULL)
1226   {
1227     puts("%%BeginFeature: *JobPatchFile 1");
1228     puts(ppd->patches);
1229     puts("%%EndFeature");
1230   }
1231
1232   ppdEmit(ppd, stdout, PPD_ORDER_PROLOG);
1233 }
1234
1235
1236 /*
1237  * 'do_setup()' - Send the necessary document setup commands...
1238  */
1239
1240 static void
1241 do_setup(ppd_file_t *ppd,               /* I - PPD file */
1242          int        copies,             /* I - Number of copies */
1243          int        collate,            /* I - Collate output? */
1244          int        slowcollate,        /* I - Slow collate */
1245          float      g,                  /* I - Gamma value */
1246          float      b)                  /* I - Brightness value */
1247 {
1248  /*
1249   * Send all the printer-specific setup commands...
1250   */
1251
1252   ppdEmit(ppd, stdout, PPD_ORDER_DOCUMENT);
1253   ppdEmit(ppd, stdout, PPD_ORDER_ANY);
1254
1255  /*
1256   * Set the number of copies for the job...
1257   */
1258
1259   if (copies != 1 && (!collate || !slowcollate))
1260   {
1261     printf("%%RBIBeginNonPPDFeature: *NumCopies %d\n", copies);
1262     printf("%d/languagelevel where{pop languagelevel 2 ge}{false}ifelse{1 dict begin"
1263             "/NumCopies exch def currentdict end " 
1264             "setpagedevice}{userdict/#copies 3 -1 roll put}ifelse\n", copies);
1265     printf("%%RBIEndNonPPDFeature\n");
1266   }
1267
1268  /*
1269   * If we are doing N-up printing, disable setpagedevice...
1270   */
1271
1272   if (NUp > 1)
1273     puts("userdict/setpagedevice{pop}bind put");
1274
1275  /*
1276   * Changes to the transfer function must be made AFTER any
1277   * setpagedevice code...
1278   */
1279
1280   if (g != 1.0 || b != 1.0)
1281     printf("{ neg 1 add dup 0 lt { pop 1 } { %.3f exp neg 1 add } "
1282            "ifelse %.3f mul } bind settransfer\n", g, b);
1283
1284  /*
1285   * Make sure we have rectclip and rectstroke procedures of some sort...
1286   */
1287
1288   WriteCommon();
1289 }
1290
1291
1292 /*
1293  * 'end_nup()' - End processing for N-up printing...
1294  */
1295
1296 static void
1297 end_nup(int number)     /* I - Page number */
1298 {
1299   puts("");
1300
1301   if (Flip || Orientation || NUp > 1)
1302     puts("userdict /ESPsave get restore");
1303
1304   switch (NUp)
1305   {
1306     case 1 :
1307         if (UseESPsp)
1308         {
1309           WriteLabels(Orientation);
1310           puts("ESPshowpage");
1311         }
1312         break;
1313
1314     case 2 :
1315     case 6 :
1316         if (is_last_page(number) && UseESPsp)
1317         {
1318           if (Orientation & 1)
1319           {
1320            /*
1321             * Rotate the labels back to portrait...
1322             */
1323
1324             WriteLabels(Orientation - 1);
1325           }
1326           else if (Orientation == 0)
1327           {
1328            /*
1329             * Rotate the labels to landscape...
1330             */
1331
1332             WriteLabels(NormalLandscape ? 1 : 3);
1333           }
1334           else
1335           {
1336            /*
1337             * Rotate the labels to landscape...
1338             */
1339
1340             WriteLabels(NormalLandscape ? 3 : 1);
1341           }
1342
1343           puts("ESPshowpage");
1344         }
1345         break;
1346
1347     default :
1348         if (is_last_page(number) && UseESPsp)
1349         {
1350           WriteLabels(Orientation);
1351           puts("ESPshowpage");
1352         }
1353         break;
1354   }
1355
1356   fflush(stdout);
1357 }
1358
1359
1360 /*
1361  * 'include_feature()' - Include a printer option/feature command.
1362  */
1363
1364 static void
1365 include_feature(ppd_file_t *ppd,        /* I - PPD file */
1366                 const char *line,       /* I - DSC line */
1367                 FILE       *out)        /* I - Output file */
1368 {
1369   char          name[255],              /* Option name */
1370                 value[255];             /* Option value */
1371   ppd_option_t  *option;                /* Option in file */
1372   ppd_choice_t  *choice;                /* Choice */
1373
1374
1375  /*
1376   * Get the "%%IncludeFeature: *Keyword OptionKeyword" values...
1377   */
1378
1379   if (sscanf(line + 17, "%254s%254s", name, value) != 2)
1380   {
1381     fprintf(stderr, "ERROR: Bad line: \"%s\"!\n", line);
1382     return;
1383   }
1384
1385  /*
1386   * Find the option and choice...
1387   */
1388
1389   if ((option = ppdFindOption(ppd, name + 1)) == NULL)
1390   {
1391     fprintf(stderr, "WARNING: Unknown option \"%s\"!\n", name + 1);
1392     return;
1393   }
1394
1395   if (option->section == PPD_ORDER_EXIT ||
1396       option->section == PPD_ORDER_JCL)
1397   {
1398     fprintf(stderr, "WARNING: Option \"%s\" cannot be included via IncludeFeature!\n",
1399             name + 1);
1400     return;
1401   }
1402
1403   if ((choice = ppdFindChoice(option, value)) == NULL)
1404   {
1405     fprintf(stderr, "WARNING: Unknown choice \"%s\" for option \"%s\"!\n",
1406             value, name + 1);
1407     return;
1408   }
1409
1410  /*
1411   * Emit the option...
1412   */
1413
1414   fputs("[{\n", out);
1415   fprintf(out, "%%%%BeginFeature: %s %s\n", name, value);
1416   if (choice->code && choice->code[0])
1417   {
1418     fputs(choice->code, out);
1419
1420     if (choice->code[strlen(choice->code) - 1] != '\n')
1421       putc('\n', out);
1422   }
1423   fputs("%%EndFeature\n", out);
1424   fputs("} stopped cleartomark\n", out);
1425 }
1426
1427
1428 /*
1429  * 'psbcp()' - Enable the binary communications protocol on the printer.
1430  */
1431
1432 static void
1433 psbcp(ppd_file_t *ppd)                  /* I - PPD file */
1434 {
1435   if (ppd->jcl_begin)
1436     fputs(ppd->jcl_begin, stdout);
1437   if (ppd->jcl_ps)
1438     fputs(ppd->jcl_ps, stdout);
1439
1440   if (ppd->language_level == 1)
1441   {
1442    /*
1443     * Use setsoftwareiomode for BCP mode...
1444     */
1445
1446     fputs("%!PS-Adobe-3.0 ExitServer\n", stdout);
1447     fputs("%%Title: (BCP - Level 1)\n", stdout);
1448     fputs("%%EndComments\n", stdout);
1449     fputs("%%BeginExitServer: 0\n", stdout);
1450     fputs("serverdict begin 0 exitserver\n", stdout);
1451     fputs("%%EndExitServer\n", stdout);
1452     fputs("statusdict begin\n", stdout);
1453     fputs("/setsoftwareiomode known {100 setsoftwareiomode}\n", stdout);
1454     fputs("end\n", stdout);
1455     fputs("%EOF\n", stdout);
1456   }
1457   else
1458   {
1459    /*
1460     * Use setdevparams for BCP mode...
1461     */
1462
1463     fputs("%!PS-Adobe-3.0\n", stdout);
1464     fputs("%%Title: (BCP - Level 2)\n", stdout);
1465     fputs("%%EndComments\n", stdout);
1466     fputs("currentsysparams\n", stdout);
1467     fputs("/CurInputDevice 2 copy known {\n", stdout);
1468     fputs("get\n", stdout);
1469     fputs("<</Protocol /Binary>> setdevparams\n", stdout);
1470     fputs("}{\n", stdout);
1471     fputs("pop pop\n", stdout);
1472     fputs("} ifelse\n", stdout);
1473     fputs("%EOF\n", stdout);
1474   }
1475
1476   if (ppd->jcl_end)
1477     fputs(ppd->jcl_end, stdout);
1478   else if (ppd->num_filters == 0)
1479     putchar(0x04);
1480 }
1481
1482
1483 /*
1484  * 'psgets()' - Get a line from a file.
1485  *
1486  * Note:
1487  *
1488  *   This function differs from the gets() function in that it
1489  *   handles any combination of CR, LF, or CR LF to end input
1490  *   lines.
1491  */
1492
1493 static char *                           /* O  - String or NULL if EOF */
1494 psgets(char   *buf,                     /* I  - Buffer to read into */
1495        size_t *bytes,                   /* IO - Length of buffer */
1496        FILE   *fp)                      /* I  - File to read from */
1497 {
1498   char          *bufptr;                /* Pointer into buffer */
1499   int           ch;                     /* Character from file */
1500   size_t        len;                    /* Max length of string */
1501
1502
1503   len    = *bytes - 1;
1504   bufptr = buf;
1505   ch     = EOF;
1506
1507   while ((bufptr - buf) < len)
1508   {
1509     if ((ch = getc(fp)) == EOF)
1510       break;
1511
1512     if (ch == '\r')
1513     {
1514      /*
1515       * Got a CR; see if there is a LF as well...
1516       */
1517
1518       ch = getc(fp);
1519
1520       if (ch != EOF && ch != '\n')
1521       {
1522         ungetc(ch, fp); /* Nope, save it for later... */
1523         ch = '\r';
1524       }
1525       else
1526         *bufptr++ = '\r';
1527       break;
1528     }
1529     else if (ch == '\n')
1530       break;
1531     else
1532       *bufptr++ = ch;
1533   }
1534
1535  /*
1536   * Add a trailing newline if it is there...
1537   */
1538
1539   if (ch == '\n' || ch == '\r')
1540   {
1541     if ((bufptr - buf) < len)
1542       *bufptr++ = ch;
1543     else
1544       ungetc(ch, fp);
1545   }
1546
1547  /*
1548   * Nul-terminate the string and return it (or NULL for EOF).
1549   */
1550
1551   *bufptr = '\0';
1552   *bytes  = bufptr - buf;
1553
1554   if (ch == EOF && bufptr == buf)
1555     return (NULL);
1556   else
1557     return (buf);
1558 }
1559
1560
1561 /*
1562  * 'pswrite()' - Write data from a file.
1563  */
1564
1565 static size_t                           /* O - Number of bytes written */
1566 pswrite(const char *buf,                /* I - Buffer to write */
1567         size_t     bytes,               /* I - Bytes to write */
1568         FILE       *fp)                 /* I - File to write to */
1569 {
1570   size_t        count;                  /* Remaining bytes */
1571
1572
1573   switch (Protocol)
1574   {
1575     case PROT_STANDARD :
1576         return (fwrite(buf, 1, bytes, fp));
1577
1578     case PROT_BCP :
1579         for (count = bytes; count > 0; count --, buf ++)
1580           switch (*buf)
1581           {
1582             case 0x01 : /* CTRL-A */
1583             case 0x03 : /* CTRL-C */
1584             case 0x04 : /* CTRL-D */
1585             case 0x05 : /* CTRL-E */
1586             case 0x11 : /* CTRL-Q */
1587             case 0x13 : /* CTRL-S */
1588             case 0x14 : /* CTRL-T */
1589             case 0x1c : /* CTRL-\ */
1590                 putchar(0x01);
1591                 putchar(*buf ^ 0x40);
1592                 break;
1593
1594             default :
1595                 putchar(*buf);
1596                 break;
1597           }
1598         return (bytes);
1599
1600     case PROT_TBCP :
1601         for (count = bytes; count > 0; count --, buf ++)
1602           switch (*buf)
1603           {
1604             case 0x01 : /* CTRL-A */
1605             case 0x03 : /* CTRL-C */
1606             case 0x04 : /* CTRL-D */
1607             case 0x05 : /* CTRL-E */
1608             case 0x11 : /* CTRL-Q */
1609             case 0x13 : /* CTRL-S */
1610             case 0x14 : /* CTRL-T */
1611             case 0x1b : /* CTRL-[ (aka ESC) */
1612             case 0x1c : /* CTRL-\ */
1613                 putchar(0x01);
1614                 putchar(*buf ^ 0x40);
1615                 break;
1616
1617             default :
1618                 putchar(*buf);
1619                 break;
1620           }
1621         return (bytes);
1622   }
1623
1624   return (fwrite(buf, 1, bytes, fp));
1625 }
1626
1627
1628 /*
1629  * 'start_nup()' - Start processing for N-up printing...
1630  */
1631
1632 static void
1633 start_nup(int number,                   /* I - Page number */
1634           int show_border)              /* I - Show the page border? */
1635 {
1636   int   pos;                            /* Position on page */
1637   int   x, y;                           /* Relative position of subpage */
1638   float w, l,                           /* Width and length of subpage */
1639         tx, ty;                         /* Translation values for subpage */
1640   float pw, pl;                         /* Printable width and length of full page */
1641
1642
1643   if (Flip || Orientation || NUp > 1)
1644     puts("userdict/ESPsave save put");
1645
1646   if (Flip)
1647     printf("%.1f 0.0 translate -1 1 scale\n", PageWidth);
1648
1649   pos = number % NUp;
1650   pw  = PageRight - PageLeft;
1651   pl  = PageTop - PageBottom;
1652
1653   fprintf(stderr, "DEBUG: pw = %.1f, pl = %.1f\n", pw, pl);
1654   fprintf(stderr, "DEBUG: PageLeft = %.1f, PageRight = %.1f\n", PageLeft, PageRight);
1655   fprintf(stderr, "DEBUG: PageTop = %.1f, PageBottom = %.1f\n", PageTop, PageBottom);
1656   fprintf(stderr, "DEBUG: PageWidth = %.1f, PageLength = %.1f\n", PageWidth, PageLength);
1657
1658   switch (Orientation)
1659   {
1660     case 1 : /* Landscape */
1661         printf("%.1f 0.0 translate 90 rotate\n", PageLength);
1662         break;
1663     case 2 : /* Reverse Portrait */
1664         printf("%.1f %.1f translate 180 rotate\n", PageWidth, PageLength);
1665         break;
1666     case 3 : /* Reverse Landscape */
1667         printf("0.0 %.1f translate -90 rotate\n", PageWidth);
1668         break;
1669   }
1670
1671   if (Duplex && NUp > 1 && ((number / NUp) & 1))
1672     printf("%.1f %.1f translate\n", PageWidth - PageRight, PageBottom);
1673   else if (NUp > 1)
1674     printf("%.1f %.1f translate\n", PageLeft, PageBottom);
1675
1676   switch (NUp)
1677   {
1678     default :
1679         w = PageWidth;
1680         l = PageLength;
1681         break;
1682
1683     case 2 :
1684         if (Orientation & 1)
1685         {
1686           x = pos & 1;
1687
1688           if (Layout & LAYOUT_NEGATEY)
1689             x = 1 - x;
1690
1691           w = pl;
1692           l = w * PageLength / PageWidth;
1693
1694           if (l > (pw * 0.5))
1695           {
1696             l = pw * 0.5;
1697             w = l * PageWidth / PageLength;
1698           }
1699
1700           tx = 0.5 * (pw * 0.5 - l);
1701           ty = 0.5 * (pl - w);
1702
1703           if (NormalLandscape)
1704             printf("0.0 %.1f translate -90 rotate\n", pl);
1705           else
1706             printf("%.1f 0.0 translate 90 rotate\n", pw);
1707
1708           printf("%.1f %.1f translate %.3f %.3f scale\n",
1709                  ty, tx + l * x, w / PageWidth, l / PageLength);
1710         }
1711         else
1712         {
1713           x = pos & 1;
1714
1715           if (Layout & LAYOUT_NEGATEX)
1716             x = 1 - x;
1717
1718           l = pw;
1719           w = l * PageWidth / PageLength;
1720
1721           if (w > (pl * 0.5))
1722           {
1723             w = pl * 0.5;
1724             l = w * PageLength / PageWidth;
1725           }
1726
1727           tx = 0.5 * (pl * 0.5 - w);
1728           ty = 0.5 * (pw - l);
1729
1730           if (NormalLandscape)
1731             printf("%.1f 0.0 translate 90 rotate\n", pw);
1732           else
1733             printf("0.0 %.1f translate -90 rotate\n", pl);
1734
1735           printf("%.1f %.1f translate %.3f %.3f scale\n",
1736                  tx + w * x, ty, w / PageWidth, l / PageLength);
1737         }
1738         break;
1739
1740     case 4 :
1741         if (Layout & LAYOUT_VERTICAL)
1742         {
1743           x = (pos / 2) & 1;
1744           y = pos & 1;
1745         }
1746         else
1747         {
1748           x = pos & 1;
1749           y = (pos / 2) & 1;
1750         }
1751
1752         if (Layout & LAYOUT_NEGATEX)
1753           x = 1 - x;
1754
1755         if (Layout & LAYOUT_NEGATEY)
1756           y = 1 - y;
1757
1758         w = pw * 0.5;
1759         l = w * PageLength / PageWidth;
1760
1761         if (l > (pl * 0.5))
1762         {
1763           l = pl * 0.5;
1764           w = l * PageWidth / PageLength;
1765         }
1766
1767         tx = 0.5 * (pw * 0.5 - w);
1768         ty = 0.5 * (pl * 0.5 - l);
1769
1770         printf("%.1f %.1f translate %.3f %.3f scale\n", tx + x * w, ty + y * l,
1771                w / PageWidth, l / PageLength);
1772         break;
1773
1774     case 6 :
1775         if (Orientation & 1)
1776         {
1777           if (Layout & LAYOUT_VERTICAL)
1778           {
1779             x = pos / 3;
1780             y = pos % 3;
1781
1782             if (Layout & LAYOUT_NEGATEX)
1783               x = 1 - x;
1784
1785             if (Layout & LAYOUT_NEGATEY)
1786               y = 2 - y;
1787           }
1788           else
1789           {
1790             x = pos & 1;
1791             y = pos / 2;
1792
1793             if (Layout & LAYOUT_NEGATEX)
1794               x = 1 - x;
1795
1796             if (Layout & LAYOUT_NEGATEY)
1797               y = 2 - y;
1798           }
1799
1800           w = pl * 0.5;
1801           l = w * PageLength / PageWidth;
1802
1803           if (l > (pw * 0.333))
1804           {
1805             l = pw * 0.333;
1806             w = l * PageWidth / PageLength;
1807           }
1808
1809           tx = 0.5 * (pl - 2 * w);
1810           ty = 0.5 * (pw - 3 * l);
1811
1812           if (NormalLandscape)
1813             printf("0.0 %.1f translate -90 rotate\n", pl);
1814           else
1815             printf("%.1f 0.0 translate 90 rotate\n", pw);
1816
1817           printf("%.1f %.1f translate %.3f %.3f scale\n",
1818                  tx + x * w, ty + y * l, w / PageWidth, l / PageLength);
1819         }
1820         else
1821         {
1822           if (Layout & LAYOUT_VERTICAL)
1823           {
1824             x = pos / 2;
1825             y = pos & 1;
1826
1827             if (Layout & LAYOUT_NEGATEX)
1828               x = 2 - x;
1829
1830             if (Layout & LAYOUT_NEGATEY)
1831               y = 1 - y;
1832           }
1833           else
1834           {
1835             x = pos % 3;
1836             y = pos / 3;
1837
1838             if (Layout & LAYOUT_NEGATEX)
1839               x = 2 - x;
1840
1841             if (Layout & LAYOUT_NEGATEY)
1842               y = 1 - y;
1843           }
1844
1845           l = pw * 0.5;
1846           w = l * PageWidth / PageLength;
1847
1848           if (w > (pl * 0.333))
1849           {
1850             w = pl * 0.333;
1851             l = w * PageLength / PageWidth;
1852           }
1853
1854           tx = 0.5 * (pl - 3 * w);
1855           ty = 0.5 * (pw - 2 * l);
1856
1857           if (NormalLandscape)
1858             printf("%.1f 0.0 translate 90 rotate\n", pw);
1859           else
1860             printf("0.0 %.1f translate -90 rotate\n", pl);
1861
1862           printf("%.1f %.1f translate %.3f %.3f scale\n",
1863                  tx + w * x, ty + l * y, w / PageWidth, l / PageLength);
1864         }
1865         break;
1866
1867     case 9 :
1868         if (Layout & LAYOUT_VERTICAL)
1869         {
1870           x = (pos / 3) % 3;
1871           y = pos % 3;
1872         }
1873         else
1874         {
1875           x = pos % 3;
1876           y = (pos / 3) % 3;
1877         }
1878
1879         if (Layout & LAYOUT_NEGATEX)
1880           x = 2 - x;
1881
1882         if (Layout & LAYOUT_NEGATEY)
1883           y = 2 - y;
1884
1885         w = pw * 0.333;
1886         l = w * PageLength / PageWidth;
1887
1888         if (l > (pl * 0.333))
1889         {
1890           l = pl * 0.333;
1891           w = l * PageWidth / PageLength;
1892         }
1893
1894         tx = 0.5 * (pw * 0.333 - w);
1895         ty = 0.5 * (pl * 0.333 - l);
1896
1897         printf("%.1f %.1f translate %.3f %.3f scale\n", tx + x * w, ty + y * l,
1898                w / PageWidth, l / PageLength);
1899         break;
1900
1901     case 16 :
1902         if (Layout & LAYOUT_VERTICAL)
1903         {
1904           x = (pos / 4) & 3;
1905           y = pos & 3;
1906         }
1907         else
1908         {
1909           x = pos & 3;
1910           y = (pos / 4) & 3;
1911         }
1912
1913         if (Layout & LAYOUT_NEGATEX)
1914           x = 3 - x;
1915
1916         if (Layout & LAYOUT_NEGATEY)
1917           y = 3 - y;
1918
1919         w = pw * 0.25;
1920         l = w * PageLength / PageWidth;
1921
1922         if (l > (pl * 0.25))
1923         {
1924           l = pl * 0.25;
1925           w = l * PageWidth / PageLength;
1926         }
1927
1928         tx = 0.5 * (pw * 0.25 - w);
1929         ty = 0.5 * (pl * 0.25 - l);
1930
1931         printf("%.1f %.1f translate %.3f %.3f scale\n", tx + x * w, ty + y * l,
1932                w / PageWidth, l / PageLength);
1933         break;
1934   }
1935
1936  /*
1937   * Draw borders as necessary...
1938   */
1939
1940   if (Border && show_border)
1941   {
1942     int         rects;          /* Number of border rectangles */
1943     float       fscale,         /* Scaling value for points */
1944                 margin;         /* Current margin for borders */
1945
1946
1947     rects  = (Border & BORDER_DOUBLE) ? 2 : 1;
1948     fscale = PageWidth / w;
1949     margin = 2.25 * fscale;
1950
1951    /*
1952     * Set the line width and color...
1953     */
1954
1955     puts("gsave");
1956     printf("%.3f setlinewidth 0 setgray newpath\n",
1957            (Border & BORDER_THICK) ? 0.5 * fscale : 0.24 * fscale);
1958
1959    /*
1960     * Draw border boxes...
1961     */
1962
1963     for (; rects > 0; rects --, margin += 2 * fscale)
1964       if (NUp > 1)
1965         printf("%.1f %.1f %.1f %.1f ESPrs\n",
1966                margin,
1967                margin,
1968                PageWidth - 2 * margin,
1969                PageLength - 2 * margin);
1970       else
1971         printf("%.1f %.1f %.1f %.1f ESPrs\n",
1972                PageLeft + margin,
1973                PageBottom + margin,
1974                PageRight - PageLeft - 2 * margin,
1975                PageTop - PageBottom - 2 * margin);
1976
1977    /*
1978     * Restore pen settings...
1979     */
1980
1981     puts("grestore");
1982   }
1983
1984   if (NUp > 1)
1985   {
1986    /*
1987     * Clip the page that follows to the bounding box of the page...
1988     */
1989
1990     printf("0 0 %.1f %.1f ESPrc\n", PageWidth, PageLength);
1991   }
1992 }
1993
1994
1995 /*
1996  * End of "$Id: pstops.c 4672 2005-09-18 04:25:46Z mike $".
1997  */