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