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