1 /* postscript.c
2 *
3 * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com>
4 * Copyright (C) 2008 Lars Karlitski (formerly Uebernickel) <lars@karlitski.net>
5 *
6 * This file is part of foomatic-rip.
7 *
8 * Foomatic-rip is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * Foomatic-rip is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 */
23
24 #include "foomaticrip.h"
25 #include "util.h"
26 #include "options.h"
27 #include "renderer.h"
28 #include "process.h"
29
30 #include <errno.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <stdlib.h>
34
35 void get_renderer_handle(const dstr_t *prepend, FILE **fd, pid_t *pid);
36 int close_renderer_handle(FILE *rendererhandle, pid_t rendererpid);
37
38 #define LT_BEGIN_FEATURE 1
39 #define LT_FOOMATIC_RIP_OPTION_SETTING 2
line_type(const char * line)40 int line_type(const char *line)
41 {
42 const char *p;
43 if (startswith(line, "%%BeginFeature:"))
44 return LT_BEGIN_FEATURE;
45 p = line;
46 while (*p && isspace(*p)) p++;
47 if (!startswith(p, "%%"))
48 return 0;
49 p += 2;
50 while (*p && isspace(*p)) p++;
51 if (startswith(p, "FoomaticRIPOptionSetting:"))
52 return LT_FOOMATIC_RIP_OPTION_SETTING;
53 return 0;
54 }
55
56
57 /* Next, examine the PostScript job for traces of command-line and
58 JCL options. PPD-aware applications and spoolers stuff option
59 settings directly into the file, they do not necessarily send
60 PPD options by the command line. Also stuff in PostScript code
61 to apply option settings given by the command line and to set
62 the defaults given in the PPD file.
63
64 Examination strategy: read lines from STDIN until the first
65 %%Page: comment appears and save them as @psheader. This is the
66 page-independent header part of the PostScript file. The
67 PostScript interpreter (renderer) must execute this part once
68 before rendering any assortment of pages. Then pages can be
69 printed in any arbitrary selection or order. All option
70 settings we find here will be collected in the default option
71 set for the RIP command line.
72
73 Now the pages will be read and sent to the renderer, one after
74 the other. Every page is read into memory until the
75 %%EndPageSetup comment appears (or a certain amount of lines was
76 read). So we can get option settings only valid for this
77 page. If we have such settings we set them in the modified
78 command set for this page.
79
80 If the renderer is not running yet (first page) we start it with
81 the command line built from the current modified command set and
82 send the first page to it, in the end we leave the renderer
83 running and keep input and output pipes open, so that it can
84 accept further pages. If the renderer is still running from
85 the previous page and the current modified command set is the
86 same as the one for the previous page, we send the page. If
87 the command set is different, we close the renderer, re-start
88 it with the command line built from the new modified command
89 set, send the header again, and then the page.
90
91 After the last page the trailer (%%Trailer) is sent.
92
93 The output pipe of this program stays open all the time so that
94 the spooler does not assume that the job has finished when the
95 renderer is re-started.
96
97 Non DSC-conforming documents will be read until a certain line
98 number is reached. Command line or JCL options inserted later
99 will be ignored.
100
101 If options are implemented by PostScript code supposed to be
102 stuffed into the job's PostScript data we stuff the code for all
103 these options into our job data, So all default settings made in
104 the PPD file (the user can have edited the PPD file to change
105 them) are taken care of and command line options get also
106 applied. To give priority to settings made by applications we
107 insert the options's code in the beginnings of their respective
108 sections, so that sommething, which is already inserted, gets
109 executed after our code. Missing sections are automatically
110 created. In non-DSC-conforming files we insert the option code
111 in the beginning of the file. This is the same policy as used by
112 the "pstops" filter of CUPS.
113
114 If CUPS is the spooler, the option settings were already
115 inserted by the "pstops" filter, so we don't insert them
116 again. The only thing we do is correcting settings of numerical
117 options when they were set to a value not available as choice in
118 the PPD file, As "pstops" does not support "real" numerical
119 options, it sees these settings as an invalid choice and stays
120 with the default setting. In this case we correct the setting in
121 the first occurence of the option's code, as this one is the one
122 added by CUPS, later occurences come from applications and
123 should not be touched.
124
125 If the input is not PostScript (if there is no "%!" after
126 $maxlinestopsstart lines) we will abort the document with an error.
127 */
128
129 /* PostScript sections */
130 #define PS_SECTION_JCLSETUP 1
131 #define PS_SECTION_PROLOG 2
132 #define PS_SECTION_SETUP 3
133 #define PS_SECTION_PAGESETUP 4
134
135 #define MAX_NON_DSC_LINES_IN_HEADER 1000
136 #define MAX_LINES_FOR_PAGE_OPTIONS 200
137
138 typedef struct {
139 size_t pos;
140
141 FILE *file;
142 const char *alreadyread;
143 size_t len;
144 } stream_t;
145
146 void _print_ps(stream_t *stream);
147
stream_next_line(dstr_t * line,stream_t * s)148 int stream_next_line(dstr_t *line, stream_t *s)
149 {
150 int c;
151 size_t cnt = 0;
152
153 dstrclear(line);
154 while (s->pos < s->len) {
155 c = s->alreadyread[s->pos++];
156 dstrputc(line, c);
157 cnt++;
158 if (c == '\n')
159 return cnt;
160 }
161
162 while ((c = fgetc(s->file)) != EOF) {
163 dstrputc(line, c);
164 cnt++;
165 if (c == '\n')
166 return cnt;
167 }
168 return cnt;
169 }
170
ps_pages(const char * filename)171 int ps_pages(const char *filename)
172 {
173 char gscommand[65536];
174 char output[31] = "";
175 int pagecount;
176 size_t bytes;
177 /* Ghostscript runs too long while printing PDF fikes converted from
178 djvu files. Using -dDEVICEWIDTHPOINTS=1 -dDEVICEHEIGHTPOINTS=1
179 solves the problem */
180 snprintf(gscommand, 65536, "%s -q -dNOPAUSE -dBATCH -sDEVICE=bbox -dDEVICEWIDTHPOINTS=1 -dDEVICEHEIGHTPOINTS=1 %s 2>&1 | grep -c HiResBoundingBox",
181 CUPS_GHOSTSCRIPT, filename);
182 FILE *pd = popen(gscommand, "r");
183 bytes = fread(output, 1, 31, pd);
184 pclose(pd);
185
186 if (bytes <= 0 || sscanf(output, "%d", &pagecount) < 1)
187 pagecount = -1;
188
189 return pagecount;
190 }
191
print_ps(FILE * file,const char * alreadyread,size_t len,const char * filename)192 int print_ps(FILE *file, const char *alreadyread, size_t len, const char *filename)
193 {
194 stream_t stream;
195
196 if (file != stdin)
197 {
198 int pagecount = ps_pages(filename);
199 if (pagecount < 0) {
200 _log("Unexpected page count\n");
201 return 0;
202 }
203 if (pagecount == 0) {
204 _log("No pages left, outputting empty file.\n");
205 return 1;
206 }
207 _log("File contains %d pages.\n", pagecount);
208 }
209
210 stream.pos = 0;
211 stream.file = file;
212 stream.alreadyread = alreadyread;
213 stream.len = len;
214 _print_ps(&stream);
215 return 1;
216 }
217
_print_ps(stream_t * stream)218 void _print_ps(stream_t *stream)
219 {
220 char *p;
221
222 int maxlines = 1000; /* Maximum number of lines to be read when the
223 documenent is not DSC-conforming.
224 "$maxlines = 0" means that all will be read and
225 examined. If it is discovered that the input
226 file is DSC-conforming, this will be set to 0. */
227
228 int maxlinestopsstart = 200; /* That many lines are allowed until the
229 "%!" indicating PS comes. These
230 additional lines in the
231 beginning are usually JCL
232 commands. The lines will be
233 ignored by our parsing but
234 passed through. */
235
236 int printprevpage = 0; /* We set this when encountering "%%Page:" and the
237 previous page is not printed yet. Then it will
238 be printed and the new page will be prepared in
239 the next run of the loop (we don't read a new
240 line and don't increase the $linect then). */
241
242 int linect = 0; /* how many lines have we examined */
243 int nonpslines = 0; /* lines before "%!" found yet. */
244 int more_stuff = 1; /* there is more stuff in stdin */
245 int saved = 0; /* DSC line not precessed yet */
246 int isdscjob = 0; /* is the job dsc conforming */
247 int inheader = 1; /* Are we still in the header, before first
248 "%%Page:" comment= */
249
250 int optionsalsointoheader = 0; /* 1: We are in a "%%BeginSetup...
251 %%EndSetup" section after the first
252 "%%Page:..." line (OpenOffice.org
253 does this and intends the options here
254 apply to the whole document and not
255 only to the current page). We have to
256 add all lines also to the end of the
257 @psheader now and we have to set
258 non-PostScript options also in the
259 "header" optionset. 0: otherwise. */
260
261 int insertoptions = 1; /* If we find out that a file with a DSC magic
262 string ("%!PS-Adobe-") is not really DSC-
263 conforming, we insert the options directly
264 after the line with the magic string. We use
265 this variable to store the number of the line
266 with the magic string */
267
268 int prologfound = 0; /* Did we find the
269 "%%BeginProlog...%%EndProlog" section? */
270 int setupfound = 0; /* Did we find the
271 %%BeginSetup...%%EndSetup" section? */
272 int pagesetupfound = 0; /* special page setup handling needed */
273
274 int inprolog = 0; /* We are between "%%BeginProlog" and "%%EndProlog" */
275 int insetup = 0; /* We are between "%%BeginSetup" and "%%EndSetup" */
276 int infeature = 0; /* We are between "%%BeginFeature" and "%%EndFeature" */
277
278 int optionreplaced = 0; /* Will be set to 1 when we are in an
279 option ("%%BeginFeature...
280 %%EndFeature") which we have replaced. */
281
282 int postscriptsection = PS_SECTION_JCLSETUP; /* In which section of the PostScript file
283 are we currently ? */
284
285 int nondsclines = 0; /* Number of subsequent lines found which are at a
286 non-DSC-conforming place, between the sections
287 of the header.*/
288
289 int nestinglevel = 0; /* Are we in the main document (0) or in an
290 embedded document bracketed by "%%BeginDocument"
291 and "%%EndDocument" (>0) We do not parse the
292 PostScript in an embedded document. */
293
294 int inpageheader = 0; /* Are we in the header of a page,
295 between "%%BeginPageSetup" and
296 "%%EndPageSetup" (1) or not (0). */
297
298 int passthru = 0; /* 0: write data into psfifo,
299 1: pass data directly to the renderer */
300
301 int lastpassthru = 0; /* State of 'passthru' in previous line
302 (to allow debug output when $passthru
303 switches. */
304
305 int ignorepageheader = 0; /* Will be set to 1 as soon as active
306 code (not between "%%BeginPageSetup"
307 and "%%EndPageSetup") appears after a
308 "%%Page:" comment. In this case
309 "%%BeginPageSetup" and
310 "%%EndPageSetup" is not allowed any
311 more on this page and will be ignored.
312 Will be set to 0 when a new "%%Page:"
313 comment appears. */
314
315 int optset = optionset("header"); /* Where do the option settings which
316 we have found go? */
317
318 /* current line */
319 dstr_t *line = create_dstr();
320
321 dstr_t *onelinebefore = create_dstr();
322 dstr_t *twolinesbefore = create_dstr();
323
324 /* The header of the PostScript file, to be send after each start of the renderer */
325 dstr_t *psheader = create_dstr();
326
327 /* The input FIFO, data which we have pulled from stdin for examination,
328 but not send to the renderer yet */
329 dstr_t *psfifo = create_dstr();
330
331 int ignoreline;
332
333 int ooo110 = 0; /* Flag to work around an application bug */
334
335 int currentpage = 0; /* The page which we are currently printing */
336
337 option_t *o;
338 const char *val;
339
340 int linetype;
341
342 dstr_t *linesafterlastbeginfeature = create_dstr(); /* All codelines after the last "%%BeginFeature" */
343
344 char optionname [128];
345 char value [128];
346 int fromcomposite = 0;
347
348 dstr_t *pdest;
349
350 double width, height;
351
352 pid_t rendererpid = 0;
353 FILE *rendererhandle = NULL;
354
355 int retval;
356
357 dstr_t *tmp = create_dstr();
358 jobhasjcl = 0;
359
360 /* We do not parse the PostScript to find Foomatic options, we check
361 only whether we have PostScript. */
362 if (dontparse)
363 maxlines = 1;
364
365 _log("Reading PostScript input ...\n");
366
367 do {
368 ignoreline = 0;
369
370 if (printprevpage || saved || stream_next_line(line, stream)) {
371 saved = 0;
372 if (linect == nonpslines) {
373 /* In the beginning should be the postscript leader,
374 sometimes after some JCL commands */
375 if ( !(line->data[0] == '%' && line->data[1] == '!') &&
376 !(line->data[1] == '%' && line->data[2] == '!')) /* There can be a Windows control character before "%!" */
377 {
378 nonpslines++;
379 if (maxlines == nonpslines)
380 maxlines ++;
381 jobhasjcl = 1;
382
383 if (nonpslines > maxlinestopsstart) {
384 /* This is not a PostScript job, abort it */
385 _log("Job does not start with \"%%!\", is it Postscript?\n");
386 rip_die(EXIT_JOBERR, "Unknown data format.\n");
387 }
388 }
389 else {
390 /* Do we have a DSC-conforming document? */
391 if ((line->data[0] == '%' && startswith(line->data, "%!PS-Adobe-")) ||
392 (line->data[1] == '%' && startswith(line->data, "%!PS-Adobe-")))
393 {
394 /* Do not stop parsing the document */
395 if (!dontparse) {
396 maxlines = 0;
397 isdscjob = 1;
398 insertoptions = linect + 1;
399 /* We have written into psfifo before, now we continue in
400 psheader and move over the data which is already in psfifo */
401 dstrcat(psheader, psfifo->data);
402 dstrclear(psfifo);
403 }
404 _log("--> This document is DSC-conforming!\n");
405 }
406 else {
407 /* Job is not DSC-conforming, stick in all PostScript
408 option settings in the beginning */
409 append_prolog_section(line, optset, 1);
410 append_setup_section(line, optset, 1);
411 append_page_setup_section(line, optset, 1);
412 prologfound = 1;
413 setupfound = 1;
414 pagesetupfound = 1;
415 }
416 }
417 }
418 else {
419 if (startswith(line->data, "%")) {
420 if (startswith(line->data, "%%BeginDocument")) {
421 /* Beginning of an embedded document
422 Note that Adobe Acrobat has a bug and so uses
423 "%%BeginDocument " instead of "%%BeginDocument:" */
424 nestinglevel++;
425 _log("Embedded document, nesting level now: %d\n", nestinglevel);
426 }
427 else if (nestinglevel > 0 && startswith(line->data, "%%EndDocument")) {
428 /* End of an embedded document */
429 nestinglevel--;
430 _log("End of embedded document, nesting level now: %d\n", nestinglevel);
431 }
432 else if (nestinglevel == 0 && startswith(line->data, "%%Creator")) {
433 /* Here we set flags to treat particular bugs of the
434 PostScript produced by certain applications */
435 p = strstr(line->data, "%%Creator") + 9;
436 while (*p && (isspace(*p) || *p == ':')) p++;
437 if (!strcmp(p, "OpenOffice.org")) {
438 p += 14;
439 while (*p && isspace(*p)) p++;
440 if (sscanf(p, "1.1.%d", &ooo110) == 1) {
441 _log("Document created with OpenOffice.org 1.1.x\n");
442 ooo110 = 1;
443 }
444 } else if (!strcmp(p, "StarOffice 8")) {
445 p += 12;
446 _log("Document created with StarOffice 8\n");
447 ooo110 = 1;
448 }
449 }
450 else if (nestinglevel == 0 && startswith(line->data, "%%BeginProlog")) {
451 /* Note: Below is another place where a "Prolog" section
452 start will be considered. There we assume start of the
453 "Prolog" if the job is DSC-Conformimg, but an arbitrary
454 comment starting with "%%Begin", but not a comment
455 explicitly treated here, is found. This is done because
456 many "dvips" (TeX/LaTeX) files miss the "%%BeginProlog"
457 comment.
458 Beginning of Prolog */
459 _log("\n-----------\nFound: %%%%BeginProlog\n");
460 inprolog = 1;
461 if (inheader)
462 postscriptsection = PS_SECTION_PROLOG;
463 nondsclines = 0;
464 /* Insert options for "Prolog" */
465 if (!prologfound) {
466 append_prolog_section(line, optset, 0);
467 prologfound = 1;
468 }
469 }
470 else if (nestinglevel == 0 && startswith(line->data, "%%EndProlog")) {
471 /* End of Prolog */
472 _log("Found: %%%%EndProlog\n");
473 inprolog = 0;
474 insertoptions = linect +1;
475 }
476 else if (nestinglevel == 0 && startswith(line->data, "%%BeginSetup")) {
477 /* Beginning of Setup */
478 _log("\n-----------\nFound: %%%%BeginSetup\n");
479 insetup = 1;
480 nondsclines = 0;
481 /* We need to distinguish with the $inheader variable
482 here whether we are in the header or on a page, as
483 OpenOffice.org inserts a "%%BeginSetup...%%EndSetup"
484 section after the first "%%Page:..." line and assumes
485 this section to be valid for all pages. */
486 if (inheader) {
487 postscriptsection = PS_SECTION_SETUP;
488 /* If there was no "Prolog" but there are
489 options for the "Prolog", push a "Prolog"
490 with these options onto the psfifo here */
491 if (!prologfound) {
492 dstrclear(tmp);
493 append_prolog_section(tmp, optset, 1);
494 dstrprepend(line, tmp->data);
495 prologfound = 1;
496 }
497 /* Insert options for "DocumentSetup" or "AnySetup" */
498 if (spooler != SPOOLER_CUPS && !setupfound) {
499 /* For non-CUPS spoolers or no spooler at all,
500 we leave everythnig as it is */
501 append_setup_section(line, optset, 0);
502 setupfound = 1;
503 }
504 }
505 else {
506 /* Found option settings must be stuffed into both
507 the header and the currrent page now. They will
508 be written into both the "header" and the
509 "currentpage" optionsets and the PostScript code
510 lines of this section will not only go into the
511 output stream, but also added to the end of the
512 @psheader, so that they get repeated (to preserve
513 the embedded PostScript option settings) on a
514 restart of the renderer due to command line
515 option changes */
516 optionsalsointoheader = 1;
517 _log("\"%%%%BeginSetup\" in page header\n");
518 }
519 }
520 else if (nestinglevel == 0 && startswith(line->data, "%%EndSetup")) {
521 /* End of Setup */
522 _log("Found: %%%%EndSetup\n");
523 insetup = 0;
524 if (inheader)
525 insertoptions = linect +1;
526 else {
527 /* The "%%BeginSetup...%%EndSetup" which
528 OpenOffice.org has inserted after the first
529 "%%Page:..." line ends here, so the following
530 options go only onto the current page again */
531 optionsalsointoheader = 0;
532 }
533 }
534 else if (nestinglevel == 0 && startswith(line->data, "%%Page:")) {
535 if (!lastpassthru && !inheader) {
536 /* In the last line we were not in passthru mode,
537 so the last page is not printed. Prepare to do
538 it now. */
539 printprevpage = 1;
540 passthru = 1;
541 _log("New page found but previous not printed, print it now.\n");
542 }
543 else {
544 /* the previous page is printed, so we can prepare
545 the current one */
546 _log("\n-----------\nNew page: %s", line->data);
547 printprevpage = 0;
548 currentpage++;
549 /* We consider the beginning of the page already as
550 page setup section, as some apps do not use
551 "%%PageSetup" tags. */
552 postscriptsection = PS_SECTION_PAGESETUP;
553
554 /* TODO can this be removed?
555 Save PostScript state before beginning the page
556 $line .= "/foomatic-saved-state save def\n"; */
557
558 /* Here begins a new page */
559 if (inheader) {
560 build_commandline(optset, NULL, 0);
561 /* Here we add some stuff which still
562 belongs into the header */
563 dstrclear(tmp);
564
565 /* If there was no "Setup" but there are
566 options for the "Setup", push a "Setup"
567 with these options onto the @psfifo here */
568 if (!setupfound) {
569 append_setup_section(tmp, optset, 1);
570 setupfound = 1;
571 }
572 /* If there was no "Prolog" but there are
573 options for the "Prolog", push a "Prolog"
574 with these options onto the @psfifo here */
575 if (!prologfound) {
576 append_prolog_section(tmp, optset, 1);
577 prologfound = 1;
578 }
579 /* Now we push this into the header */
580 dstrcat(psheader, tmp->data);
581
582 /* The first page starts, so header ends */
583 inheader = 0;
584 nondsclines = 0;
585 /* Option setting should go into the page
586 specific option set now */
587 optset = optionset("currentpage");
588 }
589 else {
590 /* Restore PostScript state after completing the
591 previous page:
592
593 foomatic-saved-state restore
594 %%Page: ...
595 /foomatic-saved-state save def
596
597 Print this directly, so that if we need to
598 restart the renderer for this page due to
599 a command line change this is done under the
600 old instance of the renderer
601 rint $rendererhandle
602 "foomatic-saved-state restore\n"; */
603
604 /* Save the option settings of the previous page */
605 optionset_copy_values(optionset("currentpage"), optionset("previouspage"));
606 optionset_delete_values(optionset("currentpage"));
607 }
608 /* Initialize the option set */
609 optionset_copy_values(optionset("header"), optionset("currentpage"));
610
611 /* Set the command line options which apply only
612 to given pages */
613 set_options_for_page(optionset("currentpage"), currentpage);
614 pagesetupfound = 0;
615 if (spooler == SPOOLER_CUPS) {
616 /* Remove the "notfirst" flag from all options
617 forseen for the "PageSetup" section, because
618 when these are numerical options for CUPS.
619 they have to be set to the correct value
620 for every page */
621 for (o = optionlist; o; o = o->next) {
622 if (option_get_section(o ) == SECTION_PAGESETUP)
623 o->notfirst = 0;
624 }
625 }
626 /* Now the page header comes, so buffer the data,
627 because we must perhaps shut down and restart
628 the renderer */
629 passthru = 0;
630 ignorepageheader = 0;
631 optionsalsointoheader = 0;
632 }
633 }
634 else if (nestinglevel == 0 && !ignorepageheader &&
635 startswith(line->data, "%%BeginPageSetup")) {
636 /* Start of the page header, up to %%EndPageSetup
637 nothing of the page will be drawn, page-specific
638 option settngs (as letter-head paper for page 1)
639 go here*/
640 _log("\nFound: %%%%BeginPageSetup\n");
641 passthru = 0;
642 inpageheader = 1;
643 postscriptsection = PS_SECTION_PAGESETUP;
644 optionsalsointoheader = (ooo110 && currentpage == 1) ? 1 : 0;
645 /* Insert PostScript option settings
646 (options for section "PageSetup") */
647 if (isdscjob) {
648 append_page_setup_section(line, optset, 0);
649 pagesetupfound = 1;
650 }
651 }
652 else if (nestinglevel == 0 && !ignorepageheader &&
653 startswith(line->data, "%%BeginPageSetup")) {
654 /* End of the page header, the page is ready to be printed */
655 _log("Found: %%%%EndPageSetup\n");
656 _log("End of page header\n");
657 /* We cannot for sure say that the page header ends here
658 OpenOffice.org puts (due to a bug) a "%%BeginSetup...
659 %%EndSetup" section after the first "%%Page:...". It
660 is possible that CUPS inserts a "%%BeginPageSetup...
661 %%EndPageSetup" before this section, which means that
662 the options in the "%%BeginSetup...%%EndSetup"
663 section are after the "%%EndPageSetup", so we
664 continue for searching options up to the buffer size
665 limit $maxlinesforpageoptions. */
666 passthru = 0;
667 inpageheader = 0;
668 optionsalsointoheader = 0;
669 }
670 else if (nestinglevel == 0 && !optionreplaced && (!passthru || !isdscjob) &&
671 ((linetype = line_type(line->data)) &&
672 (linetype == LT_BEGIN_FEATURE || linetype == LT_FOOMATIC_RIP_OPTION_SETTING))) {
673
674 /* parse */
675 if (linetype == LT_BEGIN_FEATURE) {
676 dstrcpy(tmp, line->data);
677 p = strtok(tmp->data, " \t"); /* %%BeginFeature: */
678 p = strtok(NULL, " \t="); /* Option */
679 if (*p == '*') p++;
680 strlcpy(optionname, p, 128);
681 p = strtok(NULL, " \t\r\n"); /* value */
682 fromcomposite = 0;
683 strlcpy(value, p, 128);
684 }
685 else { /* LT_FOOMATIC_RIP_OPTION_SETTING */
686 dstrcpy(tmp, line->data);
687 p = strstr(tmp->data, "FoomaticRIPOptionSetting:");
688 p = strtok(p, " \t"); /* FoomaticRIPOptionSetting */
689 p = strtok(NULL, " \t="); /* Option */
690 strlcpy(optionname, p, 128);
691 p = strtok(NULL, " \t\r\n"); /* value */
692 if (*p == '@') { /* fromcomposite */
693 p++;
694 fromcomposite = 1;
695 }
696 else
697 fromcomposite = 0;
698 strlcpy(value, p, 128);
699 }
700
701 /* Mark that we are in a "Feature" section */
702 if (linetype == LT_BEGIN_FEATURE) {
703 infeature = 1;
704 dstrclear(linesafterlastbeginfeature);
705 }
706
707 /* OK, we have an option. If it's not a
708 Postscript-style option (ie, it's command-line or
709 JCL) then we should note that fact, since the
710 attribute-to-filter option passing in CUPS is kind of
711 funky, especially wrt boolean options. */
712 _log("Found: %s", line->data);
713 if ((o = find_option(optionname)) &&
714 (o->type != TYPE_NONE)) {
715 _log(" Option: %s=%s%s\n", optionname, fromcomposite ? "From" : "", value);
716 if (spooler == SPOOLER_CUPS &&
717 linetype == LT_BEGIN_FEATURE &&
718 !option_get_value(o, optionset("notfirst")) &&
719 strcmp((val = option_get_value(o, optset)) ? val : "", value) != 0 &&
720 (inheader || option_get_section(o) == SECTION_PAGESETUP)) {
721
722 /* We have the first occurence of an option
723 setting and the spooler is CUPS, so this
724 setting is inserted by "pstops" or
725 "imagetops". The value from the command
726 line was not inserted by "pstops" or
727 "imagetops" so it seems to be not under
728 the choices in the PPD. Possible
729 reasons:
730
731 - "pstops" and "imagetops" ignore settings
732 of numerical or string options which are
733 not one of the choices in the PPD file,
734 and inserts the default value instead.
735
736 - On the command line an option was applied
737 only to selected pages:
738 "-o <page ranges>:<option>=<values>
739 This is not supported by CUPS, so not
740 taken care of by "pstops".
741
742 We must fix this here by replacing the
743 setting inserted by "pstops" or "imagetops"
744 with the exact setting given on the command
745 line. */
746
747 /* $arg->{$optionset} is already
748 range-checked, so do not check again here
749 Insert DSC comment */
750 pdest = (inheader && isdscjob) ? psheader : psfifo;
751 if (option_is_ps_command(o)) {
752 /* PostScript option, insert the code */
753
754 option_get_command(tmp, o, optset, -1);
755 if (!(val = option_get_value(o, optset)))
756 val = "";
757
758 /* Boolean and enumerated choice options can only be set in the
759 * PageSetup section */
760 if ((inheader && option_is_custom_value(o, val)) || !inheader)
761 {
762 if (o->type == TYPE_BOOL)
763 dstrcatf(pdest, "%%%%BeginFeature: *%s %s\n", o->name,
764 val && !strcmp(val, "1") ? "True" : "False");
765 else
766 dstrcatf(pdest, "%%%%BeginFeature: *%s %s\n", o->name, val);
767
768 dstrcatf(pdest, "%s\n", tmp->data);
769
770 /* We have replaced this option on the FIFO */
771 optionreplaced = 1;
772 }
773 }
774 else { /* Command line or JCL option */
775 val = option_get_value(o, optset);
776
777 if (!inheader || option_is_custom_value(o, val)) {
778 dstrcatf(pdest, "%%%% FoomaticRIPOptionSetting: %s=%s\n",
779 o->name, val ? val : "");
780 optionreplaced = 1;
781 }
782 }
783
784 if (optionreplaced) {
785 val = option_get_value(o, optset);
786 _log(" --> Correcting numerical/string option to %s=%s (Command line argument)\n",
787 o->name, val ? val : "");
788 }
789 }
790
791 /* Mark that we have already found this option */
792 o->notfirst = 1;
793 if (!optionreplaced) {
794 if (o->style != 'G') {
795 /* Controlled by '<Composite>' setting of
796 a member option of a composite option */
797 if (fromcomposite) {
798 dstrcpyf(tmp, "From%s", value);
799 strlcpy(value, tmp->data, 128);
800 }
801
802 /* Non PostScript option
803 Check whether it is valid */
804 if (option_set_value(o, optset, value)) {
805 _log("Setting option\n");
806 strlcpy(value, option_get_value(o, optset), 128);
807 if (optionsalsointoheader)
808 option_set_value(o, optionset("header"), value);
809 if (o->type == TYPE_ENUM &&
810 (!strcmp(o->name, "PageSize") || !strcmp(o->name, "PageRegion")) &&
811 startswith(value, "Custom") &&
812 linetype == LT_FOOMATIC_RIP_OPTION_SETTING) {
813 /* Custom Page size */
814 width = height = 0.0;
815 p = linesafterlastbeginfeature->data;
816 while (*p && isspace(*p)) p++;
817 width = strtod(p, &p);
818 while (*p && isspace(*p)) p++;
819 height = strtod(p, &p);
820 if (width && height) {
821 dstrcpyf(tmp, "%s.%fx%f", value, width, height);
822 strlcpy(value, tmp->data, 128);
823 option_set_value(o, optset, value);
824 if (optionsalsointoheader)
825 option_set_value(o, optionset("header"), value);
826 }
827 }
828 /* For a composite option insert the
829 code from the member options with
830 current setting "From<composite>"
831 The code from the member options
832 is chosen according to the setting
833 of the composite option. */
834 if (option_is_composite(o) && linetype == LT_FOOMATIC_RIP_OPTION_SETTING) {
835 build_commandline(optset, NULL, 0); /* TODO can this be removed? */
836
837 /* TODO merge section and ps_section */
838 if (postscriptsection == PS_SECTION_JCLSETUP)
839 option_get_command(tmp, o, optset, SECTION_JCLSETUP);
840 else if (postscriptsection == PS_SECTION_PROLOG)
841 option_get_command(tmp, o, optset, SECTION_PROLOG);
842 else if (postscriptsection == PS_SECTION_SETUP)
843 option_get_command(tmp, o, optset, SECTION_DOCUMENTSETUP);
844 else if (postscriptsection == PS_SECTION_PAGESETUP)
845 option_get_command(tmp, o, optset, SECTION_PAGESETUP);
846 dstrcat(line, tmp->data);
847 }
848 }
849 else
850 _log(" --> Invalid option setting found in job\n");
851 }
852 else if (fromcomposite) {
853 /* PostScript option, but we have to look up
854 the PostScript code to be inserted from
855 the setting of a composite option, as
856 this option is set to "Controlled by
857 '<Composite>'". */
858 /* Set the option */
859 dstrcpyf(tmp, "From%s", value);
860 strlcpy(value, tmp->data, 128);
861 if (option_set_value(o, optset, value)) {
862 _log(" --> Looking up setting in composite option %s\n", value);
863 if (optionsalsointoheader)
864 option_set_value(o, optionset("header"), value);
865 /* update composite options */
866 build_commandline(optset, NULL, 0);
867 /* Substitute PostScript comment by the real code */
868 /* TODO what exactly is the next line doing? */
869 /* dstrcpy(line, o->compositesubst->data); */
870 }
871 else
872 _log(" --> Invalid option setting found in job\n");
873 }
874 else
875 /* it is a PostScript style option with
876 the code readily inserted, no option
877 for the renderer command line/JCL to set,
878 no lookup of a composite option needed,
879 so nothing to do here... */
880 _log(" --> Option will be set by PostScript interpreter\n");
881 }
882 }
883 else
884 /* This option is unknown to us, WTF? */
885 _log("Unknown option %s=%s found in the job\n", optionname, value);
886 }
887 else if (nestinglevel == 0 && startswith(line->data, "%%EndFeature")) {
888 /* End of feature */
889 infeature = 0;
890 /* If the option setting was replaced, it ends here,
891 too, and the next option is not necessarily also replaced */
892 optionreplaced = 0;
893 dstrclear(linesafterlastbeginfeature);
894 }
895 else if (nestinglevel == 0 && isdscjob && !prologfound &&
896 startswith(line->data, "%%Begin")) {
897 /* In some PostScript files (especially when generated
898 by "dvips" of TeX/LaTeX) the "%%BeginProlog" is
899 missing, so assume that it was before the current
900 line (the first line starting with "%%Begin". */
901 _log("Job claims to be DSC-conforming, but \"%%%%BeginProlog\" "
902 "was missing before first line with another"
903 "\"%%%%BeginProlog\" comment (is this a TeX/LaTeX/dvips-generated"
904 " PostScript file?). Assuming start of \"Prolog\" here.\n");
905 /* Beginning of Prolog */
906 inprolog = 1;
907 nondsclines = 0;
908 /* Insert options for "Prolog" before the current line */
909 dstrcpyf(tmp, "%%%%BeginProlog\n");
910 append_prolog_section(tmp, optset, 0);
911 dstrprepend(line, tmp->data);
912 prologfound = 1;
913 }
914 else if (nestinglevel == 0 && (
915 startswith(line->data, "%RBINumCopies:") ||
916 startswith(line->data, "%%RBINumCopies:"))) {
917 p = strchr(line->data, ':') +1;
918 get_current_job()->rbinumcopies = atoi(p);
919 _log("Found %RBINumCopies: %d\n", get_current_job()->rbinumcopies);
920 }
921 else if (startswith(skip_whitespace(line->data), "%") ||
922 startswith(skip_whitespace(line->data), "$"))
923 /* This is an unknown PostScript comment or a blank
924 line, no active code */
925 ignoreline = 1;
926 }
927 else {
928 /* This line is active PostScript code */
929 if (infeature)
930 /* Collect coe in a "%%BeginFeature: ... %%EndFeature"
931 section, to get the values for a custom option
932 setting */
933 dstrcat(linesafterlastbeginfeature, line->data);
934
935 if (inheader) {
936 if (!inprolog && !insetup) {
937 /* Outside the "Prolog" and "Setup" section
938 a correct DSC-conforming document has no
939 active PostScript code, so consider the
940 file as non-DSC-conforming when there are
941 too many of such lines. */
942 nondsclines++;
943 if (nondsclines > MAX_NON_DSC_LINES_IN_HEADER) {
944 /* Consider document as not DSC-conforming */
945 _log("This job seems not to be DSC-conforming, "
946 "DSC-comment for next section not found, "
947 "stopping to parse the rest, passing it "
948 "directly to the renderer.\n");
949 /* Stop scanning for further option settings */
950 maxlines = 1;
951 isdscjob = 0;
952 /* Insert defaults and command line settings in
953 the beginning of the job or after the last valid
954 section */
955 dstrclear(tmp);
956 if (!prologfound)
957 append_prolog_section(tmp, optset, 1);
958 if (!setupfound)
959 append_setup_section(tmp, optset, 1);
960 if (!pagesetupfound)
961 append_page_setup_section(tmp, optset, 1);
962 dstrinsert(psheader, line_start(psheader->data, insertoptions), tmp->data);
963
964 prologfound = 1;
965 setupfound = 1;
966 pagesetupfound = 1;
967 }
968 }
969 }
970 else if (!inpageheader) {
971 /* PostScript code inside a page, but not between
972 "%%BeginPageSetup" and "%%EndPageSetup", so
973 we are perhaps already drawing onto a page now */
974 if (startswith(onelinebefore->data, "%%Page"))
975 _log("No page header or page header not DSC-conforming\n");
976 /* Stop buffering lines to search for options
977 placed not DSC-conforming */
978 if (line_count(psfifo->data) >= MAX_LINES_FOR_PAGE_OPTIONS) {
979 _log("Stopping search for page header options\n");
980 passthru = 1;
981 /* If there comes a page header now, ignore it */
982 ignorepageheader = 1;
983 optionsalsointoheader = 0;
984 }
985 /* Insert PostScript option settings (options for the
986 * section "PageSetup" */
987 if (isdscjob && !pagesetupfound) {
988 append_page_setup_section(psfifo, optset, 1);
989 pagesetupfound = 1;
990 }
991 }
992 }
993 }
994
995 /* Debug Info */
996 if (lastpassthru != passthru) {
997 if (passthru)
998 _log("Found: %s --> Output goes directly to the renderer now.\n\n", line->data);
999 else
1000 _log("Found: %s --> Output goes to the FIFO buffer now.\n\n", line->data);
1001 }
1002
1003 /* We are in an option which was replaced, do not output the current line */
1004 if (optionreplaced)
1005 dstrclear(line);
1006
1007 /* If we are in a "%%BeginSetup...%%EndSetup" section after
1008 the first "%%Page:..." and the current line belongs to
1009 an option setting, we have to copy the line also to the
1010 @psheader. */
1011 if (optionsalsointoheader && (infeature || startswith(line->data, "%%EndFeature")))
1012 dstrcat(psheader, line->data);
1013
1014 /* Store or send the current line */
1015 if (inheader && isdscjob) {
1016 /* We are still in the PostScript header, collect all lines
1017 in @psheader */
1018 dstrcat(psheader, line->data);
1019 }
1020 else {
1021 if (passthru && isdscjob) {
1022 if (!lastpassthru) {
1023 /*
1024 * We enter passthru mode with this line, so the
1025 * command line can have changed, check it and close
1026 * the renderer if needed
1027 */
1028 if (rendererpid && !optionset_equal(optionset("currentpage"), optionset("previouspage"), 0)) {
1029 _log("Command line/JCL options changed, restarting renderer\n");
1030 retval = close_renderer_handle(rendererhandle, rendererpid);
1031 if (retval != EXIT_PRINTED)
1032 rip_die(retval, "Error closing renderer\n");
1033 rendererpid = 0;
1034 }
1035 }
1036
1037 /* Flush psfifo and send line directly to the renderer */
1038 if (!rendererpid) {
1039 /* No renderer running, start it */
1040 dstrcpy(tmp, psheader->data);
1041 dstrcat(tmp, psfifo->data);
1042 get_renderer_handle(tmp, &rendererhandle, &rendererpid);
1043 /* psfifo is sent out, flush it */
1044 dstrclear(psfifo);
1045 }
1046
1047 if (!isempty(psfifo->data)) {
1048 /* Send psfifo to renderer */
1049 fwrite_or_die(psfifo->data, psfifo->len, 1, rendererhandle);
1050 /* flush psfifo */
1051 dstrclear(psfifo);
1052 }
1053
1054 /* Send line to renderer */
1055 if (!printprevpage) {
1056 fwrite_or_die(line->data, line->len, 1, rendererhandle);
1057
1058 while (stream_next_line(line, stream) > 0) {
1059 if (startswith(line->data, "%%")) {
1060 _log("Found: %s", line->data);
1061 _log(" --> Continue DSC parsing now.\n\n");
1062 saved = 1;
1063 break;
1064 }
1065 else {
1066 fwrite_or_die(line->data, line->len, 1, rendererhandle);
1067 linect++;
1068 }
1069 }
1070 }
1071 }
1072 else {
1073 /* Push the line onto the stack to split up later */
1074 dstrcat(psfifo, line->data);
1075 }
1076 }
1077
1078 if (!printprevpage)
1079 linect++;
1080 }
1081 else {
1082 /* EOF! */
1083 more_stuff = 0;
1084
1085 /* No PostScript header in the whole file? Then it's not
1086 PostScript, convert it.
1087 We open the file converter here when the file has less
1088 lines than the amount which we search for the PostScript
1089 header ($maxlinestopsstart). */
1090 if (linect <= nonpslines) {
1091 /* This is not a PostScript job, abort it */
1092 _log("Job does not start with \"%%!\", is it Postscript?\n");
1093 rip_die(EXIT_JOBERR, "Unknown data format.\n");
1094 }
1095 }
1096
1097 lastpassthru = passthru;
1098
1099 if (!ignoreline && !printprevpage) {
1100 dstrcpy(twolinesbefore, onelinebefore->data);
1101 dstrcpy(onelinebefore, line->data);
1102 }
1103
1104 } while ((maxlines == 0 || linect < maxlines) && more_stuff != 0);
1105
1106 /* Some buffer still containing data? Send it out to the renderer */
1107 if (more_stuff || inheader || !isempty(psfifo->data)) {
1108 /* Flush psfifo and send the remaining data to the renderer, this
1109 only happens with non-DSC-conforming jobs or non-Foomatic PPDs */
1110 if (more_stuff)
1111 _log("Stopped parsing the PostScript data, "
1112 "sending rest directly to the renderer.\n");
1113 else
1114 _log("Flushing FIFO.\n");
1115
1116 if (inheader) {
1117 build_commandline(optset, NULL, 0);
1118 /* No page initialized yet? Copy the "header" option set into the
1119 "currentpage" option set, so that the renderer will find the
1120 options settings. */
1121 optionset_copy_values(optionset("header"), optionset("currentpage"));
1122 optset = optionset("currentpage");
1123
1124 /* If not done yet, insert defaults and command line settings
1125 in the beginning of the job or after the last valid section */
1126 dstrclear(tmp);
1127 if (!prologfound)
1128 append_prolog_section(tmp, optset, 1);
1129 if (!setupfound)
1130 append_setup_section(tmp, optset, 1);
1131 if (!pagesetupfound)
1132 append_page_setup_section(tmp, optset, 1);
1133 dstrinsert(psheader, line_start(psheader->data, insertoptions), tmp->data);
1134
1135 prologfound = 1;
1136 setupfound = 1;
1137 pagesetupfound = 1;
1138 }
1139
1140 if (rendererpid > 0 && !optionset_equal(optionset("currentpage"), optionset("previouspage"), 0)) {
1141 _log("Command line/JCL options changed, restarting renderer\n");
1142 retval = close_renderer_handle(rendererhandle, rendererpid);
1143 if (retval != EXIT_PRINTED)
1144 rip_die(retval, "Error closing renderer\n");
1145 rendererpid = 0;
1146 }
1147
1148 if (!rendererpid) {
1149 dstrcpy(tmp, psheader->data);
1150 dstrcat(tmp, psfifo->data);
1151 get_renderer_handle(tmp, &rendererhandle, &rendererpid);
1152 /* We have sent psfifo now */
1153 dstrclear(psfifo);
1154 }
1155
1156 if (psfifo->len) {
1157 /* Send psfifo to the renderer */
1158 fwrite_or_die(psfifo->data, psfifo->len, 1, rendererhandle);
1159 dstrclear(psfifo);
1160 }
1161
1162 /* Print the rest of the input data */
1163 if (more_stuff) {
1164 while (stream_next_line(tmp, stream))
1165 fwrite_or_die(tmp->data, tmp->len, 1, rendererhandle);
1166 }
1167 }
1168
1169 /* At every "%%Page:..." comment we have saved the PostScript state
1170 and we have increased the page number. So if the page number is
1171 non-zero we had at least one "%%Page:..." comment and so we have
1172 to give a restore the PostScript state.
1173 if ($currentpage > 0) {
1174 print $rendererhandle "foomatic-saved-state restore\n";
1175 } */
1176
1177 /* Close the renderer */
1178 if (rendererpid) {
1179 retval = close_renderer_handle(rendererhandle, rendererpid);
1180 if (retval != EXIT_PRINTED)
1181 rip_die(retval, "Error closing renderer\n");
1182 rendererpid = 0;
1183 }
1184
1185 free_dstr(line);
1186 free_dstr(onelinebefore);
1187 free_dstr(twolinesbefore);
1188 free_dstr(psheader);
1189 free_dstr(psfifo);
1190 free_dstr(tmp);
1191 }
1192
1193 /*
1194 * Run the renderer command line (and if defined also the postpipe) and returns
1195 * a file handle for stuffing in the PostScript data.
1196 */
get_renderer_handle(const dstr_t * prepend,FILE ** fd,pid_t * pid)1197 void get_renderer_handle(const dstr_t *prepend, FILE **fd, pid_t *pid)
1198 {
1199 pid_t kid3;
1200 FILE *kid3in;
1201 dstr_t *cmdline = create_dstr();
1202
1203 /* Build the command line and get the JCL commands */
1204 build_commandline(optionset("currentpage"), cmdline, 0);
1205 massage_gs_commandline(cmdline);
1206
1207 _log("\nStarting renderer with command: \"%s\"\n", cmdline->data);
1208 kid3 = start_process("kid3", exec_kid3, (void *)cmdline->data, &kid3in, NULL);
1209 if (kid3 < 0)
1210 rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Cannot fork for kid3\n");
1211
1212 /* Feed the PostScript header and the FIFO contents */
1213 if (prepend)
1214 fwrite_or_die(prepend->data, prepend->len, 1, kid3in);
1215
1216 /* We are the parent, return glob to the file handle */
1217 *fd = kid3in;
1218 *pid = kid3;
1219
1220 free_dstr(cmdline);
1221 }
1222
1223 /* Close the renderer process and wait until all kid processes finish */
close_renderer_handle(FILE * rendererhandle,pid_t rendererpid)1224 int close_renderer_handle(FILE *rendererhandle, pid_t rendererpid)
1225 {
1226 int status;
1227
1228 _log("\nClosing renderer\n");
1229 fclose(rendererhandle);
1230
1231 status = wait_for_process(rendererpid);
1232 if (WIFEXITED(status))
1233 return WEXITSTATUS(status);
1234 else
1235 return EXIT_PRNERR_NORETRY_BAD_SETTINGS;
1236 }
1237
1238