/* * 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 #include #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 */