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