You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1722 lines
40 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/*
* PPD command interpreter for CUPS.
*
* Copyright © 2007-2018 by Apple Inc.
* Copyright © 1993-2007 by Easy Software Products.
*
* Licensed under Apache License v2.0. See the file "LICENSE" for more
* information.
*/
/*
* Include necessary headers...
*/
#include <cups/raster-private.h>
#include <cups/ppd-private.h>
#include "debug-internal.h"
/*
* Stack values for the PostScript mini-interpreter...
*/
typedef enum
{
CUPS_PS_NAME,
CUPS_PS_NUMBER,
CUPS_PS_STRING,
CUPS_PS_BOOLEAN,
CUPS_PS_NULL,
CUPS_PS_START_ARRAY,
CUPS_PS_END_ARRAY,
CUPS_PS_START_DICT,
CUPS_PS_END_DICT,
CUPS_PS_START_PROC,
CUPS_PS_END_PROC,
CUPS_PS_CLEARTOMARK,
CUPS_PS_COPY,
CUPS_PS_DUP,
CUPS_PS_INDEX,
CUPS_PS_POP,
CUPS_PS_ROLL,
CUPS_PS_SETPAGEDEVICE,
CUPS_PS_STOPPED,
CUPS_PS_OTHER
} _cups_ps_type_t;
typedef struct
{
_cups_ps_type_t type; /* Object type */
union
{
int boolean; /* Boolean value */
char name[64]; /* Name value */
double number; /* Number value */
char other[64]; /* Other operator */
char string[64]; /* Sring value */
} value; /* Value */
} _cups_ps_obj_t;
typedef struct
{
int num_objs, /* Number of objects on stack */
alloc_objs; /* Number of allocated objects */
_cups_ps_obj_t *objs; /* Objects in stack */
} _cups_ps_stack_t;
/*
* Local functions...
*/
static int cleartomark_stack(_cups_ps_stack_t *st);
static int copy_stack(_cups_ps_stack_t *st, int count);
static void delete_stack(_cups_ps_stack_t *st);
static void error_object(_cups_ps_obj_t *obj);
static void error_stack(_cups_ps_stack_t *st, const char *title);
static _cups_ps_obj_t *index_stack(_cups_ps_stack_t *st, int n);
static _cups_ps_stack_t *new_stack(void);
static _cups_ps_obj_t *pop_stack(_cups_ps_stack_t *st);
static _cups_ps_obj_t *push_stack(_cups_ps_stack_t *st,
_cups_ps_obj_t *obj);
static int roll_stack(_cups_ps_stack_t *st, int c, int s);
static _cups_ps_obj_t *scan_ps(_cups_ps_stack_t *st, char **ptr);
static int setpagedevice(_cups_ps_stack_t *st,
cups_page_header2_t *h,
int *preferred_bits);
#ifdef DEBUG
static void DEBUG_object(const char *prefix, _cups_ps_obj_t *obj);
static void DEBUG_stack(const char *prefix, _cups_ps_stack_t *st);
#endif /* DEBUG */
/*
* '_cupsRasterInterpretPPD()' - Interpret PPD commands to create a page header.
*
* This function is used by raster image processing (RIP) filters like
* cgpdftoraster and imagetoraster when writing CUPS raster data for a page.
* It is not used by raster printer driver filters which only read CUPS
* raster data.
*
*
* @code cupsRasterInterpretPPD@ does not mark the options in the PPD using
* the "num_options" and "options" arguments. Instead, mark the options with
* @code cupsMarkOptions@ and @code ppdMarkOption@ prior to calling it -
* this allows for per-page options without manipulating the options array.
*
* The "func" argument specifies an optional callback function that is
* called prior to the computation of the final raster data. The function
* can make changes to the @link cups_page_header2_t@ data as needed to use a
* supported raster format and then returns 0 on success and -1 if the
* requested attributes cannot be supported.
*
*
* @code cupsRasterInterpretPPD@ supports a subset of the PostScript language.
* Currently only the @code [@, @code ]@, @code <<@, @code >>@, @code {@,
* @code }@, @code cleartomark@, @code copy@, @code dup@, @code index@,
* @code pop@, @code roll@, @code setpagedevice@, and @code stopped@ operators
* are supported.
*
* @since CUPS 1.2/macOS 10.5@
*/
int /* O - 0 on success, -1 on failure */
_cupsRasterInterpretPPD(
cups_page_header2_t *h, /* O - Page header to create */
ppd_file_t *ppd, /* I - PPD file */
int num_options, /* I - Number of options */
cups_option_t *options, /* I - Options */
cups_interpret_cb_t func) /* I - Optional page header callback (@code NULL@ for none) */
{
int status; /* Cummulative status */
char *code; /* Code to run */
const char *val; /* Option value */
ppd_size_t *size; /* Current size */
float left, /* Left position */
bottom, /* Bottom position */
right, /* Right position */
top, /* Top position */
temp1, temp2; /* Temporary variables for swapping */
int preferred_bits; /* Preferred bits per color */
/*
* Range check input...
*/
_cupsRasterClearError();
if (!h)
{
_cupsRasterAddError("Page header cannot be NULL!\n");
return (-1);
}
/*
* Reset the page header to the defaults...
*/
memset(h, 0, sizeof(cups_page_header2_t));
h->NumCopies = 1;
h->PageSize[0] = 612;
h->PageSize[1] = 792;
h->HWResolution[0] = 100;
h->HWResolution[1] = 100;
h->cupsBitsPerColor = 1;
h->cupsColorOrder = CUPS_ORDER_CHUNKED;
h->cupsColorSpace = CUPS_CSPACE_K;
h->cupsBorderlessScalingFactor = 1.0f;
h->cupsPageSize[0] = 612.0f;
h->cupsPageSize[1] = 792.0f;
h->cupsImagingBBox[0] = 0.0f;
h->cupsImagingBBox[1] = 0.0f;
h->cupsImagingBBox[2] = 612.0f;
h->cupsImagingBBox[3] = 792.0f;
strlcpy(h->cupsPageSizeName, "Letter", sizeof(h->cupsPageSizeName));
#ifdef __APPLE__
/*
* cupsInteger0 is also used for the total page count on macOS; set an
* uncommon default value so we can tell if the driver is using cupsInteger0.
*/
h->cupsInteger[0] = 0x80000000;
#endif /* __APPLE__ */
/*
* Apply patches and options to the page header...
*/
status = 0;
preferred_bits = 0;
if (ppd)
{
/*
* Apply any patch code (used to override the defaults...)
*/
if (ppd->patches)
status |= _cupsRasterExecPS(h, &preferred_bits, ppd->patches);
/*
* Then apply printer options in the proper order...
*/
if ((code = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, 0.0)) != NULL)
{
status |= _cupsRasterExecPS(h, &preferred_bits, code);
free(code);
}
if ((code = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL)
{
status |= _cupsRasterExecPS(h, &preferred_bits, code);
free(code);
}
if ((code = ppdEmitString(ppd, PPD_ORDER_PROLOG, 0.0)) != NULL)
{
status |= _cupsRasterExecPS(h, &preferred_bits, code);
free(code);
}
if ((code = ppdEmitString(ppd, PPD_ORDER_PAGE, 0.0)) != NULL)
{
status |= _cupsRasterExecPS(h, &preferred_bits, code);
free(code);
}
}
/*
* Allow option override for page scaling...
*/
if ((val = cupsGetOption("cupsBorderlessScalingFactor", num_options,
options)) != NULL)
{
double sc = atof(val); /* Scale factor */
if (sc >= 0.1 && sc <= 2.0)
h->cupsBorderlessScalingFactor = (float)sc;
}
/*
* Get the margins for the current size...
*/
if ((size = ppdPageSize(ppd, NULL)) != NULL)
{
/*
* Use the margins from the PPD file...
*/
left = size->left;
bottom = size->bottom;
right = size->right;
top = size->top;
strlcpy(h->cupsPageSizeName, size->name, sizeof(h->cupsPageSizeName));
h->cupsPageSize[0] = size->width;
h->cupsPageSize[1] = size->length;
}
else
{
/*
* Use the default margins...
*/
left = 0.0f;
bottom = 0.0f;
right = 612.0f;
top = 792.0f;
}
/*
* Handle orientation...
*/
switch (h->Orientation)
{
case CUPS_ORIENT_0 :
default :
/* Do nothing */
break;
case CUPS_ORIENT_90 :
temp1 = h->cupsPageSize[0];
h->cupsPageSize[0] = h->cupsPageSize[1];
h->cupsPageSize[1] = temp1;
temp1 = left;
temp2 = right;
left = h->cupsPageSize[0] - top;
right = h->cupsPageSize[0] - bottom;
bottom = h->cupsPageSize[1] - temp1;
top = h->cupsPageSize[1] - temp2;
break;
case CUPS_ORIENT_180 :
temp1 = left;
temp2 = bottom;
left = h->cupsPageSize[0] - right;
right = h->cupsPageSize[0] - temp1;
bottom = h->cupsPageSize[1] - top;
top = h->cupsPageSize[1] - temp2;
break;
case CUPS_ORIENT_270 :
temp1 = h->cupsPageSize[0];
h->cupsPageSize[0] = h->cupsPageSize[1];
h->cupsPageSize[1] = temp1;
temp1 = left;
temp2 = right;
left = bottom;
right = top;
bottom = h->cupsPageSize[1] - temp2;
top = h->cupsPageSize[1] - temp1;
break;
}
if (left > right)
{
temp1 = left;
left = right;
right = temp1;
}
if (bottom > top)
{
temp1 = bottom;
bottom = top;
top = temp1;
}
h->PageSize[0] = (unsigned)(h->cupsPageSize[0] *
h->cupsBorderlessScalingFactor);
h->PageSize[1] = (unsigned)(h->cupsPageSize[1] *
h->cupsBorderlessScalingFactor);
h->Margins[0] = (unsigned)(left *
h->cupsBorderlessScalingFactor);
h->Margins[1] = (unsigned)(bottom *
h->cupsBorderlessScalingFactor);
h->ImagingBoundingBox[0] = (unsigned)(left *
h->cupsBorderlessScalingFactor);
h->ImagingBoundingBox[1] = (unsigned)(bottom *
h->cupsBorderlessScalingFactor);
h->ImagingBoundingBox[2] = (unsigned)(right *
h->cupsBorderlessScalingFactor);
h->ImagingBoundingBox[3] = (unsigned)(top *
h->cupsBorderlessScalingFactor);
h->cupsImagingBBox[0] = (float)left;
h->cupsImagingBBox[1] = (float)bottom;
h->cupsImagingBBox[2] = (float)right;
h->cupsImagingBBox[3] = (float)top;
/*
* Use the callback to validate the page header...
*/
if (func && (*func)(h, preferred_bits))
{
_cupsRasterAddError("Page header callback returned error.\n");
return (-1);
}
/*
* Check parameters...
*/
if (!h->HWResolution[0] || !h->HWResolution[1] ||
!h->PageSize[0] || !h->PageSize[1] ||
(h->cupsBitsPerColor != 1 && h->cupsBitsPerColor != 2 &&
h->cupsBitsPerColor != 4 && h->cupsBitsPerColor != 8 &&
h->cupsBitsPerColor != 16) ||
h->cupsBorderlessScalingFactor < 0.1 ||
h->cupsBorderlessScalingFactor > 2.0)
{
_cupsRasterAddError("Page header uses unsupported values.\n");
return (-1);
}
/*
* Compute the bitmap parameters...
*/
h->cupsWidth = (unsigned)((right - left) * h->cupsBorderlessScalingFactor *
h->HWResolution[0] / 72.0f + 0.5f);
h->cupsHeight = (unsigned)((top - bottom) * h->cupsBorderlessScalingFactor *
h->HWResolution[1] / 72.0f + 0.5f);
switch (h->cupsColorSpace)
{
case CUPS_CSPACE_W :
case CUPS_CSPACE_K :
case CUPS_CSPACE_WHITE :
case CUPS_CSPACE_GOLD :
case CUPS_CSPACE_SILVER :
case CUPS_CSPACE_SW :
h->cupsNumColors = 1;
h->cupsBitsPerPixel = h->cupsBitsPerColor;
break;
default :
/*
* Ensure that colorimetric colorspaces use at least 8 bits per
* component...
*/
if (h->cupsColorSpace >= CUPS_CSPACE_CIEXYZ &&
h->cupsBitsPerColor < 8)
h->cupsBitsPerColor = 8;
/*
* Figure out the number of bits per pixel...
*/
if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
{
if (h->cupsBitsPerColor >= 8)
h->cupsBitsPerPixel = h->cupsBitsPerColor * 3;
else
h->cupsBitsPerPixel = h->cupsBitsPerColor * 4;
}
else
h->cupsBitsPerPixel = h->cupsBitsPerColor;
h->cupsNumColors = 3;
break;
case CUPS_CSPACE_KCMYcm :
if (h->cupsBitsPerColor == 1)
{
if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
h->cupsBitsPerPixel = 8;
else
h->cupsBitsPerPixel = 1;
h->cupsNumColors = 6;
break;
}
/*
* Fall through to CMYK code...
*/
case CUPS_CSPACE_RGBA :
case CUPS_CSPACE_RGBW :
case CUPS_CSPACE_CMYK :
case CUPS_CSPACE_YMCK :
case CUPS_CSPACE_KCMY :
case CUPS_CSPACE_GMCK :
case CUPS_CSPACE_GMCS :
if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
h->cupsBitsPerPixel = h->cupsBitsPerColor * 4;
else
h->cupsBitsPerPixel = h->cupsBitsPerColor;
h->cupsNumColors = 4;
break;
case CUPS_CSPACE_DEVICE1 :
case CUPS_CSPACE_DEVICE2 :
case CUPS_CSPACE_DEVICE3 :
case CUPS_CSPACE_DEVICE4 :
case CUPS_CSPACE_DEVICE5 :
case CUPS_CSPACE_DEVICE6 :
case CUPS_CSPACE_DEVICE7 :
case CUPS_CSPACE_DEVICE8 :
case CUPS_CSPACE_DEVICE9 :
case CUPS_CSPACE_DEVICEA :
case CUPS_CSPACE_DEVICEB :
case CUPS_CSPACE_DEVICEC :
case CUPS_CSPACE_DEVICED :
case CUPS_CSPACE_DEVICEE :
case CUPS_CSPACE_DEVICEF :
h->cupsNumColors = h->cupsColorSpace - CUPS_CSPACE_DEVICE1 + 1;
if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
h->cupsBitsPerPixel = h->cupsBitsPerColor * h->cupsNumColors;
else
h->cupsBitsPerPixel = h->cupsBitsPerColor;
break;
}
h->cupsBytesPerLine = (h->cupsBitsPerPixel * h->cupsWidth + 7) / 8;
if (h->cupsColorOrder == CUPS_ORDER_BANDED)
h->cupsBytesPerLine *= h->cupsNumColors;
return (status);
}
/*
* '_cupsRasterExecPS()' - Execute PostScript code to initialize a page header.
*/
int /* O - 0 on success, -1 on error */
_cupsRasterExecPS(
cups_page_header2_t *h, /* O - Page header */
int *preferred_bits,/* O - Preferred bits per color */
const char *code) /* I - PS code to execute */
{
int error = 0; /* Error condition? */
_cups_ps_stack_t *st; /* PostScript value stack */
_cups_ps_obj_t *obj; /* Object from top of stack */
char *codecopy, /* Copy of code */
*codeptr; /* Pointer into copy of code */
DEBUG_printf(("_cupsRasterExecPS(h=%p, preferred_bits=%p, code=\"%s\")\n",
h, preferred_bits, code));
/*
* Copy the PostScript code and create a stack...
*/
if ((codecopy = strdup(code)) == NULL)
{
_cupsRasterAddError("Unable to duplicate code string.\n");
return (-1);
}
if ((st = new_stack()) == NULL)
{
_cupsRasterAddError("Unable to create stack.\n");
free(codecopy);
return (-1);
}
/*
* Parse the PS string until we run out of data...
*/
codeptr = codecopy;
while ((obj = scan_ps(st, &codeptr)) != NULL)
{
#ifdef DEBUG
DEBUG_printf(("_cupsRasterExecPS: Stack (%d objects)", st->num_objs));
DEBUG_object("_cupsRasterExecPS", obj);
#endif /* DEBUG */
switch (obj->type)
{
default :
/* Do nothing for regular values */
break;
case CUPS_PS_CLEARTOMARK :
pop_stack(st);
if (cleartomark_stack(st))
_cupsRasterAddError("cleartomark: Stack underflow.\n");
#ifdef DEBUG
DEBUG_puts("1_cupsRasterExecPS: dup");
DEBUG_stack("_cupsRasterExecPS", st);
#endif /* DEBUG */
break;
case CUPS_PS_COPY :
pop_stack(st);
if ((obj = pop_stack(st)) != NULL)
{
copy_stack(st, (int)obj->value.number);
#ifdef DEBUG
DEBUG_puts("_cupsRasterExecPS: copy");
DEBUG_stack("_cupsRasterExecPS", st);
#endif /* DEBUG */
}
break;
case CUPS_PS_DUP :
pop_stack(st);
copy_stack(st, 1);
#ifdef DEBUG
DEBUG_puts("_cupsRasterExecPS: dup");
DEBUG_stack("_cupsRasterExecPS", st);
#endif /* DEBUG */
break;
case CUPS_PS_INDEX :
pop_stack(st);
if ((obj = pop_stack(st)) != NULL)
{
index_stack(st, (int)obj->value.number);
#ifdef DEBUG
DEBUG_puts("_cupsRasterExecPS: index");
DEBUG_stack("_cupsRasterExecPS", st);
#endif /* DEBUG */
}
break;
case CUPS_PS_POP :
pop_stack(st);
pop_stack(st);
#ifdef DEBUG
DEBUG_puts("_cupsRasterExecPS: pop");
DEBUG_stack("_cupsRasterExecPS", st);
#endif /* DEBUG */
break;
case CUPS_PS_ROLL :
pop_stack(st);
if ((obj = pop_stack(st)) != NULL)
{
int c; /* Count */
c = (int)obj->value.number;
if ((obj = pop_stack(st)) != NULL)
{
roll_stack(st, (int)obj->value.number, c);
#ifdef DEBUG
DEBUG_puts("_cupsRasterExecPS: roll");
DEBUG_stack("_cupsRasterExecPS", st);
#endif /* DEBUG */
}
}
break;
case CUPS_PS_SETPAGEDEVICE :
pop_stack(st);
setpagedevice(st, h, preferred_bits);
#ifdef DEBUG
DEBUG_puts("_cupsRasterExecPS: setpagedevice");
DEBUG_stack("_cupsRasterExecPS", st);
#endif /* DEBUG */
break;
case CUPS_PS_START_PROC :
case CUPS_PS_END_PROC :
case CUPS_PS_STOPPED :
pop_stack(st);
break;
case CUPS_PS_OTHER :
_cupsRasterAddError("Unknown operator \"%s\".\n", obj->value.other);
error = 1;
DEBUG_printf(("_cupsRasterExecPS: Unknown operator \"%s\".", obj->value.other));
break;
}
if (error)
break;
}
/*
* Cleanup...
*/
free(codecopy);
if (st->num_objs > 0)
{
error_stack(st, "Stack not empty:");
#ifdef DEBUG
DEBUG_puts("_cupsRasterExecPS: Stack not empty");
DEBUG_stack("_cupsRasterExecPS", st);
#endif /* DEBUG */
delete_stack(st);
return (-1);
}
delete_stack(st);
/*
* Return success...
*/
return (0);
}
/*
* 'cleartomark_stack()' - Clear to the last mark ([) on the stack.
*/
static int /* O - 0 on success, -1 on error */
cleartomark_stack(_cups_ps_stack_t *st) /* I - Stack */
{
_cups_ps_obj_t *obj; /* Current object on stack */
while ((obj = pop_stack(st)) != NULL)
if (obj->type == CUPS_PS_START_ARRAY)
break;
return (obj ? 0 : -1);
}
/*
* 'copy_stack()' - Copy the top N stack objects.
*/
static int /* O - 0 on success, -1 on error */
copy_stack(_cups_ps_stack_t *st, /* I - Stack */
int c) /* I - Number of objects to copy */
{
int n; /* Index */
if (c < 0)
return (-1);
else if (c == 0)
return (0);
if ((n = st->num_objs - c) < 0)
return (-1);
while (c > 0)
{
if (!push_stack(st, st->objs + n))
return (-1);
n ++;
c --;
}
return (0);
}
/*
* 'delete_stack()' - Free memory used by a stack.
*/
static void
delete_stack(_cups_ps_stack_t *st) /* I - Stack */
{
free(st->objs);
free(st);
}
/*
* 'error_object()' - Add an object's value to the current error message.
*/
static void
error_object(_cups_ps_obj_t *obj) /* I - Object to add */
{
switch (obj->type)
{
case CUPS_PS_NAME :
_cupsRasterAddError(" /%s", obj->value.name);
break;
case CUPS_PS_NUMBER :
_cupsRasterAddError(" %g", obj->value.number);
break;
case CUPS_PS_STRING :
_cupsRasterAddError(" (%s)", obj->value.string);
break;
case CUPS_PS_BOOLEAN :
if (obj->value.boolean)
_cupsRasterAddError(" true");
else
_cupsRasterAddError(" false");
break;
case CUPS_PS_NULL :
_cupsRasterAddError(" null");
break;
case CUPS_PS_START_ARRAY :
_cupsRasterAddError(" [");
break;
case CUPS_PS_END_ARRAY :
_cupsRasterAddError(" ]");
break;
case CUPS_PS_START_DICT :
_cupsRasterAddError(" <<");
break;
case CUPS_PS_END_DICT :
_cupsRasterAddError(" >>");
break;
case CUPS_PS_START_PROC :
_cupsRasterAddError(" {");
break;
case CUPS_PS_END_PROC :
_cupsRasterAddError(" }");
break;
case CUPS_PS_COPY :
_cupsRasterAddError(" --copy--");
break;
case CUPS_PS_CLEARTOMARK :
_cupsRasterAddError(" --cleartomark--");
break;
case CUPS_PS_DUP :
_cupsRasterAddError(" --dup--");
break;
case CUPS_PS_INDEX :
_cupsRasterAddError(" --index--");
break;
case CUPS_PS_POP :
_cupsRasterAddError(" --pop--");
break;
case CUPS_PS_ROLL :
_cupsRasterAddError(" --roll--");
break;
case CUPS_PS_SETPAGEDEVICE :
_cupsRasterAddError(" --setpagedevice--");
break;
case CUPS_PS_STOPPED :
_cupsRasterAddError(" --stopped--");
break;
case CUPS_PS_OTHER :
_cupsRasterAddError(" --%s--", obj->value.other);
break;
}
}
/*
* 'error_stack()' - Add a stack to the current error message...
*/
static void
error_stack(_cups_ps_stack_t *st, /* I - Stack */
const char *title) /* I - Title string */
{
int c; /* Looping var */
_cups_ps_obj_t *obj; /* Current object on stack */
_cupsRasterAddError("%s", title);
for (obj = st->objs, c = st->num_objs; c > 0; c --, obj ++)
error_object(obj);
_cupsRasterAddError("\n");
}
/*
* 'index_stack()' - Copy the Nth value on the stack.
*/
static _cups_ps_obj_t * /* O - New object */
index_stack(_cups_ps_stack_t *st, /* I - Stack */
int n) /* I - Object index */
{
if (n < 0 || (n = st->num_objs - n - 1) < 0)
return (NULL);
return (push_stack(st, st->objs + n));
}
/*
* 'new_stack()' - Create a new stack.
*/
static _cups_ps_stack_t * /* O - New stack */
new_stack(void)
{
_cups_ps_stack_t *st; /* New stack */
if ((st = calloc(1, sizeof(_cups_ps_stack_t))) == NULL)
return (NULL);
st->alloc_objs = 32;
if ((st->objs = calloc(32, sizeof(_cups_ps_obj_t))) == NULL)
{
free(st);
return (NULL);
}
else
return (st);
}
/*
* 'pop_stock()' - Pop the top object off the stack.
*/
static _cups_ps_obj_t * /* O - Object */
pop_stack(_cups_ps_stack_t *st) /* I - Stack */
{
if (st->num_objs > 0)
{
st->num_objs --;
return (st->objs + st->num_objs);
}
else
return (NULL);
}
/*
* 'push_stack()' - Push an object on the stack.
*/
static _cups_ps_obj_t * /* O - New object */
push_stack(_cups_ps_stack_t *st, /* I - Stack */
_cups_ps_obj_t *obj) /* I - Object */
{
_cups_ps_obj_t *temp; /* New object */
if (st->num_objs >= st->alloc_objs)
{
st->alloc_objs += 32;
if ((temp = realloc(st->objs, (size_t)st->alloc_objs *
sizeof(_cups_ps_obj_t))) == NULL)
return (NULL);
st->objs = temp;
memset(temp + st->num_objs, 0, 32 * sizeof(_cups_ps_obj_t));
}
temp = st->objs + st->num_objs;
st->num_objs ++;
memcpy(temp, obj, sizeof(_cups_ps_obj_t));
return (temp);
}
/*
* 'roll_stack()' - Rotate stack objects.
*/
static int /* O - 0 on success, -1 on error */
roll_stack(_cups_ps_stack_t *st, /* I - Stack */
int c, /* I - Number of objects */
int s) /* I - Amount to shift */
{
_cups_ps_obj_t *temp; /* Temporary array of objects */
int n; /* Index into array */
DEBUG_printf(("3roll_stack(st=%p, s=%d, c=%d)", st, s, c));
/*
* Range check input...
*/
if (c < 0)
return (-1);
else if (c == 0)
return (0);
if ((n = st->num_objs - c) < 0)
return (-1);
s %= c;
if (s == 0)
return (0);
/*
* Copy N objects and move things around...
*/
if (s < 0)
{
/*
* Shift down...
*/
s = -s;
if ((temp = calloc((size_t)s, sizeof(_cups_ps_obj_t))) == NULL)
return (-1);
memcpy(temp, st->objs + n, (size_t)s * sizeof(_cups_ps_obj_t));
memmove(st->objs + n, st->objs + n + s, (size_t)(c - s) * sizeof(_cups_ps_obj_t));
memcpy(st->objs + n + c - s, temp, (size_t)s * sizeof(_cups_ps_obj_t));
}
else
{
/*
* Shift up...
*/
if ((temp = calloc((size_t)s, sizeof(_cups_ps_obj_t))) == NULL)
return (-1);
memcpy(temp, st->objs + n + c - s, (size_t)s * sizeof(_cups_ps_obj_t));
memmove(st->objs + n + s, st->objs + n, (size_t)(c - s) * sizeof(_cups_ps_obj_t));
memcpy(st->objs + n, temp, (size_t)s * sizeof(_cups_ps_obj_t));
}
free(temp);
return (0);
}
/*
* 'scan_ps()' - Scan a string for the next PS object.
*/
static _cups_ps_obj_t * /* O - New object or NULL on EOF */
scan_ps(_cups_ps_stack_t *st, /* I - Stack */
char **ptr) /* IO - String pointer */
{
_cups_ps_obj_t obj; /* Current object */
char *start, /* Start of object */
*cur, /* Current position */
*valptr, /* Pointer into value string */
*valend; /* End of value string */
int parens; /* Parenthesis nesting level */
/*
* Skip leading whitespace...
*/
for (cur = *ptr; *cur; cur ++)
{
if (*cur == '%')
{
/*
* Comment, skip to end of line...
*/
for (cur ++; *cur && *cur != '\n' && *cur != '\r'; cur ++);
if (!*cur)
cur --;
}
else if (!isspace(*cur & 255))
break;
}
if (!*cur)
{
*ptr = NULL;
return (NULL);
}
/*
* See what we have...
*/
memset(&obj, 0, sizeof(obj));
switch (*cur)
{
case '(' : /* (string) */
obj.type = CUPS_PS_STRING;
start = cur;
for (cur ++, parens = 1, valptr = obj.value.string,
valend = obj.value.string + sizeof(obj.value.string) - 1;
*cur;
cur ++)
{
if (*cur == ')' && parens == 1)
break;
if (*cur == '(')
parens ++;
else if (*cur == ')')
parens --;
if (valptr >= valend)
{
*ptr = start;
return (NULL);
}
if (*cur == '\\')
{
/*
* Decode escaped character...
*/
cur ++;
if (*cur == 'b')
*valptr++ = '\b';
else if (*cur == 'f')
*valptr++ = '\f';
else if (*cur == 'n')
*valptr++ = '\n';
else if (*cur == 'r')
*valptr++ = '\r';
else if (*cur == 't')
*valptr++ = '\t';
else if (*cur >= '0' && *cur <= '7')
{
int ch = *cur - '0';
if (cur[1] >= '0' && cur[1] <= '7')
{
cur ++;
ch = (ch << 3) + *cur - '0';
}
if (cur[1] >= '0' && cur[1] <= '7')
{
cur ++;
ch = (ch << 3) + *cur - '0';
}
*valptr++ = (char)ch;
}
else if (*cur == '\r')
{
if (cur[1] == '\n')
cur ++;
}
else if (*cur != '\n')
*valptr++ = *cur;
}
else
*valptr++ = *cur;
}
if (*cur != ')')
{
*ptr = start;
return (NULL);
}
cur ++;
break;
case '[' : /* Start array */
obj.type = CUPS_PS_START_ARRAY;
cur ++;
break;
case ']' : /* End array */
obj.type = CUPS_PS_END_ARRAY;
cur ++;
break;
case '<' : /* Start dictionary or hex string */
if (cur[1] == '<')
{
obj.type = CUPS_PS_START_DICT;
cur += 2;
}
else
{
obj.type = CUPS_PS_STRING;
start = cur;
for (cur ++, valptr = obj.value.string,
valend = obj.value.string + sizeof(obj.value.string) - 1;
*cur;
cur ++)
{
int ch; /* Current character */
if (*cur == '>')
break;
else if (valptr >= valend || !isxdigit(*cur & 255))
{
*ptr = start;
return (NULL);
}
if (*cur >= '0' && *cur <= '9')
ch = (*cur - '0') << 4;
else
ch = (tolower(*cur) - 'a' + 10) << 4;
if (isxdigit(cur[1] & 255))
{
cur ++;
if (*cur >= '0' && *cur <= '9')
ch |= *cur - '0';
else
ch |= tolower(*cur) - 'a' + 10;
}
*valptr++ = (char)ch;
}
if (*cur != '>')
{
*ptr = start;
return (NULL);
}
cur ++;
}
break;
case '>' : /* End dictionary? */
if (cur[1] == '>')
{
obj.type = CUPS_PS_END_DICT;
cur += 2;
}
else
{
obj.type = CUPS_PS_OTHER;
obj.value.other[0] = *cur;
cur ++;
}
break;
case '{' : /* Start procedure */
obj.type = CUPS_PS_START_PROC;
cur ++;
break;
case '}' : /* End procedure */
obj.type = CUPS_PS_END_PROC;
cur ++;
break;
case '-' : /* Possible number */
case '+' :
if (!isdigit(cur[1] & 255) && cur[1] != '.')
{
obj.type = CUPS_PS_OTHER;
obj.value.other[0] = *cur;
cur ++;
break;
}
case '0' : /* Number */
case '1' :
case '2' :
case '3' :
case '4' :
case '5' :
case '6' :
case '7' :
case '8' :
case '9' :
case '.' :
obj.type = CUPS_PS_NUMBER;
start = cur;
for (cur ++; *cur; cur ++)
if (!isdigit(*cur & 255))
break;
if (*cur == '#')
{
/*
* Integer with radix...
*/
obj.value.number = strtol(cur + 1, &cur, atoi(start));
break;
}
else if (strchr(".Ee()<>[]{}/%", *cur) || isspace(*cur & 255))
{
/*
* Integer or real number...
*/
obj.value.number = _cupsStrScand(start, &cur, localeconv());
break;
}
else
cur = start;
default : /* Operator/variable name */
start = cur;
if (*cur == '/')
{
obj.type = CUPS_PS_NAME;
valptr = obj.value.name;
valend = obj.value.name + sizeof(obj.value.name) - 1;
cur ++;
}
else
{
obj.type = CUPS_PS_OTHER;
valptr = obj.value.other;
valend = obj.value.other + sizeof(obj.value.other) - 1;
}
while (*cur)
{
if (strchr("()<>[]{}/%", *cur) || isspace(*cur & 255))
break;
else if (valptr < valend)
*valptr++ = *cur++;
else
{
*ptr = start;
return (NULL);
}
}
if (obj.type == CUPS_PS_OTHER)
{
if (!strcmp(obj.value.other, "true"))
{
obj.type = CUPS_PS_BOOLEAN;
obj.value.boolean = 1;
}
else if (!strcmp(obj.value.other, "false"))
{
obj.type = CUPS_PS_BOOLEAN;
obj.value.boolean = 0;
}
else if (!strcmp(obj.value.other, "null"))
obj.type = CUPS_PS_NULL;
else if (!strcmp(obj.value.other, "cleartomark"))
obj.type = CUPS_PS_CLEARTOMARK;
else if (!strcmp(obj.value.other, "copy"))
obj.type = CUPS_PS_COPY;
else if (!strcmp(obj.value.other, "dup"))
obj.type = CUPS_PS_DUP;
else if (!strcmp(obj.value.other, "index"))
obj.type = CUPS_PS_INDEX;
else if (!strcmp(obj.value.other, "pop"))
obj.type = CUPS_PS_POP;
else if (!strcmp(obj.value.other, "roll"))
obj.type = CUPS_PS_ROLL;
else if (!strcmp(obj.value.other, "setpagedevice"))
obj.type = CUPS_PS_SETPAGEDEVICE;
else if (!strcmp(obj.value.other, "stopped"))
obj.type = CUPS_PS_STOPPED;
}
break;
}
/*
* Save the current position in the string and return the new object...
*/
*ptr = cur;
return (push_stack(st, &obj));
}
/*
* 'setpagedevice()' - Simulate the PostScript setpagedevice operator.
*/
static int /* O - 0 on success, -1 on error */
setpagedevice(
_cups_ps_stack_t *st, /* I - Stack */
cups_page_header2_t *h, /* O - Page header */
int *preferred_bits)/* O - Preferred bits per color */
{
int i; /* Index into array */
_cups_ps_obj_t *obj, /* Current object */
*end; /* End of dictionary */
const char *name; /* Attribute name */
/*
* Make sure we have a dictionary on the stack...
*/
if (st->num_objs == 0)
return (-1);
obj = end = st->objs + st->num_objs - 1;
if (obj->type != CUPS_PS_END_DICT)
return (-1);
obj --;
while (obj > st->objs)
{
if (obj->type == CUPS_PS_START_DICT)
break;
obj --;
}
if (obj < st->objs)
return (-1);
/*
* Found the start of the dictionary, empty the stack to this point...
*/
st->num_objs = (int)(obj - st->objs);
/*
* Now pull /name and value pairs from the dictionary...
*/
DEBUG_puts("3setpagedevice: Dictionary:");
for (obj ++; obj < end; obj ++)
{
/*
* Grab the name...
*/
if (obj->type != CUPS_PS_NAME)
return (-1);
name = obj->value.name;
obj ++;
#ifdef DEBUG
DEBUG_printf(("4setpagedevice: /%s ", name));
DEBUG_object("setpagedevice", obj);
#endif /* DEBUG */
/*
* Then grab the value...
*/
if (!strcmp(name, "MediaClass") && obj->type == CUPS_PS_STRING)
strlcpy(h->MediaClass, obj->value.string, sizeof(h->MediaClass));
else if (!strcmp(name, "MediaColor") && obj->type == CUPS_PS_STRING)
strlcpy(h->MediaColor, obj->value.string, sizeof(h->MediaColor));
else if (!strcmp(name, "MediaType") && obj->type == CUPS_PS_STRING)
strlcpy(h->MediaType, obj->value.string, sizeof(h->MediaType));
else if (!strcmp(name, "OutputType") && obj->type == CUPS_PS_STRING)
strlcpy(h->OutputType, obj->value.string, sizeof(h->OutputType));
else if (!strcmp(name, "AdvanceDistance") && obj->type == CUPS_PS_NUMBER)
h->AdvanceDistance = (unsigned)obj->value.number;
else if (!strcmp(name, "AdvanceMedia") && obj->type == CUPS_PS_NUMBER)
h->AdvanceMedia = (unsigned)obj->value.number;
else if (!strcmp(name, "Collate") && obj->type == CUPS_PS_BOOLEAN)
h->Collate = (unsigned)obj->value.boolean;
else if (!strcmp(name, "CutMedia") && obj->type == CUPS_PS_NUMBER)
h->CutMedia = (cups_cut_t)(unsigned)obj->value.number;
else if (!strcmp(name, "Duplex") && obj->type == CUPS_PS_BOOLEAN)
h->Duplex = (unsigned)obj->value.boolean;
else if (!strcmp(name, "HWResolution") && obj->type == CUPS_PS_START_ARRAY)
{
if (obj[1].type == CUPS_PS_NUMBER && obj[2].type == CUPS_PS_NUMBER &&
obj[3].type == CUPS_PS_END_ARRAY)
{
h->HWResolution[0] = (unsigned)obj[1].value.number;
h->HWResolution[1] = (unsigned)obj[2].value.number;
obj += 3;
}
else
return (-1);
}
else if (!strcmp(name, "InsertSheet") && obj->type == CUPS_PS_BOOLEAN)
h->InsertSheet = (unsigned)obj->value.boolean;
else if (!strcmp(name, "Jog") && obj->type == CUPS_PS_NUMBER)
h->Jog = (unsigned)obj->value.number;
else if (!strcmp(name, "LeadingEdge") && obj->type == CUPS_PS_NUMBER)
h->LeadingEdge = (unsigned)obj->value.number;
else if (!strcmp(name, "ManualFeed") && obj->type == CUPS_PS_BOOLEAN)
h->ManualFeed = (unsigned)obj->value.boolean;
else if ((!strcmp(name, "cupsMediaPosition") ||
!strcmp(name, "MediaPosition")) && obj->type == CUPS_PS_NUMBER)
{
/*
* cupsMediaPosition is supported for backwards compatibility only.
* We added it back in the Ghostscript 5.50 days to work around a
* bug in Ghostscript WRT handling of MediaPosition and setpagedevice.
*
* All new development should set MediaPosition...
*/
h->MediaPosition = (unsigned)obj->value.number;
}
else if (!strcmp(name, "MediaWeight") && obj->type == CUPS_PS_NUMBER)
h->MediaWeight = (unsigned)obj->value.number;
else if (!strcmp(name, "MirrorPrint") && obj->type == CUPS_PS_BOOLEAN)
h->MirrorPrint = (unsigned)obj->value.boolean;
else if (!strcmp(name, "NegativePrint") && obj->type == CUPS_PS_BOOLEAN)
h->NegativePrint = (unsigned)obj->value.boolean;
else if (!strcmp(name, "NumCopies") && obj->type == CUPS_PS_NUMBER)
h->NumCopies = (unsigned)obj->value.number;
else if (!strcmp(name, "Orientation") && obj->type == CUPS_PS_NUMBER)
h->Orientation = (unsigned)obj->value.number;
else if (!strcmp(name, "OutputFaceUp") && obj->type == CUPS_PS_BOOLEAN)
h->OutputFaceUp = (unsigned)obj->value.boolean;
else if (!strcmp(name, "PageSize") && obj->type == CUPS_PS_START_ARRAY)
{
if (obj[1].type == CUPS_PS_NUMBER && obj[2].type == CUPS_PS_NUMBER &&
obj[3].type == CUPS_PS_END_ARRAY)
{
h->cupsPageSize[0] = (float)obj[1].value.number;
h->cupsPageSize[1] = (float)obj[2].value.number;
h->PageSize[0] = (unsigned)obj[1].value.number;
h->PageSize[1] = (unsigned)obj[2].value.number;
obj += 3;
}
else
return (-1);
}
else if (!strcmp(name, "Separations") && obj->type == CUPS_PS_BOOLEAN)
h->Separations = (unsigned)obj->value.boolean;
else if (!strcmp(name, "TraySwitch") && obj->type == CUPS_PS_BOOLEAN)
h->TraySwitch = (unsigned)obj->value.boolean;
else if (!strcmp(name, "Tumble") && obj->type == CUPS_PS_BOOLEAN)
h->Tumble = (unsigned)obj->value.boolean;
else if (!strcmp(name, "cupsMediaType") && obj->type == CUPS_PS_NUMBER)
h->cupsMediaType = (unsigned)obj->value.number;
else if (!strcmp(name, "cupsBitsPerColor") && obj->type == CUPS_PS_NUMBER)
h->cupsBitsPerColor = (unsigned)obj->value.number;
else if (!strcmp(name, "cupsPreferredBitsPerColor") &&
obj->type == CUPS_PS_NUMBER)
*preferred_bits = (int)obj->value.number;
else if (!strcmp(name, "cupsColorOrder") && obj->type == CUPS_PS_NUMBER)
h->cupsColorOrder = (cups_order_t)(unsigned)obj->value.number;
else if (!strcmp(name, "cupsColorSpace") && obj->type == CUPS_PS_NUMBER)
h->cupsColorSpace = (cups_cspace_t)(unsigned)obj->value.number;
else if (!strcmp(name, "cupsCompression") && obj->type == CUPS_PS_NUMBER)
h->cupsCompression = (unsigned)obj->value.number;
else if (!strcmp(name, "cupsRowCount") && obj->type == CUPS_PS_NUMBER)
h->cupsRowCount = (unsigned)obj->value.number;
else if (!strcmp(name, "cupsRowFeed") && obj->type == CUPS_PS_NUMBER)
h->cupsRowFeed = (unsigned)obj->value.number;
else if (!strcmp(name, "cupsRowStep") && obj->type == CUPS_PS_NUMBER)
h->cupsRowStep = (unsigned)obj->value.number;
else if (!strcmp(name, "cupsBorderlessScalingFactor") &&
obj->type == CUPS_PS_NUMBER)
h->cupsBorderlessScalingFactor = (float)obj->value.number;
else if (!strncmp(name, "cupsInteger", 11) && obj->type == CUPS_PS_NUMBER)
{
if ((i = atoi(name + 11)) < 0 || i > 15)
return (-1);
h->cupsInteger[i] = (unsigned)obj->value.number;
}
else if (!strncmp(name, "cupsReal", 8) && obj->type == CUPS_PS_NUMBER)
{
if ((i = atoi(name + 8)) < 0 || i > 15)
return (-1);
h->cupsReal[i] = (float)obj->value.number;
}
else if (!strncmp(name, "cupsString", 10) && obj->type == CUPS_PS_STRING)
{
if ((i = atoi(name + 10)) < 0 || i > 15)
return (-1);
strlcpy(h->cupsString[i], obj->value.string, sizeof(h->cupsString[i]));
}
else if (!strcmp(name, "cupsMarkerType") && obj->type == CUPS_PS_STRING)
strlcpy(h->cupsMarkerType, obj->value.string, sizeof(h->cupsMarkerType));
else if (!strcmp(name, "cupsPageSizeName") && obj->type == CUPS_PS_STRING)
strlcpy(h->cupsPageSizeName, obj->value.string,
sizeof(h->cupsPageSizeName));
else if (!strcmp(name, "cupsRenderingIntent") &&
obj->type == CUPS_PS_STRING)
strlcpy(h->cupsRenderingIntent, obj->value.string,
sizeof(h->cupsRenderingIntent));
else
{
/*
* Ignore unknown name+value...
*/
DEBUG_printf(("4setpagedevice: Unknown name (\"%s\") or value...\n", name));
while (obj[1].type != CUPS_PS_NAME && obj < end)
obj ++;
}
}
return (0);
}
#ifdef DEBUG
/*
* 'DEBUG_object()' - Print an object's value...
*/
static void
DEBUG_object(const char *prefix, /* I - Prefix string */
_cups_ps_obj_t *obj) /* I - Object to print */
{
switch (obj->type)
{
case CUPS_PS_NAME :
DEBUG_printf(("4%s: /%s\n", prefix, obj->value.name));
break;
case CUPS_PS_NUMBER :
DEBUG_printf(("4%s: %g\n", prefix, obj->value.number));
break;
case CUPS_PS_STRING :
DEBUG_printf(("4%s: (%s)\n", prefix, obj->value.string));
break;
case CUPS_PS_BOOLEAN :
if (obj->value.boolean)
DEBUG_printf(("4%s: true", prefix));
else
DEBUG_printf(("4%s: false", prefix));
break;
case CUPS_PS_NULL :
DEBUG_printf(("4%s: null", prefix));
break;
case CUPS_PS_START_ARRAY :
DEBUG_printf(("4%s: [", prefix));
break;
case CUPS_PS_END_ARRAY :
DEBUG_printf(("4%s: ]", prefix));
break;
case CUPS_PS_START_DICT :
DEBUG_printf(("4%s: <<", prefix));
break;
case CUPS_PS_END_DICT :
DEBUG_printf(("4%s: >>", prefix));
break;
case CUPS_PS_START_PROC :
DEBUG_printf(("4%s: {", prefix));
break;
case CUPS_PS_END_PROC :
DEBUG_printf(("4%s: }", prefix));
break;
case CUPS_PS_CLEARTOMARK :
DEBUG_printf(("4%s: --cleartomark--", prefix));
break;
case CUPS_PS_COPY :
DEBUG_printf(("4%s: --copy--", prefix));
break;
case CUPS_PS_DUP :
DEBUG_printf(("4%s: --dup--", prefix));
break;
case CUPS_PS_INDEX :
DEBUG_printf(("4%s: --index--", prefix));
break;
case CUPS_PS_POP :
DEBUG_printf(("4%s: --pop--", prefix));
break;
case CUPS_PS_ROLL :
DEBUG_printf(("4%s: --roll--", prefix));
break;
case CUPS_PS_SETPAGEDEVICE :
DEBUG_printf(("4%s: --setpagedevice--", prefix));
break;
case CUPS_PS_STOPPED :
DEBUG_printf(("4%s: --stopped--", prefix));
break;
case CUPS_PS_OTHER :
DEBUG_printf(("4%s: --%s--", prefix, obj->value.other));
break;
}
}
/*
* 'DEBUG_stack()' - Print a stack...
*/
static void
DEBUG_stack(const char *prefix, /* I - Prefix string */
_cups_ps_stack_t *st) /* I - Stack */
{
int c; /* Looping var */
_cups_ps_obj_t *obj; /* Current object on stack */
for (obj = st->objs, c = st->num_objs; c > 0; c --, obj ++)
DEBUG_object(prefix, obj);
}
#endif /* DEBUG */