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