1/*
2 * "$Id: interpret.c 11693 2014-03-11 01:24:45Z 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
50typedef 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
74typedef 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
87typedef 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
99static int		cleartomark_stack(_cups_ps_stack_t *st);
100static int		copy_stack(_cups_ps_stack_t *st, int count);
101static void		delete_stack(_cups_ps_stack_t *st);
102static void		error_object(_cups_ps_obj_t *obj);
103static void		error_stack(_cups_ps_stack_t *st, const char *title);
104static _cups_ps_obj_t	*index_stack(_cups_ps_stack_t *st, int n);
105static _cups_ps_stack_t	*new_stack(void);
106static _cups_ps_obj_t	*pop_stack(_cups_ps_stack_t *st);
107static _cups_ps_obj_t	*push_stack(_cups_ps_stack_t *st,
108			            _cups_ps_obj_t *obj);
109static int		roll_stack(_cups_ps_stack_t *st, int c, int s);
110static _cups_ps_obj_t	*scan_ps(_cups_ps_stack_t *st, char **ptr);
111static int		setpagedevice(_cups_ps_stack_t *st,
112			                cups_page_header2_t *h,
113			                int *preferred_bits);
114#ifdef DEBUG
115static void		DEBUG_object(_cups_ps_obj_t *obj);
116static 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
150int					/* O - 0 on success, -1 on failure */
151cupsRasterInterpretPPD(
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  strlcpy(h->cupsPageSizeName, "Letter", sizeof(h->cupsPageSizeName));
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
467int					/* 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  int			error = 0;	/* Error condition? */
474  _cups_ps_stack_t	*st;		/* PostScript value stack */
475  _cups_ps_obj_t	*obj;		/* Object from top of stack */
476  char			*codecopy,	/* Copy of code */
477			*codeptr;	/* Pointer into copy of code */
478
479
480  DEBUG_printf(("_cupsRasterExecPS(h=%p, preferred_bits=%p, code=\"%s\")\n",
481                h, preferred_bits, code));
482
483 /*
484  * Copy the PostScript code and create a stack...
485  */
486
487  if ((codecopy = strdup(code)) == NULL)
488  {
489    _cupsRasterAddError("Unable to duplicate code string.\n");
490    return (-1);
491  }
492
493  if ((st = new_stack()) == NULL)
494  {
495    _cupsRasterAddError("Unable to create stack.\n");
496    free(codecopy);
497    return (-1);
498  }
499
500 /*
501  * Parse the PS string until we run out of data...
502  */
503
504  codeptr = codecopy;
505
506  while ((obj = scan_ps(st, &codeptr)) != NULL)
507  {
508#ifdef DEBUG
509    DEBUG_printf(("_cupsRasterExecPS: Stack (%d objects)\n", st->num_objs));
510    DEBUG_object(obj);
511#endif /* DEBUG */
512
513    switch (obj->type)
514    {
515      default :
516          /* Do nothing for regular values */
517	  break;
518
519      case CUPS_PS_CLEARTOMARK :
520          pop_stack(st);
521
522	  if (cleartomark_stack(st))
523	    _cupsRasterAddError("cleartomark: Stack underflow!\n");
524
525#ifdef DEBUG
526          DEBUG_puts("    dup: ");
527	  DEBUG_stack(st);
528#endif /* DEBUG */
529          break;
530
531      case CUPS_PS_COPY :
532          pop_stack(st);
533	  if ((obj = pop_stack(st)) != NULL)
534	  {
535	    copy_stack(st, (int)obj->value.number);
536
537#ifdef DEBUG
538            DEBUG_puts("_cupsRasterExecPS: copy");
539	    DEBUG_stack(st);
540#endif /* DEBUG */
541          }
542          break;
543
544      case CUPS_PS_DUP :
545          pop_stack(st);
546	  copy_stack(st, 1);
547
548#ifdef DEBUG
549          DEBUG_puts("_cupsRasterExecPS: dup");
550	  DEBUG_stack(st);
551#endif /* DEBUG */
552          break;
553
554      case CUPS_PS_INDEX :
555          pop_stack(st);
556	  if ((obj = pop_stack(st)) != NULL)
557	  {
558	    index_stack(st, (int)obj->value.number);
559
560#ifdef DEBUG
561            DEBUG_puts("_cupsRasterExecPS: index");
562	    DEBUG_stack(st);
563#endif /* DEBUG */
564          }
565          break;
566
567      case CUPS_PS_POP :
568          pop_stack(st);
569          pop_stack(st);
570
571#ifdef DEBUG
572          DEBUG_puts("_cupsRasterExecPS: pop");
573	  DEBUG_stack(st);
574#endif /* DEBUG */
575          break;
576
577      case CUPS_PS_ROLL :
578          pop_stack(st);
579	  if ((obj = pop_stack(st)) != NULL)
580	  {
581            int		c;		/* Count */
582
583
584            c = (int)obj->value.number;
585
586	    if ((obj = pop_stack(st)) != NULL)
587	    {
588	      roll_stack(st, (int)obj->value.number, c);
589
590#ifdef DEBUG
591              DEBUG_puts("_cupsRasterExecPS: roll");
592	      DEBUG_stack(st);
593#endif /* DEBUG */
594            }
595	  }
596          break;
597
598      case CUPS_PS_SETPAGEDEVICE :
599          pop_stack(st);
600	  setpagedevice(st, h, preferred_bits);
601
602#ifdef DEBUG
603          DEBUG_puts("_cupsRasterExecPS: setpagedevice");
604	  DEBUG_stack(st);
605#endif /* DEBUG */
606          break;
607
608      case CUPS_PS_START_PROC :
609      case CUPS_PS_END_PROC :
610      case CUPS_PS_STOPPED :
611          pop_stack(st);
612	  break;
613
614      case CUPS_PS_OTHER :
615          _cupsRasterAddError("Unknown operator \"%s\"!\n", obj->value.other);
616	  error = 1;
617          DEBUG_printf(("_cupsRasterExecPS: Unknown operator \"%s\"!\n",
618	                obj->value.other));
619          break;
620    }
621
622    if (error)
623      break;
624  }
625
626 /*
627  * Cleanup...
628  */
629
630  free(codecopy);
631
632  if (st->num_objs > 0)
633  {
634    error_stack(st, "Stack not empty:");
635
636#ifdef DEBUG
637    DEBUG_puts("_cupsRasterExecPS: Stack not empty:");
638    DEBUG_stack(st);
639#endif /* DEBUG */
640
641    delete_stack(st);
642
643    return (-1);
644  }
645
646  delete_stack(st);
647
648 /*
649  * Return success...
650  */
651
652  return (0);
653}
654
655
656/*
657 * 'cleartomark_stack()' - Clear to the last mark ([) on the stack.
658 */
659
660static int				/* O - 0 on success, -1 on error */
661cleartomark_stack(_cups_ps_stack_t *st)	/* I - Stack */
662{
663  _cups_ps_obj_t	*obj;		/* Current object on stack */
664
665
666  while ((obj = pop_stack(st)) != NULL)
667    if (obj->type == CUPS_PS_START_ARRAY)
668      break;
669
670  return (obj ? 0 : -1);
671}
672
673
674/*
675 * 'copy_stack()' - Copy the top N stack objects.
676 */
677
678static int				/* O - 0 on success, -1 on error */
679copy_stack(_cups_ps_stack_t *st,	/* I - Stack */
680           int              c)		/* I - Number of objects to copy */
681{
682  int	n;				/* Index */
683
684
685  if (c < 0)
686    return (-1);
687  else if (c == 0)
688    return (0);
689
690  if ((n = st->num_objs - c) < 0)
691    return (-1);
692
693  while (c > 0)
694  {
695    if (!push_stack(st, st->objs + n))
696      return (-1);
697
698    n ++;
699    c --;
700  }
701
702  return (0);
703}
704
705
706/*
707 * 'delete_stack()' - Free memory used by a stack.
708 */
709
710static void
711delete_stack(_cups_ps_stack_t *st)	/* I - Stack */
712{
713  free(st->objs);
714  free(st);
715}
716
717
718/*
719 * 'error_object()' - Add an object's value to the current error message.
720 */
721
722static void
723error_object(_cups_ps_obj_t *obj)	/* I - Object to add */
724{
725  switch (obj->type)
726  {
727    case CUPS_PS_NAME :
728	_cupsRasterAddError(" /%s", obj->value.name);
729	break;
730
731    case CUPS_PS_NUMBER :
732	_cupsRasterAddError(" %g", obj->value.number);
733	break;
734
735    case CUPS_PS_STRING :
736	_cupsRasterAddError(" (%s)", obj->value.string);
737	break;
738
739    case CUPS_PS_BOOLEAN :
740	if (obj->value.boolean)
741	  _cupsRasterAddError(" true");
742	else
743	  _cupsRasterAddError(" false");
744	break;
745
746    case CUPS_PS_NULL :
747	_cupsRasterAddError(" null");
748	break;
749
750    case CUPS_PS_START_ARRAY :
751	_cupsRasterAddError(" [");
752	break;
753
754    case CUPS_PS_END_ARRAY :
755	_cupsRasterAddError(" ]");
756	break;
757
758    case CUPS_PS_START_DICT :
759	_cupsRasterAddError(" <<");
760	break;
761
762    case CUPS_PS_END_DICT :
763	_cupsRasterAddError(" >>");
764	break;
765
766    case CUPS_PS_START_PROC :
767	_cupsRasterAddError(" {");
768	break;
769
770    case CUPS_PS_END_PROC :
771	_cupsRasterAddError(" }");
772	break;
773
774    case CUPS_PS_COPY :
775	_cupsRasterAddError(" --copy--");
776        break;
777
778    case CUPS_PS_CLEARTOMARK :
779	_cupsRasterAddError(" --cleartomark--");
780        break;
781
782    case CUPS_PS_DUP :
783	_cupsRasterAddError(" --dup--");
784        break;
785
786    case CUPS_PS_INDEX :
787	_cupsRasterAddError(" --index--");
788        break;
789
790    case CUPS_PS_POP :
791	_cupsRasterAddError(" --pop--");
792        break;
793
794    case CUPS_PS_ROLL :
795	_cupsRasterAddError(" --roll--");
796        break;
797
798    case CUPS_PS_SETPAGEDEVICE :
799	_cupsRasterAddError(" --setpagedevice--");
800        break;
801
802    case CUPS_PS_STOPPED :
803	_cupsRasterAddError(" --stopped--");
804        break;
805
806    case CUPS_PS_OTHER :
807	_cupsRasterAddError(" --%s--", obj->value.other);
808	break;
809  }
810}
811
812
813/*
814 * 'error_stack()' - Add a stack to the current error message...
815 */
816
817static void
818error_stack(_cups_ps_stack_t *st,	/* I - Stack */
819            const char       *title)	/* I - Title string */
820{
821  int			c;		/* Looping var */
822  _cups_ps_obj_t	*obj;		/* Current object on stack */
823
824
825  _cupsRasterAddError("%s", title);
826
827  for (obj = st->objs, c = st->num_objs; c > 0; c --, obj ++)
828    error_object(obj);
829
830  _cupsRasterAddError("\n");
831}
832
833
834/*
835 * 'index_stack()' - Copy the Nth value on the stack.
836 */
837
838static _cups_ps_obj_t	*		/* O - New object */
839index_stack(_cups_ps_stack_t *st,	/* I - Stack */
840            int              n)		/* I - Object index */
841{
842  if (n < 0 || (n = st->num_objs - n - 1) < 0)
843    return (NULL);
844
845  return (push_stack(st, st->objs + n));
846}
847
848
849/*
850 * 'new_stack()' - Create a new stack.
851 */
852
853static _cups_ps_stack_t	*		/* O - New stack */
854new_stack(void)
855{
856  _cups_ps_stack_t	*st;		/* New stack */
857
858
859  if ((st = calloc(1, sizeof(_cups_ps_stack_t))) == NULL)
860    return (NULL);
861
862  st->alloc_objs = 32;
863
864  if ((st->objs = calloc(32, sizeof(_cups_ps_obj_t))) == NULL)
865  {
866    free(st);
867    return (NULL);
868  }
869  else
870    return (st);
871}
872
873
874/*
875 * 'pop_stock()' - Pop the top object off the stack.
876 */
877
878static _cups_ps_obj_t	*		/* O - Object */
879pop_stack(_cups_ps_stack_t *st)		/* I - Stack */
880{
881  if (st->num_objs > 0)
882  {
883    st->num_objs --;
884
885    return (st->objs + st->num_objs);
886  }
887  else
888    return (NULL);
889}
890
891
892/*
893 * 'push_stack()' - Push an object on the stack.
894 */
895
896static _cups_ps_obj_t	*		/* O - New object */
897push_stack(_cups_ps_stack_t *st,	/* I - Stack */
898           _cups_ps_obj_t   *obj)	/* I - Object */
899{
900  _cups_ps_obj_t	*temp;		/* New object */
901
902
903  if (st->num_objs >= st->alloc_objs)
904  {
905
906
907    st->alloc_objs += 32;
908
909    if ((temp = realloc(st->objs, st->alloc_objs *
910                                  sizeof(_cups_ps_obj_t))) == NULL)
911      return (NULL);
912
913    st->objs = temp;
914    memset(temp + st->num_objs, 0, 32 * sizeof(_cups_ps_obj_t));
915  }
916
917  temp = st->objs + st->num_objs;
918  st->num_objs ++;
919
920  memcpy(temp, obj, sizeof(_cups_ps_obj_t));
921
922  return (temp);
923}
924
925
926/*
927 * 'roll_stack()' - Rotate stack objects.
928 */
929
930static int				/* O - 0 on success, -1 on error */
931roll_stack(_cups_ps_stack_t *st,	/* I - Stack */
932	   int              c,		/* I - Number of objects */
933           int              s)		/* I - Amount to shift */
934{
935  _cups_ps_obj_t	*temp;		/* Temporary array of objects */
936  int			n;		/* Index into array */
937
938
939  DEBUG_printf(("    roll_stack(st=%p, s=%d, c=%d)\n", st, s, c));
940
941 /*
942  * Range check input...
943  */
944
945  if (c < 0)
946    return (-1);
947  else if (c == 0)
948    return (0);
949
950  if ((n = st->num_objs - c) < 0)
951    return (-1);
952
953  s %= c;
954
955  if (s == 0)
956    return (0);
957
958 /*
959  * Copy N objects and move things around...
960  */
961
962  if (s < 0)
963  {
964   /*
965    * Shift down...
966    */
967
968    s = -s;
969
970    if ((temp = calloc(s, sizeof(_cups_ps_obj_t))) == NULL)
971      return (-1);
972
973    memcpy(temp, st->objs + n, s * sizeof(_cups_ps_obj_t));
974    memmove(st->objs + n, st->objs + n + s, (c - s) * sizeof(_cups_ps_obj_t));
975    memcpy(st->objs + n + c - s, temp, s * sizeof(_cups_ps_obj_t));
976  }
977  else
978  {
979   /*
980    * Shift up...
981    */
982
983    if ((temp = calloc(s, sizeof(_cups_ps_obj_t))) == NULL)
984      return (-1);
985
986    memcpy(temp, st->objs + n + c - s, s * sizeof(_cups_ps_obj_t));
987    memmove(st->objs + n + s, st->objs + n,
988            (c - s) * sizeof(_cups_ps_obj_t));
989    memcpy(st->objs + n, temp, s * sizeof(_cups_ps_obj_t));
990  }
991
992  free(temp);
993
994  return (0);
995}
996
997
998/*
999 * 'scan_ps()' - Scan a string for the next PS object.
1000 */
1001
1002static _cups_ps_obj_t	*		/* O  - New object or NULL on EOF */
1003scan_ps(_cups_ps_stack_t *st,		/* I  - Stack */
1004        char             **ptr)		/* IO - String pointer */
1005{
1006  _cups_ps_obj_t	obj;		/* Current object */
1007  char			*start,		/* Start of object */
1008			*cur,		/* Current position */
1009			*valptr,	/* Pointer into value string */
1010			*valend;	/* End of value string */
1011  int			parens;		/* Parenthesis nesting level */
1012
1013
1014 /*
1015  * Skip leading whitespace...
1016  */
1017
1018  for (cur = *ptr; *cur; cur ++)
1019  {
1020    if (*cur == '%')
1021    {
1022     /*
1023      * Comment, skip to end of line...
1024      */
1025
1026      for (cur ++; *cur && *cur != '\n' && *cur != '\r'; cur ++);
1027
1028      if (!*cur)
1029        cur --;
1030    }
1031    else if (!isspace(*cur & 255))
1032      break;
1033  }
1034
1035  if (!*cur)
1036  {
1037    *ptr = NULL;
1038
1039    return (NULL);
1040  }
1041
1042 /*
1043  * See what we have...
1044  */
1045
1046  memset(&obj, 0, sizeof(obj));
1047
1048  switch (*cur)
1049  {
1050    case '(' :				/* (string) */
1051        obj.type = CUPS_PS_STRING;
1052	start    = cur;
1053
1054	for (cur ++, parens = 1, valptr = obj.value.string,
1055	         valend = obj.value.string + sizeof(obj.value.string) - 1;
1056             *cur;
1057	     cur ++)
1058	{
1059	  if (*cur == ')' && parens == 1)
1060	    break;
1061
1062          if (*cur == '(')
1063	    parens ++;
1064	  else if (*cur == ')')
1065	    parens --;
1066
1067          if (valptr >= valend)
1068	  {
1069	    *ptr = start;
1070
1071	    return (NULL);
1072	  }
1073
1074	  if (*cur == '\\')
1075	  {
1076	   /*
1077	    * Decode escaped character...
1078	    */
1079
1080	    cur ++;
1081
1082            if (*cur == 'b')
1083	      *valptr++ = '\b';
1084	    else if (*cur == 'f')
1085	      *valptr++ = '\f';
1086	    else if (*cur == 'n')
1087	      *valptr++ = '\n';
1088	    else if (*cur == 'r')
1089	      *valptr++ = '\r';
1090	    else if (*cur == 't')
1091	      *valptr++ = '\t';
1092	    else if (*cur >= '0' && *cur <= '7')
1093	    {
1094	      int ch = *cur - '0';
1095
1096              if (cur[1] >= '0' && cur[1] <= '7')
1097	      {
1098	        cur ++;
1099		ch = (ch << 3) + *cur - '0';
1100	      }
1101
1102              if (cur[1] >= '0' && cur[1] <= '7')
1103	      {
1104	        cur ++;
1105		ch = (ch << 3) + *cur - '0';
1106	      }
1107
1108	      *valptr++ = ch;
1109	    }
1110	    else if (*cur == '\r')
1111	    {
1112	      if (cur[1] == '\n')
1113	        cur ++;
1114	    }
1115	    else if (*cur != '\n')
1116	      *valptr++ = *cur;
1117	  }
1118	  else
1119	    *valptr++ = *cur;
1120	}
1121
1122	if (*cur != ')')
1123	{
1124	  *ptr = start;
1125
1126	  return (NULL);
1127	}
1128
1129	cur ++;
1130        break;
1131
1132    case '[' :				/* Start array */
1133        obj.type = CUPS_PS_START_ARRAY;
1134	cur ++;
1135        break;
1136
1137    case ']' :				/* End array */
1138        obj.type = CUPS_PS_END_ARRAY;
1139	cur ++;
1140        break;
1141
1142    case '<' :				/* Start dictionary or hex string */
1143        if (cur[1] == '<')
1144	{
1145	  obj.type = CUPS_PS_START_DICT;
1146	  cur += 2;
1147	}
1148	else
1149	{
1150          obj.type = CUPS_PS_STRING;
1151	  start    = cur;
1152
1153	  for (cur ++, valptr = obj.value.string,
1154	           valend = obj.value.string + sizeof(obj.value.string) - 1;
1155               *cur;
1156	       cur ++)
1157	  {
1158	    int	ch;			/* Current character */
1159
1160
1161
1162            if (*cur == '>')
1163	      break;
1164	    else if (valptr >= valend || !isxdigit(*cur & 255))
1165	    {
1166	      *ptr = start;
1167	      return (NULL);
1168	    }
1169
1170            if (*cur >= '0' && *cur <= '9')
1171	      ch = (*cur - '0') << 4;
1172	    else
1173	      ch = (tolower(*cur) - 'a' + 10) << 4;
1174
1175	    if (isxdigit(cur[1] & 255))
1176	    {
1177	      cur ++;
1178
1179              if (*cur >= '0' && *cur <= '9')
1180		ch |= *cur - '0';
1181	      else
1182		ch |= tolower(*cur) - 'a' + 10;
1183            }
1184
1185	    *valptr++ = ch;
1186          }
1187
1188          if (*cur != '>')
1189	  {
1190	    *ptr = start;
1191	    return (NULL);
1192	  }
1193
1194	  cur ++;
1195	}
1196        break;
1197
1198    case '>' :				/* End dictionary? */
1199        if (cur[1] == '>')
1200	{
1201	  obj.type = CUPS_PS_END_DICT;
1202	  cur += 2;
1203	}
1204	else
1205	{
1206	  obj.type           = CUPS_PS_OTHER;
1207	  obj.value.other[0] = *cur;
1208
1209	  cur ++;
1210	}
1211        break;
1212
1213    case '{' :				/* Start procedure */
1214        obj.type = CUPS_PS_START_PROC;
1215	cur ++;
1216        break;
1217
1218    case '}' :				/* End procedure */
1219        obj.type = CUPS_PS_END_PROC;
1220	cur ++;
1221        break;
1222
1223    case '-' :				/* Possible number */
1224    case '+' :
1225        if (!isdigit(cur[1] & 255) && cur[1] != '.')
1226	{
1227	  obj.type           = CUPS_PS_OTHER;
1228	  obj.value.other[0] = *cur;
1229
1230	  cur ++;
1231	  break;
1232	}
1233
1234    case '0' :				/* Number */
1235    case '1' :
1236    case '2' :
1237    case '3' :
1238    case '4' :
1239    case '5' :
1240    case '6' :
1241    case '7' :
1242    case '8' :
1243    case '9' :
1244    case '.' :
1245        obj.type = CUPS_PS_NUMBER;
1246
1247        start = cur;
1248	for (cur ++; *cur; cur ++)
1249	  if (!isdigit(*cur & 255))
1250	    break;
1251
1252        if (*cur == '#')
1253	{
1254	 /*
1255	  * Integer with radix...
1256	  */
1257
1258          obj.value.number = strtol(cur + 1, &cur, atoi(start));
1259	  break;
1260	}
1261	else if (strchr(".Ee()<>[]{}/%", *cur) || isspace(*cur & 255))
1262	{
1263	 /*
1264	  * Integer or real number...
1265	  */
1266
1267	  obj.value.number = _cupsStrScand(start, &cur, localeconv());
1268          break;
1269	}
1270	else
1271	  cur = start;
1272
1273    default :				/* Operator/variable name */
1274        start = cur;
1275
1276	if (*cur == '/')
1277	{
1278	  obj.type = CUPS_PS_NAME;
1279          valptr   = obj.value.name;
1280          valend   = obj.value.name + sizeof(obj.value.name) - 1;
1281	  cur ++;
1282	}
1283	else
1284	{
1285	  obj.type = CUPS_PS_OTHER;
1286          valptr   = obj.value.other;
1287          valend   = obj.value.other + sizeof(obj.value.other) - 1;
1288	}
1289
1290	while (*cur)
1291	{
1292	  if (strchr("()<>[]{}/%", *cur) || isspace(*cur & 255))
1293	    break;
1294	  else if (valptr < valend)
1295	    *valptr++ = *cur++;
1296	  else
1297	  {
1298	    *ptr = start;
1299	    return (NULL);
1300	  }
1301	}
1302
1303        if (obj.type == CUPS_PS_OTHER)
1304	{
1305          if (!strcmp(obj.value.other, "true"))
1306	  {
1307	    obj.type          = CUPS_PS_BOOLEAN;
1308	    obj.value.boolean = 1;
1309	  }
1310	  else if (!strcmp(obj.value.other, "false"))
1311	  {
1312	    obj.type          = CUPS_PS_BOOLEAN;
1313	    obj.value.boolean = 0;
1314	  }
1315	  else if (!strcmp(obj.value.other, "null"))
1316	    obj.type = CUPS_PS_NULL;
1317	  else if (!strcmp(obj.value.other, "cleartomark"))
1318	    obj.type = CUPS_PS_CLEARTOMARK;
1319	  else if (!strcmp(obj.value.other, "copy"))
1320	    obj.type = CUPS_PS_COPY;
1321	  else if (!strcmp(obj.value.other, "dup"))
1322	    obj.type = CUPS_PS_DUP;
1323	  else if (!strcmp(obj.value.other, "index"))
1324	    obj.type = CUPS_PS_INDEX;
1325	  else if (!strcmp(obj.value.other, "pop"))
1326	    obj.type = CUPS_PS_POP;
1327	  else if (!strcmp(obj.value.other, "roll"))
1328	    obj.type = CUPS_PS_ROLL;
1329	  else if (!strcmp(obj.value.other, "setpagedevice"))
1330	    obj.type = CUPS_PS_SETPAGEDEVICE;
1331	  else if (!strcmp(obj.value.other, "stopped"))
1332	    obj.type = CUPS_PS_STOPPED;
1333	}
1334	break;
1335  }
1336
1337 /*
1338  * Save the current position in the string and return the new object...
1339  */
1340
1341  *ptr = cur;
1342
1343  return (push_stack(st, &obj));
1344}
1345
1346
1347/*
1348 * 'setpagedevice()' - Simulate the PostScript setpagedevice operator.
1349 */
1350
1351static int				/* O - 0 on success, -1 on error */
1352setpagedevice(
1353    _cups_ps_stack_t    *st,		/* I - Stack */
1354    cups_page_header2_t *h,		/* O - Page header */
1355    int                 *preferred_bits)/* O - Preferred bits per color */
1356{
1357  int			i;		/* Index into array */
1358  _cups_ps_obj_t	*obj,		/* Current object */
1359			*end;		/* End of dictionary */
1360  const char		*name;		/* Attribute name */
1361
1362
1363 /*
1364  * Make sure we have a dictionary on the stack...
1365  */
1366
1367  if (st->num_objs == 0)
1368    return (-1);
1369
1370  obj = end = st->objs + st->num_objs - 1;
1371
1372  if (obj->type != CUPS_PS_END_DICT)
1373    return (-1);
1374
1375  obj --;
1376
1377  while (obj > st->objs)
1378  {
1379    if (obj->type == CUPS_PS_START_DICT)
1380      break;
1381
1382    obj --;
1383  }
1384
1385  if (obj < st->objs)
1386    return (-1);
1387
1388 /*
1389  * Found the start of the dictionary, empty the stack to this point...
1390  */
1391
1392  st->num_objs = (int)(obj - st->objs);
1393
1394 /*
1395  * Now pull /name and value pairs from the dictionary...
1396  */
1397
1398  DEBUG_puts("setpagedevice: Dictionary:");
1399
1400  for (obj ++; obj < end; obj ++)
1401  {
1402   /*
1403    * Grab the name...
1404    */
1405
1406    if (obj->type != CUPS_PS_NAME)
1407      return (-1);
1408
1409    name = obj->value.name;
1410    obj ++;
1411
1412#ifdef DEBUG
1413    DEBUG_printf(("setpagedevice: /%s ", name));
1414    DEBUG_object(obj);
1415#endif /* DEBUG */
1416
1417   /*
1418    * Then grab the value...
1419    */
1420
1421    if (!strcmp(name, "MediaClass") && obj->type == CUPS_PS_STRING)
1422      strlcpy(h->MediaClass, obj->value.string, sizeof(h->MediaClass));
1423    else if (!strcmp(name, "MediaColor") && obj->type == CUPS_PS_STRING)
1424      strlcpy(h->MediaColor, obj->value.string, sizeof(h->MediaColor));
1425    else if (!strcmp(name, "MediaType") && obj->type == CUPS_PS_STRING)
1426      strlcpy(h->MediaType, obj->value.string, sizeof(h->MediaType));
1427    else if (!strcmp(name, "OutputType") && obj->type == CUPS_PS_STRING)
1428      strlcpy(h->OutputType, obj->value.string, sizeof(h->OutputType));
1429    else if (!strcmp(name, "AdvanceDistance") && obj->type == CUPS_PS_NUMBER)
1430      h->AdvanceDistance = (unsigned)obj->value.number;
1431    else if (!strcmp(name, "AdvanceMedia") && obj->type == CUPS_PS_NUMBER)
1432      h->AdvanceMedia = (unsigned)obj->value.number;
1433    else if (!strcmp(name, "Collate") && obj->type == CUPS_PS_BOOLEAN)
1434      h->Collate = (unsigned)obj->value.boolean;
1435    else if (!strcmp(name, "CutMedia") && obj->type == CUPS_PS_NUMBER)
1436      h->CutMedia = (cups_cut_t)(unsigned)obj->value.number;
1437    else if (!strcmp(name, "Duplex") && obj->type == CUPS_PS_BOOLEAN)
1438      h->Duplex = (unsigned)obj->value.boolean;
1439    else if (!strcmp(name, "HWResolution") && obj->type == CUPS_PS_START_ARRAY)
1440    {
1441      if (obj[1].type == CUPS_PS_NUMBER && obj[2].type == CUPS_PS_NUMBER &&
1442          obj[3].type == CUPS_PS_END_ARRAY)
1443      {
1444        h->HWResolution[0] = (unsigned)obj[1].value.number;
1445	h->HWResolution[1] = (unsigned)obj[2].value.number;
1446	obj += 3;
1447      }
1448      else
1449        return (-1);
1450    }
1451    else if (!strcmp(name, "InsertSheet") && obj->type == CUPS_PS_BOOLEAN)
1452      h->InsertSheet = (unsigned)obj->value.boolean;
1453    else if (!strcmp(name, "Jog") && obj->type == CUPS_PS_NUMBER)
1454      h->Jog = (unsigned)obj->value.number;
1455    else if (!strcmp(name, "LeadingEdge") && obj->type == CUPS_PS_NUMBER)
1456      h->LeadingEdge = (unsigned)obj->value.number;
1457    else if (!strcmp(name, "ManualFeed") && obj->type == CUPS_PS_BOOLEAN)
1458      h->ManualFeed = (unsigned)obj->value.boolean;
1459    else if ((!strcmp(name, "cupsMediaPosition") ||
1460              !strcmp(name, "MediaPosition")) && obj->type == CUPS_PS_NUMBER)
1461    {
1462     /*
1463      * cupsMediaPosition is supported for backwards compatibility only.
1464      * We added it back in the Ghostscript 5.50 days to work around a
1465      * bug in Ghostscript WRT handling of MediaPosition and setpagedevice.
1466      *
1467      * All new development should set MediaPosition...
1468      */
1469
1470      h->MediaPosition = (unsigned)obj->value.number;
1471    }
1472    else if (!strcmp(name, "MediaWeight") && obj->type == CUPS_PS_NUMBER)
1473      h->MediaWeight = (unsigned)obj->value.number;
1474    else if (!strcmp(name, "MirrorPrint") && obj->type == CUPS_PS_BOOLEAN)
1475      h->MirrorPrint = (unsigned)obj->value.boolean;
1476    else if (!strcmp(name, "NegativePrint") && obj->type == CUPS_PS_BOOLEAN)
1477      h->NegativePrint = (unsigned)obj->value.boolean;
1478    else if (!strcmp(name, "NumCopies") && obj->type == CUPS_PS_NUMBER)
1479      h->NumCopies = (unsigned)obj->value.number;
1480    else if (!strcmp(name, "Orientation") && obj->type == CUPS_PS_NUMBER)
1481      h->Orientation = (unsigned)obj->value.number;
1482    else if (!strcmp(name, "OutputFaceUp") && obj->type == CUPS_PS_BOOLEAN)
1483      h->OutputFaceUp = (unsigned)obj->value.boolean;
1484    else if (!strcmp(name, "PageSize") && obj->type == CUPS_PS_START_ARRAY)
1485    {
1486      if (obj[1].type == CUPS_PS_NUMBER && obj[2].type == CUPS_PS_NUMBER &&
1487          obj[3].type == CUPS_PS_END_ARRAY)
1488      {
1489        h->cupsPageSize[0] = (float)obj[1].value.number;
1490	h->cupsPageSize[1] = (float)obj[2].value.number;
1491
1492        h->PageSize[0] = (unsigned)obj[1].value.number;
1493	h->PageSize[1] = (unsigned)obj[2].value.number;
1494
1495	obj += 3;
1496      }
1497      else
1498        return (-1);
1499    }
1500    else if (!strcmp(name, "Separations") && obj->type == CUPS_PS_BOOLEAN)
1501      h->Separations = (unsigned)obj->value.boolean;
1502    else if (!strcmp(name, "TraySwitch") && obj->type == CUPS_PS_BOOLEAN)
1503      h->TraySwitch = (unsigned)obj->value.boolean;
1504    else if (!strcmp(name, "Tumble") && obj->type == CUPS_PS_BOOLEAN)
1505      h->Tumble = (unsigned)obj->value.boolean;
1506    else if (!strcmp(name, "cupsMediaType") && obj->type == CUPS_PS_NUMBER)
1507      h->cupsMediaType = (unsigned)obj->value.number;
1508    else if (!strcmp(name, "cupsBitsPerColor") && obj->type == CUPS_PS_NUMBER)
1509      h->cupsBitsPerColor = (unsigned)obj->value.number;
1510    else if (!strcmp(name, "cupsPreferredBitsPerColor") &&
1511             obj->type == CUPS_PS_NUMBER)
1512      *preferred_bits = (int)obj->value.number;
1513    else if (!strcmp(name, "cupsColorOrder") && obj->type == CUPS_PS_NUMBER)
1514      h->cupsColorOrder = (cups_order_t)(unsigned)obj->value.number;
1515    else if (!strcmp(name, "cupsColorSpace") && obj->type == CUPS_PS_NUMBER)
1516      h->cupsColorSpace = (cups_cspace_t)(unsigned)obj->value.number;
1517    else if (!strcmp(name, "cupsCompression") && obj->type == CUPS_PS_NUMBER)
1518      h->cupsCompression = (unsigned)obj->value.number;
1519    else if (!strcmp(name, "cupsRowCount") && obj->type == CUPS_PS_NUMBER)
1520      h->cupsRowCount = (unsigned)obj->value.number;
1521    else if (!strcmp(name, "cupsRowFeed") && obj->type == CUPS_PS_NUMBER)
1522      h->cupsRowFeed = (unsigned)obj->value.number;
1523    else if (!strcmp(name, "cupsRowStep") && obj->type == CUPS_PS_NUMBER)
1524      h->cupsRowStep = (unsigned)obj->value.number;
1525    else if (!strcmp(name, "cupsBorderlessScalingFactor") &&
1526             obj->type == CUPS_PS_NUMBER)
1527      h->cupsBorderlessScalingFactor = (float)obj->value.number;
1528    else if (!strncmp(name, "cupsInteger", 11) && obj->type == CUPS_PS_NUMBER)
1529    {
1530      if ((i = atoi(name + 11)) < 0 || i > 15)
1531        return (-1);
1532
1533      h->cupsInteger[i] = (unsigned)obj->value.number;
1534    }
1535    else if (!strncmp(name, "cupsReal", 8) && obj->type == CUPS_PS_NUMBER)
1536    {
1537      if ((i = atoi(name + 8)) < 0 || i > 15)
1538        return (-1);
1539
1540      h->cupsReal[i] = (float)obj->value.number;
1541    }
1542    else if (!strncmp(name, "cupsString", 10) && obj->type == CUPS_PS_STRING)
1543    {
1544      if ((i = atoi(name + 10)) < 0 || i > 15)
1545        return (-1);
1546
1547      strlcpy(h->cupsString[i], obj->value.string, sizeof(h->cupsString[i]));
1548    }
1549    else if (!strcmp(name, "cupsMarkerType") && obj->type == CUPS_PS_STRING)
1550      strlcpy(h->cupsMarkerType, obj->value.string, sizeof(h->cupsMarkerType));
1551    else if (!strcmp(name, "cupsPageSizeName") && obj->type == CUPS_PS_STRING)
1552      strlcpy(h->cupsPageSizeName, obj->value.string,
1553              sizeof(h->cupsPageSizeName));
1554    else if (!strcmp(name, "cupsRenderingIntent") &&
1555             obj->type == CUPS_PS_STRING)
1556      strlcpy(h->cupsRenderingIntent, obj->value.string,
1557              sizeof(h->cupsRenderingIntent));
1558    else
1559    {
1560     /*
1561      * Ignore unknown name+value...
1562      */
1563
1564      DEBUG_printf(("    Unknown name (\"%s\") or value...\n", name));
1565
1566      while (obj[1].type != CUPS_PS_NAME && obj < end)
1567        obj ++;
1568    }
1569  }
1570
1571  return (0);
1572}
1573
1574
1575#ifdef DEBUG
1576/*
1577 * 'DEBUG_object()' - Print an object's value...
1578 */
1579
1580static void
1581DEBUG_object(_cups_ps_obj_t *obj)	/* I - Object to print */
1582{
1583  switch (obj->type)
1584  {
1585    case CUPS_PS_NAME :
1586	DEBUG_printf(("/%s\n", obj->value.name));
1587	break;
1588
1589    case CUPS_PS_NUMBER :
1590	DEBUG_printf(("%g\n", obj->value.number));
1591	break;
1592
1593    case CUPS_PS_STRING :
1594	DEBUG_printf(("(%s)\n", obj->value.string));
1595	break;
1596
1597    case CUPS_PS_BOOLEAN :
1598	if (obj->value.boolean)
1599	  DEBUG_puts("true");
1600	else
1601	  DEBUG_puts("false");
1602	break;
1603
1604    case CUPS_PS_NULL :
1605	DEBUG_puts("null");
1606	break;
1607
1608    case CUPS_PS_START_ARRAY :
1609	DEBUG_puts("[");
1610	break;
1611
1612    case CUPS_PS_END_ARRAY :
1613	DEBUG_puts("]");
1614	break;
1615
1616    case CUPS_PS_START_DICT :
1617	DEBUG_puts("<<");
1618	break;
1619
1620    case CUPS_PS_END_DICT :
1621	DEBUG_puts(">>");
1622	break;
1623
1624    case CUPS_PS_START_PROC :
1625	DEBUG_puts("{");
1626	break;
1627
1628    case CUPS_PS_END_PROC :
1629	DEBUG_puts("}");
1630	break;
1631
1632    case CUPS_PS_CLEARTOMARK :
1633	DEBUG_puts("--cleartomark--");
1634        break;
1635
1636    case CUPS_PS_COPY :
1637	DEBUG_puts("--copy--");
1638        break;
1639
1640    case CUPS_PS_DUP :
1641	DEBUG_puts("--dup--");
1642        break;
1643
1644    case CUPS_PS_INDEX :
1645	DEBUG_puts("--index--");
1646        break;
1647
1648    case CUPS_PS_POP :
1649	DEBUG_puts("--pop--");
1650        break;
1651
1652    case CUPS_PS_ROLL :
1653	DEBUG_puts("--roll--");
1654        break;
1655
1656    case CUPS_PS_SETPAGEDEVICE :
1657	DEBUG_puts("--setpagedevice--");
1658        break;
1659
1660    case CUPS_PS_STOPPED :
1661	DEBUG_puts("--stopped--");
1662        break;
1663
1664    case CUPS_PS_OTHER :
1665	DEBUG_printf(("--%s--\n", obj->value.other));
1666	break;
1667  }
1668}
1669
1670
1671/*
1672 * 'DEBUG_stack()' - Print a stack...
1673 */
1674
1675static void
1676DEBUG_stack(_cups_ps_stack_t *st)	/* I - Stack */
1677{
1678  int			c;		/* Looping var */
1679  _cups_ps_obj_t	*obj;		/* Current object on stack */
1680
1681
1682  for (obj = st->objs, c = st->num_objs; c > 0; c --, obj ++)
1683    DEBUG_object(obj);
1684}
1685#endif /* DEBUG */
1686
1687
1688/*
1689 * End of "$Id: interpret.c 11693 2014-03-11 01:24:45Z msweet $".
1690 */
1691