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