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