1 /* 2 * PPD code emission routines for CUPS. 3 * 4 * Copyright 2007-2015 by Apple Inc. 5 * Copyright 1997-2007 by Easy Software Products, all rights reserved. 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 * PostScript is a trademark of Adobe Systems, Inc. 14 * 15 * This file is subject to the Apple OS-Developed Software exception. 16 */ 17 18 /* 19 * Include necessary headers... 20 */ 21 22 #include "cups-private.h" 23 #include "ppd.h" 24 #if defined(WIN32) || defined(__EMX__) 25 # include <io.h> 26 #else 27 # include <unistd.h> 28 #endif /* WIN32 || __EMX__ */ 29 30 31 /* 32 * Local functions... 33 */ 34 35 static int ppd_compare_cparams(ppd_cparam_t *a, ppd_cparam_t *b); 36 static void ppd_handle_media(ppd_file_t *ppd); 37 38 39 /* 40 * Local globals... 41 */ 42 43 static const char ppd_custom_code[] = 44 "pop pop pop\n" 45 "<</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice\n"; 46 47 48 /* 49 * 'ppdCollect()' - Collect all marked options that reside in the specified 50 * section. 51 * 52 * The choices array should be freed using @code free@ when you are 53 * finished with it. 54 */ 55 56 int /* O - Number of options marked */ 57 ppdCollect(ppd_file_t *ppd, /* I - PPD file data */ 58 ppd_section_t section, /* I - Section to collect */ 59 ppd_choice_t ***choices) /* O - Pointers to choices */ 60 { 61 return (ppdCollect2(ppd, section, 0.0, choices)); 62 } 63 64 65 /* 66 * 'ppdCollect2()' - Collect all marked options that reside in the 67 * specified section and minimum order. 68 * 69 * The choices array should be freed using @code free@ when you are 70 * finished with it. 71 * 72 * @since CUPS 1.2/macOS 10.5@ 73 */ 74 75 int /* O - Number of options marked */ 76 ppdCollect2(ppd_file_t *ppd, /* I - PPD file data */ 77 ppd_section_t section, /* I - Section to collect */ 78 float min_order, /* I - Minimum OrderDependency value */ 79 ppd_choice_t ***choices) /* O - Pointers to choices */ 80 { 81 ppd_choice_t *c; /* Current choice */ 82 ppd_section_t csection; /* Current section */ 83 float corder; /* Current OrderDependency value */ 84 int count; /* Number of choices collected */ 85 ppd_choice_t **collect; /* Collected choices */ 86 float *orders; /* Collected order values */ 87 88 89 DEBUG_printf(("ppdCollect2(ppd=%p, section=%d, min_order=%f, choices=%p)", 90 ppd, section, min_order, choices)); 91 92 if (!ppd || !choices) 93 { 94 if (choices) 95 *choices = NULL; 96 97 return (0); 98 } 99 100 /* 101 * Allocate memory for up to N selected choices... 102 */ 103 104 count = 0; 105 if ((collect = calloc(sizeof(ppd_choice_t *), 106 (size_t)cupsArrayCount(ppd->marked))) == NULL) 107 { 108 *choices = NULL; 109 return (0); 110 } 111 112 if ((orders = calloc(sizeof(float), (size_t)cupsArrayCount(ppd->marked))) == NULL) 113 { 114 *choices = NULL; 115 free(collect); 116 return (0); 117 } 118 119 /* 120 * Loop through all options and add choices as needed... 121 */ 122 123 for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked); 124 c; 125 c = (ppd_choice_t *)cupsArrayNext(ppd->marked)) 126 { 127 csection = c->option->section; 128 corder = c->option->order; 129 130 if (!strcmp(c->choice, "Custom")) 131 { 132 ppd_attr_t *attr; /* NonUIOrderDependency value */ 133 float aorder; /* Order value */ 134 char asection[17], /* Section name */ 135 amain[PPD_MAX_NAME + 1], 136 aoption[PPD_MAX_NAME]; 137 /* *CustomFoo and True */ 138 139 140 for (attr = ppdFindAttr(ppd, "NonUIOrderDependency", NULL); 141 attr; 142 attr = ppdFindNextAttr(ppd, "NonUIOrderDependency", NULL)) 143 if (attr->value && 144 sscanf(attr->value, "%f%16s%41s%40s", &aorder, asection, amain, 145 aoption) == 4 && 146 !strncmp(amain, "*Custom", 7) && 147 !strcmp(amain + 7, c->option->keyword) && !strcmp(aoption, "True")) 148 { 149 /* 150 * Use this NonUIOrderDependency... 151 */ 152 153 corder = aorder; 154 155 if (!strcmp(asection, "DocumentSetup")) 156 csection = PPD_ORDER_DOCUMENT; 157 else if (!strcmp(asection, "ExitServer")) 158 csection = PPD_ORDER_EXIT; 159 else if (!strcmp(asection, "JCLSetup")) 160 csection = PPD_ORDER_JCL; 161 else if (!strcmp(asection, "PageSetup")) 162 csection = PPD_ORDER_PAGE; 163 else if (!strcmp(asection, "Prolog")) 164 csection = PPD_ORDER_PROLOG; 165 else 166 csection = PPD_ORDER_ANY; 167 168 break; 169 } 170 } 171 172 if (csection == section && corder >= min_order) 173 { 174 collect[count] = c; 175 orders[count] = corder; 176 count ++; 177 } 178 } 179 180 /* 181 * If we have more than 1 marked choice, sort them... 182 */ 183 184 if (count > 1) 185 { 186 int i, j; /* Looping vars */ 187 188 for (i = 0; i < (count - 1); i ++) 189 for (j = i + 1; j < count; j ++) 190 if (orders[i] > orders[j]) 191 { 192 c = collect[i]; 193 corder = orders[i]; 194 collect[i] = collect[j]; 195 orders[i] = orders[j]; 196 collect[j] = c; 197 orders[j] = corder; 198 } 199 } 200 201 free(orders); 202 203 DEBUG_printf(("2ppdCollect2: %d marked choices...", count)); 204 205 /* 206 * Return the array and number of choices; if 0, free the array since 207 * it isn't needed. 208 */ 209 210 if (count > 0) 211 { 212 *choices = collect; 213 return (count); 214 } 215 else 216 { 217 *choices = NULL; 218 free(collect); 219 return (0); 220 } 221 } 222 223 224 /* 225 * 'ppdEmit()' - Emit code for marked options to a file. 226 */ 227 228 int /* O - 0 on success, -1 on failure */ 229 ppdEmit(ppd_file_t *ppd, /* I - PPD file record */ 230 FILE *fp, /* I - File to write to */ 231 ppd_section_t section) /* I - Section to write */ 232 { 233 return (ppdEmitAfterOrder(ppd, fp, section, 0, 0.0)); 234 } 235 236 237 /* 238 * 'ppdEmitAfterOrder()' - Emit a subset of the code for marked options to a file. 239 * 240 * When "limit" is non-zero, this function only emits options whose 241 * OrderDependency value is greater than or equal to "min_order". 242 * 243 * When "limit" is zero, this function is identical to ppdEmit(). 244 * 245 * @since CUPS 1.2/macOS 10.5@ 246 */ 247 248 int /* O - 0 on success, -1 on failure */ 249 ppdEmitAfterOrder( 250 ppd_file_t *ppd, /* I - PPD file record */ 251 FILE *fp, /* I - File to write to */ 252 ppd_section_t section, /* I - Section to write */ 253 int limit, /* I - Non-zero to use min_order */ 254 float min_order) /* I - Lowest OrderDependency */ 255 { 256 char *buffer; /* Option code */ 257 int status; /* Return status */ 258 259 260 /* 261 * Range check input... 262 */ 263 264 if (!ppd || !fp) 265 return (-1); 266 267 /* 268 * Get the string... 269 */ 270 271 buffer = ppdEmitString(ppd, section, limit ? min_order : 0.0f); 272 273 /* 274 * Write it as needed and return... 275 */ 276 277 if (buffer) 278 { 279 status = fputs(buffer, fp) < 0 ? -1 : 0; 280 281 free(buffer); 282 } 283 else 284 status = 0; 285 286 return (status); 287 } 288 289 290 /* 291 * 'ppdEmitFd()' - Emit code for marked options to a file. 292 */ 293 294 int /* O - 0 on success, -1 on failure */ 295 ppdEmitFd(ppd_file_t *ppd, /* I - PPD file record */ 296 int fd, /* I - File to write to */ 297 ppd_section_t section) /* I - Section to write */ 298 { 299 char *buffer, /* Option code */ 300 *bufptr; /* Pointer into code */ 301 size_t buflength; /* Length of option code */ 302 ssize_t bytes; /* Bytes written */ 303 int status; /* Return status */ 304 305 306 /* 307 * Range check input... 308 */ 309 310 if (!ppd || fd < 0) 311 return (-1); 312 313 /* 314 * Get the string... 315 */ 316 317 buffer = ppdEmitString(ppd, section, 0.0); 318 319 /* 320 * Write it as needed and return... 321 */ 322 323 if (buffer) 324 { 325 buflength = strlen(buffer); 326 bufptr = buffer; 327 bytes = 0; 328 329 while (buflength > 0) 330 { 331 #ifdef WIN32 332 if ((bytes = (ssize_t)write(fd, bufptr, (unsigned)buflength)) < 0) 333 #else 334 if ((bytes = write(fd, bufptr, buflength)) < 0) 335 #endif /* WIN32 */ 336 { 337 if (errno == EAGAIN || errno == EINTR) 338 continue; 339 340 break; 341 } 342 343 buflength -= (size_t)bytes; 344 bufptr += bytes; 345 } 346 347 status = bytes < 0 ? -1 : 0; 348 349 free(buffer); 350 } 351 else 352 status = 0; 353 354 return (status); 355 } 356 357 358 /* 359 * 'ppdEmitJCL()' - Emit code for JCL options to a file. 360 */ 361 362 int /* O - 0 on success, -1 on failure */ 363 ppdEmitJCL(ppd_file_t *ppd, /* I - PPD file record */ 364 FILE *fp, /* I - File to write to */ 365 int job_id, /* I - Job ID */ 366 const char *user, /* I - Username */ 367 const char *title) /* I - Title */ 368 { 369 char *ptr; /* Pointer into JCL string */ 370 char temp[65], /* Local title string */ 371 displaymsg[33]; /* Local display string */ 372 373 374 /* 375 * Range check the input... 376 */ 377 378 if (!ppd || !ppd->jcl_begin || !ppd->jcl_ps) 379 return (0); 380 381 /* 382 * See if the printer supports HP PJL... 383 */ 384 385 if (!strncmp(ppd->jcl_begin, "\033%-12345X@", 10)) 386 { 387 /* 388 * This printer uses HP PJL commands for output; filter the output 389 * so that we only have a single "@PJL JOB" command in the header... 390 * 391 * To avoid bugs in the PJL implementation of certain vendors' products 392 * (Xerox in particular), we add a dummy "@PJL" command at the beginning 393 * of the PJL commands to initialize PJL processing. 394 */ 395 396 ppd_attr_t *charset; /* PJL charset */ 397 ppd_attr_t *display; /* PJL display command */ 398 399 400 if ((charset = ppdFindAttr(ppd, "cupsPJLCharset", NULL)) != NULL) 401 { 402 if (!charset->value || _cups_strcasecmp(charset->value, "UTF-8")) 403 charset = NULL; 404 } 405 406 if ((display = ppdFindAttr(ppd, "cupsPJLDisplay", NULL)) != NULL) 407 { 408 if (!display->value) 409 display = NULL; 410 } 411 412 fputs("\033%-12345X@PJL\n", fp); 413 for (ptr = ppd->jcl_begin + 9; *ptr;) 414 if (!strncmp(ptr, "@PJL JOB", 8)) 415 { 416 /* 417 * Skip job command... 418 */ 419 420 for (;*ptr; ptr ++) 421 if (*ptr == '\n') 422 break; 423 424 if (*ptr) 425 ptr ++; 426 } 427 else 428 { 429 /* 430 * Copy line... 431 */ 432 433 for (;*ptr; ptr ++) 434 { 435 putc(*ptr, fp); 436 if (*ptr == '\n') 437 break; 438 } 439 440 if (*ptr) 441 ptr ++; 442 } 443 444 /* 445 * Clean up the job title... 446 */ 447 448 if ((ptr = strrchr(title, '/')) != NULL) 449 { 450 /* 451 * Only show basename of file path... 452 */ 453 454 title = ptr + 1; 455 } 456 457 if (!strncmp(title, "smbprn.", 7)) 458 { 459 /* 460 * Skip leading smbprn.######## from Samba jobs... 461 */ 462 463 for (title += 7; *title && isdigit(*title & 255); title ++); 464 while (_cups_isspace(*title)) 465 title ++; 466 467 if ((ptr = strstr(title, " - ")) != NULL) 468 { 469 /* 470 * Skip application name in "Some Application - Title of job"... 471 */ 472 473 title = ptr + 3; 474 } 475 } 476 477 /* 478 * Replace double quotes with single quotes and UTF-8 characters with 479 * question marks so that the title does not cause a PJL syntax error. 480 */ 481 482 strlcpy(temp, title, sizeof(temp)); 483 484 for (ptr = temp; *ptr; ptr ++) 485 if (*ptr == '\"') 486 *ptr = '\''; 487 else if (!charset && (*ptr & 128)) 488 *ptr = '?'; 489 490 /* 491 * CUPS STR #3125: Long PJL JOB NAME causes problems with some printers 492 * 493 * Generate the display message, truncating at 32 characters + nul to avoid 494 * issues with some printer's PJL implementations... 495 */ 496 497 snprintf(displaymsg, sizeof(displaymsg), "%d %s %s", job_id, user, temp); 498 499 /* 500 * Send PJL JOB and PJL RDYMSG commands before we enter PostScript mode... 501 */ 502 503 if (display && strcmp(display->value, "job")) 504 fprintf(fp, "@PJL JOB NAME = \"%s\"\n", temp); 505 else if (display && !strcmp(display->value, "rdymsg")) 506 fprintf(fp, "@PJL RDYMSG DISPLAY = \"%s\"\n", displaymsg); 507 else 508 fprintf(fp, "@PJL JOB NAME = \"%s\" DISPLAY = \"%s\"\n", temp, 509 displaymsg); 510 511 /* 512 * Replace double quotes with single quotes and UTF-8 characters with 513 * question marks so that the user does not cause a PJL syntax error. 514 */ 515 516 strlcpy(temp, user, sizeof(temp)); 517 518 for (ptr = temp; *ptr; ptr ++) 519 if (*ptr == '\"') 520 *ptr = '\''; 521 else if (!charset && (*ptr & 128)) 522 *ptr = '?'; 523 524 fprintf(fp, "@PJL SET USERNAME = \"%s\"\n", temp); 525 } 526 else 527 fputs(ppd->jcl_begin, fp); 528 529 ppdEmit(ppd, fp, PPD_ORDER_JCL); 530 fputs(ppd->jcl_ps, fp); 531 532 return (0); 533 } 534 535 536 /* 537 * 'ppdEmitJCLEnd()' - Emit JCLEnd code to a file. 538 * 539 * @since CUPS 1.2/macOS 10.5@ 540 */ 541 542 int /* O - 0 on success, -1 on failure */ 543 ppdEmitJCLEnd(ppd_file_t *ppd, /* I - PPD file record */ 544 FILE *fp) /* I - File to write to */ 545 { 546 /* 547 * Range check the input... 548 */ 549 550 if (!ppd) 551 return (0); 552 553 if (!ppd->jcl_end) 554 { 555 if (ppd->num_filters == 0) 556 putc(0x04, fp); 557 558 return (0); 559 } 560 561 /* 562 * See if the printer supports HP PJL... 563 */ 564 565 if (!strncmp(ppd->jcl_end, "\033%-12345X@", 10)) 566 { 567 /* 568 * This printer uses HP PJL commands for output; filter the output 569 * so that we only have a single "@PJL JOB" command in the header... 570 * 571 * To avoid bugs in the PJL implementation of certain vendors' products 572 * (Xerox in particular), we add a dummy "@PJL" command at the beginning 573 * of the PJL commands to initialize PJL processing. 574 */ 575 576 fputs("\033%-12345X@PJL\n", fp); 577 fputs("@PJL RDYMSG DISPLAY = \"\"\n", fp); 578 fputs(ppd->jcl_end + 9, fp); 579 } 580 else 581 fputs(ppd->jcl_end, fp); 582 583 return (0); 584 } 585 586 587 /* 588 * 'ppdEmitString()' - Get a string containing the code for marked options. 589 * 590 * When "min_order" is greater than zero, this function only includes options 591 * whose OrderDependency value is greater than or equal to "min_order". 592 * Otherwise, all options in the specified section are included in the 593 * returned string. 594 * 595 * The return string is allocated on the heap and should be freed using 596 * @code free@ when you are done with it. 597 * 598 * @since CUPS 1.2/macOS 10.5@ 599 */ 600 601 char * /* O - String containing option code or @code NULL@ if there is no option code */ 602 ppdEmitString(ppd_file_t *ppd, /* I - PPD file record */ 603 ppd_section_t section, /* I - Section to write */ 604 float min_order) /* I - Lowest OrderDependency */ 605 { 606 int i, j, /* Looping vars */ 607 count; /* Number of choices */ 608 ppd_choice_t **choices; /* Choices */ 609 ppd_size_t *size; /* Custom page size */ 610 ppd_coption_t *coption; /* Custom option */ 611 ppd_cparam_t *cparam; /* Custom parameter */ 612 size_t bufsize; /* Size of string buffer needed */ 613 char *buffer, /* String buffer */ 614 *bufptr, /* Pointer into buffer */ 615 *bufend; /* End of buffer */ 616 struct lconv *loc; /* Locale data */ 617 618 619 DEBUG_printf(("ppdEmitString(ppd=%p, section=%d, min_order=%f)", 620 ppd, section, min_order)); 621 622 /* 623 * Range check input... 624 */ 625 626 if (!ppd) 627 return (NULL); 628 629 /* 630 * Use PageSize or PageRegion as required... 631 */ 632 633 ppd_handle_media(ppd); 634 635 /* 636 * Collect the options we need to emit... 637 */ 638 639 if ((count = ppdCollect2(ppd, section, min_order, &choices)) == 0) 640 return (NULL); 641 642 /* 643 * Count the number of bytes that are required to hold all of the 644 * option code... 645 */ 646 647 for (i = 0, bufsize = 1; i < count; i ++) 648 { 649 if (section == PPD_ORDER_JCL) 650 { 651 if (!_cups_strcasecmp(choices[i]->choice, "Custom") && 652 (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword)) 653 != NULL) 654 { 655 /* 656 * Add space to account for custom parameter substitution... 657 */ 658 659 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); 660 cparam; 661 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params)) 662 { 663 switch (cparam->type) 664 { 665 case PPD_CUSTOM_CURVE : 666 case PPD_CUSTOM_INVCURVE : 667 case PPD_CUSTOM_POINTS : 668 case PPD_CUSTOM_REAL : 669 case PPD_CUSTOM_INT : 670 bufsize += 10; 671 break; 672 673 case PPD_CUSTOM_PASSCODE : 674 case PPD_CUSTOM_PASSWORD : 675 case PPD_CUSTOM_STRING : 676 if (cparam->current.custom_string) 677 bufsize += strlen(cparam->current.custom_string); 678 break; 679 } 680 } 681 } 682 } 683 else if (section != PPD_ORDER_EXIT) 684 { 685 bufsize += 3; /* [{\n */ 686 687 if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") || 688 !_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) && 689 !_cups_strcasecmp(choices[i]->choice, "Custom")) 690 { 691 DEBUG_puts("2ppdEmitString: Custom size set!"); 692 693 bufsize += 37; /* %%BeginFeature: *CustomPageSize True\n */ 694 bufsize += 50; /* Five 9-digit numbers + newline */ 695 } 696 else if (!_cups_strcasecmp(choices[i]->choice, "Custom") && 697 (coption = ppdFindCustomOption(ppd, 698 choices[i]->option->keyword)) 699 != NULL) 700 { 701 bufsize += 23 + strlen(choices[i]->option->keyword) + 6; 702 /* %%BeginFeature: *Customkeyword True\n */ 703 704 705 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); 706 cparam; 707 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params)) 708 { 709 switch (cparam->type) 710 { 711 case PPD_CUSTOM_CURVE : 712 case PPD_CUSTOM_INVCURVE : 713 case PPD_CUSTOM_POINTS : 714 case PPD_CUSTOM_REAL : 715 case PPD_CUSTOM_INT : 716 bufsize += 10; 717 break; 718 719 case PPD_CUSTOM_PASSCODE : 720 case PPD_CUSTOM_PASSWORD : 721 case PPD_CUSTOM_STRING : 722 bufsize += 3; 723 if (cparam->current.custom_string) 724 bufsize += 4 * strlen(cparam->current.custom_string); 725 break; 726 } 727 } 728 } 729 else 730 bufsize += 17 + strlen(choices[i]->option->keyword) + 1 + 731 strlen(choices[i]->choice) + 1; 732 /* %%BeginFeature: *keyword choice\n */ 733 734 bufsize += 13; /* %%EndFeature\n */ 735 bufsize += 22; /* } stopped cleartomark\n */ 736 } 737 738 if (choices[i]->code) 739 bufsize += strlen(choices[i]->code) + 1; 740 else 741 bufsize += strlen(ppd_custom_code); 742 } 743 744 /* 745 * Allocate memory... 746 */ 747 748 DEBUG_printf(("2ppdEmitString: Allocating %d bytes for string...", 749 (int)bufsize)); 750 751 if ((buffer = calloc(1, bufsize)) == NULL) 752 { 753 free(choices); 754 return (NULL); 755 } 756 757 bufend = buffer + bufsize - 1; 758 loc = localeconv(); 759 760 /* 761 * Copy the option code to the buffer... 762 */ 763 764 for (i = 0, bufptr = buffer; i < count; i ++, bufptr += strlen(bufptr)) 765 if (section == PPD_ORDER_JCL) 766 { 767 if (!_cups_strcasecmp(choices[i]->choice, "Custom") && 768 choices[i]->code && 769 (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword)) 770 != NULL) 771 { 772 /* 773 * Handle substitutions in custom JCL options... 774 */ 775 776 char *cptr; /* Pointer into code */ 777 int pnum; /* Parameter number */ 778 779 780 for (cptr = choices[i]->code; *cptr && bufptr < bufend;) 781 { 782 if (*cptr == '\\') 783 { 784 cptr ++; 785 786 if (isdigit(*cptr & 255)) 787 { 788 /* 789 * Substitute parameter... 790 */ 791 792 pnum = *cptr++ - '0'; 793 while (isdigit(*cptr & 255)) 794 pnum = pnum * 10 + *cptr++ - '0'; 795 796 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); 797 cparam; 798 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params)) 799 if (cparam->order == pnum) 800 break; 801 802 if (cparam) 803 { 804 switch (cparam->type) 805 { 806 case PPD_CUSTOM_CURVE : 807 case PPD_CUSTOM_INVCURVE : 808 case PPD_CUSTOM_POINTS : 809 case PPD_CUSTOM_REAL : 810 bufptr = _cupsStrFormatd(bufptr, bufend, 811 cparam->current.custom_real, 812 loc); 813 break; 814 815 case PPD_CUSTOM_INT : 816 snprintf(bufptr, (size_t)(bufend - bufptr), "%d", cparam->current.custom_int); 817 bufptr += strlen(bufptr); 818 break; 819 820 case PPD_CUSTOM_PASSCODE : 821 case PPD_CUSTOM_PASSWORD : 822 case PPD_CUSTOM_STRING : 823 if (cparam->current.custom_string) 824 { 825 strlcpy(bufptr, cparam->current.custom_string, (size_t)(bufend - bufptr)); 826 bufptr += strlen(bufptr); 827 } 828 break; 829 } 830 } 831 } 832 else if (*cptr) 833 *bufptr++ = *cptr++; 834 } 835 else 836 *bufptr++ = *cptr++; 837 } 838 } 839 else 840 { 841 /* 842 * Otherwise just copy the option code directly... 843 */ 844 845 strlcpy(bufptr, choices[i]->code, (size_t)(bufend - bufptr + 1)); 846 bufptr += strlen(bufptr); 847 } 848 } 849 else if (section != PPD_ORDER_EXIT) 850 { 851 /* 852 * Add wrapper commands to prevent printer errors for unsupported 853 * options... 854 */ 855 856 strlcpy(bufptr, "[{\n", (size_t)(bufend - bufptr + 1)); 857 bufptr += 3; 858 859 /* 860 * Send DSC comments with option... 861 */ 862 863 DEBUG_printf(("2ppdEmitString: Adding code for %s=%s...", 864 choices[i]->option->keyword, choices[i]->choice)); 865 866 if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") || 867 !_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) && 868 !_cups_strcasecmp(choices[i]->choice, "Custom")) 869 { 870 /* 871 * Variable size; write out standard size options, using the 872 * parameter positions defined in the PPD file... 873 */ 874 875 ppd_attr_t *attr; /* PPD attribute */ 876 int pos, /* Position of custom value */ 877 orientation; /* Orientation to use */ 878 float values[5]; /* Values for custom command */ 879 880 881 strlcpy(bufptr, "%%BeginFeature: *CustomPageSize True\n", (size_t)(bufend - bufptr + 1)); 882 bufptr += 37; 883 884 size = ppdPageSize(ppd, "Custom"); 885 886 memset(values, 0, sizeof(values)); 887 888 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Width")) != NULL) 889 { 890 pos = atoi(attr->value) - 1; 891 892 if (pos < 0 || pos > 4) 893 pos = 0; 894 } 895 else 896 pos = 0; 897 898 values[pos] = size->width; 899 900 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Height")) != NULL) 901 { 902 pos = atoi(attr->value) - 1; 903 904 if (pos < 0 || pos > 4) 905 pos = 1; 906 } 907 else 908 pos = 1; 909 910 values[pos] = size->length; 911 912 /* 913 * According to the Adobe PPD specification, an orientation of 1 914 * will produce a print that comes out upside-down with the X 915 * axis perpendicular to the direction of feed, which is exactly 916 * what we want to be consistent with non-PS printers. 917 * 918 * We could also use an orientation of 3 to produce output that 919 * comes out rightside-up (this is the default for many large format 920 * printer PPDs), however for consistency we will stick with the 921 * value 1. 922 * 923 * If we wanted to get fancy, we could use orientations of 0 or 924 * 2 and swap the width and length, however we don't want to get 925 * fancy, we just want it to work consistently. 926 * 927 * The orientation value is range limited by the Orientation 928 * parameter definition, so certain non-PS printer drivers that 929 * only support an Orientation of 0 will get the value 0 as 930 * expected. 931 */ 932 933 orientation = 1; 934 935 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", 936 "Orientation")) != NULL) 937 { 938 int min_orient, max_orient; /* Minimum and maximum orientations */ 939 940 941 if (sscanf(attr->value, "%d%*s%d%d", &pos, &min_orient, 942 &max_orient) != 3) 943 pos = 4; 944 else 945 { 946 pos --; 947 948 if (pos < 0 || pos > 4) 949 pos = 4; 950 951 if (orientation > max_orient) 952 orientation = max_orient; 953 else if (orientation < min_orient) 954 orientation = min_orient; 955 } 956 } 957 else 958 pos = 4; 959 960 values[pos] = (float)orientation; 961 962 for (pos = 0; pos < 5; pos ++) 963 { 964 bufptr = _cupsStrFormatd(bufptr, bufend, values[pos], loc); 965 *bufptr++ = '\n'; 966 } 967 968 if (!choices[i]->code) 969 { 970 /* 971 * This can happen with certain buggy PPD files that don't include 972 * a CustomPageSize command sequence... We just use a generic 973 * Level 2 command sequence... 974 */ 975 976 strlcpy(bufptr, ppd_custom_code, (size_t)(bufend - bufptr + 1)); 977 bufptr += strlen(bufptr); 978 } 979 } 980 else if (!_cups_strcasecmp(choices[i]->choice, "Custom") && 981 (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword)) 982 != NULL) 983 { 984 /* 985 * Custom option... 986 */ 987 988 const char *s; /* Pointer into string value */ 989 cups_array_t *params; /* Parameters in the correct output order */ 990 991 992 params = cupsArrayNew((cups_array_func_t)ppd_compare_cparams, NULL); 993 994 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); 995 cparam; 996 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params)) 997 cupsArrayAdd(params, cparam); 998 999 snprintf(bufptr, (size_t)(bufend - bufptr + 1), "%%%%BeginFeature: *Custom%s True\n", coption->keyword); 1000 bufptr += strlen(bufptr); 1001 1002 for (cparam = (ppd_cparam_t *)cupsArrayFirst(params); 1003 cparam; 1004 cparam = (ppd_cparam_t *)cupsArrayNext(params)) 1005 { 1006 switch (cparam->type) 1007 { 1008 case PPD_CUSTOM_CURVE : 1009 case PPD_CUSTOM_INVCURVE : 1010 case PPD_CUSTOM_POINTS : 1011 case PPD_CUSTOM_REAL : 1012 bufptr = _cupsStrFormatd(bufptr, bufend, 1013 cparam->current.custom_real, loc); 1014 *bufptr++ = '\n'; 1015 break; 1016 1017 case PPD_CUSTOM_INT : 1018 snprintf(bufptr, (size_t)(bufend - bufptr + 1), "%d\n", cparam->current.custom_int); 1019 bufptr += strlen(bufptr); 1020 break; 1021 1022 case PPD_CUSTOM_PASSCODE : 1023 case PPD_CUSTOM_PASSWORD : 1024 case PPD_CUSTOM_STRING : 1025 *bufptr++ = '('; 1026 1027 if (cparam->current.custom_string) 1028 { 1029 for (s = cparam->current.custom_string; *s; s ++) 1030 { 1031 if (*s < ' ' || *s == '(' || *s == ')' || *s >= 127) 1032 { 1033 snprintf(bufptr, (size_t)(bufend - bufptr + 1), "\\%03o", *s & 255); 1034 bufptr += strlen(bufptr); 1035 } 1036 else 1037 *bufptr++ = *s; 1038 } 1039 } 1040 1041 *bufptr++ = ')'; 1042 *bufptr++ = '\n'; 1043 break; 1044 } 1045 } 1046 1047 cupsArrayDelete(params); 1048 } 1049 else 1050 { 1051 snprintf(bufptr, (size_t)(bufend - bufptr + 1), "%%%%BeginFeature: *%s %s\n", choices[i]->option->keyword, choices[i]->choice); 1052 bufptr += strlen(bufptr); 1053 } 1054 1055 if (choices[i]->code && choices[i]->code[0]) 1056 { 1057 j = (int)strlen(choices[i]->code); 1058 memcpy(bufptr, choices[i]->code, (size_t)j); 1059 bufptr += j; 1060 1061 if (choices[i]->code[j - 1] != '\n') 1062 *bufptr++ = '\n'; 1063 } 1064 1065 strlcpy(bufptr, "%%EndFeature\n" 1066 "} stopped cleartomark\n", (size_t)(bufend - bufptr + 1)); 1067 bufptr += strlen(bufptr); 1068 1069 DEBUG_printf(("2ppdEmitString: Offset in string is %d...", 1070 (int)(bufptr - buffer))); 1071 } 1072 else 1073 { 1074 strlcpy(bufptr, choices[i]->code, (size_t)(bufend - bufptr + 1)); 1075 bufptr += strlen(bufptr); 1076 } 1077 1078 /* 1079 * Nul-terminate, free, and return... 1080 */ 1081 1082 *bufptr = '\0'; 1083 1084 free(choices); 1085 1086 return (buffer); 1087 } 1088 1089 1090 /* 1091 * 'ppd_compare_cparams()' - Compare the order of two custom parameters. 1092 */ 1093 1094 static int /* O - Result of comparison */ 1095 ppd_compare_cparams(ppd_cparam_t *a, /* I - First parameter */ 1096 ppd_cparam_t *b) /* I - Second parameter */ 1097 { 1098 return (a->order - b->order); 1099 } 1100 1101 1102 /* 1103 * 'ppd_handle_media()' - Handle media selection... 1104 */ 1105 1106 static void 1107 ppd_handle_media(ppd_file_t *ppd) /* I - PPD file */ 1108 { 1109 ppd_choice_t *manual_feed, /* ManualFeed choice, if any */ 1110 *input_slot; /* InputSlot choice, if any */ 1111 ppd_size_t *size; /* Current media size */ 1112 ppd_attr_t *rpr; /* RequiresPageRegion value */ 1113 1114 1115 /* 1116 * This function determines what page size code to use, if any, for the 1117 * current media size, InputSlot, and ManualFeed selections. 1118 * 1119 * We use the PageSize code if: 1120 * 1121 * 1. A custom media size is selected. 1122 * 2. ManualFeed and InputSlot are not selected (or do not exist). 1123 * 3. ManualFeed is selected but is False and InputSlot is not selected or 1124 * the selection has no code - the latter check done to support "auto" or 1125 * "printer default" InputSlot options. 1126 * 1127 * We use the PageRegion code if: 1128 * 1129 * 4. RequiresPageRegion does not exist and the PPD contains cupsFilter 1130 * keywords, indicating this is a CUPS-based driver. 1131 * 5. RequiresPageRegion exists for the selected InputSlot (or "All" for any 1132 * InputSlot or ManualFeed selection) and is True. 1133 * 1134 * If none of the 5 conditions are true, no page size code is used and we 1135 * unmark any existing PageSize or PageRegion choices. 1136 */ 1137 1138 if ((size = ppdPageSize(ppd, NULL)) == NULL) 1139 return; 1140 1141 manual_feed = ppdFindMarkedChoice(ppd, "ManualFeed"); 1142 input_slot = ppdFindMarkedChoice(ppd, "InputSlot"); 1143 1144 if (input_slot != NULL) 1145 rpr = ppdFindAttr(ppd, "RequiresPageRegion", input_slot->choice); 1146 else 1147 rpr = NULL; 1148 1149 if (!rpr) 1150 rpr = ppdFindAttr(ppd, "RequiresPageRegion", "All"); 1151 1152 if (!_cups_strcasecmp(size->name, "Custom") || 1153 (!manual_feed && !input_slot) || 1154 (manual_feed && !_cups_strcasecmp(manual_feed->choice, "False") && 1155 (!input_slot || (input_slot->code && !input_slot->code[0]))) || 1156 (!rpr && ppd->num_filters > 0)) 1157 { 1158 /* 1159 * Use PageSize code... 1160 */ 1161 1162 ppdMarkOption(ppd, "PageSize", size->name); 1163 } 1164 else if (rpr && rpr->value && !_cups_strcasecmp(rpr->value, "True")) 1165 { 1166 /* 1167 * Use PageRegion code... 1168 */ 1169 1170 ppdMarkOption(ppd, "PageRegion", size->name); 1171 } 1172 else 1173 { 1174 /* 1175 * Do not use PageSize or PageRegion code... 1176 */ 1177 1178 ppd_choice_t *page; /* PageSize/Region choice, if any */ 1179 1180 if ((page = ppdFindMarkedChoice(ppd, "PageSize")) != NULL) 1181 { 1182 /* 1183 * Unmark PageSize... 1184 */ 1185 1186 page->marked = 0; 1187 cupsArrayRemove(ppd->marked, page); 1188 } 1189 1190 if ((page = ppdFindMarkedChoice(ppd, "PageRegion")) != NULL) 1191 { 1192 /* 1193 * Unmark PageRegion... 1194 */ 1195 1196 page->marked = 0; 1197 cupsArrayRemove(ppd->marked, page); 1198 } 1199 } 1200 } 1201