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