From 014f0e14a3c6a044d99a67c8f4e1c4065452479e Mon Sep 17 00:00:00 2001 From: Didier Raboud Date: Sun, 23 May 2010 00:05:04 +0200 Subject: Imported Upstream version 4.0-20090301 --- postscript.c | 1288 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1288 insertions(+) create mode 100644 postscript.c (limited to 'postscript.c') diff --git a/postscript.c b/postscript.c new file mode 100644 index 0000000..b82ba9a --- /dev/null +++ b/postscript.c @@ -0,0 +1,1288 @@ + +#include "foomaticrip.h" +#include "util.h" +#include "options.h" +#include "fileconverter.h" +#include "renderer.h" +#include "process.h" + +#include +#include +#include +#include + +void get_renderer_handle(const dstr_t *prepend, FILE **fd, pid_t *pid); +int close_renderer_handle(FILE *rendererhandle, pid_t rendererpid); + +#define LT_BEGIN_FEATURE 1 +#define LT_FOOMATIC_RIP_OPTION_SETTING 2 +int line_type(const char *line) +{ + const char *p; + if (startswith(line, "%%BeginFeature:")) + return LT_BEGIN_FEATURE; + p = line; + while (*p && isspace(*p)) p++; + if (!startswith(p, "%%")) + return 0; + p += 2; + while (*p && isspace(*p)) p++; + if (startswith(p, "FoomaticRIPOptionSetting:")) + return LT_FOOMATIC_RIP_OPTION_SETTING; + return 0; +} + + +/* Next, examine the PostScript job for traces of command-line and + JCL options. PPD-aware applications and spoolers stuff option + settings directly into the file, they do not necessarily send + PPD options by the command line. Also stuff in PostScript code + to apply option settings given by the command line and to set + the defaults given in the PPD file. + + Examination strategy: read lines from STDIN until the first + %%Page: comment appears and save them as @psheader. This is the + page-independent header part of the PostScript file. The + PostScript interpreter (renderer) must execute this part once + before rendering any assortment of pages. Then pages can be + printed in any arbitrary selection or order. All option + settings we find here will be collected in the default option + set for the RIP command line. + + Now the pages will be read and sent to the renderer, one after + the other. Every page is read into memory until the + %%EndPageSetup comment appears (or a certain amount of lines was + read). So we can get option settings only valid for this + page. If we have such settings we set them in the modified + command set for this page. + + If the renderer is not running yet (first page) we start it with + the command line built from the current modified command set and + send the first page to it, in the end we leave the renderer + running and keep input and output pipes open, so that it can + accept further pages. If the renderer is still running from + the previous page and the current modified command set is the + same as the one for the previous page, we send the page. If + the command set is different, we close the renderer, re-start + it with the command line built from the new modified command + set, send the header again, and then the page. + + After the last page the trailer (%%Trailer) is sent. + + The output pipe of this program stays open all the time so that + the spooler does not assume that the job has finished when the + renderer is re-started. + + Non DSC-conforming documents will be read until a certain line + number is reached. Command line or JCL options inserted later + will be ignored. + + If options are implemented by PostScript code supposed to be + stuffed into the job's PostScript data we stuff the code for all + these options into our job data, So all default settings made in + the PPD file (the user can have edited the PPD file to change + them) are taken care of and command line options get also + applied. To give priority to settings made by applications we + insert the options's code in the beginnings of their respective + sections, so that sommething, which is already inserted, gets + executed after our code. Missing sections are automatically + created. In non-DSC-conforming files we insert the option code + in the beginning of the file. This is the same policy as used by + the "pstops" filter of CUPS. + + If CUPS is the spooler, the option settings were already + inserted by the "pstops" filter, so we don't insert them + again. The only thing we do is correcting settings of numerical + options when they were set to a value not available as choice in + the PPD file, As "pstops" does not support "real" numerical + options, it sees these settings as an invalid choice and stays + with the default setting. In this case we correct the setting in + the first occurence of the option's code, as this one is the one + added by CUPS, later occurences come from applications and + should not be touched. + + If the input is not PostScript (if there is no "%!" after + $maxlinestopsstart lines) a file conversion filter will + automatically be applied to the incoming data, so that we will + process the resulting PostScript here. This way we have always + PostScript data here and so we can apply the printer/driver + features described in the PPD file. + + Supported file conversion filters are "a2ps", "enscript", + "mpage", and spooler-specific filters. All filters convert + plain text to PostScript, "a2ps" also other formats. The + conversion filter is always used when one prints the + documentation pages, as they are created as plain text, + when CUPS is the spooler "pstops" is executed after the + filter so that the default option settings from the PPD file + and CUPS-specific options as N-up get applied. On regular + printouts one gets always PostScript when CUPS or PPR is + the spooler, so the filter is only used for regular + printouts under LPD, LPRng, GNUlpr or without spooler. +*/ + +/* PostScript sections */ +#define PS_SECTION_JCLSETUP 1 +#define PS_SECTION_PROLOG 2 +#define PS_SECTION_SETUP 3 +#define PS_SECTION_PAGESETUP 4 + +#define MAX_NON_DSC_LINES_IN_HEADER 1000 +#define MAX_LINES_FOR_PAGE_OPTIONS 200 + +typedef struct { + size_t pos; + + FILE *file; + const char *alreadyread; + size_t len; +} stream_t; + +void _print_ps(stream_t *stream); + +int stream_next_line(dstr_t *line, stream_t *s) +{ + int c; + size_t cnt = 0; + + dstrclear(line); + while (s->pos < s->len) { + c = s->alreadyread[s->pos++]; + dstrputc(line, c); + cnt++; + if (c == '\n') + return cnt; + } + + while ((c = fgetc(s->file)) != EOF) { + dstrputc(line, c); + cnt++; + if (c == '\n') + return cnt; + } + return cnt; +} + +int print_ps(FILE *file, const char *alreadyread, size_t len, const char *filename) +{ + stream_t stream; + + if (file != stdin && (dup2(fileno(file), fileno(stdin)) < 0)) { + _log("Could not dup %s to stdin.\n", filename); + return 0; + } + + stream.pos = 0; + stream.file = stdin; + stream.alreadyread = alreadyread; + stream.len = len; + _print_ps(&stream); + return 1; +} + +void _print_ps(stream_t *stream) +{ + char *p; + + int maxlines = 1000; /* Maximum number of lines to be read when the + documenent is not DSC-conforming. + "$maxlines = 0" means that all will be read and + examined. If it is discovered that the input + file is DSC-conforming, this will be set to 0. */ + + int maxlinestopsstart = 200; /* That many lines are allowed until the + "%!" indicating PS comes. These + additional lines in the + beginning are usually JCL + commands. The lines will be + ignored by our parsing but + passed through. */ + + int printprevpage = 0; /* We set this when encountering "%%Page:" and the + previous page is not printed yet. Then it will + be printed and the new page will be prepared in + the next run of the loop (we don't read a new + line and don't increase the $linect then). */ + + int linect = 0; /* how many lines have we examined */ + int nonpslines = 0; /* lines before "%!" found yet. */ + int more_stuff = 1; /* there is more stuff in stdin */ + int saved = 0; /* DSC line not precessed yet */ + int isdscjob = 0; /* is the job dsc conforming */ + int inheader = 1; /* Are we still in the header, before first + "%%Page:" comment= */ + + int optionsalsointoheader = 0; /* 1: We are in a "%%BeginSetup... + %%EndSetup" section after the first + "%%Page:..." line (OpenOffice.org + does this and intends the options here + apply to the whole document and not + only to the current page). We have to + add all lines also to the end of the + @psheader now and we have to set + non-PostScript options also in the + "header" optionset. 0: otherwise. */ + + int insertoptions = 1; /* If we find out that a file with a DSC magic + string ("%!PS-Adobe-") is not really DSC- + conforming, we insert the options directly + after the line with the magic string. We use + this variable to store the number of the line + with the magic string */ + + int prologfound = 0; /* Did we find the + "%%BeginProlog...%%EndProlog" section? */ + int setupfound = 0; /* Did we find the + %%BeginSetup...%%EndSetup" section? */ + int pagesetupfound = 0; /* special page setup handling needed */ + + int inprolog = 0; /* We are between "%%BeginProlog" and "%%EndProlog" */ + int insetup = 0; /* We are between "%%BeginSetup" and "%%EndSetup" */ + int infeature = 0; /* We are between "%%BeginFeature" and "%%EndFeature" */ + + int optionreplaced = 0; /* Will be set to 1 when we are in an + option ("%%BeginFeature... + %%EndFeature") which we have replaced. */ + + int postscriptsection = PS_SECTION_JCLSETUP; /* In which section of the PostScript file + are we currently ? */ + + int nondsclines = 0; /* Number of subsequent lines found which are at a + non-DSC-conforming place, between the sections + of the header.*/ + + int nestinglevel = 0; /* Are we in the main document (0) or in an + embedded document bracketed by "%%BeginDocument" + and "%%EndDocument" (>0) We do not parse the + PostScript in an embedded document. */ + + int inpageheader = 0; /* Are we in the header of a page, + between "%%BeginPageSetup" and + "%%EndPageSetup" (1) or not (0). */ + + int passthru = 0; /* 0: write data into psfifo, + 1: pass data directly to the renderer */ + + int lastpassthru = 0; /* State of 'passthru' in previous line + (to allow debug output when $passthru + switches. */ + + int ignorepageheader = 0; /* Will be set to 1 as soon as active + code (not between "%%BeginPageSetup" + and "%%EndPageSetup") appears after a + "%%Page:" comment. In this case + "%%BeginPageSetup" and + "%%EndPageSetup" is not allowed any + more on this page and will be ignored. + Will be set to 0 when a new "%%Page:" + comment appears. */ + + int optset = optionset("header"); /* Where do the option settings which + we have found go? */ + + /* current line */ + dstr_t *line = create_dstr(); + + dstr_t *onelinebefore = create_dstr(); + dstr_t *twolinesbefore = create_dstr(); + + /* The header of the PostScript file, to be send after each start of the renderer */ + dstr_t *psheader = create_dstr(); + + /* The input FIFO, data which we have pulled from stdin for examination, + but not send to the renderer yet */ + dstr_t *psfifo = create_dstr(); + + FILE *fileconverter_handle = NULL; /* File handle to converter process */ + pid_t fileconverter_pid = 0; /* PID of the fileconverter process */ + + int ignoreline; + + int ooo110 = 0; /* Flag to work around an application bug */ + + int currentpage = 0; /* The page which we are currently printing */ + + option_t *o; + const char *val; + + int linetype; + + dstr_t *linesafterlastbeginfeature = create_dstr(); /* All codelines after the last "%%BeginFeature" */ + + char optionname [128]; + char value [128]; + int fromcomposite = 0; + + dstr_t *pdest; + + double width, height; + + pid_t rendererpid = 0; + FILE *rendererhandle = NULL; + + int retval; + + dstr_t *tmp = create_dstr(); + jobhasjcl = 0; + + /* We do not parse the PostScript to find Foomatic options, we check + only whether we have PostScript. */ + if (dontparse) + maxlines = 1; + + _log("Reading PostScript input ...\n"); + + do { + ignoreline = 0; + + if (printprevpage || saved || stream_next_line(line, stream)) { + saved = 0; + if (linect == nonpslines) { + /* In the beginning should be the postscript leader, + sometimes after some JCL commands */ + if ( !(line->data[0] == '%' && line->data[1] == '!') && + !(line->data[1] == '%' && line->data[2] == '!')) /* There can be a Windows control character before "%!" */ + { + nonpslines++; + if (maxlines == nonpslines) + maxlines ++; + jobhasjcl = 1; + + if (nonpslines > maxlinestopsstart) { + /* This is not a PostScript job, we must convert it */ + _log("Job does not start with \"%%!\", is it Postscript?\n" + "Starting file converter\n"); + + /* Reset all variables but conserve the data which we have already read */ + jobhasjcl = 0; + linect = 0; + nonpslines = 1; /* Take into account that the line of this run of the loop + will be put into @psheader, so the first line read by + the file converter is already the second line */ + maxlines = 1001; + + dstrclear(onelinebefore); + dstrclear(twolinesbefore); + + dstrcpyf(tmp, "%s%s%s", psheader, psfifo, line); + dstrclear(psheader); + dstrclear(psfifo); + dstrclear(line); + + /* Start the file conversion filter */ + if (!fileconverter_pid) + get_fileconverter_handle(tmp->data, &fileconverter_handle, &fileconverter_pid); + else + rip_die(EXIT_JOBERR, "File conversion filter probably crashed\n"); + + /* Read the further data from the file converter and not from STDIN */ + if (close(fileno(stdin)) == -1 && errno != ESPIPE) + rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Couldn't close STDIN\n"); + if (dup2(fileno(stdin), fileno(fileconverter_handle)) == -1) + rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Couldn't dup fileconverter_handle\n"); + } + } + else { + /* Do we have a DSC-conforming document? */ + if ((line->data[0] == '%' && startswith(line->data, "%!PS-Adobe-")) || + (line->data[1] == '%' && startswith(line->data, "%!PS-Adobe-"))) + { + /* Do not stop parsing the document */ + if (!dontparse) { + maxlines = 0; + isdscjob = 1; + insertoptions = linect + 1; + /* We have written into psfifo before, now we continue in + psheader and move over the data which is already in psfifo */ + dstrcat(psheader, psfifo->data); + dstrclear(psfifo); + } + _log("--> This document is DSC-conforming!\n"); + } + else { + /* Job is not DSC-conforming, stick in all PostScript + option settings in the beginning */ + append_prolog_section(line, optset, 1); + append_setup_section(line, optset, 1); + append_page_setup_section(line, optset, 1); + prologfound = 1; + setupfound = 1; + pagesetupfound = 1; + } + } + } + else { + if (startswith(line->data, "%")) { + if (startswith(line->data, "%%BeginDocument")) { + /* Beginning of an embedded document + Note that Adobe Acrobat has a bug and so uses + "%%BeginDocument " instead of "%%BeginDocument:" */ + nestinglevel++; + _log("Embedded document, nesting level now: %d\n", nestinglevel); + } + else if (nestinglevel > 0 && startswith(line->data, "%%EndDocument")) { + /* End of an embedded document */ + nestinglevel--; + _log("End of embedded document, nesting level now: %d\n", nestinglevel); + } + else if (nestinglevel == 0 && startswith(line->data, "%%Creator")) { + /* Here we set flags to treat particular bugs of the + PostScript produced by certain applications */ + p = strstr(line->data, "%%Creator") + 9; + while (*p && (isspace(*p) || *p == ':')) p++; + if (!strcmp(p, "OpenOffice.org")) { + p += 14; + while (*p && isspace(*p)) p++; + if (sscanf(p, "1.1.%d", &ooo110) == 1) { + _log("Document created with OpenOffice.org 1.1.x\n"); + ooo110 = 1; + } + } else if (!strcmp(p, "StarOffice 8")) { + p += 12; + _log("Document created with StarOffice 8\n"); + ooo110 = 1; + } + } + else if (nestinglevel == 0 && startswith(line->data, "%%BeginProlog")) { + /* Note: Below is another place where a "Prolog" section + start will be considered. There we assume start of the + "Prolog" if the job is DSC-Conformimg, but an arbitrary + comment starting with "%%Begin", but not a comment + explicitly treated here, is found. This is done because + many "dvips" (TeX/LaTeX) files miss the "%%BeginProlog" + comment. + Beginning of Prolog */ + _log("\n-----------\nFound: %%%%BeginProlog\n"); + inprolog = 1; + if (inheader) + postscriptsection = PS_SECTION_PROLOG; + nondsclines = 0; + /* Insert options for "Prolog" */ + if (!prologfound) { + append_prolog_section(line, optset, 0); + prologfound = 1; + } + } + else if (nestinglevel == 0 && startswith(line->data, "%%EndProlog")) { + /* End of Prolog */ + _log("Found: %%%%EndProlog\n"); + inprolog = 0; + insertoptions = linect +1; + } + else if (nestinglevel == 0 && startswith(line->data, "%%BeginSetup")) { + /* Beginning of Setup */ + _log("\n-----------\nFound: %%%%BeginSetup\n"); + insetup = 1; + nondsclines = 0; + /* We need to distinguish with the $inheader variable + here whether we are in the header or on a page, as + OpenOffice.org inserts a "%%BeginSetup...%%EndSetup" + section after the first "%%Page:..." line and assumes + this section to be valid for all pages. */ + if (inheader) { + postscriptsection = PS_SECTION_SETUP; + /* If there was no "Prolog" but there are + options for the "Prolog", push a "Prolog" + with these options onto the psfifo here */ + if (!prologfound) { + dstrclear(tmp); + append_prolog_section(tmp, optset, 1); + dstrprepend(line, tmp->data); + prologfound = 1; + } + /* Insert options for "DocumentSetup" or "AnySetup" */ + if (spooler != SPOOLER_CUPS && !setupfound) { + /* For non-CUPS spoolers or no spooler at all, + we leave everythnig as it is */ + append_setup_section(line, optset, 0); + setupfound = 1; + } + } + else { + /* Found option settings must be stuffed into both + the header and the currrent page now. They will + be written into both the "header" and the + "currentpage" optionsets and the PostScript code + lines of this section will not only go into the + output stream, but also added to the end of the + @psheader, so that they get repeated (to preserve + the embedded PostScript option settings) on a + restart of the renderer due to command line + option changes */ + optionsalsointoheader = 1; + _log("\"%%%%BeginSetup\" in page header\n"); + } + } + else if (nestinglevel == 0 && startswith(line->data, "%%EndSetup")) { + /* End of Setup */ + _log("Found: %%%%EndSetup\n"); + insetup = 0; + if (inheader) { + if (spooler == SPOOLER_CUPS) { + /* In case of CUPS, we must insert the + accounting stuff just before the + %%EndSetup comment in order to leave any + EndPage procedures that have been + defined by either the pstops filter or + the PostScript job itself fully + functional. */ + if (!setupfound) { + dstrclear(tmp); + append_setup_section(tmp, optset, 0); + dstrprepend(line, tmp->data); + setupfound = 1; + } + } + insertoptions = linect +1; + } + else { + /* The "%%BeginSetup...%%EndSetup" which + OpenOffice.org has inserted after the first + "%%Page:..." line ends here, so the following + options go only onto the current page again */ + optionsalsointoheader = 0; + } + } + else if (nestinglevel == 0 && startswith(line->data, "%%Page:")) { + if (!lastpassthru && !inheader) { + /* In the last line we were not in passthru mode, + so the last page is not printed. Prepare to do + it now. */ + printprevpage = 1; + passthru = 1; + _log("New page found but previous not printed, print it now.\n"); + } + else { + /* the previous page is printed, so we can prepare + the current one */ + _log("\n-----------\nNew page: %s", line->data); + printprevpage = 0; + currentpage++; + /* We consider the beginning of the page already as + page setup section, as some apps do not use + "%%PageSetup" tags. */ + postscriptsection = PS_SECTION_PAGESETUP; + + /* TODO can this be removed? + Save PostScript state before beginning the page + $line .= "/foomatic-saved-state save def\n"; */ + + /* Here begins a new page */ + if (inheader) { + build_commandline(optset, NULL, 0); + /* Here we add some stuff which still + belongs into the header */ + dstrclear(tmp); + + /* If there was no "Setup" but there are + options for the "Setup", push a "Setup" + with these options onto the @psfifo here */ + if (!setupfound) { + append_setup_section(tmp, optset, 1); + setupfound = 1; + } + /* If there was no "Prolog" but there are + options for the "Prolog", push a "Prolog" + with these options onto the @psfifo here */ + if (!prologfound) { + append_prolog_section(tmp, optset, 1); + prologfound = 1; + } + /* Now we push this into the header */ + dstrcat(psheader, tmp->data); + + /* The first page starts, so header ends */ + inheader = 0; + nondsclines = 0; + /* Option setting should go into the page + specific option set now */ + optset = optionset("currentpage"); + } + else { + /* Restore PostScript state after completing the + previous page: + + foomatic-saved-state restore + %%Page: ... + /foomatic-saved-state save def + + Print this directly, so that if we need to + restart the renderer for this page due to + a command line change this is done under the + old instance of the renderer + rint $rendererhandle + "foomatic-saved-state restore\n"; */ + + /* Save the option settings of the previous page */ + optionset_copy_values(optionset("currentpage"), optionset("previouspage")); + optionset_delete_values(optionset("currentpage")); + } + /* Initialize the option set */ + optionset_copy_values(optionset("header"), optionset("currentpage")); + + /* Set the command line options which apply only + to given pages */ + set_options_for_page(optionset("currentpage"), currentpage); + pagesetupfound = 0; + if (spooler == SPOOLER_CUPS) { + /* Remove the "notfirst" flag from all options + forseen for the "PageSetup" section, because + when these are numerical options for CUPS. + they have to be set to the correct value + for every page */ + for (o = optionlist; o; o = o->next) { + if (option_get_section(o ) == SECTION_PAGESETUP) + o->notfirst = 0; + } + } + /* Now the page header comes, so buffer the data, + because we must perhaps shut down and restart + the renderer */ + passthru = 0; + ignorepageheader = 0; + optionsalsointoheader = 0; + } + } + else if (nestinglevel == 0 && !ignorepageheader && + startswith(line->data, "%%BeginPageSetup")) { + /* Start of the page header, up to %%EndPageSetup + nothing of the page will be drawn, page-specific + option settngs (as letter-head paper for page 1) + go here*/ + _log("\nFound: %%%%BeginPageSetup\n"); + passthru = 0; + inpageheader = 1; + postscriptsection = PS_SECTION_PAGESETUP; + optionsalsointoheader = (ooo110 && currentpage == 1) ? 1 : 0; + /* Insert PostScript option settings + (options for section "PageSetup") */ + if (isdscjob) { + append_page_setup_section(line, optset, 0); + pagesetupfound = 1; + } + } + else if (nestinglevel == 0 && !ignorepageheader && + startswith(line->data, "%%BeginPageSetup")) { + /* End of the page header, the page is ready to be printed */ + _log("Found: %%%%EndPageSetup\n"); + _log("End of page header\n"); + /* We cannot for sure say that the page header ends here + OpenOffice.org puts (due to a bug) a "%%BeginSetup... + %%EndSetup" section after the first "%%Page:...". It + is possible that CUPS inserts a "%%BeginPageSetup... + %%EndPageSetup" before this section, which means that + the options in the "%%BeginSetup...%%EndSetup" + section are after the "%%EndPageSetup", so we + continue for searching options up to the buffer size + limit $maxlinesforpageoptions. */ + passthru = 0; + inpageheader = 0; + optionsalsointoheader = 0; + } + else if (nestinglevel == 0 && !optionreplaced && (!passthru || !isdscjob) && + ((linetype = line_type(line->data)) && + (linetype == LT_BEGIN_FEATURE || linetype == LT_FOOMATIC_RIP_OPTION_SETTING))) { + + /* parse */ + if (linetype == LT_BEGIN_FEATURE) { + dstrcpy(tmp, line->data); + p = strtok(tmp->data, " \t"); /* %%BeginFeature: */ + p = strtok(NULL, " \t="); /* Option */ + if (*p == '*') p++; + strlcpy(optionname, p, 128); + p = strtok(NULL, " \t\r\n"); /* value */ + fromcomposite = 0; + strlcpy(value, p, 128); + } + else { /* LT_FOOMATIC_RIP_OPTION_SETTING */ + dstrcpy(tmp, line->data); + p = strstr(tmp->data, "FoomaticRIPOptionSetting:"); + p = strtok(p, " \t"); /* FoomaticRIPOptionSetting */ + p = strtok(NULL, " \t="); /* Option */ + strlcpy(optionname, p, 128); + p = strtok(NULL, " \t\r\n"); /* value */ + if (*p == '@') { /* fromcomposite */ + p++; + fromcomposite = 1; + } + else + fromcomposite = 0; + strlcpy(value, p, 128); + } + + /* Mark that we are in a "Feature" section */ + if (linetype == LT_BEGIN_FEATURE) { + infeature = 1; + dstrclear(linesafterlastbeginfeature); + } + + /* OK, we have an option. If it's not a + Postscript-style option (ie, it's command-line or + JCL) then we should note that fact, since the + attribute-to-filter option passing in CUPS is kind of + funky, especially wrt boolean options. */ + _log("Found: %s", line->data); + if ((o = find_option(optionname))) { + _log(" Option: %s=%s%s\n", optionname, fromcomposite ? "From" : "", value); + if (spooler == SPOOLER_CUPS && + linetype == LT_BEGIN_FEATURE && + !option_get_value(o, optionset("notfirst")) && + strcmp(option_get_value(o, optset), value) != 0 && + (inheader || option_get_section(o) == SECTION_PAGESETUP)) { + + /* We have the first occurence of an option + setting and the spooler is CUPS, so this + setting is inserted by "pstops" or + "imagetops". The value from the command + line was not inserted by "pstops" or + "imagetops" so it seems to be not under + the choices in the PPD. Possible + reasons: + + - "pstops" and "imagetops" ignore settings + of numerical or string options which are + not one of the choices in the PPD file, + and inserts the default value instead. + + - On the command line an option was applied + only to selected pages: + "-o :