]> git.ipfire.org Git - thirdparty/cups.git/blame - filter/pstops.c
Merge changes from CUPS 1.5svn-r8849.
[thirdparty/cups.git] / filter / pstops.c
CommitLineData
ef416fc2 1/*
0af14961 2 * "$Id: pstops.c 7977 2008-09-23 23:44:33Z mike $"
ef416fc2 3 *
4 * PostScript filter for the Common UNIX Printing System (CUPS).
5 *
e07d4801 6 * Copyright 2007-2009 by Apple Inc.
b86bc4cf 7 * Copyright 1993-2007 by Easy Software Products.
ef416fc2 8 *
9 * These coded instructions, statements, and computer programs are the
bc44d920 10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
ef416fc2 14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
0af14961
MS
19 * main() - Main entry.
20 * add_page() - Add a page to the pages array.
c24d2134 21 * cancel_job() - Flag the job as canceled.
80ca4592 22 * check_range() - Check to see if the current page is selected for
0af14961
MS
23 * printing.
24 * copy_bytes() - Copy bytes from the input file to stdout.
25 * copy_comments() - Copy all of the comments section.
26 * copy_dsc() - Copy a DSC-conforming document.
27 * copy_non_dsc() - Copy a document that does not conform to the DSC.
28 * copy_page() - Copy a page description.
29 * copy_prolog() - Copy the document prolog section.
30 * copy_setup() - Copy the document setup section.
31 * copy_trailer() - Copy the document trailer.
32 * do_prolog() - Send the necessary document prolog commands.
33 * do_setup() - Send the necessary document setup commands.
80ca4592 34 * doc_printf() - Send a formatted string to stdout and/or the temp
35 * file.
36 * doc_puts() - Send a nul-terminated string to stdout and/or the
37 * temp file.
38 * doc_write() - Send data to stdout and/or the temp file.
0af14961 39 * end_nup() - End processing for N-up printing.
80ca4592 40 * include_feature() - Include a printer option/feature command.
0af14961
MS
41 * parse_text() - Parse a text value in a comment.
42 * set_pstops_options() - Set pstops options.
43 * skip_page() - Skip past a page that won't be printed.
44 * start_nup() - Start processing for N-up printing.
45 * write_label_prolog() - Write the prolog with the classification and page
46 * label.
ed486911 47 * write_labels() - Write the actual page labels.
0af14961 48 * write_options() - Write options provided via %%IncludeFeature.
ef416fc2 49 */
50
51/*
52 * Include necessary headers...
53 */
54
55#include "common.h"
e53920b9 56#include <limits.h>
bd7854cb 57#include <math.h>
58#include <cups/file.h>
59#include <cups/array.h>
c0e1af83 60#include <cups/i18n.h>
c24d2134 61#include <signal.h>
ef416fc2 62
63
64/*
65 * Constants...
66 */
67
80ca4592 68#define PSTOPS_BORDERNONE 0 /* No border or hairline border */
69#define PSTOPS_BORDERTHICK 1 /* Think border */
70#define PSTOPS_BORDERSINGLE 2 /* Single-line hairline border */
71#define PSTOPS_BORDERSINGLE2 3 /* Single-line thick border */
72#define PSTOPS_BORDERDOUBLE 4 /* Double-line hairline border */
73#define PSTOPS_BORDERDOUBLE2 5 /* Double-line thick border */
ef416fc2 74
80ca4592 75#define PSTOPS_LAYOUT_LRBT 0 /* Left to right, bottom to top */
76#define PSTOPS_LAYOUT_LRTB 1 /* Left to right, top to bottom */
77#define PSTOPS_LAYOUT_RLBT 2 /* Right to left, bottom to top */
78#define PSTOPS_LAYOUT_RLTB 3 /* Right to left, top to bottom */
79#define PSTOPS_LAYOUT_BTLR 4 /* Bottom to top, left to right */
80#define PSTOPS_LAYOUT_TBLR 5 /* Top to bottom, left to right */
81#define PSTOPS_LAYOUT_BTRL 6 /* Bottom to top, right to left */
82#define PSTOPS_LAYOUT_TBRL 7 /* Top to bottom, right to left */
ef416fc2 83
80ca4592 84#define PSTOPS_LAYOUT_NEGATEY 1 /* The bits for the layout */
85#define PSTOPS_LAYOUT_NEGATEX 2 /* definitions above... */
86#define PSTOPS_LAYOUT_VERTICAL 4
ef416fc2 87
88
bd7854cb 89/*
90 * Types...
91 */
92
93typedef struct /**** Page information ****/
94{
95 char *label; /* Page label */
80ca4592 96 int bounding_box[4]; /* PageBoundingBox */
bd7854cb 97 off_t offset; /* Offset to start of page */
98 ssize_t length; /* Number of bytes for page */
80ca4592 99 int num_options; /* Number of options for this page */
100 cups_option_t *options; /* Options for this page */
101} pstops_page_t;
102
103typedef struct /**** Document information ****/
104{
105 int page; /* Current page */
106 int bounding_box[4]; /* BoundingBox from header */
107 int new_bounding_box[4]; /* New composite bounding box */
108 int num_options; /* Number of document-wide options */
109 cups_option_t *options; /* Document-wide options */
110 int normal_landscape, /* Normal rotation for landscape? */
111 saw_eof, /* Saw the %%EOF comment? */
112 slow_collate, /* Collate copies by hand? */
113 slow_duplex, /* Duplex pages slowly? */
114 slow_order, /* Reverse pages slowly? */
115 use_ESPshowpage; /* Use ESPshowpage? */
116 cups_array_t *pages; /* Pages in document */
117 cups_file_t *temp; /* Temporary file, if any */
118 char tempfile[1024]; /* Temporary filename */
119 int job_id; /* Job ID */
120 const char *user, /* User name */
121 *title; /* Job name */
122 int copies; /* Number of copies */
123 const char *ap_input_slot, /* AP_FIRSTPAGE_InputSlot value */
124 *ap_manual_feed; /* AP_FIRSTPAGE_ManualFeed value */
125 float brightness; /* brightness value */
126 int collate, /* Collate copies? */
127 emit_jcl, /* Emit JCL commands? */
128 fitplot; /* Fit pages to media */
129 float gamma; /* gamma value */
130 const char *input_slot, /* InputSlot value */
131 *manual_feed; /* ManualFeed value */
132 int mirror, /* doc->mirror/mirror pages */
133 number_up, /* Number of pages on each sheet */
134 number_up_layout, /* doc->number_up_layout of N-up pages */
135 output_order, /* Requested reverse output order? */
136 page_border; /* doc->page_border around pages */
137 const char *page_label, /* page-label option, if any */
138 *page_ranges, /* page-ranges option, if any */
139 *page_set; /* page-set option, if any */
140} pstops_doc_t;
bd7854cb 141
142
ef416fc2 143/*
80ca4592 144 * Convenience macros...
ef416fc2 145 */
146
80ca4592 147#define is_first_page(p) (doc->number_up == 1 || \
148 ((p) % doc->number_up) == 1)
149#define is_last_page(p) (doc->number_up == 1 || \
150 ((p) % doc->number_up) == 0)
151#define is_not_last_page(p) (doc->number_up > 1 && \
152 ((p) % doc->number_up) != 0)
ef416fc2 153
154
c24d2134
MS
155/*
156 * Local globals...
157 */
158
159static int JobCanceled = 0;/* Set to 1 on SIGTERM */
160
161
ef416fc2 162/*
163 * Local functions...
164 */
165
80ca4592 166static pstops_page_t *add_page(pstops_doc_t *doc, const char *label);
c24d2134 167static void cancel_job(int sig);
80ca4592 168static int check_range(pstops_doc_t *doc, int page);
bd7854cb 169static void copy_bytes(cups_file_t *fp, off_t offset,
170 size_t length);
91c84a35 171static ssize_t copy_comments(cups_file_t *fp, pstops_doc_t *doc,
2abf387c 172 ppd_file_t *ppd, char *line,
91c84a35 173 ssize_t linelen, size_t linesize);
80ca4592 174static void copy_dsc(cups_file_t *fp, pstops_doc_t *doc,
91c84a35 175 ppd_file_t *ppd, char *line, ssize_t linelen,
80ca4592 176 size_t linesize);
177static void copy_non_dsc(cups_file_t *fp, pstops_doc_t *doc,
178 ppd_file_t *ppd, char *line,
91c84a35
MS
179 ssize_t linelen, size_t linesize);
180static ssize_t copy_page(cups_file_t *fp, pstops_doc_t *doc,
80ca4592 181 ppd_file_t *ppd, int number, char *line,
91c84a35
MS
182 ssize_t linelen, size_t linesize);
183static ssize_t copy_prolog(cups_file_t *fp, pstops_doc_t *doc,
80ca4592 184 ppd_file_t *ppd, char *line,
91c84a35
MS
185 ssize_t linelen, size_t linesize);
186static ssize_t copy_setup(cups_file_t *fp, pstops_doc_t *doc,
80ca4592 187 ppd_file_t *ppd, char *line,
91c84a35
MS
188 ssize_t linelen, size_t linesize);
189static ssize_t copy_trailer(cups_file_t *fp, pstops_doc_t *doc,
80ca4592 190 ppd_file_t *ppd, int number, char *line,
91c84a35 191 ssize_t linelen, size_t linesize);
80ca4592 192static void do_prolog(pstops_doc_t *doc, ppd_file_t *ppd);
193static void do_setup(pstops_doc_t *doc, ppd_file_t *ppd);
f301802f 194static void doc_printf(pstops_doc_t *doc, const char *format, ...)
195#ifdef __GNUC__
196__attribute__ ((__format__ (__printf__, 2, 3)))
197#endif /* __GNUC__ */
198;
80ca4592 199static void doc_puts(pstops_doc_t *doc, const char *s);
200static void doc_write(pstops_doc_t *doc, const char *s, size_t len);
201static void end_nup(pstops_doc_t *doc, int number);
202static int include_feature(ppd_file_t *ppd, const char *line,
203 int num_options,
204 cups_option_t **options);
205static char *parse_text(const char *start, char **end, char *buffer,
206 size_t bufsize);
207static void set_pstops_options(pstops_doc_t *doc, ppd_file_t *ppd,
208 char *argv[], int num_options,
209 cups_option_t *options);
91c84a35 210static ssize_t skip_page(cups_file_t *fp, char *line, ssize_t linelen,
80ca4592 211 size_t linesize);
212static void start_nup(pstops_doc_t *doc, int number,
213 int show_border, const int *bounding_box);
b86bc4cf 214static void write_label_prolog(pstops_doc_t *doc, const char *label,
215 float bottom, float top,
216 float width);
ed486911 217static void write_labels(pstops_doc_t *doc, int orient);
0af14961
MS
218static void write_options(pstops_doc_t *doc, ppd_file_t *ppd,
219 int num_options, cups_option_t *options);
ef416fc2 220
221
222/*
0af14961 223 * 'main()' - Main entry.
ef416fc2 224 */
225
bd7854cb 226int /* O - Exit status */
227main(int argc, /* I - Number of command-line args */
228 char *argv[]) /* I - Command-line arguments */
ef416fc2 229{
80ca4592 230 pstops_doc_t doc; /* Document information */
231 cups_file_t *fp; /* Print file */
bd7854cb 232 ppd_file_t *ppd; /* PPD file */
bd7854cb 233 int num_options; /* Number of print options */
234 cups_option_t *options; /* Print options */
bd7854cb 235 char line[8192]; /* Line buffer */
bd7854cb 236 size_t len; /* Length of line buffer */
7ff4fea9
MS
237#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
238 struct sigaction action; /* Actions for POSIX signals */
239#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
80ca4592 240
ef416fc2 241
242 /*
243 * Make sure status messages are not buffered...
244 */
245
246 setbuf(stderr, NULL);
247
248 /*
249 * Check command-line...
250 */
251
252 if (argc < 6 || argc > 7)
253 {
f0ab5bff
MS
254 _cupsLangPrintf(stderr,
255 _("Usage: %s job-id user title copies options [file]\n"),
256 argv[0]);
ef416fc2 257 return (1);
258 }
259
c24d2134
MS
260 /*
261 * Register a signal handler to cleanly cancel a job.
262 */
263
264#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
265 sigset(SIGTERM, cancel_job);
266#elif defined(HAVE_SIGACTION)
267 memset(&action, 0, sizeof(action));
268
269 sigemptyset(&action.sa_mask);
270 action.sa_handler = cancel_job;
271 sigaction(SIGTERM, &action, NULL);
272#else
273 signal(SIGTERM, cancel_job);
274#endif /* HAVE_SIGSET */
275
ef416fc2 276 /*
277 * If we have 7 arguments, print the file named on the command-line.
278 * Otherwise, send stdin instead...
279 */
280
281 if (argc == 6)
80ca4592 282 fp = cupsFileStdin();
ef416fc2 283 else
284 {
285 /*
286 * Try to open the print file...
287 */
288
80ca4592 289 if ((fp = cupsFileOpen(argv[6], "r")) == NULL)
ef416fc2 290 {
f0ab5bff
MS
291 _cupsLangPrintf(stderr, _("ERROR: Unable to open file \"%s\" - %s\n"),
292 argv[6], strerror(errno));
ef416fc2 293 return (1);
294 }
295 }
296
297 /*
80ca4592 298 * Read the first line to see if we have DSC comments...
ef416fc2 299 */
300
80ca4592 301 if ((len = cupsFileGetLine(fp, line, sizeof(line))) == 0)
302 {
4d301e69 303 _cupsLangPuts(stderr, _("ERROR: Empty print file\n"));
80ca4592 304 return (1);
305 }
ef416fc2 306
80ca4592 307 /*
308 * Process command-line options...
309 */
ef416fc2 310
311 options = NULL;
312 num_options = cupsParseOptions(argv[5], 0, &options);
80ca4592 313 ppd = SetCommonOptions(num_options, options, 1);
ef416fc2 314
80ca4592 315 set_pstops_options(&doc, ppd, argv, num_options, options);
ef416fc2 316
80ca4592 317 /*
318 * Write any "exit server" options that have been selected...
319 */
ef416fc2 320
80ca4592 321 ppdEmit(ppd, stdout, PPD_ORDER_EXIT);
ef416fc2 322
80ca4592 323 /*
324 * Write any JCL commands that are needed to print PostScript code...
325 */
bd7854cb 326
80ca4592 327 if (doc.emit_jcl)
328 ppdEmitJCL(ppd, stdout, doc.job_id, doc.user, doc.title);
ef416fc2 329
80ca4592 330 /*
331 * Start with a DSC header...
332 */
ef416fc2 333
80ca4592 334 puts("%!PS-Adobe-3.0");
ef416fc2 335
80ca4592 336 /*
337 * Skip leading PJL in the document...
338 */
ef416fc2 339
80ca4592 340 while (!strncmp(line, "\033%-12345X", 9) || !strncmp(line, "@PJL ", 5))
bd7854cb 341 {
342 /*
80ca4592 343 * Yup, we have leading PJL fun, so skip it until we hit the line
344 * with "ENTER LANGUAGE"...
bd7854cb 345 */
346
80ca4592 347 fputs("DEBUG: Skipping PJL header...\n", stderr);
bd7854cb 348
d6ae789d 349 while (strstr(line, "ENTER LANGUAGE") == NULL && strncmp(line, "%!", 2))
80ca4592 350 if ((len = cupsFileGetLine(fp, line, sizeof(line))) == 0)
351 break;
ef416fc2 352
d6ae789d 353 if (!strncmp(line, "%!", 2))
354 break;
355
80ca4592 356 if ((len = cupsFileGetLine(fp, line, sizeof(line))) == 0)
357 break;
bd7854cb 358 }
359
ef416fc2 360 /*
80ca4592 361 * Now see if the document conforms to the Adobe Document Structuring
362 * Conventions...
ef416fc2 363 */
364
80ca4592 365 if (!strncmp(line, "%!PS-Adobe-", 11))
bd7854cb 366 {
367 /*
80ca4592 368 * Yes, filter the document...
bd7854cb 369 */
370
80ca4592 371 copy_dsc(fp, &doc, ppd, line, len, sizeof(line));
bd7854cb 372 }
80ca4592 373 else
ef416fc2 374 {
375 /*
80ca4592 376 * No, display an error message and treat the file as if it contains
377 * a single page...
ef416fc2 378 */
379
80ca4592 380 copy_non_dsc(fp, &doc, ppd, line, len, sizeof(line));
ef416fc2 381 }
ef416fc2 382
bd7854cb 383 /*
80ca4592 384 * Send %%EOF as needed...
bd7854cb 385 */
386
80ca4592 387 if (!doc.saw_eof)
388 puts("%%EOF");
bd7854cb 389
ef416fc2 390 /*
80ca4592 391 * End the job with the appropriate JCL command or CTRL-D...
ef416fc2 392 */
393
80ca4592 394 if (doc.emit_jcl)
ef416fc2 395 {
80ca4592 396 if (ppd && ppd->jcl_end)
397 ppdEmitJCLEnd(ppd, stdout);
398 else
399 putchar(0x04);
ef416fc2 400 }
ef416fc2 401
402 /*
80ca4592 403 * Close files and remove the temporary file if needed...
ef416fc2 404 */
405
80ca4592 406 if (doc.temp)
ef416fc2 407 {
80ca4592 408 cupsFileClose(doc.temp);
409 unlink(doc.tempfile);
ef416fc2 410 }
411
80ca4592 412 ppdClose(ppd);
413 cupsFreeOptions(num_options, options);
ef416fc2 414
80ca4592 415 cupsFileClose(fp);
ef416fc2 416
80ca4592 417 return (0);
418}
ef416fc2 419
ef416fc2 420
80ca4592 421/*
0af14961 422 * 'add_page()' - Add a page to the pages array.
80ca4592 423 */
ef416fc2 424
80ca4592 425static pstops_page_t * /* O - New page info object */
426add_page(pstops_doc_t *doc, /* I - Document information */
427 const char *label) /* I - Page label */
428{
429 pstops_page_t *pageinfo; /* New page info object */
ef416fc2 430
ef416fc2 431
80ca4592 432 if (!doc->pages)
433 doc->pages = cupsArrayNew(NULL, NULL);
ef416fc2 434
80ca4592 435 if (!doc->pages)
ef416fc2 436 {
f0ab5bff
MS
437 _cupsLangPrintf(stderr,
438 _("EMERG: Unable to allocate memory for pages array: %s\n"),
439 strerror(errno));
80ca4592 440 exit(1);
ef416fc2 441 }
80ca4592 442
443 if ((pageinfo = calloc(1, sizeof(pstops_page_t))) == NULL)
ef416fc2 444 {
f0ab5bff
MS
445 _cupsLangPrintf(stderr,
446 _("EMERG: Unable to allocate memory for page info: %s\n"),
447 strerror(errno));
80ca4592 448 exit(1);
449 }
ef416fc2 450
80ca4592 451 pageinfo->label = strdup(label);
452 pageinfo->offset = cupsFileTell(doc->temp);
ef416fc2 453
80ca4592 454 cupsArrayAdd(doc->pages, pageinfo);
ef416fc2 455
80ca4592 456 doc->page ++;
ef416fc2 457
80ca4592 458 return (pageinfo);
459}
ef416fc2 460
ef416fc2 461
c24d2134
MS
462/*
463 * 'cancel_job()' - Flag the job as canceled.
464 */
465
466static void
467cancel_job(int sig) /* I - Signal number (unused) */
468{
469 (void)sig;
470
471 JobCanceled = 1;
472}
473
474
80ca4592 475/*
476 * 'check_range()' - Check to see if the current page is selected for
477 * printing.
478 */
ef416fc2 479
80ca4592 480static int /* O - 1 if selected, 0 otherwise */
481check_range(pstops_doc_t *doc, /* I - Document information */
482 int page) /* I - Page number */
483{
484 const char *range; /* Pointer into range string */
485 int lower, upper; /* Lower and upper page numbers */
ef416fc2 486
ef416fc2 487
80ca4592 488 if (doc->page_set)
ef416fc2 489 {
490 /*
80ca4592 491 * See if we only print even or odd pages...
ef416fc2 492 */
493
e53920b9 494 if (!strcasecmp(doc->page_set, "even") && (page & 1))
80ca4592 495 return (0);
ef416fc2 496
e53920b9 497 if (!strcasecmp(doc->page_set, "odd") && !(page & 1))
80ca4592 498 return (0);
499 }
ef416fc2 500
80ca4592 501 if (!doc->page_ranges)
502 return (1); /* No range, print all pages... */
ef416fc2 503
80ca4592 504 for (range = doc->page_ranges; *range != '\0';)
505 {
506 if (*range == '-')
507 {
508 lower = 1;
509 range ++;
510 upper = strtol(range, (char **)&range, 10);
511 }
512 else
513 {
514 lower = strtol(range, (char **)&range, 10);
ef416fc2 515
80ca4592 516 if (*range == '-')
ef416fc2 517 {
80ca4592 518 range ++;
519 if (!isdigit(*range & 255))
520 upper = 65535;
521 else
522 upper = strtol(range, (char **)&range, 10);
ef416fc2 523 }
80ca4592 524 else
525 upper = lower;
526 }
527
528 if (page >= lower && page <= upper)
529 return (1);
530
531 if (*range == ',')
532 range ++;
533 else
534 break;
535 }
536
537 return (0);
538}
539
540
541/*
0af14961 542 * 'copy_bytes()' - Copy bytes from the input file to stdout.
80ca4592 543 */
544
545static void
546copy_bytes(cups_file_t *fp, /* I - File to read from */
547 off_t offset, /* I - Offset to page data */
548 size_t length) /* I - Length of page data */
549{
550 char buffer[8192]; /* Data buffer */
551 ssize_t nbytes; /* Number of bytes read */
552 size_t nleft; /* Number of bytes left/remaining */
553
554
555 nleft = length;
556
557 if (cupsFileSeek(fp, offset) < 0)
558 {
f0ab5bff 559 _cupsLangPrintf(stderr,
c0e1af83 560#ifdef HAVE_LONG_LONG
f0ab5bff 561 _("ERROR: Unable to seek to offset %lld in file - %s\n"),
c0e1af83 562#else
f0ab5bff 563 _("ERROR: Unable to seek to offset %ld in file - %s\n"),
c0e1af83 564#endif /* HAVE_LONG_LONG */
f0ab5bff 565 CUPS_LLCAST offset, strerror(errno));
80ca4592 566 return;
567 }
568
569 while (nleft > 0 || length == 0)
570 {
571 if (nleft > sizeof(buffer) || length == 0)
572 nbytes = sizeof(buffer);
573 else
574 nbytes = nleft;
575
576 if ((nbytes = cupsFileRead(fp, buffer, nbytes)) < 1)
577 return;
578
579 nleft -= nbytes;
580
581 fwrite(buffer, 1, nbytes, stdout);
582 }
583}
584
585
586/*
0af14961 587 * 'copy_comments()' - Copy all of the comments section.
80ca4592 588 *
589 * This function expects "line" to be filled with a comment line.
590 * On return, "line" will contain the next line in the file, if any.
591 */
592
91c84a35 593static ssize_t /* O - Length of next line */
80ca4592 594copy_comments(cups_file_t *fp, /* I - File to read from */
595 pstops_doc_t *doc, /* I - Document info */
2abf387c 596 ppd_file_t *ppd, /* I - PPD file */
80ca4592 597 char *line, /* I - Line buffer */
91c84a35 598 ssize_t linelen, /* I - Length of initial line */
80ca4592 599 size_t linesize) /* I - Size of line buffer */
600{
601 int saw_bounding_box, /* Saw %%BoundingBox: comment? */
602 saw_for, /* Saw %%For: comment? */
603 saw_pages, /* Saw %%Pages: comment? */
604 saw_title; /* Saw %%Title: comment? */
605
606
607 /*
608 * Loop until we see %%EndComments or a non-comment line...
609 */
610
611 saw_bounding_box = 0;
612 saw_for = 0;
613 saw_pages = 0;
614 saw_title = 0;
615
616 while (line[0] == '%')
617 {
618 /*
619 * Strip trailing whitespace...
620 */
621
622 while (linelen > 0)
623 {
624 linelen --;
625
626 if (!isspace(line[linelen] & 255))
627 break;
628 else
629 line[linelen] = '\0';
630 }
631
632 /*
633 * Log the header...
634 */
635
636 fprintf(stderr, "DEBUG: %s\n", line);
637
638 /*
639 * Pull the headers out...
640 */
641
642 if (!strncmp(line, "%%Pages:", 8))
643 {
2abf387c 644 int pages; /* Number of pages */
645
646
80ca4592 647 if (saw_pages)
4d301e69 648 _cupsLangPuts(stderr, _("ERROR: Duplicate %%Pages: comment seen\n"));
80ca4592 649
650 saw_pages = 1;
2abf387c 651
652 if (Duplex && (pages = atoi(line + 8)) > 0 && pages <= doc->number_up)
653 {
654 /*
655 * Since we will only be printing on a single page, disable duplexing.
656 */
657
658 Duplex = 0;
659 doc->slow_duplex = 0;
660
661 if (cupsGetOption("sides", doc->num_options, doc->options))
662 doc->num_options = cupsAddOption("sides", "one-sided",
663 doc->num_options, &(doc->options));
664
665 if (cupsGetOption("Duplex", doc->num_options, doc->options))
666 doc->num_options = cupsAddOption("Duplex", "None",
667 doc->num_options, &(doc->options));
668
669 if (cupsGetOption("EFDuplex", doc->num_options, doc->options))
670 doc->num_options = cupsAddOption("EFDuplex", "None",
671 doc->num_options, &(doc->options));
672
673 if (cupsGetOption("EFDuplexing", doc->num_options, doc->options))
674 doc->num_options = cupsAddOption("EFDuplexing", "False",
675 doc->num_options, &(doc->options));
676
677 if (cupsGetOption("KD03Duplex", doc->num_options, doc->options))
678 doc->num_options = cupsAddOption("KD03Duplex", "None",
679 doc->num_options, &(doc->options));
680
681 if (cupsGetOption("JCLDuplex", doc->num_options, doc->options))
682 doc->num_options = cupsAddOption("JCLDuplex", "None",
683 doc->num_options, &(doc->options));
684
685 ppdMarkOption(ppd, "Duplex", "None");
686 ppdMarkOption(ppd, "EFDuplex", "None");
687 ppdMarkOption(ppd, "EFDuplexing", "False");
688 ppdMarkOption(ppd, "KD03Duplex", "None");
689 ppdMarkOption(ppd, "JCLDuplex", "None");
690 }
80ca4592 691 }
692 else if (!strncmp(line, "%%BoundingBox:", 14))
693 {
694 if (saw_bounding_box)
f0ab5bff 695 _cupsLangPuts(stderr,
4d301e69 696 _("ERROR: Duplicate %%BoundingBox: comment seen\n"));
d6ae789d 697 else if (strstr(line + 14, "(atend)"))
698 {
699 /*
700 * Do nothing for now but use the default imageable area...
701 */
702 }
80ca4592 703 else if (sscanf(line + 14, "%d%d%d%d", doc->bounding_box + 0,
704 doc->bounding_box + 1, doc->bounding_box + 2,
705 doc->bounding_box + 3) != 4)
ef416fc2 706 {
4d301e69 707 _cupsLangPuts(stderr, _("ERROR: Bad %%BoundingBox: comment seen\n"));
80ca4592 708
709 doc->bounding_box[0] = (int)PageLeft;
710 doc->bounding_box[1] = (int)PageBottom;
711 doc->bounding_box[2] = (int)PageRight;
712 doc->bounding_box[3] = (int)PageTop;
ef416fc2 713 }
80ca4592 714
715 saw_bounding_box = 1;
716 }
717 else if (!strncmp(line, "%%For:", 6))
718 {
719 saw_for = 1;
b86bc4cf 720 doc_printf(doc, "%s\n", line);
80ca4592 721 }
722 else if (!strncmp(line, "%%Title:", 8))
723 {
724 saw_title = 1;
b86bc4cf 725 doc_printf(doc, "%s\n", line);
80ca4592 726 }
727 else if (!strncmp(line, "%cupsRotation:", 14))
728 {
729 /*
730 * Reset orientation of document?
731 */
732
733 int orient = (atoi(line + 14) / 90) & 3;
734
735 if (orient != Orientation)
ef416fc2 736 {
737 /*
80ca4592 738 * Yes, update things so that the pages come out right...
ef416fc2 739 */
740
80ca4592 741 Orientation = (4 - Orientation + orient) & 3;
742 UpdatePageVars();
743 Orientation = orient;
ef416fc2 744 }
80ca4592 745 }
746 else if (!strcmp(line, "%%EndComments"))
747 {
748 linelen = cupsFileGetLine(fp, line, linesize);
749 break;
750 }
751 else if (strncmp(line, "%!", 2) && strncmp(line, "%cups", 5))
b86bc4cf 752 doc_printf(doc, "%s\n", line);
80ca4592 753
754 if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
755 break;
756 }
757
758 if (!saw_bounding_box)
4d301e69 759 _cupsLangPuts(stderr, _("ERROR: No %%BoundingBox: comment in header\n"));
80ca4592 760
761 if (!saw_pages)
4d301e69 762 _cupsLangPuts(stderr, _("ERROR: No %%Pages: comment in header\n"));
80ca4592 763
764 if (!saw_for)
2abf387c 765 WriteTextComment("For", doc->user);
80ca4592 766
767 if (!saw_title)
2abf387c 768 WriteTextComment("Title", doc->title);
80ca4592 769
770 if (doc->copies != 1 && (!doc->collate || !doc->slow_collate))
771 {
772 /*
773 * Tell the document processor the copy and duplex options
774 * that are required...
775 */
776
b86bc4cf 777 doc_printf(doc, "%%%%Requirements: numcopies(%d)%s%s\n", doc->copies,
778 doc->collate ? " collate" : "",
779 Duplex ? " duplex" : "");
80ca4592 780
781 /*
782 * Apple uses RBI comments for various non-PPD options...
783 */
784
b86bc4cf 785 doc_printf(doc, "%%RBINumCopies: %d\n", doc->copies);
80ca4592 786 }
787 else
788 {
789 /*
790 * Tell the document processor the duplex option that is required...
791 */
792
793 if (Duplex)
b86bc4cf 794 doc_puts(doc, "%%Requirements: duplex\n");
80ca4592 795
796 /*
797 * Apple uses RBI comments for various non-PPD options...
798 */
799
b86bc4cf 800 doc_puts(doc, "%RBINumCopies: 1\n");
80ca4592 801 }
802
b86bc4cf 803 doc_puts(doc, "%%Pages: (atend)\n");
804 doc_puts(doc, "%%BoundingBox: (atend)\n");
805 doc_puts(doc, "%%EndComments\n");
80ca4592 806
807 return (linelen);
808}
809
810
811/*
0af14961 812 * 'copy_dsc()' - Copy a DSC-conforming document.
80ca4592 813 *
814 * This function expects "line" to be filled with the %!PS-Adobe comment line.
815 */
816
817static void
818copy_dsc(cups_file_t *fp, /* I - File to read from */
819 pstops_doc_t *doc, /* I - Document info */
820 ppd_file_t *ppd, /* I - PPD file */
821 char *line, /* I - Line buffer */
91c84a35 822 ssize_t linelen, /* I - Length of initial line */
80ca4592 823 size_t linesize) /* I - Size of line buffer */
824{
825 int number; /* Page number */
826 pstops_page_t *pageinfo; /* Page information */
827
828
829 /*
830 * Make sure we use ESPshowpage for EPS files...
831 */
832
833 if (strstr(line, "EPSF"))
834 {
835 doc->use_ESPshowpage = 1;
836 doc->number_up = 1;
837 }
838
839 /*
840 * Start sending the document with any commands needed...
841 */
842
843 fprintf(stderr, "DEBUG: Before copy_comments - %s", line);
2abf387c 844 linelen = copy_comments(fp, doc, ppd, line, linelen, linesize);
80ca4592 845
846 /*
847 * Now find the prolog section, if any...
848 */
849
850 fprintf(stderr, "DEBUG: Before copy_prolog - %s", line);
d6ae789d 851 linelen = copy_prolog(fp, doc, ppd, line, linelen, linesize);
80ca4592 852
853 /*
854 * Then the document setup section...
855 */
856
857 fprintf(stderr, "DEBUG: Before copy_setup - %s", line);
d6ae789d 858 linelen = copy_setup(fp, doc, ppd, line, linelen, linesize);
859
860 /*
861 * Copy until we see %%Page:...
862 */
863
864 while (strncmp(line, "%%Page:", 7) && strncmp(line, "%%Trailer", 9))
865 {
b86bc4cf 866 doc_write(doc, line, linelen);
d6ae789d 867
868 if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
869 break;
870 }
80ca4592 871
872 /*
873 * Then process pages until we have no more...
874 */
875
876 number = 0;
877
878 fprintf(stderr, "DEBUG: Before page loop - %s", line);
879 while (!strncmp(line, "%%Page:", 7))
880 {
c24d2134
MS
881 if (JobCanceled)
882 break;
883
80ca4592 884 number ++;
885
886 if (check_range(doc, (number - 1) / doc->number_up + 1))
887 {
888 fprintf(stderr, "DEBUG: Copying page %d...\n", number);
889 linelen = copy_page(fp, doc, ppd, number, line, linelen, linesize);
890 }
891 else
892 {
893 fprintf(stderr, "DEBUG: Skipping page %d...\n", number);
894 linelen = skip_page(fp, line, linelen, linesize);
895 }
896 }
897
898 /*
899 * Finish up the last page(s)...
900 */
901
080811b1
MS
902 if (number && is_not_last_page(number) && cupsArrayLast(doc->pages) &&
903 check_range(doc, (number - 1) / doc->number_up + 1))
80ca4592 904 {
905 pageinfo = (pstops_page_t *)cupsArrayLast(doc->pages);
906
d6ae789d 907 start_nup(doc, doc->number_up, 0, doc->bounding_box);
80ca4592 908 doc_puts(doc, "showpage\n");
d6ae789d 909 end_nup(doc, doc->number_up);
80ca4592 910
911 pageinfo->length = cupsFileTell(doc->temp) - pageinfo->offset;
912 }
913
914 if (doc->slow_duplex && (doc->page & 1))
915 {
916 /*
917 * Make sure we have an even number of pages...
918 */
919
920 pageinfo = add_page(doc, "(filler)");
921
922 if (!doc->slow_order)
923 {
924 if (!ppd || !ppd->num_filters)
925 fprintf(stderr, "PAGE: %d %d\n", doc->page,
926 doc->slow_collate ? 1 : doc->copies);
927
928 printf("%%%%Page: (filler) %d\n", doc->page);
929 }
930
d6ae789d 931 start_nup(doc, doc->number_up, 0, doc->bounding_box);
80ca4592 932 doc_puts(doc, "showpage\n");
d6ae789d 933 end_nup(doc, doc->number_up);
80ca4592 934
935 pageinfo->length = cupsFileTell(doc->temp) - pageinfo->offset;
936 }
937
938 /*
939 * Make additional copies as necessary...
940 */
941
942 number = doc->slow_order ? 0 : doc->page;
943
91c84a35 944 if (doc->temp && !JobCanceled && cupsArrayCount(doc->pages) > 0)
80ca4592 945 {
946 int copy; /* Current copy */
947
948
949 /*
950 * Reopen the temporary file for reading...
951 */
952
953 cupsFileClose(doc->temp);
954
955 doc->temp = cupsFileOpen(doc->tempfile, "r");
956
957 /*
958 * Make the copies...
959 */
960
2e4ff8af
MS
961 if (doc->slow_collate)
962 copy = !doc->slow_order;
963 else
964 copy = doc->copies - 1;
965
966 for (; copy < doc->copies; copy ++)
80ca4592 967 {
c24d2134
MS
968 if (JobCanceled)
969 break;
970
2abf387c 971 /*
972 * Send end-of-job stuff followed by any start-of-job stuff required
973 * for the JCL options...
974 */
975
b86bc4cf 976 if (number && doc->emit_jcl && ppd && ppd->jcl_end)
2abf387c 977 {
b86bc4cf 978 /*
979 * Send the trailer...
980 */
981
982 puts("%%Trailer");
983 printf("%%%%Pages: %d\n", cupsArrayCount(doc->pages));
984 if (doc->number_up > 1 || doc->fitplot)
985 printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
986 PageLeft, PageBottom, PageRight, PageTop);
2abf387c 987 else
b86bc4cf 988 printf("%%%%BoundingBox: %d %d %d %d\n",
989 doc->new_bounding_box[0], doc->new_bounding_box[1],
990 doc->new_bounding_box[2], doc->new_bounding_box[3]);
991 puts("%%EOF");
2abf387c 992
b86bc4cf 993 /*
994 * Start a new document...
995 */
996
997 ppdEmitJCLEnd(ppd, stdout);
2abf387c 998 ppdEmitJCL(ppd, stdout, doc->job_id, doc->user, doc->title);
2abf387c 999
b86bc4cf 1000 puts("%!PS-Adobe-3.0");
b86bc4cf 1001
1002 number = 0;
1003 }
2abf387c 1004
f7deaa1a 1005 /*
1006 * Copy the prolog as needed...
1007 */
1008
1009 if (!number)
1010 {
1011 pageinfo = (pstops_page_t *)cupsArrayFirst(doc->pages);
1012 copy_bytes(doc->temp, 0, pageinfo->offset);
1013 }
1014
2abf387c 1015 /*
1016 * Then copy all of the pages...
1017 */
1018
80ca4592 1019 pageinfo = doc->slow_order ? (pstops_page_t *)cupsArrayLast(doc->pages) :
1020 (pstops_page_t *)cupsArrayFirst(doc->pages);
1021
1022 while (pageinfo)
ef416fc2 1023 {
c24d2134
MS
1024 if (JobCanceled)
1025 break;
1026
80ca4592 1027 number ++;
ef416fc2 1028
80ca4592 1029 if (!ppd || !ppd->num_filters)
2e4ff8af
MS
1030 fprintf(stderr, "PAGE: %d %d\n", number,
1031 doc->slow_collate ? 1 : doc->copies);
ef416fc2 1032
80ca4592 1033 if (doc->number_up > 1)
1034 {
1035 printf("%%%%Page: (%d) %d\n", number, number);
1036 printf("%%%%PageBoundingBox: %.0f %.0f %.0f %.0f\n",
1037 PageLeft, PageBottom, PageRight, PageTop);
1038 }
1039 else
ef416fc2 1040 {
411affcf 1041 printf("%%%%Page: %s %d\n", pageinfo->label, number);
80ca4592 1042 printf("%%%%PageBoundingBox: %d %d %d %d\n",
1043 pageinfo->bounding_box[0], pageinfo->bounding_box[1],
1044 pageinfo->bounding_box[2], pageinfo->bounding_box[3]);
ef416fc2 1045 }
80ca4592 1046
1047 copy_bytes(doc->temp, pageinfo->offset, pageinfo->length);
1048
1049 pageinfo = doc->slow_order ? (pstops_page_t *)cupsArrayPrev(doc->pages) :
1050 (pstops_page_t *)cupsArrayNext(doc->pages);
ef416fc2 1051 }
80ca4592 1052 }
1053 }
1054
f301802f 1055 /*
1056 * Restore the old showpage operator as needed...
1057 */
1058
1059 if (doc->use_ESPshowpage)
1060 puts("userdict/showpage/ESPshowpage load put\n");
1061
80ca4592 1062 /*
1063 * Write/copy the trailer...
1064 */
1065
c24d2134 1066 if (!JobCanceled)
1f0275e3 1067 copy_trailer(fp, doc, ppd, number, line, linelen, linesize);
80ca4592 1068}
1069
1070
1071/*
0af14961 1072 * 'copy_non_dsc()' - Copy a document that does not conform to the DSC.
80ca4592 1073 *
1074 * This function expects "line" to be filled with the %! comment line.
1075 */
1076
1077static void
1078copy_non_dsc(cups_file_t *fp, /* I - File to read from */
1079 pstops_doc_t *doc, /* I - Document info */
1080 ppd_file_t *ppd, /* I - PPD file */
1081 char *line, /* I - Line buffer */
91c84a35 1082 ssize_t linelen, /* I - Length of initial line */
80ca4592 1083 size_t linesize) /* I - Size of line buffer */
1084{
1085 int copy; /* Current copy */
1086 char buffer[8192]; /* Copy buffer */
1087 int bytes; /* Number of bytes copied */
1088
1089
1090 /*
1091 * First let the user know that they are attempting to print a file
1092 * that may not print correctly...
1093 */
1094
f0ab5bff
MS
1095 _cupsLangPuts(stderr,
1096 _("WARNING: This document does not conform to the Adobe "
1097 "Document Structuring Conventions and may not print "
4d301e69 1098 "correctly\n"));
80ca4592 1099
1100 /*
1101 * Then write a standard DSC comment section...
1102 */
1103
1104 printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n", PageLeft, PageBottom,
1105 PageRight, PageTop);
1106
1107 if (doc->slow_collate && doc->copies > 1)
1108 printf("%%%%Pages: %d\n", doc->copies);
1109 else
1110 puts("%%Pages: 1");
1111
2abf387c 1112 WriteTextComment("For", doc->user);
1113 WriteTextComment("Title", doc->title);
80ca4592 1114
1115 if (doc->copies != 1 && (!doc->collate || !doc->slow_collate))
1116 {
1117 /*
1118 * Tell the document processor the copy and duplex options
1119 * that are required...
1120 */
1121
1122 printf("%%%%Requirements: numcopies(%d)%s%s\n", doc->copies,
1123 doc->collate ? " collate" : "",
1124 Duplex ? " duplex" : "");
1125
1126 /*
1127 * Apple uses RBI comments for various non-PPD options...
1128 */
1129
1130 printf("%%RBINumCopies: %d\n", doc->copies);
1131 }
1132 else
1133 {
1134 /*
1135 * Tell the document processor the duplex option that is required...
1136 */
1137
1138 if (Duplex)
1139 puts("%%Requirements: duplex");
1140
1141 /*
1142 * Apple uses RBI comments for various non-PPD options...
1143 */
1144
1145 puts("%RBINumCopies: 1");
1146 }
1147
1148 puts("%%EndComments");
1149
1150 /*
1151 * Then the prolog...
1152 */
1153
1154 puts("%%BeginProlog");
1155
1156 do_prolog(doc, ppd);
1157
1158 puts("%%EndProlog");
1159
1160 /*
1161 * Then the setup section...
1162 */
1163
1164 puts("%%BeginSetup");
1165
1166 do_setup(doc, ppd);
1167
1168 puts("%%EndSetup");
1169
1170 /*
1171 * Finally, embed a copy of the file inside a %%Page...
1172 */
1173
1174 if (!ppd || !ppd->num_filters)
1175 fprintf(stderr, "PAGE: 1 %d\n", doc->temp ? 1 : doc->copies);
1176
1177 puts("%%Page: 1 1");
1178 puts("%%BeginPageSetup");
1179 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
1180 puts("%%EndPageSetup");
1181 puts("%%BeginDocument: nondsc");
1182
1183 fwrite(line, linelen, 1, stdout);
1184
1185 if (doc->temp)
1186 cupsFileWrite(doc->temp, line, linelen);
1187
1188 while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
1189 {
1190 fwrite(buffer, 1, bytes, stdout);
1191
1192 if (doc->temp)
1193 cupsFileWrite(doc->temp, buffer, bytes);
1194 }
1195
1196 puts("%%EndDocument");
1197
1198 if (doc->use_ESPshowpage)
1199 {
1200 WriteLabels(Orientation);
1201 puts("ESPshowpage");
1202 }
1203
c24d2134 1204 if (doc->temp && !JobCanceled)
80ca4592 1205 {
1206 /*
1207 * Reopen the temporary file for reading...
1208 */
1209
1210 cupsFileClose(doc->temp);
1211
1212 doc->temp = cupsFileOpen(doc->tempfile, "r");
1213
1214 /*
1215 * Make the additional copies as needed...
1216 */
1217
1218 for (copy = 1; copy < doc->copies; copy ++)
1219 {
c24d2134
MS
1220 if (JobCanceled)
1221 break;
1222
80ca4592 1223 if (!ppd || !ppd->num_filters)
1224 fputs("PAGE: 1 1\n", stderr);
1225
1226 printf("%%%%Page: %d %d\n", copy + 1, copy + 1);
1227 puts("%%BeginPageSetup");
1228 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
1229 puts("%%EndPageSetup");
1230 puts("%%BeginDocument: nondsc");
1231
1232 copy_bytes(doc->temp, 0, 0);
1233
1234 puts("%%EndDocument");
1235
1236 if (doc->use_ESPshowpage)
1237 {
1238 WriteLabels(Orientation);
1239 puts("ESPshowpage");
1240 }
1241 }
1242 }
f301802f 1243
1244 /*
1245 * Restore the old showpage operator as needed...
1246 */
1247
1248 if (doc->use_ESPshowpage)
1249 puts("userdict/showpage/ESPshowpage load put\n");
80ca4592 1250}
1251
1252
1253/*
0af14961 1254 * 'copy_page()' - Copy a page description.
80ca4592 1255 *
1256 * This function expects "line" to be filled with a %%Page comment line.
1257 * On return, "line" will contain the next line in the file, if any.
1258 */
1259
91c84a35 1260static ssize_t /* O - Length of next line */
80ca4592 1261copy_page(cups_file_t *fp, /* I - File to read from */
1262 pstops_doc_t *doc, /* I - Document info */
1263 ppd_file_t *ppd, /* I - PPD file */
1264 int number, /* I - Current page number */
1265 char *line, /* I - Line buffer */
91c84a35 1266 ssize_t linelen, /* I - Length of initial line */
80ca4592 1267 size_t linesize) /* I - Size of line buffer */
1268{
1269 char label[256], /* Page label string */
1270 *ptr; /* Pointer into line */
1271 int level; /* Embedded document level */
1272 pstops_page_t *pageinfo; /* Page information */
1273 int first_page; /* First page on N-up output? */
75bd9771 1274 int has_page_setup; /* Does the page have %%Begin/EndPageSetup? */
80ca4592 1275 int bounding_box[4]; /* PageBoundingBox */
1276
1277
1278 /*
1279 * Get the page label for this page...
1280 */
1281
1282 first_page = is_first_page(number);
1283
1284 if (!parse_text(line + 7, &ptr, label, sizeof(label)))
1285 {
4d301e69 1286 _cupsLangPuts(stderr, _("ERROR: Bad %%Page: comment in file\n"));
80ca4592 1287 label[0] = '\0';
1288 number = doc->page;
1289 }
1290 else if (strtol(ptr, &ptr, 10) == LONG_MAX || !isspace(*ptr & 255))
1291 {
4d301e69 1292 _cupsLangPuts(stderr, _("ERROR: Bad %%Page: comment in file\n"));
80ca4592 1293 number = doc->page;
1294 }
1295
1296 /*
1297 * Create or update the current output page...
1298 */
1299
1300 if (first_page)
1301 pageinfo = add_page(doc, label);
1302 else
1303 pageinfo = (pstops_page_t *)cupsArrayLast(doc->pages);
1304
1305 /*
1306 * Handle first page override...
1307 */
1308
1309 if (doc->ap_input_slot || doc->ap_manual_feed)
1310 {
1311 if (doc->page == 1)
1312 {
1313 /*
1314 * First page/sheet gets AP_FIRSTPAGE_* options...
1315 */
1316
1317 pageinfo->num_options = cupsAddOption("InputSlot", doc->ap_input_slot,
1318 pageinfo->num_options,
1319 &(pageinfo->options));
c0e1af83 1320 pageinfo->num_options = cupsAddOption("ManualFeed",
1321 doc->ap_input_slot ? "False" :
1322 doc->ap_manual_feed,
80ca4592 1323 pageinfo->num_options,
1324 &(pageinfo->options));
1325 }
1326 else if (doc->page == (Duplex + 2))
1327 {
1328 /*
1329 * Second page/sheet gets default options...
1330 */
1331
1332 pageinfo->num_options = cupsAddOption("InputSlot", doc->input_slot,
1333 pageinfo->num_options,
1334 &(pageinfo->options));
c0e1af83 1335 pageinfo->num_options = cupsAddOption("ManualFeed",
1336 doc->input_slot ? "False" :
1337 doc->manual_feed,
80ca4592 1338 pageinfo->num_options,
1339 &(pageinfo->options));
1340 }
1341 }
1342
1343 /*
1344 * Scan comments until we see something other than %%Page*: or
1345 * %%Include*...
1346 */
1347
1348 memcpy(bounding_box, doc->bounding_box, sizeof(bounding_box));
1349
1350 while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0)
1351 {
1352 if (!strncmp(line, "%%PageBoundingBox:", 18))
1353 {
1354 /*
1355 * %%PageBoundingBox: llx lly urx ury
1356 */
1357
1358 if (sscanf(line + 18, "%d%d%d%d", bounding_box + 0,
1359 bounding_box + 1, bounding_box + 2,
1360 bounding_box + 3) != 4)
1361 {
f0ab5bff 1362 _cupsLangPuts(stderr,
4d301e69 1363 _("ERROR: Bad %%PageBoundingBox: comment in file\n"));
80ca4592 1364 memcpy(bounding_box, doc->bounding_box,
1365 sizeof(bounding_box));
1366 }
b86bc4cf 1367 else if (doc->number_up == 1 && !doc->fitplot && Orientation)
411affcf 1368 {
1369 int temp_bbox[4]; /* Temporary bounding box */
1370
1371
b86bc4cf 1372 memcpy(temp_bbox, bounding_box, sizeof(temp_bbox));
1373
1374 fprintf(stderr, "DEBUG: Orientation = %d\n", Orientation);
1375 fprintf(stderr, "DEBUG: original bounding_box = [ %d %d %d %d ]\n",
1376 bounding_box[0], bounding_box[1],
1377 bounding_box[2], bounding_box[3]);
1378 fprintf(stderr, "DEBUG: PageWidth = %.1f, PageLength = %.1f\n",
1379 PageWidth, PageLength);
1380
411affcf 1381 switch (Orientation)
1382 {
411affcf 1383 case 1 : /* Landscape */
b86bc4cf 1384 bounding_box[0] = PageLength - temp_bbox[3];
1385 bounding_box[1] = temp_bbox[0];
1386 bounding_box[2] = PageLength - temp_bbox[1];
1387 bounding_box[3] = temp_bbox[2];
411affcf 1388 break;
1389
1390 case 2 : /* Reverse Portrait */
b86bc4cf 1391 bounding_box[0] = PageWidth - temp_bbox[2];
1392 bounding_box[1] = PageLength - temp_bbox[3];
1393 bounding_box[2] = PageWidth - temp_bbox[0];
1394 bounding_box[3] = PageLength - temp_bbox[1];
411affcf 1395 break;
1396
1397 case 3 : /* Reverse Landscape */
b86bc4cf 1398 bounding_box[0] = temp_bbox[1];
1399 bounding_box[1] = PageWidth - temp_bbox[2];
1400 bounding_box[2] = temp_bbox[3];
1401 bounding_box[3] = PageWidth - temp_bbox[0];
411affcf 1402 break;
1403 }
b86bc4cf 1404
1405 fprintf(stderr, "DEBUG: updated bounding_box = [ %d %d %d %d ]\n",
1406 bounding_box[0], bounding_box[1],
1407 bounding_box[2], bounding_box[3]);
411affcf 1408 }
80ca4592 1409 }
1410#if 0
1411 else if (!strncmp(line, "%%PageCustomColors:", 19) ||
1412 !strncmp(line, "%%PageMedia:", 12) ||
1413 !strncmp(line, "%%PageOrientation:", 18) ||
1414 !strncmp(line, "%%PageProcessColors:", 20) ||
1415 !strncmp(line, "%%PageRequirements:", 18) ||
1416 !strncmp(line, "%%PageResources:", 16))
1417 {
1418 /*
1419 * Copy literal...
1420 */
1421 }
1422#endif /* 0 */
1423 else if (!strncmp(line, "%%PageCustomColors:", 19))
1424 {
1425 /*
1426 * %%PageCustomColors: ...
1427 */
1428 }
1429 else if (!strncmp(line, "%%PageMedia:", 12))
1430 {
1431 /*
1432 * %%PageMedia: ...
1433 */
1434 }
1435 else if (!strncmp(line, "%%PageOrientation:", 18))
1436 {
1437 /*
1438 * %%PageOrientation: ...
1439 */
1440 }
1441 else if (!strncmp(line, "%%PageProcessColors:", 20))
1442 {
1443 /*
1444 * %%PageProcessColors: ...
1445 */
1446 }
1447 else if (!strncmp(line, "%%PageRequirements:", 18))
1448 {
1449 /*
1450 * %%PageRequirements: ...
1451 */
1452 }
1453 else if (!strncmp(line, "%%PageResources:", 16))
1454 {
1455 /*
1456 * %%PageResources: ...
1457 */
1458 }
1459 else if (!strncmp(line, "%%IncludeFeature:", 17))
1460 {
1461 /*
1462 * %%IncludeFeature: *MainKeyword OptionKeyword
1463 */
1464
1465 if (doc->number_up == 1 &&!doc->fitplot)
1466 pageinfo->num_options = include_feature(ppd, line,
1467 pageinfo->num_options,
1468 &(pageinfo->options));
1469 }
1470 else if (strncmp(line, "%%Include", 9))
1471 break;
1472 }
1473
1474 if (doc->number_up == 1)
1475 {
1476 /*
1477 * Update the document's composite and page bounding box...
1478 */
1479
1480 memcpy(pageinfo->bounding_box, bounding_box,
1481 sizeof(pageinfo->bounding_box));
1482
1483 if (bounding_box[0] < doc->new_bounding_box[0])
1484 doc->new_bounding_box[0] = bounding_box[0];
1485 if (bounding_box[1] < doc->new_bounding_box[1])
1486 doc->new_bounding_box[1] = bounding_box[1];
1487 if (bounding_box[2] > doc->new_bounding_box[2])
1488 doc->new_bounding_box[2] = bounding_box[2];
1489 if (bounding_box[3] > doc->new_bounding_box[3])
1490 doc->new_bounding_box[3] = bounding_box[3];
1491 }
1492
1493 /*
1494 * Output the page header as needed...
1495 */
1496
1497 if (!doc->slow_order && first_page)
1498 {
1499 if (!ppd || !ppd->num_filters)
1500 fprintf(stderr, "PAGE: %d %d\n", doc->page,
1501 doc->slow_collate ? 1 : doc->copies);
1502
1503 if (doc->number_up > 1)
1504 {
1505 printf("%%%%Page: (%d) %d\n", doc->page, doc->page);
1506 printf("%%%%PageBoundingBox: %.0f %.0f %.0f %.0f\n",
1507 PageLeft, PageBottom, PageRight, PageTop);
1508 }
1509 else
1510 {
1511 printf("%%%%Page: %s %d\n", pageinfo->label, doc->page);
1512 printf("%%%%PageBoundingBox: %d %d %d %d\n",
1513 pageinfo->bounding_box[0], pageinfo->bounding_box[1],
1514 pageinfo->bounding_box[2], pageinfo->bounding_box[3]);
1515 }
1516 }
1517
1518 /*
1519 * Copy any page setup commands...
1520 */
1521
e53920b9 1522 if (first_page)
dd1abb6b
MS
1523 doc_puts(doc, "%%BeginPageSetup\n");
1524
75bd9771 1525 if ((has_page_setup = !strncmp(line, "%%BeginPageSetup", 16)) != 0)
80ca4592 1526 {
dd1abb6b
MS
1527 int feature = 0; /* In a Begin/EndFeature block? */
1528
1529 while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0)
1530 {
1531 if (!strncmp(line, "%%EndPageSetup", 14))
1532 break;
1533 else if (!strncmp(line, "%%BeginFeature:", 15))
1534 {
1535 feature = 1;
ed486911 1536
dd1abb6b
MS
1537 if (doc->number_up > 1 || doc->fitplot)
1538 continue;
1539 }
1540 else if (!strncmp(line, "%%EndFeature", 12))
1541 {
1542 feature = 0;
1543
1544 if (doc->number_up > 1 || doc->fitplot)
1545 continue;
1546 }
1547 else if (!strncmp(line, "%%IncludeFeature:", 17))
1548 {
1549 pageinfo->num_options = include_feature(ppd, line,
1550 pageinfo->num_options,
1551 &(pageinfo->options));
1552 continue;
1553 }
1554 else if (!strncmp(line, "%%Include", 9))
1555 continue;
1556
75bd9771
MS
1557 if (line[0] != '%' && !feature)
1558 break;
1559
dd1abb6b
MS
1560 if (!feature || (doc->number_up == 1 && !doc->fitplot))
1561 doc_write(doc, line, linelen);
1562 }
1563
1564 /*
1565 * Skip %%EndPageSetup...
1566 */
1567
75bd9771
MS
1568 if (linelen > 0 && !strncmp(line, "%%EndPageSetup", 14))
1569 {
1570 linelen = cupsFileGetLine(fp, line, linesize);
1571 has_page_setup = 0;
1572 }
dd1abb6b
MS
1573 }
1574
1575 if (first_page)
1576 {
1577 char *page_setup; /* PageSetup commands to send */
ed486911 1578
80ca4592 1579
e53920b9 1580 if (pageinfo->num_options > 0)
0af14961 1581 write_options(doc, ppd, pageinfo->num_options, pageinfo->options);
80ca4592 1582
ed486911 1583 /*
1584 * Output commands for the current page...
1585 */
80ca4592 1586
ed486911 1587 page_setup = ppdEmitString(ppd, PPD_ORDER_PAGE, 0);
80ca4592 1588
ed486911 1589 if (page_setup)
1590 {
1591 doc_puts(doc, page_setup);
1592 free(page_setup);
e53920b9 1593 }
1594 }
80ca4592 1595
e53920b9 1596 /*
1597 * Prep for the start of the page description...
1598 */
80ca4592 1599
e53920b9 1600 start_nup(doc, number, 1, bounding_box);
80ca4592 1601
80ca4592 1602 /*
e53920b9 1603 * Finish the PageSetup section as needed...
80ca4592 1604 */
1605
75bd9771
MS
1606 if (has_page_setup)
1607 {
1608 int feature = 0; /* In a Begin/EndFeature block? */
1609
1610 doc_write(doc, line, linelen);
1611
1612 while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0)
1613 {
1614 if (!strncmp(line, "%%EndPageSetup", 14))
1615 break;
1616 else if (!strncmp(line, "%%BeginFeature:", 15))
1617 {
1618 feature = 1;
1619
1620 if (doc->number_up > 1 || doc->fitplot)
1621 continue;
1622 }
1623 else if (!strncmp(line, "%%EndFeature", 12))
1624 {
1625 feature = 0;
1626
1627 if (doc->number_up > 1 || doc->fitplot)
1628 continue;
1629 }
1630 else if (!strncmp(line, "%%Include", 9))
1631 continue;
1632
1633 if (!feature || (doc->number_up == 1 && !doc->fitplot))
1634 doc_write(doc, line, linelen);
1635 }
1636
1637 /*
1638 * Skip %%EndPageSetup...
1639 */
1640
1641 if (linelen > 0 && !strncmp(line, "%%EndPageSetup", 14))
1642 linelen = cupsFileGetLine(fp, line, linesize);
1643 }
1644
e53920b9 1645 if (first_page)
1646 doc_puts(doc, "%%EndPageSetup\n");
80ca4592 1647
1648 /*
1649 * Read the rest of the page description...
1650 */
1651
1652 level = 0;
1653
1654 do
1655 {
1656 if (level == 0 &&
1657 (!strncmp(line, "%%Page:", 7) ||
d6ae789d 1658 !strncmp(line, "%%Trailer", 9) ||
80ca4592 1659 !strncmp(line, "%%EOF", 5)))
1660 break;
1661 else if (!strncmp(line, "%%BeginDocument", 15) ||
1662 !strncmp(line, "%ADO_BeginApplication", 21))
1663 {
1664 doc_write(doc, line, linelen);
1665
1666 level ++;
1667 }
1668 else if ((!strncmp(line, "%%EndDocument", 13) ||
1669 !strncmp(line, "%ADO_EndApplication", 19)) && level > 0)
1670 {
1671 doc_write(doc, line, linelen);
1672
1673 level --;
1674 }
1675 else if (!strncmp(line, "%%BeginBinary:", 14) ||
1676 (!strncmp(line, "%%BeginData:", 12) &&
1677 !strstr(line, "ASCII") && !strstr(line, "Hex")))
1678 {
1679 /*
1680 * Copy binary data...
1681 */
ef416fc2 1682
80ca4592 1683 int bytes; /* Bytes of data */
ef416fc2 1684
ef416fc2 1685
80ca4592 1686 doc_write(doc, line, linelen);
ef416fc2 1687
80ca4592 1688 bytes = atoi(strchr(line, ':') + 1);
1689
1690 while (bytes > 0)
1691 {
1692 if (bytes > linesize)
1693 linelen = cupsFileRead(fp, line, linesize);
1694 else
1695 linelen = cupsFileRead(fp, line, bytes);
ef416fc2 1696
80ca4592 1697 if (linelen < 1)
ef416fc2 1698 {
80ca4592 1699 line[0] = '\0';
1700 perror("ERROR: Early end-of-file while reading binary data");
1701 return (0);
1702 }
ef416fc2 1703
80ca4592 1704 doc_write(doc, line, linelen);
ef416fc2 1705
80ca4592 1706 bytes -= linelen;
ef416fc2 1707 }
ef416fc2 1708 }
80ca4592 1709 else
1710 doc_write(doc, line, linelen);
1711 }
1712 while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0);
ef416fc2 1713
80ca4592 1714 /*
1715 * Finish up this page and return...
1716 */
ef416fc2 1717
80ca4592 1718 end_nup(doc, number);
ef416fc2 1719
80ca4592 1720 pageinfo->length = cupsFileTell(doc->temp) - pageinfo->offset;
ef416fc2 1721
80ca4592 1722 return (linelen);
1723}
ef416fc2 1724
ef416fc2 1725
80ca4592 1726/*
0af14961 1727 * 'copy_prolog()' - Copy the document prolog section.
80ca4592 1728 *
1729 * This function expects "line" to be filled with a %%BeginProlog comment line.
1730 * On return, "line" will contain the next line in the file, if any.
1731 */
ef416fc2 1732
91c84a35 1733static ssize_t /* O - Length of next line */
80ca4592 1734copy_prolog(cups_file_t *fp, /* I - File to read from */
1735 pstops_doc_t *doc, /* I - Document info */
1736 ppd_file_t *ppd, /* I - PPD file */
1737 char *line, /* I - Line buffer */
91c84a35 1738 ssize_t linelen, /* I - Length of initial line */
80ca4592 1739 size_t linesize) /* I - Size of line buffer */
1740{
1741 while (strncmp(line, "%%BeginProlog", 13))
1742 {
1743 if (!strncmp(line, "%%BeginSetup", 12) || !strncmp(line, "%%Page:", 7))
1744 break;
ef416fc2 1745
b86bc4cf 1746 doc_write(doc, line, linelen);
ef416fc2 1747
80ca4592 1748 if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
1749 break;
1750 }
ef416fc2 1751
b86bc4cf 1752 doc_puts(doc, "%%BeginProlog\n");
ef416fc2 1753
80ca4592 1754 do_prolog(doc, ppd);
ef416fc2 1755
80ca4592 1756 if (!strncmp(line, "%%BeginProlog", 13))
1757 {
1758 while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0)
ef416fc2 1759 {
80ca4592 1760 if (!strncmp(line, "%%EndProlog", 11) ||
1761 !strncmp(line, "%%BeginSetup", 12) ||
1762 !strncmp(line, "%%Page:", 7))
1763 break;
ef416fc2 1764
b86bc4cf 1765 doc_write(doc, line, linelen);
ef416fc2 1766 }
80ca4592 1767
1768 if (!strncmp(line, "%%EndProlog", 11))
1769 linelen = cupsFileGetLine(fp, line, linesize);
ef416fc2 1770 else
4d301e69 1771 _cupsLangPuts(stderr, _("ERROR: Missing %%EndProlog\n"));
80ca4592 1772 }
ef416fc2 1773
b86bc4cf 1774 doc_puts(doc, "%%EndProlog\n");
ef416fc2 1775
80ca4592 1776 return (linelen);
1777}
ef416fc2 1778
bd7854cb 1779
80ca4592 1780/*
0af14961 1781 * 'copy_setup()' - Copy the document setup section.
80ca4592 1782 *
1783 * This function expects "line" to be filled with a %%BeginSetup comment line.
1784 * On return, "line" will contain the next line in the file, if any.
1785 */
bd7854cb 1786
91c84a35 1787static ssize_t /* O - Length of next line */
80ca4592 1788copy_setup(cups_file_t *fp, /* I - File to read from */
1789 pstops_doc_t *doc, /* I - Document info */
1790 ppd_file_t *ppd, /* I - PPD file */
1791 char *line, /* I - Line buffer */
91c84a35 1792 ssize_t linelen, /* I - Length of initial line */
80ca4592 1793 size_t linesize) /* I - Size of line buffer */
1794{
0af14961
MS
1795 int num_options; /* Number of options */
1796 cups_option_t *options; /* Options */
1797
1798
80ca4592 1799 while (strncmp(line, "%%BeginSetup", 12))
1800 {
1801 if (!strncmp(line, "%%Page:", 7))
1802 break;
bd7854cb 1803
b86bc4cf 1804 doc_write(doc, line, linelen);
bd7854cb 1805
80ca4592 1806 if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
1807 break;
1808 }
1809
b86bc4cf 1810 doc_puts(doc, "%%BeginSetup\n");
d09495fa 1811
0af14961
MS
1812 do_setup(doc, ppd);
1813
1814 num_options = 0;
1815 options = NULL;
1816
80ca4592 1817 if (!strncmp(line, "%%BeginSetup", 12))
1818 {
1819 while (strncmp(line, "%%EndSetup", 10))
1820 {
1821 if (!strncmp(line, "%%Page:", 7))
ef416fc2 1822 break;
80ca4592 1823 else if (!strncmp(line, "%%IncludeFeature:", 17))
ef416fc2 1824 {
80ca4592 1825 /*
1826 * %%IncludeFeature: *MainKeyword OptionKeyword
1827 */
ef416fc2 1828
80ca4592 1829 if (doc->number_up == 1 && !doc->fitplot)
0af14961 1830 num_options = include_feature(ppd, line, num_options, &options);
80ca4592 1831 }
d09495fa 1832 else if (strncmp(line, "%%BeginSetup", 12))
b86bc4cf 1833 doc_write(doc, line, linelen);
ef416fc2 1834
80ca4592 1835 if ((linelen = cupsFileGetLine(fp, line, linesize)) == 0)
1836 break;
1837 }
bd7854cb 1838
80ca4592 1839 if (!strncmp(line, "%%EndSetup", 10))
1840 linelen = cupsFileGetLine(fp, line, linesize);
1841 else
4d301e69 1842 _cupsLangPuts(stderr, _("ERROR: Missing %%EndSetup\n"));
80ca4592 1843 }
ef416fc2 1844
0af14961
MS
1845 if (num_options > 0)
1846 {
1847 write_options(doc, ppd, num_options, options);
1848 cupsFreeOptions(num_options, options);
1849 }
dd1abb6b 1850
b86bc4cf 1851 doc_puts(doc, "%%EndSetup\n");
ef416fc2 1852
80ca4592 1853 return (linelen);
1854}
ef416fc2 1855
ef416fc2 1856
80ca4592 1857/*
0af14961 1858 * 'copy_trailer()' - Copy the document trailer.
80ca4592 1859 *
1860 * This function expects "line" to be filled with a %%Trailer comment line.
1861 * On return, "line" will contain the next line in the file, if any.
1862 */
ef416fc2 1863
91c84a35 1864static ssize_t /* O - Length of next line */
80ca4592 1865copy_trailer(cups_file_t *fp, /* I - File to read from */
1866 pstops_doc_t *doc, /* I - Document info */
1867 ppd_file_t *ppd, /* I - PPD file */
1868 int number, /* I - Number of pages */
1869 char *line, /* I - Line buffer */
91c84a35 1870 ssize_t linelen, /* I - Length of initial line */
80ca4592 1871 size_t linesize) /* I - Size of line buffer */
1872{
1873 /*
1874 * Write the trailer comments...
1875 */
ef416fc2 1876
80ca4592 1877 puts("%%Trailer");
ef416fc2 1878
80ca4592 1879 while (linelen > 0)
1880 {
1881 if (!strncmp(line, "%%EOF", 5))
1882 break;
1883 else if (strncmp(line, "%%Trailer", 9) &&
1884 strncmp(line, "%%Pages:", 8) &&
1885 strncmp(line, "%%BoundingBox:", 14))
1886 fwrite(line, 1, linelen, stdout);
ef416fc2 1887
80ca4592 1888 linelen = cupsFileGetLine(fp, line, linesize);
1889 }
ef416fc2 1890
80ca4592 1891 fprintf(stderr, "DEBUG: Wrote %d pages...\n", number);
ef416fc2 1892
80ca4592 1893 printf("%%%%Pages: %d\n", number);
1894 if (doc->number_up > 1 || doc->fitplot)
1895 printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
1896 PageLeft, PageBottom, PageRight, PageTop);
1897 else
1898 printf("%%%%BoundingBox: %d %d %d %d\n",
1899 doc->new_bounding_box[0], doc->new_bounding_box[1],
1900 doc->new_bounding_box[2], doc->new_bounding_box[3]);
ef416fc2 1901
80ca4592 1902 return (linelen);
1903}
ef416fc2 1904
ef416fc2 1905
80ca4592 1906/*
0af14961 1907 * 'do_prolog()' - Send the necessary document prolog commands.
80ca4592 1908 */
ef416fc2 1909
80ca4592 1910static void
1911do_prolog(pstops_doc_t *doc, /* I - Document information */
1912 ppd_file_t *ppd) /* I - PPD file */
1913{
b86bc4cf 1914 char *ps; /* PS commands */
1915
1916
80ca4592 1917 /*
1918 * Send the document prolog commands...
1919 */
ef416fc2 1920
80ca4592 1921 if (ppd && ppd->patches)
1922 {
b86bc4cf 1923 doc_puts(doc, "%%BeginFeature: *JobPatchFile 1\n");
1924 doc_puts(doc, ppd->patches);
1925 doc_puts(doc, "\n%%EndFeature\n");
80ca4592 1926 }
bd7854cb 1927
b86bc4cf 1928 if ((ps = ppdEmitString(ppd, PPD_ORDER_PROLOG, 0.0)) != NULL)
1929 {
1930 doc_puts(doc, ps);
1931 free(ps);
1932 }
bd7854cb 1933
80ca4592 1934 /*
1935 * Define ESPshowpage here so that applications that define their
1936 * own procedure to do a showpage pick it up...
1937 */
ef416fc2 1938
80ca4592 1939 if (doc->use_ESPshowpage)
b86bc4cf 1940 doc_puts(doc, "userdict/ESPshowpage/showpage load put\n"
1941 "userdict/showpage{}put\n");
80ca4592 1942}
ef416fc2 1943
ef416fc2 1944
80ca4592 1945/*
0af14961 1946 * 'do_setup()' - Send the necessary document setup commands.
80ca4592 1947 */
ef416fc2 1948
80ca4592 1949static void
1950do_setup(pstops_doc_t *doc, /* I - Document information */
1951 ppd_file_t *ppd) /* I - PPD file */
1952{
b86bc4cf 1953 char *ps; /* PS commands */
1954
1955
f301802f 1956 /*
1957 * Disable CTRL-D so that embedded files don't cause printing
1958 * errors...
1959 */
1960
b86bc4cf 1961 doc_puts(doc, "% Disable CTRL-D as an end-of-file marker...\n");
1962 doc_puts(doc, "userdict dup(\\004)cvn{}put (\\004\\004)cvn{}put\n");
f301802f 1963
80ca4592 1964 /*
0af14961 1965 * Mark job options...
80ca4592 1966 */
ef416fc2 1967
80ca4592 1968 cupsMarkOptions(ppd, doc->num_options, doc->options);
ef416fc2 1969
80ca4592 1970 /*
1971 * Send all the printer-specific setup commands...
1972 */
ef416fc2 1973
b86bc4cf 1974 if ((ps = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, 0.0)) != NULL)
1975 {
1976 doc_puts(doc, ps);
1977 free(ps);
1978 }
1979
1980 if ((ps = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL)
1981 {
1982 doc_puts(doc, ps);
1983 free(ps);
1984 }
ef416fc2 1985
80ca4592 1986 /*
1987 * Set the number of copies for the job...
1988 */
ef416fc2 1989
80ca4592 1990 if (doc->copies != 1 && (!doc->collate || !doc->slow_collate))
1991 {
b86bc4cf 1992 doc_printf(doc, "%%RBIBeginNonPPDFeature: *NumCopies %d\n", doc->copies);
1993 doc_printf(doc,
1994 "%d/languagelevel where{pop languagelevel 2 ge}{false}ifelse\n"
1995 "{1 dict begin/NumCopies exch def currentdict end "
1996 "setpagedevice}\n"
1997 "{userdict/#copies 3 -1 roll put}ifelse\n", doc->copies);
1998 doc_puts(doc, "%RBIEndNonPPDFeature\n");
80ca4592 1999 }
ef416fc2 2000
80ca4592 2001 /*
2002 * If we are doing N-up printing, disable setpagedevice...
2003 */
ef416fc2 2004
80ca4592 2005 if (doc->number_up > 1)
e07d4801
MS
2006 {
2007 doc_puts(doc, "userdict/CUPSsetpagedevice/setpagedevice load put\n");
b86bc4cf 2008 doc_puts(doc, "userdict/setpagedevice{pop}bind put\n");
e07d4801 2009 }
ef416fc2 2010
80ca4592 2011 /*
2012 * Changes to the transfer function must be made AFTER any
2013 * setpagedevice code...
2014 */
ef416fc2 2015
80ca4592 2016 if (doc->gamma != 1.0f || doc->brightness != 1.0f)
b86bc4cf 2017 doc_printf(doc, "{ neg 1 add dup 0 lt { pop 1 } { %.3f exp neg 1 add } "
2018 "ifelse %.3f mul } bind settransfer\n",
2019 doc->gamma, doc->brightness);
ef416fc2 2020
80ca4592 2021 /*
2022 * Make sure we have rectclip and rectstroke procedures of some sort...
2023 */
ef416fc2 2024
b86bc4cf 2025 doc_puts(doc,
2026 "% x y w h ESPrc - Clip to a rectangle.\n"
2027 "userdict/ESPrc/rectclip where{pop/rectclip load}\n"
2028 "{{newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
2029 "neg 0 rlineto closepath clip newpath}bind}ifelse put\n");
2030
2031 doc_puts(doc,
2032 "% x y w h ESPrf - Fill a rectangle.\n"
2033 "userdict/ESPrf/rectfill where{pop/rectfill load}\n"
2034 "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
2035 "neg 0 rlineto closepath fill grestore}bind}ifelse put\n");
2036
2037 doc_puts(doc,
2038 "% x y w h ESPrs - Stroke a rectangle.\n"
2039 "userdict/ESPrs/rectstroke where{pop/rectstroke load}\n"
2040 "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
2041 "neg 0 rlineto closepath stroke grestore}bind}ifelse put\n");
ef416fc2 2042
80ca4592 2043 /*
2044 * Write the page and label prologs...
2045 */
ef416fc2 2046
80ca4592 2047 if (doc->number_up == 2 || doc->number_up == 6)
2048 {
ef416fc2 2049 /*
80ca4592 2050 * For 2- and 6-up output, rotate the labels to match the orientation
2051 * of the pages...
ef416fc2 2052 */
2053
80ca4592 2054 if (Orientation & 1)
b86bc4cf 2055 write_label_prolog(doc, doc->page_label, PageBottom,
2056 PageWidth - PageLength + PageTop, PageLength);
80ca4592 2057 else
b86bc4cf 2058 write_label_prolog(doc, doc->page_label, PageLeft, PageRight,
2059 PageLength);
80ca4592 2060 }
2061 else
b86bc4cf 2062 write_label_prolog(doc, doc->page_label, PageBottom, PageTop, PageWidth);
80ca4592 2063}
2064
ef416fc2 2065
80ca4592 2066/*
2067 * 'doc_printf()' - Send a formatted string to stdout and/or the temp file.
2068 *
2069 * This function should be used for all page-level output that is affected
2070 * by ordering, collation, etc.
2071 */
ef416fc2 2072
80ca4592 2073static void
2074doc_printf(pstops_doc_t *doc, /* I - Document information */
2075 const char *format, /* I - Printf-style format string */
2076 ...) /* I - Additional arguments as needed */
2077{
2078 va_list ap; /* Pointer to arguments */
2079 char buffer[1024]; /* Output buffer */
2080 size_t bytes; /* Number of bytes to write */
ef416fc2 2081
ef416fc2 2082
80ca4592 2083 va_start(ap, format);
2084 bytes = vsnprintf(buffer, sizeof(buffer), format, ap);
2085 va_end(ap);
2086
2087 if (bytes > sizeof(buffer))
2088 {
f0ab5bff
MS
2089 _cupsLangPrintf(stderr,
2090 _("ERROR: doc_printf overflow (%d bytes) detected, "
4d301e69 2091 "aborting\n"), (int)bytes);
80ca4592 2092 exit(1);
2093 }
2094
2095 doc_write(doc, buffer, bytes);
2096}
2097
2098
2099/*
2100 * 'doc_puts()' - Send a nul-terminated string to stdout and/or the temp file.
2101 *
2102 * This function should be used for all page-level output that is affected
2103 * by ordering, collation, etc.
2104 */
2105
2106static void
2107doc_puts(pstops_doc_t *doc, /* I - Document information */
2108 const char *s) /* I - String to send */
2109{
2110 doc_write(doc, s, strlen(s));
2111}
2112
ef416fc2 2113
80ca4592 2114/*
2115 * 'doc_write()' - Send data to stdout and/or the temp file.
2116 */
ef416fc2 2117
80ca4592 2118static void
2119doc_write(pstops_doc_t *doc, /* I - Document information */
2120 const char *s, /* I - Data to send */
2121 size_t len) /* I - Number of bytes to send */
2122{
2123 if (!doc->slow_order)
2124 fwrite(s, 1, len, stdout);
ef416fc2 2125
80ca4592 2126 if (doc->temp)
2127 cupsFileWrite(doc->temp, s, len);
2128}
ef416fc2 2129
ef416fc2 2130
80ca4592 2131/*
0af14961 2132 * 'end_nup()' - End processing for N-up printing.
80ca4592 2133 */
ef416fc2 2134
80ca4592 2135static void
2136end_nup(pstops_doc_t *doc, /* I - Document information */
2137 int number) /* I - Page number */
2138{
2abf387c 2139 if (doc->number_up > 1)
ed486911 2140 doc_puts(doc, "userdict/ESPsave get restore\n");
ef416fc2 2141
80ca4592 2142 switch (doc->number_up)
2143 {
2144 case 1 :
2145 if (doc->use_ESPshowpage)
2146 {
ed486911 2147 write_labels(doc, Orientation);
2148 doc_puts(doc, "ESPshowpage\n");
80ca4592 2149 }
2150 break;
ef416fc2 2151
80ca4592 2152 case 2 :
2153 case 6 :
2154 if (is_last_page(number) && doc->use_ESPshowpage)
2155 {
2156 if (Orientation & 1)
2157 {
2158 /*
2159 * Rotate the labels back to portrait...
2160 */
ef416fc2 2161
ed486911 2162 write_labels(doc, Orientation - 1);
80ca4592 2163 }
2164 else if (Orientation == 0)
2165 {
2166 /*
2167 * Rotate the labels to landscape...
2168 */
ef416fc2 2169
ed486911 2170 write_labels(doc, doc->normal_landscape ? 1 : 3);
80ca4592 2171 }
2172 else
2173 {
2174 /*
2175 * Rotate the labels to landscape...
2176 */
ef416fc2 2177
ed486911 2178 write_labels(doc, doc->normal_landscape ? 3 : 1);
80ca4592 2179 }
ef416fc2 2180
ed486911 2181 doc_puts(doc, "ESPshowpage\n");
80ca4592 2182 }
2183 break;
ef416fc2 2184
80ca4592 2185 default :
2186 if (is_last_page(number) && doc->use_ESPshowpage)
ef416fc2 2187 {
ed486911 2188 write_labels(doc, Orientation);
2189 doc_puts(doc, "ESPshowpage\n");
ef416fc2 2190 }
80ca4592 2191 break;
ef416fc2 2192 }
2193
80ca4592 2194 fflush(stdout);
2195}
2196
2197
2198/*
2199 * 'include_feature()' - Include a printer option/feature command.
2200 */
2201
2202static int /* O - New number of options */
2203include_feature(
2204 ppd_file_t *ppd, /* I - PPD file */
2205 const char *line, /* I - DSC line */
2206 int num_options, /* I - Number of options */
2207 cups_option_t **options) /* IO - Options */
2208{
2209 char name[255], /* Option name */
2210 value[255]; /* Option value */
2211 ppd_option_t *option; /* Option in file */
ef416fc2 2212
ef416fc2 2213
2214 /*
80ca4592 2215 * Get the "%%IncludeFeature: *Keyword OptionKeyword" values...
ef416fc2 2216 */
2217
80ca4592 2218 if (sscanf(line + 17, "%254s%254s", name, value) != 2)
bd7854cb 2219 {
4d301e69 2220 _cupsLangPuts(stderr, _("ERROR: Bad %%IncludeFeature: comment\n"));
80ca4592 2221 return (num_options);
bd7854cb 2222 }
ef416fc2 2223
2224 /*
80ca4592 2225 * Find the option and choice...
ef416fc2 2226 */
2227
80ca4592 2228 if ((option = ppdFindOption(ppd, name + 1)) == NULL)
ef416fc2 2229 {
4d301e69 2230 _cupsLangPrintf(stderr, _("WARNING: Unknown option \"%s\"\n"), name + 1);
80ca4592 2231 return (num_options);
ef416fc2 2232 }
2233
80ca4592 2234 if (option->section == PPD_ORDER_EXIT ||
2235 option->section == PPD_ORDER_JCL)
2236 {
f0ab5bff 2237 _cupsLangPrintf(stderr, _("WARNING: Option \"%s\" cannot be included via "
4d301e69 2238 "IncludeFeature\n"), name + 1);
80ca4592 2239 return (num_options);
2240 }
ef416fc2 2241
1f0275e3 2242 if (!ppdFindChoice(option, value))
80ca4592 2243 {
f0ab5bff 2244 _cupsLangPrintf(stderr,
4d301e69 2245 _("WARNING: Unknown choice \"%s\" for option \"%s\"\n"),
f0ab5bff 2246 value, name + 1);
80ca4592 2247 return (num_options);
2248 }
ef416fc2 2249
80ca4592 2250 /*
2251 * Add the option to the option array and return...
2252 */
2253
2254 return (cupsAddOption(name + 1, value, num_options, options));
ef416fc2 2255}
2256
2257
2258/*
0af14961 2259 * 'parse_text()' - Parse a text value in a comment.
80ca4592 2260 *
2261 * This function parses a DSC text value as defined on page 36 of the
2262 * DSC specification. Text values are either surrounded by parenthesis
2263 * or whitespace-delimited.
2264 *
2265 * The value returned is the literal characters for the entire text
2266 * string, including any parenthesis and escape characters.
ef416fc2 2267 */
2268
80ca4592 2269static char * /* O - Value or NULL on error */
2270parse_text(const char *start, /* I - Start of text value */
2271 char **end, /* O - End of text value */
2272 char *buffer, /* I - Buffer */
2273 size_t bufsize) /* I - Size of buffer */
ef416fc2 2274{
80ca4592 2275 char *bufptr, /* Pointer in buffer */
2276 *bufend; /* End of buffer */
2277 int level; /* Parenthesis level */
ef416fc2 2278
2279
80ca4592 2280 /*
2281 * Skip leading whitespace...
2282 */
ef416fc2 2283
80ca4592 2284 while (isspace(*start & 255))
2285 start ++;
2286
2287 /*
2288 * Then copy the value...
2289 */
ef416fc2 2290
80ca4592 2291 level = 0;
2292 bufptr = buffer;
2293 bufend = buffer + bufsize - 1;
ef416fc2 2294
80ca4592 2295 while (bufptr < bufend)
ef416fc2 2296 {
80ca4592 2297 if (isspace(*start & 255) && !level)
2298 break;
ef416fc2 2299
80ca4592 2300 *bufptr++ = *start;
2301
2302 if (*start == '(')
2303 level ++;
2304 else if (*start == ')')
2305 {
2306 if (!level)
ef416fc2 2307 {
80ca4592 2308 start ++;
2309 break;
ef416fc2 2310 }
2311 else
80ca4592 2312 level --;
ef416fc2 2313 }
80ca4592 2314 else if (*start == '\\')
2315 {
2316 /*
2317 * Copy escaped character...
2318 */
ef416fc2 2319
80ca4592 2320 int i; /* Looping var */
ef416fc2 2321
80ca4592 2322
2323 for (i = 1;
2324 i <= 3 && isdigit(start[i] & 255) && bufptr < bufend;
2325 *bufptr++ = start[i], i ++);
2326 }
2327
2328 start ++;
ef416fc2 2329 }
2330
80ca4592 2331 *bufptr = '\0';
2332
2333 /*
2334 * Return the value and new pointer into the line...
2335 */
2336
2337 if (end)
2338 *end = (char *)start;
2339
2340 if (bufptr == bufend)
2341 return (NULL);
2342 else
2343 return (buffer);
ef416fc2 2344}
2345
2346
bd7854cb 2347/*
0af14961 2348 * 'set_pstops_options()' - Set pstops options.
bd7854cb 2349 */
2350
80ca4592 2351static void
2352set_pstops_options(
2353 pstops_doc_t *doc, /* I - Document information */
2354 ppd_file_t *ppd, /* I - PPD file */
2355 char *argv[], /* I - Command-line arguments */
2356 int num_options, /* I - Number of options */
2357 cups_option_t *options) /* I - Options */
bd7854cb 2358{
80ca4592 2359 const char *val; /* Option value */
2360 int intval; /* Integer option value */
2361 ppd_attr_t *attr; /* PPD attribute */
2362 ppd_option_t *option; /* PPD option */
2363 ppd_choice_t *choice; /* PPD choice */
bd7854cb 2364
bd7854cb 2365
80ca4592 2366 /*
2367 * Initialize document information structure...
2368 */
bd7854cb 2369
80ca4592 2370 memset(doc, 0, sizeof(pstops_doc_t));
bd7854cb 2371
80ca4592 2372 doc->job_id = atoi(argv[1]);
2373 doc->user = argv[2];
2374 doc->title = argv[3];
2375 doc->copies = atoi(argv[4]);
bd7854cb 2376
80ca4592 2377 if (ppd && ppd->landscape > 0)
2378 doc->normal_landscape = 1;
bd7854cb 2379
80ca4592 2380 doc->bounding_box[0] = (int)PageLeft;
2381 doc->bounding_box[1] = (int)PageBottom;
2382 doc->bounding_box[2] = (int)PageRight;
2383 doc->bounding_box[3] = (int)PageTop;
bd7854cb 2384
80ca4592 2385 doc->new_bounding_box[0] = INT_MAX;
2386 doc->new_bounding_box[1] = INT_MAX;
2387 doc->new_bounding_box[2] = INT_MIN;
2388 doc->new_bounding_box[3] = INT_MIN;
bd7854cb 2389
80ca4592 2390 /*
2391 * AP_FIRSTPAGE_InputSlot
2392 */
ef416fc2 2393
80ca4592 2394 doc->ap_input_slot = cupsGetOption("AP_FIRSTPAGE_InputSlot", num_options,
2395 options);
ef416fc2 2396
80ca4592 2397 /*
2398 * AP_FIRSTPAGE_ManualFeed
2399 */
ef416fc2 2400
80ca4592 2401 doc->ap_manual_feed = cupsGetOption("AP_FIRSTPAGE_ManualFeed", num_options,
2402 options);
ef416fc2 2403
80ca4592 2404 /*
2405 * brightness
2406 */
bd7854cb 2407
80ca4592 2408 if ((val = cupsGetOption("brightness", num_options, options)) != NULL)
ef416fc2 2409 {
80ca4592 2410 /*
2411 * Get brightness value from 10 to 1000.
2412 */
ef416fc2 2413
80ca4592 2414 intval = atoi(val);
ef416fc2 2415
80ca4592 2416 if (intval < 10 || intval > 1000)
2417 {
f0ab5bff 2418 _cupsLangPrintf(stderr, _("ERROR: Unsupported brightness value %s, using "
4d301e69 2419 "brightness=100\n"), val);
80ca4592 2420 doc->brightness = 1.0f;
2421 }
2422 else
2423 doc->brightness = intval * 0.01f;
ef416fc2 2424 }
80ca4592 2425 else
2426 doc->brightness = 1.0f;
ef416fc2 2427
ef416fc2 2428 /*
80ca4592 2429 * collate, multiple-document-handling
ef416fc2 2430 */
2431
80ca4592 2432 if ((val = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
ef416fc2 2433 {
80ca4592 2434 /*
2435 * This IPP attribute is unnecessarily complicated...
2436 *
2437 * single-document, separate-documents-collated-copies, and
2438 * single-document-new-sheet all require collated copies.
2439 *
2440 * separate-documents-uncollated-copies allows for uncollated copies.
2441 */
2442
2443 doc->collate = strcasecmp(val, "separate-documents-uncollated-copies") != 0;
ef416fc2 2444 }
2445
80ca4592 2446 if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
2447 (!strcasecmp(val, "true") ||!strcasecmp(val, "on") ||
2448 !strcasecmp(val, "yes")))
2449 doc->collate = 1;
ef416fc2 2450
80ca4592 2451 /*
2452 * emit-jcl
2453 */
ef416fc2 2454
80ca4592 2455 if ((val = cupsGetOption("emit-jcl", num_options, options)) != NULL &&
2456 (!strcasecmp(val, "false") || !strcasecmp(val, "off") ||
2457 !strcasecmp(val, "no") || !strcmp(val, "0")))
2458 doc->emit_jcl = 0;
2459 else
2460 doc->emit_jcl = 1;
ef416fc2 2461
ef416fc2 2462 /*
ed6e7faf 2463 * fitplot/fit-to-page
ef416fc2 2464 */
2465
80ca4592 2466 if ((val = cupsGetOption("fitplot", num_options, options)) != NULL &&
ed6e7faf
MS
2467 !strcasecmp(val, "true"))
2468 doc->fitplot = 1;
2469 else if ((val = cupsGetOption("fit-to-page", num_options, options)) != NULL &&
2470 !strcasecmp(val, "true"))
80ca4592 2471 doc->fitplot = 1;
ef416fc2 2472
2473 /*
80ca4592 2474 * gamma
ef416fc2 2475 */
2476
80ca4592 2477 if ((val = cupsGetOption("gamma", num_options, options)) != NULL)
ef416fc2 2478 {
80ca4592 2479 /*
2480 * Get gamma value from 1 to 10000...
2481 */
ef416fc2 2482
80ca4592 2483 intval = atoi(val);
ef416fc2 2484
80ca4592 2485 if (intval < 1 || intval > 10000)
2486 {
f0ab5bff 2487 _cupsLangPrintf(stderr, _("ERROR: Unsupported gamma value %s, using "
4d301e69 2488 "gamma=1000\n"), val);
80ca4592 2489 doc->gamma = 1.0f;
2490 }
2491 else
2492 doc->gamma = intval * 0.001f;
2493 }
2494 else
2495 doc->gamma = 1.0f;
ef416fc2 2496
2497 /*
80ca4592 2498 * InputSlot
ef416fc2 2499 */
2500
80ca4592 2501 if ((choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL)
2502 doc->input_slot = choice->choice;
ef416fc2 2503
2504 /*
80ca4592 2505 * ManualFeed
ef416fc2 2506 */
2507
80ca4592 2508 if ((choice = ppdFindMarkedChoice(ppd, "ManualFeed")) != NULL)
2509 doc->manual_feed = choice->choice;
ef416fc2 2510
09a101d6 2511 if ((choice = ppdFindMarkedChoice(ppd, "MirrorPrint")) != NULL)
2512 {
2513 val = choice->choice;
2514 choice->marked = 0;
2515 }
2516 else
2517 val = cupsGetOption("mirror", num_options, options);
2518
2519 if (val && (!strcasecmp(val, "true") || !strcasecmp(val, "on") ||
2520 !strcasecmp(val, "yes")))
80ca4592 2521 doc->mirror = 1;
ef416fc2 2522
80ca4592 2523 /*
2524 * number-up
2525 */
ef416fc2 2526
80ca4592 2527 if ((val = cupsGetOption("number-up", num_options, options)) != NULL)
2528 {
2529 switch (intval = atoi(val))
2530 {
2531 case 1 :
2532 case 2 :
2533 case 4 :
2534 case 6 :
2535 case 9 :
2536 case 16 :
2537 doc->number_up = intval;
2538 break;
2539 default :
f0ab5bff
MS
2540 _cupsLangPrintf(stderr,
2541 _("ERROR: Unsupported number-up value %d, using "
4d301e69 2542 "number-up=1\n"), intval);
80ca4592 2543 doc->number_up = 1;
2544 break;
2545 }
2546 }
2547 else
2548 doc->number_up = 1;
ef416fc2 2549
80ca4592 2550 /*
2551 * number-up-layout
2552 */
ef416fc2 2553
80ca4592 2554 if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL)
ef416fc2 2555 {
80ca4592 2556 if (!strcasecmp(val, "lrtb"))
2557 doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
2558 else if (!strcasecmp(val, "lrbt"))
2559 doc->number_up_layout = PSTOPS_LAYOUT_LRBT;
2560 else if (!strcasecmp(val, "rltb"))
2561 doc->number_up_layout = PSTOPS_LAYOUT_RLTB;
2562 else if (!strcasecmp(val, "rlbt"))
2563 doc->number_up_layout = PSTOPS_LAYOUT_RLBT;
2564 else if (!strcasecmp(val, "tblr"))
2565 doc->number_up_layout = PSTOPS_LAYOUT_TBLR;
2566 else if (!strcasecmp(val, "tbrl"))
2567 doc->number_up_layout = PSTOPS_LAYOUT_TBRL;
2568 else if (!strcasecmp(val, "btlr"))
2569 doc->number_up_layout = PSTOPS_LAYOUT_BTLR;
2570 else if (!strcasecmp(val, "btrl"))
2571 doc->number_up_layout = PSTOPS_LAYOUT_BTRL;
2572 else
2573 {
f0ab5bff 2574 _cupsLangPrintf(stderr, _("ERROR: Unsupported number-up-layout value %s, "
4d301e69 2575 "using number-up-layout=lrtb\n"), val);
80ca4592 2576 doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
2577 }
2578 }
2579 else
2580 doc->number_up_layout = PSTOPS_LAYOUT_LRTB;
ef416fc2 2581
80ca4592 2582 /*
2583 * OutputOrder
2584 */
ef416fc2 2585
80ca4592 2586 if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL)
2587 {
2588 if (!strcasecmp(val, "Reverse"))
2589 doc->output_order = 1;
2590 }
2591 else if (ppd)
2592 {
2593 /*
2594 * Figure out the right default output order from the PPD file...
2595 */
ef416fc2 2596
80ca4592 2597 if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL &&
2598 (attr = ppdFindAttr(ppd, "PageStackOrder", choice->choice)) != NULL &&
2599 attr->value)
2600 doc->output_order = !strcasecmp(attr->value, "Reverse");
2601 else if ((attr = ppdFindAttr(ppd, "DefaultOutputOrder", NULL)) != NULL &&
2602 attr->value)
2603 doc->output_order = !strcasecmp(attr->value, "Reverse");
2604 }
ef416fc2 2605
80ca4592 2606 /*
2607 * page-border
2608 */
ef416fc2 2609
80ca4592 2610 if ((val = cupsGetOption("page-border", num_options, options)) != NULL)
2611 {
2612 if (!strcasecmp(val, "none"))
2613 doc->page_border = PSTOPS_BORDERNONE;
2614 else if (!strcasecmp(val, "single"))
2615 doc->page_border = PSTOPS_BORDERSINGLE;
2616 else if (!strcasecmp(val, "single-thick"))
2617 doc->page_border = PSTOPS_BORDERSINGLE2;
2618 else if (!strcasecmp(val, "double"))
2619 doc->page_border = PSTOPS_BORDERDOUBLE;
2620 else if (!strcasecmp(val, "double-thick"))
2621 doc->page_border = PSTOPS_BORDERDOUBLE2;
2622 else
2623 {
f0ab5bff 2624 _cupsLangPrintf(stderr, _("ERROR: Unsupported page-border value %s, "
4d301e69 2625 "using page-border=none\n"), val);
80ca4592 2626 doc->page_border = PSTOPS_BORDERNONE;
2627 }
ef416fc2 2628 }
80ca4592 2629 else
2630 doc->page_border = PSTOPS_BORDERNONE;
ef416fc2 2631
80ca4592 2632 /*
2633 * page-label
2634 */
ef416fc2 2635
80ca4592 2636 doc->page_label = cupsGetOption("page-label", num_options, options);
ef416fc2 2637
80ca4592 2638 /*
2639 * page-ranges
2640 */
ef416fc2 2641
80ca4592 2642 doc->page_ranges = cupsGetOption("page-ranges", num_options, options);
ef416fc2 2643
2644 /*
80ca4592 2645 * page-set
ef416fc2 2646 */
2647
80ca4592 2648 doc->page_set = cupsGetOption("page-set", num_options, options);
ef416fc2 2649
2650 /*
80ca4592 2651 * Now figure out if we have to force collated copies, etc.
ef416fc2 2652 */
2653
80ca4592 2654 if (ppd && ppd->manual_copies && Duplex && doc->copies > 1)
ef416fc2 2655 {
80ca4592 2656 /*
2657 * Force collated copies when printing a duplexed document to
2658 * a non-PS printer that doesn't do hardware copy generation.
2659 * Otherwise the copies will end up on the front/back side of
2660 * each page.
2661 */
ef416fc2 2662
80ca4592 2663 doc->collate = 1;
ef416fc2 2664 }
2665
2666 /*
80ca4592 2667 * See if we have to filter the fast or slow way...
ef416fc2 2668 */
2669
80ca4592 2670 if (doc->collate && doc->copies > 1)
ef416fc2 2671 {
80ca4592 2672 /*
2673 * See if we need to manually collate the pages...
2674 */
2675
2676 doc->slow_collate = 1;
2677
2678 if ((choice = ppdFindMarkedChoice(ppd, "Collate")) != NULL &&
2679 !strcasecmp(choice->choice, "True"))
bd7854cb 2680 {
80ca4592 2681 /*
2682 * Hardware collate option is selected, see if the option is
2683 * conflicting - if not, collate in hardware. Otherwise,
2684 * turn the hardware collate option off...
2685 */
2686
2abf387c 2687 if ((option = ppdFindOption(ppd, "Collate")) != NULL &&
80ca4592 2688 !option->conflicted)
2689 doc->slow_collate = 0;
bd7854cb 2690 else
80ca4592 2691 ppdMarkOption(ppd, "Collate", "False");
bd7854cb 2692 }
ef416fc2 2693 }
2694 else
80ca4592 2695 doc->slow_collate = 0;
2696
2697 if (!ppdFindOption(ppd, "OutputOrder") && doc->output_order)
2698 doc->slow_order = 1;
2699 else
2700 doc->slow_order = 0;
2701
2abf387c 2702 if (Duplex &&
2703 (doc->slow_collate || doc->slow_order ||
2704 ((attr = ppdFindAttr(ppd, "cupsEvenDuplex", NULL)) != NULL &&
2705 attr->value && !strcasecmp(attr->value, "true"))))
80ca4592 2706 doc->slow_duplex = 1;
2707 else
2708 doc->slow_duplex = 0;
2709
2710 /*
2711 * Create a temporary file for page data if we need to filter slowly...
2712 */
2713
2714 if (doc->slow_order || doc->slow_collate)
ef416fc2 2715 {
80ca4592 2716 if ((doc->temp = cupsTempFile2(doc->tempfile,
2717 sizeof(doc->tempfile))) == NULL)
bd7854cb 2718 {
f0ab5bff 2719 _cupsLangPrintError(_("ERROR: Unable to create temporary file"));
80ca4592 2720 exit(1);
bd7854cb 2721 }
ef416fc2 2722 }
80ca4592 2723
2724 /*
2725 * Figure out if we should use ESPshowpage or not...
2726 */
2727
2728 if (doc->page_label || getenv("CLASSIFICATION") || doc->number_up > 1 ||
2729 doc->page_border)
2730 {
2731 /*
2732 * Yes, use ESPshowpage...
2733 */
2734
2735 doc->use_ESPshowpage = 1;
2736 }
2737
2738 fprintf(stderr, "DEBUG: slow_collate=%d, slow_duplex=%d, slow_order=%d\n",
2739 doc->slow_collate, doc->slow_duplex, doc->slow_order);
ef416fc2 2740}
2741
2742
2743/*
0af14961 2744 * 'skip_page()' - Skip past a page that won't be printed.
ef416fc2 2745 */
2746
91c84a35 2747static ssize_t /* O - Length of next line */
80ca4592 2748skip_page(cups_file_t *fp, /* I - File to read from */
2749 char *line, /* I - Line buffer */
91c84a35 2750 ssize_t linelen, /* I - Length of initial line */
80ca4592 2751 size_t linesize) /* I - Size of line buffer */
ef416fc2 2752{
80ca4592 2753 int level; /* Embedded document level */
ef416fc2 2754
2755
80ca4592 2756 level = 0;
ef416fc2 2757
80ca4592 2758 while ((linelen = cupsFileGetLine(fp, line, linesize)) > 0)
ef416fc2 2759 {
80ca4592 2760 if (level == 0 &&
d6ae789d 2761 (!strncmp(line, "%%Page:", 7) || !strncmp(line, "%%Trailer", 9)))
ef416fc2 2762 break;
80ca4592 2763 else if (!strncmp(line, "%%BeginDocument", 15) ||
2764 !strncmp(line, "%ADO_BeginApplication", 21))
2765 level ++;
2766 else if ((!strncmp(line, "%%EndDocument", 13) ||
2767 !strncmp(line, "%ADO_EndApplication", 19)) && level > 0)
2768 level --;
2769 else if (!strncmp(line, "%%BeginBinary:", 14) ||
2770 (!strncmp(line, "%%BeginData:", 12) &&
2771 !strstr(line, "ASCII") && !strstr(line, "Hex")))
ef416fc2 2772 {
2773 /*
80ca4592 2774 * Skip binary data...
ef416fc2 2775 */
2776
80ca4592 2777 int bytes; /* Bytes of data */
ef416fc2 2778
ef416fc2 2779
80ca4592 2780 bytes = atoi(strchr(line, ':') + 1);
ef416fc2 2781
80ca4592 2782 while (bytes > 0)
2783 {
2784 if (bytes > linesize)
2785 linelen = cupsFileRead(fp, line, linesize);
2786 else
2787 linelen = cupsFileRead(fp, line, bytes);
ef416fc2 2788
80ca4592 2789 if (linelen < 1)
2790 {
2791 line[0] = '\0';
2792 perror("ERROR: Early end-of-file while reading binary data");
2793 return (0);
2794 }
ef416fc2 2795
80ca4592 2796 bytes -= linelen;
2797 }
2798 }
2799 }
ef416fc2 2800
80ca4592 2801 return (linelen);
ef416fc2 2802}
2803
2804
ef416fc2 2805/*
0af14961 2806 * 'start_nup()' - Start processing for N-up printing.
ef416fc2 2807 */
2808
2809static void
80ca4592 2810start_nup(pstops_doc_t *doc, /* I - Document information */
2811 int number, /* I - Page number */
2812 int show_border, /* I - Show the border? */
2813 const int *bounding_box) /* I - BoundingBox value */
ef416fc2 2814{
80ca4592 2815 int pos; /* Position on page */
2816 int x, y; /* Relative position of subpage */
2817 float w, l, /* Width and length of subpage */
2818 tx, ty; /* Translation values for subpage */
2819 float pagew, /* Printable width of page */
2820 pagel; /* Printable height of page */
a41f09e2
MS
2821 int bboxx, /* BoundingBox X origin */
2822 bboxy, /* BoundingBox Y origin */
2823 bboxw, /* BoundingBox width */
80ca4592 2824 bboxl; /* BoundingBox height */
a41f09e2 2825 float margin = 0; /* Current margin for border */
80ca4592 2826
ef416fc2 2827
2abf387c 2828 if (doc->number_up > 1)
80ca4592 2829 doc_puts(doc, "userdict/ESPsave save put\n");
ef416fc2 2830
80ca4592 2831 pos = (number - 1) % doc->number_up;
2832 pagew = PageRight - PageLeft;
2833 pagel = PageTop - PageBottom;
ef416fc2 2834
80ca4592 2835 if (doc->fitplot)
2836 {
a41f09e2
MS
2837 bboxx = bounding_box[0];
2838 bboxy = bounding_box[1];
80ca4592 2839 bboxw = bounding_box[2] - bounding_box[0];
2840 bboxl = bounding_box[3] - bounding_box[1];
2841 }
2842 else
2843 {
a41f09e2
MS
2844 bboxx = 0;
2845 bboxy = 0;
80ca4592 2846 bboxw = PageWidth;
2847 bboxl = PageLength;
2848 }
ef416fc2 2849
80ca4592 2850 fprintf(stderr, "DEBUG: pagew = %.1f, pagel = %.1f\n", pagew, pagel);
a41f09e2
MS
2851 fprintf(stderr, "DEBUG: bboxx = %d, bboxy = %d, bboxw = %d, bboxl = %d\n",
2852 bboxx, bboxy, bboxw, bboxl);
80ca4592 2853 fprintf(stderr, "DEBUG: PageLeft = %.1f, PageRight = %.1f\n",
2854 PageLeft, PageRight);
2855 fprintf(stderr, "DEBUG: PageTop = %.1f, PageBottom = %.1f\n",
2856 PageTop, PageBottom);
2857 fprintf(stderr, "DEBUG: PageWidth = %.1f, PageLength = %.1f\n",
2858 PageWidth, PageLength);
ef416fc2 2859
2860 switch (Orientation)
2861 {
2862 case 1 : /* Landscape */
80ca4592 2863 doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", PageLength);
ef416fc2 2864 break;
2865 case 2 : /* Reverse Portrait */
80ca4592 2866 doc_printf(doc, "%.1f %.1f translate 180 rotate\n", PageWidth,
2867 PageLength);
ef416fc2 2868 break;
2869 case 3 : /* Reverse Landscape */
80ca4592 2870 doc_printf(doc, "0.0 %.1f translate -90 rotate\n", PageWidth);
ef416fc2 2871 break;
2872 }
2873
80ca4592 2874 if (Duplex && doc->number_up > 1 && ((number / doc->number_up) & 1))
2875 doc_printf(doc, "%.1f %.1f translate\n", PageWidth - PageRight, PageBottom);
2876 else if (doc->number_up > 1 || doc->fitplot)
2877 doc_printf(doc, "%.1f %.1f translate\n", PageLeft, PageBottom);
ef416fc2 2878
c5571a1d
MS
2879 if (doc->mirror)
2880 doc_printf(doc, "%.1f 0.0 translate -1 1 scale\n", PageWidth);
2881
80ca4592 2882 switch (doc->number_up)
ef416fc2 2883 {
2884 default :
80ca4592 2885 if (doc->fitplot)
bd7854cb 2886 {
80ca4592 2887 w = pagew;
2888 l = w * bboxl / bboxw;
bd7854cb 2889
80ca4592 2890 if (l > pagel)
bd7854cb 2891 {
80ca4592 2892 l = pagel;
2893 w = l * bboxw / bboxl;
bd7854cb 2894 }
2895
80ca4592 2896 tx = 0.5 * (pagew - w);
2897 ty = 0.5 * (pagel - l);
bd7854cb 2898
80ca4592 2899 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n", tx, ty,
2900 w / bboxw, l / bboxl);
bd7854cb 2901 }
2902 else
bd7854cb 2903 w = PageWidth;
ef416fc2 2904 break;
2905
2906 case 2 :
2907 if (Orientation & 1)
2908 {
2909 x = pos & 1;
2910
80ca4592 2911 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
ef416fc2 2912 x = 1 - x;
2913
80ca4592 2914 w = pagel;
2915 l = w * bboxl / bboxw;
ef416fc2 2916
80ca4592 2917 if (l > (pagew * 0.5))
ef416fc2 2918 {
80ca4592 2919 l = pagew * 0.5;
2920 w = l * bboxw / bboxl;
ef416fc2 2921 }
2922
80ca4592 2923 tx = 0.5 * (pagew * 0.5 - l);
2924 ty = 0.5 * (pagel - w);
ef416fc2 2925
80ca4592 2926 if (doc->normal_landscape)
2927 doc_printf(doc, "0.0 %.1f translate -90 rotate\n", pagel);
ef416fc2 2928 else
80ca4592 2929 doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", pagew);
ef416fc2 2930
80ca4592 2931 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
2932 ty, tx + pagew * 0.5 * x, w / bboxw, l / bboxl);
ef416fc2 2933 }
2934 else
2935 {
2936 x = pos & 1;
2937
80ca4592 2938 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
ef416fc2 2939 x = 1 - x;
2940
80ca4592 2941 l = pagew;
2942 w = l * bboxw / bboxl;
ef416fc2 2943
80ca4592 2944 if (w > (pagel * 0.5))
ef416fc2 2945 {
80ca4592 2946 w = pagel * 0.5;
2947 l = w * bboxl / bboxw;
ef416fc2 2948 }
2949
80ca4592 2950 tx = 0.5 * (pagel * 0.5 - w);
2951 ty = 0.5 * (pagew - l);
ef416fc2 2952
80ca4592 2953 if (doc->normal_landscape)
2954 doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", pagew);
ef416fc2 2955 else
80ca4592 2956 doc_printf(doc, "0.0 %.1f translate -90 rotate\n", pagel);
ef416fc2 2957
80ca4592 2958 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
2959 tx + pagel * 0.5 * x, ty, w / bboxw, l / bboxl);
ef416fc2 2960 }
2961 break;
2962
2963 case 4 :
80ca4592 2964 if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
ef416fc2 2965 {
2966 x = (pos / 2) & 1;
2967 y = pos & 1;
2968 }
2969 else
2970 {
2971 x = pos & 1;
2972 y = (pos / 2) & 1;
2973 }
2974
80ca4592 2975 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
ef416fc2 2976 x = 1 - x;
2977
80ca4592 2978 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
ef416fc2 2979 y = 1 - y;
2980
80ca4592 2981 w = pagew * 0.5;
2982 l = w * bboxl / bboxw;
ef416fc2 2983
80ca4592 2984 if (l > (pagel * 0.5))
ef416fc2 2985 {
80ca4592 2986 l = pagel * 0.5;
2987 w = l * bboxw / bboxl;
ef416fc2 2988 }
2989
80ca4592 2990 tx = 0.5 * (pagew * 0.5 - w);
2991 ty = 0.5 * (pagel * 0.5 - l);
ef416fc2 2992
80ca4592 2993 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
2994 tx + x * pagew * 0.5, ty + y * pagel * 0.5,
2995 w / bboxw, l / bboxl);
ef416fc2 2996 break;
2997
2998 case 6 :
2999 if (Orientation & 1)
3000 {
80ca4592 3001 if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
ef416fc2 3002 {
3003 x = pos / 3;
3004 y = pos % 3;
3005
80ca4592 3006 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
ef416fc2 3007 x = 1 - x;
3008
80ca4592 3009 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
ef416fc2 3010 y = 2 - y;
3011 }
3012 else
3013 {
3014 x = pos & 1;
3015 y = pos / 2;
3016
80ca4592 3017 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
ef416fc2 3018 x = 1 - x;
3019
80ca4592 3020 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
ef416fc2 3021 y = 2 - y;
3022 }
3023
80ca4592 3024 w = pagel * 0.5;
3025 l = w * bboxl / bboxw;
ef416fc2 3026
80ca4592 3027 if (l > (pagew * 0.333))
ef416fc2 3028 {
80ca4592 3029 l = pagew * 0.333;
3030 w = l * bboxw / bboxl;
ef416fc2 3031 }
3032
80ca4592 3033 tx = 0.5 * (pagel - 2 * w);
3034 ty = 0.5 * (pagew - 3 * l);
ef416fc2 3035
80ca4592 3036 if (doc->normal_landscape)
f301802f 3037 doc_printf(doc, "0 %.1f translate -90 rotate\n", pagel);
ef416fc2 3038 else
f301802f 3039 doc_printf(doc, "%.1f 0 translate 90 rotate\n", pagew);
ef416fc2 3040
80ca4592 3041 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
f301802f 3042 tx + x * w, ty + y * l, l / bboxl, w / bboxw);
ef416fc2 3043 }
3044 else
3045 {
80ca4592 3046 if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
ef416fc2 3047 {
3048 x = pos / 2;
3049 y = pos & 1;
3050
80ca4592 3051 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
ef416fc2 3052 x = 2 - x;
3053
80ca4592 3054 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
ef416fc2 3055 y = 1 - y;
3056 }
3057 else
3058 {
3059 x = pos % 3;
3060 y = pos / 3;
3061
80ca4592 3062 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
ef416fc2 3063 x = 2 - x;
3064
80ca4592 3065 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
ef416fc2 3066 y = 1 - y;
3067 }
3068
80ca4592 3069 l = pagew * 0.5;
3070 w = l * bboxw / bboxl;
ef416fc2 3071
80ca4592 3072 if (w > (pagel * 0.333))
ef416fc2 3073 {
80ca4592 3074 w = pagel * 0.333;
3075 l = w * bboxl / bboxw;
ef416fc2 3076 }
3077
f301802f 3078 tx = 0.5 * (pagel - 3 * w);
3079 ty = 0.5 * (pagew - 2 * l);
ef416fc2 3080
80ca4592 3081 if (doc->normal_landscape)
f301802f 3082 doc_printf(doc, "%.1f 0 translate 90 rotate\n", pagew);
ef416fc2 3083 else
f301802f 3084 doc_printf(doc, "0 %.1f translate -90 rotate\n", pagel);
ef416fc2 3085
80ca4592 3086 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
f301802f 3087 tx + w * x, ty + l * y, w / bboxw, l / bboxl);
3088
ef416fc2 3089 }
3090 break;
3091
3092 case 9 :
80ca4592 3093 if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
ef416fc2 3094 {
3095 x = (pos / 3) % 3;
3096 y = pos % 3;
3097 }
3098 else
3099 {
3100 x = pos % 3;
3101 y = (pos / 3) % 3;
3102 }
3103
80ca4592 3104 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
ef416fc2 3105 x = 2 - x;
3106
80ca4592 3107 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
ef416fc2 3108 y = 2 - y;
3109
80ca4592 3110 w = pagew * 0.333;
3111 l = w * bboxl / bboxw;
ef416fc2 3112
80ca4592 3113 if (l > (pagel * 0.333))
ef416fc2 3114 {
80ca4592 3115 l = pagel * 0.333;
3116 w = l * bboxw / bboxl;
ef416fc2 3117 }
3118
80ca4592 3119 tx = 0.5 * (pagew * 0.333 - w);
3120 ty = 0.5 * (pagel * 0.333 - l);
ef416fc2 3121
80ca4592 3122 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
3123 tx + x * pagew * 0.333, ty + y * pagel * 0.333,
3124 w / bboxw, l / bboxl);
ef416fc2 3125 break;
3126
3127 case 16 :
80ca4592 3128 if (doc->number_up_layout & PSTOPS_LAYOUT_VERTICAL)
ef416fc2 3129 {
3130 x = (pos / 4) & 3;
3131 y = pos & 3;
3132 }
3133 else
3134 {
3135 x = pos & 3;
3136 y = (pos / 4) & 3;
3137 }
3138
80ca4592 3139 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEX)
ef416fc2 3140 x = 3 - x;
3141
80ca4592 3142 if (doc->number_up_layout & PSTOPS_LAYOUT_NEGATEY)
ef416fc2 3143 y = 3 - y;
3144
80ca4592 3145 w = pagew * 0.25;
3146 l = w * bboxl / bboxw;
ef416fc2 3147
80ca4592 3148 if (l > (pagel * 0.25))
ef416fc2 3149 {
80ca4592 3150 l = pagel * 0.25;
3151 w = l * bboxw / bboxl;
ef416fc2 3152 }
3153
80ca4592 3154 tx = 0.5 * (pagew * 0.25 - w);
3155 ty = 0.5 * (pagel * 0.25 - l);
ef416fc2 3156
80ca4592 3157 doc_printf(doc, "%.1f %.1f translate %.3f %.3f scale\n",
3158 tx + x * pagew * 0.25, ty + y * pagel * 0.25,
3159 w / bboxw, l / bboxl);
ef416fc2 3160 break;
3161 }
3162
3163 /*
3164 * Draw borders as necessary...
3165 */
3166
80ca4592 3167 if (doc->page_border && show_border)
ef416fc2 3168 {
80ca4592 3169 int rects; /* Number of border rectangles */
a41f09e2 3170 float fscale; /* Scaling value for points */
ef416fc2 3171
3172
80ca4592 3173 rects = (doc->page_border & PSTOPS_BORDERDOUBLE) ? 2 : 1;
ef416fc2 3174 fscale = PageWidth / w;
3175 margin = 2.25 * fscale;
3176
3177 /*
3178 * Set the line width and color...
3179 */
3180
80ca4592 3181 doc_puts(doc, "gsave\n");
3182 doc_printf(doc, "%.3f setlinewidth 0 setgray newpath\n",
3183 (doc->page_border & PSTOPS_BORDERTHICK) ? 0.5 * fscale :
3184 0.24 * fscale);
ef416fc2 3185
3186 /*
3187 * Draw border boxes...
3188 */
3189
3190 for (; rects > 0; rects --, margin += 2 * fscale)
80ca4592 3191 if (doc->number_up > 1)
3192 doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrs\n",
a41f09e2
MS
3193 margin,
3194 margin,
3195 bboxw - 2 * margin,
3196 bboxl - 2 * margin);
ef416fc2 3197 else
80ca4592 3198 doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrs\n",
3199 PageLeft + margin,
3200 PageBottom + margin,
3201 PageRight - PageLeft - 2 * margin,
3202 PageTop - PageBottom - 2 * margin);
ef416fc2 3203
3204 /*
3205 * Restore pen settings...
3206 */
3207
80ca4592 3208 doc_puts(doc, "grestore\n");
ef416fc2 3209 }
3210
80ca4592 3211 if (doc->fitplot)
ef416fc2 3212 {
3213 /*
a41f09e2 3214 * Offset the page by its bounding box...
ef416fc2 3215 */
3216
80ca4592 3217 doc_printf(doc, "%d %d translate\n", -bounding_box[0],
3218 -bounding_box[1]);
80ca4592 3219 }
a41f09e2
MS
3220
3221 if (doc->fitplot || doc->number_up > 1)
80ca4592 3222 {
3223 /*
a41f09e2 3224 * Clip the page to the page's bounding box...
80ca4592 3225 */
3226
a41f09e2
MS
3227 doc_printf(doc, "%.1f %.1f %.1f %.1f ESPrc\n",
3228 bboxx + margin, bboxy + margin,
3229 bboxw - 2 * margin, bboxl - 2 * margin);
ef416fc2 3230 }
3231}
3232
3233
b86bc4cf 3234/*
3235 * 'write_label_prolog()' - Write the prolog with the classification
3236 * and page label.
3237 */
3238
3239static void
3240write_label_prolog(pstops_doc_t *doc, /* I - Document info */
3241 const char *label, /* I - Page label */
3242 float bottom, /* I - Bottom position in points */
3243 float top, /* I - Top position in points */
3244 float width) /* I - Width in points */
3245{
3246 const char *classification; /* CLASSIFICATION environment variable */
3247 const char *ptr; /* Temporary string pointer */
3248
3249
3250 /*
3251 * First get the current classification...
3252 */
3253
3254 if ((classification = getenv("CLASSIFICATION")) == NULL)
3255 classification = "";
3256 if (strcmp(classification, "none") == 0)
3257 classification = "";
3258
3259 /*
3260 * If there is nothing to show, bind an empty 'write labels' procedure
3261 * and return...
3262 */
3263
3264 if (!classification[0] && (label == NULL || !label[0]))
3265 {
3266 doc_puts(doc, "userdict/ESPwl{}bind put\n");
3267 return;
3268 }
3269
3270 /*
3271 * Set the classification + page label string...
3272 */
3273
3274 doc_puts(doc, "userdict");
3275 if (!strcmp(classification, "confidential"))
3276 doc_puts(doc, "/ESPpl(CONFIDENTIAL");
3277 else if (!strcmp(classification, "classified"))
3278 doc_puts(doc, "/ESPpl(CLASSIFIED");
3279 else if (!strcmp(classification, "secret"))
3280 doc_puts(doc, "/ESPpl(SECRET");
3281 else if (!strcmp(classification, "topsecret"))
3282 doc_puts(doc, "/ESPpl(TOP SECRET");
3283 else if (!strcmp(classification, "unclassified"))
3284 doc_puts(doc, "/ESPpl(UNCLASSIFIED");
3285 else
3286 {
3287 doc_puts(doc, "/ESPpl(");
3288
3289 for (ptr = classification; *ptr; ptr ++)
3290 {
3291 if (*ptr < 32 || *ptr > 126)
3292 doc_printf(doc, "\\%03o", *ptr);
3293 else if (*ptr == '_')
3294 doc_puts(doc, " ");
3295 else if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
3296 doc_printf(doc, "\\%c", *ptr);
3297 else
3298 doc_printf(doc, "%c", *ptr);
3299 }
3300 }
3301
3302 if (label)
3303 {
3304 if (classification[0])
3305 doc_puts(doc, " - ");
3306
3307 /*
3308 * Quote the label string as needed...
3309 */
3310
3311 for (ptr = label; *ptr; ptr ++)
3312 {
3313 if (*ptr < 32 || *ptr > 126)
3314 doc_printf(doc, "\\%03o", *ptr);
3315 else if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
3316 doc_printf(doc, "\\%c", *ptr);
3317 else
3318 doc_printf(doc, "%c", *ptr);
3319 }
3320 }
3321
3322 doc_puts(doc, ")put\n");
3323
3324 /*
3325 * Then get a 14 point Helvetica-Bold font...
3326 */
3327
3328 doc_puts(doc, "userdict/ESPpf /Helvetica-Bold findfont 14 scalefont put\n");
3329
3330 /*
3331 * Finally, the procedure to write the labels on the page...
3332 */
3333
3334 doc_puts(doc, "userdict/ESPwl{\n");
3335 doc_puts(doc, " ESPpf setfont\n");
3336 doc_printf(doc, " ESPpl stringwidth pop dup 12 add exch -0.5 mul %.0f add\n",
3337 width * 0.5f);
3338 doc_puts(doc, " 1 setgray\n");
3339 doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrf\n", bottom - 2.0);
3340 doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrf\n", top - 18.0);
3341 doc_puts(doc, " 0 setgray\n");
3342 doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrs\n", bottom - 2.0);
3343 doc_printf(doc, " dup 6 sub %.0f 3 index 20 ESPrs\n", top - 18.0);
3344 doc_printf(doc, " dup %.0f moveto ESPpl show\n", bottom + 2.0);
3345 doc_printf(doc, " %.0f moveto ESPpl show\n", top - 14.0);
3346 doc_puts(doc, "pop\n");
3347 doc_puts(doc, "}bind put\n");
3348}
3349
3350
ed486911 3351/*
3352 * 'write_labels()' - Write the actual page labels.
3353 *
3354 * This function is a copy of the one in common.c since we need to
3355 * use doc_puts/doc_printf instead of puts/printf...
3356 */
3357
3358static void
3359write_labels(pstops_doc_t *doc, /* I - Document information */
3360 int orient) /* I - Orientation of the page */
3361{
3362 float width, /* Width of page */
3363 length; /* Length of page */
3364
3365
3366 doc_puts(doc, "gsave\n");
3367
3368 if ((orient ^ Orientation) & 1)
3369 {
3370 width = PageLength;
3371 length = PageWidth;
3372 }
3373 else
3374 {
3375 width = PageWidth;
3376 length = PageLength;
3377 }
3378
3379 switch (orient & 3)
3380 {
3381 case 1 : /* Landscape */
3382 doc_printf(doc, "%.1f 0.0 translate 90 rotate\n", length);
3383 break;
3384 case 2 : /* Reverse Portrait */
3385 doc_printf(doc, "%.1f %.1f translate 180 rotate\n", width, length);
3386 break;
3387 case 3 : /* Reverse Landscape */
3388 doc_printf(doc, "0.0 %.1f translate -90 rotate\n", width);
3389 break;
3390 }
3391
3392 doc_puts(doc, "ESPwl\n");
3393 doc_puts(doc, "grestore\n");
3394}
3395
3396
ef416fc2 3397/*
0af14961
MS
3398 * 'write_options()' - Write options provided via %%IncludeFeature.
3399 */
3400
3401static void
3402write_options(
3403 pstops_doc_t *doc, /* I - Document */
3404 ppd_file_t *ppd, /* I - PPD file */
3405 int num_options, /* I - Number of options */
3406 cups_option_t *options) /* I - Options */
3407{
3408 int i; /* Looping var */
3409 ppd_option_t *option; /* PPD option */
3410 int min_order; /* Minimum OrderDependency value */
3411 char *doc_setup, /* DocumentSetup commands to send */
3412 *any_setup; /* AnySetup commands to send */
3413
3414
3415 /*
3416 * Figure out the minimum OrderDependency value...
3417 */
3418
3419 if ((option = ppdFindOption(ppd, "PageRegion")) != NULL)
3420 min_order = option->order;
3421 else
3422 min_order = 999.0f;
3423
3424 for (i = 0; i < num_options; i ++)
3425 if ((option = ppdFindOption(ppd, options[i].name)) != NULL &&
3426 option->order < min_order)
3427 min_order = option->order;
3428
3429 /*
3430 * Mark and extract them...
3431 */
3432
3433 cupsMarkOptions(ppd, num_options, options);
3434
3435 doc_setup = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, min_order);
3436 any_setup = ppdEmitString(ppd, PPD_ORDER_ANY, min_order);
3437
3438 /*
3439 * Then send them out...
3440 */
3441
e07d4801
MS
3442 if (doc->number_up > 1)
3443 {
3444 /*
3445 * Temporarily restore setpagedevice so we can set the options...
3446 */
3447
3448 doc_puts(doc, "userdict/setpagedevice/CUPSsetpagedevice load put\n");
3449 }
3450
0af14961
MS
3451 if (doc_setup)
3452 {
3453 doc_puts(doc, doc_setup);
3454 free(doc_setup);
3455 }
3456
3457 if (any_setup)
3458 {
3459 doc_puts(doc, any_setup);
3460 free(any_setup);
3461 }
e07d4801
MS
3462
3463 if (doc->number_up > 1)
3464 {
3465 /*
3466 * Disable setpagedevice again...
3467 */
3468
3469 doc_puts(doc, "userdict/setpagedevice{pop}bind put\n");
3470 }
0af14961
MS
3471}
3472
3473
3474/*
3475 * End of "$Id: pstops.c 7977 2008-09-23 23:44:33Z mike $".
ef416fc2 3476 */