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