]> git.ipfire.org Git - thirdparty/cups.git/blame_incremental - cups/emit.c
Merge changes from CUPS 1.4svn-r7961.
[thirdparty/cups.git] / cups / emit.c
... / ...
CommitLineData
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-2008 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
58static int ppd_compare_cparams(ppd_cparam_t *a, ppd_cparam_t *b);
59static void ppd_handle_media(ppd_file_t *ppd);
60
61
62/*
63 * Local globals...
64 */
65
66static 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
79int /* O - Number of options marked */
80ppdCollect(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@
96 */
97
98int /* O - Number of options marked */
99ppdCollect2(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)\n",
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(("ppdCollect2: %d marked choices...\n", 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
251int /* O - 0 on success, -1 on failure */
252ppdEmit(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@
269 */
270
271int /* O - 0 on success, -1 on failure */
272ppdEmitAfterOrder(
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
317int /* O - 0 on success, -1 on failure */
318ppdEmitFd(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
385int /* O - 0 on success, -1 on failure */
386ppdEmitJCL(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[81]; /* Local title string */
394
395
396 /*
397 * Range check the input...
398 */
399
400 if (!ppd || !ppd->jcl_begin || !ppd->jcl_ps)
401 return (0);
402
403 /*
404 * See if the printer supports HP PJL...
405 */
406
407 if (!strncmp(ppd->jcl_begin, "\033%-12345X@", 10))
408 {
409 /*
410 * This printer uses HP PJL commands for output; filter the output
411 * so that we only have a single "@PJL JOB" command in the header...
412 *
413 * To avoid bugs in the PJL implementation of certain vendors' products
414 * (Xerox in particular), we add a dummy "@PJL" command at the beginning
415 * of the PJL commands to initialize PJL processing.
416 */
417
418 ppd_attr_t *charset; /* PJL charset */
419 ppd_attr_t *display; /* PJL display command */
420
421
422 if ((charset = ppdFindAttr(ppd, "cupsPJLCharset", NULL)) != NULL)
423 {
424 if (!charset->value || strcasecmp(charset->value, "UTF-8"))
425 charset = NULL;
426 }
427
428 if ((display = ppdFindAttr(ppd, "cupsPJLDisplay", NULL)) != NULL)
429 {
430 if (!display->value)
431 display = NULL;
432 }
433
434 fputs("\033%-12345X@PJL\n", fp);
435 for (ptr = ppd->jcl_begin + 9; *ptr;)
436 if (!strncmp(ptr, "@PJL JOB", 8))
437 {
438 /*
439 * Skip job command...
440 */
441
442 for (;*ptr; ptr ++)
443 if (*ptr == '\n')
444 break;
445
446 if (*ptr)
447 ptr ++;
448 }
449 else
450 {
451 /*
452 * Copy line...
453 */
454
455 for (;*ptr; ptr ++)
456 {
457 putc(*ptr, fp);
458 if (*ptr == '\n')
459 break;
460 }
461
462 if (*ptr)
463 ptr ++;
464 }
465
466 /*
467 * Eliminate any path info from the job title...
468 */
469
470 if ((ptr = strrchr(title, '/')) != NULL)
471 title = ptr + 1;
472
473 /*
474 * Replace double quotes with single quotes and 8-bit characters with
475 * question marks so that the title does not cause a PJL syntax error.
476 */
477
478 strlcpy(temp, title, sizeof(temp));
479
480 for (ptr = temp; *ptr; ptr ++)
481 if (*ptr == '\"')
482 *ptr = '\'';
483 else if (charset && (*ptr & 128))
484 *ptr = '?';
485
486 /*
487 * Send PJL JOB and PJL RDYMSG commands before we enter PostScript mode...
488 */
489
490 if (display && strcmp(display->value, "job"))
491 {
492 fprintf(fp, "@PJL JOB NAME = \"%s\"\n", temp);
493
494 if (display && !strcmp(display->value, "rdymsg"))
495 fprintf(fp, "@PJL RDYMSG DISPLAY = \"%d %s %s\"\n", job_id, user, temp);
496 }
497 else
498 fprintf(fp, "@PJL JOB NAME = \"%s\" DISPLAY = \"%d %s %s\"\n", temp,
499 job_id, user, temp);
500 }
501 else
502 fputs(ppd->jcl_begin, fp);
503
504 ppdEmit(ppd, fp, PPD_ORDER_JCL);
505 fputs(ppd->jcl_ps, fp);
506
507 return (0);
508}
509
510
511/*
512 * 'ppdEmitJCLEnd()' - Emit JCLEnd code to a file.
513 *
514 * @since CUPS 1.2@
515 */
516
517int /* O - 0 on success, -1 on failure */
518ppdEmitJCLEnd(ppd_file_t *ppd, /* I - PPD file record */
519 FILE *fp) /* I - File to write to */
520{
521 /*
522 * Range check the input...
523 */
524
525 if (!ppd)
526 return (0);
527
528 if (!ppd->jcl_end)
529 {
530 if (ppd->num_filters == 0)
531 putc(0x04, fp);
532
533 return (0);
534 }
535
536 /*
537 * See if the printer supports HP PJL...
538 */
539
540 if (!strncmp(ppd->jcl_end, "\033%-12345X@", 10))
541 {
542 /*
543 * This printer uses HP PJL commands for output; filter the output
544 * so that we only have a single "@PJL JOB" command in the header...
545 *
546 * To avoid bugs in the PJL implementation of certain vendors' products
547 * (Xerox in particular), we add a dummy "@PJL" command at the beginning
548 * of the PJL commands to initialize PJL processing.
549 */
550
551 fputs("\033%-12345X@PJL\n", fp);
552 fputs("@PJL RDYMSG DISPLAY = \"\"\n", fp);
553 fputs(ppd->jcl_end + 9, fp);
554 }
555 else
556 fputs(ppd->jcl_end, fp);
557
558 return (0);
559}
560
561
562/*
563 * 'ppdEmitString()' - Get a string containing the code for marked options.
564 *
565 * When "min_order" is greater than zero, this function only includes options
566 * whose OrderDependency value is greater than or equal to "min_order".
567 * Otherwise, all options in the specified section are included in the
568 * returned string.
569 *
570 * The return string is allocated on the heap and should be freed using
571 * @code free@ when you are done with it.
572 *
573 * @since CUPS 1.2@
574 */
575
576char * /* O - String containing option code or @code NULL@ if there is no option code */
577ppdEmitString(ppd_file_t *ppd, /* I - PPD file record */
578 ppd_section_t section, /* I - Section to write */
579 float min_order) /* I - Lowest OrderDependency */
580{
581 int i, j, /* Looping vars */
582 count; /* Number of choices */
583 ppd_choice_t **choices; /* Choices */
584 ppd_size_t *size; /* Custom page size */
585 ppd_coption_t *coption; /* Custom option */
586 ppd_cparam_t *cparam; /* Custom parameter */
587 size_t bufsize; /* Size of string buffer needed */
588 char *buffer, /* String buffer */
589 *bufptr, /* Pointer into buffer */
590 *bufend; /* End of buffer */
591 struct lconv *loc; /* Locale data */
592
593
594 DEBUG_printf(("ppdEmitString(ppd=%p, section=%d, min_order=%f)\n",
595 ppd, section, min_order));
596
597 /*
598 * Range check input...
599 */
600
601 if (!ppd)
602 return (NULL);
603
604 /*
605 * Use PageSize or PageRegion as required...
606 */
607
608 ppd_handle_media(ppd);
609
610 /*
611 * Collect the options we need to emit...
612 */
613
614 if ((count = ppdCollect2(ppd, section, min_order, &choices)) == 0)
615 return (NULL);
616
617 /*
618 * Count the number of bytes that are required to hold all of the
619 * option code...
620 */
621
622 for (i = 0, bufsize = 1; i < count; i ++)
623 {
624 if (section == PPD_ORDER_JCL)
625 {
626 if (!strcasecmp(choices[i]->choice, "Custom") &&
627 (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
628 != NULL)
629 {
630 /*
631 * Add space to account for custom parameter substitution...
632 */
633
634 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
635 cparam;
636 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
637 {
638 switch (cparam->type)
639 {
640 case PPD_CUSTOM_CURVE :
641 case PPD_CUSTOM_INVCURVE :
642 case PPD_CUSTOM_POINTS :
643 case PPD_CUSTOM_REAL :
644 case PPD_CUSTOM_INT :
645 bufsize += 10;
646 break;
647
648 case PPD_CUSTOM_PASSCODE :
649 case PPD_CUSTOM_PASSWORD :
650 case PPD_CUSTOM_STRING :
651 bufsize += strlen(cparam->current.custom_string);
652 break;
653 }
654 }
655 }
656 }
657 else if (section != PPD_ORDER_EXIT)
658 {
659 bufsize += 3; /* [{\n */
660
661 if ((!strcasecmp(choices[i]->option->keyword, "PageSize") ||
662 !strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
663 !strcasecmp(choices[i]->choice, "Custom"))
664 {
665 DEBUG_puts("ppdEmitString: Custom size set!");
666
667 bufsize += 37; /* %%BeginFeature: *CustomPageSize True\n */
668 bufsize += 50; /* Five 9-digit numbers + newline */
669 }
670 else if (!strcasecmp(choices[i]->choice, "Custom") &&
671 (coption = ppdFindCustomOption(ppd,
672 choices[i]->option->keyword))
673 != NULL)
674 {
675 bufsize += 17 + strlen(choices[i]->option->keyword) + 6;
676 /* %%BeginFeature: *keyword True\n */
677
678
679 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
680 cparam;
681 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
682 {
683 switch (cparam->type)
684 {
685 case PPD_CUSTOM_CURVE :
686 case PPD_CUSTOM_INVCURVE :
687 case PPD_CUSTOM_POINTS :
688 case PPD_CUSTOM_REAL :
689 case PPD_CUSTOM_INT :
690 bufsize += 10;
691 break;
692
693 case PPD_CUSTOM_PASSCODE :
694 case PPD_CUSTOM_PASSWORD :
695 case PPD_CUSTOM_STRING :
696 bufsize += 3 + 4 * strlen(cparam->current.custom_string);
697 break;
698 }
699 }
700 }
701 else
702 bufsize += 17 + strlen(choices[i]->option->keyword) + 1 +
703 strlen(choices[i]->choice) + 1;
704 /* %%BeginFeature: *keyword choice\n */
705
706 bufsize += 13; /* %%EndFeature\n */
707 bufsize += 22; /* } stopped cleartomark\n */
708 }
709
710 if (choices[i]->code)
711 bufsize += strlen(choices[i]->code) + 1;
712 else
713 bufsize += strlen(ppd_custom_code);
714 }
715
716 /*
717 * Allocate memory...
718 */
719
720 DEBUG_printf(("ppdEmitString: Allocating %d bytes for string...\n",
721 (int)bufsize));
722
723 if ((buffer = calloc(1, bufsize)) == NULL)
724 {
725 free(choices);
726 return (NULL);
727 }
728
729 bufend = buffer + bufsize - 1;
730 loc = localeconv();
731
732 /*
733 * Copy the option code to the buffer...
734 */
735
736 for (i = 0, bufptr = buffer; i < count; i ++, bufptr += strlen(bufptr))
737 if (section == PPD_ORDER_JCL)
738 {
739 if (!strcasecmp(choices[i]->choice, "Custom") &&
740 (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
741 != NULL)
742 {
743 /*
744 * Handle substitutions in custom JCL options...
745 */
746
747 char *cptr; /* Pointer into code */
748 int pnum; /* Parameter number */
749
750
751 for (cptr = choices[i]->code; *cptr && bufptr < bufend;)
752 {
753 if (*cptr == '\\')
754 {
755 cptr ++;
756
757 if (isdigit(*cptr & 255))
758 {
759 /*
760 * Substitute parameter...
761 */
762
763 pnum = *cptr++ - '0';
764 while (isalnum(*cptr & 255))
765 pnum = pnum * 10 + *cptr - '0';
766
767 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
768 cparam;
769 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
770 if (cparam->order == pnum)
771 break;
772
773 if (cparam)
774 {
775 switch (cparam->type)
776 {
777 case PPD_CUSTOM_CURVE :
778 case PPD_CUSTOM_INVCURVE :
779 case PPD_CUSTOM_POINTS :
780 case PPD_CUSTOM_REAL :
781 bufptr = _cupsStrFormatd(bufptr, bufend,
782 cparam->current.custom_real,
783 loc);
784 break;
785
786 case PPD_CUSTOM_INT :
787 snprintf(bufptr, bufend - bufptr, "%d",
788 cparam->current.custom_int);
789 bufptr += strlen(bufptr);
790 break;
791
792 case PPD_CUSTOM_PASSCODE :
793 case PPD_CUSTOM_PASSWORD :
794 case PPD_CUSTOM_STRING :
795 strlcpy(bufptr, cparam->current.custom_string,
796 bufend - bufptr);
797 bufptr += strlen(bufptr);
798 break;
799 }
800 }
801 }
802 else if (*cptr)
803 *bufptr++ = *cptr++;
804 }
805 else
806 *bufptr++ = *cptr++;
807 }
808 }
809 else
810 {
811 /*
812 * Otherwise just copy the option code directly...
813 */
814
815 strlcpy(bufptr, choices[i]->code, bufend - bufptr + 1);
816 bufptr += strlen(bufptr);
817 }
818 }
819 else if (section != PPD_ORDER_EXIT)
820 {
821 /*
822 * Add wrapper commands to prevent printer errors for unsupported
823 * options...
824 */
825
826 strlcpy(bufptr, "[{\n", bufend - bufptr + 1);
827 bufptr += 3;
828
829 /*
830 * Send DSC comments with option...
831 */
832
833 DEBUG_printf(("Adding code for %s=%s...\n", choices[i]->option->keyword,
834 choices[i]->choice));
835
836 if ((!strcasecmp(choices[i]->option->keyword, "PageSize") ||
837 !strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
838 !strcasecmp(choices[i]->choice, "Custom"))
839 {
840 /*
841 * Variable size; write out standard size options, using the
842 * parameter positions defined in the PPD file...
843 */
844
845 ppd_attr_t *attr; /* PPD attribute */
846 int pos, /* Position of custom value */
847 orientation; /* Orientation to use */
848 float values[5]; /* Values for custom command */
849
850
851 strlcpy(bufptr, "%%BeginFeature: *CustomPageSize True\n",
852 bufend - bufptr + 1);
853 bufptr += 37;
854
855 size = ppdPageSize(ppd, "Custom");
856
857 memset(values, 0, sizeof(values));
858
859 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Width")) != NULL)
860 {
861 pos = atoi(attr->value) - 1;
862
863 if (pos < 0 || pos > 4)
864 pos = 0;
865 }
866 else
867 pos = 0;
868
869 values[pos] = size->width;
870
871 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Height")) != NULL)
872 {
873 pos = atoi(attr->value) - 1;
874
875 if (pos < 0 || pos > 4)
876 pos = 1;
877 }
878 else
879 pos = 1;
880
881 values[pos] = size->length;
882
883 /*
884 * According to the Adobe PPD specification, an orientation of 1
885 * will produce a print that comes out upside-down with the X
886 * axis perpendicular to the direction of feed, which is exactly
887 * what we want to be consistent with non-PS printers.
888 *
889 * We could also use an orientation of 3 to produce output that
890 * comes out rightside-up (this is the default for many large format
891 * printer PPDs), however for consistency we will stick with the
892 * value 1.
893 *
894 * If we wanted to get fancy, we could use orientations of 0 or
895 * 2 and swap the width and length, however we don't want to get
896 * fancy, we just want it to work consistently.
897 *
898 * The orientation value is range limited by the Orientation
899 * parameter definition, so certain non-PS printer drivers that
900 * only support an Orientation of 0 will get the value 0 as
901 * expected.
902 */
903
904 orientation = 1;
905
906 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize",
907 "Orientation")) != NULL)
908 {
909 int min_orient, max_orient; /* Minimum and maximum orientations */
910
911
912 if (sscanf(attr->value, "%d%*s%d%d", &pos, &min_orient,
913 &max_orient) != 3)
914 pos = 4;
915 else
916 {
917 pos --;
918
919 if (pos < 0 || pos > 4)
920 pos = 4;
921
922 if (orientation > max_orient)
923 orientation = max_orient;
924 else if (orientation < min_orient)
925 orientation = min_orient;
926 }
927 }
928 else
929 pos = 4;
930
931 values[pos] = (float)orientation;
932
933 for (pos = 0; pos < 5; pos ++)
934 {
935 bufptr = _cupsStrFormatd(bufptr, bufend, values[pos], loc);
936 *bufptr++ = '\n';
937 }
938
939 if (!choices[i]->code)
940 {
941 /*
942 * This can happen with certain buggy PPD files that don't include
943 * a CustomPageSize command sequence... We just use a generic
944 * Level 2 command sequence...
945 */
946
947 strlcpy(bufptr, ppd_custom_code, bufend - bufptr + 1);
948 bufptr += strlen(bufptr);
949 }
950 }
951 else if (!strcasecmp(choices[i]->choice, "Custom") &&
952 (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
953 != NULL)
954 {
955 /*
956 * Custom option...
957 */
958
959 const char *s; /* Pointer into string value */
960 cups_array_t *params; /* Parameters in the correct output order */
961
962
963 params = cupsArrayNew((cups_array_func_t)ppd_compare_cparams, NULL);
964
965 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
966 cparam;
967 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
968 cupsArrayAdd(params, cparam);
969
970 snprintf(bufptr, bufend - bufptr + 1,
971 "%%%%BeginFeature: *Custom%s True\n", coption->keyword);
972 bufptr += strlen(bufptr);
973
974 for (cparam = (ppd_cparam_t *)cupsArrayFirst(params);
975 cparam;
976 cparam = (ppd_cparam_t *)cupsArrayNext(params))
977 {
978 switch (cparam->type)
979 {
980 case PPD_CUSTOM_CURVE :
981 case PPD_CUSTOM_INVCURVE :
982 case PPD_CUSTOM_POINTS :
983 case PPD_CUSTOM_REAL :
984 bufptr = _cupsStrFormatd(bufptr, bufend,
985 cparam->current.custom_real, loc);
986 *bufptr++ = '\n';
987 break;
988
989 case PPD_CUSTOM_INT :
990 snprintf(bufptr, bufend - bufptr + 1, "%d\n",
991 cparam->current.custom_int);
992 bufptr += strlen(bufptr);
993 break;
994
995 case PPD_CUSTOM_PASSCODE :
996 case PPD_CUSTOM_PASSWORD :
997 case PPD_CUSTOM_STRING :
998 *bufptr++ = '(';
999
1000 for (s = cparam->current.custom_string; *s; s ++)
1001 if (*s < ' ' || *s == '(' || *s == ')' || *s >= 127)
1002 {
1003 snprintf(bufptr, bufend - bufptr + 1, "\\%03o", *s & 255);
1004 bufptr += strlen(bufptr);
1005 }
1006 else
1007 *bufptr++ = *s;
1008
1009 *bufptr++ = ')';
1010 *bufptr++ = '\n';
1011 break;
1012 }
1013 }
1014
1015 cupsArrayDelete(params);
1016 }
1017 else
1018 {
1019 snprintf(bufptr, bufend - bufptr + 1, "%%%%BeginFeature: *%s %s\n",
1020 choices[i]->option->keyword, choices[i]->choice);
1021 bufptr += strlen(bufptr);
1022 }
1023
1024 if (choices[i]->code && choices[i]->code[0])
1025 {
1026 j = (int)strlen(choices[i]->code);
1027 memcpy(bufptr, choices[i]->code, j);
1028 bufptr += j;
1029
1030 if (choices[i]->code[j - 1] != '\n')
1031 *bufptr++ = '\n';
1032 }
1033
1034 strlcpy(bufptr, "%%EndFeature\n"
1035 "} stopped cleartomark\n", bufend - bufptr + 1);
1036 bufptr += strlen(bufptr);
1037
1038 DEBUG_printf(("ppdEmitString: Offset in string is %d...\n",
1039 (int)(bufptr - buffer)));
1040 }
1041 else
1042 {
1043 strlcpy(bufptr, choices[i]->code, bufend - bufptr + 1);
1044 bufptr += strlen(bufptr);
1045 }
1046
1047 /*
1048 * Nul-terminate, free, and return...
1049 */
1050
1051 *bufptr = '\0';
1052
1053 free(choices);
1054
1055 return (buffer);
1056}
1057
1058
1059/*
1060 * 'ppd_compare_cparams()' - Compare the order of two custom parameters.
1061 */
1062
1063static int /* O - Result of comparison */
1064ppd_compare_cparams(ppd_cparam_t *a, /* I - First parameter */
1065 ppd_cparam_t *b) /* I - Second parameter */
1066{
1067 return (a->order - b->order);
1068}
1069
1070
1071/*
1072 * 'ppd_handle_media()' - Handle media selection...
1073 */
1074
1075static void
1076ppd_handle_media(ppd_file_t *ppd) /* I - PPD file */
1077{
1078 ppd_choice_t *manual_feed, /* ManualFeed choice, if any */
1079 *input_slot, /* InputSlot choice, if any */
1080 *page; /* PageSize/PageRegion */
1081 ppd_size_t *size; /* Current media size */
1082 ppd_attr_t *rpr; /* RequiresPageRegion value */
1083
1084
1085 /*
1086 * This function determines if the user has selected a media source
1087 * via the InputSlot or ManualFeed options; if so, it marks the
1088 * PageRegion option corresponding to the current media size.
1089 * Otherwise it marks the PageSize option.
1090 */
1091
1092 if ((size = ppdPageSize(ppd, NULL)) == NULL)
1093 return;
1094
1095 manual_feed = ppdFindMarkedChoice(ppd, "ManualFeed");
1096 input_slot = ppdFindMarkedChoice(ppd, "InputSlot");
1097
1098 if (input_slot != NULL)
1099 rpr = ppdFindAttr(ppd, "RequiresPageRegion", input_slot->choice);
1100 else
1101 rpr = NULL;
1102
1103 if (!rpr)
1104 rpr = ppdFindAttr(ppd, "RequiresPageRegion", "All");
1105
1106 if (!strcasecmp(size->name, "Custom") || (!manual_feed && !input_slot) ||
1107 !((manual_feed && !strcasecmp(manual_feed->choice, "True")) ||
1108 (input_slot && input_slot->code && input_slot->code[0])))
1109 {
1110 /*
1111 * Manual feed was not selected and/or the input slot selection does
1112 * not contain any PostScript code. Use the PageSize option...
1113 */
1114
1115 ppdMarkOption(ppd, "PageSize", size->name);
1116 }
1117 else
1118 {
1119 /*
1120 * Manual feed was selected and/or the input slot selection contains
1121 * PostScript code. Use the PageRegion option...
1122 */
1123
1124 ppdMarkOption(ppd, "PageRegion", size->name);
1125
1126 /*
1127 * RequiresPageRegion does not apply to manual feed so we need to
1128 * check that we are not doing manual feed before unmarking PageRegion.
1129 */
1130
1131 if (!(manual_feed && !strcasecmp(manual_feed->choice, "True")) &&
1132 ((rpr && rpr->value && !strcmp(rpr->value, "False")) ||
1133 (!rpr && !ppd->num_filters)))
1134 {
1135 /*
1136 * Either the PPD file specifies no PageRegion code or the PPD file
1137 * not for a CUPS raster driver and thus defaults to no PageRegion
1138 * code... Unmark the PageRegion choice so that we don't output the
1139 * code...
1140 */
1141
1142 page = ppdFindMarkedChoice(ppd, "PageRegion");
1143
1144 if (page)
1145 page->marked = 0;
1146 }
1147 }
1148}
1149
1150
1151/*
1152 * End of "$Id: emit.c 7863 2008-08-26 03:39:59Z mike $".
1153 */