Imported Upstream version 1.6.4
[platform/upstream/cups.git] / filter / interpret.c
1 /*
2  * "$Id: interpret.c 11173 2013-07-23 12:31:34Z msweet $"
3  *
4  *   PPD command interpreter for CUPS.
5  *
6  *   Copyright 2007-2012 by Apple Inc.
7  *   Copyright 1993-2007 by Easy Software Products.
8  *
9  *   These coded instructions, statements, and computer programs are the
10  *   property of Apple Inc. and are protected by Federal copyright
11  *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12  *   which should have been included with this file.  If this file is
13  *   file is missing or damaged, see the license at "http://www.cups.org/".
14  *
15  *   This file is subject to the Apple OS-Developed Software exception.
16  *
17  * Contents:
18  *
19  *   cupsRasterInterpretPPD() - Interpret PPD commands to create a page header.
20  *   _cupsRasterExecPS()      - Execute PostScript code to initialize a page
21  *                              header.
22  *   cleartomark_stack()      - Clear to the last mark ([) on the stack.
23  *   copy_stack()             - Copy the top N stack objects.
24  *   delete_stack()           - Free memory used by a stack.
25  *   error_object()           - Add an object's value to the current error
26  *                              message.
27  *   error_stack()            - Add a stack to the current error message.
28  *   index_stack()            - Copy the Nth value on the stack.
29  *   new_stack()              - Create a new stack.
30  *   pop_stock()              - Pop the top object off the stack.
31  *   push_stack()             - Push an object on the stack.
32  *   roll_stack()             - Rotate stack objects.
33  *   scan_ps()                - Scan a string for the next PS object.
34  *   setpagedevice()          - Simulate the PostScript setpagedevice operator.
35  *   DEBUG_object()           - Print an object value.
36  *   DEBUG_stack()            - Print a stack.
37  */
38
39 /*
40  * Include necessary headers...
41  */
42
43 #include <cups/raster-private.h>
44
45
46 /*
47  * Stack values for the PostScript mini-interpreter...
48  */
49
50 typedef enum
51 {
52   CUPS_PS_NAME,
53   CUPS_PS_NUMBER,
54   CUPS_PS_STRING,
55   CUPS_PS_BOOLEAN,
56   CUPS_PS_NULL,
57   CUPS_PS_START_ARRAY,
58   CUPS_PS_END_ARRAY,
59   CUPS_PS_START_DICT,
60   CUPS_PS_END_DICT,
61   CUPS_PS_START_PROC,
62   CUPS_PS_END_PROC,
63   CUPS_PS_CLEARTOMARK,
64   CUPS_PS_COPY,
65   CUPS_PS_DUP,
66   CUPS_PS_INDEX,
67   CUPS_PS_POP,
68   CUPS_PS_ROLL,
69   CUPS_PS_SETPAGEDEVICE,
70   CUPS_PS_STOPPED,
71   CUPS_PS_OTHER
72 } _cups_ps_type_t;
73
74 typedef struct
75 {
76   _cups_ps_type_t       type;           /* Object type */
77   union
78   {
79     int         boolean;                /* Boolean value */
80     char        name[64];               /* Name value */
81     double      number;                 /* Number value */
82     char        other[64];              /* Other operator */
83     char        string[64];             /* Sring value */
84   }                     value;          /* Value */
85 } _cups_ps_obj_t;
86
87 typedef struct
88 {
89   int                   num_objs,       /* Number of objects on stack */
90                         alloc_objs;     /* Number of allocated objects */
91   _cups_ps_obj_t        *objs;          /* Objects in stack */
92 } _cups_ps_stack_t;
93
94
95 /*
96  * Local functions...
97  */
98
99 static int              cleartomark_stack(_cups_ps_stack_t *st);
100 static int              copy_stack(_cups_ps_stack_t *st, int count);
101 static void             delete_stack(_cups_ps_stack_t *st);
102 static void             error_object(_cups_ps_obj_t *obj);
103 static void             error_stack(_cups_ps_stack_t *st, const char *title);
104 static _cups_ps_obj_t   *index_stack(_cups_ps_stack_t *st, int n);
105 static _cups_ps_stack_t *new_stack(void);
106 static _cups_ps_obj_t   *pop_stack(_cups_ps_stack_t *st);
107 static _cups_ps_obj_t   *push_stack(_cups_ps_stack_t *st,
108                                     _cups_ps_obj_t *obj);
109 static int              roll_stack(_cups_ps_stack_t *st, int c, int s);
110 static _cups_ps_obj_t   *scan_ps(_cups_ps_stack_t *st, char **ptr);
111 static int              setpagedevice(_cups_ps_stack_t *st,
112                                         cups_page_header2_t *h,
113                                         int *preferred_bits);
114 #ifdef DEBUG
115 static void             DEBUG_object(_cups_ps_obj_t *obj);
116 static void             DEBUG_stack(_cups_ps_stack_t *st);
117 #endif /* DEBUG */
118
119
120 /*
121  * 'cupsRasterInterpretPPD()' - Interpret PPD commands to create a page header.
122  *
123  * This function is used by raster image processing (RIP) filters like
124  * cgpdftoraster and imagetoraster when writing CUPS raster data for a page.
125  * It is not used by raster printer driver filters which only read CUPS
126  * raster data.
127  *
128  *
129  * @code cupsRasterInterpretPPD@ does not mark the options in the PPD using
130  * the "num_options" and "options" arguments.  Instead, mark the options with
131  * @code cupsMarkOptions@ and @code ppdMarkOption@ prior to calling it -
132  * this allows for per-page options without manipulating the options array.
133  *
134  * The "func" argument specifies an optional callback function that is
135  * called prior to the computation of the final raster data.  The function
136  * can make changes to the @link cups_page_header2_t@ data as needed to use a
137  * supported raster format and then returns 0 on success and -1 if the
138  * requested attributes cannot be supported.
139  *
140  *
141  * @code cupsRasterInterpretPPD@ supports a subset of the PostScript language.
142  * Currently only the @code [@, @code ]@, @code <<@, @code >>@, @code {@,
143  * @code }@, @code cleartomark@, @code copy@, @code dup@, @code index@,
144  * @code pop@, @code roll@, @code setpagedevice@, and @code stopped@ operators
145  * are supported.
146  *
147  * @since CUPS 1.2/OS X 10.5@
148  */
149
150 int                                     /* O - 0 on success, -1 on failure */
151 cupsRasterInterpretPPD(
152     cups_page_header2_t *h,             /* O - Page header to create */
153     ppd_file_t          *ppd,           /* I - PPD file */
154     int                 num_options,    /* I - Number of options */
155     cups_option_t       *options,       /* I - Options */
156     cups_interpret_cb_t func)           /* I - Optional page header callback (@code NULL@ for none) */
157 {
158   int           status;                 /* Cummulative status */
159   char          *code;                  /* Code to run */
160   const char    *val;                   /* Option value */
161   ppd_size_t    *size;                  /* Current size */
162   float         left,                   /* Left position */
163                 bottom,                 /* Bottom position */
164                 right,                  /* Right position */
165                 top;                    /* Top position */
166   int           preferred_bits;         /* Preferred bits per color */
167
168
169  /*
170   * Range check input...
171   */
172
173   _cupsRasterClearError();
174
175   if (!h)
176   {
177     _cupsRasterAddError("Page header cannot be NULL!\n");
178     return (-1);
179   }
180
181  /*
182   * Reset the page header to the defaults...
183   */
184
185   memset(h, 0, sizeof(cups_page_header2_t));
186
187   h->NumCopies                   = 1;
188   h->PageSize[0]                 = 612;
189   h->PageSize[1]                 = 792;
190   h->HWResolution[0]             = 100;
191   h->HWResolution[1]             = 100;
192   h->cupsBitsPerColor            =  1;
193   h->cupsColorOrder              = CUPS_ORDER_CHUNKED;
194   h->cupsColorSpace              = CUPS_CSPACE_K;
195   h->cupsBorderlessScalingFactor = 1.0f;
196   h->cupsPageSize[0]             = 612.0f;
197   h->cupsPageSize[1]             = 792.0f;
198   h->cupsImagingBBox[0]          = 0.0f;
199   h->cupsImagingBBox[1]          = 0.0f;
200   h->cupsImagingBBox[2]          = 612.0f;
201   h->cupsImagingBBox[3]          = 792.0f;
202
203   strcpy(h->cupsPageSizeName, "Letter");
204
205 #ifdef __APPLE__
206  /*
207   * cupsInteger0 is also used for the total page count on OS X; set an
208   * uncommon default value so we can tell if the driver is using cupsInteger0.
209   */
210
211   h->cupsInteger[0] = 0x80000000;
212 #endif /* __APPLE__ */
213
214  /*
215   * Apply patches and options to the page header...
216   */
217
218   status         = 0;
219   preferred_bits = 0;
220
221   if (ppd)
222   {
223    /*
224     * Apply any patch code (used to override the defaults...)
225     */
226
227     if (ppd->patches)
228       status |= _cupsRasterExecPS(h, &preferred_bits, ppd->patches);
229
230    /*
231     * Then apply printer options in the proper order...
232     */
233
234     if ((code = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, 0.0)) != NULL)
235     {
236       status |= _cupsRasterExecPS(h, &preferred_bits, code);
237       free(code);
238     }
239
240     if ((code = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL)
241     {
242       status |= _cupsRasterExecPS(h, &preferred_bits, code);
243       free(code);
244     }
245
246     if ((code = ppdEmitString(ppd, PPD_ORDER_PROLOG, 0.0)) != NULL)
247     {
248       status |= _cupsRasterExecPS(h, &preferred_bits, code);
249       free(code);
250     }
251
252     if ((code = ppdEmitString(ppd, PPD_ORDER_PAGE, 0.0)) != NULL)
253     {
254       status |= _cupsRasterExecPS(h, &preferred_bits, code);
255       free(code);
256     }
257   }
258
259  /*
260   * Allow option override for page scaling...
261   */
262
263   if ((val = cupsGetOption("cupsBorderlessScalingFactor", num_options,
264                            options)) != NULL)
265   {
266     double sc = atof(val);              /* Scale factor */
267
268     if (sc >= 0.1 && sc <= 2.0)
269       h->cupsBorderlessScalingFactor = (float)sc;
270   }
271
272  /*
273   * Get the margins for the current size...
274   */
275
276   if ((size = ppdPageSize(ppd, NULL)) != NULL)
277   {
278    /*
279     * Use the margins from the PPD file...
280     */
281
282     left   = size->left;
283     bottom = size->bottom;
284     right  = size->right;
285     top    = size->top;
286
287     strlcpy(h->cupsPageSizeName, size->name, sizeof(h->cupsPageSizeName));
288
289     h->cupsPageSize[0] = size->width;
290     h->cupsPageSize[1] = size->length;
291   }
292   else
293   {
294    /*
295     * Use the default margins...
296     */
297
298     left   = 0.0f;
299     bottom = 0.0f;
300     right  = 612.0f;
301     top    = 792.0f;
302   }
303
304   h->PageSize[0]           = (unsigned)(h->cupsPageSize[0] *
305                                         h->cupsBorderlessScalingFactor);
306   h->PageSize[1]           = (unsigned)(h->cupsPageSize[1] *
307                                         h->cupsBorderlessScalingFactor);
308   h->Margins[0]            = (unsigned)(left *
309                                         h->cupsBorderlessScalingFactor);
310   h->Margins[1]            = (unsigned)(bottom *
311                                         h->cupsBorderlessScalingFactor);
312   h->ImagingBoundingBox[0] = (unsigned)(left *
313                                         h->cupsBorderlessScalingFactor);
314   h->ImagingBoundingBox[1] = (unsigned)(bottom *
315                                         h->cupsBorderlessScalingFactor);
316   h->ImagingBoundingBox[2] = (unsigned)(right *
317                                         h->cupsBorderlessScalingFactor);
318   h->ImagingBoundingBox[3] = (unsigned)(top *
319                                         h->cupsBorderlessScalingFactor);
320   h->cupsImagingBBox[0]    = (float)left;
321   h->cupsImagingBBox[1]    = (float)bottom;
322   h->cupsImagingBBox[2]    = (float)right;
323   h->cupsImagingBBox[3]    = (float)top;
324
325  /*
326   * Use the callback to validate the page header...
327   */
328
329   if (func && (*func)(h, preferred_bits))
330   {
331     _cupsRasterAddError("Page header callback returned error.\n");
332     return (-1);
333   }
334
335  /*
336   * Check parameters...
337   */
338
339   if (!h->HWResolution[0] || !h->HWResolution[1] ||
340       !h->PageSize[0] || !h->PageSize[1] ||
341       (h->cupsBitsPerColor != 1 && h->cupsBitsPerColor != 2 &&
342        h->cupsBitsPerColor != 4 && h->cupsBitsPerColor != 8 &&
343        h->cupsBitsPerColor != 16) ||
344       h->cupsBorderlessScalingFactor < 0.1 ||
345       h->cupsBorderlessScalingFactor > 2.0)
346   {
347     _cupsRasterAddError("Page header uses unsupported values.\n");
348     return (-1);
349   }
350
351  /*
352   * Compute the bitmap parameters...
353   */
354
355   h->cupsWidth  = (int)((right - left) * h->cupsBorderlessScalingFactor *
356                         h->HWResolution[0] / 72.0f + 0.5f);
357   h->cupsHeight = (int)((top - bottom) * h->cupsBorderlessScalingFactor *
358                         h->HWResolution[1] / 72.0f + 0.5f);
359
360   switch (h->cupsColorSpace)
361   {
362     case CUPS_CSPACE_W :
363     case CUPS_CSPACE_K :
364     case CUPS_CSPACE_WHITE :
365     case CUPS_CSPACE_GOLD :
366     case CUPS_CSPACE_SILVER :
367     case CUPS_CSPACE_SW :
368         h->cupsNumColors    = 1;
369         h->cupsBitsPerPixel = h->cupsBitsPerColor;
370         break;
371
372     default :
373        /*
374         * Ensure that colorimetric colorspaces use at least 8 bits per
375         * component...
376         */
377
378         if (h->cupsColorSpace >= CUPS_CSPACE_CIEXYZ &&
379             h->cupsBitsPerColor < 8)
380           h->cupsBitsPerColor = 8;
381
382        /*
383         * Figure out the number of bits per pixel...
384         */
385
386         if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
387         {
388           if (h->cupsBitsPerColor >= 8)
389             h->cupsBitsPerPixel = h->cupsBitsPerColor * 3;
390           else
391             h->cupsBitsPerPixel = h->cupsBitsPerColor * 4;
392         }
393         else
394           h->cupsBitsPerPixel = h->cupsBitsPerColor;
395
396         h->cupsNumColors = 3;
397         break;
398
399     case CUPS_CSPACE_KCMYcm :
400         if (h->cupsBitsPerColor == 1)
401         {
402           if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
403             h->cupsBitsPerPixel = 8;
404           else
405             h->cupsBitsPerPixel = 1;
406
407           h->cupsNumColors = 6;
408           break;
409         }
410
411        /*
412         * Fall through to CMYK code...
413         */
414
415     case CUPS_CSPACE_RGBA :
416     case CUPS_CSPACE_RGBW :
417     case CUPS_CSPACE_CMYK :
418     case CUPS_CSPACE_YMCK :
419     case CUPS_CSPACE_KCMY :
420     case CUPS_CSPACE_GMCK :
421     case CUPS_CSPACE_GMCS :
422         if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
423           h->cupsBitsPerPixel = h->cupsBitsPerColor * 4;
424         else
425           h->cupsBitsPerPixel = h->cupsBitsPerColor;
426
427         h->cupsNumColors = 4;
428         break;
429
430     case CUPS_CSPACE_DEVICE1 :
431     case CUPS_CSPACE_DEVICE2 :
432     case CUPS_CSPACE_DEVICE3 :
433     case CUPS_CSPACE_DEVICE4 :
434     case CUPS_CSPACE_DEVICE5 :
435     case CUPS_CSPACE_DEVICE6 :
436     case CUPS_CSPACE_DEVICE7 :
437     case CUPS_CSPACE_DEVICE8 :
438     case CUPS_CSPACE_DEVICE9 :
439     case CUPS_CSPACE_DEVICEA :
440     case CUPS_CSPACE_DEVICEB :
441     case CUPS_CSPACE_DEVICEC :
442     case CUPS_CSPACE_DEVICED :
443     case CUPS_CSPACE_DEVICEE :
444     case CUPS_CSPACE_DEVICEF :
445         h->cupsNumColors = h->cupsColorSpace - CUPS_CSPACE_DEVICE1 + 1;
446
447         if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
448           h->cupsBitsPerPixel = h->cupsBitsPerColor * h->cupsNumColors;
449         else
450           h->cupsBitsPerPixel = h->cupsBitsPerColor;
451         break;
452   }
453
454   h->cupsBytesPerLine = (h->cupsBitsPerPixel * h->cupsWidth + 7) / 8;
455
456   if (h->cupsColorOrder == CUPS_ORDER_BANDED)
457     h->cupsBytesPerLine *= h->cupsNumColors;
458
459   return (status);
460 }
461
462
463 /*
464  * '_cupsRasterExecPS()' - Execute PostScript code to initialize a page header.
465  */
466
467 int                                     /* O - 0 on success, -1 on error */
468 _cupsRasterExecPS(
469     cups_page_header2_t *h,             /* O - Page header */
470     int                 *preferred_bits,/* O - Preferred bits per color */
471     const char          *code)          /* I - PS code to execute */
472 {
473   _cups_ps_stack_t      *st;            /* PostScript value stack */
474   _cups_ps_obj_t        *obj;           /* Object from top of stack */
475   char                  *codecopy,      /* Copy of code */
476                         *codeptr;       /* Pointer into copy of code */
477
478
479   DEBUG_printf(("_cupsRasterExecPS(h=%p, preferred_bits=%p, code=\"%s\")\n",
480                 h, preferred_bits, code ? code : "(null)"));
481
482  /*
483   * Copy the PostScript code and create a stack...
484   */
485
486   if ((codecopy = strdup(code)) == NULL)
487   {
488     _cupsRasterAddError("Unable to duplicate code string.\n");
489     return (-1);
490   }
491
492   if ((st = new_stack()) == NULL)
493   {
494     _cupsRasterAddError("Unable to create stack.\n");
495     free(codecopy);
496     return (-1);
497   }
498
499  /*
500   * Parse the PS string until we run out of data...
501   */
502
503   codeptr = codecopy;
504
505   while ((obj = scan_ps(st, &codeptr)) != NULL)
506   {
507 #ifdef DEBUG
508     DEBUG_printf(("_cupsRasterExecPS: Stack (%d objects)\n", st->num_objs));
509     DEBUG_object(obj);
510 #endif /* DEBUG */
511
512     switch (obj->type)
513     {
514       default :
515           /* Do nothing for regular values */
516           break;
517
518       case CUPS_PS_CLEARTOMARK :
519           pop_stack(st);
520
521           if (cleartomark_stack(st))
522             _cupsRasterAddError("cleartomark: Stack underflow!\n");
523
524 #ifdef DEBUG
525           DEBUG_puts("    dup: ");
526           DEBUG_stack(st);
527 #endif /* DEBUG */
528           break;
529
530       case CUPS_PS_COPY :
531           pop_stack(st);
532           if ((obj = pop_stack(st)) != NULL)
533           {
534             copy_stack(st, (int)obj->value.number);
535
536 #ifdef DEBUG
537             DEBUG_puts("_cupsRasterExecPS: copy");
538             DEBUG_stack(st);
539 #endif /* DEBUG */
540           }
541           break;
542
543       case CUPS_PS_DUP :
544           pop_stack(st);
545           copy_stack(st, 1);
546
547 #ifdef DEBUG
548           DEBUG_puts("_cupsRasterExecPS: dup");
549           DEBUG_stack(st);
550 #endif /* DEBUG */
551           break;
552
553       case CUPS_PS_INDEX :
554           pop_stack(st);
555           if ((obj = pop_stack(st)) != NULL)
556           {
557             index_stack(st, (int)obj->value.number);
558
559 #ifdef DEBUG
560             DEBUG_puts("_cupsRasterExecPS: index");
561             DEBUG_stack(st);
562 #endif /* DEBUG */
563           }
564           break;
565
566       case CUPS_PS_POP :
567           pop_stack(st);
568           pop_stack(st);
569
570 #ifdef DEBUG
571           DEBUG_puts("_cupsRasterExecPS: pop");
572           DEBUG_stack(st);
573 #endif /* DEBUG */
574           break;
575
576       case CUPS_PS_ROLL :
577           pop_stack(st);
578           if ((obj = pop_stack(st)) != NULL)
579           {
580             int         c;              /* Count */
581
582
583             c = (int)obj->value.number;
584
585             if ((obj = pop_stack(st)) != NULL)
586             {
587               roll_stack(st, (int)obj->value.number, c);
588
589 #ifdef DEBUG
590               DEBUG_puts("_cupsRasterExecPS: roll");
591               DEBUG_stack(st);
592 #endif /* DEBUG */
593             }
594           }
595           break;
596
597       case CUPS_PS_SETPAGEDEVICE :
598           pop_stack(st);
599           setpagedevice(st, h, preferred_bits);
600
601 #ifdef DEBUG
602           DEBUG_puts("_cupsRasterExecPS: setpagedevice");
603           DEBUG_stack(st);
604 #endif /* DEBUG */
605           break;
606
607       case CUPS_PS_START_PROC :
608       case CUPS_PS_END_PROC :
609       case CUPS_PS_STOPPED :
610           pop_stack(st);
611           break;
612
613       case CUPS_PS_OTHER :
614           _cupsRasterAddError("Unknown operator \"%s\"!\n", obj->value.other);
615           DEBUG_printf(("_cupsRasterExecPS: Unknown operator \"%s\"!\n",
616                         obj->value.other));
617           break;
618     }
619
620     if (obj && obj->type == CUPS_PS_OTHER)
621       break;
622   }
623
624  /*
625   * Cleanup...
626   */
627
628   free(codecopy);
629
630   if (st->num_objs > 0)
631   {
632     error_stack(st, "Stack not empty:");
633
634 #ifdef DEBUG
635     DEBUG_puts("_cupsRasterExecPS: Stack not empty:");
636     DEBUG_stack(st);
637 #endif /* DEBUG */
638
639     delete_stack(st);
640
641     return (-1);
642   }
643
644   delete_stack(st);
645
646  /*
647   * Return success...
648   */
649
650   return (0);
651 }
652
653
654 /*
655  * 'cleartomark_stack()' - Clear to the last mark ([) on the stack.
656  */
657
658 static int                              /* O - 0 on success, -1 on error */
659 cleartomark_stack(_cups_ps_stack_t *st) /* I - Stack */
660 {
661   _cups_ps_obj_t        *obj;           /* Current object on stack */
662
663
664   while ((obj = pop_stack(st)) != NULL)
665     if (obj->type == CUPS_PS_START_ARRAY)
666       break;
667
668   return (obj ? 0 : -1);
669 }
670
671
672 /*
673  * 'copy_stack()' - Copy the top N stack objects.
674  */
675
676 static int                              /* O - 0 on success, -1 on error */
677 copy_stack(_cups_ps_stack_t *st,        /* I - Stack */
678            int              c)          /* I - Number of objects to copy */
679 {
680   int   n;                              /* Index */
681
682
683   if (c < 0)
684     return (-1);
685   else if (c == 0)
686     return (0);
687
688   if ((n = st->num_objs - c) < 0)
689     return (-1);
690
691   while (c > 0)
692   {
693     if (!push_stack(st, st->objs + n))
694       return (-1);
695
696     n ++;
697     c --;
698   }
699
700   return (0);
701 }
702
703
704 /*
705  * 'delete_stack()' - Free memory used by a stack.
706  */
707
708 static void
709 delete_stack(_cups_ps_stack_t *st)      /* I - Stack */
710 {
711   free(st->objs);
712   free(st);
713 }
714
715
716 /*
717  * 'error_object()' - Add an object's value to the current error message.
718  */
719
720 static void
721 error_object(_cups_ps_obj_t *obj)       /* I - Object to add */
722 {
723   switch (obj->type)
724   {
725     case CUPS_PS_NAME :
726         _cupsRasterAddError(" /%s", obj->value.name);
727         break;
728
729     case CUPS_PS_NUMBER :
730         _cupsRasterAddError(" %g", obj->value.number);
731         break;
732
733     case CUPS_PS_STRING :
734         _cupsRasterAddError(" (%s)", obj->value.string);
735         break;
736
737     case CUPS_PS_BOOLEAN :
738         if (obj->value.boolean)
739           _cupsRasterAddError(" true");
740         else
741           _cupsRasterAddError(" false");
742         break;
743
744     case CUPS_PS_NULL :
745         _cupsRasterAddError(" null");
746         break;
747
748     case CUPS_PS_START_ARRAY :
749         _cupsRasterAddError(" [");
750         break;
751
752     case CUPS_PS_END_ARRAY :
753         _cupsRasterAddError(" ]");
754         break;
755
756     case CUPS_PS_START_DICT :
757         _cupsRasterAddError(" <<");
758         break;
759
760     case CUPS_PS_END_DICT :
761         _cupsRasterAddError(" >>");
762         break;
763
764     case CUPS_PS_START_PROC :
765         _cupsRasterAddError(" {");
766         break;
767
768     case CUPS_PS_END_PROC :
769         _cupsRasterAddError(" }");
770         break;
771
772     case CUPS_PS_COPY :
773         _cupsRasterAddError(" --copy--");
774         break;
775
776     case CUPS_PS_CLEARTOMARK :
777         _cupsRasterAddError(" --cleartomark--");
778         break;
779
780     case CUPS_PS_DUP :
781         _cupsRasterAddError(" --dup--");
782         break;
783
784     case CUPS_PS_INDEX :
785         _cupsRasterAddError(" --index--");
786         break;
787
788     case CUPS_PS_POP :
789         _cupsRasterAddError(" --pop--");
790         break;
791
792     case CUPS_PS_ROLL :
793         _cupsRasterAddError(" --roll--");
794         break;
795
796     case CUPS_PS_SETPAGEDEVICE :
797         _cupsRasterAddError(" --setpagedevice--");
798         break;
799
800     case CUPS_PS_STOPPED :
801         _cupsRasterAddError(" --stopped--");
802         break;
803
804     case CUPS_PS_OTHER :
805         _cupsRasterAddError(" --%s--", obj->value.other);
806         break;
807   }
808 }
809
810
811 /*
812  * 'error_stack()' - Add a stack to the current error message...
813  */
814
815 static void
816 error_stack(_cups_ps_stack_t *st,       /* I - Stack */
817             const char       *title)    /* I - Title string */
818 {
819   int                   c;              /* Looping var */
820   _cups_ps_obj_t        *obj;           /* Current object on stack */
821
822
823   _cupsRasterAddError("%s", title);
824
825   for (obj = st->objs, c = st->num_objs; c > 0; c --, obj ++)
826     error_object(obj);
827
828   _cupsRasterAddError("\n");
829 }
830
831
832 /*
833  * 'index_stack()' - Copy the Nth value on the stack.
834  */
835
836 static _cups_ps_obj_t   *               /* O - New object */
837 index_stack(_cups_ps_stack_t *st,       /* I - Stack */
838             int              n)         /* I - Object index */
839 {
840   if (n < 0 || (n = st->num_objs - n - 1) < 0)
841     return (NULL);
842
843   return (push_stack(st, st->objs + n));
844 }
845
846
847 /*
848  * 'new_stack()' - Create a new stack.
849  */
850
851 static _cups_ps_stack_t *               /* O - New stack */
852 new_stack(void)
853 {
854   _cups_ps_stack_t      *st;            /* New stack */
855
856
857   if ((st = calloc(1, sizeof(_cups_ps_stack_t))) == NULL)
858     return (NULL);
859
860   st->alloc_objs = 32;
861
862   if ((st->objs = calloc(32, sizeof(_cups_ps_obj_t))) == NULL)
863   {
864     free(st);
865     return (NULL);
866   }
867   else
868     return (st);
869 }
870
871
872 /*
873  * 'pop_stock()' - Pop the top object off the stack.
874  */
875
876 static _cups_ps_obj_t   *               /* O - Object */
877 pop_stack(_cups_ps_stack_t *st)         /* I - Stack */
878 {
879   if (st->num_objs > 0)
880   {
881     st->num_objs --;
882
883     return (st->objs + st->num_objs);
884   }
885   else
886     return (NULL);
887 }
888
889
890 /*
891  * 'push_stack()' - Push an object on the stack.
892  */
893
894 static _cups_ps_obj_t   *               /* O - New object */
895 push_stack(_cups_ps_stack_t *st,        /* I - Stack */
896            _cups_ps_obj_t   *obj)       /* I - Object */
897 {
898   _cups_ps_obj_t        *temp;          /* New object */
899
900
901   if (st->num_objs >= st->alloc_objs)
902   {
903
904
905     st->alloc_objs += 32;
906
907     if ((temp = realloc(st->objs, st->alloc_objs *
908                                   sizeof(_cups_ps_obj_t))) == NULL)
909       return (NULL);
910
911     st->objs = temp;
912     memset(temp + st->num_objs, 0, 32 * sizeof(_cups_ps_obj_t));
913   }
914
915   temp = st->objs + st->num_objs;
916   st->num_objs ++;
917
918   memcpy(temp, obj, sizeof(_cups_ps_obj_t));
919
920   return (temp);
921 }
922
923
924 /*
925  * 'roll_stack()' - Rotate stack objects.
926  */
927
928 static int                              /* O - 0 on success, -1 on error */
929 roll_stack(_cups_ps_stack_t *st,        /* I - Stack */
930            int              c,          /* I - Number of objects */
931            int              s)          /* I - Amount to shift */
932 {
933   _cups_ps_obj_t        *temp;          /* Temporary array of objects */
934   int                   n;              /* Index into array */
935
936
937   DEBUG_printf(("    roll_stack(st=%p, s=%d, c=%d)\n", st, s, c));
938
939  /*
940   * Range check input...
941   */
942
943   if (c < 0)
944     return (-1);
945   else if (c == 0)
946     return (0);
947
948   if ((n = st->num_objs - c) < 0)
949     return (-1);
950
951   s %= c;
952
953   if (s == 0)
954     return (0);
955
956  /*
957   * Copy N objects and move things around...
958   */
959
960   if (s < 0)
961   {
962    /*
963     * Shift down...
964     */
965
966     s = -s;
967
968     if ((temp = calloc(s, sizeof(_cups_ps_obj_t))) == NULL)
969       return (-1);
970
971     memcpy(temp, st->objs + n, s * sizeof(_cups_ps_obj_t));
972     memmove(st->objs + n, st->objs + n + s, (c - s) * sizeof(_cups_ps_obj_t));
973     memcpy(st->objs + n + c - s, temp, s * sizeof(_cups_ps_obj_t));
974   }
975   else
976   {
977    /*
978     * Shift up...
979     */
980
981     if ((temp = calloc(s, sizeof(_cups_ps_obj_t))) == NULL)
982       return (-1);
983
984     memcpy(temp, st->objs + n + c - s, s * sizeof(_cups_ps_obj_t));
985     memmove(st->objs + n + s, st->objs + n,
986             (c - s) * sizeof(_cups_ps_obj_t));
987     memcpy(st->objs + n, temp, s * sizeof(_cups_ps_obj_t));
988   }
989
990   free(temp);
991
992   return (0);
993 }
994
995
996 /*
997  * 'scan_ps()' - Scan a string for the next PS object.
998  */
999
1000 static _cups_ps_obj_t   *               /* O  - New object or NULL on EOF */
1001 scan_ps(_cups_ps_stack_t *st,           /* I  - Stack */
1002         char             **ptr)         /* IO - String pointer */
1003 {
1004   _cups_ps_obj_t        obj;            /* Current object */
1005   char                  *start,         /* Start of object */
1006                         *cur,           /* Current position */
1007                         *valptr,        /* Pointer into value string */
1008                         *valend;        /* End of value string */
1009   int                   parens;         /* Parenthesis nesting level */
1010
1011
1012  /*
1013   * Skip leading whitespace...
1014   */
1015
1016   for (cur = *ptr; *cur; cur ++)
1017   {
1018     if (*cur == '%')
1019     {
1020      /*
1021       * Comment, skip to end of line...
1022       */
1023
1024       for (cur ++; *cur && *cur != '\n' && *cur != '\r'; cur ++);
1025
1026       if (!*cur)
1027         cur --;
1028     }
1029     else if (!isspace(*cur & 255))
1030       break;
1031   }
1032
1033   if (!*cur)
1034   {
1035     *ptr = NULL;
1036
1037     return (NULL);
1038   }
1039
1040  /*
1041   * See what we have...
1042   */
1043
1044   memset(&obj, 0, sizeof(obj));
1045
1046   switch (*cur)
1047   {
1048     case '(' :                          /* (string) */
1049         obj.type = CUPS_PS_STRING;
1050         start    = cur;
1051
1052         for (cur ++, parens = 1, valptr = obj.value.string,
1053                  valend = obj.value.string + sizeof(obj.value.string) - 1;
1054              *cur;
1055              cur ++)
1056         {
1057           if (*cur == ')' && parens == 1)
1058             break;
1059
1060           if (*cur == '(')
1061             parens ++;
1062           else if (*cur == ')')
1063             parens --;
1064
1065           if (valptr >= valend)
1066           {
1067             *ptr = start;
1068
1069             return (NULL);
1070           }
1071
1072           if (*cur == '\\')
1073           {
1074            /*
1075             * Decode escaped character...
1076             */
1077
1078             cur ++;
1079
1080             if (*cur == 'b')
1081               *valptr++ = '\b';
1082             else if (*cur == 'f')
1083               *valptr++ = '\f';
1084             else if (*cur == 'n')
1085               *valptr++ = '\n';
1086             else if (*cur == 'r')
1087               *valptr++ = '\r';
1088             else if (*cur == 't')
1089               *valptr++ = '\t';
1090             else if (*cur >= '0' && *cur <= '7')
1091             {
1092               int ch = *cur - '0';
1093
1094               if (cur[1] >= '0' && cur[1] <= '7')
1095               {
1096                 cur ++;
1097                 ch = (ch << 3) + *cur - '0';
1098               }
1099
1100               if (cur[1] >= '0' && cur[1] <= '7')
1101               {
1102                 cur ++;
1103                 ch = (ch << 3) + *cur - '0';
1104               }
1105
1106               *valptr++ = ch;
1107             }
1108             else if (*cur == '\r')
1109             {
1110               if (cur[1] == '\n')
1111                 cur ++;
1112             }
1113             else if (*cur != '\n')
1114               *valptr++ = *cur;
1115           }
1116           else
1117             *valptr++ = *cur;
1118         }
1119
1120         if (*cur != ')')
1121         {
1122           *ptr = start;
1123
1124           return (NULL);
1125         }
1126
1127         cur ++;
1128         break;
1129
1130     case '[' :                          /* Start array */
1131         obj.type = CUPS_PS_START_ARRAY;
1132         cur ++;
1133         break;
1134
1135     case ']' :                          /* End array */
1136         obj.type = CUPS_PS_END_ARRAY;
1137         cur ++;
1138         break;
1139
1140     case '<' :                          /* Start dictionary or hex string */
1141         if (cur[1] == '<')
1142         {
1143           obj.type = CUPS_PS_START_DICT;
1144           cur += 2;
1145         }
1146         else
1147         {
1148           obj.type = CUPS_PS_STRING;
1149           start    = cur;
1150
1151           for (cur ++, valptr = obj.value.string,
1152                    valend = obj.value.string + sizeof(obj.value.string) - 1;
1153                *cur;
1154                cur ++)
1155           {
1156             int ch;                     /* Current character */
1157
1158
1159
1160             if (*cur == '>')
1161               break;
1162             else if (valptr >= valend || !isxdigit(*cur & 255))
1163             {
1164               *ptr = start;
1165               return (NULL);
1166             }
1167
1168             if (*cur >= '0' && *cur <= '9')
1169               ch = (*cur - '0') << 4;
1170             else
1171               ch = (tolower(*cur) - 'a' + 10) << 4;
1172
1173             if (isxdigit(cur[1] & 255))
1174             {
1175               cur ++;
1176
1177               if (*cur >= '0' && *cur <= '9')
1178                 ch |= *cur - '0';
1179               else
1180                 ch |= tolower(*cur) - 'a' + 10;
1181             }
1182
1183             *valptr++ = ch;
1184           }
1185
1186           if (*cur != '>')
1187           {
1188             *ptr = start;
1189             return (NULL);
1190           }
1191
1192           cur ++;
1193         }
1194         break;
1195
1196     case '>' :                          /* End dictionary? */
1197         if (cur[1] == '>')
1198         {
1199           obj.type = CUPS_PS_END_DICT;
1200           cur += 2;
1201         }
1202         else
1203         {
1204           obj.type           = CUPS_PS_OTHER;
1205           obj.value.other[0] = *cur;
1206
1207           cur ++;
1208         }
1209         break;
1210
1211     case '{' :                          /* Start procedure */
1212         obj.type = CUPS_PS_START_PROC;
1213         cur ++;
1214         break;
1215
1216     case '}' :                          /* End procedure */
1217         obj.type = CUPS_PS_END_PROC;
1218         cur ++;
1219         break;
1220
1221     case '-' :                          /* Possible number */
1222     case '+' :
1223         if (!isdigit(cur[1] & 255) && cur[1] != '.')
1224         {
1225           obj.type           = CUPS_PS_OTHER;
1226           obj.value.other[0] = *cur;
1227
1228           cur ++;
1229           break;
1230         }
1231
1232     case '0' :                          /* Number */
1233     case '1' :
1234     case '2' :
1235     case '3' :
1236     case '4' :
1237     case '5' :
1238     case '6' :
1239     case '7' :
1240     case '8' :
1241     case '9' :
1242     case '.' :
1243         obj.type = CUPS_PS_NUMBER;
1244
1245         start = cur;
1246         for (cur ++; *cur; cur ++)
1247           if (!isdigit(*cur & 255))
1248             break;
1249
1250         if (*cur == '#')
1251         {
1252          /*
1253           * Integer with radix...
1254           */
1255
1256           obj.value.number = strtol(cur + 1, &cur, atoi(start));
1257           break;
1258         }
1259         else if (strchr(".Ee()<>[]{}/%", *cur) || isspace(*cur & 255))
1260         {
1261          /*
1262           * Integer or real number...
1263           */
1264
1265           obj.value.number = _cupsStrScand(start, &cur, localeconv());
1266           break;
1267         }
1268         else
1269           cur = start;
1270
1271     default :                           /* Operator/variable name */
1272         start = cur;
1273
1274         if (*cur == '/')
1275         {
1276           obj.type = CUPS_PS_NAME;
1277           valptr   = obj.value.name;
1278           valend   = obj.value.name + sizeof(obj.value.name) - 1;
1279           cur ++;
1280         }
1281         else
1282         {
1283           obj.type = CUPS_PS_OTHER;
1284           valptr   = obj.value.other;
1285           valend   = obj.value.other + sizeof(obj.value.other) - 1;
1286         }
1287
1288         while (*cur)
1289         {
1290           if (strchr("()<>[]{}/%", *cur) || isspace(*cur & 255))
1291             break;
1292           else if (valptr < valend)
1293             *valptr++ = *cur++;
1294           else
1295           {
1296             *ptr = start;
1297             return (NULL);
1298           }
1299         }
1300
1301         if (obj.type == CUPS_PS_OTHER)
1302         {
1303           if (!strcmp(obj.value.other, "true"))
1304           {
1305             obj.type          = CUPS_PS_BOOLEAN;
1306             obj.value.boolean = 1;
1307           }
1308           else if (!strcmp(obj.value.other, "false"))
1309           {
1310             obj.type          = CUPS_PS_BOOLEAN;
1311             obj.value.boolean = 0;
1312           }
1313           else if (!strcmp(obj.value.other, "null"))
1314             obj.type = CUPS_PS_NULL;
1315           else if (!strcmp(obj.value.other, "cleartomark"))
1316             obj.type = CUPS_PS_CLEARTOMARK;
1317           else if (!strcmp(obj.value.other, "copy"))
1318             obj.type = CUPS_PS_COPY;
1319           else if (!strcmp(obj.value.other, "dup"))
1320             obj.type = CUPS_PS_DUP;
1321           else if (!strcmp(obj.value.other, "index"))
1322             obj.type = CUPS_PS_INDEX;
1323           else if (!strcmp(obj.value.other, "pop"))
1324             obj.type = CUPS_PS_POP;
1325           else if (!strcmp(obj.value.other, "roll"))
1326             obj.type = CUPS_PS_ROLL;
1327           else if (!strcmp(obj.value.other, "setpagedevice"))
1328             obj.type = CUPS_PS_SETPAGEDEVICE;
1329           else if (!strcmp(obj.value.other, "stopped"))
1330             obj.type = CUPS_PS_STOPPED;
1331         }
1332         break;
1333   }
1334
1335  /*
1336   * Save the current position in the string and return the new object...
1337   */
1338
1339   *ptr = cur;
1340
1341   return (push_stack(st, &obj));
1342 }
1343
1344
1345 /*
1346  * 'setpagedevice()' - Simulate the PostScript setpagedevice operator.
1347  */
1348
1349 static int                              /* O - 0 on success, -1 on error */
1350 setpagedevice(
1351     _cups_ps_stack_t    *st,            /* I - Stack */
1352     cups_page_header2_t *h,             /* O - Page header */
1353     int                 *preferred_bits)/* O - Preferred bits per color */
1354 {
1355   int                   i;              /* Index into array */
1356   _cups_ps_obj_t        *obj,           /* Current object */
1357                         *end;           /* End of dictionary */
1358   const char            *name;          /* Attribute name */
1359
1360
1361  /*
1362   * Make sure we have a dictionary on the stack...
1363   */
1364
1365   if (st->num_objs == 0)
1366     return (-1);
1367
1368   obj = end = st->objs + st->num_objs - 1;
1369
1370   if (obj->type != CUPS_PS_END_DICT)
1371     return (-1);
1372
1373   obj --;
1374
1375   while (obj > st->objs)
1376   {
1377     if (obj->type == CUPS_PS_START_DICT)
1378       break;
1379
1380     obj --;
1381   }
1382
1383   if (obj < st->objs)
1384     return (-1);
1385
1386  /*
1387   * Found the start of the dictionary, empty the stack to this point...
1388   */
1389
1390   st->num_objs = (int)(obj - st->objs);
1391
1392  /*
1393   * Now pull /name and value pairs from the dictionary...
1394   */
1395
1396   DEBUG_puts("setpagedevice: Dictionary:");
1397
1398   for (obj ++; obj < end; obj ++)
1399   {
1400    /*
1401     * Grab the name...
1402     */
1403
1404     if (obj->type != CUPS_PS_NAME)
1405       return (-1);
1406
1407     name = obj->value.name;
1408     obj ++;
1409
1410 #ifdef DEBUG
1411     DEBUG_printf(("setpagedevice: /%s ", name));
1412     DEBUG_object(obj);
1413 #endif /* DEBUG */
1414
1415    /*
1416     * Then grab the value...
1417     */
1418
1419     if (!strcmp(name, "MediaClass") && obj->type == CUPS_PS_STRING)
1420       strlcpy(h->MediaClass, obj->value.string, sizeof(h->MediaClass));
1421     else if (!strcmp(name, "MediaColor") && obj->type == CUPS_PS_STRING)
1422       strlcpy(h->MediaColor, obj->value.string, sizeof(h->MediaColor));
1423     else if (!strcmp(name, "MediaType") && obj->type == CUPS_PS_STRING)
1424       strlcpy(h->MediaType, obj->value.string, sizeof(h->MediaType));
1425     else if (!strcmp(name, "OutputType") && obj->type == CUPS_PS_STRING)
1426       strlcpy(h->OutputType, obj->value.string, sizeof(h->OutputType));
1427     else if (!strcmp(name, "AdvanceDistance") && obj->type == CUPS_PS_NUMBER)
1428       h->AdvanceDistance = (unsigned)obj->value.number;
1429     else if (!strcmp(name, "AdvanceMedia") && obj->type == CUPS_PS_NUMBER)
1430       h->AdvanceMedia = (unsigned)obj->value.number;
1431     else if (!strcmp(name, "Collate") && obj->type == CUPS_PS_BOOLEAN)
1432       h->Collate = (unsigned)obj->value.boolean;
1433     else if (!strcmp(name, "CutMedia") && obj->type == CUPS_PS_NUMBER)
1434       h->CutMedia = (cups_cut_t)(unsigned)obj->value.number;
1435     else if (!strcmp(name, "Duplex") && obj->type == CUPS_PS_BOOLEAN)
1436       h->Duplex = (unsigned)obj->value.boolean;
1437     else if (!strcmp(name, "HWResolution") && obj->type == CUPS_PS_START_ARRAY)
1438     {
1439       if (obj[1].type == CUPS_PS_NUMBER && obj[2].type == CUPS_PS_NUMBER &&
1440           obj[3].type == CUPS_PS_END_ARRAY)
1441       {
1442         h->HWResolution[0] = (unsigned)obj[1].value.number;
1443         h->HWResolution[1] = (unsigned)obj[2].value.number;
1444         obj += 3;
1445       }
1446       else
1447         return (-1);
1448     }
1449     else if (!strcmp(name, "InsertSheet") && obj->type == CUPS_PS_BOOLEAN)
1450       h->InsertSheet = (unsigned)obj->value.boolean;
1451     else if (!strcmp(name, "Jog") && obj->type == CUPS_PS_NUMBER)
1452       h->Jog = (unsigned)obj->value.number;
1453     else if (!strcmp(name, "LeadingEdge") && obj->type == CUPS_PS_NUMBER)
1454       h->LeadingEdge = (unsigned)obj->value.number;
1455     else if (!strcmp(name, "ManualFeed") && obj->type == CUPS_PS_BOOLEAN)
1456       h->ManualFeed = (unsigned)obj->value.boolean;
1457     else if ((!strcmp(name, "cupsMediaPosition") ||
1458               !strcmp(name, "MediaPosition")) && obj->type == CUPS_PS_NUMBER)
1459     {
1460      /*
1461       * cupsMediaPosition is supported for backwards compatibility only.
1462       * We added it back in the Ghostscript 5.50 days to work around a
1463       * bug in Ghostscript WRT handling of MediaPosition and setpagedevice.
1464       *
1465       * All new development should set MediaPosition...
1466       */
1467
1468       h->MediaPosition = (unsigned)obj->value.number;
1469     }
1470     else if (!strcmp(name, "MediaWeight") && obj->type == CUPS_PS_NUMBER)
1471       h->MediaWeight = (unsigned)obj->value.number;
1472     else if (!strcmp(name, "MirrorPrint") && obj->type == CUPS_PS_BOOLEAN)
1473       h->MirrorPrint = (unsigned)obj->value.boolean;
1474     else if (!strcmp(name, "NegativePrint") && obj->type == CUPS_PS_BOOLEAN)
1475       h->NegativePrint = (unsigned)obj->value.boolean;
1476     else if (!strcmp(name, "NumCopies") && obj->type == CUPS_PS_NUMBER)
1477       h->NumCopies = (unsigned)obj->value.number;
1478     else if (!strcmp(name, "Orientation") && obj->type == CUPS_PS_NUMBER)
1479       h->Orientation = (unsigned)obj->value.number;
1480     else if (!strcmp(name, "OutputFaceUp") && obj->type == CUPS_PS_BOOLEAN)
1481       h->OutputFaceUp = (unsigned)obj->value.boolean;
1482     else if (!strcmp(name, "PageSize") && obj->type == CUPS_PS_START_ARRAY)
1483     {
1484       if (obj[1].type == CUPS_PS_NUMBER && obj[2].type == CUPS_PS_NUMBER &&
1485           obj[3].type == CUPS_PS_END_ARRAY)
1486       {
1487         h->cupsPageSize[0] = (float)obj[1].value.number;
1488         h->cupsPageSize[1] = (float)obj[2].value.number;
1489
1490         h->PageSize[0] = (unsigned)obj[1].value.number;
1491         h->PageSize[1] = (unsigned)obj[2].value.number;
1492
1493         obj += 3;
1494       }
1495       else
1496         return (-1);
1497     }
1498     else if (!strcmp(name, "Separations") && obj->type == CUPS_PS_BOOLEAN)
1499       h->Separations = (unsigned)obj->value.boolean;
1500     else if (!strcmp(name, "TraySwitch") && obj->type == CUPS_PS_BOOLEAN)
1501       h->TraySwitch = (unsigned)obj->value.boolean;
1502     else if (!strcmp(name, "Tumble") && obj->type == CUPS_PS_BOOLEAN)
1503       h->Tumble = (unsigned)obj->value.boolean;
1504     else if (!strcmp(name, "cupsMediaType") && obj->type == CUPS_PS_NUMBER)
1505       h->cupsMediaType = (unsigned)obj->value.number;
1506     else if (!strcmp(name, "cupsBitsPerColor") && obj->type == CUPS_PS_NUMBER)
1507       h->cupsBitsPerColor = (unsigned)obj->value.number;
1508     else if (!strcmp(name, "cupsPreferredBitsPerColor") &&
1509              obj->type == CUPS_PS_NUMBER)
1510       *preferred_bits = (int)obj->value.number;
1511     else if (!strcmp(name, "cupsColorOrder") && obj->type == CUPS_PS_NUMBER)
1512       h->cupsColorOrder = (cups_order_t)(unsigned)obj->value.number;
1513     else if (!strcmp(name, "cupsColorSpace") && obj->type == CUPS_PS_NUMBER)
1514       h->cupsColorSpace = (cups_cspace_t)(unsigned)obj->value.number;
1515     else if (!strcmp(name, "cupsCompression") && obj->type == CUPS_PS_NUMBER)
1516       h->cupsCompression = (unsigned)obj->value.number;
1517     else if (!strcmp(name, "cupsRowCount") && obj->type == CUPS_PS_NUMBER)
1518       h->cupsRowCount = (unsigned)obj->value.number;
1519     else if (!strcmp(name, "cupsRowFeed") && obj->type == CUPS_PS_NUMBER)
1520       h->cupsRowFeed = (unsigned)obj->value.number;
1521     else if (!strcmp(name, "cupsRowStep") && obj->type == CUPS_PS_NUMBER)
1522       h->cupsRowStep = (unsigned)obj->value.number;
1523     else if (!strcmp(name, "cupsBorderlessScalingFactor") &&
1524              obj->type == CUPS_PS_NUMBER)
1525       h->cupsBorderlessScalingFactor = (float)obj->value.number;
1526     else if (!strncmp(name, "cupsInteger", 11) && obj->type == CUPS_PS_NUMBER)
1527     {
1528       if ((i = atoi(name + 11)) < 0 || i > 15)
1529         return (-1);
1530
1531       h->cupsInteger[i] = (unsigned)obj->value.number;
1532     }
1533     else if (!strncmp(name, "cupsReal", 8) && obj->type == CUPS_PS_NUMBER)
1534     {
1535       if ((i = atoi(name + 8)) < 0 || i > 15)
1536         return (-1);
1537
1538       h->cupsReal[i] = (float)obj->value.number;
1539     }
1540     else if (!strncmp(name, "cupsString", 10) && obj->type == CUPS_PS_STRING)
1541     {
1542       if ((i = atoi(name + 10)) < 0 || i > 15)
1543         return (-1);
1544
1545       strlcpy(h->cupsString[i], obj->value.string, sizeof(h->cupsString[i]));
1546     }
1547     else if (!strcmp(name, "cupsMarkerType") && obj->type == CUPS_PS_STRING)
1548       strlcpy(h->cupsMarkerType, obj->value.string, sizeof(h->cupsMarkerType));
1549     else if (!strcmp(name, "cupsPageSizeName") && obj->type == CUPS_PS_STRING)
1550       strlcpy(h->cupsPageSizeName, obj->value.string,
1551               sizeof(h->cupsPageSizeName));
1552     else if (!strcmp(name, "cupsRenderingIntent") &&
1553              obj->type == CUPS_PS_STRING)
1554       strlcpy(h->cupsRenderingIntent, obj->value.string,
1555               sizeof(h->cupsRenderingIntent));
1556     else
1557     {
1558      /*
1559       * Ignore unknown name+value...
1560       */
1561
1562       DEBUG_printf(("    Unknown name (\"%s\") or value...\n", name));
1563
1564       while (obj[1].type != CUPS_PS_NAME && obj < end)
1565         obj ++;
1566     }
1567   }
1568
1569   return (0);
1570 }
1571
1572
1573 #ifdef DEBUG
1574 /*
1575  * 'DEBUG_object()' - Print an object's value...
1576  */
1577
1578 static void
1579 DEBUG_object(_cups_ps_obj_t *obj)       /* I - Object to print */
1580 {
1581   switch (obj->type)
1582   {
1583     case CUPS_PS_NAME :
1584         DEBUG_printf(("/%s\n", obj->value.name));
1585         break;
1586
1587     case CUPS_PS_NUMBER :
1588         DEBUG_printf(("%g\n", obj->value.number));
1589         break;
1590
1591     case CUPS_PS_STRING :
1592         DEBUG_printf(("(%s)\n", obj->value.string));
1593         break;
1594
1595     case CUPS_PS_BOOLEAN :
1596         if (obj->value.boolean)
1597           DEBUG_puts("true");
1598         else
1599           DEBUG_puts("false");
1600         break;
1601
1602     case CUPS_PS_NULL :
1603         DEBUG_puts("null");
1604         break;
1605
1606     case CUPS_PS_START_ARRAY :
1607         DEBUG_puts("[");
1608         break;
1609
1610     case CUPS_PS_END_ARRAY :
1611         DEBUG_puts("]");
1612         break;
1613
1614     case CUPS_PS_START_DICT :
1615         DEBUG_puts("<<");
1616         break;
1617
1618     case CUPS_PS_END_DICT :
1619         DEBUG_puts(">>");
1620         break;
1621
1622     case CUPS_PS_START_PROC :
1623         DEBUG_puts("{");
1624         break;
1625
1626     case CUPS_PS_END_PROC :
1627         DEBUG_puts("}");
1628         break;
1629
1630     case CUPS_PS_CLEARTOMARK :
1631         DEBUG_puts("--cleartomark--");
1632         break;
1633
1634     case CUPS_PS_COPY :
1635         DEBUG_puts("--copy--");
1636         break;
1637
1638     case CUPS_PS_DUP :
1639         DEBUG_puts("--dup--");
1640         break;
1641
1642     case CUPS_PS_INDEX :
1643         DEBUG_puts("--index--");
1644         break;
1645
1646     case CUPS_PS_POP :
1647         DEBUG_puts("--pop--");
1648         break;
1649
1650     case CUPS_PS_ROLL :
1651         DEBUG_puts("--roll--");
1652         break;
1653
1654     case CUPS_PS_SETPAGEDEVICE :
1655         DEBUG_puts("--setpagedevice--");
1656         break;
1657
1658     case CUPS_PS_STOPPED :
1659         DEBUG_puts("--stopped--");
1660         break;
1661
1662     case CUPS_PS_OTHER :
1663         DEBUG_printf(("--%s--\n", obj->value.other));
1664         break;
1665   }
1666 }
1667
1668
1669 /*
1670  * 'DEBUG_stack()' - Print a stack...
1671  */
1672
1673 static void
1674 DEBUG_stack(_cups_ps_stack_t *st)       /* I - Stack */
1675 {
1676   int                   c;              /* Looping var */
1677   _cups_ps_obj_t        *obj;           /* Current object on stack */
1678
1679
1680   for (obj = st->objs, c = st->num_objs; c > 0; c --, obj ++)
1681     DEBUG_object(obj);
1682 }
1683 #endif /* DEBUG */
1684
1685
1686 /*
1687  * End of "$Id: interpret.c 11173 2013-07-23 12:31:34Z msweet $".
1688  */