]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - filter/interpret.c
Update svn:keyword properties.
[thirdparty/cups.git] / filter / interpret.c
index 3e7b47be16a1cc3725db38afc97ff355dd3d1724..77157fb97597c64bc3be8939136762c3fe999e72 100644 (file)
 /*
- * "$Id: interpret.c 5513 2006-05-11 18:07:25Z mike $"
+ * "$Id$"
  *
- *   PPD command interpreter for the Common UNIX Printing System (CUPS).
+ *   PPD command interpreter 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:
  *
  *   cupsRasterInterpretPPD() - Interpret PPD commands to create a page header.
- *   exec_code()              - Execute PostScript setpagedevice commands as
- *                              appropriate.
+ *   _cupsRasterExecPS()      - Execute PostScript code to initialize a page
+ *                              header.
+ *   cleartomark_stack()      - Clear to the last mark ([) on the stack.
+ *   copy_stack()             - Copy the top N stack objects.
+ *   delete_stack()           - Free memory used by a stack.
+ *   error_object()           - Add an object's value to the current error
+ *                              message.
+ *   error_stack()            - Add a stack to the current error message.
+ *   index_stack()            - Copy the Nth value on the stack.
+ *   new_stack()              - Create a new stack.
+ *   pop_stock()              - Pop the top object off the stack.
+ *   push_stack()             - Push an object on the stack.
+ *   roll_stack()             - Rotate stack objects.
+ *   scan_ps()                - Scan a string for the next PS object.
+ *   setpagedevice()          - Simulate the PostScript setpagedevice operator.
+ *   DEBUG_object()           - Print an object value.
+ *   DEBUG_stack()            - Print a stack.
  */
 
 /*
  * Include necessary headers...
  */
 
-#include <cups/string.h>
-#include "raster.h"
-#include <stdlib.h>
+#include <cups/raster-private.h>
 
 
 /*
- * Value types for PS code...
+ * Stack values for the PostScript mini-interpreter...
  */
 
-#define CUPS_TYPE_NUMBER       0       /* Integer or real number */
-#define CUPS_TYPE_NAME         1       /* Name */
-#define CUPS_TYPE_STRING       2       /* String */
-#define CUPS_TYPE_ARRAY                3       /* Array of integers */
+typedef enum
+{
+  CUPS_PS_NAME,
+  CUPS_PS_NUMBER,
+  CUPS_PS_STRING,
+  CUPS_PS_BOOLEAN,
+  CUPS_PS_NULL,
+  CUPS_PS_START_ARRAY,
+  CUPS_PS_END_ARRAY,
+  CUPS_PS_START_DICT,
+  CUPS_PS_END_DICT,
+  CUPS_PS_START_PROC,
+  CUPS_PS_END_PROC,
+  CUPS_PS_CLEARTOMARK,
+  CUPS_PS_COPY,
+  CUPS_PS_DUP,
+  CUPS_PS_INDEX,
+  CUPS_PS_POP,
+  CUPS_PS_ROLL,
+  CUPS_PS_SETPAGEDEVICE,
+  CUPS_PS_STOPPED,
+  CUPS_PS_OTHER
+} _cups_ps_type_t;
+
+typedef struct
+{
+  _cups_ps_type_t      type;           /* Object type */
+  union
+  {
+    int                boolean;                /* Boolean value */
+    char       name[64];               /* Name value */
+    double     number;                 /* Number value */
+    char       other[64];              /* Other operator */
+    char       string[64];             /* Sring value */
+  }                    value;          /* Value */
+} _cups_ps_obj_t;
+
+typedef struct
+{
+  int                  num_objs,       /* Number of objects on stack */
+                       alloc_objs;     /* Number of allocated objects */
+  _cups_ps_obj_t       *objs;          /* Objects in stack */
+} _cups_ps_stack_t;
 
 
 /*
  * Local functions...
  */
 
-static int     exec_code(cups_page_header2_t *header, int *preferred_bits,
-                         const char *code);
+static int             cleartomark_stack(_cups_ps_stack_t *st);
+static int             copy_stack(_cups_ps_stack_t *st, int count);
+static void            delete_stack(_cups_ps_stack_t *st);
+static void            error_object(_cups_ps_obj_t *obj);
+static void            error_stack(_cups_ps_stack_t *st, const char *title);
+static _cups_ps_obj_t  *index_stack(_cups_ps_stack_t *st, int n);
+static _cups_ps_stack_t        *new_stack(void);
+static _cups_ps_obj_t  *pop_stack(_cups_ps_stack_t *st);
+static _cups_ps_obj_t  *push_stack(_cups_ps_stack_t *st,
+                                   _cups_ps_obj_t *obj);
+static int             roll_stack(_cups_ps_stack_t *st, int c, int s);
+static _cups_ps_obj_t  *scan_ps(_cups_ps_stack_t *st, char **ptr);
+static int             setpagedevice(_cups_ps_stack_t *st,
+                                       cups_page_header2_t *h,
+                                       int *preferred_bits);
+#ifdef DEBUG
+static void            DEBUG_object(_cups_ps_obj_t *obj);
+static void            DEBUG_stack(_cups_ps_stack_t *st);
+#endif /* DEBUG */
 
 
 /*
  * 'cupsRasterInterpretPPD()' - Interpret PPD commands to create a page header.
  *
- * This function does not mark the options in the PPD using the "num_options"
- * and "options" arguments.  Instead, mark the options prior to calling
- * cupsRasterInterpretPPD() - this allows you to do per-page options
- * without manipulating the options array.
+ * This function is used by raster image processing (RIP) filters like
+ * cgpdftoraster and imagetoraster when writing CUPS raster data for a page.
+ * It is not used by raster printer driver filters which only read CUPS
+ * raster data.
+ *
+ *
+ * @code cupsRasterInterpretPPD@ does not mark the options in the PPD using
+ * the "num_options" and "options" arguments.  Instead, mark the options with
+ * @code cupsMarkOptions@ and @code ppdMarkOption@ prior to calling it -
+ * this allows for per-page options without manipulating the options array.
  *
  * The "func" argument specifies an optional callback function that is
  * called prior to the computation of the final raster data.  The function
- * can make changes to the cups_page_header2_t data as needed to use a
+ * can make changes to the @link cups_page_header2_t@ data as needed to use a
  * supported raster format and then returns 0 on success and -1 if the
  * requested attributes cannot be supported.
  *
- * @since CUPS 1.2@
+ *
+ * @code cupsRasterInterpretPPD@ supports a subset of the PostScript language.
+ * Currently only the @code [@, @code ]@, @code <<@, @code >>@, @code {@,
+ * @code }@, @code cleartomark@, @code copy@, @code dup@, @code index@,
+ * @code pop@, @code roll@, @code setpagedevice@, and @code stopped@ operators
+ * are supported.
+ *
+ * @since CUPS 1.2/OS X 10.5@
  */
 
 int                                    /* O - 0 on success, -1 on failure */
 cupsRasterInterpretPPD(
-    cups_page_header2_t *h,            /* O - Page header */
+    cups_page_header2_t *h,            /* O - Page header to create */
     ppd_file_t          *ppd,          /* I - PPD file */
     int                 num_options,   /* I - Number of options */
     cups_option_t       *options,      /* I - Options */
-    cups_interpret_cb_t func)          /* I - Optional page header callback */
+    cups_interpret_cb_t func)          /* I - Optional page header callback (@code NULL@ for none) */
 {
-  int          i;                      /* Looping var */
   int          status;                 /* Cummulative status */
-  int          count;                  /* Number of marked choices */
+  char         *code;                  /* Code to run */
   const char   *val;                   /* Option value */
-  ppd_choice_t **choices;              /* List of marked choices */
   ppd_size_t   *size;                  /* Current size */
   float                left,                   /* Left position */
                bottom,                 /* Bottom position */
@@ -99,8 +170,13 @@ cupsRasterInterpretPPD(
   * Range check input...
   */
 
+  _cupsRasterClearError();
+
   if (!h)
+  {
+    _cupsRasterAddError("Page header cannot be NULL!\n");
     return (-1);
+  }
 
  /*
   * Reset the page header to the defaults...
@@ -124,7 +200,16 @@ cupsRasterInterpretPPD(
   h->cupsImagingBBox[2]          = 612.0f;
   h->cupsImagingBBox[3]          = 792.0f;
 
-  strcpy(h->cupsPageSizeName, "Letter");
+  strlcpy(h->cupsPageSizeName, "Letter", sizeof(h->cupsPageSizeName));
+
+#ifdef __APPLE__
+ /*
+  * cupsInteger0 is also used for the total page count on OS X; set an
+  * uncommon default value so we can tell if the driver is using cupsInteger0.
+  */
+
+  h->cupsInteger[0] = 0x80000000;
+#endif /* __APPLE__ */
 
  /*
   * Apply patches and options to the page header...
@@ -140,34 +225,34 @@ cupsRasterInterpretPPD(
     */
 
     if (ppd->patches)
-      status |= exec_code(h, &preferred_bits, ppd->patches);
+      status |= _cupsRasterExecPS(h, &preferred_bits, ppd->patches);
 
    /*
     * Then apply printer options in the proper order...
     */
 
-    if ((count = ppdCollect(ppd, PPD_ORDER_DOCUMENT, &choices)) > 0)
+    if ((code = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, 0.0)) != NULL)
     {
-      for (i = 0; i < count; i ++)
-       status |= exec_code(h, &preferred_bits, choices[i]->code);
+      status |= _cupsRasterExecPS(h, &preferred_bits, code);
+      free(code);
     }
 
-    if ((count = ppdCollect(ppd, PPD_ORDER_ANY, &choices)) > 0)
+    if ((code = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL)
     {
-      for (i = 0; i < count; i ++)
-       status |= exec_code(h, &preferred_bits, choices[i]->code);
+      status |= _cupsRasterExecPS(h, &preferred_bits, code);
+      free(code);
     }
 
-    if ((count = ppdCollect(ppd, PPD_ORDER_PROLOG, &choices)) > 0)
+    if ((code = ppdEmitString(ppd, PPD_ORDER_PROLOG, 0.0)) != NULL)
     {
-      for (i = 0; i < count; i ++)
-       status |= exec_code(h, &preferred_bits, choices[i]->code);
+      status |= _cupsRasterExecPS(h, &preferred_bits, code);
+      free(code);
     }
 
-    if ((count = ppdCollect(ppd, PPD_ORDER_PAGE, &choices)) > 0)
+    if ((code = ppdEmitString(ppd, PPD_ORDER_PAGE, 0.0)) != NULL)
     {
-      for (i = 0; i < count; i ++)
-       status |= exec_code(h, &preferred_bits, choices[i]->code);
+      status |= _cupsRasterExecPS(h, &preferred_bits, code);
+      free(code);
     }
   }
 
@@ -178,10 +263,10 @@ cupsRasterInterpretPPD(
   if ((val = cupsGetOption("cupsBorderlessScalingFactor", num_options,
                            options)) != NULL)
   {
-    float sc = atof(val);
+    double sc = atof(val);             /* Scale factor */
 
-    if (sc >= 0.9 && sc <= 1.1)
-      h->cupsBorderlessScalingFactor = sc;
+    if (sc >= 0.1 && sc <= 2.0)
+      h->cupsBorderlessScalingFactor = (float)sc;
   }
 
  /*
@@ -200,6 +285,9 @@ cupsRasterInterpretPPD(
     top    = size->top;
 
     strlcpy(h->cupsPageSizeName, size->name, sizeof(h->cupsPageSizeName));
+
+    h->cupsPageSize[0] = size->width;
+    h->cupsPageSize[1] = size->length;
   }
   else
   {
@@ -213,27 +301,36 @@ cupsRasterInterpretPPD(
     top    = 792.0f;
   }
 
-  h->PageSize[0]           = h->cupsPageSize[0] *
-                             h->cupsBorderlessScalingFactor;
-  h->PageSize[1]           = h->cupsPageSize[1] *
-                             h->cupsBorderlessScalingFactor;
-  h->Margins[0]            = left * h->cupsBorderlessScalingFactor;
-  h->Margins[1]            = bottom * h->cupsBorderlessScalingFactor;
-  h->ImagingBoundingBox[0] = left * h->cupsBorderlessScalingFactor;
-  h->ImagingBoundingBox[1] = bottom * h->cupsBorderlessScalingFactor;
-  h->ImagingBoundingBox[2] = right * h->cupsBorderlessScalingFactor;
-  h->ImagingBoundingBox[3] = top * h->cupsBorderlessScalingFactor;
-  h->cupsImagingBBox[0]    = left;
-  h->cupsImagingBBox[1]    = bottom;
-  h->cupsImagingBBox[2]    = right;
-  h->cupsImagingBBox[3]    = top;
+  h->PageSize[0]           = (unsigned)(h->cupsPageSize[0] *
+                                        h->cupsBorderlessScalingFactor);
+  h->PageSize[1]           = (unsigned)(h->cupsPageSize[1] *
+                                        h->cupsBorderlessScalingFactor);
+  h->Margins[0]            = (unsigned)(left *
+                                        h->cupsBorderlessScalingFactor);
+  h->Margins[1]            = (unsigned)(bottom *
+                                        h->cupsBorderlessScalingFactor);
+  h->ImagingBoundingBox[0] = (unsigned)(left *
+                                        h->cupsBorderlessScalingFactor);
+  h->ImagingBoundingBox[1] = (unsigned)(bottom *
+                                        h->cupsBorderlessScalingFactor);
+  h->ImagingBoundingBox[2] = (unsigned)(right *
+                                        h->cupsBorderlessScalingFactor);
+  h->ImagingBoundingBox[3] = (unsigned)(top *
+                                        h->cupsBorderlessScalingFactor);
+  h->cupsImagingBBox[0]    = (float)left;
+  h->cupsImagingBBox[1]    = (float)bottom;
+  h->cupsImagingBBox[2]    = (float)right;
+  h->cupsImagingBBox[3]    = (float)top;
 
  /*
   * Use the callback to validate the page header...
   */
 
   if (func && (*func)(h, preferred_bits))
+  {
+    _cupsRasterAddError("Page header callback returned error.\n");
     return (-1);
+  }
 
  /*
   * Check parameters...
@@ -244,9 +341,12 @@ cupsRasterInterpretPPD(
       (h->cupsBitsPerColor != 1 && h->cupsBitsPerColor != 2 &&
        h->cupsBitsPerColor != 4 && h->cupsBitsPerColor != 8 &&
        h->cupsBitsPerColor != 16) ||
-      h->cupsBorderlessScalingFactor < 0.9 ||
-      h->cupsBorderlessScalingFactor > 1.1)
+      h->cupsBorderlessScalingFactor < 0.1 ||
+      h->cupsBorderlessScalingFactor > 2.0)
+  {
+    _cupsRasterAddError("Page header uses unsupported values.\n");
     return (-1);
+  }
 
  /*
   * Compute the bitmap parameters...
@@ -264,6 +364,7 @@ cupsRasterInterpretPPD(
     case CUPS_CSPACE_WHITE :
     case CUPS_CSPACE_GOLD :
     case CUPS_CSPACE_SILVER :
+    case CUPS_CSPACE_SW :
         h->cupsNumColors    = 1;
         h->cupsBitsPerPixel = h->cupsBitsPerColor;
        break;
@@ -325,6 +426,29 @@ cupsRasterInterpretPPD(
 
         h->cupsNumColors = 4;
        break;
+
+    case CUPS_CSPACE_DEVICE1 :
+    case CUPS_CSPACE_DEVICE2 :
+    case CUPS_CSPACE_DEVICE3 :
+    case CUPS_CSPACE_DEVICE4 :
+    case CUPS_CSPACE_DEVICE5 :
+    case CUPS_CSPACE_DEVICE6 :
+    case CUPS_CSPACE_DEVICE7 :
+    case CUPS_CSPACE_DEVICE8 :
+    case CUPS_CSPACE_DEVICE9 :
+    case CUPS_CSPACE_DEVICEA :
+    case CUPS_CSPACE_DEVICEB :
+    case CUPS_CSPACE_DEVICEC :
+    case CUPS_CSPACE_DEVICED :
+    case CUPS_CSPACE_DEVICEE :
+    case CUPS_CSPACE_DEVICEF :
+        h->cupsNumColors = h->cupsColorSpace - CUPS_CSPACE_DEVICE1 + 1;
+
+        if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
+          h->cupsBitsPerPixel = h->cupsBitsPerColor * h->cupsNumColors;
+       else
+         h->cupsBitsPerPixel = h->cupsBitsPerColor;
+       break;
   }
 
   h->cupsBytesPerLine = (h->cupsBitsPerPixel * h->cupsWidth + 7) / 8;
@@ -337,276 +461,1228 @@ cupsRasterInterpretPPD(
 
 
 /*
- * 'exec_code()' - Execute PostScript setpagedevice commands as appropriate.
+ * '_cupsRasterExecPS()' - Execute PostScript code to initialize a page header.
  */
 
-static int                             /* O - 0 on success, -1 on error */
-exec_code(
+int                                    /* O - 0 on success, -1 on error */
+_cupsRasterExecPS(
     cups_page_header2_t *h,            /* O - Page header */
     int                 *preferred_bits,/* O - Preferred bits per color */
-    const char          *code)         /* I - Option choice to execute */
+    const char          *code)         /* I - PS code to execute */
 {
-  int  i;                              /* Index into array */
-  int  type;                           /* Type of value */
-  char *ptr,                           /* Pointer into name/value string */
-       name[255],                      /* Name of pagedevice entry */
-       value[1024];                    /* Value of pagedevice entry */
+  _cups_ps_stack_t     *st;            /* PostScript value stack */
+  _cups_ps_obj_t       *obj;           /* Object from top of stack */
+  char                 *codecopy,      /* Copy of code */
+                       *codeptr;       /* Pointer into copy of code */
+
 
+  DEBUG_printf(("_cupsRasterExecPS(h=%p, preferred_bits=%p, code=\"%s\")\n",
+                h, preferred_bits, code ? code : "(null)"));
 
  /*
-  * Range check input...
+  * Copy the PostScript code and create a stack...
   */
 
-  if (!code || !h)
+  if ((codecopy = strdup(code)) == NULL)
+  {
+    _cupsRasterAddError("Unable to duplicate code string.\n");
+    return (-1);
+  }
+
+  if ((st = new_stack()) == NULL)
+  {
+    _cupsRasterAddError("Unable to create stack.\n");
+    free(codecopy);
     return (-1);
+  }
 
  /*
-  * Parse the code string...
+  * Parse the PS string until we run out of data...
   */
 
-  while (*code)
+  codeptr = codecopy;
+
+  while ((obj = scan_ps(st, &codeptr)) != NULL)
   {
-   /*
-    * Search for the start of a dictionary name...
-    */
+#ifdef DEBUG
+    DEBUG_printf(("_cupsRasterExecPS: Stack (%d objects)\n", st->num_objs));
+    DEBUG_object(obj);
+#endif /* DEBUG */
+
+    switch (obj->type)
+    {
+      default :
+          /* Do nothing for regular values */
+         break;
+
+      case CUPS_PS_CLEARTOMARK :
+          pop_stack(st);
+
+         if (cleartomark_stack(st))
+           _cupsRasterAddError("cleartomark: Stack underflow!\n");
+
+#ifdef DEBUG
+          DEBUG_puts("    dup: ");
+         DEBUG_stack(st);
+#endif /* DEBUG */
+          break;
+
+      case CUPS_PS_COPY :
+          pop_stack(st);
+         if ((obj = pop_stack(st)) != NULL)
+         {
+           copy_stack(st, (int)obj->value.number);
+
+#ifdef DEBUG
+            DEBUG_puts("_cupsRasterExecPS: copy");
+           DEBUG_stack(st);
+#endif /* DEBUG */
+          }
+          break;
+
+      case CUPS_PS_DUP :
+          pop_stack(st);
+         copy_stack(st, 1);
+
+#ifdef DEBUG
+          DEBUG_puts("_cupsRasterExecPS: dup");
+         DEBUG_stack(st);
+#endif /* DEBUG */
+          break;
+
+      case CUPS_PS_INDEX :
+          pop_stack(st);
+         if ((obj = pop_stack(st)) != NULL)
+         {
+           index_stack(st, (int)obj->value.number);
+
+#ifdef DEBUG
+            DEBUG_puts("_cupsRasterExecPS: index");
+           DEBUG_stack(st);
+#endif /* DEBUG */
+          }
+          break;
+
+      case CUPS_PS_POP :
+          pop_stack(st);
+          pop_stack(st);
+
+#ifdef DEBUG
+          DEBUG_puts("_cupsRasterExecPS: pop");
+         DEBUG_stack(st);
+#endif /* DEBUG */
+          break;
+
+      case CUPS_PS_ROLL :
+          pop_stack(st);
+         if ((obj = pop_stack(st)) != NULL)
+         {
+            int                c;              /* Count */
+
+
+            c = (int)obj->value.number;
+
+           if ((obj = pop_stack(st)) != NULL)
+           {
+             roll_stack(st, (int)obj->value.number, c);
 
-    while (*code && *code != '/')
-      code ++;
+#ifdef DEBUG
+              DEBUG_puts("_cupsRasterExecPS: roll");
+             DEBUG_stack(st);
+#endif /* DEBUG */
+            }
+         }
+          break;
+
+      case CUPS_PS_SETPAGEDEVICE :
+          pop_stack(st);
+         setpagedevice(st, h, preferred_bits);
+
+#ifdef DEBUG
+          DEBUG_puts("_cupsRasterExecPS: setpagedevice");
+         DEBUG_stack(st);
+#endif /* DEBUG */
+          break;
+
+      case CUPS_PS_START_PROC :
+      case CUPS_PS_END_PROC :
+      case CUPS_PS_STOPPED :
+          pop_stack(st);
+         break;
+
+      case CUPS_PS_OTHER :
+          _cupsRasterAddError("Unknown operator \"%s\"!\n", obj->value.other);
+          DEBUG_printf(("_cupsRasterExecPS: Unknown operator \"%s\"!\n",
+                       obj->value.other));
+          break;
+    }
 
-    if (!*code)
+    if (obj && obj->type == CUPS_PS_OTHER)
       break;
+  }
+
+ /*
+  * Cleanup...
+  */
+
+  free(codecopy);
+
+  if (st->num_objs > 0)
+  {
+    error_stack(st, "Stack not empty:");
+
+#ifdef DEBUG
+    DEBUG_puts("_cupsRasterExecPS: Stack not empty:");
+    DEBUG_stack(st);
+#endif /* DEBUG */
+
+    delete_stack(st);
+
+    return (-1);
+  }
+
+  delete_stack(st);
+
+ /*
+  * Return success...
+  */
+
+  return (0);
+}
+
+
+/*
+ * 'cleartomark_stack()' - Clear to the last mark ([) on the stack.
+ */
+
+static int                             /* O - 0 on success, -1 on error */
+cleartomark_stack(_cups_ps_stack_t *st)        /* I - Stack */
+{
+  _cups_ps_obj_t       *obj;           /* Current object on stack */
+
+
+  while ((obj = pop_stack(st)) != NULL)
+    if (obj->type == CUPS_PS_START_ARRAY)
+      break;
+
+  return (obj ? 0 : -1);
+}
+
+
+/*
+ * 'copy_stack()' - Copy the top N stack objects.
+ */
+
+static int                             /* O - 0 on success, -1 on error */
+copy_stack(_cups_ps_stack_t *st,       /* I - Stack */
+           int              c)         /* I - Number of objects to copy */
+{
+  int  n;                              /* Index */
+
+
+  if (c < 0)
+    return (-1);
+  else if (c == 0)
+    return (0);
+
+  if ((n = st->num_objs - c) < 0)
+    return (-1);
+
+  while (c > 0)
+  {
+    if (!push_stack(st, st->objs + n))
+      return (-1);
+
+    n ++;
+    c --;
+  }
+
+  return (0);
+}
+
+
+/*
+ * 'delete_stack()' - Free memory used by a stack.
+ */
+
+static void
+delete_stack(_cups_ps_stack_t *st)     /* I - Stack */
+{
+  free(st->objs);
+  free(st);
+}
+
+
+/*
+ * 'error_object()' - Add an object's value to the current error message.
+ */
+
+static void
+error_object(_cups_ps_obj_t *obj)      /* I - Object to add */
+{
+  switch (obj->type)
+  {
+    case CUPS_PS_NAME :
+       _cupsRasterAddError(" /%s", obj->value.name);
+       break;
+
+    case CUPS_PS_NUMBER :
+       _cupsRasterAddError(" %g", obj->value.number);
+       break;
+
+    case CUPS_PS_STRING :
+       _cupsRasterAddError(" (%s)", obj->value.string);
+       break;
+
+    case CUPS_PS_BOOLEAN :
+       if (obj->value.boolean)
+         _cupsRasterAddError(" true");
+       else
+         _cupsRasterAddError(" false");
+       break;
+
+    case CUPS_PS_NULL :
+       _cupsRasterAddError(" null");
+       break;
+
+    case CUPS_PS_START_ARRAY :
+       _cupsRasterAddError(" [");
+       break;
+
+    case CUPS_PS_END_ARRAY :
+       _cupsRasterAddError(" ]");
+       break;
+
+    case CUPS_PS_START_DICT :
+       _cupsRasterAddError(" <<");
+       break;
+
+    case CUPS_PS_END_DICT :
+       _cupsRasterAddError(" >>");
+       break;
+
+    case CUPS_PS_START_PROC :
+       _cupsRasterAddError(" {");
+       break;
+
+    case CUPS_PS_END_PROC :
+       _cupsRasterAddError(" }");
+       break;
+
+    case CUPS_PS_COPY :
+       _cupsRasterAddError(" --copy--");
+        break;
+
+    case CUPS_PS_CLEARTOMARK :
+       _cupsRasterAddError(" --cleartomark--");
+        break;
+
+    case CUPS_PS_DUP :
+       _cupsRasterAddError(" --dup--");
+        break;
+
+    case CUPS_PS_INDEX :
+       _cupsRasterAddError(" --index--");
+        break;
+
+    case CUPS_PS_POP :
+       _cupsRasterAddError(" --pop--");
+        break;
+
+    case CUPS_PS_ROLL :
+       _cupsRasterAddError(" --roll--");
+        break;
+
+    case CUPS_PS_SETPAGEDEVICE :
+       _cupsRasterAddError(" --setpagedevice--");
+        break;
+
+    case CUPS_PS_STOPPED :
+       _cupsRasterAddError(" --stopped--");
+        break;
+
+    case CUPS_PS_OTHER :
+       _cupsRasterAddError(" --%s--", obj->value.other);
+       break;
+  }
+}
+
+
+/*
+ * 'error_stack()' - Add a stack to the current error message...
+ */
+
+static void
+error_stack(_cups_ps_stack_t *st,      /* I - Stack */
+            const char       *title)   /* I - Title string */
+{
+  int                  c;              /* Looping var */
+  _cups_ps_obj_t       *obj;           /* Current object on stack */
+
+
+  _cupsRasterAddError("%s", title);
+
+  for (obj = st->objs, c = st->num_objs; c > 0; c --, obj ++)
+    error_object(obj);
+
+  _cupsRasterAddError("\n");
+}
+
+
+/*
+ * 'index_stack()' - Copy the Nth value on the stack.
+ */
+
+static _cups_ps_obj_t  *               /* O - New object */
+index_stack(_cups_ps_stack_t *st,      /* I - Stack */
+            int              n)                /* I - Object index */
+{
+  if (n < 0 || (n = st->num_objs - n - 1) < 0)
+    return (NULL);
+
+  return (push_stack(st, st->objs + n));
+}
+
+
+/*
+ * 'new_stack()' - Create a new stack.
+ */
+
+static _cups_ps_stack_t        *               /* O - New stack */
+new_stack(void)
+{
+  _cups_ps_stack_t     *st;            /* New stack */
+
+
+  if ((st = calloc(1, sizeof(_cups_ps_stack_t))) == NULL)
+    return (NULL);
+
+  st->alloc_objs = 32;
+
+  if ((st->objs = calloc(32, sizeof(_cups_ps_obj_t))) == NULL)
+  {
+    free(st);
+    return (NULL);
+  }
+  else
+    return (st);
+}
+
+
+/*
+ * 'pop_stock()' - Pop the top object off the stack.
+ */
+
+static _cups_ps_obj_t  *               /* O - Object */
+pop_stack(_cups_ps_stack_t *st)                /* I - Stack */
+{
+  if (st->num_objs > 0)
+  {
+    st->num_objs --;
+
+    return (st->objs + st->num_objs);
+  }
+  else
+    return (NULL);
+}
+
+
+/*
+ * 'push_stack()' - Push an object on the stack.
+ */
+
+static _cups_ps_obj_t  *               /* O - New object */
+push_stack(_cups_ps_stack_t *st,       /* I - Stack */
+           _cups_ps_obj_t   *obj)      /* I - Object */
+{
+  _cups_ps_obj_t       *temp;          /* New object */
+
+
+  if (st->num_objs >= st->alloc_objs)
+  {
+
+
+    st->alloc_objs += 32;
+
+    if ((temp = realloc(st->objs, st->alloc_objs *
+                                  sizeof(_cups_ps_obj_t))) == NULL)
+      return (NULL);
+
+    st->objs = temp;
+    memset(temp + st->num_objs, 0, 32 * sizeof(_cups_ps_obj_t));
+  }
+
+  temp = st->objs + st->num_objs;
+  st->num_objs ++;
+
+  memcpy(temp, obj, sizeof(_cups_ps_obj_t));
+
+  return (temp);
+}
+
+
+/*
+ * 'roll_stack()' - Rotate stack objects.
+ */
+
+static int                             /* O - 0 on success, -1 on error */
+roll_stack(_cups_ps_stack_t *st,       /* I - Stack */
+          int              c,          /* I - Number of objects */
+           int              s)         /* I - Amount to shift */
+{
+  _cups_ps_obj_t       *temp;          /* Temporary array of objects */
+  int                  n;              /* Index into array */
 
+
+  DEBUG_printf(("    roll_stack(st=%p, s=%d, c=%d)\n", st, s, c));
+
+ /*
+  * Range check input...
+  */
+
+  if (c < 0)
+    return (-1);
+  else if (c == 0)
+    return (0);
+
+  if ((n = st->num_objs - c) < 0)
+    return (-1);
+
+  s %= c;
+
+  if (s == 0)
+    return (0);
+
+ /*
+  * Copy N objects and move things around...
+  */
+
+  if (s < 0)
+  {
    /*
-    * Get the name...
+    * Shift down...
     */
 
-    code ++;
-    for (ptr = name; isalnum(*code & 255); code ++)
-      if (ptr < (name + sizeof(name) - 1))
-        *ptr++ = *code;
-      else
-        return (-1);
+    s = -s;
 
-    *ptr = '\0';
+    if ((temp = calloc(s, sizeof(_cups_ps_obj_t))) == NULL)
+      return (-1);
 
+    memcpy(temp, st->objs + n, s * sizeof(_cups_ps_obj_t));
+    memmove(st->objs + n, st->objs + n + s, (c - s) * sizeof(_cups_ps_obj_t));
+    memcpy(st->objs + n + c - s, temp, s * sizeof(_cups_ps_obj_t));
+  }
+  else
+  {
    /*
-    * Then parse the value as needed...
+    * Shift up...
     */
 
-    while (isspace(*code & 255))
-      code ++;
+    if ((temp = calloc(s, sizeof(_cups_ps_obj_t))) == NULL)
+      return (-1);
 
-    if (!*code)
-      break;
+    memcpy(temp, st->objs + n + c - s, s * sizeof(_cups_ps_obj_t));
+    memmove(st->objs + n + s, st->objs + n,
+            (c - s) * sizeof(_cups_ps_obj_t));
+    memcpy(st->objs + n, temp, s * sizeof(_cups_ps_obj_t));
+  }
 
-    if (*code == '[')
-    {
-     /*
-      * Read array of values...
-      */
+  free(temp);
 
-      type = CUPS_TYPE_ARRAY;
+  return (0);
+}
 
-      for (ptr = value; *code && *code != ']'; code ++)
-        if (ptr < (value + sizeof(value) - 1))
-         *ptr++ = *code;
-       else
-         return (-1);
 
-      if (*code == ']')
-        *ptr++ = *code++;
+/*
+ * 'scan_ps()' - Scan a string for the next PS object.
+ */
 
-      *ptr = '\0';
-    }
-    else if (*code == '(')
+static _cups_ps_obj_t  *               /* O  - New object or NULL on EOF */
+scan_ps(_cups_ps_stack_t *st,          /* I  - Stack */
+        char             **ptr)                /* IO - String pointer */
+{
+  _cups_ps_obj_t       obj;            /* Current object */
+  char                 *start,         /* Start of object */
+                       *cur,           /* Current position */
+                       *valptr,        /* Pointer into value string */
+                       *valend;        /* End of value string */
+  int                  parens;         /* Parenthesis nesting level */
+
+
+ /*
+  * Skip leading whitespace...
+  */
+
+  for (cur = *ptr; *cur; cur ++)
+  {
+    if (*cur == '%')
     {
      /*
-      * Read string value...
+      * Comment, skip to end of line...
       */
 
-      type = CUPS_TYPE_STRING;
+      for (cur ++; *cur && *cur != '\n' && *cur != '\r'; cur ++);
 
-      for (ptr = value; *code && *code != ')'; code ++)
-        if (ptr < (value + sizeof(value) - 1))
-         *ptr++ = *code;
-       else
-         return (-1);
+      if (!*cur)
+        cur --;
+    }
+    else if (!isspace(*cur & 255))
+      break;
+  }
 
-      if (*code == ')')
-        *ptr++ = *code++;
+  if (!*cur)
+  {
+    *ptr = NULL;
 
-      *ptr = '\0';
-    }
-    else if (isdigit(*code & 255) || *code == '-' || *code == '.')
-    {
-     /*
-      * Read single number...
-      */
+    return (NULL);
+  }
+
+ /*
+  * See what we have...
+  */
 
-      type = CUPS_TYPE_NUMBER;
+  memset(&obj, 0, sizeof(obj));
+
+  switch (*cur)
+  {
+    case '(' :                         /* (string) */
+        obj.type = CUPS_PS_STRING;
+       start    = cur;
+
+       for (cur ++, parens = 1, valptr = obj.value.string,
+                valend = obj.value.string + sizeof(obj.value.string) - 1;
+             *cur;
+            cur ++)
+       {
+         if (*cur == ')' && parens == 1)
+           break;
+
+          if (*cur == '(')
+           parens ++;
+         else if (*cur == ')')
+           parens --;
+
+          if (valptr >= valend)
+         {
+           *ptr = start;
+
+           return (NULL);
+         }
+
+         if (*cur == '\\')
+         {
+          /*
+           * Decode escaped character...
+           */
+
+           cur ++;
+
+            if (*cur == 'b')
+             *valptr++ = '\b';
+           else if (*cur == 'f')
+             *valptr++ = '\f';
+           else if (*cur == 'n')
+             *valptr++ = '\n';
+           else if (*cur == 'r')
+             *valptr++ = '\r';
+           else if (*cur == 't')
+             *valptr++ = '\t';
+           else if (*cur >= '0' && *cur <= '7')
+           {
+             int ch = *cur - '0';
+
+              if (cur[1] >= '0' && cur[1] <= '7')
+             {
+               cur ++;
+               ch = (ch << 3) + *cur - '0';
+             }
+
+              if (cur[1] >= '0' && cur[1] <= '7')
+             {
+               cur ++;
+               ch = (ch << 3) + *cur - '0';
+             }
+
+             *valptr++ = ch;
+           }
+           else if (*cur == '\r')
+           {
+             if (cur[1] == '\n')
+               cur ++;
+           }
+           else if (*cur != '\n')
+             *valptr++ = *cur;
+         }
+         else
+           *valptr++ = *cur;
+       }
+
+       if (*cur != ')')
+       {
+         *ptr = start;
 
-      for (ptr = value;
-           isdigit(*code & 255) || *code == '-' || *code == '.';
-          code ++)
-        if (ptr < (value + sizeof(value) - 1))
-         *ptr++ = *code;
+         return (NULL);
+       }
+
+       cur ++;
+        break;
+
+    case '[' :                         /* Start array */
+        obj.type = CUPS_PS_START_ARRAY;
+       cur ++;
+        break;
+
+    case ']' :                         /* End array */
+        obj.type = CUPS_PS_END_ARRAY;
+       cur ++;
+        break;
+
+    case '<' :                         /* Start dictionary or hex string */
+        if (cur[1] == '<')
+       {
+         obj.type = CUPS_PS_START_DICT;
+         cur += 2;
+       }
        else
-         return (-1);
+       {
+          obj.type = CUPS_PS_STRING;
+         start    = cur;
+
+         for (cur ++, valptr = obj.value.string,
+                  valend = obj.value.string + sizeof(obj.value.string) - 1;
+               *cur;
+              cur ++)
+         {
+           int ch;                     /* Current character */
+
+
+
+            if (*cur == '>')
+             break;
+           else if (valptr >= valend || !isxdigit(*cur & 255))
+           {
+             *ptr = start;
+             return (NULL);
+           }
+
+            if (*cur >= '0' && *cur <= '9')
+             ch = (*cur - '0') << 4;
+           else
+             ch = (tolower(*cur) - 'a' + 10) << 4;
+
+           if (isxdigit(cur[1] & 255))
+           {
+             cur ++;
+
+              if (*cur >= '0' && *cur <= '9')
+               ch |= *cur - '0';
+             else
+               ch |= tolower(*cur) - 'a' + 10;
+            }
+
+           *valptr++ = ch;
+          }
+
+          if (*cur != '>')
+         {
+           *ptr = start;
+           return (NULL);
+         }
+
+         cur ++;
+       }
+        break;
 
-      *ptr = '\0';
-    }
-    else
-    {
-     /*
-      * Read a single name...
-      */
+    case '>' :                         /* End dictionary? */
+        if (cur[1] == '>')
+       {
+         obj.type = CUPS_PS_END_DICT;
+         cur += 2;
+       }
+       else
+       {
+         obj.type           = CUPS_PS_OTHER;
+         obj.value.other[0] = *cur;
+
+         cur ++;
+       }
+        break;
+
+    case '{' :                         /* Start procedure */
+        obj.type = CUPS_PS_START_PROC;
+       cur ++;
+        break;
+
+    case '}' :                         /* End procedure */
+        obj.type = CUPS_PS_END_PROC;
+       cur ++;
+        break;
+
+    case '-' :                         /* Possible number */
+    case '+' :
+        if (!isdigit(cur[1] & 255) && cur[1] != '.')
+       {
+         obj.type           = CUPS_PS_OTHER;
+         obj.value.other[0] = *cur;
+
+         cur ++;
+         break;
+       }
+
+    case '0' :                         /* Number */
+    case '1' :
+    case '2' :
+    case '3' :
+    case '4' :
+    case '5' :
+    case '6' :
+    case '7' :
+    case '8' :
+    case '9' :
+    case '.' :
+        obj.type = CUPS_PS_NUMBER;
+
+        start = cur;
+       for (cur ++; *cur; cur ++)
+         if (!isdigit(*cur & 255))
+           break;
+
+        if (*cur == '#')
+       {
+        /*
+         * Integer with radix...
+         */
+
+          obj.value.number = strtol(cur + 1, &cur, atoi(start));
+         break;
+       }
+       else if (strchr(".Ee()<>[]{}/%", *cur) || isspace(*cur & 255))
+       {
+        /*
+         * Integer or real number...
+         */
+
+         obj.value.number = _cupsStrScand(start, &cur, localeconv());
+          break;
+       }
+       else
+         cur = start;
 
-      type = CUPS_TYPE_NAME;
+    default :                          /* Operator/variable name */
+        start = cur;
 
-      for (ptr = value; isalnum(*code & 255) || *code == '_'; code ++) 
-        if (ptr < (value + sizeof(value) - 1))
-         *ptr++ = *code;
+       if (*cur == '/')
+       {
+         obj.type = CUPS_PS_NAME;
+          valptr   = obj.value.name;
+          valend   = obj.value.name + sizeof(obj.value.name) - 1;
+         cur ++;
+       }
        else
-         return (-1);
+       {
+         obj.type = CUPS_PS_OTHER;
+          valptr   = obj.value.other;
+          valend   = obj.value.other + sizeof(obj.value.other) - 1;
+       }
 
-      *ptr = '\0';
-    }
+       while (*cur)
+       {
+         if (strchr("()<>[]{}/%", *cur) || isspace(*cur & 255))
+           break;
+         else if (valptr < valend)
+           *valptr++ = *cur++;
+         else
+         {
+           *ptr = start;
+           return (NULL);
+         }
+       }
+
+        if (obj.type == CUPS_PS_OTHER)
+       {
+          if (!strcmp(obj.value.other, "true"))
+         {
+           obj.type          = CUPS_PS_BOOLEAN;
+           obj.value.boolean = 1;
+         }
+         else if (!strcmp(obj.value.other, "false"))
+         {
+           obj.type          = CUPS_PS_BOOLEAN;
+           obj.value.boolean = 0;
+         }
+         else if (!strcmp(obj.value.other, "null"))
+           obj.type = CUPS_PS_NULL;
+         else if (!strcmp(obj.value.other, "cleartomark"))
+           obj.type = CUPS_PS_CLEARTOMARK;
+         else if (!strcmp(obj.value.other, "copy"))
+           obj.type = CUPS_PS_COPY;
+         else if (!strcmp(obj.value.other, "dup"))
+           obj.type = CUPS_PS_DUP;
+         else if (!strcmp(obj.value.other, "index"))
+           obj.type = CUPS_PS_INDEX;
+         else if (!strcmp(obj.value.other, "pop"))
+           obj.type = CUPS_PS_POP;
+         else if (!strcmp(obj.value.other, "roll"))
+           obj.type = CUPS_PS_ROLL;
+         else if (!strcmp(obj.value.other, "setpagedevice"))
+           obj.type = CUPS_PS_SETPAGEDEVICE;
+         else if (!strcmp(obj.value.other, "stopped"))
+           obj.type = CUPS_PS_STOPPED;
+       }
+       break;
+  }
+
+ /*
+  * Save the current position in the string and return the new object...
+  */
+
+  *ptr = cur;
+
+  return (push_stack(st, &obj));
+}
+
+
+/*
+ * 'setpagedevice()' - Simulate the PostScript setpagedevice operator.
+ */
 
+static int                             /* O - 0 on success, -1 on error */
+setpagedevice(
+    _cups_ps_stack_t    *st,           /* I - Stack */
+    cups_page_header2_t *h,            /* O - Page header */
+    int                 *preferred_bits)/* O - Preferred bits per color */
+{
+  int                  i;              /* Index into array */
+  _cups_ps_obj_t       *obj,           /* Current object */
+                       *end;           /* End of dictionary */
+  const char           *name;          /* Attribute name */
+
+
+ /*
+  * Make sure we have a dictionary on the stack...
+  */
+
+  if (st->num_objs == 0)
+    return (-1);
+
+  obj = end = st->objs + st->num_objs - 1;
+
+  if (obj->type != CUPS_PS_END_DICT)
+    return (-1);
+
+  obj --;
+
+  while (obj > st->objs)
+  {
+    if (obj->type == CUPS_PS_START_DICT)
+      break;
+
+    obj --;
+  }
+
+  if (obj < st->objs)
+    return (-1);
+
+ /*
+  * Found the start of the dictionary, empty the stack to this point...
+  */
+
+  st->num_objs = (int)(obj - st->objs);
+
+ /*
+  * Now pull /name and value pairs from the dictionary...
+  */
+
+  DEBUG_puts("setpagedevice: Dictionary:");
+
+  for (obj ++; obj < end; obj ++)
+  {
    /*
-    * Assign the value as needed...
+    * Grab the name...
     */
 
-    if (!strcmp(name, "MediaClass") && type == CUPS_TYPE_STRING)
-    {
-      if (sscanf(value, "(%63[^)])", h->MediaClass) != 1)
-        return (-1);
-    }
-    else if (!strcmp(name, "MediaColor") && type == CUPS_TYPE_STRING)
-    {
-      if (sscanf(value, "(%63[^)])", h->MediaColor) != 1)
-        return (-1);
-    }
-    else if (!strcmp(name, "MediaType") && type == CUPS_TYPE_STRING)
-    {
-      if (sscanf(value, "(%63[^)])", h->MediaType) != 1)
-        return (-1);
-    }
-    else if (!strcmp(name, "OutputType") && type == CUPS_TYPE_STRING)
+    if (obj->type != CUPS_PS_NAME)
+      return (-1);
+
+    name = obj->value.name;
+    obj ++;
+
+#ifdef DEBUG
+    DEBUG_printf(("setpagedevice: /%s ", name));
+    DEBUG_object(obj);
+#endif /* DEBUG */
+
+   /*
+    * Then grab the value...
+    */
+
+    if (!strcmp(name, "MediaClass") && obj->type == CUPS_PS_STRING)
+      strlcpy(h->MediaClass, obj->value.string, sizeof(h->MediaClass));
+    else if (!strcmp(name, "MediaColor") && obj->type == CUPS_PS_STRING)
+      strlcpy(h->MediaColor, obj->value.string, sizeof(h->MediaColor));
+    else if (!strcmp(name, "MediaType") && obj->type == CUPS_PS_STRING)
+      strlcpy(h->MediaType, obj->value.string, sizeof(h->MediaType));
+    else if (!strcmp(name, "OutputType") && obj->type == CUPS_PS_STRING)
+      strlcpy(h->OutputType, obj->value.string, sizeof(h->OutputType));
+    else if (!strcmp(name, "AdvanceDistance") && obj->type == CUPS_PS_NUMBER)
+      h->AdvanceDistance = (unsigned)obj->value.number;
+    else if (!strcmp(name, "AdvanceMedia") && obj->type == CUPS_PS_NUMBER)
+      h->AdvanceMedia = (unsigned)obj->value.number;
+    else if (!strcmp(name, "Collate") && obj->type == CUPS_PS_BOOLEAN)
+      h->Collate = (unsigned)obj->value.boolean;
+    else if (!strcmp(name, "CutMedia") && obj->type == CUPS_PS_NUMBER)
+      h->CutMedia = (cups_cut_t)(unsigned)obj->value.number;
+    else if (!strcmp(name, "Duplex") && obj->type == CUPS_PS_BOOLEAN)
+      h->Duplex = (unsigned)obj->value.boolean;
+    else if (!strcmp(name, "HWResolution") && obj->type == CUPS_PS_START_ARRAY)
     {
-      if (sscanf(value, "(%63[^)])", h->OutputType) != 1)
+      if (obj[1].type == CUPS_PS_NUMBER && obj[2].type == CUPS_PS_NUMBER &&
+          obj[3].type == CUPS_PS_END_ARRAY)
+      {
+        h->HWResolution[0] = (unsigned)obj[1].value.number;
+       h->HWResolution[1] = (unsigned)obj[2].value.number;
+       obj += 3;
+      }
+      else
         return (-1);
     }
-    else if (!strcmp(name, "AdvanceDistance") && type == CUPS_TYPE_NUMBER)
-      h->AdvanceDistance = atoi(value);
-    else if (!strcmp(name, "AdvanceMedia") && type == CUPS_TYPE_NUMBER)
-      h->AdvanceMedia = atoi(value);
-    else if (!strcmp(name, "Collate") && type == CUPS_TYPE_NAME)
-      h->Collate = !strcmp(value, "true");
-    else if (!strcmp(name, "CutMedia") && type == CUPS_TYPE_NUMBER)
-      h->CutMedia = (cups_cut_t)atoi(value);
-    else if (!strcmp(name, "Duplex") && type == CUPS_TYPE_NAME)
-      h->Duplex = !strcmp(value, "true");
-    else if (!strcmp(name, "HWResolution") && type == CUPS_TYPE_ARRAY)
+    else if (!strcmp(name, "InsertSheet") && obj->type == CUPS_PS_BOOLEAN)
+      h->InsertSheet = (unsigned)obj->value.boolean;
+    else if (!strcmp(name, "Jog") && obj->type == CUPS_PS_NUMBER)
+      h->Jog = (unsigned)obj->value.number;
+    else if (!strcmp(name, "LeadingEdge") && obj->type == CUPS_PS_NUMBER)
+      h->LeadingEdge = (unsigned)obj->value.number;
+    else if (!strcmp(name, "ManualFeed") && obj->type == CUPS_PS_BOOLEAN)
+      h->ManualFeed = (unsigned)obj->value.boolean;
+    else if ((!strcmp(name, "cupsMediaPosition") ||
+              !strcmp(name, "MediaPosition")) && obj->type == CUPS_PS_NUMBER)
     {
-      if (sscanf(value, "[%d%d]", h->HWResolution + 0,
-                 h->HWResolution + 1) != 2)
-        return (-1);
+     /*
+      * cupsMediaPosition is supported for backwards compatibility only.
+      * We added it back in the Ghostscript 5.50 days to work around a
+      * bug in Ghostscript WRT handling of MediaPosition and setpagedevice.
+      *
+      * All new development should set MediaPosition...
+      */
+
+      h->MediaPosition = (unsigned)obj->value.number;
     }
-    else if (!strcmp(name, "InsertSheet") && type == CUPS_TYPE_NAME)
-      h->InsertSheet = !strcmp(value, "true");
-    else if (!strcmp(name, "Jog") && type == CUPS_TYPE_NUMBER)
-      h->Jog = atoi(value);
-    else if (!strcmp(name, "LeadingEdge") && type == CUPS_TYPE_NUMBER)
-      h->LeadingEdge = atoi(value);
-    else if (!strcmp(name, "ManualFeed") && type == CUPS_TYPE_NAME)
-      h->ManualFeed = !strcmp(value, "true");
-    else if ((!strcmp(name, "cupsMediaPosition") || /* Compatibility */
-              !strcmp(name, "MediaPosition")) && type == CUPS_TYPE_NUMBER)
-      h->MediaPosition = atoi(value);
-    else if (!strcmp(name, "MediaWeight") && type == CUPS_TYPE_NUMBER)
-      h->MediaWeight = atoi(value);
-    else if (!strcmp(name, "MirrorPrint") && type == CUPS_TYPE_NAME)
-      h->MirrorPrint = !strcmp(value, "true");
-    else if (!strcmp(name, "NegativePrint") && type == CUPS_TYPE_NAME)
-      h->NegativePrint = !strcmp(value, "true");
-    else if (!strcmp(name, "Orientation") && type == CUPS_TYPE_NUMBER)
-      h->Orientation = atoi(value);
-    else if (!strcmp(name, "OutputFaceUp") && type == CUPS_TYPE_NAME)
-      h->OutputFaceUp = !strcmp(value, "true");
-    else if (!strcmp(name, "PageSize") && type == CUPS_TYPE_ARRAY)
+    else if (!strcmp(name, "MediaWeight") && obj->type == CUPS_PS_NUMBER)
+      h->MediaWeight = (unsigned)obj->value.number;
+    else if (!strcmp(name, "MirrorPrint") && obj->type == CUPS_PS_BOOLEAN)
+      h->MirrorPrint = (unsigned)obj->value.boolean;
+    else if (!strcmp(name, "NegativePrint") && obj->type == CUPS_PS_BOOLEAN)
+      h->NegativePrint = (unsigned)obj->value.boolean;
+    else if (!strcmp(name, "NumCopies") && obj->type == CUPS_PS_NUMBER)
+      h->NumCopies = (unsigned)obj->value.number;
+    else if (!strcmp(name, "Orientation") && obj->type == CUPS_PS_NUMBER)
+      h->Orientation = (unsigned)obj->value.number;
+    else if (!strcmp(name, "OutputFaceUp") && obj->type == CUPS_PS_BOOLEAN)
+      h->OutputFaceUp = (unsigned)obj->value.boolean;
+    else if (!strcmp(name, "PageSize") && obj->type == CUPS_PS_START_ARRAY)
     {
-      if (sscanf(value, "[%f%f]", h->cupsPageSize + 0, h->cupsPageSize + 1) != 2)
+      if (obj[1].type == CUPS_PS_NUMBER && obj[2].type == CUPS_PS_NUMBER &&
+          obj[3].type == CUPS_PS_END_ARRAY)
+      {
+        h->cupsPageSize[0] = (float)obj[1].value.number;
+       h->cupsPageSize[1] = (float)obj[2].value.number;
+
+        h->PageSize[0] = (unsigned)obj[1].value.number;
+       h->PageSize[1] = (unsigned)obj[2].value.number;
+
+       obj += 3;
+      }
+      else
         return (-1);
     }
-    else if (!strcmp(name, "Separations") && type == CUPS_TYPE_NAME)
-      h->Separations = !strcmp(value, "true");
-    else if (!strcmp(name, "TraySwitch") && type == CUPS_TYPE_NAME)
-      h->TraySwitch = !strcmp(value, "true");
-    else if (!strcmp(name, "Tumble") && type == CUPS_TYPE_NAME)
-      h->Tumble = !strcmp(value, "true");
-    else if (!strcmp(name, "cupsMediaType") && type == CUPS_TYPE_NUMBER)
-      h->cupsMediaType = atoi(value);
-    else if (!strcmp(name, "cupsBitsPerColor") && type == CUPS_TYPE_NUMBER)
-      h->cupsBitsPerColor = atoi(value);
-    else if (!strcmp(name, "cupsPreferredBitsPerColor") && type == CUPS_TYPE_NUMBER)
-      *preferred_bits = atoi(value);
-    else if (!strcmp(name, "cupsColorOrder") && type == CUPS_TYPE_NUMBER)
-      h->cupsColorOrder = (cups_order_t)atoi(value);
-    else if (!strcmp(name, "cupsColorSpace") && type == CUPS_TYPE_NUMBER)
-      h->cupsColorSpace = (cups_cspace_t)atoi(value);
-    else if (!strcmp(name, "cupsCompression") && type == CUPS_TYPE_NUMBER)
-      h->cupsCompression = atoi(value);
-    else if (!strcmp(name, "cupsRowCount") && type == CUPS_TYPE_NUMBER)
-      h->cupsRowCount = atoi(value);
-    else if (!strcmp(name, "cupsRowFeed") && type == CUPS_TYPE_NUMBER)
-      h->cupsRowFeed = atoi(value);
-    else if (!strcmp(name, "cupsRowStep") && type == CUPS_TYPE_NUMBER)
-      h->cupsRowStep = atoi(value);
+    else if (!strcmp(name, "Separations") && obj->type == CUPS_PS_BOOLEAN)
+      h->Separations = (unsigned)obj->value.boolean;
+    else if (!strcmp(name, "TraySwitch") && obj->type == CUPS_PS_BOOLEAN)
+      h->TraySwitch = (unsigned)obj->value.boolean;
+    else if (!strcmp(name, "Tumble") && obj->type == CUPS_PS_BOOLEAN)
+      h->Tumble = (unsigned)obj->value.boolean;
+    else if (!strcmp(name, "cupsMediaType") && obj->type == CUPS_PS_NUMBER)
+      h->cupsMediaType = (unsigned)obj->value.number;
+    else if (!strcmp(name, "cupsBitsPerColor") && obj->type == CUPS_PS_NUMBER)
+      h->cupsBitsPerColor = (unsigned)obj->value.number;
+    else if (!strcmp(name, "cupsPreferredBitsPerColor") &&
+             obj->type == CUPS_PS_NUMBER)
+      *preferred_bits = (int)obj->value.number;
+    else if (!strcmp(name, "cupsColorOrder") && obj->type == CUPS_PS_NUMBER)
+      h->cupsColorOrder = (cups_order_t)(unsigned)obj->value.number;
+    else if (!strcmp(name, "cupsColorSpace") && obj->type == CUPS_PS_NUMBER)
+      h->cupsColorSpace = (cups_cspace_t)(unsigned)obj->value.number;
+    else if (!strcmp(name, "cupsCompression") && obj->type == CUPS_PS_NUMBER)
+      h->cupsCompression = (unsigned)obj->value.number;
+    else if (!strcmp(name, "cupsRowCount") && obj->type == CUPS_PS_NUMBER)
+      h->cupsRowCount = (unsigned)obj->value.number;
+    else if (!strcmp(name, "cupsRowFeed") && obj->type == CUPS_PS_NUMBER)
+      h->cupsRowFeed = (unsigned)obj->value.number;
+    else if (!strcmp(name, "cupsRowStep") && obj->type == CUPS_PS_NUMBER)
+      h->cupsRowStep = (unsigned)obj->value.number;
     else if (!strcmp(name, "cupsBorderlessScalingFactor") &&
-             type == CUPS_TYPE_NUMBER)
-      h->cupsBorderlessScalingFactor = atof(value);
-    else if (!strncmp(name, "cupsInteger", 11) && type == CUPS_TYPE_NUMBER)
+             obj->type == CUPS_PS_NUMBER)
+      h->cupsBorderlessScalingFactor = (float)obj->value.number;
+    else if (!strncmp(name, "cupsInteger", 11) && obj->type == CUPS_PS_NUMBER)
     {
-      if ((i = atoi(name + 11)) >= 0 || i > 15)
+      if ((i = atoi(name + 11)) < 0 || i > 15)
         return (-1);
 
-      h->cupsInteger[i] = atoi(value);
+      h->cupsInteger[i] = (unsigned)obj->value.number;
     }
-    else if (!strncmp(name, "cupsReal", 8) && type == CUPS_TYPE_NUMBER)
+    else if (!strncmp(name, "cupsReal", 8) && obj->type == CUPS_PS_NUMBER)
     {
-      if ((i = atoi(name + 8)) >= 0 || i > 15)
+      if ((i = atoi(name + 8)) < 0 || i > 15)
         return (-1);
 
-      h->cupsReal[i] = atof(value);
+      h->cupsReal[i] = (float)obj->value.number;
     }
-    else if (!strncmp(name, "cupsString", 10) && type == CUPS_TYPE_STRING)
+    else if (!strncmp(name, "cupsString", 10) && obj->type == CUPS_PS_STRING)
     {
-      if ((i = atoi(name + 10)) >= 0 || i > 15)
+      if ((i = atoi(name + 10)) < 0 || i > 15)
         return (-1);
 
-      if (sscanf(value, "(%63[^)])", h->cupsString[i]) != 1)
-        return (-1);
+      strlcpy(h->cupsString[i], obj->value.string, sizeof(h->cupsString[i]));
     }
-    else if (!strcmp(name, "cupsMarkerType") && type == CUPS_TYPE_STRING)
-    {
-      if (sscanf(value, "(%63[^)])", h->cupsMarkerType) != 1)
-        return (-1);
-    }
-    else if (!strcmp(name, "cupsRenderingIntent") && type == CUPS_TYPE_STRING)
+    else if (!strcmp(name, "cupsMarkerType") && obj->type == CUPS_PS_STRING)
+      strlcpy(h->cupsMarkerType, obj->value.string, sizeof(h->cupsMarkerType));
+    else if (!strcmp(name, "cupsPageSizeName") && obj->type == CUPS_PS_STRING)
+      strlcpy(h->cupsPageSizeName, obj->value.string,
+              sizeof(h->cupsPageSizeName));
+    else if (!strcmp(name, "cupsRenderingIntent") &&
+             obj->type == CUPS_PS_STRING)
+      strlcpy(h->cupsRenderingIntent, obj->value.string,
+              sizeof(h->cupsRenderingIntent));
+    else
     {
-      if (sscanf(value, "(%63[^)])", h->cupsRenderingIntent) != 1)
-        return (-1);
+     /*
+      * Ignore unknown name+value...
+      */
+
+      DEBUG_printf(("    Unknown name (\"%s\") or value...\n", name));
+
+      while (obj[1].type != CUPS_PS_NAME && obj < end)
+        obj ++;
     }
   }
 
- /*
-  * Return success...
-  */
-
   return (0);
 }
 
 
+#ifdef DEBUG
+/*
+ * 'DEBUG_object()' - Print an object's value...
+ */
+
+static void
+DEBUG_object(_cups_ps_obj_t *obj)      /* I - Object to print */
+{
+  switch (obj->type)
+  {
+    case CUPS_PS_NAME :
+       DEBUG_printf(("/%s\n", obj->value.name));
+       break;
+
+    case CUPS_PS_NUMBER :
+       DEBUG_printf(("%g\n", obj->value.number));
+       break;
+
+    case CUPS_PS_STRING :
+       DEBUG_printf(("(%s)\n", obj->value.string));
+       break;
+
+    case CUPS_PS_BOOLEAN :
+       if (obj->value.boolean)
+         DEBUG_puts("true");
+       else
+         DEBUG_puts("false");
+       break;
+
+    case CUPS_PS_NULL :
+       DEBUG_puts("null");
+       break;
+
+    case CUPS_PS_START_ARRAY :
+       DEBUG_puts("[");
+       break;
+
+    case CUPS_PS_END_ARRAY :
+       DEBUG_puts("]");
+       break;
+
+    case CUPS_PS_START_DICT :
+       DEBUG_puts("<<");
+       break;
+
+    case CUPS_PS_END_DICT :
+       DEBUG_puts(">>");
+       break;
+
+    case CUPS_PS_START_PROC :
+       DEBUG_puts("{");
+       break;
+
+    case CUPS_PS_END_PROC :
+       DEBUG_puts("}");
+       break;
+
+    case CUPS_PS_CLEARTOMARK :
+       DEBUG_puts("--cleartomark--");
+        break;
+
+    case CUPS_PS_COPY :
+       DEBUG_puts("--copy--");
+        break;
+
+    case CUPS_PS_DUP :
+       DEBUG_puts("--dup--");
+        break;
+
+    case CUPS_PS_INDEX :
+       DEBUG_puts("--index--");
+        break;
+
+    case CUPS_PS_POP :
+       DEBUG_puts("--pop--");
+        break;
+
+    case CUPS_PS_ROLL :
+       DEBUG_puts("--roll--");
+        break;
+
+    case CUPS_PS_SETPAGEDEVICE :
+       DEBUG_puts("--setpagedevice--");
+        break;
+
+    case CUPS_PS_STOPPED :
+       DEBUG_puts("--stopped--");
+        break;
+
+    case CUPS_PS_OTHER :
+       DEBUG_printf(("--%s--\n", obj->value.other));
+       break;
+  }
+}
+
+
+/*
+ * 'DEBUG_stack()' - Print a stack...
+ */
+
+static void
+DEBUG_stack(_cups_ps_stack_t *st)      /* I - Stack */
+{
+  int                  c;              /* Looping var */
+  _cups_ps_obj_t       *obj;           /* Current object on stack */
+
+
+  for (obj = st->objs, c = st->num_objs; c > 0; c --, obj ++)
+    DEBUG_object(obj);
+}
+#endif /* DEBUG */
+
+
 /*
- * End of "$Id: interpret.c 5513 2006-05-11 18:07:25Z mike $".
+ * End of "$Id$".
  */