]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - filter/pstops.c
Import CUPS 1.4svn-r7226.
[thirdparty/cups.git] / filter / pstops.c
index 0a2de3196daffb0140d1b966c1d00aaaab0d0a47..f8ab6b00a6dc3d3fee8f26130b1f5d19aba31650 100644 (file)
@@ -1,40 +1,47 @@
 /*
- * "$Id: pstops.c 5092 2006-02-09 00:54:31Z mike $"
+ * "$Id: pstops.c 7006 2007-10-04 17:43:38Z mike $"
  *
  *   PostScript filter for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 1993-2006 by Easy Software Products.
+ *   Copyright 2007-2008 by Apple Inc.
+ *   Copyright 1993-2007 by Easy Software Products.
  *
  *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
- *   "LICENSE.txt" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
- *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636 USA
- *
- *       Voice: (301) 373-9600
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
+ *   property of Apple Inc. and are protected by Federal copyright
+ *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ *   which should have been included with this file.  If this file is
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
  *
  *   This file is subject to the Apple OS-Developed Software exception.
  *
  * Contents:
  *
- *   main()            - Main entry...
- *   add_page()        - Add a page to the Pages array...
- *   check_range()     - Check to see if the current page is selected for
- *   copy_bytes()      - Copy bytes from the input file to stdout...
- *   do_prolog()       - Send the necessary document prolog commands...
- *   do_setup()        - Send the necessary document setup commands...
- *   end_nup()         - End processing for N-up printing...
- *   include_feature() - Include a printer option/feature command.
- *   psgets()          - Get a line from a file.
- *   start_nup()       - Start processing for N-up printing...
+ *   main()               - Main entry...
+ *   add_page()           - Add a page to the pages array...
+ *   cancel_job()         - Flag the job as canceled.
+ *   check_range()        - Check to see if the current page is selected for
+ *   copy_bytes()         - Copy bytes from the input file to stdout...
+ *   copy_comments()      - Copy all of the comments section...
+ *   copy_dsc()           - Copy a DSC-conforming document...
+ *   copy_non_dsc()       - Copy a document that does not conform to the DSC...
+ *   copy_page()          - Copy a page description...
+ *   copy_prolog()        - Copy the document prolog section...
+ *   copy_setup()         - Copy the document setup section...
+ *   copy_trailer()       - Copy the document trailer...
+ *   do_prolog()          - Send the necessary document prolog commands...
+ *   do_setup()           - Send the necessary document setup commands...
+ *   doc_printf()         - Send a formatted string to stdout and/or the temp
+ *                          file.
+ *   doc_puts()           - Send a nul-terminated string to stdout and/or the
+ *                          temp file.
+ *   doc_write()          - Send data to stdout and/or the temp file.
+ *   end_nup()            - End processing for N-up printing...
+ *   include_feature()    - Include a printer option/feature command.
+ *   parse_text()         - Parse a text value in a comment...
+ *   set_pstops_options() - Set pstops options...
+ *   skip_page()          - Skip past a page that won't be printed...
+ *   start_nup()          - Start processing for N-up printing...
+ *   write_labels()       - Write the actual page labels.
  */
 
 /*
  */
 
 #include "common.h"
+#include <limits.h>
 #include <math.h>
 #include <cups/file.h>
 #include <cups/array.h>
+#include <cups/i18n.h>
+#include <signal.h>
 
 
 /*
  * Constants...
  */
 
-#define BORDER_NONE    0               /* No border or hairline border */
-#define BORDER_THICK   1               /* Think border */
-#define BORDER_SINGLE  2               /* Single-line hairline border */
-#define BORDER_SINGLE2 3               /* Single-line thick border */
-#define BORDER_DOUBLE  4               /* Double-line hairline border */
-#define BORDER_DOUBLE2 5               /* Double-line thick border */
+#define PSTOPS_BORDERNONE      0       /* No border or hairline border */
+#define PSTOPS_BORDERTHICK     1       /* Think border */
+#define PSTOPS_BORDERSINGLE    2       /* Single-line hairline border */
+#define PSTOPS_BORDERSINGLE2   3       /* Single-line thick border */
+#define PSTOPS_BORDERDOUBLE    4       /* Double-line hairline border */
+#define PSTOPS_BORDERDOUBLE2   5       /* Double-line thick border */
 
-#define LAYOUT_LRBT    0               /* Left to right, bottom to top */
-#define LAYOUT_LRTB    1               /* Left to right, top to bottom */
-#define LAYOUT_RLBT    2               /* Right to left, bottom to top */
-#define LAYOUT_RLTB    3               /* Right to left, top to bottom */
-#define LAYOUT_BTLR    4               /* Bottom to top, left to right */
-#define LAYOUT_TBLR    5               /* Top to bottom, left to right */
-#define LAYOUT_BTRL    6               /* Bottom to top, right to left */
-#define LAYOUT_TBRL    7               /* Top to bottom, right to left */
+#define PSTOPS_LAYOUT_LRBT     0       /* Left to right, bottom to top */
+#define PSTOPS_LAYOUT_LRTB     1       /* Left to right, top to bottom */
+#define PSTOPS_LAYOUT_RLBT     2       /* Right to left, bottom to top */
+#define PSTOPS_LAYOUT_RLTB     3       /* Right to left, top to bottom */
+#define PSTOPS_LAYOUT_BTLR     4       /* Bottom to top, left to right */
+#define PSTOPS_LAYOUT_TBLR     5       /* Top to bottom, left to right */
+#define PSTOPS_LAYOUT_BTRL     6       /* Bottom to top, right to left */
+#define PSTOPS_LAYOUT_TBRL     7       /* Top to bottom, right to left */
 
-#define LAYOUT_NEGATEY 1               /* The bits for the layout */
-#define LAYOUT_NEGATEX 2               /* definitions above... */
-#define LAYOUT_VERTICAL        4
-
-#define PROT_STANDARD  0               /* Adobe standard protocol */
-#define PROT_BCP       1               /* Adobe BCP protocol */
-#define PROT_TBCP      2               /* Adobe TBCP protocol */
+#define PSTOPS_LAYOUT_NEGATEY  1       /* The bits for the layout */
+#define PSTOPS_LAYOUT_NEGATEX  2       /* definitions above... */
+#define PSTOPS_LAYOUT_VERTICAL 4
 
 
 /*
 typedef struct                         /**** Page information ****/
 {
   char         *label;                 /* Page label */
+  int          bounding_box[4];        /* PageBoundingBox */
   off_t                offset;                 /* Offset to start of page */
   ssize_t      length;                 /* Number of bytes for page */
-  int          lbrt[4];                /* PageBoundingBox */
-  const char   *input_slot,            /* Input slot option or NULL */
-               *manual_feed;           /* Manual feed option or NULL */
-} page_info_t;
+  int          num_options;            /* Number of options for this page */
+  cups_option_t        *options;               /* Options for this page */
+} pstops_page_t;
+
+typedef struct                         /**** Document information ****/
+{
+  int          page;                   /* Current page */
+  int          bounding_box[4];        /* BoundingBox from header */
+  int          new_bounding_box[4];    /* New composite bounding box */
+  int          num_options;            /* Number of document-wide options */
+  cups_option_t        *options;               /* Document-wide options */
+  int          normal_landscape,       /* Normal rotation for landscape? */
+               saw_eof,                /* Saw the %%EOF comment? */
+               slow_collate,           /* Collate copies by hand? */
+               slow_duplex,            /* Duplex pages slowly? */
+               slow_order,             /* Reverse pages slowly? */
+               use_ESPshowpage;        /* Use ESPshowpage? */
+  cups_array_t *pages;                 /* Pages in document */
+  cups_file_t  *temp;                  /* Temporary file, if any */
+  char         tempfile[1024];         /* Temporary filename */
+  int          job_id;                 /* Job ID */
+  const char   *user,                  /* User name */
+               *title;                 /* Job name */
+  int          copies;                 /* Number of copies */
+  const char   *ap_input_slot,         /* AP_FIRSTPAGE_InputSlot value */
+               *ap_manual_feed;        /* AP_FIRSTPAGE_ManualFeed value */
+  float                brightness;             /* brightness value */
+  int          collate,                /* Collate copies? */
+               emit_jcl,               /* Emit JCL commands? */
+               fitplot;                /* Fit pages to media */
+  float                gamma;                  /* gamma value */
+  const char   *input_slot,            /* InputSlot value */
+               *manual_feed;           /* ManualFeed value */
+  int          mirror,                 /* doc->mirror/mirror pages */
+               number_up,              /* Number of pages on each sheet */
+               number_up_layout,       /* doc->number_up_layout of N-up pages */
+               output_order,           /* Requested reverse output order? */
+               page_border;            /* doc->page_border around pages */
+  const char   *page_label,            /* page-label option, if any */
+               *page_ranges,           /* page-ranges option, if any */
+               *page_set;              /* page-set option, if any */
+} pstops_doc_t;
+
+
+/*
+ * Convenience macros...
+ */
+
+#define        is_first_page(p)        (doc->number_up == 1 || \
+                                ((p) % doc->number_up) == 1)
+#define        is_last_page(p)         (doc->number_up == 1 || \
+                                ((p) % doc->number_up) == 0)
+#define is_not_last_page(p)    (doc->number_up > 1 && \
+                                ((p) % doc->number_up) != 0)
 
 
 /*
- * Globals...
+ * Local globals...
  */
 
-int            NumPages = 0;           /* Number of pages in file */
-cups_array_t   *Pages;                 /* Info on each page */
-const char     *PageRanges = NULL;     /* Range of pages selected */
-const char     *PageSet = NULL;        /* All, Even, Odd pages */
-int            Order = 0,              /* 0 = normal, 1 = reverse pages */
-               Flip = 0,               /* Flip/mirror pages */
-               FitPlot = 0,            /* Fit pages to media */
-               NUp = 1,                /* Number of pages on each sheet (1, 2, 4) */
-               Collate = 0,            /* Collate copies? */
-               Copies = 1,             /* Number of copies */
-               UseESPsp = 0,           /* Use ESPshowpage? */
-               Border = BORDER_NONE,   /* Border around pages */
-               Layout = LAYOUT_LRTB,   /* Layout of N-up pages */
-               NormalLandscape = 0;    /* Normal rotation for landscape? */
+static int             JobCanceled = 0;/* Set to 1 on SIGTERM */
 
 
 /*
  * Local functions...
  */
 
-static page_info_t     *add_page(const char *label, off_t offset,
-                                 const int *lbrt);
-static int             check_range(int page);
+static pstops_page_t   *add_page(pstops_doc_t *doc, const char *label);
+static void            cancel_job(int sig);
+static int             check_range(pstops_doc_t *doc, int page);
 static void            copy_bytes(cups_file_t *fp, off_t offset,
                                   size_t length);
-static void            do_prolog(ppd_file_t *ppd);
-static void            do_setup(ppd_file_t *ppd, int copies,  int collate,
-                                int slowcollate, float g, float b);
-static void            end_nup(int number);
-static void            include_feature(ppd_file_t *ppd, const char *line,
-                                       cups_file_t *out);
-#define                        is_first_page(p)        (NUp == 1 || (((p)+1) % NUp) == 1)
-#define                        is_last_page(p)         (NUp > 1 && (((p)+1) % NUp) == 0)
-#define                is_not_last_page(p)     (NUp > 1 && ((p) % NUp) != 0)
-static char            *psgets(char *buf, size_t *bytes, FILE *fp);
-static void            start_nup(int number, int show_border, const int *lbrt);
+static ssize_t         copy_comments(cups_file_t *fp, pstops_doc_t *doc,
+                                     ppd_file_t *ppd, char *line,
+                                     ssize_t linelen, size_t linesize);
+static void            copy_dsc(cups_file_t *fp, pstops_doc_t *doc,
+                                ppd_file_t *ppd, char *line, ssize_t linelen,
+                                size_t linesize);
+static void            copy_non_dsc(cups_file_t *fp, pstops_doc_t *doc,
+                                    ppd_file_t *ppd, char *line,
+                                    ssize_t linelen, size_t linesize);
+static ssize_t         copy_page(cups_file_t *fp, pstops_doc_t *doc,
+                                 ppd_file_t *ppd, int number, char *line,
+                                 ssize_t linelen, size_t linesize);
+static ssize_t         copy_prolog(cups_file_t *fp, pstops_doc_t *doc,
+                                   ppd_file_t *ppd, char *line,
+                                   ssize_t linelen, size_t linesize);
+static ssize_t         copy_setup(cups_file_t *fp, pstops_doc_t *doc,
+                                  ppd_file_t *ppd, char *line,
+                                  ssize_t linelen, size_t linesize);
+static ssize_t         copy_trailer(cups_file_t *fp, pstops_doc_t *doc,
+                                    ppd_file_t *ppd, int number, char *line,
+                                    ssize_t linelen, size_t linesize);
+static void            do_prolog(pstops_doc_t *doc, ppd_file_t *ppd);
+static void            do_setup(pstops_doc_t *doc, ppd_file_t *ppd);
+static void            doc_printf(pstops_doc_t *doc, const char *format, ...)
+#ifdef __GNUC__
+__attribute__ ((__format__ (__printf__, 2, 3)))
+#endif /* __GNUC__ */
+;
+static void            doc_puts(pstops_doc_t *doc, const char *s);
+static void            doc_write(pstops_doc_t *doc, const char *s, size_t len);
+static void            end_nup(pstops_doc_t *doc, int number);
+static int             include_feature(ppd_file_t *ppd, const char *line,
+                                       int num_options,
+                                       cups_option_t **options);
+static char            *parse_text(const char *start, char **end, char *buffer,
+                                   size_t bufsize);
+static void            set_pstops_options(pstops_doc_t *doc, ppd_file_t *ppd,
+                                          char *argv[], int num_options,
+                                          cups_option_t *options);
+static ssize_t         skip_page(cups_file_t *fp, char *line, ssize_t linelen,
+                                 size_t linesize);
+static void            start_nup(pstops_doc_t *doc, int number,
+                                 int show_border, const int *bounding_box);
+static void            write_label_prolog(pstops_doc_t *doc, const char *label,
+                                          float bottom, float top,
+                                          float width);
+static void            write_labels(pstops_doc_t *doc, int orient);
 
 
 /*
@@ -141,47 +221,17 @@ int                                       /* O - Exit status */
 main(int  argc,                                /* I - Number of command-line args */
      char *argv[])                     /* I - Command-line arguments */
 {
-  FILE         *fp;                    /* Print file */
+  pstops_doc_t doc;                    /* Document information */
+  cups_file_t  *fp;                    /* Print file */
   ppd_file_t   *ppd;                   /* PPD file */
-  ppd_attr_t   *attr;                  /* Attribute in PPD file */
-  ppd_option_t *option;                /* Option */
-  ppd_choice_t *choice;                /* Marked option choice */
   int          num_options;            /* Number of print options */
   cups_option_t        *options;               /* Print options */
-  const char   *val;                   /* Option value */
-  char         tempfile[255];          /* Temporary file name */
-  cups_file_t  *temp;                  /* Temporary file */
-  int          number;                 /* Page number */
-  int          slowcollate;            /* 1 if we need to collate manually */
-  int          sloworder;              /* 1 if we need to order manually */
-  int          slowduplex;             /* 1 if we need an even page count */
   char         line[8192];             /* Line buffer */
-  int          lbrt[4],                /* BoundingBox */
-               pagelbrt[4];            /* PageBoundingBox */
   size_t       len;                    /* Length of line buffer */
-  float                g;                      /* Gamma correction value */
-  float                b;                      /* Brightness factor */
-  int          level;                  /* Nesting level for embedded files */
-  int          nbytes,                 /* Number of bytes read */
-               tbytes;                 /* Bytes to read for binary data */
-  int          page;                   /* Current page sequence number */
-  int          real_page;              /* "Real" page number in document */
-  int          page_count;             /* Page count for NUp */
-  int          basepage;               /* Base page number */
-  int          subpage;                /* Sub-page number */
-  int          copy;                   /* Current copy */
-  int          saweof;                 /* Did we see a %%EOF tag? */
-  int          sent_espsp,             /* Did we send ESPshowpage commands? */
-               sent_prolog,            /* Did we send the prolog commands? */
-               sent_setup,             /* Did we send the setup commands? */
-               emit_jcl;               /* Emit JCL? */
-  float                min_order;              /* Minimum output order for selection */
-  char         label[256];             /* Page label */
-  page_info_t  *pageinfo;              /* Page information */
-  const char   *ap_input_slot,         /* First page InputSlot option */
-               *ap_manual_feed,        /* First page ManualFeed option */
-               *input_slot,            /* Original InputSlot option */
-               *manual_feed;           /* Original ManualFeed option */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+  struct sigaction action;             /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
 
  /*
   * Make sure status messages are not buffered...
@@ -195,1609 +245,2528 @@ main(int  argc,                               /* I - Number of command-line args */
 
   if (argc < 6 || argc > 7)
   {
-    fputs("ERROR: pstops job-id user title copies options [file]\n", stderr);
+    fprintf(stderr, _("Usage: %s job-id user title copies options [file]\n"),
+            argv[0]);
     return (1);
   }
 
+ /*
+  * Register a signal handler to cleanly cancel a job.
+  */
+
+#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+  sigset(SIGTERM, cancel_job);
+#elif defined(HAVE_SIGACTION)
+  memset(&action, 0, sizeof(action));
+
+  sigemptyset(&action.sa_mask);
+  action.sa_handler = cancel_job;
+  sigaction(SIGTERM, &action, NULL);
+#else
+  signal(SIGTERM, cancel_job);
+#endif /* HAVE_SIGSET */
+
  /*
   * If we have 7 arguments, print the file named on the command-line.
   * Otherwise, send stdin instead...
   */
 
   if (argc == 6)
-    fp = stdin;
+    fp = cupsFileStdin();
   else
   {
    /*
     * Try to open the print file...
     */
 
-    if ((fp = fopen(argv[6], "rb")) == NULL)
+    if ((fp = cupsFileOpen(argv[6], "r")) == NULL)
     {
-      fprintf(stderr, "ERROR: unable to open print file \"%s\" - %s\n",
+      fprintf(stderr, _("ERROR: Unable to open file \"%s\" - %s\n"),
               argv[6], strerror(errno));
       return (1);
     }
   }
 
  /*
-  * Process command-line options and write the prolog...
+  * Read the first line to see if we have DSC comments...
   */
 
-  g = 1.0;
-  b = 1.0;
+  if ((len = cupsFileGetLine(fp, line, sizeof(line))) == 0)
+  {
+    fputs(_("ERROR: Empty print file!\n"), stderr);
+    return (1);
+  }
 
-  Copies = atoi(argv[4]);
+ /*
+  * Process command-line options...
+  */
 
   options     = NULL;
   num_options = cupsParseOptions(argv[5], 0, &options);
+  ppd         = SetCommonOptions(num_options, options, 1);
 
-  ppd = SetCommonOptions(num_options, options, 1);
-
-  if (ppd && ppd->landscape > 0)
-    NormalLandscape = 1;
-
-  if ((val = cupsGetOption("page-ranges", num_options, options)) != NULL)
-    PageRanges = val;
-
-  if ((val = cupsGetOption("page-set", num_options, options)) != NULL)
-    PageSet = val;
-
-  if ((val = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
-  {
-   /*
-    * This IPP attribute is unnecessarily complicated...
-    *
-    *   single-document, separate-documents-collated-copies, and
-    *   single-document-new-sheet all require collated copies.
-    *
-    *   separate-documents-uncollated-copies allows for uncollated copies.
-    */
+  set_pstops_options(&doc, ppd, argv, num_options, options);
 
-    Collate = strcasecmp(val, "separate-documents-uncollated-copies") != 0;
-  }
+ /*
+  * Write any "exit server" options that have been selected...
+  */
 
-  if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
-      (!strcasecmp(val, "true") ||!strcasecmp(val, "on") ||
-       !strcasecmp(val, "yes")))
-    Collate = 1;
+  ppdEmit(ppd, stdout, PPD_ORDER_EXIT);
 
-  if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL)
-  {
-    if (!strcasecmp(val, "Reverse"))
-      Order = 1;
-  }
-  else if (ppd)
-  {
-   /*
-    * Figure out the right default output order from the PPD file...
-    */
+ /*
+  * Write any JCL commands that are needed to print PostScript code...
+  */
 
-    if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL &&
-        (attr = ppdFindAttr(ppd, "PageStackOrder", choice->choice)) != NULL &&
-       attr->value)
-      Order = !strcasecmp(attr->value, "Reverse");
-    else if ((attr = ppdFindAttr(ppd, "DefaultOutputOrder", NULL)) != NULL &&
-             attr->value)
-      Order = !strcasecmp(attr->value, "Reverse");
-  }
+  if (doc.emit_jcl)
+    ppdEmitJCL(ppd, stdout, doc.job_id, doc.user, doc.title);
 
-  if ((val = cupsGetOption("number-up", num_options, options)) != NULL)
-    NUp = atoi(val);
+ /*
+  * Start with a DSC header...
+  */
 
-  if ((val = cupsGetOption("page-border", num_options, options)) != NULL)
-  {
-    if (!strcasecmp(val, "none"))
-      Border = BORDER_NONE;
-    else if (!strcasecmp(val, "single"))
-      Border = BORDER_SINGLE;
-    else if (!strcasecmp(val, "single-thick"))
-      Border = BORDER_SINGLE2;
-    else if (!strcasecmp(val, "double"))
-      Border = BORDER_DOUBLE;
-    else if (!strcasecmp(val, "double-thick"))
-      Border = BORDER_DOUBLE2;
-  }
+  puts("%!PS-Adobe-3.0");
 
-  if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL)
-  {
-    if (!strcasecmp(val, "lrtb"))
-      Layout = LAYOUT_LRTB;
-    else if (!strcasecmp(val, "lrbt"))
-      Layout = LAYOUT_LRBT;
-    else if (!strcasecmp(val, "rltb"))
-      Layout = LAYOUT_RLTB;
-    else if (!strcasecmp(val, "rlbt"))
-      Layout = LAYOUT_RLBT;
-    else if (!strcasecmp(val, "tblr"))
-      Layout = LAYOUT_TBLR;
-    else if (!strcasecmp(val, "tbrl"))
-      Layout = LAYOUT_TBRL;
-    else if (!strcasecmp(val, "btlr"))
-      Layout = LAYOUT_BTLR;
-    else if (!strcasecmp(val, "btrl"))
-      Layout = LAYOUT_BTRL;
-  }
+ /*
+  * Skip leading PJL in the document...
+  */
 
-  if ((val = cupsGetOption("gamma", num_options, options)) != NULL)
+  while (!strncmp(line, "\033%-12345X", 9) || !strncmp(line, "@PJL ", 5))
   {
    /*
-    * Get gamma value from 1 to 10000...
+    * Yup, we have leading PJL fun, so skip it until we hit the line
+    * with "ENTER LANGUAGE"...
     */
 
-    g = atoi(val) * 0.001f;
-
-    if (g < 0.001f)
-      g = 0.001f;
-    else if (g > 10.0f)
-      g = 10.0f;
-  }
+    fputs("DEBUG: Skipping PJL header...\n", stderr);
 
-  if ((val = cupsGetOption("brightness", num_options, options)) != NULL)
-  {
-   /*
-    * Get brightness value from 10 to 1000.
-    */
+    while (strstr(line, "ENTER LANGUAGE") == NULL && strncmp(line, "%!", 2))
+      if ((len = cupsFileGetLine(fp, line, sizeof(line))) == 0)
+        break;
 
-    b = atoi(val) * 0.01f;
+    if (!strncmp(line, "%!", 2))
+      break;
 
-    if (b < 0.1f)
-      b = 0.1f;
-    else if (b > 10.0f)
-      b = 10.0f;
+    if ((len = cupsFileGetLine(fp, line, sizeof(line))) == 0)
+      break;
   }
 
-  if ((val = cupsGetOption("mirror", num_options, options)) != NULL &&
-      (!strcasecmp(val, "true") || !strcasecmp(val, "on") ||
-       !strcasecmp(val, "yes")))
-    Flip = 1;
-
-  if ((val = cupsGetOption("fitplot", num_options, options)) != NULL &&
-      (!strcasecmp(val, "true") || !strcasecmp(val, "on") ||
-       !strcasecmp(val, "yes")))
-    FitPlot = 1;
-
-  if ((val = cupsGetOption("emit-jcl", num_options, options)) != NULL &&
-      (!strcasecmp(val, "false") || !strcasecmp(val, "off") ||
-       !strcasecmp(val, "no") || !strcmp(val, "0")))
-    emit_jcl = 0;
-  else
-    emit_jcl = 1;
-
  /*
-  * Handle input slot/manual feed selections...
+  * Now see if the document conforms to the Adobe Document Structuring
+  * Conventions...
   */
 
-  if ((choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL)
-    input_slot = choice->choice;
-  else
-    input_slot = NULL;
-
-  if ((choice = ppdFindMarkedChoice(ppd, "ManualFeed")) != NULL)
-    manual_feed = choice->choice;
-  else
-    manual_feed = NULL;
-
-  ap_input_slot  = cupsGetOption("AP_FIRSTPAGE_InputSlot", num_options,
-                                 options);
-  ap_manual_feed = cupsGetOption("AP_FIRSTPAGE_ManualFeed", num_options,
-                                 options);
-  min_order      = 999.0f;
-
-  if (ppd && (ap_input_slot || ap_manual_feed))
+  if (!strncmp(line, "%!PS-Adobe-", 11))
   {
    /*
-    * The first page/sheet will be using different options than
-    * the rest, so figure out the minimum order dependency for
-    * each of the options...
+    * Yes, filter the document...
     */
 
-    if ((option = ppdFindOption(ppd, "PageRegion")) != NULL &&
-        option->order < min_order)
-      min_order = option->order;
-
-    if ((option = ppdFindOption(ppd, "InputSlot")) != NULL &&
-        option->order < min_order)
-      min_order = option->order;
-
-    if ((option = ppdFindOption(ppd, "ManualFeed")) != NULL &&
-        option->order < min_order)
-      min_order = option->order;
+    copy_dsc(fp, &doc, ppd, line, len, sizeof(line));
   }
-
-  if (ppd && ppd->manual_copies && Duplex && Copies > 1)
+  else
   {
    /*
-    * Force collated copies when printing a duplexed document to
-    * a non-PS printer that doesn't do hardware copy generation.
-    * Otherwise the copies will end up on the front/back side of
-    * each page.
+    * No, display an error message and treat the file as if it contains
+    * a single page...
     */
 
-    Collate = 1;
+    copy_non_dsc(fp, &doc, ppd, line, len, sizeof(line));
   }
 
  /*
-  * See if we have to filter the fast or slow way...
+  * Send %%EOF as needed...
   */
 
-  if (Collate && Copies > 1)
-  {
-   /*
-    * See if we need to manually collate the pages...
-    */
-
-    slowcollate = 1;
-
-    if ((choice = ppdFindMarkedChoice(ppd, "Collate")) != NULL &&
-        !strcasecmp(choice->choice, "True"))
-    {
-     /*
-      * Hardware collate option is selected, see if the option is
-      * conflicting - if not, collate in hardware.  Otherwise,
-      * turn the hardware collate option off...
-      */
-
-      if ((option = ppdFindOption(ppd, "Option")) != NULL &&
-          !option->conflicted)
-       slowcollate = 0;
-      else
-        ppdMarkOption(ppd, "Collate", "False");
-    }
-  }
-  else
-    slowcollate = 0;
-
-  if (ppdFindOption(ppd, "OutputOrder") == NULL && Order)
-    sloworder = 1;
-  else
-    sloworder = 0;
-
-  if ((slowcollate || sloworder) && Duplex)
-    slowduplex = 1;
-  else
-    slowduplex = 0;
+  if (!doc.saw_eof)
+    puts("%%EOF");
 
  /*
-  * If we need to filter slowly, then create a temporary file for page data...
-  *
-  * If the temp file can't be created, then we'll ignore the collating/output
-  * order options...
+  * End the job with the appropriate JCL command or CTRL-D...
   */
 
-  if (sloworder || slowcollate)
+  if (doc.emit_jcl)
   {
-    if ((temp = cupsTempFile2(tempfile, sizeof(tempfile))) == NULL)
-      slowcollate = sloworder = 0;
+    if (ppd && ppd->jcl_end)
+      ppdEmitJCLEnd(ppd, stdout);
+    else
+      putchar(0x04);
   }
-  else
-    temp = NULL;
 
  /*
-  * Write any "exit server" options that have been selected...
+  * Close files and remove the temporary file if needed...
   */
 
-  ppdEmit(ppd, stdout, PPD_ORDER_EXIT);
+  if (doc.temp)
+  {
+    cupsFileClose(doc.temp);
+    unlink(doc.tempfile);
+  }
 
- /*
-  * Write any JCL commands that are needed to print PostScript code...
-  */
+  ppdClose(ppd);
+  cupsFreeOptions(num_options, options);
 
-  if (emit_jcl)
-    ppdEmitJCL(ppd, stdout, atoi(argv[1]), argv[2], argv[3]);
+  cupsFileClose(fp);
 
- /*
-  * Read the first line to see if we have DSC comments...
-  */
+  return (0);
+}
 
-  len = sizeof(line);
-  if (psgets(line, &len, fp) == NULL)
-  {
-    fputs("ERROR: Empty print file!\n", stderr);
-    ppdClose(ppd);
-    return (1);
-  }
 
- /*
 * Handle leading PJL fun...
 */
+/*
* 'add_page()' - Add a page to the pages array...
+ */
 
-  while (!strncmp(line, "\033%-12345X", 9) || !strncmp(line, "@PJL ", 5))
-  {
-   /*
-    * Yup, we have leading PJL fun, so skip it until we hit the line
-    * with "ENTER LANGUAGE"...
-    */
+static pstops_page_t *                 /* O - New page info object */
+add_page(pstops_doc_t *doc,            /* I - Document information */
+         const char   *label)          /* I - Page label */
+{
+  pstops_page_t        *pageinfo;              /* New page info object */
 
-    fputs("DEBUG: Skipping PJL header...\n", stderr);
 
-    while (strstr(line, "ENTER LANGUAGE") == NULL)
-    {
-      len = sizeof(line);
-      if (psgets(line, &len, fp) == NULL)
-        break;
-    }
+  if (!doc->pages)
+    doc->pages = cupsArrayNew(NULL, NULL);
 
-    len = sizeof(line);
-    if (psgets(line, &len, fp) == NULL)
-      break;
+  if (!doc->pages)
+  {
+    fprintf(stderr, _("EMERG: Unable to allocate memory for pages array: %s\n"),
+            strerror(errno));
+    exit(1);
   }
 
- /*
-  * Start sending the document with any commands needed...
-  */
+  if ((pageinfo = calloc(1, sizeof(pstops_page_t))) == NULL)
+  {
+    fprintf(stderr, _("EMERG: Unable to allocate memory for page info: %s\n"),
+            strerror(errno));
+    exit(1);
+  }
 
-  fwrite(line, 1, len, stdout);
+  pageinfo->label  = strdup(label);
+  pageinfo->offset = cupsFileTell(doc->temp);
 
-  saweof      = 0;
-  sent_espsp  = 0;
-  sent_prolog = 0;
-  sent_setup  = 0;
+  cupsArrayAdd(doc->pages, pageinfo);
 
-  if (Copies != 1 && (!Collate || !slowcollate))
-  {
-   /*
-    * Tell the document processor the copy and duplex options
-    * that are required...
-    */
+  doc->page ++;
 
-    printf("%%%%Requirements: numcopies(%d)%s%s\n", Copies,
-           Collate ? " collate" : "",
-          Duplex ? " duplex" : "");
+  return (pageinfo);
+}
 
-   /*
-    * Apple uses RBI comments for various non-PPD options...
-    */
 
-    printf("%%RBINumCopies: %d\n", Copies);
-  }
-  else
-  {
-   /*
-    * Tell the document processor the duplex option that is required...
-    */
+/*
+ * 'cancel_job()' - Flag the job as canceled.
+ */
 
-    if (Duplex)
-      puts("%%Requirements: duplex");
+static void
+cancel_job(int sig)                    /* I - Signal number (unused) */
+{
+  (void)sig;
 
-   /*
-    * Apple uses RBI comments for various non-PPD options...
-    */
+  JobCanceled = 1;
+}
 
-    puts("%RBINumCopies: 1");
-  }
 
- /*
-  * Figure out if we should use ESPshowpage or not...
-  */
+/*
+ * 'check_range()' - Check to see if the current page is selected for
+ *                   printing.
+ */
+
+static int                             /* O - 1 if selected, 0 otherwise */
+check_range(pstops_doc_t *doc,         /* I - Document information */
+            int          page)         /* I - Page number */
+{
+  const char   *range;                 /* Pointer into range string */
+  int          lower, upper;           /* Lower and upper page numbers */
 
-  val = cupsGetOption("page-label", num_options, options);
 
-  if (val != NULL || getenv("CLASSIFICATION") != NULL || NUp > 1 ||
-      Border || strstr(line, "EPS") != NULL)
+  if (doc->page_set)
   {
    /*
-    * Yes, use ESPshowpage...
+    * See if we only print even or odd pages...
     */
 
-    UseESPsp = 1;
+    if (!strcasecmp(doc->page_set, "even") && (page & 1))
+      return (0);
+
+    if (!strcasecmp(doc->page_set, "odd") && !(page & 1))
+      return (0);
   }
 
-  fprintf(stderr, "DEBUG: slowcollate=%d, slowduplex=%d, sloworder=%d\n",
-          slowcollate, slowduplex, sloworder);
+  if (!doc->page_ranges)
+    return (1);                                /* No range, print all pages... */
 
-  if (!strncmp(line, "%!PS-Adobe-", 11) && !strstr(line, "EPSF"))
+  for (range = doc->page_ranges; *range != '\0';)
   {
-   /*
-    * OK, we have DSC comments and this isn't an EPS file; read until we
-    * find a %%Page comment...
-    */
-
-    puts("%%Pages: (atend)");
-    printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n", PageLeft, PageBottom,
-           PageRight, PageTop);
-
-    level   = 0;
-    lbrt[0] = 0;
-    lbrt[1] = 0;
-    lbrt[2] = (int)PageWidth;
-    lbrt[3] = (int)PageLength;
-
-    while (!feof(fp))
+    if (*range == '-')
     {
-      len = sizeof(line);
-      if (psgets(line, &len, fp) == NULL)
-        break;
-
-      if (!strncmp(line, "%%", 2))
-        fprintf(stderr, "DEBUG: %d %s", level, line);
-      else if (line[0] != '%' && line[0] && !sent_espsp && UseESPsp)
-      {
-       /*
-        * Send ESPshowpage stuff...
-       */
-
-        sent_espsp = 1;
-
-       puts("userdict/ESPshowpage/showpage load put\n"
-            "userdict/showpage{}put");
-      }
+      lower = 1;
+      range ++;
+      upper = strtol(range, (char **)&range, 10);
+    }
+    else
+    {
+      lower = strtol(range, (char **)&range, 10);
 
-      if (!strncmp(line, "%%BeginDocument:", 16) ||
-          !strncmp(line, "%%BeginDocument ", 16) ||    /* Adobe Acrobat BUG */
-         !strncmp(line, "%ADO_BeginApplication", 21))
-      {
-       fputs(line, stdout);
-        level ++;
-      }
-      else if ((!strncmp(line, "%%EndDocument", 13) ||
-               !strncmp(line, "%ADO_EndApplication", 19)) && level > 0)
-      {
-       fputs(line, stdout);
-        level --;
-      }
-      else if (!strncmp(line, "%cupsRotation:", 14) && level == 0)
+      if (*range == '-')
       {
-       /*
-        * Reset orientation of document?
-       */
-
-        int orient = (atoi(line + 14) / 90) & 3;
-
-        if (orient != Orientation)
-       {
-         Orientation = (4 - Orientation + orient) & 3;
-         UpdatePageVars();
-         Orientation = orient;
-       }
+        range ++;
+       if (!isdigit(*range & 255))
+         upper = 65535;
+       else
+         upper = strtol(range, (char **)&range, 10);
       }
-      else if (!strncmp(line, "%%BeginProlog", 13) && level == 0)
-      {
-       /*
-        * Write the existing comment line, and then follow with patches
-       * and prolog commands...
-       */
+      else
+        upper = lower;
+    }
 
-        fputs(line, stdout);
+    if (page >= lower && page <= upper)
+      return (1);
 
-       if (!sent_prolog)
-       {
-         sent_prolog = 1;
-          do_prolog(ppd);
-       }
-      }
-      else if (!strncmp(line, "%%BeginSetup", 12) && level == 0)
-      {
-       /*
-        * Write the existing comment line, and then follow with document
-       * setup commands...
-       */
+    if (*range == ',')
+      range ++;
+    else
+      break;
+  }
 
-        fputs(line, stdout);
+  return (0);
+}
 
-       if (!sent_prolog)
-       {
-         sent_prolog = 1;
-          do_prolog(ppd);
-       }
 
-       if (!sent_setup)
-       {
-         sent_setup = 1;
-          do_setup(ppd, Copies, Collate, slowcollate, g, b);
-       }
-      }
-      else if (!strncmp(line, "%%BoundingBox:", 14) && level == 0)
-      {
-        if (sscanf(line + 14, "%d%d%d%d", pagelbrt + 0, pagelbrt + 1,
-                  pagelbrt + 2, pagelbrt + 3) == 4)
-         memcpy(lbrt, pagelbrt, sizeof(lbrt));
-      }
-      else if (!strncmp(line, "%%Page:", 7) && level == 0)
-        break;
-      else if (!strncmp(line, "%%IncludeFeature:", 17) && level == 0 &&
-               NUp == 1 && !FitPlot)
-        include_feature(ppd, line, NULL);
-      else if (!strncmp(line, "%%BeginBinary:", 14) ||
-               (!strncmp(line, "%%BeginData:", 12) &&
-               !strstr(line, "ASCII") && !strstr(line, "Hex")))
-      {
-       /*
-        * Copy binary data...
-       */
+/*
+ * 'copy_bytes()' - Copy bytes from the input file to stdout...
+ */
 
-        tbytes = atoi(strchr(line, ':') + 1);
-       fputs(line, stdout);
+static void
+copy_bytes(cups_file_t *fp,            /* I - File to read from */
+           off_t       offset,         /* I - Offset to page data */
+           size_t      length)         /* I - Length of page data */
+{
+  char         buffer[8192];           /* Data buffer */
+  ssize_t      nbytes;                 /* Number of bytes read */
+  size_t       nleft;                  /* Number of bytes left/remaining */
 
-       while (tbytes > 0)
-       {
-         if (tbytes > sizeof(line))
-           nbytes = fread(line, 1, sizeof(line), fp);
-         else
-           nbytes = fread(line, 1, tbytes, fp);
 
-          if (nbytes < 1)
-         {
-           perror("ERROR: Early end-of-file while reading binary data");
-           return (1);
-         }
+  nleft = length;
 
-         fwrite(line, 1, nbytes, stdout);
-         tbytes -= nbytes;
-       }
-      }
-      else if (strncmp(line, "%%Pages:", 8) != 0)
-        fwrite(line, 1, len, stdout);
-    }
+  if (cupsFileSeek(fp, offset) < 0)
+  {
+    fprintf(stderr,
+#ifdef HAVE_LONG_LONG
+            _("ERROR: Unable to seek to offset %lld in file - %s\n"),
+#else
+            _("ERROR: Unable to seek to offset %ld in file - %s\n"),
+#endif /* HAVE_LONG_LONG */
+            CUPS_LLCAST offset, strerror(errno));
+    return;
+  }
 
-   /*
-    * Make sure we have the prolog and setup commands written...
-    */
+  while (nleft > 0 || length == 0)
+  {
+    if (nleft > sizeof(buffer) || length == 0)
+      nbytes = sizeof(buffer);
+    else
+      nbytes = nleft;
 
-    if (!sent_prolog)
-    {
-      puts("%%BeginProlog");
+    if ((nbytes = cupsFileRead(fp, buffer, nbytes)) < 1)
+      return;
 
-      sent_prolog = 1;
-      do_prolog(ppd);
+    nleft -= nbytes;
 
-      puts("%%EndProlog");
-    }
+    fwrite(buffer, 1, nbytes, stdout);
+  }
+}
 
-    if (!sent_setup)
-    {
-      puts("%%BeginSetup");
 
-      sent_setup = 1;
-      do_setup(ppd, Copies, Collate, slowcollate, g, b);
+/*
+ * 'copy_comments()' - Copy all of the comments section...
+ *
+ * This function expects "line" to be filled with a comment line.
+ * On return, "line" will contain the next line in the file, if any.
+ */
 
-      puts("%%EndSetup");
-    }
+static ssize_t                         /* O - Length of next line */
+copy_comments(cups_file_t  *fp,                /* I - File to read from */
+              pstops_doc_t *doc,       /* I - Document info */
+             ppd_file_t   *ppd,        /* I - PPD file */
+              char         *line,      /* I - Line buffer */
+             ssize_t      linelen,     /* I - Length of initial line */
+             size_t       linesize)    /* I - Size of line buffer */
+{
+  int  saw_bounding_box,               /* Saw %%BoundingBox: comment? */
+       saw_for,                        /* Saw %%For: comment? */
+       saw_pages,                      /* Saw %%Pages: comment? */
+       saw_title;                      /* Saw %%Title: comment? */
 
-    if (!sent_espsp && UseESPsp)
-    {
-     /*
-      * Send ESPshowpage stuff...
-      */
 
-      sent_espsp = 1;
+ /*
+  * Loop until we see %%EndComments or a non-comment line...
+  */
 
-      puts("userdict/ESPshowpage/showpage load put\n"
-          "userdict/showpage{}put");
-    }
+  saw_bounding_box = 0;
+  saw_for          = 0;
+  saw_pages        = 0;
+  saw_title        = 0;        
 
+  while (line[0] == '%')
+  {
    /*
-    * Write the page and label prologs...
+    * Strip trailing whitespace...
     */
 
-    if (NUp == 2 || NUp == 6)
+    while (linelen > 0)
     {
-     /*
-      * For 2- and 6-up output, rotate the labels to match the orientation
-      * of the pages...
-      */
+      linelen --;
 
-      if (Orientation & 1)
-       WriteLabelProlog(val, PageBottom, PageWidth - PageLength + PageTop,
-                        PageLength);
+      if (!isspace(line[linelen] & 255))
+        break;
       else
-       WriteLabelProlog(val, PageLeft, PageRight, PageLength);
+        line[linelen] = '\0';
     }
-    else
-      WriteLabelProlog(val, PageBottom, PageTop, PageWidth);
 
    /*
-    * Then read all of the pages, filtering as needed...
+    * Log the header...
     */
 
-    for (page = 1, real_page = 1, pageinfo = NULL;;)
-    {
-      if (!strncmp(line, "%%", 2))
-        fprintf(stderr, "DEBUG: %d %s", level, line);
-
-      if (!strncmp(line, "%%BeginDocument:", 16) ||
-          !strncmp(line, "%%BeginDocument ", 16) ||    /* Adobe Acrobat BUG */
-         !strncmp(line, "%ADO_BeginApplication", 21))
-      {
-        level ++;
-
-       if (!sloworder)
-          fputs(line, stdout);
-
-       if (slowcollate || sloworder)
-         cupsFilePuts(temp, line);
-      }
-      else if ((!strncmp(line, "%%EndDocument", 13) ||
-               !strncmp(line, "%ADO_EndApplication", 19)) && level > 0)
-      {
-        level --;
-
-       if (!sloworder)
-          fputs(line, stdout);
-
-       if (slowcollate || sloworder)
-         cupsFilePuts(temp, line);
-      }
-      else if (!strcmp(line, "\004") && len == 1)
-        break;
-      else if (!strncmp(line, "%%EOF", 5) && level == 0)
-      {
-        fputs("DEBUG: Saw EOF!\n", stderr);
-        saweof = 1;
-       break;
-      }
-      else if (!strncmp(line, "%%Page:", 7) && level == 0)
-      {
-       if (!check_range(real_page))
-       {
-         while (!feof(fp))
-         {
-           len = sizeof(line);
-           if (psgets(line, &len, fp) == NULL)
-             break;
-
-           if (!strncmp(line, "%%", 2))
-              fprintf(stderr, "DEBUG: %d %s", level, line);
-
-           if (!strncmp(line, "%%BeginDocument:", 16) ||
-               !strncmp(line, "%%BeginDocument ", 16) || /* Adobe Acrobat BUG */
-               !strncmp(line, "%ADO_BeginApplication", 21))
-              level ++;
-           else if ((!strncmp(line, "%%EndDocument", 13) ||
-                     !strncmp(line, "%ADO_EndApplication", 19)) && level > 0)
-              level --;
-           else if (!strncmp(line, "%%Page:", 7) && level == 0)
-           {
-             real_page ++;
-             break;
-           }
-           else if (!strncmp(line, "%%BeginBinary:", 14) ||
-                    (!strncmp(line, "%%BeginData:", 12) &&
-                     !strstr(line, "ASCII") && !strstr(line, "Hex")))
-           {
-            /*
-              * Skip binary data...
-             */
-
-              tbytes = atoi(strchr(line, ':') + 1);
-
-             while (tbytes > 0)
-             {
-               if (tbytes > sizeof(line))
-                 nbytes = fread(line, 1, sizeof(line), fp);
-               else
-                 nbytes = fread(line, 1, tbytes, fp);
-
-               if (nbytes < 1)
-               {
-                 perror("ERROR: Early end-of-file while reading binary data");
-                 return (1);
-               }
-
-               tbytes -= nbytes;
-             }
-           }
-          }
-
-          continue;
-        }
-
-        if (!sloworder && NumPages > 0)
-         end_nup(NumPages - 1);
+    fprintf(stderr, "DEBUG: %s\n", line);
 
-        if (sscanf(line, "%%%%Page:%255s%*d", label) != 1)
-         sprintf(label, "%d", page);
+   /*
+    * Pull the headers out...
+    */
 
-       if (slowcollate || sloworder)
-       {
-         pageinfo = add_page(label, cupsFileTell(temp), lbrt);
+    if (!strncmp(line, "%%Pages:", 8))
+    {
+      int      pages;                  /* Number of pages */
 
-          if (ap_input_slot || ap_manual_feed)
-         {
-           if (page == 0)
-           {
-             pageinfo->input_slot  = ap_input_slot;
-             pageinfo->manual_feed = ap_manual_feed;
-           }
-           else if (page == (1 + Duplex))
-           {
-             pageinfo->input_slot  = input_slot;
-             pageinfo->manual_feed = manual_feed;
-           }
-         }
-       }
-       else
-         pageinfo = NULL;
 
-        if (!sloworder)
-       {
-         if (is_first_page(NumPages))
-         {
-           if (ppd == NULL || ppd->num_filters == 0)
-             fprintf(stderr, "PAGE: %d %d\n", page, slowcollate ? 1 : Copies);
-
-            if (NUp > 1)
-             printf("%%%%Page: %d %d\n", page, page);
-           else
-              printf("%%%%Page: %s %d\n", label, page);
-
-            if (ap_input_slot || ap_manual_feed)
-           {
-             if (page == 0)
-             {
-               if (ap_input_slot)
-                 ppdMarkOption(ppd, "InputSlot", ap_input_slot);
-               if (ap_manual_feed)
-                 ppdMarkOption(ppd, "ManualFeed", ap_manual_feed);
-             }
-             else if (page == (1 + Duplex))
-             {
-               if (input_slot)
-                 ppdMarkOption(ppd, "InputSlot", input_slot);
-               if (manual_feed)
-                 ppdMarkOption(ppd, "ManualFeed", manual_feed);
-             }
-
-              ppdEmitAfterOrder(ppd, stdout, PPD_ORDER_DOCUMENT, 1, min_order);
-              ppdEmitAfterOrder(ppd, stdout, PPD_ORDER_ANY, 1, min_order);
-           }
-
-           page ++;
-           ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
-         }
+      if (saw_pages)
+        fputs(_("ERROR: Duplicate %%Pages: comment seen!\n"), stderr);
 
-         start_nup(NumPages, 1, lbrt);
-       }
+      saw_pages = 1;
 
-       NumPages ++;
-       real_page ++;
-      }
-      else if (!strncmp(line, "%%PageBoundingBox:", 18) && level == 0 &&
-               pageinfo)
-      {
-        if (sscanf(line + 18, "%d%d%d%d", pagelbrt + 0, pagelbrt + 1,
-                  pagelbrt + 2, pagelbrt + 3) == 4)
-         memcpy(pageinfo->lbrt, pagelbrt, sizeof(pageinfo->lbrt));
-      }
-      else if (!strncmp(line, "%%BeginBinary:", 14) ||
-               (!strncmp(line, "%%BeginData:", 12) &&
-               !strstr(line, "ASCII") && !strstr(line, "Hex")))
+      if (Duplex && (pages = atoi(line + 8)) > 0 && pages <= doc->number_up)
       {
        /*
-        * Copy binary data...
+        * Since we will only be printing on a single page, disable duplexing.
        */
 
-        tbytes = atoi(strchr(line, ':') + 1);
-
-       if (!sloworder)
-         fputs(line, stdout);
-       if (slowcollate || sloworder)
-         cupsFilePuts(temp, line);
+       Duplex           = 0;
+       doc->slow_duplex = 0;
 
-       while (tbytes > 0)
-       {
-         if (tbytes > sizeof(line))
-           nbytes = fread(line, 1, sizeof(line), fp);
-         else
-           nbytes = fread(line, 1, tbytes, fp);
+       if (cupsGetOption("sides", doc->num_options, doc->options))
+         doc->num_options = cupsAddOption("sides", "one-sided",
+                                          doc->num_options, &(doc->options));
 
-          if (nbytes < 1)
-         {
-           perror("ERROR: Early end-of-file while reading binary data");
-           return (1);
-         }
+       if (cupsGetOption("Duplex", doc->num_options, doc->options))
+         doc->num_options = cupsAddOption("Duplex", "None",
+                                          doc->num_options, &(doc->options));
 
-          if (!sloworder)
-           fwrite(line, 1, nbytes, stdout);
+       if (cupsGetOption("EFDuplex", doc->num_options, doc->options))
+         doc->num_options = cupsAddOption("EFDuplex", "None",
+                                          doc->num_options, &(doc->options));
 
-          if (slowcollate || sloworder)
-           cupsFileWrite(temp, line, nbytes);
+       if (cupsGetOption("EFDuplexing", doc->num_options, doc->options))
+         doc->num_options = cupsAddOption("EFDuplexing", "False",
+                                          doc->num_options, &(doc->options));
 
-         tbytes -= nbytes;
-       }
-      }
-      else if (!strncmp(line, "%%IncludeFeature:", 17) && level == 0 &&
-               NUp == 1 && !FitPlot)
-      {
-       /*
-        * Embed printer commands as needed...
-       */
+       if (cupsGetOption("KD03Duplex", doc->num_options, doc->options))
+         doc->num_options = cupsAddOption("KD03Duplex", "None",
+                                          doc->num_options, &(doc->options));
 
-        if (level == 0 && NUp == 1)
-       {
-         include_feature(ppd, line, NULL);
+       if (cupsGetOption("JCLDuplex", doc->num_options, doc->options))
+         doc->num_options = cupsAddOption("JCLDuplex", "None",
+                                          doc->num_options, &(doc->options));
 
-          if (slowcollate || sloworder)
-           include_feature(ppd, line, temp);
-       }
+       ppdMarkOption(ppd, "Duplex", "None");
+       ppdMarkOption(ppd, "EFDuplex", "None");
+       ppdMarkOption(ppd, "EFDuplexing", "False");
+       ppdMarkOption(ppd, "KD03Duplex", "None");
+       ppdMarkOption(ppd, "JCLDuplex", "None");
       }
-      else if (!strncmp(line, "%%BeginFeature:", 15) && (NUp > 1 || FitPlot))
+    }
+    else if (!strncmp(line, "%%BoundingBox:", 14))
+    {
+      if (saw_bounding_box)
+        fputs(_("ERROR: Duplicate %%BoundingBox: comment seen!\n"), stderr);
+      else if (strstr(line + 14, "(atend)"))
       {
        /*
-        * Strip page options for N-up > 1 or "fitplot"...
+        * Do nothing for now but use the default imageable area...
        */
-
-        do
-       {
-         len = sizeof(line);
-         if (psgets(line, &len, fp) == NULL)
-           break;
-        }
-       while (strncmp(line, "%%EndFeature", 12));
-      }
-      else if (!strncmp(line, "%%Trailer", 9) && level == 0)
-      {
-        fputs("DEBUG: Saw Trailer!\n", stderr);
-        break;
       }
-      else
+      else if (sscanf(line + 14, "%d%d%d%d", doc->bounding_box + 0,
+                     doc->bounding_box + 1, doc->bounding_box + 2,
+                     doc->bounding_box + 3) != 4)
       {
-        if (!sloworder)
-          fwrite(line, 1, len, stdout);
+       fputs(_("ERROR: Bad %%BoundingBox: comment seen!\n"), stderr);
 
-       if (slowcollate || sloworder)
-         cupsFileWrite(temp, line, len);
+       doc->bounding_box[0] = (int)PageLeft;
+       doc->bounding_box[1] = (int)PageBottom;
+       doc->bounding_box[2] = (int)PageRight;
+       doc->bounding_box[3] = (int)PageTop;
       }
 
-     /*
-      * Get next line from file...
-      */
-
-      if (pageinfo)
-       pageinfo->length = cupsFileTell(temp) - pageinfo->offset;
-
-      len = sizeof(line);
-      if (psgets(line, &len, fp) == NULL)
-        break;
+      saw_bounding_box = 1;
     }
-
-    if (!sloworder)
+    else if (!strncmp(line, "%%For:", 6))
     {
-      end_nup(NumPages - 1);
+      saw_for = 1;
+      doc_printf(doc, "%s\n", line);
+    }
+    else if (!strncmp(line, "%%Title:", 8))
+    {
+      saw_title = 1;
+      doc_printf(doc, "%s\n", line);
+    }
+    else if (!strncmp(line, "%cupsRotation:", 14))
+    {
+     /*
+      * Reset orientation of document?
+      */
 
-      if (is_not_last_page(NumPages))
-      {
-       start_nup(NUp - 1, 0, lbrt);
-        end_nup(NUp - 1);
-      }
+      int orient = (atoi(line + 14) / 90) & 3;
 
-      if (slowduplex && !(page & 1))
+      if (orient != Orientation)
       {
        /*
-        * Make sure we have an even number of pages...
+        * Yes, update things so that the pages come out right...
        */
 
-       if (ppd == NULL || ppd->num_filters == 0)
-         fprintf(stderr, "PAGE: %d %d\n", page, slowcollate ? 1 : Copies);
-
-        printf("%%%%Page: %d %d\n", page, page);
-       page ++;
-       ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
-
-       start_nup(NUp - 1, 0, lbrt);
-       puts("showpage");
-        end_nup(NUp - 1);
+       Orientation = (4 - Orientation + orient) & 3;
+       UpdatePageVars();
+       Orientation = orient;
       }
     }
-
-    if (slowcollate || sloworder)
+    else if (!strcmp(line, "%%EndComments"))
     {
-      if (!sloworder)
-      {
-        while (Copies > 1)
-       {
-         for (number = 0, pageinfo = (page_info_t *)cupsArrayFirst(Pages);
-              number < NumPages && pageinfo;
-              number ++, pageinfo = (page_info_t *)cupsArrayNext(Pages))
-         {
-           if (is_first_page(number))
-           {
-             if (ppd == NULL || ppd->num_filters == 0)
-               fprintf(stderr, "PAGE: %d 1\n", page);
-
-              if (NUp == 1)
-               printf("%%%%Page: %s %d\n", pageinfo->label, page);
-              else
-               printf("%%%%Page: %d %d\n", page, page);
-
-              if (pageinfo->input_slot || pageinfo->manual_feed)
-             {
-               if (pageinfo->input_slot)
-                 ppdMarkOption(ppd, "InputSlot", pageinfo->input_slot);
-               if (pageinfo->manual_feed)
-                 ppdMarkOption(ppd, "ManualFeed", pageinfo->manual_feed);
-
-               ppdEmitAfterOrder(ppd, stdout, PPD_ORDER_DOCUMENT, 1, min_order);
-               ppdEmitAfterOrder(ppd, stdout, PPD_ORDER_ANY, 1, min_order);
-              }
-
-             page ++;
-             ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
-           }
-
-           start_nup(number, 1, pageinfo->lbrt);
-           copy_bytes(temp, pageinfo->offset, pageinfo->length);
-           end_nup(number);
-         }
-
-          if (is_not_last_page(NumPages))
-         {
-           start_nup(NUp - 1, 0, lbrt);
-            end_nup(NUp - 1);
-         }
-
-         if (slowduplex && !(page & 1))
-         {
-          /*
-            * Make sure we have an even number of pages...
-           */
-
-           if (ppd == NULL || ppd->num_filters == 0)
-             fprintf(stderr, "PAGE: %d 1\n", page);
-
-            printf("%%%%Page: %d %d\n", page, page);
-           page ++;
-           ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
-
-           start_nup(NUp - 1, 0, lbrt);
-           puts("showpage");
-            end_nup(NUp - 1);
-         }
+      linelen = cupsFileGetLine(fp, line, linesize);
+      break;
+    }
+    else if (strncmp(line, "%!", 2) && strncmp(line, "%cups", 5))
+      doc_printf(doc, "%s\n", line);
 
-         Copies --;
-       }
-      }
-      else
-      {
-        page_count = (NumPages + NUp - 1) / NUp;
-       copy       = 0;
+    if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
+      break;
+  }
 
-        fprintf(stderr, "DEBUG: page_count=%d\n", page_count);
+  if (!saw_bounding_box)
+    fputs(_("ERROR: No %%BoundingBox: comment in header!\n"), stderr);
 
-        do
-       {
-         if (slowduplex && (page_count & 1))
-            basepage = page_count;
-         else
-           basepage = page_count - 1;
+  if (!saw_pages)
+    fputs(_("ERROR: No %%Pages: comment in header!\n"), stderr);
 
-         for (; basepage >= 0; basepage --)
-         {
-           if (ppd == NULL || ppd->num_filters == 0)
-             fprintf(stderr, "PAGE: %d %d\n", page,
-                     slowcollate ? 1 : Copies);
-
-           pageinfo = (page_info_t *)cupsArrayIndex(Pages, basepage);
-
-            if (NUp == 1)
-              printf("%%%%Page: %s %d\n", pageinfo->label, page);
-           else
-              printf("%%%%Page: %d %d\n", page, page);
-
-            if (pageinfo->input_slot || pageinfo->manual_feed)
-           {
-             if (pageinfo->input_slot)
-               ppdMarkOption(ppd, "InputSlot", pageinfo->input_slot);
-             if (pageinfo->manual_feed)
-               ppdMarkOption(ppd, "ManualFeed", pageinfo->manual_feed);
-
-              ppdEmitAfterOrder(ppd, stdout, PPD_ORDER_DOCUMENT, 1, min_order);
-              ppdEmitAfterOrder(ppd, stdout, PPD_ORDER_ANY, 1, min_order);
-            }
-
-           page ++;
-
-           ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
-
-            if (basepage >= page_count)
-           {
-             start_nup(NUp - 1, 0, pageinfo->lbrt);
-             puts("showpage");
-              end_nup(NUp - 1);
-           }
-           else
-           {
-             for (subpage = 0, number = basepage * NUp;
-                  subpage < NUp && number < NumPages;
-                  subpage ++, number ++)
-             {
-               pageinfo = (page_info_t *)cupsArrayIndex(Pages, number);
-
-               start_nup(number, 1, pageinfo->lbrt);
-               copy_bytes(temp, pageinfo->offset, pageinfo->length);
-               end_nup(number);
-             }
-
-              if (is_not_last_page(number))
-             {
-               start_nup(NUp - 1, 0, lbrt);
-               end_nup(NUp - 1);
-             }
-           }
-         }
+  if (!saw_for)
+    WriteTextComment("For", doc->user);
 
-         copy ++;
-       }
-       while (copy < Copies && slowcollate);
-      }
-    }
+  if (!saw_title)
+    WriteTextComment("Title", doc->title);
 
+  if (doc->copies != 1 && (!doc->collate || !doc->slow_collate))
+  {
    /*
-    * Copy the trailer, if any...
+    * Tell the document processor the copy and duplex options
+    * that are required...
     */
 
-    puts("%%Trailer");
-    printf("%%%%Pages: %d\n", page - 1);
-
-    if (UseESPsp)
-      puts("userdict/showpage/ESPshowpage load put\n");
-
-    while (!feof(fp))
-    {
-      len = sizeof(line);
-      if (psgets(line, &len, fp) == NULL)
-        break;
+    doc_printf(doc, "%%%%Requirements: numcopies(%d)%s%s\n", doc->copies,
+               doc->collate ? " collate" : "",
+              Duplex ? " duplex" : "");
 
-      if (!(!strcmp(line, "\004") && len == 1) &&
-          strncmp(line, "%%Pages:", 8) != 0)
-        fwrite(line, 1, len, stdout);
+   /*
+    * Apple uses RBI comments for various non-PPD options...
+    */
 
-      if (!strncmp(line, "%%EOF", 5))
-      {
-        fputs("DEBUG: Saw EOF!\n", stderr);
-        saweof = 1;
-       break;
-      }
-    }
+    doc_printf(doc, "%%RBINumCopies: %d\n", doc->copies);
   }
   else
   {
    /*
-    * No DSC comments - write any page commands and then the rest of the file...
+    * Tell the document processor the duplex option that is required...
     */
 
-    if (slowcollate && Copies > 1)
-      printf("%%%%Pages: %d\n", Copies);
-    else
-      puts("%%Pages: 1");
+    if (Duplex)
+      doc_puts(doc, "%%Requirements: duplex\n");
 
-    if (UseESPsp)
-      puts("userdict/ESPshowpage/showpage load put\n"
-          "userdict/showpage{}put");
+   /*
+    * Apple uses RBI comments for various non-PPD options...
+    */
 
-    puts("%%BeginProlog");
-    WriteLabelProlog(val, PageBottom, PageTop, PageWidth);
-    do_prolog(ppd);
-    puts("%%EndProlog");
+    doc_puts(doc, "%RBINumCopies: 1\n");
+  }
 
-    puts("%%BeginSetup");
-    do_setup(ppd, Copies, Collate, slowcollate, g, b);
-    puts("%%EndSetup");
+  doc_puts(doc, "%%Pages: (atend)\n");
+  doc_puts(doc, "%%BoundingBox: (atend)\n");
+  doc_puts(doc, "%%EndComments\n");
 
-    if (ppd == NULL || ppd->num_filters == 0)
-      fprintf(stderr, "PAGE: 1 %d\n", slowcollate ? 1 : Copies);
+  return (linelen);
+}
 
-    ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
 
-    saweof = 1;
+/*
+ * 'copy_dsc()' - Copy a DSC-conforming document...
+ *
+ * This function expects "line" to be filled with the %!PS-Adobe comment line.
+ */
 
-    while ((nbytes = fread(line, 1, sizeof(line), fp)) > 0)
-    {
-      fwrite(line, 1, nbytes, stdout);
+static void
+copy_dsc(cups_file_t  *fp,             /* I - File to read from */
+         pstops_doc_t *doc,            /* I - Document info */
+         ppd_file_t   *ppd,            /* I - PPD file */
+        char         *line,            /* I - Line buffer */
+        ssize_t      linelen,          /* I - Length of initial line */
+        size_t       linesize)         /* I - Size of line buffer */
+{
+  int          number;                 /* Page number */
+  pstops_page_t        *pageinfo;              /* Page information */
 
-      if (slowcollate)
-       cupsFileWrite(temp, line, nbytes);
-    }
 
-    if (UseESPsp)
-    {
-      WriteLabels(Orientation);
-      puts("ESPshowpage");
-    }
+ /*
+  * Make sure we use ESPshowpage for EPS files...
+  */
 
-    if (slowcollate)
-    {
-      while (Copies > 1)
-      {
-       if (ppd == NULL || ppd->num_filters == 0)
-         fputs("PAGE: 1 1\n", stderr);
+  if (strstr(line, "EPSF"))
+  {
+    doc->use_ESPshowpage = 1;
+    doc->number_up       = 1;
+  }
 
-        ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
-       copy_bytes(temp, 0, 0);
-       Copies --;
+ /*
+  * Start sending the document with any commands needed...
+  */
 
-        if (UseESPsp)
-       {
-         WriteLabels(Orientation);
-          puts("ESPshowpage");
-       }
-      }
-    }
-  }
+  fprintf(stderr, "DEBUG: Before copy_comments - %s", line);
+  linelen = copy_comments(fp, doc, ppd, line, linelen, linesize);
 
  /*
-  * Send %%EOF if needed...
+  * Now find the prolog section, if any...
   */
 
-  if (!saweof)
-    puts("%%EOF");
+  fprintf(stderr, "DEBUG: Before copy_prolog - %s", line);
+  linelen = copy_prolog(fp, doc, ppd, line, linelen, linesize);
 
  /*
-  * End the job with the appropriate JCL command or CTRL-D otherwise.
+  * Then the document setup section...
   */
 
-  if (emit_jcl)
+  fprintf(stderr, "DEBUG: Before copy_setup - %s", line);
+  linelen = copy_setup(fp, doc, ppd, line, linelen, linesize);
+
+ /*
+  * Copy until we see %%Page:...
+  */
+
+  while (strncmp(line, "%%Page:", 7) && strncmp(line, "%%Trailer", 9))
   {
-    if (ppd && ppd->jcl_end)
-      ppdEmitJCLEnd(ppd, stdout);
+    doc_write(doc, line, linelen);
+
+    if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
+      break;
+  }
+
+ /*
+  * Then process pages until we have no more...
+  */
+
+  number = 0;
+
+  fprintf(stderr, "DEBUG: Before page loop - %s", line);
+  while (!strncmp(line, "%%Page:", 7))
+  {
+    if (JobCanceled)
+      break;
+
+    number ++;
+
+    if (check_range(doc, (number - 1) / doc->number_up + 1))
+    {
+      fprintf(stderr, "DEBUG: Copying page %d...\n", number);
+      linelen = copy_page(fp, doc, ppd, number, line, linelen, linesize);
+    }
     else
-      putchar(0x04);
+    {
+      fprintf(stderr, "DEBUG: Skipping page %d...\n", number);
+      linelen = skip_page(fp, line, linelen, linesize);
+    }
   }
 
  /*
-  * Close files and remove the temporary file if needed...
+  * Finish up the last page(s)...
   */
 
-  if (slowcollate || sloworder)
+  if (number && is_not_last_page(number) && cupsArrayLast(doc->pages) &&
+      check_range(doc, (number - 1) / doc->number_up + 1))
   {
-    cupsFileClose(temp);
-    unlink(tempfile);
+    pageinfo = (pstops_page_t *)cupsArrayLast(doc->pages);
+
+    start_nup(doc, doc->number_up, 0, doc->bounding_box);
+    doc_puts(doc, "showpage\n");
+    end_nup(doc, doc->number_up);
+
+    pageinfo->length = cupsFileTell(doc->temp) - pageinfo->offset;
   }
 
-  ppdClose(ppd);
+  if (doc->slow_duplex && (doc->page & 1))
+  {
+   /*
+    * Make sure we have an even number of pages...
+    */
 
-  if (fp != stdin)
-    fclose(fp);
+    pageinfo = add_page(doc, "(filler)");
 
-  return (0);
-}
+    if (!doc->slow_order)
+    {
+      if (!ppd || !ppd->num_filters)
+       fprintf(stderr, "PAGE: %d %d\n", doc->page,
+               doc->slow_collate ? 1 : doc->copies);
 
+      printf("%%%%Page: (filler) %d\n", doc->page);
+    }
 
-/*
- * 'check_range()' - Check to see if the current page is selected for
- *                   printing.
- */
+    start_nup(doc, doc->number_up, 0, doc->bounding_box);
+    doc_puts(doc, "showpage\n");
+    end_nup(doc, doc->number_up);
 
-static int             /* O - 1 if selected, 0 otherwise */
-check_range(int page)  /* I - Page number */
-{
-  const char   *range;         /* Pointer into range string */
-  int          lower, upper;   /* Lower and upper page numbers */
+    pageinfo->length = cupsFileTell(doc->temp) - pageinfo->offset;
+  }
 
+ /*
+  * Make additional copies as necessary...
+  */
 
-  if (PageSet != NULL)
+  number = doc->slow_order ? 0 : doc->page;
+
+  if (doc->temp && !JobCanceled && cupsArrayCount(doc->pages) > 0)
   {
+    int        copy;                           /* Current copy */
+
+
    /*
-    * See if we only print even or odd pages...
+    * Reopen the temporary file for reading...
     */
 
-    if (!strcasecmp(PageSet, "even") && ((page - 1) % (NUp << 1)) <  NUp)
-      return (0);
-    if (!strcasecmp(PageSet, "odd") && ((page - 1) % (NUp << 1)) >= NUp)
-      return (0);
-  }
+    cupsFileClose(doc->temp);
 
-  if (PageRanges == NULL)
-    return (1);                /* No range, print all pages... */
+    doc->temp = cupsFileOpen(doc->tempfile, "r");
 
-  for (range = PageRanges; *range != '\0';)
-  {
-    if (*range == '-')
-    {
-      lower = 1;
-      range ++;
-      upper = strtol(range, (char **)&range, 10);
-    }
+   /*
+    * Make the copies...
+    */
+
+    if (doc->slow_collate)
+      copy = !doc->slow_order;
     else
+      copy = doc->copies - 1;
+
+    for (; copy < doc->copies; copy ++)
     {
-      lower = strtol(range, (char **)&range, 10);
+      if (JobCanceled)
+       break;
 
-      if (*range == '-')
+     /*
+      * Send end-of-job stuff followed by any start-of-job stuff required
+      * for the JCL options...
+      */
+
+      if (number && doc->emit_jcl && ppd && ppd->jcl_end)
       {
-        range ++;
-       if (!isdigit(*range & 255))
-         upper = 65535;
+       /*
+        * Send the trailer...
+       */
+
+        puts("%%Trailer");
+       printf("%%%%Pages: %d\n", cupsArrayCount(doc->pages));
+       if (doc->number_up > 1 || doc->fitplot)
+         printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
+                PageLeft, PageBottom, PageRight, PageTop);
        else
-         upper = strtol(range, (char **)&range, 10);
+         printf("%%%%BoundingBox: %d %d %d %d\n",
+                doc->new_bounding_box[0], doc->new_bounding_box[1],
+                doc->new_bounding_box[2], doc->new_bounding_box[3]);
+        puts("%%EOF");
+
+       /*
+        * Start a new document...
+       */
+
+        ppdEmitJCLEnd(ppd, stdout);
+        ppdEmitJCL(ppd, stdout, doc->job_id, doc->user, doc->title);
+
+       puts("%!PS-Adobe-3.0");
+
+       number = 0;
       }
-      else
-        upper = lower;
-    }
 
-    if (page >= lower && page <= upper)
-      return (1);
+     /*
+      * Copy the prolog as needed...
+      */
 
-    if (*range == ',')
-      range ++;
-    else
-      break;
+      if (!number)
+      {
+        pageinfo = (pstops_page_t *)cupsArrayFirst(doc->pages);
+       copy_bytes(doc->temp, 0, pageinfo->offset);
+      }
+
+     /*
+      * Then copy all of the pages...
+      */
+
+      pageinfo = doc->slow_order ? (pstops_page_t *)cupsArrayLast(doc->pages) :
+                                   (pstops_page_t *)cupsArrayFirst(doc->pages);
+
+      while (pageinfo)
+      {
+        if (JobCanceled)
+         break;
+
+        number ++;
+
+       if (!ppd || !ppd->num_filters)
+         fprintf(stderr, "PAGE: %d %d\n", number,
+                 doc->slow_collate ? 1 : doc->copies);
+
+       if (doc->number_up > 1)
+       {
+         printf("%%%%Page: (%d) %d\n", number, number);
+         printf("%%%%PageBoundingBox: %.0f %.0f %.0f %.0f\n",
+                PageLeft, PageBottom, PageRight, PageTop);
+       }
+       else
+       {
+          printf("%%%%Page: %s %d\n", pageinfo->label, number);
+         printf("%%%%PageBoundingBox: %d %d %d %d\n",
+                pageinfo->bounding_box[0], pageinfo->bounding_box[1],
+                pageinfo->bounding_box[2], pageinfo->bounding_box[3]);
+       }
+
+       copy_bytes(doc->temp, pageinfo->offset, pageinfo->length);
+
+       pageinfo = doc->slow_order ? (pstops_page_t *)cupsArrayPrev(doc->pages) :
+                                     (pstops_page_t *)cupsArrayNext(doc->pages);
+      }
+    }
   }
 
-  return (0);
+ /*
+  * Restore the old showpage operator as needed...
+  */
+
+  if (doc->use_ESPshowpage)
+    puts("userdict/showpage/ESPshowpage load put\n");
+
+ /*
+  * Write/copy the trailer...
+  */
+
+  if (!JobCanceled)
+    linelen = copy_trailer(fp, doc, ppd, number, line, linelen, linesize);
 }
 
 
 /*
- * 'add_page()' - Add a page to the Pages array...
+ * 'copy_non_dsc()' - Copy a document that does not conform to the DSC...
+ *
+ * This function expects "line" to be filled with the %! comment line.
  */
 
-static page_info_t *                   /* O - New page info object */
-add_page(const char *label,            /* I - Page label */
-         off_t      offset,            /* I - Offset in file */
-        const int  *lbrt)              /* I - BoundingBox for page */
+static void
+copy_non_dsc(cups_file_t  *fp,         /* I - File to read from */
+             pstops_doc_t *doc,                /* I - Document info */
+             ppd_file_t   *ppd,                /* I - PPD file */
+            char         *line,        /* I - Line buffer */
+            ssize_t      linelen,      /* I - Length of initial line */
+            size_t       linesize)     /* I - Size of line buffer */
 {
-  page_info_t  *pageinfo;              /* New page info object */
+  int  copy;                           /* Current copy */
+  char buffer[8192];                   /* Copy buffer */
+  int  bytes;                          /* Number of bytes copied */
+
+
+ /*
+  * First let the user know that they are attempting to print a file
+  * that may not print correctly...
+  */
+
+  fputs(_("WARNING: This document does not conform to the Adobe Document "
+          "Structuring Conventions and may not print correctly!\n"), stderr);
+
+ /*
+  * Then write a standard DSC comment section...
+  */
 
+  printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n", PageLeft, PageBottom,
+         PageRight, PageTop);
 
-  if (!Pages)
-    Pages = cupsArrayNew(NULL, NULL);
+  if (doc->slow_collate && doc->copies > 1)
+    printf("%%%%Pages: %d\n", doc->copies);
+  else
+    puts("%%Pages: 1");
+
+  WriteTextComment("For", doc->user);
+  WriteTextComment("Title", doc->title);
 
-  if (!Pages)
+  if (doc->copies != 1 && (!doc->collate || !doc->slow_collate))
   {
-    fprintf(stderr, "EMERG: Unable to allocate memory for pages array: %s\n",
-            strerror(errno));
-    exit(1);
-  }
+   /*
+    * Tell the document processor the copy and duplex options
+    * that are required...
+    */
+
+    printf("%%%%Requirements: numcopies(%d)%s%s\n", doc->copies,
+           doc->collate ? " collate" : "",
+          Duplex ? " duplex" : "");
+
+   /*
+    * Apple uses RBI comments for various non-PPD options...
+    */
 
-  if ((pageinfo = calloc(1, sizeof(page_info_t))) == NULL)
+    printf("%%RBINumCopies: %d\n", doc->copies);
+  }
+  else
   {
-    fprintf(stderr, "EMERG: Unable to allocate memory for page info: %s\n",
-            strerror(errno));
-    exit(1);
+   /*
+    * Tell the document processor the duplex option that is required...
+    */
+
+    if (Duplex)
+      puts("%%Requirements: duplex");
+
+   /*
+    * Apple uses RBI comments for various non-PPD options...
+    */
+
+    puts("%RBINumCopies: 1");
   }
 
-  pageinfo->label  = strdup(label);
-  pageinfo->offset = offset;
+  puts("%%EndComments");
 
-  memcpy(pageinfo->lbrt, lbrt, sizeof(pageinfo->lbrt));
+ /*
+  * Then the prolog...
+  */
 
-  cupsArrayAdd(Pages, pageinfo);
+  puts("%%BeginProlog");
 
-  return (pageinfo);
-}
+  do_prolog(doc, ppd);
 
+  puts("%%EndProlog");
 
-/*
* 'copy_bytes()' - Copy bytes from the input file to stdout...
- */
+ /*
 * Then the setup section...
 */
 
-static void
-copy_bytes(cups_file_t *fp,            /* I - File to read from */
-           off_t       offset,         /* I - Offset to page data */
-           size_t      length)         /* I - Length of page data */
-{
-  char         buffer[8192];           /* Data buffer */
-  ssize_t      nbytes;                 /* Number of bytes read */
-  size_t       nleft;                  /* Number of bytes left/remaining */
+  puts("%%BeginSetup");
 
+  do_setup(doc, ppd);
 
-  nleft = length;
+  puts("%%EndSetup");
+
+ /*
+  * Finally, embed a copy of the file inside a %%Page...
+  */
 
-  cupsFileSeek(fp, offset);
+  if (!ppd || !ppd->num_filters)
+    fprintf(stderr, "PAGE: 1 %d\n", doc->temp ? 1 : doc->copies);
 
-  while (nleft > 0 || length == 0)
+  puts("%%Page: 1 1");
+  puts("%%BeginPageSetup");
+  ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
+  puts("%%EndPageSetup");
+  puts("%%BeginDocument: nondsc");
+
+  fwrite(line, linelen, 1, stdout);
+
+  if (doc->temp)
+    cupsFileWrite(doc->temp, line, linelen);
+
+  while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
   {
-    if (nleft > sizeof(buffer) || length == 0)
-      nbytes = sizeof(buffer);
-    else
-      nbytes = nleft;
+    fwrite(buffer, 1, bytes, stdout);
 
-    if ((nbytes = cupsFileRead(fp, buffer, nbytes)) < 1)
-      return;
+    if (doc->temp)
+      cupsFileWrite(doc->temp, buffer, bytes);
+  }
 
-    nleft -= nbytes;
+  puts("%%EndDocument");
 
-    fwrite(buffer, 1, nbytes, stdout);
+  if (doc->use_ESPshowpage)
+  {
+    WriteLabels(Orientation);
+    puts("ESPshowpage");
+  }
+
+  if (doc->temp && !JobCanceled)
+  {
+   /*
+    * Reopen the temporary file for reading...
+    */
+
+    cupsFileClose(doc->temp);
+
+    doc->temp = cupsFileOpen(doc->tempfile, "r");
+
+   /*
+    * Make the additional copies as needed...
+    */
+
+    for (copy = 1; copy < doc->copies; copy ++)
+    {
+      if (JobCanceled)
+       break;
+
+      if (!ppd || !ppd->num_filters)
+       fputs("PAGE: 1 1\n", stderr);
+
+      printf("%%%%Page: %d %d\n", copy + 1, copy + 1);
+      puts("%%BeginPageSetup");
+      ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
+      puts("%%EndPageSetup");
+      puts("%%BeginDocument: nondsc");
+
+      copy_bytes(doc->temp, 0, 0);
+
+      puts("%%EndDocument");
+
+      if (doc->use_ESPshowpage)
+      {
+       WriteLabels(Orientation);
+        puts("ESPshowpage");
+      }
+    }
   }
+
+ /*
+  * Restore the old showpage operator as needed...
+  */
+
+  if (doc->use_ESPshowpage)
+    puts("userdict/showpage/ESPshowpage load put\n");
 }
 
 
 /*
- * 'do_prolog()' - Send the necessary document prolog commands...
+ * 'copy_page()' - Copy a page description...
+ *
+ * This function expects "line" to be filled with a %%Page comment line.
+ * On return, "line" will contain the next line in the file, if any.
  */
 
-static void
-do_prolog(ppd_file_t *ppd)             /* I - PPD file */
+static ssize_t                         /* O - Length of next line */
+copy_page(cups_file_t  *fp,            /* I - File to read from */
+          pstops_doc_t *doc,           /* I - Document info */
+          ppd_file_t   *ppd,           /* I - PPD file */
+         int          number,          /* I - Current page number */
+         char         *line,           /* I - Line buffer */
+         ssize_t      linelen,         /* I - Length of initial line */
+         size_t       linesize)        /* I - Size of line buffer */
 {
+  char         label[256],             /* Page label string */
+               *ptr;                   /* Pointer into line */
+  int          level;                  /* Embedded document level */
+  pstops_page_t        *pageinfo;              /* Page information */
+  int          first_page;             /* First page on N-up output? */
+  int          bounding_box[4];        /* PageBoundingBox */
+
+
  /*
-  * Send the document prolog commands...
+  * Get the page label for this page...
   */
 
-  if (ppd && ppd->patches)
+  first_page = is_first_page(number);
+
+  if (!parse_text(line + 7, &ptr, label, sizeof(label)))
   {
-    puts("%%BeginFeature: *JobPatchFile 1");
-    puts(ppd->patches);
-    puts("%%EndFeature");
+    fputs(_("ERROR: Bad %%Page: comment in file!\n"), stderr);
+    label[0] = '\0';
+    number   = doc->page;
+  }
+  else if (strtol(ptr, &ptr, 10) == LONG_MAX || !isspace(*ptr & 255))
+  {
+    fputs(_("ERROR: Bad %%Page: comment in file!\n"), stderr);
+    number = doc->page;
   }
 
-  ppdEmit(ppd, stdout, PPD_ORDER_PROLOG);
-}
+ /*
+  * Create or update the current output page...
+  */
 
+  if (first_page)
+    pageinfo = add_page(doc, label);
+  else
+    pageinfo = (pstops_page_t *)cupsArrayLast(doc->pages);
 
-/*
- * 'do_setup()' - Send the necessary document setup commands...
- */
+ /*
+  * Handle first page override...
+  */
+
+  if (doc->ap_input_slot || doc->ap_manual_feed)
+  {
+    if (doc->page == 1)
+    {
+     /*
+      * First page/sheet gets AP_FIRSTPAGE_* options...
+      */
+
+      pageinfo->num_options = cupsAddOption("InputSlot", doc->ap_input_slot,
+                                            pageinfo->num_options,
+                                           &(pageinfo->options));
+      pageinfo->num_options = cupsAddOption("ManualFeed",
+                                            doc->ap_input_slot ? "False" :
+                                               doc->ap_manual_feed,
+                                            pageinfo->num_options,
+                                           &(pageinfo->options));
+    }
+    else if (doc->page == (Duplex + 2))
+    {
+     /*
+      * Second page/sheet gets default options...
+      */
+
+      pageinfo->num_options = cupsAddOption("InputSlot", doc->input_slot,
+                                            pageinfo->num_options,
+                                           &(pageinfo->options));
+      pageinfo->num_options = cupsAddOption("ManualFeed",
+                                            doc->input_slot ? "False" :
+                                               doc->manual_feed,
+                                            pageinfo->num_options,
+                                           &(pageinfo->options));
+    }
+  }
+
+ /*
+  * Scan comments until we see something other than %%Page*: or
+  * %%Include*...
+  */
+
+  memcpy(bounding_box, doc->bounding_box, sizeof(bounding_box));
+
+  while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0)
+  {
+    if (!strncmp(line, "%%PageBoundingBox:", 18))
+    {
+     /*
+      * %%PageBoundingBox: llx lly urx ury
+      */
+
+      if (sscanf(line + 18, "%d%d%d%d", bounding_box + 0,
+                 bounding_box + 1, bounding_box + 2,
+                bounding_box + 3) != 4)
+      {
+        fputs(_("ERROR: Bad %%PageBoundingBox: comment in file!\n"), stderr);
+        memcpy(bounding_box, doc->bounding_box,
+              sizeof(bounding_box));
+      }
+      else if (doc->number_up == 1 && !doc->fitplot  && Orientation)
+      {
+        int    temp_bbox[4];           /* Temporary bounding box */
+
+
+        memcpy(temp_bbox, bounding_box, sizeof(temp_bbox));
+
+        fprintf(stderr, "DEBUG: Orientation = %d\n", Orientation);
+        fprintf(stderr, "DEBUG: original bounding_box = [ %d %d %d %d ]\n",
+               bounding_box[0], bounding_box[1],
+               bounding_box[2], bounding_box[3]);
+        fprintf(stderr, "DEBUG: PageWidth = %.1f, PageLength = %.1f\n",
+               PageWidth, PageLength);
+
+        switch (Orientation)
+       {
+         case 1 : /* Landscape */
+             bounding_box[0] = PageLength - temp_bbox[3];
+             bounding_box[1] = temp_bbox[0];
+             bounding_box[2] = PageLength - temp_bbox[1];
+             bounding_box[3] = temp_bbox[2];
+              break;
+
+         case 2 : /* Reverse Portrait */
+             bounding_box[0] = PageWidth - temp_bbox[2];
+             bounding_box[1] = PageLength - temp_bbox[3];
+             bounding_box[2] = PageWidth - temp_bbox[0];
+             bounding_box[3] = PageLength - temp_bbox[1];
+              break;
+
+         case 3 : /* Reverse Landscape */
+             bounding_box[0] = temp_bbox[1];
+             bounding_box[1] = PageWidth - temp_bbox[2];
+             bounding_box[2] = temp_bbox[3];
+             bounding_box[3] = PageWidth - temp_bbox[0];
+              break;
+       }
+
+        fprintf(stderr, "DEBUG: updated bounding_box = [ %d %d %d %d ]\n",
+               bounding_box[0], bounding_box[1],
+               bounding_box[2], bounding_box[3]);
+      }
+    }
+#if 0
+    else if (!strncmp(line, "%%PageCustomColors:", 19) ||
+             !strncmp(line, "%%PageMedia:", 12) ||
+            !strncmp(line, "%%PageOrientation:", 18) ||
+            !strncmp(line, "%%PageProcessColors:", 20) ||
+            !strncmp(line, "%%PageRequirements:", 18) ||
+            !strncmp(line, "%%PageResources:", 16))
+    {
+     /*
+      * Copy literal...
+      */
+    }
+#endif /* 0 */
+    else if (!strncmp(line, "%%PageCustomColors:", 19))
+    {
+     /*
+      * %%PageCustomColors: ...
+      */
+    }
+    else if (!strncmp(line, "%%PageMedia:", 12))
+    {
+     /*
+      * %%PageMedia: ...
+      */
+    }
+    else if (!strncmp(line, "%%PageOrientation:", 18))
+    {
+     /*
+      * %%PageOrientation: ...
+      */
+    }
+    else if (!strncmp(line, "%%PageProcessColors:", 20))
+    {
+     /*
+      * %%PageProcessColors: ...
+      */
+    }
+    else if (!strncmp(line, "%%PageRequirements:", 18))
+    {
+     /*
+      * %%PageRequirements: ...
+      */
+    }
+    else if (!strncmp(line, "%%PageResources:", 16))
+    {
+     /*
+      * %%PageResources: ...
+      */
+    }
+    else if (!strncmp(line, "%%IncludeFeature:", 17))
+    {
+     /*
+      * %%IncludeFeature: *MainKeyword OptionKeyword
+      */
+
+      if (doc->number_up == 1 &&!doc->fitplot)
+       pageinfo->num_options = include_feature(ppd, line,
+                                               pageinfo->num_options,
+                                               &(pageinfo->options));
+    }
+    else if (strncmp(line, "%%Include", 9))
+      break;
+  }
+
+  if (doc->number_up == 1)
+  {
+   /*
+    * Update the document's composite and page bounding box...
+    */
+
+    memcpy(pageinfo->bounding_box, bounding_box,
+           sizeof(pageinfo->bounding_box));
+
+    if (bounding_box[0] < doc->new_bounding_box[0])
+      doc->new_bounding_box[0] = bounding_box[0];
+    if (bounding_box[1] < doc->new_bounding_box[1])
+      doc->new_bounding_box[1] = bounding_box[1];
+    if (bounding_box[2] > doc->new_bounding_box[2])
+      doc->new_bounding_box[2] = bounding_box[2];
+    if (bounding_box[3] > doc->new_bounding_box[3])
+      doc->new_bounding_box[3] = bounding_box[3];
+  }
+
+ /*
+  * Output the page header as needed...
+  */
+
+  if (!doc->slow_order && first_page)
+  {
+    if (!ppd || !ppd->num_filters)
+      fprintf(stderr, "PAGE: %d %d\n", doc->page,
+             doc->slow_collate ? 1 : doc->copies);
+
+    if (doc->number_up > 1)
+    {
+      printf("%%%%Page: (%d) %d\n", doc->page, doc->page);
+      printf("%%%%PageBoundingBox: %.0f %.0f %.0f %.0f\n",
+            PageLeft, PageBottom, PageRight, PageTop);
+    }
+    else
+    {
+      printf("%%%%Page: %s %d\n", pageinfo->label, doc->page);
+      printf("%%%%PageBoundingBox: %d %d %d %d\n",
+            pageinfo->bounding_box[0], pageinfo->bounding_box[1],
+            pageinfo->bounding_box[2], pageinfo->bounding_box[3]);
+    }
+  }
+
+ /*
+  * Copy any page setup commands...
+  */
+
+  if (first_page)
+  {
+    char       *page_setup;            /* PageSetup commands to send */
+
+
+    doc_puts(doc, "%%BeginPageSetup\n");
+
+    if (pageinfo->num_options > 0)
+    {
+      int              i;              /* Looping var */
+      ppd_option_t     *option;        /* PPD option */
+      int              min_order;      /* Minimum OrderDependency value */
+      char             *doc_setup,     /* DocumentSetup commands to send */
+                       *any_setup;     /* AnySetup commands to send */
+
+
+     /*
+      * Yes, figure out the minimum OrderDependency value...
+      */
+
+      if ((option = ppdFindOption(ppd, "PageRegion")) != NULL)
+       min_order = option->order;
+      else
+       min_order = 999.0f;
+
+      for (i = 0; i < pageinfo->num_options; i ++)
+       if ((option = ppdFindOption(ppd, pageinfo->options[i].name)) != NULL &&
+            option->order < min_order)
+          min_order = option->order;
+
+     /*
+      * Mark and extract them...
+      */
+
+      cupsMarkOptions(ppd, pageinfo->num_options, pageinfo->options);
+
+      doc_setup = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, min_order);
+      any_setup = ppdEmitString(ppd, PPD_ORDER_ANY, min_order);
+
+     /*
+      * Then send them out...
+      */
+
+      if (doc_setup)
+      {
+       doc_puts(doc, doc_setup);
+       free(doc_setup);
+      }
+
+      if (any_setup)
+      {
+       doc_puts(doc, any_setup);
+       free(any_setup);
+      }
+    }
+
+   /*
+    * Output commands for the current page...
+    */
+
+    page_setup = ppdEmitString(ppd, PPD_ORDER_PAGE, 0);
+
+    if (page_setup)
+    {
+      doc_puts(doc, page_setup);
+      free(page_setup);
+    }
+  }
+
+ /*
+  * Prep for the start of the page description...
+  */
+
+  start_nup(doc, number, 1, bounding_box);
+
+ /*
+  * Copy page setup commands as needed...
+  */
+
+  if (!strncmp(line, "%%BeginPageSetup", 16))
+  {
+    int        feature = 0;                    /* In a Begin/EndFeature block? */
+
+
+    while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0)
+    {
+      if (!strncmp(line, "%%EndPageSetup", 14))
+        break;
+      else if (!strncmp(line, "%%BeginFeature:", 15))
+      {
+        feature = 1;
+
+       if (doc->number_up > 1 || doc->fitplot)
+         continue;
+      }
+      else if (!strncmp(line, "%%EndFeature", 12))
+      {
+        feature = 0;
+
+       if (doc->number_up > 1 || doc->fitplot)
+         continue;
+      }
+      else if (!strncmp(line, "%%Include", 9))
+        continue;
+
+      if (!feature || (doc->number_up == 1 && !doc->fitplot))
+       doc_write(doc, line, linelen);
+    }
+
+   /*
+    * Skip %%EndPageSetup...
+    */
+
+    if (linelen > 0)
+      linelen = cupsFileGetLine(fp, line, linesize);
+  }
+
+ /*
+  * Finish the PageSetup section as needed...
+  */
+
+  if (first_page)
+    doc_puts(doc, "%%EndPageSetup\n");
+
+ /*
+  * Read the rest of the page description...
+  */
+
+  level = 0;
+
+  do
+  {
+    if (level == 0 &&
+        (!strncmp(line, "%%Page:", 7) ||
+        !strncmp(line, "%%Trailer", 9) ||
+        !strncmp(line, "%%EOF", 5)))
+      break;
+    else if (!strncmp(line, "%%BeginDocument", 15) ||
+            !strncmp(line, "%ADO_BeginApplication", 21))
+    {
+      doc_write(doc, line, linelen);
+
+      level ++;
+    }
+    else if ((!strncmp(line, "%%EndDocument", 13) ||
+             !strncmp(line, "%ADO_EndApplication", 19)) && level > 0)
+    {
+      doc_write(doc, line, linelen);
+
+      level --;
+    }
+    else if (!strncmp(line, "%%BeginBinary:", 14) ||
+             (!strncmp(line, "%%BeginData:", 12) &&
+             !strstr(line, "ASCII") && !strstr(line, "Hex")))
+    {
+     /*
+      * Copy binary data...
+      */
+
+      int      bytes;                  /* Bytes of data */
+
+
+      doc_write(doc, line, linelen);
+
+      bytes = atoi(strchr(line, ':') + 1);
+
+      while (bytes > 0)
+      {
+       if (bytes > linesize)
+         linelen = cupsFileRead(fp, line, linesize);
+       else
+         linelen = cupsFileRead(fp, line, bytes);
+
+       if (linelen < 1)
+       {
+         line[0] = '\0';
+         perror("ERROR: Early end-of-file while reading binary data");
+         return (0);
+       }
+
+        doc_write(doc, line, linelen);
+
+       bytes -= linelen;
+      }
+    }
+    else
+      doc_write(doc, line, linelen);
+  }
+  while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0);
+
+ /*
+  * Finish up this page and return...
+  */
+
+  end_nup(doc, number);
+
+  pageinfo->length = cupsFileTell(doc->temp) - pageinfo->offset;
+
+  return (linelen);
+}
+
+
+/*
+ * 'copy_prolog()' - Copy the document prolog section...
+ *
+ * This function expects "line" to be filled with a %%BeginProlog comment line.
+ * On return, "line" will contain the next line in the file, if any.
+ */
+
+static ssize_t                         /* O - Length of next line */
+copy_prolog(cups_file_t  *fp,          /* I - File to read from */
+            pstops_doc_t *doc,         /* I - Document info */
+            ppd_file_t   *ppd,         /* I - PPD file */
+           char         *line,         /* I - Line buffer */
+           ssize_t      linelen,       /* I - Length of initial line */
+           size_t       linesize)      /* I - Size of line buffer */
+{
+  while (strncmp(line, "%%BeginProlog", 13))
+  {
+    if (!strncmp(line, "%%BeginSetup", 12) || !strncmp(line, "%%Page:", 7))
+      break;
+
+    doc_write(doc, line, linelen);
+
+    if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
+      break;
+  }
+
+  doc_puts(doc, "%%BeginProlog\n");
+
+  do_prolog(doc, ppd);
+
+  if (!strncmp(line, "%%BeginProlog", 13))
+  {
+    while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0)
+    {
+      if (!strncmp(line, "%%EndProlog", 11) ||
+          !strncmp(line, "%%BeginSetup", 12) ||
+          !strncmp(line, "%%Page:", 7))
+        break;
+
+      doc_write(doc, line, linelen);
+    }
+
+    if (!strncmp(line, "%%EndProlog", 11))
+      linelen = cupsFileGetLine(fp, line, linesize);
+    else
+      fputs(_("ERROR: Missing %%EndProlog!\n"), stderr);
+  }
+
+  doc_puts(doc, "%%EndProlog\n");
+
+  return (linelen);
+}
+
+
+/*
+ * 'copy_setup()' - Copy the document setup section...
+ *
+ * This function expects "line" to be filled with a %%BeginSetup comment line.
+ * On return, "line" will contain the next line in the file, if any.
+ */
+
+static ssize_t                         /* O - Length of next line */
+copy_setup(cups_file_t  *fp,           /* I - File to read from */
+           pstops_doc_t *doc,          /* I - Document info */
+           ppd_file_t   *ppd,          /* I - PPD file */
+          char         *line,          /* I - Line buffer */
+          ssize_t      linelen,        /* I - Length of initial line */
+          size_t       linesize)       /* I - Size of line buffer */
+{
+  while (strncmp(line, "%%BeginSetup", 12))
+  {
+    if (!strncmp(line, "%%Page:", 7))
+      break;
+
+    doc_write(doc, line, linelen);
+
+    if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
+      break;
+  }
+
+  doc_puts(doc, "%%BeginSetup\n");
+  
+  do_setup(doc, ppd);
+
+  if (!strncmp(line, "%%BeginSetup", 12))
+  {
+    while (strncmp(line, "%%EndSetup", 10))
+    {
+      if (!strncmp(line, "%%Page:", 7))
+        break;
+      else if (!strncmp(line, "%%IncludeFeature:", 17))
+      {
+       /*
+       * %%IncludeFeature: *MainKeyword OptionKeyword
+       */
+
+        if (doc->number_up == 1 && !doc->fitplot)
+         doc->num_options = include_feature(ppd, line, doc->num_options,
+                                             &(doc->options));
+      }
+      else if (strncmp(line, "%%BeginSetup", 12))
+        doc_write(doc, line, linelen);
+
+      if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
+       break;
+    }
+
+    if (!strncmp(line, "%%EndSetup", 10))
+      linelen = cupsFileGetLine(fp, line, linesize);
+    else
+      fputs(_("ERROR: Missing %%EndSetup!\n"), stderr);
+  }
+
+  doc_puts(doc, "%%EndSetup\n");
+
+  return (linelen);
+}
+
+
+/*
+ * 'copy_trailer()' - Copy the document trailer...
+ *
+ * This function expects "line" to be filled with a %%Trailer comment line.
+ * On return, "line" will contain the next line in the file, if any.
+ */
+
+static ssize_t                         /* O - Length of next line */
+copy_trailer(cups_file_t  *fp,         /* I - File to read from */
+             pstops_doc_t *doc,                /* I - Document info */
+             ppd_file_t   *ppd,                /* I - PPD file */
+            int          number,       /* I - Number of pages */
+            char         *line,        /* I - Line buffer */
+            ssize_t      linelen,      /* I - Length of initial line */
+            size_t       linesize)     /* I - Size of line buffer */
+{
+ /*
+  * Write the trailer comments...
+  */
+
+  puts("%%Trailer");
+
+  while (linelen > 0)
+  {
+    if (!strncmp(line, "%%EOF", 5))
+      break;
+    else if (strncmp(line, "%%Trailer", 9) &&
+             strncmp(line, "%%Pages:", 8) &&
+             strncmp(line, "%%BoundingBox:", 14))
+      fwrite(line, 1, linelen, stdout);
+
+    linelen = cupsFileGetLine(fp, line, linesize);
+  }
+
+  fprintf(stderr, "DEBUG: Wrote %d pages...\n", number);
+
+  printf("%%%%Pages: %d\n", number);
+  if (doc->number_up > 1 || doc->fitplot)
+    printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
+          PageLeft, PageBottom, PageRight, PageTop);
+  else
+    printf("%%%%BoundingBox: %d %d %d %d\n",
+          doc->new_bounding_box[0], doc->new_bounding_box[1],
+          doc->new_bounding_box[2], doc->new_bounding_box[3]);
+
+  return (linelen);
+}
+
+
+/*
+ * 'do_prolog()' - Send the necessary document prolog commands...
+ */
+
+static void
+do_prolog(pstops_doc_t *doc,           /* I - Document information */
+          ppd_file_t   *ppd)           /* I - PPD file */
+{
+  char *ps;                            /* PS commands */
+
+
+ /*
+  * Send the document prolog commands...
+  */
+
+  if (ppd && ppd->patches)
+  {
+    doc_puts(doc, "%%BeginFeature: *JobPatchFile 1\n");
+    doc_puts(doc, ppd->patches);
+    doc_puts(doc, "\n%%EndFeature\n");
+  }
+
+  if ((ps = ppdEmitString(ppd, PPD_ORDER_PROLOG, 0.0)) != NULL)
+  {
+    doc_puts(doc, ps);
+    free(ps);
+  }
+
+ /*
+  * Define ESPshowpage here so that applications that define their
+  * own procedure to do a showpage pick it up...
+  */
+
+  if (doc->use_ESPshowpage)
+    doc_puts(doc, "userdict/ESPshowpage/showpage load put\n"
+                 "userdict/showpage{}put\n");
+}
+
+
+/*
+ * 'do_setup()' - Send the necessary document setup commands...
+ */
+
+static void
+do_setup(pstops_doc_t *doc,            /* I - Document information */
+         ppd_file_t   *ppd)            /* I - PPD file */
+{
+  char *ps;                            /* PS commands */
+
+
+ /*
+  * Disable CTRL-D so that embedded files don't cause printing
+  * errors...
+  */
+
+  doc_puts(doc, "% Disable CTRL-D as an end-of-file marker...\n");
+  doc_puts(doc, "userdict dup(\\004)cvn{}put (\\004\\004)cvn{}put\n");
+
+ /*
+  * Mark any options from %%IncludeFeature: comments...
+  */
+
+  cupsMarkOptions(ppd, doc->num_options, doc->options);
+
+ /*
+  * Send all the printer-specific setup commands...
+  */
+
+  if ((ps = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, 0.0)) != NULL)
+  {
+    doc_puts(doc, ps);
+    free(ps);
+  }
+
+  if ((ps = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL)
+  {
+    doc_puts(doc, ps);
+    free(ps);
+  }
+
+ /*
+  * Set the number of copies for the job...
+  */
+
+  if (doc->copies != 1 && (!doc->collate || !doc->slow_collate))
+  {
+    doc_printf(doc, "%%RBIBeginNonPPDFeature: *NumCopies %d\n", doc->copies);
+    doc_printf(doc,
+               "%d/languagelevel where{pop languagelevel 2 ge}{false}ifelse\n"
+               "{1 dict begin/NumCopies exch def currentdict end "
+              "setpagedevice}\n"
+              "{userdict/#copies 3 -1 roll put}ifelse\n", doc->copies);
+    doc_puts(doc, "%RBIEndNonPPDFeature\n");
+  }
+
+ /*
+  * If we are doing N-up printing, disable setpagedevice...
+  */
+
+  if (doc->number_up > 1)
+    doc_puts(doc, "userdict/setpagedevice{pop}bind put\n");
+
+ /*
+  * Changes to the transfer function must be made AFTER any
+  * setpagedevice code...
+  */
+
+  if (doc->gamma != 1.0f || doc->brightness != 1.0f)
+    doc_printf(doc, "{ neg 1 add dup 0 lt { pop 1 } { %.3f exp neg 1 add } "
+                   "ifelse %.3f mul } bind settransfer\n",
+              doc->gamma, doc->brightness);
+
+ /*
+  * Make sure we have rectclip and rectstroke procedures of some sort...
+  */
+
+  doc_puts(doc,
+           "% x y w h ESPrc - Clip to a rectangle.\n"
+          "userdict/ESPrc/rectclip where{pop/rectclip load}\n"
+          "{{newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
+          "neg 0 rlineto closepath clip newpath}bind}ifelse put\n");
+
+  doc_puts(doc,
+           "% x y w h ESPrf - Fill a rectangle.\n"
+          "userdict/ESPrf/rectfill where{pop/rectfill load}\n"
+          "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
+          "neg 0 rlineto closepath fill grestore}bind}ifelse put\n");
+
+  doc_puts(doc,
+           "% x y w h ESPrs - Stroke a rectangle.\n"
+          "userdict/ESPrs/rectstroke where{pop/rectstroke load}\n"
+          "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
+          "neg 0 rlineto closepath stroke grestore}bind}ifelse put\n");
+
+ /*
+  * Write the page and label prologs...
+  */
+
+  if (doc->number_up == 2 || doc->number_up == 6)
+  {
+   /*
+    * For 2- and 6-up output, rotate the labels to match the orientation
+    * of the pages...
+    */
+
+    if (Orientation & 1)
+      write_label_prolog(doc, doc->page_label, PageBottom,
+                         PageWidth - PageLength + PageTop, PageLength);
+    else
+      write_label_prolog(doc, doc->page_label, PageLeft, PageRight,
+                         PageLength);
+  }
+  else
+    write_label_prolog(doc, doc->page_label, PageBottom, PageTop, PageWidth);
+}
+
+
+/*
+ * 'doc_printf()' - Send a formatted string to stdout and/or the temp file.
+ *
+ * This function should be used for all page-level output that is affected
+ * by ordering, collation, etc.
+ */
+
+static void
+doc_printf(pstops_doc_t *doc,          /* I - Document information */
+           const char   *format,       /* I - Printf-style format string */
+          ...)                         /* I - Additional arguments as needed */
+{
+  va_list      ap;                     /* Pointer to arguments */
+  char         buffer[1024];           /* Output buffer */
+  size_t       bytes;                  /* Number of bytes to write */
+
+
+  va_start(ap, format);
+  bytes = vsnprintf(buffer, sizeof(buffer), format, ap);
+  va_end(ap);
+
+  if (bytes > sizeof(buffer))
+  {
+    fprintf(stderr,
+            _("ERROR: doc_printf overflow (%d bytes) detected, aborting!\n"),
+            (int)bytes);
+    exit(1);
+  }
+
+  doc_write(doc, buffer, bytes);
+}
+
+
+/*
+ * 'doc_puts()' - Send a nul-terminated string to stdout and/or the temp file.
+ *
+ * This function should be used for all page-level output that is affected
+ * by ordering, collation, etc.
+ */
+
+static void
+doc_puts(pstops_doc_t *doc,            /* I - Document information */
+         const char   *s)              /* I - String to send */
+{
+  doc_write(doc, s, strlen(s));
+}
+
+
+/*
+ * 'doc_write()' - Send data to stdout and/or the temp file.
+ */
+
+static void
+doc_write(pstops_doc_t *doc,           /* I - Document information */
+          const char   *s,             /* I - Data to send */
+         size_t       len)             /* I - Number of bytes to send */
+{
+  if (!doc->slow_order)
+    fwrite(s, 1, len, stdout);
+
+  if (doc->temp)
+    cupsFileWrite(doc->temp, s, len);
+}
+
+
+/*
+ * 'end_nup()' - End processing for N-up printing...
+ */
+
+static void
+end_nup(pstops_doc_t *doc,             /* I - Document information */
+        int          number)           /* I - Page number */
+{
+  if (doc->number_up > 1)
+    doc_puts(doc, "userdict/ESPsave get restore\n");
+
+  switch (doc->number_up)
+  {
+    case 1 :
+       if (doc->use_ESPshowpage)
+       {
+         write_labels(doc, Orientation);
+          doc_puts(doc, "ESPshowpage\n");
+       }
+       break;
+
+    case 2 :
+    case 6 :
+       if (is_last_page(number) && doc->use_ESPshowpage)
+       {
+         if (Orientation & 1)
+         {
+          /*
+           * Rotate the labels back to portrait...
+           */
+
+           write_labels(doc, Orientation - 1);
+         }
+         else if (Orientation == 0)
+         {
+          /*
+           * Rotate the labels to landscape...
+           */
+
+           write_labels(doc, doc->normal_landscape ? 1 : 3);
+         }
+         else
+         {
+          /*
+           * Rotate the labels to landscape...
+           */
+
+           write_labels(doc, doc->normal_landscape ? 3 : 1);
+         }
+
+          doc_puts(doc, "ESPshowpage\n");
+       }
+        break;
+
+    default :
+       if (is_last_page(number) && doc->use_ESPshowpage)
+       {
+         write_labels(doc, Orientation);
+          doc_puts(doc, "ESPshowpage\n");
+       }
+        break;
+  }
+
+  fflush(stdout);
+}
+
+
+/*
+ * 'include_feature()' - Include a printer option/feature command.
+ */
+
+static int                             /* O  - New number of options */
+include_feature(
+    ppd_file_t    *ppd,                        /* I  - PPD file */
+    const char    *line,               /* I  - DSC line */
+    int           num_options,         /* I  - Number of options */
+    cups_option_t **options)           /* IO - Options */
+{
+  char         name[255],              /* Option name */
+               value[255];             /* Option value */
+  ppd_option_t *option;                /* Option in file */
+  ppd_choice_t *choice;                /* Choice */
+
+
+ /*
+  * Get the "%%IncludeFeature: *Keyword OptionKeyword" values...
+  */
+
+  if (sscanf(line + 17, "%254s%254s", name, value) != 2)
+  {
+    fputs(_("ERROR: Bad %%IncludeFeature: comment!\n"), stderr);
+    return (num_options);
+  }
+
+ /*
+  * Find the option and choice...
+  */
+
+  if ((option = ppdFindOption(ppd, name + 1)) == NULL)
+  {
+    fprintf(stderr, _("WARNING: Unknown option \"%s\"!\n"), name + 1);
+    return (num_options);
+  }
+
+  if (option->section == PPD_ORDER_EXIT ||
+      option->section == PPD_ORDER_JCL)
+  {
+    fprintf(stderr, _("WARNING: Option \"%s\" cannot be included via "
+                      "IncludeFeature!\n"), name + 1);
+    return (num_options);
+  }
+
+  if ((choice = ppdFindChoice(option, value)) == NULL)
+  {
+    fprintf(stderr, _("WARNING: Unknown choice \"%s\" for option \"%s\"!\n"),
+            value, name + 1);
+    return (num_options);
+  }
+
+ /*
+  * Add the option to the option array and return...
+  */
+
+  return (cupsAddOption(name + 1, value, num_options, options));
+}
+
+
+/*
+ * 'parse_text()' - Parse a text value in a comment...
+ *
+ * This function parses a DSC text value as defined on page 36 of the
+ * DSC specification.  Text values are either surrounded by parenthesis
+ * or whitespace-delimited.
+ *
+ * The value returned is the literal characters for the entire text
+ * string, including any parenthesis and escape characters.
+ */
+
+static char *                          /* O - Value or NULL on error */
+parse_text(const char *start,          /* I - Start of text value */
+           char       **end,           /* O - End of text value */
+          char       *buffer,          /* I - Buffer */
+           size_t     bufsize)         /* I - Size of buffer */
+{
+  char *bufptr,                        /* Pointer in buffer */
+       *bufend;                        /* End of buffer */
+  int  level;                          /* Parenthesis level */
+
+
+ /*
+  * Skip leading whitespace...
+  */
+
+  while (isspace(*start & 255))
+    start ++;
+
+ /*
+  * Then copy the value...
+  */
+
+  level  = 0;
+  bufptr = buffer;
+  bufend = buffer + bufsize - 1;
+
+  while (bufptr < bufend)
+  {
+    if (isspace(*start & 255) && !level)
+      break;
+
+    *bufptr++ = *start;
+
+    if (*start == '(')
+      level ++;
+    else if (*start == ')')
+    {
+      if (!level)
+      {
+        start ++;
+        break;
+      }
+      else
+        level --;
+    }
+    else if (*start == '\\')
+    {
+     /*
+      * Copy escaped character...
+      */
+
+      int      i;                      /* Looping var */
+
+
+      for (i = 1;
+           i <= 3 && isdigit(start[i] & 255) && bufptr < bufend;
+          *bufptr++ = start[i], i ++);
+    }
+
+    start ++;
+  }
+
+  *bufptr = '\0';
+
+ /*
+  * Return the value and new pointer into the line...
+  */
+
+  if (end)
+    *end = (char *)start;
+
+  if (bufptr == bufend)
+    return (NULL);
+  else
+    return (buffer);
+}
+
+
+/*
+ * 'set_pstops_options()' - Set pstops options...
+ */
+
+static void
+set_pstops_options(
+    pstops_doc_t  *doc,                        /* I - Document information */
+    ppd_file_t    *ppd,                        /* I - PPD file */
+    char          *argv[],             /* I - Command-line arguments */
+    int           num_options,         /* I - Number of options */
+    cups_option_t *options)            /* I - Options */
+{
+  const char   *val;                   /* Option value */
+  int          intval;                 /* Integer option value */
+  ppd_attr_t   *attr;                  /* PPD attribute */
+  ppd_option_t *option;                /* PPD option */
+  ppd_choice_t *choice;                /* PPD choice */
+
+
+ /*
+  * Initialize document information structure...
+  */
+
+  memset(doc, 0, sizeof(pstops_doc_t));
+
+  doc->job_id = atoi(argv[1]);
+  doc->user   = argv[2];
+  doc->title  = argv[3];
+  doc->copies = atoi(argv[4]);
+
+  if (ppd && ppd->landscape > 0)
+    doc->normal_landscape = 1;
+
+  doc->bounding_box[0] = (int)PageLeft;
+  doc->bounding_box[1] = (int)PageBottom;
+  doc->bounding_box[2] = (int)PageRight;
+  doc->bounding_box[3] = (int)PageTop;
+
+  doc->new_bounding_box[0] = INT_MAX;
+  doc->new_bounding_box[1] = INT_MAX;
+  doc->new_bounding_box[2] = INT_MIN;
+  doc->new_bounding_box[3] = INT_MIN;
+
+ /*
+  * AP_FIRSTPAGE_InputSlot
+  */
+
+  doc->ap_input_slot = cupsGetOption("AP_FIRSTPAGE_InputSlot", num_options,
+                                     options);
+
+ /*
+  * AP_FIRSTPAGE_ManualFeed
+  */
+
+  doc->ap_manual_feed = cupsGetOption("AP_FIRSTPAGE_ManualFeed", num_options,
+                                      options);
+
+ /*
+  * brightness
+  */
+
+  if ((val = cupsGetOption("brightness", num_options, options)) != NULL)
+  {
+   /*
+    * Get brightness value from 10 to 1000.
+    */
+
+    intval = atoi(val);
+
+    if (intval < 10 || intval > 1000)
+    {
+      fprintf(stderr, _("ERROR: Unsupported brightness value %s, using "
+                        "brightness=100!\n"), val);
+      doc->brightness = 1.0f;
+    }
+    else
+      doc->brightness = intval * 0.01f;
+  }
+  else
+    doc->brightness = 1.0f;
+
+ /*
+  * collate, multiple-document-handling
+  */
+
+  if ((val = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
+  {
+   /*
+    * This IPP attribute is unnecessarily complicated...
+    *
+    *   single-document, separate-documents-collated-copies, and
+    *   single-document-new-sheet all require collated copies.
+    *
+    *   separate-documents-uncollated-copies allows for uncollated copies.
+    */
+
+    doc->collate = strcasecmp(val, "separate-documents-uncollated-copies") != 0;
+  }
+
+  if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
+      (!strcasecmp(val, "true") ||!strcasecmp(val, "on") ||
+       !strcasecmp(val, "yes")))
+    doc->collate = 1;
+
+ /*
+  * emit-jcl
+  */
+
+  if ((val = cupsGetOption("emit-jcl", num_options, options)) != NULL &&
+      (!strcasecmp(val, "false") || !strcasecmp(val, "off") ||
+       !strcasecmp(val, "no") || !strcmp(val, "0")))
+    doc->emit_jcl = 0;
+  else
+    doc->emit_jcl = 1;
+
+ /*
+  * fitplot
+  */
+
+  if ((val = cupsGetOption("fitplot", num_options, options)) != NULL &&
+      (!strcasecmp(val, "true") || !strcasecmp(val, "on") ||
+       !strcasecmp(val, "yes")))
+    doc->fitplot = 1;
+
+ /*
+  * gamma
+  */
+
+  if ((val = cupsGetOption("gamma", num_options, options)) != NULL)
+  {
+   /*
+    * Get gamma value from 1 to 10000...
+    */
+
+    intval = atoi(val);
+
+    if (intval < 1 || intval > 10000)
+    {
+      fprintf(stderr, _("ERROR: Unsupported gamma value %s, using "
+                        "gamma=1000!\n"), val);
+      doc->gamma = 1.0f;
+    }
+    else
+      doc->gamma = intval * 0.001f;
+  }
+  else
+    doc->gamma = 1.0f;
+
+ /*
+  * InputSlot
+  */
+
+  if ((choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL)
+    doc->input_slot = choice->choice;
+
+ /*
+  * ManualFeed
+  */
+
+  if ((choice = ppdFindMarkedChoice(ppd, "ManualFeed")) != NULL)
+    doc->manual_feed = choice->choice;
+
+  if ((choice = ppdFindMarkedChoice(ppd, "MirrorPrint")) != NULL)
+  {
+    val = choice->choice;
+    choice->marked = 0;
+  }
+  else
+    val = cupsGetOption("mirror", num_options, options);
+
+  if (val && (!strcasecmp(val, "true") || !strcasecmp(val, "on") ||
+              !strcasecmp(val, "yes")))
+    doc->mirror = 1;
+
+ /*
+  * number-up
+  */
+
+  if ((val = cupsGetOption("number-up", num_options, options)) != NULL)
+  {
+    switch (intval = atoi(val))
+    {
+      case 1 :
+      case 2 :
+      case 4 :
+      case 6 :
+      case 9 :
+      case 16 :
+          doc->number_up = intval;
+         break;
+      default :
+          fprintf(stderr,
+                 _("ERROR: Unsupported number-up value %d, using "
+                   "number-up=1!\n"), intval);
+          doc->number_up = 1;
+         break;
+    }
+  }
+  else
+    doc->number_up = 1;
+
+ /*
+  * number-up-layout
+  */
+
+  if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL)
+  {
+    if (!strcasecmp(val, "lrtb"))
+      doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
+    else if (!strcasecmp(val, "lrbt"))
+      doc->number_up_layout = PSTOPS_LAYOUT_LRBT;
+    else if (!strcasecmp(val, "rltb"))
+      doc->number_up_layout = PSTOPS_LAYOUT_RLTB;
+    else if (!strcasecmp(val, "rlbt"))
+      doc->number_up_layout = PSTOPS_LAYOUT_RLBT;
+    else if (!strcasecmp(val, "tblr"))
+      doc->number_up_layout = PSTOPS_LAYOUT_TBLR;
+    else if (!strcasecmp(val, "tbrl"))
+      doc->number_up_layout = PSTOPS_LAYOUT_TBRL;
+    else if (!strcasecmp(val, "btlr"))
+      doc->number_up_layout = PSTOPS_LAYOUT_BTLR;
+    else if (!strcasecmp(val, "btrl"))
+      doc->number_up_layout = PSTOPS_LAYOUT_BTRL;
+    else
+    {
+      fprintf(stderr, _("ERROR: Unsupported number-up-layout value %s, using "
+                        "number-up-layout=lrtb!\n"), val);
+      doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
+    }
+  }
+  else
+    doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
+
+ /*
+  * OutputOrder
+  */
+
+  if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL)
+  {
+    if (!strcasecmp(val, "Reverse"))
+      doc->output_order = 1;
+  }
+  else if (ppd)
+  {
+   /*
+    * Figure out the right default output order from the PPD file...
+    */
+
+    if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL &&
+        (attr = ppdFindAttr(ppd, "PageStackOrder", choice->choice)) != NULL &&
+       attr->value)
+      doc->output_order = !strcasecmp(attr->value, "Reverse");
+    else if ((attr = ppdFindAttr(ppd, "DefaultOutputOrder", NULL)) != NULL &&
+             attr->value)
+      doc->output_order = !strcasecmp(attr->value, "Reverse");
+  }
+
+ /*
+  * page-border
+  */
+
+  if ((val = cupsGetOption("page-border", num_options, options)) != NULL)
+  {
+    if (!strcasecmp(val, "none"))
+      doc->page_border = PSTOPS_BORDERNONE;
+    else if (!strcasecmp(val, "single"))
+      doc->page_border = PSTOPS_BORDERSINGLE;
+    else if (!strcasecmp(val, "single-thick"))
+      doc->page_border = PSTOPS_BORDERSINGLE2;
+    else if (!strcasecmp(val, "double"))
+      doc->page_border = PSTOPS_BORDERDOUBLE;
+    else if (!strcasecmp(val, "double-thick"))
+      doc->page_border = PSTOPS_BORDERDOUBLE2;
+    else
+    {
+      fprintf(stderr, _("ERROR: Unsupported page-border value %s, using "
+                        "page-border=none!\n"), val);
+      doc->page_border = PSTOPS_BORDERNONE;
+    }
+  }
+  else
+    doc->page_border = PSTOPS_BORDERNONE;
 
-static void
-do_setup(ppd_file_t *ppd,              /* I - PPD file */
-         int        copies,            /* I - Number of copies */
-        int        collate,            /* I - Collate output? */
-        int        slowcollate,        /* I - Slow collate */
-        float      g,                  /* I - Gamma value */
-        float      b)                  /* I - Brightness value */
-{
  /*
-  * Send all the printer-specific setup commands...
+  * page-label
   */
 
-  ppdEmit(ppd, stdout, PPD_ORDER_DOCUMENT);
-  ppdEmit(ppd, stdout, PPD_ORDER_ANY);
+  doc->page_label = cupsGetOption("page-label", num_options, options);
 
  /*
-  * Set the number of copies for the job...
+  * page-ranges
   */
 
-  if (copies != 1 && (!collate || !slowcollate))
-  {
-    printf("%%RBIBeginNonPPDFeature: *NumCopies %d\n", copies);
-    printf("%d/languagelevel where{pop languagelevel 2 ge}{false}ifelse{1 dict begin"
-           "/NumCopies exch def currentdict end " 
-           "setpagedevice}{userdict/#copies 3 -1 roll put}ifelse\n", copies);
-    printf("%%RBIEndNonPPDFeature\n");
-  }
+  doc->page_ranges = cupsGetOption("page-ranges", num_options, options);
 
  /*
-  * If we are doing N-up printing, disable setpagedevice...
+  * page-set
   */
 
-  if (NUp > 1)
-    puts("userdict/setpagedevice{pop}bind put");
+  doc->page_set = cupsGetOption("page-set", num_options, options);
 
  /*
-  * Changes to the transfer function must be made AFTER any
-  * setpagedevice code...
+  * Now figure out if we have to force collated copies, etc.
   */
 
-  if (g != 1.0 || b != 1.0)
-    printf("{ neg 1 add dup 0 lt { pop 1 } { %.3f exp neg 1 add } "
-          "ifelse %.3f mul } bind settransfer\n", g, b);
+  if (ppd && ppd->manual_copies && Duplex && doc->copies > 1)
+  {
+   /*
+    * Force collated copies when printing a duplexed document to
+    * a non-PS printer that doesn't do hardware copy generation.
+    * Otherwise the copies will end up on the front/back side of
+    * each page.
+    */
+
+    doc->collate = 1;
+  }
 
  /*
-  * Make sure we have rectclip and rectstroke procedures of some sort...
+  * See if we have to filter the fast or slow way...
   */
 
-  WriteCommon();
-}
-
-
-/*
- * 'end_nup()' - End processing for N-up printing...
- */
-
-static void
-end_nup(int number)    /* I - Page number */
-{
-  puts("");
-
-  if (Flip || Orientation || NUp > 1)
-    puts("userdict /ESPsave get restore");
-
-  switch (NUp)
+  if (doc->collate && doc->copies > 1)
   {
-    case 1 :
-       if (UseESPsp)
-       {
-         WriteLabels(Orientation);
-          puts("ESPshowpage");
-       }
-       break;
-
-    case 2 :
-    case 6 :
-       if (is_last_page(number) && UseESPsp)
-       {
-         if (Orientation & 1)
-         {
-          /*
-           * Rotate the labels back to portrait...
-           */
-
-           WriteLabels(Orientation - 1);
-         }
-         else if (Orientation == 0)
-         {
-          /*
-           * Rotate the labels to landscape...
-           */
-
-           WriteLabels(NormalLandscape ? 1 : 3);
-         }
-         else
-         {
-          /*
-           * Rotate the labels to landscape...
-           */
+   /*
+    * See if we need to manually collate the pages...
+    */
 
-           WriteLabels(NormalLandscape ? 3 : 1);
-         }
+    doc->slow_collate = 1;
 
-          puts("ESPshowpage");
-       }
-        break;
+    if ((choice = ppdFindMarkedChoice(ppd, "Collate")) != NULL &&
+        !strcasecmp(choice->choice, "True"))
+    {
+     /*
+      * Hardware collate option is selected, see if the option is
+      * conflicting - if not, collate in hardware.  Otherwise,
+      * turn the hardware collate option off...
+      */
 
-    default :
-       if (is_last_page(number) && UseESPsp)
-       {
-         WriteLabels(Orientation);
-          puts("ESPshowpage");
-       }
-        break;
+      if ((option = ppdFindOption(ppd, "Collate")) != NULL &&
+          !option->conflicted)
+       doc->slow_collate = 0;
+      else
+        ppdMarkOption(ppd, "Collate", "False");
+    }
   }
+  else
+    doc->slow_collate = 0;
 
-  fflush(stdout);
-}
-
-
-/*
- * 'include_feature()' - Include a printer option/feature command.
- */
-
-static void
-include_feature(ppd_file_t  *ppd,      /* I - PPD file */
-                const char  *line,     /* I - DSC line */
-               cups_file_t *out)       /* I - Output file */
-{
-  char         name[255],              /* Option name */
-               value[255];             /* Option value */
-  ppd_option_t *option;                /* Option in file */
-  ppd_choice_t *choice;                /* Choice */
+  if (!ppdFindOption(ppd, "OutputOrder") && doc->output_order)
+    doc->slow_order = 1;
+  else
+    doc->slow_order = 0;
 
+  if (Duplex &&
+       (doc->slow_collate || doc->slow_order ||
+        ((attr = ppdFindAttr(ppd, "cupsEvenDuplex", NULL)) != NULL &&
+        attr->value && !strcasecmp(attr->value, "true"))))
+    doc->slow_duplex = 1;
+  else
+    doc->slow_duplex = 0;
 
  /*
-  * Get the "%%IncludeFeature: *Keyword OptionKeyword" values...
+  * Create a temporary file for page data if we need to filter slowly...
   */
 
-  if (sscanf(line + 17, "%254s%254s", name, value) != 2)
+  if (doc->slow_order || doc->slow_collate)
   {
-    fprintf(stderr, "ERROR: Bad line: \"%s\"!\n", line);
-    return;
+    if ((doc->temp = cupsTempFile2(doc->tempfile,
+                                   sizeof(doc->tempfile))) == NULL)
+    {
+      fprintf(stderr, _("ERROR: Unable to create temporary file: %s\n"),
+              strerror(errno));
+      exit(1);
+    }
   }
 
  /*
-  * Find the option and choice...
+  * Figure out if we should use ESPshowpage or not...
   */
 
-  if ((option = ppdFindOption(ppd, name + 1)) == NULL)
-  {
-    fprintf(stderr, "WARNING: Unknown option \"%s\"!\n", name + 1);
-    return;
-  }
-
-  if (option->section == PPD_ORDER_EXIT ||
-      option->section == PPD_ORDER_JCL)
+  if (doc->page_label || getenv("CLASSIFICATION") || doc->number_up > 1 ||
+      doc->page_border)
   {
-    fprintf(stderr, "WARNING: Option \"%s\" cannot be included via "
-                    "IncludeFeature!\n", name + 1);
-    return;
-  }
+   /*
+    * Yes, use ESPshowpage...
+    */
 
-  if ((choice = ppdFindChoice(option, value)) == NULL)
-  {
-    fprintf(stderr, "WARNING: Unknown choice \"%s\" for option \"%s\"!\n",
-            value, name + 1);
-    return;
+    doc->use_ESPshowpage = 1;
   }
 
- /*
-  * Emit the option...
-  */
-
-  if (out)
-  {
-    cupsFilePuts(out, "[{\n");
-    cupsFilePrintf(out, "%%%%BeginFeature: %s %s\n", name, value);
-    if (choice->code && choice->code[0])
-    {
-      if (choice->code[strlen(choice->code) - 1] != '\n')
-       cupsFilePrintf(out, "%s\n", choice->code);
-      else
-       cupsFilePuts(out, choice->code);
-    }
-    cupsFilePuts(out, "%%EndFeature\n");
-    cupsFilePuts(out, "} stopped cleartomark\n");
-  }
-  else
-  {
-    puts("[{");
-    printf("%%%%BeginFeature: %s %s\n", name, value);
-    if (choice->code && choice->code[0])
-    {
-      if (choice->code[strlen(choice->code) - 1] != '\n')
-        printf("%s\n", choice->code);
-      else
-        fputs(choice->code, stdout);
-    }
-    puts("%%EndFeature");
-    puts("} stopped cleartomark");
-  }
+  fprintf(stderr, "DEBUG: slow_collate=%d, slow_duplex=%d, slow_order=%d\n",
+          doc->slow_collate, doc->slow_duplex, doc->slow_order);
 }
 
 
 /*
- * 'psgets()' - Get a line from a file.
- *
- * Note:
- *
- *   This function differs from the gets() function in that it
- *   handles any combination of CR, LF, or CR LF to end input
- *   lines.
+ * 'skip_page()' - Skip past a page that won't be printed...
  */
 
-static char *                          /* O  - String or NULL if EOF */
-psgets(char   *buf,                    /* I  - Buffer to read into */
-       size_t *bytes,                  /* IO - Length of buffer */
-       FILE   *fp)                     /* I  - File to read from */
+static ssize_t                         /* O - Length of next line */
+skip_page(cups_file_t *fp,             /* I - File to read from */
+          char        *line,           /* I - Line buffer */
+         ssize_t     linelen,          /* I - Length of initial line */
+          size_t      linesize)                /* I - Size of line buffer */
 {
-  char         *bufptr;                /* Pointer into buffer */
-  int          ch;                     /* Character from file */
-  size_t       len;                    /* Max length of string */
+  int  level;                          /* Embedded document level */
 
 
-  len    = *bytes - 1;
-  bufptr = buf;
-  ch     = EOF;
+  level = 0;
 
-  while ((bufptr - buf) < len)
+  while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0)
   {
-    if ((ch = getc(fp)) == EOF)
+    if (level == 0 &&
+        (!strncmp(line, "%%Page:", 7) || !strncmp(line, "%%Trailer", 9)))
       break;
-
-    if (ch == '\r')
+    else if (!strncmp(line, "%%BeginDocument", 15) ||
+            !strncmp(line, "%ADO_BeginApplication", 21))
+      level ++;
+    else if ((!strncmp(line, "%%EndDocument", 13) ||
+             !strncmp(line, "%ADO_EndApplication", 19)) && level > 0)
+      level --;
+    else if (!strncmp(line, "%%BeginBinary:", 14) ||
+             (!strncmp(line, "%%BeginData:", 12) &&
+             !strstr(line, "ASCII") && !strstr(line, "Hex")))
     {
      /*
-      * Got a CR; see if there is a LF as well...
+      * Skip binary data...
       */
 
-      ch = getc(fp);
+      int      bytes;                  /* Bytes of data */
 
-      if (ch != EOF && ch != '\n')
-      {
-        ungetc(ch, fp);        /* Nope, save it for later... */
-        ch = '\r';
-      }
-      else
-        *bufptr++ = '\r';
-      break;
-    }
-    else if (ch == '\n')
-      break;
-    else
-      *bufptr++ = ch;
-  }
 
- /*
-  * Add a trailing newline if it is there...
-  */
+      bytes = atoi(strchr(line, ':') + 1);
 
-  if (ch == '\n' || ch == '\r')
-  {
-    if ((bufptr - buf) < len)
-      *bufptr++ = ch;
-    else
-      ungetc(ch, fp);
-  }
+      while (bytes > 0)
+      {
+       if (bytes > linesize)
+         linelen = cupsFileRead(fp, line, linesize);
+       else
+         linelen = cupsFileRead(fp, line, bytes);
 
- /*
-  * Nul-terminate the string and return it (or NULL for EOF).
-  */
+       if (linelen < 1)
+       {
+         line[0] = '\0';
+         perror("ERROR: Early end-of-file while reading binary data");
+         return (0);
+       }
 
-  *bufptr = '\0';
-  *bytes  = bufptr - buf;
+       bytes -= linelen;
+      }
+    }
+  }
 
-  if (ch == EOF && bufptr == buf)
-    return (NULL);
-  else
-    return (buf);
+  return (linelen);
 }
 
 
@@ -1806,69 +2775,97 @@ psgets(char   *buf,                     /* I  - Buffer to read into */
  */
 
 static void
-start_nup(int       number,            /* I - Page number */
-          int       show_border,       /* I - Show the page border? */
-         const int *lbrt)              /* I - Page BoundingBox */ 
+start_nup(pstops_doc_t *doc,           /* I - Document information */
+          int          number,         /* I - Page number */
+         int          show_border,     /* I - Show the border? */
+         const int    *bounding_box)   /* I - BoundingBox value */
 {
-  int  pos;                            /* Position on page */
-  int  x, y;                           /* Relative position of subpage */
-  float        w, l,                           /* Width and length of subpage */
-       tx, ty;                         /* Translation values for subpage */
-  float        pw, pl;                         /* Printable width and length of full page */
-
-
-  if (Flip || Orientation || NUp > 1)
-    puts("userdict/ESPsave save put");
-
-  if (Flip)
-    printf("%.1f 0.0 translate -1 1 scale\n", PageWidth);
-
-  pos = number % NUp;
-  pw  = lbrt[2] - lbrt[0];
-  pl  = lbrt[3] - lbrt[1];
+  int          pos;                    /* Position on page */
+  int          x, y;                   /* Relative position of subpage */
+  float                w, l,                   /* Width and length of subpage */
+               tx, ty;                 /* Translation values for subpage */
+  float                pagew,                  /* Printable width of page */
+               pagel;                  /* Printable height of page */
+  int          bboxx,                  /* BoundingBox X origin */
+               bboxy,                  /* BoundingBox Y origin */
+               bboxw,                  /* BoundingBox width */
+               bboxl;                  /* BoundingBox height */
+  float                margin = 0;             /* Current margin for border */
+
+
+  if (doc->number_up > 1)
+    doc_puts(doc, "userdict/ESPsave save put\n");
+
+  if (doc->mirror)
+    doc_printf(doc, "%.1f 0.0 translate -1 1 scale\n", PageWidth);
+
+  pos   = (number - 1) % doc->number_up;
+  pagew = PageRight - PageLeft;
+  pagel = PageTop - PageBottom;
+
+  if (doc->fitplot)
+  {
+    bboxx = bounding_box[0];
+    bboxy = bounding_box[1];
+    bboxw = bounding_box[2] - bounding_box[0];
+    bboxl = bounding_box[3] - bounding_box[1];
+  }
+  else
+  {
+    bboxx = 0;
+    bboxy = 0;
+    bboxw = PageWidth;
+    bboxl = PageLength;
+  }
 
-  fprintf(stderr, "DEBUG: pw = %.1f, pl = %.1f\n", pw, pl);
-  fprintf(stderr, "DEBUG: PageLeft = %.1f, PageRight = %.1f\n", PageLeft, PageRight);
-  fprintf(stderr, "DEBUG: PageTop = %.1f, PageBottom = %.1f\n", PageTop, PageBottom);
-  fprintf(stderr, "DEBUG: PageWidth = %.1f, PageLength = %.1f\n", PageWidth, PageLength);
+  fprintf(stderr, "DEBUG: pagew = %.1f, pagel = %.1f\n", pagew, pagel);
+  fprintf(stderr, "DEBUG: bboxx = %d, bboxy = %d, bboxw = %d, bboxl = %d\n",
+          bboxx, bboxy, bboxw, bboxl);
+  fprintf(stderr, "DEBUG: PageLeft = %.1f, PageRight = %.1f\n",
+          PageLeft, PageRight);
+  fprintf(stderr, "DEBUG: PageTop = %.1f, PageBottom = %.1f\n",
+          PageTop, PageBottom);
+  fprintf(stderr, "DEBUG: PageWidth = %.1f, PageLength = %.1f\n",
+          PageWidth, PageLength);
 
   switch (Orientation)
   {
     case 1 : /* Landscape */
-        printf("%.1f 0.0 translate 90 rotate\n", PageLength);
+        doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", PageLength);
         break;
     case 2 : /* Reverse Portrait */
-        printf("%.1f %.1f translate 180 rotate\n", PageWidth, PageLength);
+        doc_printf(doc, "%.1f %.1f translate 180 rotate\n", PageWidth,
+                  PageLength);
         break;
     case 3 : /* Reverse Landscape */
-        printf("0.0 %.1f translate -90 rotate\n", PageWidth);
+        doc_printf(doc, "0.0 %.1f translate -90 rotate\n", PageWidth);
         break;
   }
 
-  if (Duplex && NUp > 1 && ((number / NUp) & 1))
-    printf("%.1f %.1f translate\n", PageWidth - PageRight, PageBottom);
-  else if (NUp > 1 || FitPlot)
-    printf("%.1f %.1f translate\n", PageLeft, PageBottom);
+  if (Duplex && doc->number_up > 1 && ((number / doc->number_up) & 1))
+    doc_printf(doc, "%.1f %.1f translate\n", PageWidth - PageRight, PageBottom);
+  else if (doc->number_up > 1 || doc->fitplot)
+    doc_printf(doc, "%.1f %.1f translate\n", PageLeft, PageBottom);
 
-  switch (NUp)
+  switch (doc->number_up)
   {
     default :
-        if (FitPlot)
+        if (doc->fitplot)
        {
-          w = PageRight - PageLeft;
-          l = w * pl / pw;
+          w = pagew;
+          l = w * bboxl / bboxw;
 
-          if (l > (PageTop - PageBottom))
+          if (l > pagel)
           {
-            l = PageTop - PageBottom;
-            w = l * pw / pl;
+            l = pagel;
+            w = l * bboxw / bboxl;
           }
 
-          tx = 0.5 * (PageRight - PageLeft - w);
-          ty = 0.5 * (PageTop - PageBottom - l);
+          tx = 0.5 * (pagew - w);
+          ty = 0.5 * (pagel - l);
 
-         printf("%.1f %.1f translate %.3f %.3f scale\n", tx, ty, w / pw,
-                l / pl);
+         doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n", tx, ty,
+                    w / bboxw, l / bboxl);
        }
        else
        {
@@ -1882,60 +2879,60 @@ start_nup(int       number,             /* I - Page number */
        {
           x = pos & 1;
 
-          if (Layout & LAYOUT_NEGATEY)
+          if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
            x = 1 - x;
 
-          w = pl;
-          l = w * PageLength / PageWidth;
+          w = pagel;
+          l = w * bboxl / bboxw;
 
-          if (l > (pw * 0.5))
+          if (l > (pagew * 0.5))
           {
-            l = pw * 0.5;
-            w = l * PageWidth / PageLength;
+            l = pagew * 0.5;
+            w = l * bboxw / bboxl;
           }
 
-          tx = 0.5 * (pw * 0.5 - l);
-          ty = 0.5 * (pl - w);
+          tx = 0.5 * (pagew * 0.5 - l);
+          ty = 0.5 * (pagel - w);
 
-          if (NormalLandscape)
-            printf("0.0 %.1f translate -90 rotate\n", pl);
+          if (doc->normal_landscape)
+            doc_printf(doc, "0.0 %.1f translate -90 rotate\n", pagel);
          else
-           printf("%.1f 0.0 translate 90 rotate\n", pw);
+           doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", pagew);
 
-          printf("%.1f %.1f translate %.3f %.3f scale\n",
-                 ty, tx + l * x, w / PageWidth, l / PageLength);
+          doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
+                     ty, tx + pagew * 0.5 * x, w / bboxw, l / bboxl);
         }
        else
        {
           x = pos & 1;
 
-          if (Layout & LAYOUT_NEGATEX)
+          if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
            x = 1 - x;
 
-          l = pw;
-          w = l * PageWidth / PageLength;
+          l = pagew;
+          w = l * bboxw / bboxl;
 
-          if (w > (pl * 0.5))
+          if (w > (pagel * 0.5))
           {
-            w = pl * 0.5;
-            l = w * PageLength / PageWidth;
+            w = pagel * 0.5;
+            l = w * bboxl / bboxw;
           }
 
-          tx = 0.5 * (pl * 0.5 - w);
-          ty = 0.5 * (pw - l);
+          tx = 0.5 * (pagel * 0.5 - w);
+          ty = 0.5 * (pagew - l);
 
-          if (NormalLandscape)
-           printf("%.1f 0.0 translate 90 rotate\n", pw);
+          if (doc->normal_landscape)
+           doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", pagew);
          else
-            printf("0.0 %.1f translate -90 rotate\n", pl);
+            doc_printf(doc, "0.0 %.1f translate -90 rotate\n", pagel);
 
-          printf("%.1f %.1f translate %.3f %.3f scale\n",
-                 tx + w * x, ty, w / PageWidth, l / PageLength);
+          doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
+                     tx + pagel * 0.5 * x, ty, w / bboxw, l / bboxl);
         }
         break;
 
     case 4 :
-        if (Layout & LAYOUT_VERTICAL)
+        if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
        {
          x = (pos / 2) & 1;
           y = pos & 1;
@@ -1946,40 +2943,41 @@ start_nup(int       number,             /* I - Page number */
          y = (pos / 2) & 1;
         }
 
-        if (Layout & LAYOUT_NEGATEX)
+        if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
          x = 1 - x;
 
-       if (Layout & LAYOUT_NEGATEY)
+       if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
          y = 1 - y;
 
-        w = pw * 0.5;
-       l = w * PageLength / PageWidth;
+        w = pagew * 0.5;
+       l = w * bboxl / bboxw;
 
-       if (l > (pl * 0.5))
+       if (l > (pagel * 0.5))
        {
-         l = pl * 0.5;
-         w = l * PageWidth / PageLength;
+         l = pagel * 0.5;
+         w = l * bboxw / bboxl;
        }
 
-        tx = 0.5 * (pw * 0.5 - w);
-        ty = 0.5 * (pl * 0.5 - l);
+        tx = 0.5 * (pagew * 0.5 - w);
+        ty = 0.5 * (pagel * 0.5 - l);
 
-       printf("%.1f %.1f translate %.3f %.3f scale\n", tx + x * w, ty + y * l,
-              w / PageWidth, l / PageLength);
+       doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
+                  tx + x * pagew * 0.5, ty + y * pagel * 0.5,
+                  w / bboxw, l / bboxl);
         break;
 
     case 6 :
         if (Orientation & 1)
        {
-         if (Layout & LAYOUT_VERTICAL)
+         if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
          {
            x = pos / 3;
            y = pos % 3;
 
-            if (Layout & LAYOUT_NEGATEX)
+            if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
              x = 1 - x;
 
-            if (Layout & LAYOUT_NEGATEY)
+            if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
              y = 2 - y;
          }
          else
@@ -1987,44 +2985,44 @@ start_nup(int       number,             /* I - Page number */
            x = pos & 1;
            y = pos / 2;
 
-            if (Layout & LAYOUT_NEGATEX)
+            if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
              x = 1 - x;
 
-            if (Layout & LAYOUT_NEGATEY)
+            if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
              y = 2 - y;
          }
 
-          w = pl * 0.5;
-          l = w * PageLength / PageWidth;
+          w = pagel * 0.5;
+          l = w * bboxl / bboxw;
 
-          if (l > (pw * 0.333))
+          if (l > (pagew * 0.333))
           {
-            l = pw * 0.333;
-            w = l * PageWidth / PageLength;
+            l = pagew * 0.333;
+            w = l * bboxw / bboxl;
           }
 
-          tx = 0.5 * (pl - 2 * w);
-          ty = 0.5 * (pw - 3 * l);
+          tx = 0.5 * (pagel - 2 * w);
+          ty = 0.5 * (pagew - 3 * l);
 
-          if (NormalLandscape)
-            printf("0.0 %.1f translate -90 rotate\n", pl);
+          if (doc->normal_landscape)
+            doc_printf(doc, "0 %.1f translate -90 rotate\n", pagel);
          else
-           printf("%.1f 0.0 translate 90 rotate\n", pw);
+           doc_printf(doc, "%.1f 0 translate 90 rotate\n", pagew);
 
-          printf("%.1f %.1f translate %.3f %.3f scale\n",
-                 tx + x * w, ty + y * l, w / PageWidth, l / PageLength);
+          doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
+                     tx + x * w, ty + y * l, l / bboxl, w / bboxw);
         }
        else
        {
-         if (Layout & LAYOUT_VERTICAL)
+         if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
          {
            x = pos / 2;
            y = pos & 1;
 
-            if (Layout & LAYOUT_NEGATEX)
+            if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
              x = 2 - x;
 
-            if (Layout & LAYOUT_NEGATEY)
+            if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
              y = 1 - y;
          }
          else
@@ -2032,37 +3030,38 @@ start_nup(int       number,             /* I - Page number */
            x = pos % 3;
            y = pos / 3;
 
-            if (Layout & LAYOUT_NEGATEX)
+            if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
              x = 2 - x;
 
-            if (Layout & LAYOUT_NEGATEY)
+            if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
              y = 1 - y;
          }
 
-          l = pw * 0.5;
-          w = l * PageWidth / PageLength;
+          l = pagew * 0.5;
+          w = l * bboxw / bboxl;
 
-          if (w > (pl * 0.333))
+          if (w > (pagel * 0.333))
           {
-            w = pl * 0.333;
-            l = w * PageLength / PageWidth;
+            w = pagel * 0.333;
+            l = w * bboxl / bboxw;
           }
 
-          tx = 0.5 * (pl - 3 * w);
-          ty = 0.5 * (pw - 2 * l);
+         tx = 0.5 * (pagel - 3 * w);
+         ty = 0.5 * (pagew - 2 * l);
 
-          if (NormalLandscape)
-           printf("%.1f 0.0 translate 90 rotate\n", pw);
+          if (doc->normal_landscape)
+           doc_printf(doc, "%.1f 0 translate 90 rotate\n", pagew);
          else
-            printf("0.0 %.1f translate -90 rotate\n", pl);
+            doc_printf(doc, "0 %.1f translate -90 rotate\n", pagel);
+
+          doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
+                     tx + w * x, ty + l * y, w / bboxw, l / bboxl);
 
-          printf("%.1f %.1f translate %.3f %.3f scale\n",
-                 tx + w * x, ty + l * y, w / PageWidth, l / PageLength);
         }
         break;
 
     case 9 :
-        if (Layout & LAYOUT_VERTICAL)
+        if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
        {
          x = (pos / 3) % 3;
           y = pos % 3;
@@ -2073,30 +3072,31 @@ start_nup(int       number,             /* I - Page number */
          y = (pos / 3) % 3;
         }
 
-        if (Layout & LAYOUT_NEGATEX)
+        if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
          x = 2 - x;
 
-       if (Layout & LAYOUT_NEGATEY)
+       if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
          y = 2 - y;
 
-        w = pw * 0.333;
-       l = w * PageLength / PageWidth;
+        w = pagew * 0.333;
+       l = w * bboxl / bboxw;
 
-       if (l > (pl * 0.333))
+       if (l > (pagel * 0.333))
        {
-         l = pl * 0.333;
-         w = l * PageWidth / PageLength;
+         l = pagel * 0.333;
+         w = l * bboxw / bboxl;
        }
 
-        tx = 0.5 * (pw * 0.333 - w);
-        ty = 0.5 * (pl * 0.333 - l);
+        tx = 0.5 * (pagew * 0.333 - w);
+        ty = 0.5 * (pagel * 0.333 - l);
 
-       printf("%.1f %.1f translate %.3f %.3f scale\n", tx + x * w, ty + y * l,
-              w / PageWidth, l / PageLength);
+       doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
+                  tx + x * pagew * 0.333, ty + y * pagel * 0.333,
+                  w / bboxw, l / bboxl);
         break;
 
     case 16 :
-        if (Layout & LAYOUT_VERTICAL)
+        if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
        {
          x = (pos / 4) & 3;
           y = pos & 3;
@@ -2107,26 +3107,27 @@ start_nup(int       number,             /* I - Page number */
          y = (pos / 4) & 3;
         }
 
-        if (Layout & LAYOUT_NEGATEX)
+        if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
          x = 3 - x;
 
-       if (Layout & LAYOUT_NEGATEY)
+       if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
          y = 3 - y;
 
-        w = pw * 0.25;
-       l = w * PageLength / PageWidth;
+        w = pagew * 0.25;
+       l = w * bboxl / bboxw;
 
-       if (l > (pl * 0.25))
+       if (l > (pagel * 0.25))
        {
-         l = pl * 0.25;
-         w = l * PageWidth / PageLength;
+         l = pagel * 0.25;
+         w = l * bboxw / bboxl;
        }
 
-        tx = 0.5 * (pw * 0.25 - w);
-        ty = 0.5 * (pl * 0.25 - l);
+        tx = 0.5 * (pagew * 0.25 - w);
+        ty = 0.5 * (pagel * 0.25 - l);
 
-       printf("%.1f %.1f translate %.3f %.3f scale\n", tx + x * w, ty + y * l,
-              w / PageWidth, l / PageLength);
+       doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
+                  tx + x * pagew * 0.25, ty + y * pagel * 0.25,
+                  w / bboxw, l / bboxl);
         break;
   }
 
@@ -2134,14 +3135,13 @@ start_nup(int       number,             /* I - Page number */
   * Draw borders as necessary...
   */
 
-  if (Border && show_border)
+  if (doc->page_border && show_border)
   {
-    int                rects;          /* Number of border rectangles */
-    float      fscale,         /* Scaling value for points */
-               margin;         /* Current margin for borders */
+    int                rects;                  /* Number of border rectangles */
+    float      fscale;                 /* Scaling value for points */
 
 
-    rects  = (Border & BORDER_DOUBLE) ? 2 : 1;
+    rects  = (doc->page_border & PSTOPS_BORDERDOUBLE) ? 2 : 1;
     fscale = PageWidth / w;
     margin = 2.25 * fscale;
 
@@ -2149,47 +3149,222 @@ start_nup(int       number,            /* I - Page number */
     * Set the line width and color...
     */
 
-    puts("gsave");
-    printf("%.3f setlinewidth 0 setgray newpath\n",
-           (Border & BORDER_THICK) ? 0.5 * fscale : 0.24 * fscale);
+    doc_puts(doc, "gsave\n");
+    doc_printf(doc, "%.3f setlinewidth 0 setgray newpath\n",
+               (doc->page_border & PSTOPS_BORDERTHICK) ? 0.5 * fscale :
+                                                        0.24 * fscale);
 
    /*
     * Draw border boxes...
     */
 
     for (; rects > 0; rects --, margin += 2 * fscale)
-      if (NUp > 1)
-       printf("%.1f %.1f %.1f %.1f ESPrs\n",
-              margin,
-              margin,
-              PageWidth - 2 * margin,
-              PageLength - 2 * margin);
+      if (doc->number_up > 1)
+       doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrs\n",
+                  margin,
+                  margin,
+                  bboxw - 2 * margin,
+                  bboxl - 2 * margin);
       else
-       printf("%.1f %.1f %.1f %.1f ESPrs\n",
-               PageLeft + margin,
-              PageBottom + margin,
-              PageRight - PageLeft - 2 * margin,
-              PageTop - PageBottom - 2 * margin);
+       doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrs\n",
+                  PageLeft + margin,
+                  PageBottom + margin,
+                  PageRight - PageLeft - 2 * margin,
+                  PageTop - PageBottom - 2 * margin);
 
    /*
     * Restore pen settings...
     */
 
-    puts("grestore");
+    doc_puts(doc, "grestore\n");
+  }
+
+  if (doc->fitplot)
+  {
+   /*
+    * Offset the page by its bounding box...
+    */
+
+    doc_printf(doc, "%d %d translate\n", -bounding_box[0],
+               -bounding_box[1]);
+  }
+
+  if (doc->fitplot || doc->number_up > 1)
+  {
+   /*
+    * Clip the page to the page's bounding box...
+    */
+
+    doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrc\n",
+               bboxx + margin, bboxy + margin,
+               bboxw - 2 * margin, bboxl - 2 * margin);
+  }
+}
+
+
+/*
+ * 'write_label_prolog()' - Write the prolog with the classification
+ *                          and page label.
+ */
+
+static void
+write_label_prolog(pstops_doc_t *doc,  /* I - Document info */
+                   const char   *label,        /* I - Page label */
+                  float        bottom, /* I - Bottom position in points */
+                  float        top,    /* I - Top position in points */
+                  float        width)  /* I - Width in points */
+{
+  const char   *classification;        /* CLASSIFICATION environment variable */
+  const char   *ptr;                   /* Temporary string pointer */
+
+
+ /*
+  * First get the current classification...
+  */
+
+  if ((classification = getenv("CLASSIFICATION")) == NULL)
+    classification = "";
+  if (strcmp(classification, "none") == 0)
+    classification = "";
+
+ /*
+  * If there is nothing to show, bind an empty 'write labels' procedure
+  * and return...
+  */
+
+  if (!classification[0] && (label == NULL || !label[0]))
+  {
+    doc_puts(doc, "userdict/ESPwl{}bind put\n");
+    return;
+  }
+
+ /*
+  * Set the classification + page label string...
+  */
+
+  doc_puts(doc, "userdict");
+  if (!strcmp(classification, "confidential"))
+    doc_puts(doc, "/ESPpl(CONFIDENTIAL");
+  else if (!strcmp(classification, "classified"))
+    doc_puts(doc, "/ESPpl(CLASSIFIED");
+  else if (!strcmp(classification, "secret"))
+    doc_puts(doc, "/ESPpl(SECRET");
+  else if (!strcmp(classification, "topsecret"))
+    doc_puts(doc, "/ESPpl(TOP SECRET");
+  else if (!strcmp(classification, "unclassified"))
+    doc_puts(doc, "/ESPpl(UNCLASSIFIED");
+  else
+  {
+    doc_puts(doc, "/ESPpl(");
+
+    for (ptr = classification; *ptr; ptr ++)
+    {
+      if (*ptr < 32 || *ptr > 126)
+        doc_printf(doc, "\\%03o", *ptr);
+      else if (*ptr == '_')
+        doc_puts(doc, " ");
+      else if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
+       doc_printf(doc, "\\%c", *ptr);
+      else
+        doc_printf(doc, "%c", *ptr);
+    }
   }
 
-  if (NUp > 1)
+  if (label)
   {
+    if (classification[0])
+      doc_puts(doc, " - ");
+
    /*
-    * Clip the page that follows to the bounding box of the page...
+    * Quote the label string as needed...
     */
 
-    printf("%d %d translate\n", -lbrt[0], -lbrt[1]);
-    printf("0 0 %.1f %.1f ESPrc\n", w, l);
+    for (ptr = label; *ptr; ptr ++)
+    {
+      if (*ptr < 32 || *ptr > 126)
+        doc_printf(doc, "\\%03o", *ptr);
+      else if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
+       doc_printf(doc, "\\%c", *ptr);
+      else
+        doc_printf(doc, "%c", *ptr);
+    }
+  }
+
+  doc_puts(doc, ")put\n");
+
+ /*
+  * Then get a 14 point Helvetica-Bold font...
+  */
+
+  doc_puts(doc, "userdict/ESPpf /Helvetica-Bold findfont 14 scalefont put\n");
+
+ /*
+  * Finally, the procedure to write the labels on the page...
+  */
+
+  doc_puts(doc, "userdict/ESPwl{\n");
+  doc_puts(doc, "  ESPpf setfont\n");
+  doc_printf(doc, "  ESPpl stringwidth pop dup 12 add exch -0.5 mul %.0f add\n",
+             width * 0.5f);
+  doc_puts(doc, "  1 setgray\n");
+  doc_printf(doc, "  dup 6 sub %.0f 3 index 20 ESPrf\n", bottom - 2.0);
+  doc_printf(doc, "  dup 6 sub %.0f 3 index 20 ESPrf\n", top - 18.0);
+  doc_puts(doc, "  0 setgray\n");
+  doc_printf(doc, "  dup 6 sub %.0f 3 index 20 ESPrs\n", bottom - 2.0);
+  doc_printf(doc, "  dup 6 sub %.0f 3 index 20 ESPrs\n", top - 18.0);
+  doc_printf(doc, "  dup %.0f moveto ESPpl show\n", bottom + 2.0);
+  doc_printf(doc, "  %.0f moveto ESPpl show\n", top - 14.0);
+  doc_puts(doc, "pop\n");
+  doc_puts(doc, "}bind put\n");
+}
+
+
+/*
+ * 'write_labels()' - Write the actual page labels.
+ *
+ * This function is a copy of the one in common.c since we need to
+ * use doc_puts/doc_printf instead of puts/printf...
+ */
+
+static void
+write_labels(pstops_doc_t *doc,                /* I - Document information */
+             int          orient)      /* I - Orientation of the page */
+{
+  float        width,                          /* Width of page */
+       length;                         /* Length of page */
+
+
+  doc_puts(doc, "gsave\n");
+
+  if ((orient ^ Orientation) & 1)
+  {
+    width  = PageLength;
+    length = PageWidth;
+  }
+  else
+  {
+    width  = PageWidth;
+    length = PageLength;
+  }
+
+  switch (orient & 3)
+  {
+    case 1 : /* Landscape */
+        doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", length);
+        break;
+    case 2 : /* Reverse Portrait */
+        doc_printf(doc, "%.1f %.1f translate 180 rotate\n", width, length);
+        break;
+    case 3 : /* Reverse Landscape */
+        doc_printf(doc, "0.0 %.1f translate -90 rotate\n", width);
+        break;
   }
+
+  doc_puts(doc, "ESPwl\n");
+  doc_puts(doc, "grestore\n");
 }
 
 
 /*
- * End of "$Id: pstops.c 5092 2006-02-09 00:54:31Z mike $".
+ * End of "$Id: pstops.c 7006 2007-10-04 17:43:38Z mike $".
  */