/*
- * "$Id: emit.c 4980 2006-01-25 19:57:45Z mike $"
+ * "$Id: emit.c 6649 2007-07-11 21:46:42Z mike $"
*
* PPD code emission routines for the Common UNIX Printing System (CUPS).
*
- * Copyright 1997-2005 by Easy Software Products, all rights reserved.
+ * Copyright 2007 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
*
* These coded instructions, statements, and computer programs are the
- * property of Easy Software Products and are protected by Federal
- * copyright law. Distribution and use rights are outlined in the file
- * "LICENSE.txt" which should have been included with this file. If this
- * file is missing or damaged please contact Easy Software Products
- * at:
- *
- * Attn: CUPS Licensing Information
- * Easy Software Products
- * 44141 Airport View Drive, Suite 204
- * Hollywood, Maryland 20636 USA
- *
- * Voice: (301) 373-9600
- * EMail: cups-info@cups.org
- * WWW: http://www.cups.org
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
*
* PostScript is a trademark of Adobe Systems, Inc.
*
* ppdCollect2() - Collect all marked options that reside in the
* specified section and minimum order.
* ppdEmit() - Emit code for marked options to a file.
- * ppdEmitAfterOrder() - Emit a subset of the code for marked options to a file.
+ * ppdEmitAfterOrder() - Emit a subset of the code for marked options to a
+ * file.
* ppdEmitFd() - Emit code for marked options to a file.
* ppdEmitJCL() - Emit code for JCL options to a file.
* ppdEmitJCLEnd() - Emit JCLEnd code to a file.
+ * ppdEmitString() - Get a string containing the code for marked options.
* ppd_handle_media() - Handle media selection...
- * ppd_sort() - Sort options by ordering numbers...
*/
/*
#include "ppd.h"
#include <stdlib.h>
#include "string.h"
+#include <errno.h>
+#include "debug.h"
#if defined(WIN32) || defined(__EMX__)
# include <io.h>
*/
static void ppd_handle_media(ppd_file_t *ppd);
-static int ppd_sort(ppd_choice_t **c1, ppd_choice_t **c2);
/*
float min_order, /* I - Minimum OrderDependency value */
ppd_choice_t ***choices) /* O - Pointers to choices */
{
- int i, j, k, m; /* Looping vars */
- ppd_group_t *g, /* Current group */
- *sg; /* Current sub-group */
- ppd_option_t *o; /* Current option */
ppd_choice_t *c; /* Current choice */
+ ppd_section_t csection; /* Current section */
+ float corder; /* Current OrderDependency value */
int count; /* Number of choices collected */
ppd_choice_t **collect; /* Collected choices */
+ float *orders; /* Collected order values */
+ DEBUG_printf(("ppdCollect2(ppd=%p, section=%d, min_order=%f, choices=%p)\n",
+ ppd, section, min_order, choices));
+
if (ppd == NULL)
return (0);
/*
- * Allocate memory for up to 1000 selected choices...
+ * Allocate memory for up to N selected choices...
*/
count = 0;
- collect = calloc(sizeof(ppd_choice_t *), 1000);
+ collect = calloc(sizeof(ppd_choice_t *), cupsArrayCount(ppd->marked));
+ orders = calloc(sizeof(float), cupsArrayCount(ppd->marked));
/*
* Loop through all options and add choices as needed...
*/
- for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
+ for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
+ c;
+ c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
{
- for (j = g->num_options, o = g->options; j > 0; j --, o ++)
- if (o->section == section && o->order >= min_order)
- for (k = o->num_choices, c = o->choices; k > 0; k --, c ++)
- if (c->marked && count < 1000)
- {
- collect[count] = c;
- count ++;
- }
+ csection = c->option->section;
+ corder = c->option->order;
+
+ if (!strcmp(c->choice, "Custom"))
+ {
+ ppd_attr_t *attr; /* NonUIOrderDependency value */
+ float aorder; /* Order value */
+ char asection[17], /* Section name */
+ amain[PPD_MAX_NAME + 1],
+ aoption[PPD_MAX_NAME];
+ /* *CustomFoo and True */
+
+
+ for (attr = ppdFindAttr(ppd, "NonUIOrderDependency", NULL);
+ attr;
+ attr = ppdFindNextAttr(ppd, "NonUIOrderDependency", NULL))
+ if (attr->value &&
+ sscanf(attr->value, "%f%16s%41s%40s", &aorder, asection, amain,
+ aoption) == 4 &&
+ !strncmp(amain, "*Custom", 7) &&
+ !strcmp(amain + 7, c->option->keyword) && !strcmp(aoption, "True"))
+ {
+ /*
+ * Use this NonUIOrderDependency...
+ */
+
+ corder = aorder;
+
+ if (!strcmp(asection, "DocumentSetup"))
+ csection = PPD_ORDER_DOCUMENT;
+ else if (!strcmp(asection, "ExitServer"))
+ csection = PPD_ORDER_EXIT;
+ else if (!strcmp(asection, "JCLSetup"))
+ csection = PPD_ORDER_JCL;
+ else if (!strcmp(asection, "PageSetup"))
+ csection = PPD_ORDER_PAGE;
+ else if (!strcmp(asection, "Prolog"))
+ csection = PPD_ORDER_PROLOG;
+ else
+ csection = PPD_ORDER_ANY;
- for (j = g->num_subgroups, sg = g->subgroups; j > 0; j --, sg ++)
- for (k = sg->num_options, o = sg->options; k > 0; k --, o ++)
- if (o->section == section && o->order >= min_order)
- for (m = o->num_choices, c = o->choices; m > 0; m --, c ++)
- if (c->marked && count < 1000)
- {
- collect[count] = c;
- count ++;
- }
+ break;
+ }
+ }
+
+ if (csection == section && corder >= min_order)
+ {
+ collect[count] = c;
+ orders[count] = corder;
+ count ++;
+ }
}
/*
*/
if (count > 1)
- qsort(collect, count, sizeof(ppd_choice_t *),
- (int (*)(const void *, const void *))ppd_sort);
+ {
+ int i, j; /* Looping vars */
+
+ for (i = 0; i < (count - 1); i ++)
+ for (j = i + 1; j < count; j ++)
+ if (orders[i] > orders[j])
+ {
+ c = collect[i];
+ corder = orders[i];
+ collect[i] = collect[j];
+ orders[i] = orders[j];
+ collect[j] = c;
+ orders[j] = corder;
+ }
+ }
+
+ free(orders);
+
+ DEBUG_printf(("ppdCollect2: %d marked choices...\n", count));
/*
* Return the array and number of choices; if 0, free the array since
ppd_file_t *ppd, /* I - PPD file record */
FILE *fp, /* I - File to write to */
ppd_section_t section, /* I - Section to write */
- int limit, /* I - Non-zero to use min_order, 0 to include all */
- float min_order) /* I - Lowest order dependency to include */
+ int limit, /* I - Non-zero to use min_order */
+ float min_order) /* I - Lowest OrderDependency */
{
- int i, /* Looping var */
- count; /* Number of choices */
- ppd_choice_t **choices; /* Choices */
- ppd_size_t *size; /* Custom page size */
+ char *buffer; /* Option code */
+ int status; /* Return status */
/*
- * Use PageSize or PageRegion as required...
+ * Range check input...
*/
- ppd_handle_media(ppd);
+ if (!ppd || !fp)
+ return (-1);
/*
- * Collect the options we need to emit and emit them!
+ * Get the string...
*/
- if ((count = ppdCollect2(ppd, section, min_order, &choices)) == 0)
- return (0);
-
- for (i = 0; i < count; i ++)
- if (section != PPD_ORDER_EXIT && section != PPD_ORDER_JCL)
- {
- /*
- * Send wrapper commands to prevent printer errors for unsupported
- * options...
- */
-
- if (fputs("[{\n", fp) < 0)
- {
- free(choices);
- return (-1);
- }
-
- /*
- * Send DSC comments with option...
- */
-
- if ((!strcasecmp(choices[i]->option->keyword, "PageSize") ||
- !strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
- !strcasecmp(choices[i]->choice, "Custom"))
- {
- /*
- * Variable size; write out standard size options, using the
- * parameter positions defined in the PPD file...
- */
-
- ppd_attr_t *attr; /* PPD attribute */
- int pos, /* Position of custom value */
- orientation; /* Orientation to use */
- float values[5]; /* Values for custom command */
- int isfloat[5]; /* Whether each value is float or int */
-
- fputs("%%BeginFeature: *CustomPageSize True\n", fp);
-
- size = ppdPageSize(ppd, "Custom");
-
- memset(values, 0, sizeof(values));
- memset(isfloat, 0, sizeof(isfloat));
-
- if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Width")) != NULL)
- {
- pos = atoi(attr->value) - 1;
-
- if (pos < 0 || pos > 4)
- pos = 0;
- }
- else
- pos = 0;
+ buffer = ppdEmitString(ppd, section, min_order);
- values[pos] = size->width;
- isfloat[pos] = 1;
+ /*
+ * Write it as needed and return...
+ */
- if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Height")) != NULL)
- {
- pos = atoi(attr->value) - 1;
+ if (buffer)
+ {
+ status = fputs(buffer, fp) < 0 ? -1 : 0;
- if (pos < 0 || pos > 4)
- pos = 1;
- }
- else
- pos = 1;
+ free(buffer);
+ }
+ else
+ status = 0;
- values[pos] = size->length;
- isfloat[pos] = 1;
+ return (status);
+}
- /*
- * According to the Adobe PPD specification, an orientation of 1
- * will produce a print that comes out upside-down with the X
- * axis perpendicular to the direction of feed, which is exactly
- * what we want to be consistent with non-PS printers.
- *
- * We could also use an orientation of 3 to produce output that
- * comes out rightside-up (this is the default for many large format
- * printer PPDs), however for consistency we will stick with the
- * value 1.
- *
- * If we wanted to get fancy, we could use orientations of 0 or
- * 2 and swap the width and length, however we don't want to get
- * fancy, we just want it to work consistently.
- *
- * The orientation value is range limited by the Orientation
- * parameter definition, so certain non-PS printer drivers that
- * only support an Orientation of 0 will get the value 0 as
- * expected.
- */
- orientation = 1;
+/*
+ * 'ppdEmitFd()' - Emit code for marked options to a file.
+ */
- if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize",
- "Orientation")) != NULL)
- {
- int min_orient, max_orient; /* Minimum and maximum orientations */
+int /* O - 0 on success, -1 on failure */
+ppdEmitFd(ppd_file_t *ppd, /* I - PPD file record */
+ int fd, /* I - File to write to */
+ ppd_section_t section) /* I - Section to write */
+{
+ char *buffer, /* Option code */
+ *bufptr; /* Pointer into code */
+ size_t buflength; /* Length of option code */
+ ssize_t bytes; /* Bytes written */
+ int status; /* Return status */
- if (sscanf(attr->value, "%d%*s%d%d", &pos, &min_orient,
- &max_orient) != 3)
- pos = 4;
- else
- {
- pos --;
+ /*
+ * Range check input...
+ */
- if (pos < 0 || pos > 4)
- pos = 4;
+ if (!ppd || fd < 0)
+ return (-1);
- if (orientation > max_orient)
- orientation = max_orient;
- else if (orientation < min_orient)
- orientation = min_orient;
- }
- }
- else
- pos = 4;
+ /*
+ * Get the string...
+ */
- values[pos] = orientation;
+ buffer = ppdEmitString(ppd, section, 0.0);
- for (pos = 0; pos < 5; pos ++)
- if (isfloat[pos])
- fprintf(fp, "%.2f\n", values[pos]);
- else
- fprintf(fp, "%.0f\n", values[pos]);
+ /*
+ * Write it as needed and return...
+ */
- if (choices[i]->code == NULL)
- {
- /*
- * This can happen with certain buggy PPD files that don't include
- * a CustomPageSize command sequence... We just use a generic
- * Level 2 command sequence...
- */
+ if (buffer)
+ {
+ buflength = strlen(buffer);
+ bufptr = buffer;
+ bytes = 0;
- fputs(ppd_custom_code, fp);
- }
- }
- else if (!strcasecmp(choices[i]->choice, "Custom"))
+ while (buflength > 0)
+ {
+#ifdef WIN32
+ if ((bytes = (ssize_t)write(fd, bufptr, (unsigned)buflength)) < 0)
+#else
+ if ((bytes = write(fd, bufptr, buflength)) < 0)
+#endif /* WIN32 */
{
- /*
- * Custom option...
- */
-
- ppd_coption_t *coption; /* Custom option */
- ppd_cparam_t *cparam; /* Custom parameter */
- const char *s; /* Pointer into string value */
-
-
- /*
- * TODO: Support custom options with more than 1 parameter...
- */
-
- if ((coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
- != NULL &&
- (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params)) != NULL)
- {
- if (fprintf(fp, "%%%%BeginFeature: *Custom%s True\n",
- coption->keyword) < 0)
- {
- free(choices);
- return (-1);
- }
-
- switch (cparam->type)
- {
- case PPD_CUSTOM_CURVE :
- case PPD_CUSTOM_INVCURVE :
- case PPD_CUSTOM_POINTS :
- case PPD_CUSTOM_REAL :
- if (fprintf(fp, "%f\n", cparam->current.custom_real) < 0)
- {
- free(choices);
- return (-1);
- }
- break;
-
- case PPD_CUSTOM_INT :
- if (fprintf(fp, "%d\n", cparam->current.custom_int) < 0)
- {
- free(choices);
- return (-1);
- }
- break;
-
- case PPD_CUSTOM_PASSCODE :
- case PPD_CUSTOM_PASSWORD :
- case PPD_CUSTOM_STRING :
- putc('(', fp);
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
- for (s = cparam->current.custom_string; *s; s ++)
- if (*s < ' ' || *s == '(' || *s == ')' || *s >= 127)
- fprintf(fp, "\\%03o", *s & 255);
- else
- putc(*s, fp);
-
- if (fputs(")\n", fp) < 0)
- {
- free(choices);
- return (-1);
- }
- break;
- }
- }
- }
- else if (fprintf(fp, "%%%%BeginFeature: *%s %s\n",
- choices[i]->option->keyword,
- choices[i]->choice) < 0)
- {
- free(choices);
- return (-1);
+ break;
}
- if (choices[i]->code != NULL && choices[i]->code[0] != '\0')
- {
- if (fputs(choices[i]->code, fp) < 0)
- {
- free(choices);
- return (-1);
- }
-
- if (choices[i]->code[strlen(choices[i]->code) - 1] != '\n')
- putc('\n', fp);
- }
+ buflength -= bytes;
+ bufptr += bytes;
+ }
- if (fputs("%%EndFeature\n", fp) < 0)
- {
- free(choices);
- return (-1);
- }
+ status = bytes < 0 ? -1 : 0;
- if (fputs("} stopped cleartomark\n", fp) < 0)
- {
- free(choices);
- return (-1);
- }
- }
- else if (fputs(choices[i]->code, fp) < 0)
- {
- free(choices);
- return (-1);
- }
+ free(buffer);
+ }
+ else
+ status = 0;
- free(choices);
- return (0);
+ return (status);
}
/*
- * 'ppdEmitFd()' - Emit code for marked options to a file.
+ * 'ppdEmitJCL()' - Emit code for JCL options to a file.
*/
int /* O - 0 on success, -1 on failure */
-ppdEmitFd(ppd_file_t *ppd, /* I - PPD file record */
- int fd, /* I - File to write to */
- ppd_section_t section) /* I - Section to write */
+ppdEmitJCL(ppd_file_t *ppd, /* I - PPD file record */
+ FILE *fp, /* I - File to write to */
+ int job_id, /* I - Job ID */
+ const char *user, /* I - Username */
+ const char *title) /* I - Title */
{
- int i, /* Looping var */
- count, /* Number of choices */
- custom_size; /* Non-zero if this option is a custom size */
- ppd_choice_t **choices; /* Choices */
- ppd_size_t *size; /* Custom page size */
- char buf[1024]; /* Output buffer for feature */
+ char *ptr; /* Pointer into JCL string */
+ char temp[81]; /* Local title string */
/*
- * Use PageSize or PageRegion as required...
+ * Range check the input...
*/
- ppd_handle_media(ppd);
+ if (!ppd || !ppd->jcl_begin || !ppd->jcl_ps)
+ return (0);
/*
- * Collect the options we need to emit and emit them!
+ * See if the printer supports HP PJL...
*/
- if ((count = ppdCollect(ppd, section, &choices)) == 0)
- return (0);
+ if (!strncmp(ppd->jcl_begin, "\033%-12345X@", 10))
+ {
+ /*
+ * This printer uses HP PJL commands for output; filter the output
+ * so that we only have a single "@PJL JOB" command in the header...
+ *
+ * To avoid bugs in the PJL implementation of certain vendors' products
+ * (Xerox in particular), we add a dummy "@PJL" command at the beginning
+ * of the PJL commands to initialize PJL processing.
+ */
- for (i = 0; i < count; i ++)
- if (section != PPD_ORDER_EXIT && section != PPD_ORDER_JCL)
+ ppd_attr_t *charset; /* PJL charset */
+
+
+ if ((charset = ppdFindAttr(ppd, "cupsPJLCharset", NULL)) != NULL)
{
- /*
- * Send wrapper commands to prevent printer errors for unsupported
- * options...
- */
+ if (!charset->value || strcasecmp(charset->value, "UTF-8"))
+ charset = NULL;
+ }
- if (write(fd, "[{\n", 3) < 1)
+ fputs("\033%-12345X@PJL\n", fp);
+ for (ptr = ppd->jcl_begin + 9; *ptr;)
+ if (!strncmp(ptr, "@PJL JOB", 8))
{
- free(choices);
- return (-1);
- }
+ /*
+ * Skip job command...
+ */
- /*
- * Send DSC comments with option...
- */
+ for (;*ptr; ptr ++)
+ if (*ptr == '\n')
+ break;
+
+ if (*ptr)
+ ptr ++;
+ }
+ else
+ {
+ /*
+ * Copy line...
+ */
+
+ for (;*ptr; ptr ++)
+ {
+ putc(*ptr, fp);
+ if (*ptr == '\n')
+ break;
+ }
+
+ if (*ptr)
+ ptr ++;
+ }
+
+ /*
+ * Eliminate any path info from the job title...
+ */
+
+ if ((ptr = strrchr(title, '/')) != NULL)
+ title = ptr + 1;
+
+ /*
+ * Replace double quotes with single quotes and 8-bit characters with
+ * question marks so that the title does not cause a PJL syntax error.
+ */
+
+ strlcpy(temp, title, sizeof(temp));
+
+ for (ptr = temp; *ptr; ptr ++)
+ if (*ptr == '\"')
+ *ptr = '\'';
+ else if (charset && (*ptr & 128))
+ *ptr = '?';
+
+ /*
+ * Send PJL JOB and PJL RDYMSG commands before we enter PostScript mode...
+ */
+
+ fprintf(fp, "@PJL JOB NAME = \"%s\" DISPLAY = \"%d %s %s\"\n", temp,
+ job_id, user, temp);
+ fprintf(fp, "@PJL RDYMSG DISPLAY = \"%d %s %s\"\n", job_id, user, temp);
+ }
+ else
+ fputs(ppd->jcl_begin, fp);
+
+ ppdEmit(ppd, fp, PPD_ORDER_JCL);
+ fputs(ppd->jcl_ps, fp);
+
+ return (0);
+}
+
+
+/*
+ * 'ppdEmitJCLEnd()' - Emit JCLEnd code to a file.
+ *
+ * @since CUPS 1.2@
+ */
+
+int /* O - 0 on success, -1 on failure */
+ppdEmitJCLEnd(ppd_file_t *ppd, /* I - PPD file record */
+ FILE *fp) /* I - File to write to */
+{
+ /*
+ * Range check the input...
+ */
+
+ if (!ppd)
+ return (0);
+
+ if (!ppd->jcl_end)
+ {
+ if (ppd->num_filters == 0)
+ putc(0x04, fp);
+
+ return (0);
+ }
+
+ /*
+ * See if the printer supports HP PJL...
+ */
+
+ if (!strncmp(ppd->jcl_end, "\033%-12345X@", 10))
+ {
+ /*
+ * This printer uses HP PJL commands for output; filter the output
+ * so that we only have a single "@PJL JOB" command in the header...
+ *
+ * To avoid bugs in the PJL implementation of certain vendors' products
+ * (Xerox in particular), we add a dummy "@PJL" command at the beginning
+ * of the PJL commands to initialize PJL processing.
+ */
+
+ fputs("\033%-12345X@PJL\n", fp);
+ fputs("@PJL RDYMSG DISPLAY = \"READY\"\n", fp);
+ fputs(ppd->jcl_end + 9, fp);
+ }
+ else
+ fputs(ppd->jcl_end, fp);
+
+ return (0);
+}
+
+
+/*
+ * 'ppdEmitString()' - Get a string containing the code for marked options.
+ *
+ * When "min_order" is greater than zero, this function only includes options
+ * whose OrderDependency value is greater than or equal to "min_order".
+ * Otherwise, all options in the specified section are included in the
+ * returned string.
+ *
+ * The return string is allocated on the heap and should be freed using
+ * free() when you are done with it.
+ *
+ * @since CUPS 1.2@
+ */
+
+char * /* O - String containing option code */
+ppdEmitString(ppd_file_t *ppd, /* I - PPD file record */
+ ppd_section_t section, /* I - Section to write */
+ float min_order) /* I - Lowest OrderDependency */
+{
+ int i, j, /* Looping vars */
+ count; /* Number of choices */
+ ppd_choice_t **choices; /* Choices */
+ ppd_size_t *size; /* Custom page size */
+ ppd_coption_t *coption; /* Custom option */
+ ppd_cparam_t *cparam; /* Custom parameter */
+ size_t bufsize; /* Size of string buffer needed */
+ char *buffer, /* String buffer */
+ *bufptr, /* Pointer into buffer */
+ *bufend; /* End of buffer */
+ struct lconv *loc; /* Locale data */
+
+
+ DEBUG_printf(("ppdEmitString(ppd=%p, section=%d, min_order=%f)\n",
+ ppd, section, min_order));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ppd)
+ return (NULL);
+
+ /*
+ * Use PageSize or PageRegion as required...
+ */
+
+ ppd_handle_media(ppd);
+
+ /*
+ * Collect the options we need to emit...
+ */
+
+ if ((count = ppdCollect2(ppd, section, min_order, &choices)) == 0)
+ return (NULL);
+
+ /*
+ * Count the number of bytes that are required to hold all of the
+ * option code...
+ */
+
+ for (i = 0, bufsize = 1; i < count; i ++)
+ {
+ if (section != PPD_ORDER_EXIT && section != PPD_ORDER_JCL)
+ {
+ bufsize += 3; /* [{\n */
if ((!strcasecmp(choices[i]->option->keyword, "PageSize") ||
!strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
!strcasecmp(choices[i]->choice, "Custom"))
{
- custom_size = 1;
+ DEBUG_puts("ppdEmitString: Custom size set!");
- strcpy(buf, "%%BeginFeature: *CustomPageSize True\n");
+ bufsize += 37; /* %%BeginFeature: *CustomPageSize True\n */
+ bufsize += 50; /* Five 9-digit numbers + newline */
}
- else
+ else if (!strcasecmp(choices[i]->choice, "Custom") &&
+ (coption = ppdFindCustomOption(ppd,
+ choices[i]->option->keyword))
+ != NULL)
{
- custom_size = 0;
+ bufsize += 17 + strlen(choices[i]->option->keyword) + 6;
+ /* %%BeginFeature: *keyword True\n */
- snprintf(buf, sizeof(buf), "%%%%BeginFeature: *%s %s\n",
- choices[i]->option->keyword, choices[i]->choice);
- }
+
+ for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
+ cparam;
+ cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
+ {
+ switch (cparam->type)
+ {
+ case PPD_CUSTOM_CURVE :
+ case PPD_CUSTOM_INVCURVE :
+ case PPD_CUSTOM_POINTS :
+ case PPD_CUSTOM_REAL :
+ case PPD_CUSTOM_INT :
+ bufsize += 10;
+ break;
- if (write(fd, buf, strlen(buf)) < 1)
- {
- free(choices);
- return (-1);
+ case PPD_CUSTOM_PASSCODE :
+ case PPD_CUSTOM_PASSWORD :
+ case PPD_CUSTOM_STRING :
+ bufsize += 3 + 4 * strlen(cparam->current.custom_string);
+ break;
+ }
+ }
}
+ else
+ bufsize += 17 + strlen(choices[i]->option->keyword) + 1 +
+ strlen(choices[i]->choice) + 1;
+ /* %%BeginFeature: *keyword choice\n */
+
+ bufsize += 13; /* %%EndFeature\n */
+ bufsize += 22; /* } stopped cleartomark\n */
+ }
+
+ if (choices[i]->code)
+ bufsize += strlen(choices[i]->code) + 1;
+ else
+ bufsize += strlen(ppd_custom_code);
+ }
+
+ /*
+ * Allocate memory...
+ */
+
+ DEBUG_printf(("ppdEmitString: Allocating %d bytes for string...\n", bufsize));
- if (custom_size)
+ if ((buffer = calloc(1, bufsize)) == NULL)
+ {
+ free(choices);
+ return (NULL);
+ }
+
+ bufend = buffer + bufsize - 1;
+ loc = localeconv();
+
+ /*
+ * Copy the option code to the buffer...
+ */
+
+ for (i = 0, bufptr = buffer; i < count; i ++, bufptr += strlen(bufptr))
+ if (section != PPD_ORDER_EXIT && section != PPD_ORDER_JCL)
+ {
+ /*
+ * Add wrapper commands to prevent printer errors for unsupported
+ * options...
+ */
+
+ strlcpy(bufptr, "[{\n", bufend - bufptr + 1);
+ bufptr += 3;
+
+ /*
+ * Send DSC comments with option...
+ */
+
+ DEBUG_printf(("Adding code for %s=%s...\n", choices[i]->option->keyword,
+ choices[i]->choice));
+
+ if ((!strcasecmp(choices[i]->option->keyword, "PageSize") ||
+ !strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
+ !strcasecmp(choices[i]->choice, "Custom"))
{
/*
* Variable size; write out standard size options, using the
ppd_attr_t *attr; /* PPD attribute */
int pos, /* Position of custom value */
- values[5], /* Values for custom command */
orientation; /* Orientation to use */
+ float values[5]; /* Values for custom command */
+
+ strlcpy(bufptr, "%%BeginFeature: *CustomPageSize True\n",
+ bufend - bufptr + 1);
+ bufptr += 37;
size = ppdPageSize(ppd, "Custom");
else
pos = 0;
- values[pos] = (int)size->width;
+ values[pos] = size->width;
if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Height")) != NULL)
{
else
pos = 1;
- values[pos] = (int)size->length;
+ values[pos] = size->length;
- if (size->width < size->length)
- orientation = 1;
- else
- orientation = 0;
+ /*
+ * According to the Adobe PPD specification, an orientation of 1
+ * will produce a print that comes out upside-down with the X
+ * axis perpendicular to the direction of feed, which is exactly
+ * what we want to be consistent with non-PS printers.
+ *
+ * We could also use an orientation of 3 to produce output that
+ * comes out rightside-up (this is the default for many large format
+ * printer PPDs), however for consistency we will stick with the
+ * value 1.
+ *
+ * If we wanted to get fancy, we could use orientations of 0 or
+ * 2 and swap the width and length, however we don't want to get
+ * fancy, we just want it to work consistently.
+ *
+ * The orientation value is range limited by the Orientation
+ * parameter definition, so certain non-PS printer drivers that
+ * only support an Orientation of 0 will get the value 0 as
+ * expected.
+ */
+
+ orientation = 1;
if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize",
"Orientation")) != NULL)
else
pos = 4;
- values[pos] = orientation;
-
- snprintf(buf, sizeof(buf), "%d %d %d %d %d\n", values[0], values[1],
- values[2], values[3], values[4]);
+ values[pos] = (float)orientation;
- if (write(fd, buf, strlen(buf)) < 1)
+ for (pos = 0; pos < 5; pos ++)
{
- free(choices);
- return (-1);
- }
+ bufptr = _cupsStrFormatd(bufptr, bufend, values[pos], loc);
+ *bufptr++ = '\n';
+ }
- if (choices[i]->code == NULL)
+ if (!choices[i]->code)
{
/*
* This can happen with certain buggy PPD files that don't include
* Level 2 command sequence...
*/
- if (write(fd, ppd_custom_code, strlen(ppd_custom_code)) < 1)
- {
- free(choices);
- return (-1);
- }
+ strlcpy(bufptr, ppd_custom_code, bufend - bufptr + 1);
+ bufptr += strlen(bufptr);
}
}
-
- if (choices[i]->code != NULL && choices[i]->code[0] != '\0')
- {
- if (write(fd, choices[i]->code, strlen(choices[i]->code)) < 1)
- {
- free(choices);
- return (-1);
- }
- }
-
- if (write(fd, "%%EndFeature\n", 13) < 1)
- {
- free(choices);
- return (-1);
- }
-
- if (write(fd, "} stopped cleartomark\n", 22) < 1)
+ else if (!strcasecmp(choices[i]->choice, "Custom") &&
+ (coption = ppdFindCustomOption(ppd,
+ choices[i]->option->keyword))
+ != NULL)
{
- free(choices);
- return (-1);
- }
- }
- else if (write(fd, choices[i]->code, strlen(choices[i]->code)) < 1)
- {
- free(choices);
- return (-1);
- }
-
- free(choices);
- return (0);
-}
-
-
-/*
- * 'ppdEmitJCL()' - Emit code for JCL options to a file.
- */
-
-int /* O - 0 on success, -1 on failure */
-ppdEmitJCL(ppd_file_t *ppd, /* I - PPD file record */
- FILE *fp, /* I - File to write to */
- int job_id, /* I - Job ID */
- const char *user, /* I - Username */
- const char *title) /* I - Title */
-{
- char *ptr; /* Pointer into JCL string */
- char temp[81]; /* Local title string */
+ /*
+ * Custom option...
+ */
+ const char *s; /* Pointer into string value */
- /*
- * Range check the input...
- */
- if (ppd == NULL || ppd->jcl_begin == NULL || ppd->jcl_ps == NULL)
- return (0);
+ snprintf(bufptr, bufend - bufptr + 1,
+ "%%%%BeginFeature: *Custom%s True\n", coption->keyword);
+ bufptr += strlen(bufptr);
- /*
- * See if the printer supports HP PJL...
- */
+ for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
+ cparam;
+ cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
+ {
+ switch (cparam->type)
+ {
+ case PPD_CUSTOM_CURVE :
+ case PPD_CUSTOM_INVCURVE :
+ case PPD_CUSTOM_POINTS :
+ case PPD_CUSTOM_REAL :
+ bufptr = _cupsStrFormatd(bufptr, bufend,
+ cparam->current.custom_real, loc);
+ *bufptr++ = '\n';
+ break;
- if (!strncmp(ppd->jcl_begin, "\033%-12345X@", 10))
- {
- /*
- * This printer uses HP PJL commands for output; filter the output
- * so that we only have a single "@PJL JOB" command in the header...
- *
- * To avoid bugs in the PJL implementation of certain vendors' products
- * (Xerox in particular), we add a dummy "@PJL" command at the beginning
- * of the PJL commands to initialize PJL processing.
- */
+ case PPD_CUSTOM_INT :
+ snprintf(bufptr, bufend - bufptr + 1, "%d\n",
+ cparam->current.custom_int);
+ bufptr += strlen(bufptr);
+ break;
- fputs("\033%-12345X@PJL\n", fp);
- for (ptr = ppd->jcl_begin + 9; *ptr;)
- if (!strncmp(ptr, "@PJL JOB", 8))
- {
- /*
- * Skip job command...
- */
+ case PPD_CUSTOM_PASSCODE :
+ case PPD_CUSTOM_PASSWORD :
+ case PPD_CUSTOM_STRING :
+ *bufptr++ = '(';
- for (;*ptr; ptr ++)
- if (*ptr == '\n')
- break;
+ for (s = cparam->current.custom_string; *s; s ++)
+ if (*s < ' ' || *s == '(' || *s == ')' || *s >= 127)
+ {
+ snprintf(bufptr, bufend - bufptr + 1, "\\%03o", *s & 255);
+ bufptr += strlen(bufptr);
+ }
+ else
+ *bufptr++ = *s;
- if (*ptr)
- ptr ++;
+ *bufptr++ = ')';
+ *bufptr++ = '\n';
+ break;
+ }
+ }
}
else
{
- /*
- * Copy line...
- */
-
- for (;*ptr; ptr ++)
- {
- putc(*ptr, fp);
- if (*ptr == '\n')
- break;
- }
-
- if (*ptr)
- ptr ++;
+ snprintf(bufptr, bufend - bufptr + 1, "%%%%BeginFeature: *%s %s\n",
+ choices[i]->option->keyword, choices[i]->choice);
+ bufptr += strlen(bufptr);
}
- /*
- * Eliminate any path info from the job title...
- */
-
- if ((ptr = strrchr(title, '/')) != NULL)
- title = ptr + 1;
-
- /*
- * Replace double quotes with single quotes so that the title
- * does not cause a PJL syntax error.
- */
-
- strlcpy(temp, title, sizeof(temp));
-
- for (ptr = temp; *ptr; ptr ++)
- if (*ptr == '\"')
- *ptr = '\'';
-
- /*
- * Send PJL JOB and PJL RDYMSG commands before we enter PostScript mode...
- */
-
- fprintf(fp, "@PJL JOB NAME = \"%s\" DISPLAY = \"%d %s %s\"\n", temp,
- job_id, user, temp);
- fprintf(fp, "@PJL RDYMSG DISPLAY = \"%d %s %s\"\n", job_id, user, temp);
- }
- else
- fputs(ppd->jcl_begin, fp);
-
- ppdEmit(ppd, fp, PPD_ORDER_JCL);
- fputs(ppd->jcl_ps, fp);
-
- return (0);
-}
-
-
-/*
- * 'ppdEmitJCLEnd()' - Emit JCLEnd code to a file.
- *
- * @since CUPS 1.2@
- */
-
-int /* O - 0 on success, -1 on failure */
-ppdEmitJCLEnd(ppd_file_t *ppd, /* I - PPD file record */
- FILE *fp) /* I - File to write to */
-{
- ppd_attr_t *attr; /* PPD attributes */
-
-
- /*
- * Range check the input...
- */
-
- if (ppd == NULL)
- return (0);
+ if (choices[i]->code && choices[i]->code[0])
+ {
+ j = (int)strlen(choices[i]->code);
+ memcpy(bufptr, choices[i]->code, j);
+ bufptr += j;
- if (ppd->jcl_end == NULL)
- {
- if (ppd->num_filters == 0)
- fputc(0x04, fp);
+ if (choices[i]->code[j - 1] != '\n')
+ *bufptr++ = '\n';
+ }
- if ((attr = ppdFindAttr(ppd, "cupsProtocol", NULL)) != NULL &&
- attr->value != NULL && !strcasecmp(attr->value, "TBCP"))
- fputs("\033%-12345X", stdout);
+ strlcpy(bufptr, "%%EndFeature\n"
+ "} stopped cleartomark\n", bufend - bufptr + 1);
+ bufptr += strlen(bufptr);
- return (0);
- }
+ DEBUG_printf(("ppdEmitString: Offset in string is %d...\n",
+ bufptr - buffer));
+ }
+ else
+ {
+ strlcpy(bufptr, choices[i]->code, bufend - bufptr + 1);
+ bufptr += strlen(bufptr);
+ }
/*
- * See if the printer supports HP PJL...
+ * Nul-terminate, free, and return...
*/
- if (!strncmp(ppd->jcl_end, "\033%-12345X@", 10))
- {
- /*
- * This printer uses HP PJL commands for output; filter the output
- * so that we only have a single "@PJL JOB" command in the header...
- *
- * To avoid bugs in the PJL implementation of certain vendors' products
- * (Xerox in particular), we add a dummy "@PJL" command at the beginning
- * of the PJL commands to initialize PJL processing.
- */
+ *bufptr = '\0';
- fputs("\033%-12345X@PJL\n", fp);
- fputs("@PJL RDYMSG DISPLAY = \"READY\"\n", fp);
- fputs(ppd->jcl_end + 9, fp);
- }
- else
- fputs(ppd->jcl_end, fp);
+ free(choices);
- return (0);
+ return (buffer);
}
/*
- * 'ppd_sort()' - Sort options by ordering numbers...
- */
-
-static int /* O - -1 if c1 < c2, 0 if equal, 1 otherwise */
-ppd_sort(ppd_choice_t **c1, /* I - First choice */
- ppd_choice_t **c2) /* I - Second choice */
-{
- if ((*c1)->option->order < (*c2)->option->order)
- return (-1);
- else if ((*c1)->option->order > (*c2)->option->order)
- return (1);
- else
- return (0);
-}
-
-
-/*
- * End of "$Id: emit.c 4980 2006-01-25 19:57:45Z mike $".
+ * End of "$Id: emit.c 6649 2007-07-11 21:46:42Z mike $".
*/