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