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