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