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