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