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