]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/emit.c
Fix build errors on Fedora.
[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, (size_t)(bufend - bufptr), "%d", cparam->current.custom_int);
818 bufptr += strlen(bufptr);
819 break;
820
821 case PPD_CUSTOM_PASSCODE :
822 case PPD_CUSTOM_PASSWORD :
823 case PPD_CUSTOM_STRING :
824 if (cparam->current.custom_string)
825 {
826 strlcpy(bufptr, cparam->current.custom_string, (size_t)(bufend - bufptr));
827 bufptr += strlen(bufptr);
828 }
829 break;
830 }
831 }
832 }
833 else if (*cptr)
834 *bufptr++ = *cptr++;
835 }
836 else
837 *bufptr++ = *cptr++;
838 }
839 }
840 else
841 {
842 /*
843 * Otherwise just copy the option code directly...
844 */
845
846 strlcpy(bufptr, choices[i]->code, (size_t)(bufend - bufptr + 1));
847 bufptr += strlen(bufptr);
848 }
849 }
850 else if (section != PPD_ORDER_EXIT)
851 {
852 /*
853 * Add wrapper commands to prevent printer errors for unsupported
854 * options...
855 */
856
857 strlcpy(bufptr, "[{\n", (size_t)(bufend - bufptr + 1));
858 bufptr += 3;
859
860 /*
861 * Send DSC comments with option...
862 */
863
864 DEBUG_printf(("2ppdEmitString: Adding code for %s=%s...",
865 choices[i]->option->keyword, choices[i]->choice));
866
867 if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") ||
868 !_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
869 !_cups_strcasecmp(choices[i]->choice, "Custom"))
870 {
871 /*
872 * Variable size; write out standard size options, using the
873 * parameter positions defined in the PPD file...
874 */
875
876 ppd_attr_t *attr; /* PPD attribute */
877 int pos, /* Position of custom value */
878 orientation; /* Orientation to use */
879 float values[5]; /* Values for custom command */
880
881
882 strlcpy(bufptr, "%%BeginFeature: *CustomPageSize True\n", (size_t)(bufend - bufptr + 1));
883 bufptr += 37;
884
885 size = ppdPageSize(ppd, "Custom");
886
887 memset(values, 0, sizeof(values));
888
889 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Width")) != NULL)
890 {
891 pos = atoi(attr->value) - 1;
892
893 if (pos < 0 || pos > 4)
894 pos = 0;
895 }
896 else
897 pos = 0;
898
899 values[pos] = size->width;
900
901 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Height")) != NULL)
902 {
903 pos = atoi(attr->value) - 1;
904
905 if (pos < 0 || pos > 4)
906 pos = 1;
907 }
908 else
909 pos = 1;
910
911 values[pos] = size->length;
912
913 /*
914 * According to the Adobe PPD specification, an orientation of 1
915 * will produce a print that comes out upside-down with the X
916 * axis perpendicular to the direction of feed, which is exactly
917 * what we want to be consistent with non-PS printers.
918 *
919 * We could also use an orientation of 3 to produce output that
920 * comes out rightside-up (this is the default for many large format
921 * printer PPDs), however for consistency we will stick with the
922 * value 1.
923 *
924 * If we wanted to get fancy, we could use orientations of 0 or
925 * 2 and swap the width and length, however we don't want to get
926 * fancy, we just want it to work consistently.
927 *
928 * The orientation value is range limited by the Orientation
929 * parameter definition, so certain non-PS printer drivers that
930 * only support an Orientation of 0 will get the value 0 as
931 * expected.
932 */
933
934 orientation = 1;
935
936 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize",
937 "Orientation")) != NULL)
938 {
939 int min_orient, max_orient; /* Minimum and maximum orientations */
940
941
942 if (sscanf(attr->value, "%d%*s%d%d", &pos, &min_orient,
943 &max_orient) != 3)
944 pos = 4;
945 else
946 {
947 pos --;
948
949 if (pos < 0 || pos > 4)
950 pos = 4;
951
952 if (orientation > max_orient)
953 orientation = max_orient;
954 else if (orientation < min_orient)
955 orientation = min_orient;
956 }
957 }
958 else
959 pos = 4;
960
961 values[pos] = (float)orientation;
962
963 for (pos = 0; pos < 5; pos ++)
964 {
965 bufptr = _cupsStrFormatd(bufptr, bufend, values[pos], loc);
966 *bufptr++ = '\n';
967 }
968
969 if (!choices[i]->code)
970 {
971 /*
972 * This can happen with certain buggy PPD files that don't include
973 * a CustomPageSize command sequence... We just use a generic
974 * Level 2 command sequence...
975 */
976
977 strlcpy(bufptr, ppd_custom_code, (size_t)(bufend - bufptr + 1));
978 bufptr += strlen(bufptr);
979 }
980 }
981 else if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
982 (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
983 != NULL)
984 {
985 /*
986 * Custom option...
987 */
988
989 const char *s; /* Pointer into string value */
990 cups_array_t *params; /* Parameters in the correct output order */
991
992
993 params = cupsArrayNew((cups_array_func_t)ppd_compare_cparams, NULL);
994
995 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
996 cparam;
997 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
998 cupsArrayAdd(params, cparam);
999
1000 snprintf(bufptr, (size_t)(bufend - bufptr + 1), "%%%%BeginFeature: *Custom%s True\n", coption->keyword);
1001 bufptr += strlen(bufptr);
1002
1003 for (cparam = (ppd_cparam_t *)cupsArrayFirst(params);
1004 cparam;
1005 cparam = (ppd_cparam_t *)cupsArrayNext(params))
1006 {
1007 switch (cparam->type)
1008 {
1009 case PPD_CUSTOM_CURVE :
1010 case PPD_CUSTOM_INVCURVE :
1011 case PPD_CUSTOM_POINTS :
1012 case PPD_CUSTOM_REAL :
1013 bufptr = _cupsStrFormatd(bufptr, bufend,
1014 cparam->current.custom_real, loc);
1015 *bufptr++ = '\n';
1016 break;
1017
1018 case PPD_CUSTOM_INT :
1019 snprintf(bufptr, (size_t)(bufend - bufptr + 1), "%d\n", cparam->current.custom_int);
1020 bufptr += strlen(bufptr);
1021 break;
1022
1023 case PPD_CUSTOM_PASSCODE :
1024 case PPD_CUSTOM_PASSWORD :
1025 case PPD_CUSTOM_STRING :
1026 *bufptr++ = '(';
1027
1028 if (cparam->current.custom_string)
1029 {
1030 for (s = cparam->current.custom_string; *s; s ++)
1031 {
1032 if (*s < ' ' || *s == '(' || *s == ')' || *s >= 127)
1033 {
1034 snprintf(bufptr, (size_t)(bufend - bufptr + 1), "\\%03o", *s & 255);
1035 bufptr += strlen(bufptr);
1036 }
1037 else
1038 *bufptr++ = *s;
1039 }
1040 }
1041
1042 *bufptr++ = ')';
1043 *bufptr++ = '\n';
1044 break;
1045 }
1046 }
1047
1048 cupsArrayDelete(params);
1049 }
1050 else
1051 {
1052 snprintf(bufptr, (size_t)(bufend - bufptr + 1), "%%%%BeginFeature: *%s %s\n", choices[i]->option->keyword, choices[i]->choice);
1053 bufptr += strlen(bufptr);
1054 }
1055
1056 if (choices[i]->code && choices[i]->code[0])
1057 {
1058 j = (int)strlen(choices[i]->code);
1059 memcpy(bufptr, choices[i]->code, (size_t)j);
1060 bufptr += j;
1061
1062 if (choices[i]->code[j - 1] != '\n')
1063 *bufptr++ = '\n';
1064 }
1065
1066 strlcpy(bufptr, "%%EndFeature\n"
1067 "} stopped cleartomark\n", (size_t)(bufend - bufptr + 1));
1068 bufptr += strlen(bufptr);
1069
1070 DEBUG_printf(("2ppdEmitString: Offset in string is %d...",
1071 (int)(bufptr - buffer)));
1072 }
1073 else
1074 {
1075 strlcpy(bufptr, choices[i]->code, (size_t)(bufend - bufptr + 1));
1076 bufptr += strlen(bufptr);
1077 }
1078
1079 /*
1080 * Nul-terminate, free, and return...
1081 */
1082
1083 *bufptr = '\0';
1084
1085 free(choices);
1086
1087 return (buffer);
1088 }
1089
1090
1091 /*
1092 * 'ppd_compare_cparams()' - Compare the order of two custom parameters.
1093 */
1094
1095 static int /* O - Result of comparison */
1096 ppd_compare_cparams(ppd_cparam_t *a, /* I - First parameter */
1097 ppd_cparam_t *b) /* I - Second parameter */
1098 {
1099 return (a->order - b->order);
1100 }
1101
1102
1103 /*
1104 * 'ppd_handle_media()' - Handle media selection...
1105 */
1106
1107 static void
1108 ppd_handle_media(ppd_file_t *ppd) /* I - PPD file */
1109 {
1110 ppd_choice_t *manual_feed, /* ManualFeed choice, if any */
1111 *input_slot; /* InputSlot choice, if any */
1112 ppd_size_t *size; /* Current media size */
1113 ppd_attr_t *rpr; /* RequiresPageRegion value */
1114
1115
1116 /*
1117 * This function determines what page size code to use, if any, for the
1118 * current media size, InputSlot, and ManualFeed selections.
1119 *
1120 * We use the PageSize code if:
1121 *
1122 * 1. A custom media size is selected.
1123 * 2. ManualFeed and InputSlot are not selected (or do not exist).
1124 * 3. ManualFeed is selected but is False and InputSlot is not selected or
1125 * the selection has no code - the latter check done to support "auto" or
1126 * "printer default" InputSlot options.
1127 *
1128 * We use the PageRegion code if:
1129 *
1130 * 4. RequiresPageRegion does not exist and the PPD contains cupsFilter
1131 * keywords, indicating this is a CUPS-based driver.
1132 * 5. RequiresPageRegion exists for the selected InputSlot (or "All" for any
1133 * InputSlot or ManualFeed selection) and is True.
1134 *
1135 * If none of the 5 conditions are true, no page size code is used and we
1136 * unmark any existing PageSize or PageRegion choices.
1137 */
1138
1139 if ((size = ppdPageSize(ppd, NULL)) == NULL)
1140 return;
1141
1142 manual_feed = ppdFindMarkedChoice(ppd, "ManualFeed");
1143 input_slot = ppdFindMarkedChoice(ppd, "InputSlot");
1144
1145 if (input_slot != NULL)
1146 rpr = ppdFindAttr(ppd, "RequiresPageRegion", input_slot->choice);
1147 else
1148 rpr = NULL;
1149
1150 if (!rpr)
1151 rpr = ppdFindAttr(ppd, "RequiresPageRegion", "All");
1152
1153 if (!_cups_strcasecmp(size->name, "Custom") ||
1154 (!manual_feed && !input_slot) ||
1155 (manual_feed && !_cups_strcasecmp(manual_feed->choice, "False") &&
1156 (!input_slot || (input_slot->code && !input_slot->code[0]))) ||
1157 (!rpr && ppd->num_filters > 0))
1158 {
1159 /*
1160 * Use PageSize code...
1161 */
1162
1163 ppdMarkOption(ppd, "PageSize", size->name);
1164 }
1165 else if (rpr && rpr->value && !_cups_strcasecmp(rpr->value, "True"))
1166 {
1167 /*
1168 * Use PageRegion code...
1169 */
1170
1171 ppdMarkOption(ppd, "PageRegion", size->name);
1172 }
1173 else
1174 {
1175 /*
1176 * Do not use PageSize or PageRegion code...
1177 */
1178
1179 ppd_choice_t *page; /* PageSize/Region choice, if any */
1180
1181 if ((page = ppdFindMarkedChoice(ppd, "PageSize")) != NULL)
1182 {
1183 /*
1184 * Unmark PageSize...
1185 */
1186
1187 page->marked = 0;
1188 cupsArrayRemove(ppd->marked, page);
1189 }
1190
1191 if ((page = ppdFindMarkedChoice(ppd, "PageRegion")) != NULL)
1192 {
1193 /*
1194 * Unmark PageRegion...
1195 */
1196
1197 page->marked = 0;
1198 cupsArrayRemove(ppd->marked, page);
1199 }
1200 }
1201 }
1202
1203
1204 /*
1205 * End of "$Id$".
1206 */