2 * "$Id: interpret.c 11551 2014-01-29 16:31:35Z msweet $"
4 * PPD command interpreter for CUPS.
6 * Copyright 2007-2012 by Apple Inc.
7 * Copyright 1993-2007 by Easy Software Products.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * This file is subject to the Apple OS-Developed Software exception.
19 * cupsRasterInterpretPPD() - Interpret PPD commands to create a page header.
20 * _cupsRasterExecPS() - Execute PostScript code to initialize a page
22 * cleartomark_stack() - Clear to the last mark ([) on the stack.
23 * copy_stack() - Copy the top N stack objects.
24 * delete_stack() - Free memory used by a stack.
25 * error_object() - Add an object's value to the current error
27 * error_stack() - Add a stack to the current error message.
28 * index_stack() - Copy the Nth value on the stack.
29 * new_stack() - Create a new stack.
30 * pop_stock() - Pop the top object off the stack.
31 * push_stack() - Push an object on the stack.
32 * roll_stack() - Rotate stack objects.
33 * scan_ps() - Scan a string for the next PS object.
34 * setpagedevice() - Simulate the PostScript setpagedevice operator.
35 * DEBUG_object() - Print an object value.
36 * DEBUG_stack() - Print a stack.
40 * Include necessary headers...
43 #include <cups/raster-private.h>
47 * Stack values for the PostScript mini-interpreter...
69 CUPS_PS_SETPAGEDEVICE
,
76 _cups_ps_type_t type
; /* Object type */
79 int boolean
; /* Boolean value */
80 char name
[64]; /* Name value */
81 double number
; /* Number value */
82 char other
[64]; /* Other operator */
83 char string
[64]; /* Sring value */
89 int num_objs
, /* Number of objects on stack */
90 alloc_objs
; /* Number of allocated objects */
91 _cups_ps_obj_t
*objs
; /* Objects in stack */
99 static int cleartomark_stack(_cups_ps_stack_t
*st
);
100 static int copy_stack(_cups_ps_stack_t
*st
, int count
);
101 static void delete_stack(_cups_ps_stack_t
*st
);
102 static void error_object(_cups_ps_obj_t
*obj
);
103 static void error_stack(_cups_ps_stack_t
*st
, const char *title
);
104 static _cups_ps_obj_t
*index_stack(_cups_ps_stack_t
*st
, int n
);
105 static _cups_ps_stack_t
*new_stack(void);
106 static _cups_ps_obj_t
*pop_stack(_cups_ps_stack_t
*st
);
107 static _cups_ps_obj_t
*push_stack(_cups_ps_stack_t
*st
,
108 _cups_ps_obj_t
*obj
);
109 static int roll_stack(_cups_ps_stack_t
*st
, int c
, int s
);
110 static _cups_ps_obj_t
*scan_ps(_cups_ps_stack_t
*st
, char **ptr
);
111 static int setpagedevice(_cups_ps_stack_t
*st
,
112 cups_page_header2_t
*h
,
113 int *preferred_bits
);
115 static void DEBUG_object(_cups_ps_obj_t
*obj
);
116 static void DEBUG_stack(_cups_ps_stack_t
*st
);
121 * 'cupsRasterInterpretPPD()' - Interpret PPD commands to create a page header.
123 * This function is used by raster image processing (RIP) filters like
124 * cgpdftoraster and imagetoraster when writing CUPS raster data for a page.
125 * It is not used by raster printer driver filters which only read CUPS
129 * @code cupsRasterInterpretPPD@ does not mark the options in the PPD using
130 * the "num_options" and "options" arguments. Instead, mark the options with
131 * @code cupsMarkOptions@ and @code ppdMarkOption@ prior to calling it -
132 * this allows for per-page options without manipulating the options array.
134 * The "func" argument specifies an optional callback function that is
135 * called prior to the computation of the final raster data. The function
136 * can make changes to the @link cups_page_header2_t@ data as needed to use a
137 * supported raster format and then returns 0 on success and -1 if the
138 * requested attributes cannot be supported.
141 * @code cupsRasterInterpretPPD@ supports a subset of the PostScript language.
142 * Currently only the @code [@, @code ]@, @code <<@, @code >>@, @code {@,
143 * @code }@, @code cleartomark@, @code copy@, @code dup@, @code index@,
144 * @code pop@, @code roll@, @code setpagedevice@, and @code stopped@ operators
147 * @since CUPS 1.2/OS X 10.5@
150 int /* O - 0 on success, -1 on failure */
151 cupsRasterInterpretPPD(
152 cups_page_header2_t
*h
, /* O - Page header to create */
153 ppd_file_t
*ppd
, /* I - PPD file */
154 int num_options
, /* I - Number of options */
155 cups_option_t
*options
, /* I - Options */
156 cups_interpret_cb_t func
) /* I - Optional page header callback (@code NULL@ for none) */
158 int status
; /* Cummulative status */
159 char *code
; /* Code to run */
160 const char *val
; /* Option value */
161 ppd_size_t
*size
; /* Current size */
162 float left
, /* Left position */
163 bottom
, /* Bottom position */
164 right
, /* Right position */
165 top
; /* Top position */
166 int preferred_bits
; /* Preferred bits per color */
170 * Range check input...
173 _cupsRasterClearError();
177 _cupsRasterAddError("Page header cannot be NULL!\n");
182 * Reset the page header to the defaults...
185 memset(h
, 0, sizeof(cups_page_header2_t
));
188 h
->PageSize
[0] = 612;
189 h
->PageSize
[1] = 792;
190 h
->HWResolution
[0] = 100;
191 h
->HWResolution
[1] = 100;
192 h
->cupsBitsPerColor
= 1;
193 h
->cupsColorOrder
= CUPS_ORDER_CHUNKED
;
194 h
->cupsColorSpace
= CUPS_CSPACE_K
;
195 h
->cupsBorderlessScalingFactor
= 1.0f
;
196 h
->cupsPageSize
[0] = 612.0f
;
197 h
->cupsPageSize
[1] = 792.0f
;
198 h
->cupsImagingBBox
[0] = 0.0f
;
199 h
->cupsImagingBBox
[1] = 0.0f
;
200 h
->cupsImagingBBox
[2] = 612.0f
;
201 h
->cupsImagingBBox
[3] = 792.0f
;
203 strlcpy(h
->cupsPageSizeName
, "Letter", sizeof(h
->cupsPageSizeName
));
207 * cupsInteger0 is also used for the total page count on OS X; set an
208 * uncommon default value so we can tell if the driver is using cupsInteger0.
211 h
->cupsInteger
[0] = 0x80000000;
212 #endif /* __APPLE__ */
215 * Apply patches and options to the page header...
224 * Apply any patch code (used to override the defaults...)
228 status
|= _cupsRasterExecPS(h
, &preferred_bits
, ppd
->patches
);
231 * Then apply printer options in the proper order...
234 if ((code
= ppdEmitString(ppd
, PPD_ORDER_DOCUMENT
, 0.0)) != NULL
)
236 status
|= _cupsRasterExecPS(h
, &preferred_bits
, code
);
240 if ((code
= ppdEmitString(ppd
, PPD_ORDER_ANY
, 0.0)) != NULL
)
242 status
|= _cupsRasterExecPS(h
, &preferred_bits
, code
);
246 if ((code
= ppdEmitString(ppd
, PPD_ORDER_PROLOG
, 0.0)) != NULL
)
248 status
|= _cupsRasterExecPS(h
, &preferred_bits
, code
);
252 if ((code
= ppdEmitString(ppd
, PPD_ORDER_PAGE
, 0.0)) != NULL
)
254 status
|= _cupsRasterExecPS(h
, &preferred_bits
, code
);
260 * Allow option override for page scaling...
263 if ((val
= cupsGetOption("cupsBorderlessScalingFactor", num_options
,
266 double sc
= atof(val
); /* Scale factor */
268 if (sc
>= 0.1 && sc
<= 2.0)
269 h
->cupsBorderlessScalingFactor
= (float)sc
;
273 * Get the margins for the current size...
276 if ((size
= ppdPageSize(ppd
, NULL
)) != NULL
)
279 * Use the margins from the PPD file...
283 bottom
= size
->bottom
;
287 strlcpy(h
->cupsPageSizeName
, size
->name
, sizeof(h
->cupsPageSizeName
));
289 h
->cupsPageSize
[0] = size
->width
;
290 h
->cupsPageSize
[1] = size
->length
;
295 * Use the default margins...
304 h
->PageSize
[0] = (unsigned)(h
->cupsPageSize
[0] *
305 h
->cupsBorderlessScalingFactor
);
306 h
->PageSize
[1] = (unsigned)(h
->cupsPageSize
[1] *
307 h
->cupsBorderlessScalingFactor
);
308 h
->Margins
[0] = (unsigned)(left
*
309 h
->cupsBorderlessScalingFactor
);
310 h
->Margins
[1] = (unsigned)(bottom
*
311 h
->cupsBorderlessScalingFactor
);
312 h
->ImagingBoundingBox
[0] = (unsigned)(left
*
313 h
->cupsBorderlessScalingFactor
);
314 h
->ImagingBoundingBox
[1] = (unsigned)(bottom
*
315 h
->cupsBorderlessScalingFactor
);
316 h
->ImagingBoundingBox
[2] = (unsigned)(right
*
317 h
->cupsBorderlessScalingFactor
);
318 h
->ImagingBoundingBox
[3] = (unsigned)(top
*
319 h
->cupsBorderlessScalingFactor
);
320 h
->cupsImagingBBox
[0] = (float)left
;
321 h
->cupsImagingBBox
[1] = (float)bottom
;
322 h
->cupsImagingBBox
[2] = (float)right
;
323 h
->cupsImagingBBox
[3] = (float)top
;
326 * Use the callback to validate the page header...
329 if (func
&& (*func
)(h
, preferred_bits
))
331 _cupsRasterAddError("Page header callback returned error.\n");
336 * Check parameters...
339 if (!h
->HWResolution
[0] || !h
->HWResolution
[1] ||
340 !h
->PageSize
[0] || !h
->PageSize
[1] ||
341 (h
->cupsBitsPerColor
!= 1 && h
->cupsBitsPerColor
!= 2 &&
342 h
->cupsBitsPerColor
!= 4 && h
->cupsBitsPerColor
!= 8 &&
343 h
->cupsBitsPerColor
!= 16) ||
344 h
->cupsBorderlessScalingFactor
< 0.1 ||
345 h
->cupsBorderlessScalingFactor
> 2.0)
347 _cupsRasterAddError("Page header uses unsupported values.\n");
352 * Compute the bitmap parameters...
355 h
->cupsWidth
= (int)((right
- left
) * h
->cupsBorderlessScalingFactor
*
356 h
->HWResolution
[0] / 72.0f
+ 0.5f
);
357 h
->cupsHeight
= (int)((top
- bottom
) * h
->cupsBorderlessScalingFactor
*
358 h
->HWResolution
[1] / 72.0f
+ 0.5f
);
360 switch (h
->cupsColorSpace
)
364 case CUPS_CSPACE_WHITE
:
365 case CUPS_CSPACE_GOLD
:
366 case CUPS_CSPACE_SILVER
:
367 case CUPS_CSPACE_SW
:
368 h
->cupsNumColors
= 1;
369 h
->cupsBitsPerPixel
= h
->cupsBitsPerColor
;
374 * Ensure that colorimetric colorspaces use at least 8 bits per
378 if (h
->cupsColorSpace
>= CUPS_CSPACE_CIEXYZ
&&
379 h
->cupsBitsPerColor
< 8)
380 h
->cupsBitsPerColor
= 8;
383 * Figure out the number of bits per pixel...
386 if (h
->cupsColorOrder
== CUPS_ORDER_CHUNKED
)
388 if (h
->cupsBitsPerColor
>= 8)
389 h
->cupsBitsPerPixel
= h
->cupsBitsPerColor
* 3;
391 h
->cupsBitsPerPixel
= h
->cupsBitsPerColor
* 4;
394 h
->cupsBitsPerPixel
= h
->cupsBitsPerColor
;
396 h
->cupsNumColors
= 3;
399 case CUPS_CSPACE_KCMYcm
:
400 if (h
->cupsBitsPerColor
== 1)
402 if (h
->cupsColorOrder
== CUPS_ORDER_CHUNKED
)
403 h
->cupsBitsPerPixel
= 8;
405 h
->cupsBitsPerPixel
= 1;
407 h
->cupsNumColors
= 6;
412 * Fall through to CMYK code...
415 case CUPS_CSPACE_RGBA
:
416 case CUPS_CSPACE_RGBW
:
417 case CUPS_CSPACE_CMYK
:
418 case CUPS_CSPACE_YMCK
:
419 case CUPS_CSPACE_KCMY
:
420 case CUPS_CSPACE_GMCK
:
421 case CUPS_CSPACE_GMCS
:
422 if (h
->cupsColorOrder
== CUPS_ORDER_CHUNKED
)
423 h
->cupsBitsPerPixel
= h
->cupsBitsPerColor
* 4;
425 h
->cupsBitsPerPixel
= h
->cupsBitsPerColor
;
427 h
->cupsNumColors
= 4;
430 case CUPS_CSPACE_DEVICE1
:
431 case CUPS_CSPACE_DEVICE2
:
432 case CUPS_CSPACE_DEVICE3
:
433 case CUPS_CSPACE_DEVICE4
:
434 case CUPS_CSPACE_DEVICE5
:
435 case CUPS_CSPACE_DEVICE6
:
436 case CUPS_CSPACE_DEVICE7
:
437 case CUPS_CSPACE_DEVICE8
:
438 case CUPS_CSPACE_DEVICE9
:
439 case CUPS_CSPACE_DEVICEA
:
440 case CUPS_CSPACE_DEVICEB
:
441 case CUPS_CSPACE_DEVICEC
:
442 case CUPS_CSPACE_DEVICED
:
443 case CUPS_CSPACE_DEVICEE
:
444 case CUPS_CSPACE_DEVICEF
:
445 h
->cupsNumColors
= h
->cupsColorSpace
- CUPS_CSPACE_DEVICE1
+ 1;
447 if (h
->cupsColorOrder
== CUPS_ORDER_CHUNKED
)
448 h
->cupsBitsPerPixel
= h
->cupsBitsPerColor
* h
->cupsNumColors
;
450 h
->cupsBitsPerPixel
= h
->cupsBitsPerColor
;
454 h
->cupsBytesPerLine
= (h
->cupsBitsPerPixel
* h
->cupsWidth
+ 7) / 8;
456 if (h
->cupsColorOrder
== CUPS_ORDER_BANDED
)
457 h
->cupsBytesPerLine
*= h
->cupsNumColors
;
464 * '_cupsRasterExecPS()' - Execute PostScript code to initialize a page header.
467 int /* O - 0 on success, -1 on error */
469 cups_page_header2_t
*h
, /* O - Page header */
470 int *preferred_bits
,/* O - Preferred bits per color */
471 const char *code
) /* I - PS code to execute */
473 int error
= 0; /* Error condition? */
474 _cups_ps_stack_t
*st
; /* PostScript value stack */
475 _cups_ps_obj_t
*obj
; /* Object from top of stack */
476 char *codecopy
, /* Copy of code */
477 *codeptr
; /* Pointer into copy of code */
480 DEBUG_printf(("_cupsRasterExecPS(h=%p, preferred_bits=%p, code=\"%s\")\n",
481 h
, preferred_bits
, code
));
484 * Copy the PostScript code and create a stack...
487 if ((codecopy
= strdup(code
)) == NULL
)
489 _cupsRasterAddError("Unable to duplicate code string.\n");
493 if ((st
= new_stack()) == NULL
)
495 _cupsRasterAddError("Unable to create stack.\n");
501 * Parse the PS string until we run out of data...
506 while ((obj
= scan_ps(st
, &codeptr
)) != NULL
)
509 DEBUG_printf(("_cupsRasterExecPS: Stack (%d objects)\n", st
->num_objs
));
516 /* Do nothing for regular values */
519 case CUPS_PS_CLEARTOMARK
:
522 if (cleartomark_stack(st
))
523 _cupsRasterAddError("cleartomark: Stack underflow!\n");
526 DEBUG_puts(" dup: ");
533 if ((obj
= pop_stack(st
)) != NULL
)
535 copy_stack(st
, (int)obj
->value
.number
);
538 DEBUG_puts("_cupsRasterExecPS: copy");
549 DEBUG_puts("_cupsRasterExecPS: dup");
556 if ((obj
= pop_stack(st
)) != NULL
)
558 index_stack(st
, (int)obj
->value
.number
);
561 DEBUG_puts("_cupsRasterExecPS: index");
572 DEBUG_puts("_cupsRasterExecPS: pop");
579 if ((obj
= pop_stack(st
)) != NULL
)
584 c
= (int)obj
->value
.number
;
586 if ((obj
= pop_stack(st
)) != NULL
)
588 roll_stack(st
, (int)obj
->value
.number
, c
);
591 DEBUG_puts("_cupsRasterExecPS: roll");
598 case CUPS_PS_SETPAGEDEVICE
:
600 setpagedevice(st
, h
, preferred_bits
);
603 DEBUG_puts("_cupsRasterExecPS: setpagedevice");
608 case CUPS_PS_START_PROC
:
609 case CUPS_PS_END_PROC
:
610 case CUPS_PS_STOPPED
:
615 _cupsRasterAddError("Unknown operator \"%s\"!\n", obj
->value
.other
);
617 DEBUG_printf(("_cupsRasterExecPS: Unknown operator \"%s\"!\n",
632 if (st
->num_objs
> 0)
634 error_stack(st
, "Stack not empty:");
637 DEBUG_puts("_cupsRasterExecPS: Stack not empty:");
657 * 'cleartomark_stack()' - Clear to the last mark ([) on the stack.
660 static int /* O - 0 on success, -1 on error */
661 cleartomark_stack(_cups_ps_stack_t
*st
) /* I - Stack */
663 _cups_ps_obj_t
*obj
; /* Current object on stack */
666 while ((obj
= pop_stack(st
)) != NULL
)
667 if (obj
->type
== CUPS_PS_START_ARRAY
)
670 return (obj
? 0 : -1);
675 * 'copy_stack()' - Copy the top N stack objects.
678 static int /* O - 0 on success, -1 on error */
679 copy_stack(_cups_ps_stack_t
*st
, /* I - Stack */
680 int c
) /* I - Number of objects to copy */
690 if ((n
= st
->num_objs
- c
) < 0)
695 if (!push_stack(st
, st
->objs
+ n
))
707 * 'delete_stack()' - Free memory used by a stack.
711 delete_stack(_cups_ps_stack_t
*st
) /* I - Stack */
719 * 'error_object()' - Add an object's value to the current error message.
723 error_object(_cups_ps_obj_t
*obj
) /* I - Object to add */
728 _cupsRasterAddError(" /%s", obj
->value
.name
);
731 case CUPS_PS_NUMBER
:
732 _cupsRasterAddError(" %g", obj
->value
.number
);
735 case CUPS_PS_STRING
:
736 _cupsRasterAddError(" (%s)", obj
->value
.string
);
739 case CUPS_PS_BOOLEAN
:
740 if (obj
->value
.boolean
)
741 _cupsRasterAddError(" true");
743 _cupsRasterAddError(" false");
747 _cupsRasterAddError(" null");
750 case CUPS_PS_START_ARRAY
:
751 _cupsRasterAddError(" [");
754 case CUPS_PS_END_ARRAY
:
755 _cupsRasterAddError(" ]");
758 case CUPS_PS_START_DICT
:
759 _cupsRasterAddError(" <<");
762 case CUPS_PS_END_DICT
:
763 _cupsRasterAddError(" >>");
766 case CUPS_PS_START_PROC
:
767 _cupsRasterAddError(" {");
770 case CUPS_PS_END_PROC
:
771 _cupsRasterAddError(" }");
775 _cupsRasterAddError(" --copy--");
778 case CUPS_PS_CLEARTOMARK
:
779 _cupsRasterAddError(" --cleartomark--");
783 _cupsRasterAddError(" --dup--");
787 _cupsRasterAddError(" --index--");
791 _cupsRasterAddError(" --pop--");
795 _cupsRasterAddError(" --roll--");
798 case CUPS_PS_SETPAGEDEVICE
:
799 _cupsRasterAddError(" --setpagedevice--");
802 case CUPS_PS_STOPPED
:
803 _cupsRasterAddError(" --stopped--");
807 _cupsRasterAddError(" --%s--", obj
->value
.other
);
814 * 'error_stack()' - Add a stack to the current error message...
818 error_stack(_cups_ps_stack_t
*st
, /* I - Stack */
819 const char *title
) /* I - Title string */
821 int c
; /* Looping var */
822 _cups_ps_obj_t
*obj
; /* Current object on stack */
825 _cupsRasterAddError("%s", title
);
827 for (obj
= st
->objs
, c
= st
->num_objs
; c
> 0; c
--, obj
++)
830 _cupsRasterAddError("\n");
835 * 'index_stack()' - Copy the Nth value on the stack.
838 static _cups_ps_obj_t
* /* O - New object */
839 index_stack(_cups_ps_stack_t
*st
, /* I - Stack */
840 int n
) /* I - Object index */
842 if (n
< 0 || (n
= st
->num_objs
- n
- 1) < 0)
845 return (push_stack(st
, st
->objs
+ n
));
850 * 'new_stack()' - Create a new stack.
853 static _cups_ps_stack_t
* /* O - New stack */
856 _cups_ps_stack_t
*st
; /* New stack */
859 if ((st
= calloc(1, sizeof(_cups_ps_stack_t
))) == NULL
)
864 if ((st
->objs
= calloc(32, sizeof(_cups_ps_obj_t
))) == NULL
)
875 * 'pop_stock()' - Pop the top object off the stack.
878 static _cups_ps_obj_t
* /* O - Object */
879 pop_stack(_cups_ps_stack_t
*st
) /* I - Stack */
881 if (st
->num_objs
> 0)
885 return (st
->objs
+ st
->num_objs
);
893 * 'push_stack()' - Push an object on the stack.
896 static _cups_ps_obj_t
* /* O - New object */
897 push_stack(_cups_ps_stack_t
*st
, /* I - Stack */
898 _cups_ps_obj_t
*obj
) /* I - Object */
900 _cups_ps_obj_t
*temp
; /* New object */
903 if (st
->num_objs
>= st
->alloc_objs
)
907 st
->alloc_objs
+= 32;
909 if ((temp
= realloc(st
->objs
, st
->alloc_objs
*
910 sizeof(_cups_ps_obj_t
))) == NULL
)
914 memset(temp
+ st
->num_objs
, 0, 32 * sizeof(_cups_ps_obj_t
));
917 temp
= st
->objs
+ st
->num_objs
;
920 memcpy(temp
, obj
, sizeof(_cups_ps_obj_t
));
927 * 'roll_stack()' - Rotate stack objects.
930 static int /* O - 0 on success, -1 on error */
931 roll_stack(_cups_ps_stack_t
*st
, /* I - Stack */
932 int c
, /* I - Number of objects */
933 int s
) /* I - Amount to shift */
935 _cups_ps_obj_t
*temp
; /* Temporary array of objects */
936 int n
; /* Index into array */
939 DEBUG_printf((" roll_stack(st=%p, s=%d, c=%d)\n", st
, s
, c
));
942 * Range check input...
950 if ((n
= st
->num_objs
- c
) < 0)
959 * Copy N objects and move things around...
970 if ((temp
= calloc(s
, sizeof(_cups_ps_obj_t
))) == NULL
)
973 memcpy(temp
, st
->objs
+ n
, s
* sizeof(_cups_ps_obj_t
));
974 memmove(st
->objs
+ n
, st
->objs
+ n
+ s
, (c
- s
) * sizeof(_cups_ps_obj_t
));
975 memcpy(st
->objs
+ n
+ c
- s
, temp
, s
* sizeof(_cups_ps_obj_t
));
983 if ((temp
= calloc(s
, sizeof(_cups_ps_obj_t
))) == NULL
)
986 memcpy(temp
, st
->objs
+ n
+ c
- s
, s
* sizeof(_cups_ps_obj_t
));
987 memmove(st
->objs
+ n
+ s
, st
->objs
+ n
,
988 (c
- s
) * sizeof(_cups_ps_obj_t
));
989 memcpy(st
->objs
+ n
, temp
, s
* sizeof(_cups_ps_obj_t
));
999 * 'scan_ps()' - Scan a string for the next PS object.
1002 static _cups_ps_obj_t
* /* O - New object or NULL on EOF */
1003 scan_ps(_cups_ps_stack_t
*st
, /* I - Stack */
1004 char **ptr
) /* IO - String pointer */
1006 _cups_ps_obj_t obj
; /* Current object */
1007 char *start
, /* Start of object */
1008 *cur
, /* Current position */
1009 *valptr
, /* Pointer into value string */
1010 *valend
; /* End of value string */
1011 int parens
; /* Parenthesis nesting level */
1015 * Skip leading whitespace...
1018 for (cur
= *ptr
; *cur
; cur
++)
1023 * Comment, skip to end of line...
1026 for (cur
++; *cur
&& *cur
!= '\n' && *cur
!= '\r'; cur
++);
1031 else if (!isspace(*cur
& 255))
1043 * See what we have...
1046 memset(&obj
, 0, sizeof(obj
));
1050 case '(' : /* (string) */
1051 obj
.type
= CUPS_PS_STRING
;
1054 for (cur
++, parens
= 1, valptr
= obj
.value
.string
,
1055 valend
= obj
.value
.string
+ sizeof(obj
.value
.string
) - 1;
1059 if (*cur
== ')' && parens
== 1)
1064 else if (*cur
== ')')
1067 if (valptr
>= valend
)
1077 * Decode escaped character...
1084 else if (*cur
== 'f')
1086 else if (*cur
== 'n')
1088 else if (*cur
== 'r')
1090 else if (*cur
== 't')
1092 else if (*cur
>= '0' && *cur
<= '7')
1094 int ch
= *cur
- '0';
1096 if (cur
[1] >= '0' && cur
[1] <= '7')
1099 ch
= (ch
<< 3) + *cur
- '0';
1102 if (cur
[1] >= '0' && cur
[1] <= '7')
1105 ch
= (ch
<< 3) + *cur
- '0';
1110 else if (*cur
== '\r')
1115 else if (*cur
!= '\n')
1132 case '[' : /* Start array */
1133 obj
.type
= CUPS_PS_START_ARRAY
;
1137 case ']' : /* End array */
1138 obj
.type
= CUPS_PS_END_ARRAY
;
1142 case '<' : /* Start dictionary or hex string */
1145 obj
.type
= CUPS_PS_START_DICT
;
1150 obj
.type
= CUPS_PS_STRING
;
1153 for (cur
++, valptr
= obj
.value
.string
,
1154 valend
= obj
.value
.string
+ sizeof(obj
.value
.string
) - 1;
1158 int ch
; /* Current character */
1164 else if (valptr
>= valend
|| !isxdigit(*cur
& 255))
1170 if (*cur
>= '0' && *cur
<= '9')
1171 ch
= (*cur
- '0') << 4;
1173 ch
= (tolower(*cur
) - 'a' + 10) << 4;
1175 if (isxdigit(cur
[1] & 255))
1179 if (*cur
>= '0' && *cur
<= '9')
1182 ch
|= tolower(*cur
) - 'a' + 10;
1198 case '>' : /* End dictionary? */
1201 obj
.type
= CUPS_PS_END_DICT
;
1206 obj
.type
= CUPS_PS_OTHER
;
1207 obj
.value
.other
[0] = *cur
;
1213 case '{' : /* Start procedure */
1214 obj
.type
= CUPS_PS_START_PROC
;
1218 case '}' : /* End procedure */
1219 obj
.type
= CUPS_PS_END_PROC
;
1223 case '-' : /* Possible number */
1225 if (!isdigit(cur
[1] & 255) && cur
[1] != '.')
1227 obj
.type
= CUPS_PS_OTHER
;
1228 obj
.value
.other
[0] = *cur
;
1234 case '0' : /* Number */
1245 obj
.type
= CUPS_PS_NUMBER
;
1248 for (cur
++; *cur
; cur
++)
1249 if (!isdigit(*cur
& 255))
1255 * Integer with radix...
1258 obj
.value
.number
= strtol(cur
+ 1, &cur
, atoi(start
));
1261 else if (strchr(".Ee()<>[]{}/%", *cur
) || isspace(*cur
& 255))
1264 * Integer or real number...
1267 obj
.value
.number
= _cupsStrScand(start
, &cur
, localeconv());
1273 default : /* Operator/variable name */
1278 obj
.type
= CUPS_PS_NAME
;
1279 valptr
= obj
.value
.name
;
1280 valend
= obj
.value
.name
+ sizeof(obj
.value
.name
) - 1;
1285 obj
.type
= CUPS_PS_OTHER
;
1286 valptr
= obj
.value
.other
;
1287 valend
= obj
.value
.other
+ sizeof(obj
.value
.other
) - 1;
1292 if (strchr("()<>[]{}/%", *cur
) || isspace(*cur
& 255))
1294 else if (valptr
< valend
)
1303 if (obj
.type
== CUPS_PS_OTHER
)
1305 if (!strcmp(obj
.value
.other
, "true"))
1307 obj
.type
= CUPS_PS_BOOLEAN
;
1308 obj
.value
.boolean
= 1;
1310 else if (!strcmp(obj
.value
.other
, "false"))
1312 obj
.type
= CUPS_PS_BOOLEAN
;
1313 obj
.value
.boolean
= 0;
1315 else if (!strcmp(obj
.value
.other
, "null"))
1316 obj
.type
= CUPS_PS_NULL
;
1317 else if (!strcmp(obj
.value
.other
, "cleartomark"))
1318 obj
.type
= CUPS_PS_CLEARTOMARK
;
1319 else if (!strcmp(obj
.value
.other
, "copy"))
1320 obj
.type
= CUPS_PS_COPY
;
1321 else if (!strcmp(obj
.value
.other
, "dup"))
1322 obj
.type
= CUPS_PS_DUP
;
1323 else if (!strcmp(obj
.value
.other
, "index"))
1324 obj
.type
= CUPS_PS_INDEX
;
1325 else if (!strcmp(obj
.value
.other
, "pop"))
1326 obj
.type
= CUPS_PS_POP
;
1327 else if (!strcmp(obj
.value
.other
, "roll"))
1328 obj
.type
= CUPS_PS_ROLL
;
1329 else if (!strcmp(obj
.value
.other
, "setpagedevice"))
1330 obj
.type
= CUPS_PS_SETPAGEDEVICE
;
1331 else if (!strcmp(obj
.value
.other
, "stopped"))
1332 obj
.type
= CUPS_PS_STOPPED
;
1338 * Save the current position in the string and return the new object...
1343 return (push_stack(st
, &obj
));
1348 * 'setpagedevice()' - Simulate the PostScript setpagedevice operator.
1351 static int /* O - 0 on success, -1 on error */
1353 _cups_ps_stack_t
*st
, /* I - Stack */
1354 cups_page_header2_t
*h
, /* O - Page header */
1355 int *preferred_bits
)/* O - Preferred bits per color */
1357 int i
; /* Index into array */
1358 _cups_ps_obj_t
*obj
, /* Current object */
1359 *end
; /* End of dictionary */
1360 const char *name
; /* Attribute name */
1364 * Make sure we have a dictionary on the stack...
1367 if (st
->num_objs
== 0)
1370 obj
= end
= st
->objs
+ st
->num_objs
- 1;
1372 if (obj
->type
!= CUPS_PS_END_DICT
)
1377 while (obj
> st
->objs
)
1379 if (obj
->type
== CUPS_PS_START_DICT
)
1389 * Found the start of the dictionary, empty the stack to this point...
1392 st
->num_objs
= (int)(obj
- st
->objs
);
1395 * Now pull /name and value pairs from the dictionary...
1398 DEBUG_puts("setpagedevice: Dictionary:");
1400 for (obj
++; obj
< end
; obj
++)
1406 if (obj
->type
!= CUPS_PS_NAME
)
1409 name
= obj
->value
.name
;
1413 DEBUG_printf(("setpagedevice: /%s ", name
));
1418 * Then grab the value...
1421 if (!strcmp(name
, "MediaClass") && obj
->type
== CUPS_PS_STRING
)
1422 strlcpy(h
->MediaClass
, obj
->value
.string
, sizeof(h
->MediaClass
));
1423 else if (!strcmp(name
, "MediaColor") && obj
->type
== CUPS_PS_STRING
)
1424 strlcpy(h
->MediaColor
, obj
->value
.string
, sizeof(h
->MediaColor
));
1425 else if (!strcmp(name
, "MediaType") && obj
->type
== CUPS_PS_STRING
)
1426 strlcpy(h
->MediaType
, obj
->value
.string
, sizeof(h
->MediaType
));
1427 else if (!strcmp(name
, "OutputType") && obj
->type
== CUPS_PS_STRING
)
1428 strlcpy(h
->OutputType
, obj
->value
.string
, sizeof(h
->OutputType
));
1429 else if (!strcmp(name
, "AdvanceDistance") && obj
->type
== CUPS_PS_NUMBER
)
1430 h
->AdvanceDistance
= (unsigned)obj
->value
.number
;
1431 else if (!strcmp(name
, "AdvanceMedia") && obj
->type
== CUPS_PS_NUMBER
)
1432 h
->AdvanceMedia
= (unsigned)obj
->value
.number
;
1433 else if (!strcmp(name
, "Collate") && obj
->type
== CUPS_PS_BOOLEAN
)
1434 h
->Collate
= (unsigned)obj
->value
.boolean
;
1435 else if (!strcmp(name
, "CutMedia") && obj
->type
== CUPS_PS_NUMBER
)
1436 h
->CutMedia
= (cups_cut_t
)(unsigned)obj
->value
.number
;
1437 else if (!strcmp(name
, "Duplex") && obj
->type
== CUPS_PS_BOOLEAN
)
1438 h
->Duplex
= (unsigned)obj
->value
.boolean
;
1439 else if (!strcmp(name
, "HWResolution") && obj
->type
== CUPS_PS_START_ARRAY
)
1441 if (obj
[1].type
== CUPS_PS_NUMBER
&& obj
[2].type
== CUPS_PS_NUMBER
&&
1442 obj
[3].type
== CUPS_PS_END_ARRAY
)
1444 h
->HWResolution
[0] = (unsigned)obj
[1].value
.number
;
1445 h
->HWResolution
[1] = (unsigned)obj
[2].value
.number
;
1451 else if (!strcmp(name
, "InsertSheet") && obj
->type
== CUPS_PS_BOOLEAN
)
1452 h
->InsertSheet
= (unsigned)obj
->value
.boolean
;
1453 else if (!strcmp(name
, "Jog") && obj
->type
== CUPS_PS_NUMBER
)
1454 h
->Jog
= (unsigned)obj
->value
.number
;
1455 else if (!strcmp(name
, "LeadingEdge") && obj
->type
== CUPS_PS_NUMBER
)
1456 h
->LeadingEdge
= (unsigned)obj
->value
.number
;
1457 else if (!strcmp(name
, "ManualFeed") && obj
->type
== CUPS_PS_BOOLEAN
)
1458 h
->ManualFeed
= (unsigned)obj
->value
.boolean
;
1459 else if ((!strcmp(name
, "cupsMediaPosition") ||
1460 !strcmp(name
, "MediaPosition")) && obj
->type
== CUPS_PS_NUMBER
)
1463 * cupsMediaPosition is supported for backwards compatibility only.
1464 * We added it back in the Ghostscript 5.50 days to work around a
1465 * bug in Ghostscript WRT handling of MediaPosition and setpagedevice.
1467 * All new development should set MediaPosition...
1470 h
->MediaPosition
= (unsigned)obj
->value
.number
;
1472 else if (!strcmp(name
, "MediaWeight") && obj
->type
== CUPS_PS_NUMBER
)
1473 h
->MediaWeight
= (unsigned)obj
->value
.number
;
1474 else if (!strcmp(name
, "MirrorPrint") && obj
->type
== CUPS_PS_BOOLEAN
)
1475 h
->MirrorPrint
= (unsigned)obj
->value
.boolean
;
1476 else if (!strcmp(name
, "NegativePrint") && obj
->type
== CUPS_PS_BOOLEAN
)
1477 h
->NegativePrint
= (unsigned)obj
->value
.boolean
;
1478 else if (!strcmp(name
, "NumCopies") && obj
->type
== CUPS_PS_NUMBER
)
1479 h
->NumCopies
= (unsigned)obj
->value
.number
;
1480 else if (!strcmp(name
, "Orientation") && obj
->type
== CUPS_PS_NUMBER
)
1481 h
->Orientation
= (unsigned)obj
->value
.number
;
1482 else if (!strcmp(name
, "OutputFaceUp") && obj
->type
== CUPS_PS_BOOLEAN
)
1483 h
->OutputFaceUp
= (unsigned)obj
->value
.boolean
;
1484 else if (!strcmp(name
, "PageSize") && obj
->type
== CUPS_PS_START_ARRAY
)
1486 if (obj
[1].type
== CUPS_PS_NUMBER
&& obj
[2].type
== CUPS_PS_NUMBER
&&
1487 obj
[3].type
== CUPS_PS_END_ARRAY
)
1489 h
->cupsPageSize
[0] = (float)obj
[1].value
.number
;
1490 h
->cupsPageSize
[1] = (float)obj
[2].value
.number
;
1492 h
->PageSize
[0] = (unsigned)obj
[1].value
.number
;
1493 h
->PageSize
[1] = (unsigned)obj
[2].value
.number
;
1500 else if (!strcmp(name
, "Separations") && obj
->type
== CUPS_PS_BOOLEAN
)
1501 h
->Separations
= (unsigned)obj
->value
.boolean
;
1502 else if (!strcmp(name
, "TraySwitch") && obj
->type
== CUPS_PS_BOOLEAN
)
1503 h
->TraySwitch
= (unsigned)obj
->value
.boolean
;
1504 else if (!strcmp(name
, "Tumble") && obj
->type
== CUPS_PS_BOOLEAN
)
1505 h
->Tumble
= (unsigned)obj
->value
.boolean
;
1506 else if (!strcmp(name
, "cupsMediaType") && obj
->type
== CUPS_PS_NUMBER
)
1507 h
->cupsMediaType
= (unsigned)obj
->value
.number
;
1508 else if (!strcmp(name
, "cupsBitsPerColor") && obj
->type
== CUPS_PS_NUMBER
)
1509 h
->cupsBitsPerColor
= (unsigned)obj
->value
.number
;
1510 else if (!strcmp(name
, "cupsPreferredBitsPerColor") &&
1511 obj
->type
== CUPS_PS_NUMBER
)
1512 *preferred_bits
= (int)obj
->value
.number
;
1513 else if (!strcmp(name
, "cupsColorOrder") && obj
->type
== CUPS_PS_NUMBER
)
1514 h
->cupsColorOrder
= (cups_order_t
)(unsigned)obj
->value
.number
;
1515 else if (!strcmp(name
, "cupsColorSpace") && obj
->type
== CUPS_PS_NUMBER
)
1516 h
->cupsColorSpace
= (cups_cspace_t
)(unsigned)obj
->value
.number
;
1517 else if (!strcmp(name
, "cupsCompression") && obj
->type
== CUPS_PS_NUMBER
)
1518 h
->cupsCompression
= (unsigned)obj
->value
.number
;
1519 else if (!strcmp(name
, "cupsRowCount") && obj
->type
== CUPS_PS_NUMBER
)
1520 h
->cupsRowCount
= (unsigned)obj
->value
.number
;
1521 else if (!strcmp(name
, "cupsRowFeed") && obj
->type
== CUPS_PS_NUMBER
)
1522 h
->cupsRowFeed
= (unsigned)obj
->value
.number
;
1523 else if (!strcmp(name
, "cupsRowStep") && obj
->type
== CUPS_PS_NUMBER
)
1524 h
->cupsRowStep
= (unsigned)obj
->value
.number
;
1525 else if (!strcmp(name
, "cupsBorderlessScalingFactor") &&
1526 obj
->type
== CUPS_PS_NUMBER
)
1527 h
->cupsBorderlessScalingFactor
= (float)obj
->value
.number
;
1528 else if (!strncmp(name
, "cupsInteger", 11) && obj
->type
== CUPS_PS_NUMBER
)
1530 if ((i
= atoi(name
+ 11)) < 0 || i
> 15)
1533 h
->cupsInteger
[i
] = (unsigned)obj
->value
.number
;
1535 else if (!strncmp(name
, "cupsReal", 8) && obj
->type
== CUPS_PS_NUMBER
)
1537 if ((i
= atoi(name
+ 8)) < 0 || i
> 15)
1540 h
->cupsReal
[i
] = (float)obj
->value
.number
;
1542 else if (!strncmp(name
, "cupsString", 10) && obj
->type
== CUPS_PS_STRING
)
1544 if ((i
= atoi(name
+ 10)) < 0 || i
> 15)
1547 strlcpy(h
->cupsString
[i
], obj
->value
.string
, sizeof(h
->cupsString
[i
]));
1549 else if (!strcmp(name
, "cupsMarkerType") && obj
->type
== CUPS_PS_STRING
)
1550 strlcpy(h
->cupsMarkerType
, obj
->value
.string
, sizeof(h
->cupsMarkerType
));
1551 else if (!strcmp(name
, "cupsPageSizeName") && obj
->type
== CUPS_PS_STRING
)
1552 strlcpy(h
->cupsPageSizeName
, obj
->value
.string
,
1553 sizeof(h
->cupsPageSizeName
));
1554 else if (!strcmp(name
, "cupsRenderingIntent") &&
1555 obj
->type
== CUPS_PS_STRING
)
1556 strlcpy(h
->cupsRenderingIntent
, obj
->value
.string
,
1557 sizeof(h
->cupsRenderingIntent
));
1561 * Ignore unknown name+value...
1564 DEBUG_printf((" Unknown name (\"%s\") or value...\n", name
));
1566 while (obj
[1].type
!= CUPS_PS_NAME
&& obj
< end
)
1577 * 'DEBUG_object()' - Print an object's value...
1581 DEBUG_object(_cups_ps_obj_t
*obj
) /* I - Object to print */
1586 DEBUG_printf(("/%s\n", obj
->value
.name
));
1589 case CUPS_PS_NUMBER
:
1590 DEBUG_printf(("%g\n", obj
->value
.number
));
1593 case CUPS_PS_STRING
:
1594 DEBUG_printf(("(%s)\n", obj
->value
.string
));
1597 case CUPS_PS_BOOLEAN
:
1598 if (obj
->value
.boolean
)
1601 DEBUG_puts("false");
1608 case CUPS_PS_START_ARRAY
:
1612 case CUPS_PS_END_ARRAY
:
1616 case CUPS_PS_START_DICT
:
1620 case CUPS_PS_END_DICT
:
1624 case CUPS_PS_START_PROC
:
1628 case CUPS_PS_END_PROC
:
1632 case CUPS_PS_CLEARTOMARK
:
1633 DEBUG_puts("--cleartomark--");
1637 DEBUG_puts("--copy--");
1641 DEBUG_puts("--dup--");
1644 case CUPS_PS_INDEX
:
1645 DEBUG_puts("--index--");
1649 DEBUG_puts("--pop--");
1653 DEBUG_puts("--roll--");
1656 case CUPS_PS_SETPAGEDEVICE
:
1657 DEBUG_puts("--setpagedevice--");
1660 case CUPS_PS_STOPPED
:
1661 DEBUG_puts("--stopped--");
1664 case CUPS_PS_OTHER
:
1665 DEBUG_printf(("--%s--\n", obj
->value
.other
));
1672 * 'DEBUG_stack()' - Print a stack...
1676 DEBUG_stack(_cups_ps_stack_t
*st
) /* I - Stack */
1678 int c
; /* Looping var */
1679 _cups_ps_obj_t
*obj
; /* Current object on stack */
1682 for (obj
= st
->objs
, c
= st
->num_objs
; c
> 0; c
--, obj
++)
1689 * End of "$Id: interpret.c 11551 2014-01-29 16:31:35Z msweet $".