]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - filter/pstops.c
Update svn:keyword properties.
[thirdparty/cups.git] / filter / pstops.c
index 69dce4f5463799007a9b397fedf4388c1359675f..2230a8b6162aade52f77dacd88b0bc73c8701b5c 100644 (file)
@@ -1,55 +1,51 @@
 /*
- * "$Id: pstops.c 6094 2006-11-14 16:54:50Z mike $"
+ * "$Id$"
  *
- *   PostScript filter for the Common UNIX Printing System (CUPS).
+ *   PostScript filter for CUPS.
  *
- *   Copyright 1993-2006 by Easy Software Products.
+ *   Copyright 2007-2012 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...
+ *   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...
+ *                          printing.
+ *   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...
+ *   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...
+ *   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_label_prolog() - Write the prolog with the classification and page
+ *                          label.
  *   write_labels()       - Write the actual page labels.
+ *   write_options()      - Write options provided via %%IncludeFeature.
  */
 
 /*
@@ -61,6 +57,8 @@
 #include <math.h>
 #include <cups/file.h>
 #include <cups/array.h>
+#include <cups/language-private.h>
+#include <signal.h>
 
 
 /*
@@ -123,14 +121,20 @@ typedef struct                            /**** Document information ****/
                *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 */
+               *ap_manual_feed,        /* AP_FIRSTPAGE_ManualFeed value */
+               *ap_media_color,        /* AP_FIRSTPAGE_MediaColor value */
+               *ap_media_type,         /* AP_FIRSTPAGE_MediaType value */
+               *ap_page_region,        /* AP_FIRSTPAGE_PageRegion value */
+               *ap_page_size;          /* AP_FIRSTPAGE_PageSize value */
   int          collate,                /* Collate copies? */
                emit_jcl,               /* Emit JCL commands? */
-               fitplot;                /* Fit pages to media */
-  float                gamma;                  /* gamma value */
+               fit_to_page;            /* Fit pages to media */
   const char   *input_slot,            /* InputSlot value */
-               *manual_feed;           /* ManualFeed value */
+               *manual_feed,           /* ManualFeed value */
+               *media_color,           /* MediaColor value */
+               *media_type,            /* MediaType value */
+               *page_region,           /* PageRegion value */
+               *page_size;             /* PageSize 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 */
@@ -154,42 +158,47 @@ typedef struct                            /**** Document information ****/
                                 ((p) % doc->number_up) != 0)
 
 
+/*
+ * Local globals...
+ */
+
+static int             JobCanceled = 0;/* Set to 1 on SIGTERM */
+
+
 /*
  * Local functions...
  */
 
 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 size_t          copy_comments(cups_file_t *fp, pstops_doc_t *doc,
+static ssize_t         copy_comments(cups_file_t *fp, pstops_doc_t *doc,
                                      ppd_file_t *ppd, char *line,
-                                     size_t linelen, size_t linesize);
+                                     ssize_t linelen, size_t linesize);
 static void            copy_dsc(cups_file_t *fp, pstops_doc_t *doc,
-                                ppd_file_t *ppd, char *line, size_t linelen,
+                                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,
-                                    size_t linelen, size_t linesize);
-static size_t          copy_page(cups_file_t *fp, pstops_doc_t *doc,
+                                    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,
-                                 size_t linelen, size_t linesize);
-static size_t          copy_prolog(cups_file_t *fp, pstops_doc_t *doc,
+                                 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,
-                                   size_t linelen, size_t linesize);
-static size_t          copy_setup(cups_file_t *fp, pstops_doc_t *doc,
+                                   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,
-                                  size_t linelen, size_t linesize);
-static size_t          copy_trailer(cups_file_t *fp, pstops_doc_t *doc,
+                                  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,
-                                    size_t linelen, size_t linesize);
+                                    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__ */
-;
+                       __attribute__ ((__format__ (__printf__, 2, 3)));
 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);
@@ -201,15 +210,20 @@ static char               *parse_text(const char *start, char **end, char *buffer,
 static void            set_pstops_options(pstops_doc_t *doc, ppd_file_t *ppd,
                                           char *argv[], int num_options,
                                           cups_option_t *options);
-static size_t          skip_page(cups_file_t *fp, char *line, size_t linelen,
+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);
+static void            write_options(pstops_doc_t  *doc, ppd_file_t *ppd,
+                                     int num_options, cups_option_t *options);
 
 
 /*
- * 'main()' - Main entry...
+ * 'main()' - Main entry.
  */
 
 int                                    /* O - Exit status */
@@ -223,6 +237,9 @@ main(int  argc,                             /* I - Number of command-line args */
   cups_option_t        *options;               /* Print options */
   char         line[8192];             /* Line buffer */
   size_t       len;                    /* Length of line buffer */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+  struct sigaction action;             /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
 
 
  /*
@@ -231,16 +248,40 @@ main(int  argc,                           /* I - Number of command-line args */
 
   setbuf(stderr, NULL);
 
+ /*
+  * Ignore broken pipe signals...
+  */
+
+  signal(SIGPIPE, SIG_IGN);
+
  /*
   * Check command-line...
   */
 
   if (argc < 6 || argc > 7)
   {
-    fputs("ERROR: pstops job-id user title copies options [file]\n", stderr);
+    _cupsLangPrintf(stderr,
+                    _("Usage: %s job-id user title copies options [file]"),
+                    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...
@@ -256,8 +297,7 @@ main(int  argc,                             /* I - Number of command-line args */
 
     if ((fp = cupsFileOpen(argv[6], "r")) == NULL)
     {
-      fprintf(stderr, "ERROR: Unable to open print file \"%s\" - %s\n",
-              argv[6], strerror(errno));
+      _cupsLangPrintError("ERROR", _("Unable to open print file"));
       return (1);
     }
   }
@@ -268,7 +308,7 @@ main(int  argc,                             /* I - Number of command-line args */
 
   if ((len = cupsFileGetLine(fp, line, sizeof(line))) == 0)
   {
-    fputs("ERROR: Empty print file!\n", stderr);
+    fputs("DEBUG: The print file is empty.\n", stderr);
     return (1);
   }
 
@@ -387,7 +427,7 @@ main(int  argc,                             /* I - Number of command-line args */
 
 
 /*
- * 'add_page()' - Add a page to the pages array...
+ * 'add_page()' - Add a page to the pages array.
  */
 
 static pstops_page_t *                 /* O - New page info object */
@@ -402,15 +442,13 @@ add_page(pstops_doc_t *doc,               /* I - Document information */
 
   if (!doc->pages)
   {
-    fprintf(stderr, "EMERG: Unable to allocate memory for pages array: %s\n",
-            strerror(errno));
+    _cupsLangPrintError("EMERG", _("Unable to allocate memory for pages array"));
     exit(1);
   }
 
   if ((pageinfo = calloc(1, sizeof(pstops_page_t))) == NULL)
   {
-    fprintf(stderr, "EMERG: Unable to allocate memory for page info: %s\n",
-            strerror(errno));
+    _cupsLangPrintError("EMERG", _("Unable to allocate memory for page info"));
     exit(1);
   }
 
@@ -425,6 +463,19 @@ add_page(pstops_doc_t *doc,                /* I - Document information */
 }
 
 
+/*
+ * 'cancel_job()' - Flag the job as canceled.
+ */
+
+static void
+cancel_job(int sig)                    /* I - Signal number (unused) */
+{
+  (void)sig;
+
+  JobCanceled = 1;
+}
+
+
 /*
  * 'check_range()' - Check to see if the current page is selected for
  *                   printing.
@@ -444,10 +495,10 @@ check_range(pstops_doc_t *doc,            /* I - Document information */
     * See if we only print even or odd pages...
     */
 
-    if (!strcasecmp(doc->page_set, "even") && (page & 1))
+    if (!_cups_strcasecmp(doc->page_set, "even") && (page & 1))
       return (0);
 
-    if (!strcasecmp(doc->page_set, "odd") && !(page & 1))
+    if (!_cups_strcasecmp(doc->page_set, "odd") && !(page & 1))
       return (0);
   }
 
@@ -492,7 +543,7 @@ check_range(pstops_doc_t *doc,              /* I - Document information */
 
 
 /*
- * 'copy_bytes()' - Copy bytes from the input file to stdout...
+ * 'copy_bytes()' - Copy bytes from the input file to stdout.
  */
 
 static void
@@ -509,9 +560,7 @@ copy_bytes(cups_file_t *fp,         /* I - File to read from */
 
   if (cupsFileSeek(fp, offset) < 0)
   {
-    fprintf(stderr, "ERROR: Unable to seek to offset " CUPS_LLFMT
-                    " in file - %s\n",
-            CUPS_LLCAST offset, strerror(errno));
+    _cupsLangPrintError("ERROR", _("Unable to see in file"));
     return;
   }
 
@@ -533,18 +582,18 @@ copy_bytes(cups_file_t *fp,               /* I - File to read from */
 
 
 /*
- * 'copy_comments()' - Copy all of the comments section...
+ * '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.
  */
 
-static size_t                          /* O - Length of next line */
+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 */
-             size_t       linelen,     /* I - Length of initial line */
+             ssize_t      linelen,     /* I - Length of initial line */
              size_t       linesize)    /* I - Size of line buffer */
 {
   int  saw_bounding_box,               /* Saw %%BoundingBox: comment? */
@@ -560,7 +609,7 @@ copy_comments(cups_file_t  *fp,             /* I - File to read from */
   saw_bounding_box = 0;
   saw_for          = 0;
   saw_pages        = 0;
-  saw_title        = 0;        
+  saw_title        = 0;
 
   while (line[0] == '%')
   {
@@ -592,9 +641,8 @@ copy_comments(cups_file_t  *fp,             /* I - File to read from */
     {
       int      pages;                  /* Number of pages */
 
-
       if (saw_pages)
-        fputs("ERROR: Duplicate %%Pages: comment seen!\n", stderr);
+       fputs("DEBUG: A duplicate %%Pages: comment was seen.\n", stderr);
 
       saw_pages = 1;
 
@@ -641,7 +689,7 @@ copy_comments(cups_file_t  *fp,             /* I - File to read from */
     else if (!strncmp(line, "%%BoundingBox:", 14))
     {
       if (saw_bounding_box)
-        fputs("ERROR: Duplicate %%BoundingBox: comment seen!\n", stderr);
+       fputs("DEBUG: A duplicate %%BoundingBox: comment was seen.\n", stderr);
       else if (strstr(line + 14, "(atend)"))
       {
        /*
@@ -652,7 +700,7 @@ copy_comments(cups_file_t  *fp,             /* I - File to read from */
                      doc->bounding_box + 1, doc->bounding_box + 2,
                      doc->bounding_box + 3) != 4)
       {
-       fputs("ERROR: Bad %%BoundingBox: comment seen!\n", stderr);
+       fputs("DEBUG: A bad %%BoundingBox: comment was seen.\n", stderr);
 
        doc->bounding_box[0] = (int)PageLeft;
        doc->bounding_box[1] = (int)PageBottom;
@@ -665,12 +713,12 @@ copy_comments(cups_file_t  *fp,           /* I - File to read from */
     else if (!strncmp(line, "%%For:", 6))
     {
       saw_for = 1;
-      printf("%s\n", line);
+      doc_printf(doc, "%s\n", line);
     }
     else if (!strncmp(line, "%%Title:", 8))
     {
       saw_title = 1;
-      printf("%s\n", line);
+      doc_printf(doc, "%s\n", line);
     }
     else if (!strncmp(line, "%cupsRotation:", 14))
     {
@@ -697,17 +745,18 @@ copy_comments(cups_file_t  *fp,           /* I - File to read from */
       break;
     }
     else if (strncmp(line, "%!", 2) && strncmp(line, "%cups", 5))
-      printf("%s\n", line);
+      doc_printf(doc, "%s\n", line);
 
     if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
       break;
   }
 
   if (!saw_bounding_box)
-    fputs("ERROR: No %%BoundingBox: comment in header!\n", stderr);
+    fputs("DEBUG: There wasn't a %%BoundingBox: comment in the header.\n",
+          stderr);
 
   if (!saw_pages)
-    fputs("ERROR: No %%Pages: comment in header!\n", stderr);
+    fputs("DEBUG: There wasn't a %%Pages: comment in the header.\n", stderr);
 
   if (!saw_for)
     WriteTextComment("For", doc->user);
@@ -722,15 +771,15 @@ copy_comments(cups_file_t  *fp,           /* I - File to read from */
     * that are required...
     */
 
-    printf("%%%%Requirements: numcopies(%d)%s%s\n", doc->copies,
-           doc->collate ? " collate" : "",
-          Duplex ? " duplex" : "");
+    doc_printf(doc, "%%%%Requirements: numcopies(%d)%s%s\n", doc->copies,
+               doc->collate ? " collate" : "",
+              Duplex ? " duplex" : "");
 
    /*
     * Apple uses RBI comments for various non-PPD options...
     */
 
-    printf("%%RBINumCopies: %d\n", doc->copies);
+    doc_printf(doc, "%%RBINumCopies: %d\n", doc->copies);
   }
   else
   {
@@ -739,25 +788,25 @@ copy_comments(cups_file_t  *fp,           /* I - File to read from */
     */
 
     if (Duplex)
-      puts("%%Requirements: duplex");
+      doc_puts(doc, "%%Requirements: duplex\n");
 
    /*
     * Apple uses RBI comments for various non-PPD options...
     */
 
-    puts("%RBINumCopies: 1");
+    doc_puts(doc, "%RBINumCopies: 1\n");
   }
 
-  puts("%%Pages: (atend)");
-  puts("%%BoundingBox: (atend)");
-  puts("%%EndComments");
+  doc_puts(doc, "%%Pages: (atend)\n");
+  doc_puts(doc, "%%BoundingBox: (atend)\n");
+  doc_puts(doc, "%%EndComments\n");
 
   return (linelen);
 }
 
 
 /*
- * 'copy_dsc()' - Copy a DSC-conforming document...
+ * 'copy_dsc()' - Copy a DSC-conforming document.
  *
  * This function expects "line" to be filled with the %!PS-Adobe comment line.
  */
@@ -767,7 +816,7 @@ 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 */
-        size_t       linelen,          /* I - Length of initial line */
+        ssize_t      linelen,          /* I - Length of initial line */
         size_t       linesize)         /* I - Size of line buffer */
 {
   int          number;                 /* Page number */
@@ -811,7 +860,7 @@ copy_dsc(cups_file_t  *fp,          /* I - File to read from */
 
   while (strncmp(line, "%%Page:", 7) && strncmp(line, "%%Trailer", 9))
   {
-    fwrite(line, 1, linelen, stdout);
+    doc_write(doc, line, linelen);
 
     if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
       break;
@@ -826,6 +875,9 @@ copy_dsc(cups_file_t  *fp,          /* I - File to read from */
   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))
@@ -844,7 +896,8 @@ copy_dsc(cups_file_t  *fp,          /* I - File to read from */
   * Finish up the last page(s)...
   */
 
-  if (number && is_not_last_page(number) && cupsArrayLast(doc->pages))
+  if (number && is_not_last_page(number) && cupsArrayLast(doc->pages) &&
+      check_range(doc, (number - 1) / doc->number_up + 1))
   {
     pageinfo = (pstops_page_t *)cupsArrayLast(doc->pages);
 
@@ -885,7 +938,7 @@ copy_dsc(cups_file_t  *fp,          /* I - File to read from */
 
   number = doc->slow_order ? 0 : doc->page;
 
-  if (doc->temp)
+  if (doc->temp && !JobCanceled && cupsArrayCount(doc->pages) > 0)
   {
     int        copy;                           /* Current copy */
 
@@ -902,30 +955,59 @@ copy_dsc(cups_file_t  *fp,                /* I - File to read from */
     * Make the copies...
     */
 
-    for (copy = !doc->slow_order; copy < doc->copies; copy ++)
+    if (doc->slow_collate)
+      copy = !doc->slow_order;
+    else
+      copy = doc->copies - 1;
+
+    for (; copy < doc->copies; copy ++)
     {
+      if (JobCanceled)
+       break;
+
      /*
       * Send end-of-job stuff followed by any start-of-job stuff required
       * for the JCL options...
       */
 
-      if (!doc->saw_eof)
-       puts("%%EOF");
-
-      if (doc->emit_jcl)
+      if (number && doc->emit_jcl && ppd && ppd->jcl_end)
       {
-       if (ppd && ppd->jcl_end)
-         ppdEmitJCLEnd(ppd, stdout);
+       /*
+        * Send the trailer...
+       */
+
+        puts("%%Trailer");
+       printf("%%%%Pages: %d\n", cupsArrayCount(doc->pages));
+       if (doc->number_up > 1 || doc->fit_to_page)
+         printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
+                PageLeft, PageBottom, PageRight, PageTop);
        else
-         putchar(0x04);
+         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;
       }
 
-      puts("%!PS-Adobe-3.0");
-      puts("%%Pages: (atend)");
-      puts("%%BoundingBox: (atend)");
-      puts("%%EndComments");
+     /*
+      * Copy the prolog as needed...
+      */
+
+      if (!number)
+      {
+        pageinfo = (pstops_page_t *)cupsArrayFirst(doc->pages);
+       copy_bytes(doc->temp, 0, pageinfo->offset);
+      }
 
      /*
       * Then copy all of the pages...
@@ -936,10 +1018,14 @@ copy_dsc(cups_file_t  *fp,               /* I - File to read from */
 
       while (pageinfo)
       {
+        if (JobCanceled)
+         break;
+
         number ++;
 
        if (!ppd || !ppd->num_filters)
-         fprintf(stderr, "PAGE: %d 1\n", number);
+         fprintf(stderr, "PAGE: %d %d\n", number,
+                 doc->slow_collate ? 1 : doc->copies);
 
        if (doc->number_up > 1)
        {
@@ -974,12 +1060,13 @@ copy_dsc(cups_file_t  *fp,               /* I - File to read from */
   * Write/copy the trailer...
   */
 
-  linelen = copy_trailer(fp, doc, ppd, number, line, linelen, linesize);
+  if (!JobCanceled)
+    copy_trailer(fp, doc, ppd, number, line, linelen, linesize);
 }
 
 
 /*
- * 'copy_non_dsc()' - Copy a document that does not conform to the DSC...
+ * 'copy_non_dsc()' - Copy a document that does not conform to the DSC.
  *
  * This function expects "line" to be filled with the %! comment line.
  */
@@ -989,7 +1076,7 @@ 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 */
-            size_t       linelen,      /* I - Length of initial line */
+            ssize_t      linelen,      /* I - Length of initial line */
             size_t       linesize)     /* I - Size of line buffer */
 {
   int  copy;                           /* Current copy */
@@ -1002,8 +1089,8 @@ copy_non_dsc(cups_file_t  *fp,            /* I - File to read from */
   * 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);
+  fputs("DEBUG: 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...
@@ -1109,7 +1196,7 @@ copy_non_dsc(cups_file_t  *fp,            /* I - File to read from */
     puts("ESPshowpage");
   }
 
-  if (doc->temp)
+  if (doc->temp && !JobCanceled)
   {
    /*
     * Reopen the temporary file for reading...
@@ -1125,6 +1212,9 @@ copy_non_dsc(cups_file_t  *fp,            /* I - File to read from */
 
     for (copy = 1; copy < doc->copies; copy ++)
     {
+      if (JobCanceled)
+       break;
+
       if (!ppd || !ppd->num_filters)
        fputs("PAGE: 1 1\n", stderr);
 
@@ -1156,19 +1246,19 @@ copy_non_dsc(cups_file_t  *fp,          /* I - File to read from */
 
 
 /*
- * 'copy_page()' - Copy a page description...
+ * '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 size_t                          /* O - Length of next line */
+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 */
-         size_t       linelen,         /* I - Length of initial line */
+         ssize_t      linelen,         /* I - Length of initial line */
          size_t       linesize)        /* I - Size of line buffer */
 {
   char         label[256],             /* Page label string */
@@ -1176,6 +1266,7 @@ copy_page(cups_file_t  *fp,               /* I - File to read from */
   int          level;                  /* Embedded document level */
   pstops_page_t        *pageinfo;              /* Page information */
   int          first_page;             /* First page on N-up output? */
+  int          has_page_setup = 0;     /* Does the page have %%Begin/EndPageSetup? */
   int          bounding_box[4];        /* PageBoundingBox */
 
 
@@ -1187,13 +1278,13 @@ copy_page(cups_file_t  *fp,             /* I - File to read from */
 
   if (!parse_text(line + 7, &ptr, label, sizeof(label)))
   {
-    fputs("ERROR: Bad %%Page: comment in file!\n", stderr);
+    fputs("DEBUG: There was a bad %%Page: comment in the 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);
+    fputs("DEBUG: There was a bad %%Page: comment in the file.\n", stderr);
     number = doc->page;
   }
 
@@ -1212,7 +1303,8 @@ copy_page(cups_file_t  *fp,               /* I - File to read from */
 
   if (doc->ap_input_slot || doc->ap_manual_feed)
   {
-    if (doc->page == 1)
+    if ((doc->page == 1 && (!doc->slow_order || !Duplex)) ||
+        (doc->page == 2 && doc->slow_order && Duplex))
     {
      /*
       * First page/sheet gets AP_FIRSTPAGE_* options...
@@ -1221,7 +1313,21 @@ copy_page(cups_file_t  *fp,              /* I - File to read from */
       pageinfo->num_options = cupsAddOption("InputSlot", doc->ap_input_slot,
                                             pageinfo->num_options,
                                            &(pageinfo->options));
-      pageinfo->num_options = cupsAddOption("ManualFeed", doc->ap_manual_feed,
+      pageinfo->num_options = cupsAddOption("ManualFeed",
+                                            doc->ap_input_slot ? "False" :
+                                               doc->ap_manual_feed,
+                                            pageinfo->num_options,
+                                           &(pageinfo->options));
+      pageinfo->num_options = cupsAddOption("MediaColor", doc->ap_media_color,
+                                            pageinfo->num_options,
+                                           &(pageinfo->options));
+      pageinfo->num_options = cupsAddOption("MediaType", doc->ap_media_type,
+                                            pageinfo->num_options,
+                                           &(pageinfo->options));
+      pageinfo->num_options = cupsAddOption("PageRegion", doc->ap_page_region,
+                                            pageinfo->num_options,
+                                           &(pageinfo->options));
+      pageinfo->num_options = cupsAddOption("PageSize", doc->ap_page_size,
                                             pageinfo->num_options,
                                            &(pageinfo->options));
     }
@@ -1234,7 +1340,21 @@ copy_page(cups_file_t  *fp,              /* I - File to read from */
       pageinfo->num_options = cupsAddOption("InputSlot", doc->input_slot,
                                             pageinfo->num_options,
                                            &(pageinfo->options));
-      pageinfo->num_options = cupsAddOption("ManualFeed", doc->manual_feed,
+      pageinfo->num_options = cupsAddOption("ManualFeed",
+                                            doc->input_slot ? "False" :
+                                               doc->manual_feed,
+                                            pageinfo->num_options,
+                                           &(pageinfo->options));
+      pageinfo->num_options = cupsAddOption("MediaColor", doc->media_color,
+                                            pageinfo->num_options,
+                                           &(pageinfo->options));
+      pageinfo->num_options = cupsAddOption("MediaType", doc->media_type,
+                                            pageinfo->num_options,
+                                           &(pageinfo->options));
+      pageinfo->num_options = cupsAddOption("PageRegion", doc->page_region,
+                                            pageinfo->num_options,
+                                           &(pageinfo->options));
+      pageinfo->num_options = cupsAddOption("PageSize", doc->page_size,
                                             pageinfo->num_options,
                                            &(pageinfo->options));
     }
@@ -1259,47 +1379,51 @@ copy_page(cups_file_t  *fp,             /* I - File to read from */
                  bounding_box + 1, bounding_box + 2,
                 bounding_box + 3) != 4)
       {
-        fputs("ERROR: Bad %%PageBoundingBox: comment in file!\n", stderr);
+       fputs("DEBUG: There was a bad %%PageBoundingBox: comment in the file.\n", stderr);
         memcpy(bounding_box, doc->bounding_box,
               sizeof(bounding_box));
       }
-      else if (doc->number_up == 1 && !doc->fitplot)
+      else if (doc->number_up == 1 && !doc->fit_to_page  && 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 0 : /* Portrait */
-              break;
-
          case 1 : /* Landscape */
-             temp_bbox[0] = PageWidth - bounding_box[3];
-             temp_bbox[1] = bounding_box[0];
-             temp_bbox[2] = PageWidth - bounding_box[1];
-             temp_bbox[3] = bounding_box[2];
-
-             memcpy(bounding_box, temp_bbox, sizeof(bounding_box));
+             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 */
-             temp_bbox[0] = PageWidth - bounding_box[0];
-             temp_bbox[1] = PageLength - bounding_box[1];
-             temp_bbox[2] = PageWidth - bounding_box[2];
-             temp_bbox[3] = PageLength - bounding_box[3];
-
-             memcpy(bounding_box, temp_bbox, sizeof(bounding_box));
+             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 */
-             temp_bbox[0] = bounding_box[1];
-             temp_bbox[1] = PageLength - bounding_box[2];
-             temp_bbox[2] = bounding_box[3];
-             temp_bbox[3] = PageLength - bounding_box[0];
-
-             memcpy(bounding_box, temp_bbox, sizeof(bounding_box));
+             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
@@ -1357,12 +1481,17 @@ copy_page(cups_file_t  *fp,             /* I - File to read from */
       * %%IncludeFeature: *MainKeyword OptionKeyword
       */
 
-      if (doc->number_up == 1 &&!doc->fitplot)
+      if (doc->number_up == 1 &&!doc->fit_to_page)
        pageinfo->num_options = include_feature(ppd, line,
                                                pageinfo->num_options,
                                                &(pageinfo->options));
     }
-    else if (strncmp(line, "%%Include", 9))
+    else if (!strncmp(line, "%%BeginPageSetup", 16))
+    {
+      has_page_setup = 1;
+      break;
+    }
+    else
       break;
   }
 
@@ -1415,111 +1544,44 @@ copy_page(cups_file_t  *fp,            /* I - File to read from */
   */
 
   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))
+  if (has_page_setup)
   {
     int        feature = 0;                    /* In a Begin/EndFeature block? */
 
-
     while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0)
     {
       if (!strncmp(line, "%%EndPageSetup", 14))
-        break;
+       break;
       else if (!strncmp(line, "%%BeginFeature:", 15))
       {
-        feature = 1;
+       feature = 1;
 
-       if (doc->number_up > 1 || doc->fitplot)
+       if (doc->number_up > 1 || doc->fit_to_page)
          continue;
       }
-      else if (!strncmp(line, "%%EndFeature:", 13))
+      else if (!strncmp(line, "%%EndFeature", 12))
       {
-        feature = 0;
+       feature = 0;
 
-       if (doc->number_up > 1 || doc->fitplot)
+       if (doc->number_up > 1 || doc->fit_to_page)
          continue;
       }
+      else if (!strncmp(line, "%%IncludeFeature:", 17))
+      {
+       pageinfo->num_options = include_feature(ppd, line,
+                                               pageinfo->num_options,
+                                               &(pageinfo->options));
+       continue;
+      }
       else if (!strncmp(line, "%%Include", 9))
-        continue;
+       continue;
 
-      if (!feature || (doc->number_up == 1 && !doc->fitplot))
+      if (line[0] != '%' && !feature)
+        break;
+
+      if (!feature || (doc->number_up == 1 && !doc->fit_to_page))
        doc_write(doc, line, linelen);
     }
 
@@ -1527,14 +1589,37 @@ copy_page(cups_file_t  *fp,             /* I - File to read from */
     * Skip %%EndPageSetup...
     */
 
-    if (linelen > 0)
+    if (linelen > 0 && !strncmp(line, "%%EndPageSetup", 14))
       linelen = cupsFileGetLine(fp, line, linesize);
   }
 
+  if (first_page)
+  {
+    char       *page_setup;            /* PageSetup commands to send */
+
+
+    if (pageinfo->num_options > 0)
+      write_options(doc, ppd, pageinfo->num_options, pageinfo->options);
+
+   /*
+    * 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);
+    }
+  }
+
  /*
-  * Finish the PageSetup section as needed...
+  * Prep for the start of the page description...
   */
 
+  start_nup(doc, number, 1, bounding_box);
+
   if (first_page)
     doc_puts(doc, "%%EndPageSetup\n");
 
@@ -1617,18 +1702,18 @@ copy_page(cups_file_t  *fp,             /* I - File to read from */
 
 
 /*
- * 'copy_prolog()' - Copy the document prolog section...
+ * '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 size_t                          /* O - Length of next line */
+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 */
-           size_t       linelen,       /* I - Length of initial line */
+           ssize_t      linelen,       /* I - Length of initial line */
            size_t       linesize)      /* I - Size of line buffer */
 {
   while (strncmp(line, "%%BeginProlog", 13))
@@ -1636,13 +1721,13 @@ copy_prolog(cups_file_t  *fp,           /* I - File to read from */
     if (!strncmp(line, "%%BeginSetup", 12) || !strncmp(line, "%%Page:", 7))
       break;
 
-    fwrite(line, 1, linelen, stdout);
+    doc_write(doc, line, linelen);
 
     if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
       break;
   }
 
-  puts("%%BeginProlog");
+  doc_puts(doc, "%%BeginProlog\n");
 
   do_prolog(doc, ppd);
 
@@ -1655,51 +1740,58 @@ copy_prolog(cups_file_t  *fp,           /* I - File to read from */
           !strncmp(line, "%%Page:", 7))
         break;
 
-      fwrite(line, 1, linelen, stdout);
+      doc_write(doc, line, linelen);
     }
 
     if (!strncmp(line, "%%EndProlog", 11))
       linelen = cupsFileGetLine(fp, line, linesize);
     else
-      fputs("ERROR: Missing %%EndProlog!\n", stderr);
+      fputs("DEBUG: The %%EndProlog comment is missing.\n", stderr);
   }
 
-  puts("%%EndProlog");
+  doc_puts(doc, "%%EndProlog\n");
 
   return (linelen);
 }
 
 
 /*
- * 'copy_setup()' - Copy the document setup section...
+ * '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 size_t                          /* O - Length of next line */
+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 */
-          size_t       linelen,        /* I - Length of initial line */
+          ssize_t      linelen,        /* I - Length of initial line */
           size_t       linesize)       /* I - Size of line buffer */
 {
+  int          num_options;            /* Number of options */
+  cups_option_t        *options;               /* Options */
+
+
   while (strncmp(line, "%%BeginSetup", 12))
   {
     if (!strncmp(line, "%%Page:", 7))
       break;
 
-    fwrite(line, 1, linelen, stdout);
+    doc_write(doc, line, linelen);
 
     if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
       break;
   }
 
-  puts("%%BeginSetup");
-  
+  doc_puts(doc, "%%BeginSetup\n");
+
   do_setup(doc, ppd);
 
+  num_options = 0;
+  options     = NULL;
+
   if (!strncmp(line, "%%BeginSetup", 12))
   {
     while (strncmp(line, "%%EndSetup", 10))
@@ -1712,12 +1804,11 @@ copy_setup(cups_file_t  *fp,            /* I - File to read from */
        * %%IncludeFeature: *MainKeyword OptionKeyword
        */
 
-        if (doc->number_up == 1 && !doc->fitplot)
-         doc->num_options = include_feature(ppd, line, doc->num_options,
-                                             &(doc->options));
+        if (doc->number_up == 1 && !doc->fit_to_page)
+         num_options = include_feature(ppd, line, num_options, &options);
       }
       else if (strncmp(line, "%%BeginSetup", 12))
-        fwrite(line, 1, linelen, stdout);
+        doc_write(doc, line, linelen);
 
       if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
        break;
@@ -1726,29 +1817,35 @@ copy_setup(cups_file_t  *fp,            /* I - File to read from */
     if (!strncmp(line, "%%EndSetup", 10))
       linelen = cupsFileGetLine(fp, line, linesize);
     else
-      fputs("ERROR: Missing %%EndSetup!\n", stderr);
+      fputs("DEBUG: The %%EndSetup comment is missing.\n", stderr);
   }
 
-  puts("%%EndSetup");
+  if (num_options > 0)
+  {
+    write_options(doc, ppd, num_options, options);
+    cupsFreeOptions(num_options, options);
+  }
+
+  doc_puts(doc, "%%EndSetup\n");
 
   return (linelen);
 }
 
 
 /*
- * 'copy_trailer()' - Copy the document trailer...
+ * '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 size_t                          /* O - Length of next line */
+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 */
-            size_t       linelen,      /* I - Length of initial line */
+            ssize_t      linelen,      /* I - Length of initial line */
             size_t       linesize)     /* I - Size of line buffer */
 {
  /*
@@ -1772,7 +1869,7 @@ copy_trailer(cups_file_t  *fp,            /* I - File to read from */
   fprintf(stderr, "DEBUG: Wrote %d pages...\n", number);
 
   printf("%%%%Pages: %d\n", number);
-  if (doc->number_up > 1 || doc->fitplot)
+  if (doc->number_up > 1 || doc->fit_to_page)
     printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
           PageLeft, PageBottom, PageRight, PageTop);
   else
@@ -1785,25 +1882,32 @@ copy_trailer(cups_file_t  *fp,          /* I - File to read from */
 
 
 /*
- * 'do_prolog()' - Send the necessary document prolog commands...
+ * '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)
   {
-    puts("%%BeginFeature: *JobPatchFile 1");
-    puts(ppd->patches);
-    puts("%%EndFeature");
+    doc_puts(doc, "%%BeginFeature: *JobPatchFile 1\n");
+    doc_puts(doc, ppd->patches);
+    doc_puts(doc, "\n%%EndFeature\n");
   }
 
-  ppdEmit(ppd, stdout, PPD_ORDER_PROLOG);
+  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
@@ -1811,30 +1915,32 @@ do_prolog(pstops_doc_t *doc,            /* I - Document information */
   */
 
   if (doc->use_ESPshowpage)
-    puts("userdict/ESPshowpage/showpage load put\n"
-        "userdict/showpage{}put");
-
+    doc_puts(doc, "userdict/ESPshowpage/showpage load put\n"
+                 "userdict/showpage{}put\n");
 }
 
 
 /*
- * 'do_setup()' - Send the necessary document setup commands...
+ * '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...
   */
 
-  puts("% Disable CTRL-D as an end-of-file marker...");
-  puts("userdict dup(\\004)cvn{}put (\\004\\004)cvn{}put");
+  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...
+  * Mark job options...
   */
 
   cupsMarkOptions(ppd, doc->num_options, doc->options);
@@ -1843,8 +1949,17 @@ do_setup(pstops_doc_t *doc,              /* I - Document information */
   * Send all the printer-specific setup commands...
   */
 
-  ppdEmit(ppd, stdout, PPD_ORDER_DOCUMENT);
-  ppdEmit(ppd, stdout, PPD_ORDER_ANY);
+  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...
@@ -1852,11 +1967,13 @@ do_setup(pstops_doc_t *doc,             /* I - Document information */
 
   if (doc->copies != 1 && (!doc->collate || !doc->slow_collate))
   {
-    printf("%%RBIBeginNonPPDFeature: *NumCopies %d\n", doc->copies);
-    printf("%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);
-    puts("%RBIEndNonPPDFeature");
+    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");
   }
 
  /*
@@ -1864,23 +1981,32 @@ do_setup(pstops_doc_t *doc,             /* I - Document information */
   */
 
   if (doc->number_up > 1)
-    puts("userdict/setpagedevice{pop}bind put");
+  {
+    doc_puts(doc, "userdict/CUPSsetpagedevice/setpagedevice load put\n");
+    doc_puts(doc, "userdict/setpagedevice{pop}bind put\n");
+  }
 
  /*
-  * Changes to the transfer function must be made AFTER any
-  * setpagedevice code...
+  * Make sure we have rectclip and rectstroke procedures of some sort...
   */
 
-  if (doc->gamma != 1.0f || doc->brightness != 1.0f)
-    printf("{ neg 1 add dup 0 lt { pop 1 } { %.3f exp neg 1 add } "
-          "ifelse %.3f mul } bind settransfer\n", doc->gamma,
-          doc->brightness);
+  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");
 
- /*
-  * Make sure we have rectclip and rectstroke procedures of some sort...
-  */
+  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");
 
-  WriteCommon();
+  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...
@@ -1894,13 +2020,14 @@ do_setup(pstops_doc_t *doc,             /* I - Document information */
     */
 
     if (Orientation & 1)
-      WriteLabelProlog(doc->page_label, PageBottom,
-                       PageWidth - PageLength + PageTop, PageLength);
+      write_label_prolog(doc, doc->page_label, PageBottom,
+                         PageWidth - PageLength + PageTop, PageLength);
     else
-      WriteLabelProlog(doc->page_label, PageLeft, PageRight, PageLength);
+      write_label_prolog(doc, doc->page_label, PageLeft, PageRight,
+                         PageLength);
   }
   else
-    WriteLabelProlog(doc->page_label, PageBottom, PageTop, PageWidth);
+    write_label_prolog(doc, doc->page_label, PageBottom, PageTop, PageWidth);
 }
 
 
@@ -1927,9 +2054,8 @@ doc_printf(pstops_doc_t *doc,             /* I - Document information */
 
   if (bytes > sizeof(buffer))
   {
-    fprintf(stderr,
-            "ERROR: doc_printf overflow (%d bytes) detected, aborting!\n",
-            (int)bytes);
+    _cupsLangPrintFilter(stderr, "ERROR",
+                         _("Buffer overflow detected, aborting."));
     exit(1);
   }
 
@@ -1970,7 +2096,7 @@ doc_write(pstops_doc_t *doc,              /* I - Document information */
 
 
 /*
- * 'end_nup()' - End processing for N-up printing...
+ * 'end_nup()' - End processing for N-up printing.
  */
 
 static void
@@ -2050,7 +2176,6 @@ include_feature(
   char         name[255],              /* Option name */
                value[255];             /* Option value */
   ppd_option_t *option;                /* Option in file */
-  ppd_choice_t *choice;                /* Choice */
 
 
  /*
@@ -2059,7 +2184,7 @@ include_feature(
 
   if (sscanf(line + 17, "%254s%254s", name, value) != 2)
   {
-    fputs("ERROR: Bad %%IncludeFeature: comment!\n", stderr);
+    fputs("DEBUG: The %%IncludeFeature: comment is not valid.\n", stderr);
     return (num_options);
   }
 
@@ -2069,22 +2194,25 @@ include_feature(
 
   if ((option = ppdFindOption(ppd, name + 1)) == NULL)
   {
-    fprintf(stderr, "WARNING: Unknown option \"%s\"!\n", name + 1);
+    _cupsLangPrintFilter(stderr, "WARNING", _("Unknown option \"%s\"."),
+                         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);
+    _cupsLangPrintFilter(stderr, "WARNING",
+                         _("Option \"%s\" cannot be included via "
+                          "%%%%IncludeFeature."), name + 1);
     return (num_options);
   }
 
-  if ((choice = ppdFindChoice(option, value)) == NULL)
+  if (!ppdFindChoice(option, value))
   {
-    fprintf(stderr, "WARNING: Unknown choice \"%s\" for option \"%s\"!\n",
-            value, name + 1);
+    _cupsLangPrintFilter(stderr, "WARNING",
+                        _("Unknown choice \"%s\" for option \"%s\"."),
+                        value, name + 1);
     return (num_options);
   }
 
@@ -2097,7 +2225,7 @@ include_feature(
 
 
 /*
- * 'parse_text()' - Parse a text value in a comment...
+ * '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
@@ -2186,7 +2314,7 @@ parse_text(const char *start,             /* I - Start of text value */
 
 
 /*
- * 'set_pstops_options()' - Set pstops options...
+ * 'set_pstops_options()' - Set pstops options.
  */
 
 static void
@@ -2202,6 +2330,8 @@ set_pstops_options(
   ppd_attr_t   *attr;                  /* PPD attribute */
   ppd_option_t *option;                /* PPD option */
   ppd_choice_t *choice;                /* PPD choice */
+  const char   *content_type;          /* Original content type */
+  int          max_copies;             /* Maximum number of copies supported */
 
 
  /*
@@ -2229,42 +2359,34 @@ set_pstops_options(
   doc->new_bounding_box[3] = INT_MIN;
 
  /*
-  * AP_FIRSTPAGE_InputSlot
-  */
-
-  doc->ap_input_slot = cupsGetOption("AP_FIRSTPAGE_InputSlot", num_options,
-                                     options);
-
- /*
-  * AP_FIRSTPAGE_ManualFeed
+  * AP_FIRSTPAGE_* and the corresponding non-first-page options.
   */
 
+  doc->ap_input_slot  = cupsGetOption("AP_FIRSTPAGE_InputSlot", num_options,
+                                      options);
   doc->ap_manual_feed = cupsGetOption("AP_FIRSTPAGE_ManualFeed", num_options,
                                       options);
+  doc->ap_media_color = cupsGetOption("AP_FIRSTPAGE_MediaColor", num_options,
+                                      options);
+  doc->ap_media_type  = cupsGetOption("AP_FIRSTPAGE_MediaType", num_options,
+                                      options);
+  doc->ap_page_region = cupsGetOption("AP_FIRSTPAGE_PageRegion", num_options,
+                                      options);
+  doc->ap_page_size   = cupsGetOption("AP_FIRSTPAGE_PageSize", 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;
+  if ((choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL)
+    doc->input_slot = choice->choice;
+  if ((choice = ppdFindMarkedChoice(ppd, "ManualFeed")) != NULL)
+    doc->manual_feed = choice->choice;
+  if ((choice = ppdFindMarkedChoice(ppd, "MediaColor")) != NULL)
+    doc->media_color = choice->choice;
+  if ((choice = ppdFindMarkedChoice(ppd, "MediaType")) != NULL)
+    doc->media_type = choice->choice;
+  if ((choice = ppdFindMarkedChoice(ppd, "PageRegion")) != NULL)
+    doc->page_region = choice->choice;
+  if ((choice = ppdFindMarkedChoice(ppd, "PageSize")) != NULL)
+    doc->page_size = choice->choice;
 
  /*
   * collate, multiple-document-handling
@@ -2281,12 +2403,12 @@ set_pstops_options(
     *   separate-documents-uncollated-copies allows for uncollated copies.
     */
 
-    doc->collate = strcasecmp(val, "separate-documents-uncollated-copies") != 0;
+    doc->collate = _cups_strcasecmp(val, "separate-documents-uncollated-copies") != 0;
   }
 
   if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
-      (!strcasecmp(val, "true") ||!strcasecmp(val, "on") ||
-       !strcasecmp(val, "yes")))
+      (!_cups_strcasecmp(val, "true") ||!_cups_strcasecmp(val, "on") ||
+       !_cups_strcasecmp(val, "yes")))
     doc->collate = 1;
 
  /*
@@ -2294,62 +2416,46 @@ set_pstops_options(
   */
 
   if ((val = cupsGetOption("emit-jcl", num_options, options)) != NULL &&
-      (!strcasecmp(val, "false") || !strcasecmp(val, "off") ||
-       !strcasecmp(val, "no") || !strcmp(val, "0")))
+      (!_cups_strcasecmp(val, "false") || !_cups_strcasecmp(val, "off") ||
+       !_cups_strcasecmp(val, "no") || !strcmp(val, "0")))
     doc->emit_jcl = 0;
   else
     doc->emit_jcl = 1;
 
  /*
-  * fitplot
+  * fit-to-page/ipp-attribute-fidelity
+  *
+  * (Only for original PostScript content)
   */
 
-  if ((val = cupsGetOption("fitplot", num_options, options)) != NULL &&
-      (!strcasecmp(val, "true") || !strcasecmp(val, "on") ||
-       !strcasecmp(val, "yes")))
-    doc->fitplot = 1;
-
- /*
-  * gamma
-  */
+  if ((content_type = getenv("CONTENT_TYPE")) == NULL)
+    content_type = "application/postscript";
 
-  if ((val = cupsGetOption("gamma", num_options, options)) != NULL)
+  if (!_cups_strcasecmp(content_type, "application/postscript"))
   {
-   /*
-    * 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;
+    if ((val = cupsGetOption("fit-to-page", num_options, options)) != NULL &&
+       !_cups_strcasecmp(val, "true"))
+      doc->fit_to_page = 1;
+    else if ((val = cupsGetOption("ipp-attribute-fidelity", num_options,
+                                  options)) != NULL &&
+            !_cups_strcasecmp(val, "true"))
+      doc->fit_to_page = 1;
   }
-  else
-    doc->gamma = 1.0f;
 
  /*
-  * InputSlot
+  * mirror/MirrorPrint
   */
 
-  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 = cupsGetOption("mirror", num_options, options)) != NULL &&
-      (!strcasecmp(val, "true") || !strcasecmp(val, "on") ||
-       !strcasecmp(val, "yes")))
+  if (val && (!_cups_strcasecmp(val, "true") || !_cups_strcasecmp(val, "on") ||
+              !_cups_strcasecmp(val, "yes")))
     doc->mirror = 1;
 
  /*
@@ -2369,9 +2475,9 @@ set_pstops_options(
           doc->number_up = intval;
          break;
       default :
-          fprintf(stderr,
-                 "ERROR: Unsupported number-up value %d, using number-up=1!\n",
-                 intval);
+          _cupsLangPrintFilter(stderr, "ERROR",
+                              _("Unsupported number-up value %d, using "
+                                "number-up=1."), intval);
           doc->number_up = 1;
          break;
     }
@@ -2385,26 +2491,27 @@ set_pstops_options(
 
   if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL)
   {
-    if (!strcasecmp(val, "lrtb"))
+    if (!_cups_strcasecmp(val, "lrtb"))
       doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
-    else if (!strcasecmp(val, "lrbt"))
+    else if (!_cups_strcasecmp(val, "lrbt"))
       doc->number_up_layout = PSTOPS_LAYOUT_LRBT;
-    else if (!strcasecmp(val, "rltb"))
+    else if (!_cups_strcasecmp(val, "rltb"))
       doc->number_up_layout = PSTOPS_LAYOUT_RLTB;
-    else if (!strcasecmp(val, "rlbt"))
+    else if (!_cups_strcasecmp(val, "rlbt"))
       doc->number_up_layout = PSTOPS_LAYOUT_RLBT;
-    else if (!strcasecmp(val, "tblr"))
+    else if (!_cups_strcasecmp(val, "tblr"))
       doc->number_up_layout = PSTOPS_LAYOUT_TBLR;
-    else if (!strcasecmp(val, "tbrl"))
+    else if (!_cups_strcasecmp(val, "tbrl"))
       doc->number_up_layout = PSTOPS_LAYOUT_TBRL;
-    else if (!strcasecmp(val, "btlr"))
+    else if (!_cups_strcasecmp(val, "btlr"))
       doc->number_up_layout = PSTOPS_LAYOUT_BTLR;
-    else if (!strcasecmp(val, "btrl"))
+    else if (!_cups_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);
+      _cupsLangPrintFilter(stderr, "ERROR",
+                           _("Unsupported number-up-layout value %s, using "
+                            "number-up-layout=lrtb."), val);
       doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
     }
   }
@@ -2417,7 +2524,7 @@ set_pstops_options(
 
   if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL)
   {
-    if (!strcasecmp(val, "Reverse"))
+    if (!_cups_strcasecmp(val, "Reverse"))
       doc->output_order = 1;
   }
   else if (ppd)
@@ -2429,10 +2536,10 @@ set_pstops_options(
     if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL &&
         (attr = ppdFindAttr(ppd, "PageStackOrder", choice->choice)) != NULL &&
        attr->value)
-      doc->output_order = !strcasecmp(attr->value, "Reverse");
+      doc->output_order = !_cups_strcasecmp(attr->value, "Reverse");
     else if ((attr = ppdFindAttr(ppd, "DefaultOutputOrder", NULL)) != NULL &&
              attr->value)
-      doc->output_order = !strcasecmp(attr->value, "Reverse");
+      doc->output_order = !_cups_strcasecmp(attr->value, "Reverse");
   }
 
  /*
@@ -2441,20 +2548,21 @@ set_pstops_options(
 
   if ((val = cupsGetOption("page-border", num_options, options)) != NULL)
   {
-    if (!strcasecmp(val, "none"))
+    if (!_cups_strcasecmp(val, "none"))
       doc->page_border = PSTOPS_BORDERNONE;
-    else if (!strcasecmp(val, "single"))
+    else if (!_cups_strcasecmp(val, "single"))
       doc->page_border = PSTOPS_BORDERSINGLE;
-    else if (!strcasecmp(val, "single-thick"))
+    else if (!_cups_strcasecmp(val, "single-thick"))
       doc->page_border = PSTOPS_BORDERSINGLE2;
-    else if (!strcasecmp(val, "double"))
+    else if (!_cups_strcasecmp(val, "double"))
       doc->page_border = PSTOPS_BORDERDOUBLE;
-    else if (!strcasecmp(val, "double-thick"))
+    else if (!_cups_strcasecmp(val, "double-thick"))
       doc->page_border = PSTOPS_BORDERDOUBLE2;
     else
     {
-      fprintf(stderr, "ERROR: Unsupported page-border value %s, using "
-                      "page-border=none!\n", val);
+      _cupsLangPrintFilter(stderr, "ERROR",
+                           _("Unsupported page-border value %s, using "
+                            "page-border=none."), val);
       doc->page_border = PSTOPS_BORDERNONE;
     }
   }
@@ -2483,7 +2591,16 @@ set_pstops_options(
   * Now figure out if we have to force collated copies, etc.
   */
 
-  if (ppd && ppd->manual_copies && Duplex && doc->copies > 1)
+  if ((attr = ppdFindAttr(ppd, "cupsMaxCopies", NULL)) != NULL)
+    max_copies = atoi(attr->value);
+  else if (ppd && ppd->manual_copies)
+    max_copies = 1;
+  else
+    max_copies = 9999;
+
+  if (doc->copies > max_copies)
+    doc->collate = 1;
+  else if (ppd && ppd->manual_copies && Duplex && doc->copies > 1)
   {
    /*
     * Force collated copies when printing a duplexed document to
@@ -2507,8 +2624,9 @@ set_pstops_options(
 
     doc->slow_collate = 1;
 
-    if ((choice = ppdFindMarkedChoice(ppd, "Collate")) != NULL &&
-        !strcasecmp(choice->choice, "True"))
+    if (doc->copies <= max_copies &&
+        (choice = ppdFindMarkedChoice(ppd, "Collate")) != NULL &&
+        !_cups_strcasecmp(choice->choice, "True"))
     {
      /*
       * Hardware collate option is selected, see if the option is
@@ -2534,7 +2652,7 @@ set_pstops_options(
   if (Duplex &&
        (doc->slow_collate || doc->slow_order ||
         ((attr = ppdFindAttr(ppd, "cupsEvenDuplex", NULL)) != NULL &&
-        attr->value && !strcasecmp(attr->value, "true"))))
+        attr->value && !_cups_strcasecmp(attr->value, "true"))))
     doc->slow_duplex = 1;
   else
     doc->slow_duplex = 0;
@@ -2548,8 +2666,7 @@ set_pstops_options(
     if ((doc->temp = cupsTempFile2(doc->tempfile,
                                    sizeof(doc->tempfile))) == NULL)
     {
-      fprintf(stderr, "ERROR: Unable to create temporary file: %s\n",
-              strerror(errno));
+      perror("DEBUG: Unable to create temporary file");
       exit(1);
     }
   }
@@ -2574,13 +2691,13 @@ set_pstops_options(
 
 
 /*
- * 'skip_page()' - Skip past a page that won't be printed...
+ * 'skip_page()' - Skip past a page that won't be printed.
  */
 
-static size_t                          /* O - Length of next line */
+static ssize_t                         /* O - Length of next line */
 skip_page(cups_file_t *fp,             /* I - File to read from */
           char        *line,           /* I - Line buffer */
-         size_t      linelen,          /* I - Length of initial line */
+         ssize_t     linelen,          /* I - Length of initial line */
           size_t      linesize)                /* I - Size of line buffer */
 {
   int  level;                          /* Embedded document level */
@@ -2636,7 +2753,7 @@ skip_page(cups_file_t *fp,                /* I - File to read from */
 
 
 /*
- * 'start_nup()' - Start processing for N-up printing...
+ * 'start_nup()' - Start processing for N-up printing.
  */
 
 static void
@@ -2651,33 +2768,38 @@ start_nup(pstops_doc_t *doc,            /* I - Document information */
                tx, ty;                 /* Translation values for subpage */
   float                pagew,                  /* Printable width of page */
                pagel;                  /* Printable height of page */
-  int          bboxw,                  /* BoundingBox width */
+  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)
+  if (doc->fit_to_page)
   {
+    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: pagew = %.1f, pagel = %.1f\n", pagew, pagel);
-  fprintf(stderr, "DEBUG: bboxw = %d, bboxl = %d\n", bboxw, bboxl);
+  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",
@@ -2699,15 +2821,26 @@ start_nup(pstops_doc_t *doc,            /* I - Document information */
         break;
   }
 
+ /*
+  * Mirror the page as needed...
+  */
+
+  if (doc->mirror)
+    doc_printf(doc, "%.1f 0.0 translate -1 1 scale\n", PageWidth);
+
+ /*
+  * Offset and scale as necessary for fit_to_page/fit-to-page/number-up...
+  */
+
   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)
+  else if (doc->number_up > 1 || doc->fit_to_page)
     doc_printf(doc, "%.1f %.1f translate\n", PageLeft, PageBottom);
 
   switch (doc->number_up)
   {
     default :
-        if (doc->fitplot)
+        if (doc->fit_to_page)
        {
           w = pagew;
           l = w * bboxl / bboxw;
@@ -2725,10 +2858,7 @@ start_nup(pstops_doc_t *doc,             /* I - Document information */
                     w / bboxw, l / bboxl);
        }
        else
-       {
           w = PageWidth;
-         l = PageLength;
-       }
        break;
 
     case 2 :
@@ -2995,8 +3125,7 @@ start_nup(pstops_doc_t *doc,              /* I - Document information */
   if (doc->page_border && show_border)
   {
     int                rects;                  /* Number of border rectangles */
-    float      fscale,                 /* Scaling value for points */
-               margin;                 /* Current margin for borders */
+    float      fscale;                 /* Scaling value for points */
 
 
     rects  = (doc->page_border & PSTOPS_BORDERDOUBLE) ? 2 : 1;
@@ -3019,10 +3148,10 @@ start_nup(pstops_doc_t *doc,            /* I - Document information */
     for (; rects > 0; rects --, margin += 2 * fscale)
       if (doc->number_up > 1)
        doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrs\n",
-                  margin - 2.25 * fscale,
-                  margin - 2.25 * fscale,
-                  bboxw + 4.5 * fscale - 2 * margin,
-                  bboxl + 4.5 * fscale - 2 * margin);
+                  margin,
+                  margin,
+                  bboxw - 2 * margin,
+                  bboxl - 2 * margin);
       else
        doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrs\n",
                   PageLeft + margin,
@@ -3037,28 +3166,146 @@ start_nup(pstops_doc_t *doc,           /* I - Document information */
     doc_puts(doc, "grestore\n");
   }
 
-  if (doc->fitplot)
+  if (doc->fit_to_page)
   {
    /*
-    * Clip the page that follows to the bounding box of the page...
+    * Offset the page by its bounding box...
     */
 
     doc_printf(doc, "%d %d translate\n", -bounding_box[0],
                -bounding_box[1]);
-    doc_printf(doc, "%d %d %d %d ESPrc\n", bounding_box[0], bounding_box[1],
-               bboxw, bboxl);
   }
-  else if (doc->number_up > 1)
+
+  if (doc->fit_to_page || doc->number_up > 1)
   {
    /*
-    * Clip the page that follows to the default page size...
+    * Clip the page to the page's bounding box...
     */
 
-    doc_printf(doc, "0 0 %d %d ESPrc\n", bboxw, bboxl);
+    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 (label)
+  {
+    if (classification[0])
+      doc_puts(doc, " - ");
+
+   /*
+    * Quote the label string as needed...
+    */
+
+    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.
  *
@@ -3106,5 +3353,82 @@ write_labels(pstops_doc_t *doc,          /* I - Document information */
 
 
 /*
- * End of "$Id: pstops.c 6094 2006-11-14 16:54:50Z mike $".
+ * 'write_options()' - Write options provided via %%IncludeFeature.
+ */
+
+static void
+write_options(
+    pstops_doc_t  *doc,                /* I - Document */
+    ppd_file_t    *ppd,                /* I - PPD file */
+    int           num_options, /* I - Number of options */
+    cups_option_t *options)    /* I - Options */
+{
+  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 */
+
+
+ /*
+  * 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 < num_options; i ++)
+    if ((option = ppdFindOption(ppd, options[i].name)) != NULL &&
+       option->order < min_order)
+      min_order = option->order;
+
+ /*
+  * Mark and extract them...
+  */
+
+  cupsMarkOptions(ppd, num_options, 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->number_up > 1)
+  {
+   /*
+    * Temporarily restore setpagedevice so we can set the options...
+    */
+
+    doc_puts(doc, "userdict/setpagedevice/CUPSsetpagedevice load put\n");
+  }
+
+  if (doc_setup)
+  {
+    doc_puts(doc, doc_setup);
+    free(doc_setup);
+  }
+
+  if (any_setup)
+  {
+    doc_puts(doc, any_setup);
+    free(any_setup);
+  }
+
+  if (doc->number_up > 1)
+  {
+   /*
+    * Disable setpagedevice again...
+    */
+
+    doc_puts(doc, "userdict/setpagedevice{pop}bind put\n");
+  }
+}
+
+
+/*
+ * End of "$Id$".
  */