Revert manifest to default one
[external/cups.git] / cups / emit.c
1 /*
2  * "$Id: emit.c 9993 2011-09-09 21:55:11Z mike $"
3  *
4  *   PPD code emission routines for CUPS.
5  *
6  *   Copyright 2007-2011 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/Mac 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/Mac 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     {
523       fprintf(fp, "@PJL JOB NAME = \"%s\"\n", temp);
524
525       if (display && !strcmp(display->value, "rdymsg"))
526         fprintf(fp, "@PJL RDYMSG DISPLAY = \"%s\"\n", displaymsg);
527     }
528     else
529       fprintf(fp, "@PJL JOB NAME = \"%s\" DISPLAY = \"%s\"\n", temp,
530               displaymsg);
531   }
532   else
533     fputs(ppd->jcl_begin, fp);
534
535   ppdEmit(ppd, fp, PPD_ORDER_JCL);
536   fputs(ppd->jcl_ps, fp);
537
538   return (0);
539 }
540
541
542 /*
543  * 'ppdEmitJCLEnd()' - Emit JCLEnd code to a file.
544  *
545  * @since CUPS 1.2/Mac OS X 10.5@
546  */
547
548 int                                     /* O - 0 on success, -1 on failure */
549 ppdEmitJCLEnd(ppd_file_t *ppd,          /* I - PPD file record */
550               FILE       *fp)           /* I - File to write to */
551 {
552  /*
553   * Range check the input...
554   */
555
556   if (!ppd)
557     return (0);
558
559   if (!ppd->jcl_end)
560   {
561     if (ppd->num_filters == 0)
562       putc(0x04, fp);
563
564     return (0);
565   }
566
567  /*
568   * See if the printer supports HP PJL...
569   */
570
571   if (!strncmp(ppd->jcl_end, "\033%-12345X@", 10))
572   {
573    /*
574     * This printer uses HP PJL commands for output; filter the output
575     * so that we only have a single "@PJL JOB" command in the header...
576     *
577     * To avoid bugs in the PJL implementation of certain vendors' products
578     * (Xerox in particular), we add a dummy "@PJL" command at the beginning
579     * of the PJL commands to initialize PJL processing.
580     */
581
582     fputs("\033%-12345X@PJL\n", fp);
583     fputs("@PJL RDYMSG DISPLAY = \"\"\n", fp);
584     fputs(ppd->jcl_end + 9, fp);
585   }
586   else
587     fputs(ppd->jcl_end, fp);
588
589   return (0);
590 }
591
592
593 /*
594  * 'ppdEmitString()' - Get a string containing the code for marked options.
595  *
596  * When "min_order" is greater than zero, this function only includes options
597  * whose OrderDependency value is greater than or equal to "min_order".
598  * Otherwise, all options in the specified section are included in the
599  * returned string.
600  *
601  * The return string is allocated on the heap and should be freed using
602  * @code free@ when you are done with it.
603  *
604  * @since CUPS 1.2/Mac OS X 10.5@
605  */
606
607 char *                                  /* O - String containing option code or @code NULL@ if there is no option code */
608 ppdEmitString(ppd_file_t    *ppd,       /* I - PPD file record */
609               ppd_section_t section,    /* I - Section to write */
610               float         min_order)  /* I - Lowest OrderDependency */
611 {
612   int           i, j,                   /* Looping vars */
613                 count;                  /* Number of choices */
614   ppd_choice_t  **choices;              /* Choices */
615   ppd_size_t    *size;                  /* Custom page size */
616   ppd_coption_t *coption;               /* Custom option */
617   ppd_cparam_t  *cparam;                /* Custom parameter */
618   size_t        bufsize;                /* Size of string buffer needed */
619   char          *buffer,                /* String buffer */
620                 *bufptr,                /* Pointer into buffer */
621                 *bufend;                /* End of buffer */
622   struct lconv  *loc;                   /* Locale data */
623
624
625   DEBUG_printf(("ppdEmitString(ppd=%p, section=%d, min_order=%f)",
626                 ppd, section, min_order));
627
628  /*
629   * Range check input...
630   */
631
632   if (!ppd)
633     return (NULL);
634
635  /*
636   * Use PageSize or PageRegion as required...
637   */
638
639   ppd_handle_media(ppd);
640
641  /*
642   * Collect the options we need to emit...
643   */
644
645   if ((count = ppdCollect2(ppd, section, min_order, &choices)) == 0)
646     return (NULL);
647
648  /*
649   * Count the number of bytes that are required to hold all of the
650   * option code...
651   */
652
653   for (i = 0, bufsize = 1; i < count; i ++)
654   {
655     if (section == PPD_ORDER_JCL)
656     {
657       if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
658           (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
659               != NULL)
660       {
661        /*
662         * Add space to account for custom parameter substitution...
663         */
664
665         for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
666              cparam;
667              cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
668         {
669           switch (cparam->type)
670           {
671             case PPD_CUSTOM_CURVE :
672             case PPD_CUSTOM_INVCURVE :
673             case PPD_CUSTOM_POINTS :
674             case PPD_CUSTOM_REAL :
675             case PPD_CUSTOM_INT :
676                 bufsize += 10;
677                 break;
678
679             case PPD_CUSTOM_PASSCODE :
680             case PPD_CUSTOM_PASSWORD :
681             case PPD_CUSTOM_STRING :
682                 if (cparam->current.custom_string)
683                   bufsize += strlen(cparam->current.custom_string);
684                 break;
685           }
686         }
687       }
688     }
689     else if (section != PPD_ORDER_EXIT)
690     {
691       bufsize += 3;                     /* [{\n */
692
693       if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") ||
694            !_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
695           !_cups_strcasecmp(choices[i]->choice, "Custom"))
696       {
697         DEBUG_puts("2ppdEmitString: Custom size set!");
698
699         bufsize += 37;                  /* %%BeginFeature: *CustomPageSize True\n */
700         bufsize += 50;                  /* Five 9-digit numbers + newline */
701       }
702       else if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
703                (coption = ppdFindCustomOption(ppd,
704                                               choices[i]->option->keyword))
705                    != NULL)
706       {
707         bufsize += 23 + strlen(choices[i]->option->keyword) + 6;
708                                         /* %%BeginFeature: *Customkeyword True\n */
709
710
711         for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
712              cparam;
713              cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
714         {
715           switch (cparam->type)
716           {
717             case PPD_CUSTOM_CURVE :
718             case PPD_CUSTOM_INVCURVE :
719             case PPD_CUSTOM_POINTS :
720             case PPD_CUSTOM_REAL :
721             case PPD_CUSTOM_INT :
722                 bufsize += 10;
723                 break;
724
725             case PPD_CUSTOM_PASSCODE :
726             case PPD_CUSTOM_PASSWORD :
727             case PPD_CUSTOM_STRING :
728                 bufsize += 3;
729                 if (cparam->current.custom_string)
730                   bufsize += 4 * strlen(cparam->current.custom_string);
731                 break;
732           }
733         }
734       }
735       else
736         bufsize += 17 + strlen(choices[i]->option->keyword) + 1 +
737                    strlen(choices[i]->choice) + 1;
738                                         /* %%BeginFeature: *keyword choice\n */
739
740       bufsize += 13;                    /* %%EndFeature\n */
741       bufsize += 22;                    /* } stopped cleartomark\n */
742     }
743
744     if (choices[i]->code)
745       bufsize += strlen(choices[i]->code) + 1;
746     else
747       bufsize += strlen(ppd_custom_code);
748   }
749
750  /*
751   * Allocate memory...
752   */
753
754   DEBUG_printf(("2ppdEmitString: Allocating %d bytes for string...",
755                 (int)bufsize));
756
757   if ((buffer = calloc(1, bufsize)) == NULL)
758   {
759     free(choices);
760     return (NULL);
761   }
762
763   bufend = buffer + bufsize - 1;
764   loc    = localeconv();
765
766  /*
767   * Copy the option code to the buffer...
768   */
769
770   for (i = 0, bufptr = buffer; i < count; i ++, bufptr += strlen(bufptr))
771     if (section == PPD_ORDER_JCL)
772     {
773       if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
774           choices[i]->code &&
775           (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
776               != NULL)
777       {
778        /*
779         * Handle substitutions in custom JCL options...
780         */
781
782         char    *cptr;                  /* Pointer into code */
783         int     pnum;                   /* Parameter number */
784
785
786         for (cptr = choices[i]->code; *cptr && bufptr < bufend;)
787         {
788           if (*cptr == '\\')
789           {
790             cptr ++;
791
792             if (isdigit(*cptr & 255))
793             {
794              /*
795               * Substitute parameter...
796               */
797
798               pnum = *cptr++ - '0';
799               while (isdigit(*cptr & 255))
800                 pnum = pnum * 10 + *cptr++ - '0';
801
802               for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
803                    cparam;
804                    cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
805                 if (cparam->order == pnum)
806                   break;
807
808               if (cparam)
809               {
810                 switch (cparam->type)
811                 {
812                   case PPD_CUSTOM_CURVE :
813                   case PPD_CUSTOM_INVCURVE :
814                   case PPD_CUSTOM_POINTS :
815                   case PPD_CUSTOM_REAL :
816                       bufptr = _cupsStrFormatd(bufptr, bufend,
817                                                cparam->current.custom_real,
818                                                loc);
819                       break;
820
821                   case PPD_CUSTOM_INT :
822                       snprintf(bufptr, bufend - bufptr, "%d",
823                                cparam->current.custom_int);
824                       bufptr += strlen(bufptr);
825                       break;
826
827                   case PPD_CUSTOM_PASSCODE :
828                   case PPD_CUSTOM_PASSWORD :
829                   case PPD_CUSTOM_STRING :
830                       if (cparam->current.custom_string)
831                       {
832                         strlcpy(bufptr, cparam->current.custom_string,
833                                 bufend - bufptr);
834                         bufptr += strlen(bufptr);
835                       }
836                       break;
837                 }
838               }
839             }
840             else if (*cptr)
841               *bufptr++ = *cptr++;
842           }
843           else
844             *bufptr++ = *cptr++;
845         }
846       }
847       else
848       {
849        /*
850         * Otherwise just copy the option code directly...
851         */
852
853         strlcpy(bufptr, choices[i]->code, bufend - bufptr + 1);
854         bufptr += strlen(bufptr);
855       }
856     }
857     else if (section != PPD_ORDER_EXIT)
858     {
859      /*
860       * Add wrapper commands to prevent printer errors for unsupported
861       * options...
862       */
863
864       strlcpy(bufptr, "[{\n", bufend - bufptr + 1);
865       bufptr += 3;
866
867      /*
868       * Send DSC comments with option...
869       */
870
871       DEBUG_printf(("2ppdEmitString: Adding code for %s=%s...",
872                     choices[i]->option->keyword, choices[i]->choice));
873
874       if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") ||
875            !_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
876           !_cups_strcasecmp(choices[i]->choice, "Custom"))
877       {
878        /*
879         * Variable size; write out standard size options, using the
880         * parameter positions defined in the PPD file...
881         */
882
883         ppd_attr_t      *attr;          /* PPD attribute */
884         int             pos,            /* Position of custom value */
885                         orientation;    /* Orientation to use */
886         float           values[5];      /* Values for custom command */
887
888
889         strlcpy(bufptr, "%%BeginFeature: *CustomPageSize True\n",
890                 bufend - bufptr + 1);
891         bufptr += 37;
892
893         size = ppdPageSize(ppd, "Custom");
894
895         memset(values, 0, sizeof(values));
896
897         if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Width")) != NULL)
898         {
899           pos = atoi(attr->value) - 1;
900
901           if (pos < 0 || pos > 4)
902             pos = 0;
903         }
904         else
905           pos = 0;
906
907         values[pos] = size->width;
908
909         if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Height")) != NULL)
910         {
911           pos = atoi(attr->value) - 1;
912
913           if (pos < 0 || pos > 4)
914             pos = 1;
915         }
916         else
917           pos = 1;
918
919         values[pos] = size->length;
920
921        /*
922         * According to the Adobe PPD specification, an orientation of 1
923         * will produce a print that comes out upside-down with the X
924         * axis perpendicular to the direction of feed, which is exactly
925         * what we want to be consistent with non-PS printers.
926         *
927         * We could also use an orientation of 3 to produce output that
928         * comes out rightside-up (this is the default for many large format
929         * printer PPDs), however for consistency we will stick with the
930         * value 1.
931         *
932         * If we wanted to get fancy, we could use orientations of 0 or
933         * 2 and swap the width and length, however we don't want to get
934         * fancy, we just want it to work consistently.
935         *
936         * The orientation value is range limited by the Orientation
937         * parameter definition, so certain non-PS printer drivers that
938         * only support an Orientation of 0 will get the value 0 as
939         * expected.
940         */
941
942         orientation = 1;
943
944         if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize",
945                                 "Orientation")) != NULL)
946         {
947           int min_orient, max_orient;   /* Minimum and maximum orientations */
948
949
950           if (sscanf(attr->value, "%d%*s%d%d", &pos, &min_orient,
951                      &max_orient) != 3)
952             pos = 4;
953           else
954           {
955             pos --;
956
957             if (pos < 0 || pos > 4)
958               pos = 4;
959
960             if (orientation > max_orient)
961               orientation = max_orient;
962             else if (orientation < min_orient)
963               orientation = min_orient;
964           }
965         }
966         else
967           pos = 4;
968
969         values[pos] = (float)orientation;
970
971         for (pos = 0; pos < 5; pos ++)
972         {
973           bufptr    = _cupsStrFormatd(bufptr, bufend, values[pos], loc);
974           *bufptr++ = '\n';
975         }
976
977         if (!choices[i]->code)
978         {
979          /*
980           * This can happen with certain buggy PPD files that don't include
981           * a CustomPageSize command sequence...  We just use a generic
982           * Level 2 command sequence...
983           */
984
985           strlcpy(bufptr, ppd_custom_code, bufend - bufptr + 1);
986           bufptr += strlen(bufptr);
987         }
988       }
989       else if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
990                (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
991                    != NULL)
992       {
993        /*
994         * Custom option...
995         */
996
997         const char      *s;             /* Pointer into string value */
998         cups_array_t    *params;        /* Parameters in the correct output order */
999
1000
1001         params = cupsArrayNew((cups_array_func_t)ppd_compare_cparams, NULL);
1002
1003         for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
1004              cparam;
1005              cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
1006           cupsArrayAdd(params, cparam);
1007
1008         snprintf(bufptr, bufend - bufptr + 1,
1009                  "%%%%BeginFeature: *Custom%s True\n", coption->keyword);
1010         bufptr += strlen(bufptr);
1011
1012         for (cparam = (ppd_cparam_t *)cupsArrayFirst(params);
1013              cparam;
1014              cparam = (ppd_cparam_t *)cupsArrayNext(params))
1015         {
1016           switch (cparam->type)
1017           {
1018             case PPD_CUSTOM_CURVE :
1019             case PPD_CUSTOM_INVCURVE :
1020             case PPD_CUSTOM_POINTS :
1021             case PPD_CUSTOM_REAL :
1022                 bufptr    = _cupsStrFormatd(bufptr, bufend,
1023                                             cparam->current.custom_real, loc);
1024                 *bufptr++ = '\n';
1025                 break;
1026
1027             case PPD_CUSTOM_INT :
1028                 snprintf(bufptr, bufend - bufptr + 1, "%d\n",
1029                          cparam->current.custom_int);
1030                 bufptr += strlen(bufptr);
1031                 break;
1032
1033             case PPD_CUSTOM_PASSCODE :
1034             case PPD_CUSTOM_PASSWORD :
1035             case PPD_CUSTOM_STRING :
1036                 *bufptr++ = '(';
1037
1038                 if (cparam->current.custom_string)
1039                 {
1040                   for (s = cparam->current.custom_string; *s; s ++)
1041                   {
1042                     if (*s < ' ' || *s == '(' || *s == ')' || *s >= 127)
1043                     {
1044                       snprintf(bufptr, bufend - bufptr + 1, "\\%03o", *s & 255);
1045                       bufptr += strlen(bufptr);
1046                     }
1047                     else
1048                       *bufptr++ = *s;
1049                   }
1050                 }
1051
1052                 *bufptr++ = ')';
1053                 *bufptr++ = '\n';
1054                 break;
1055           }
1056         }
1057
1058         cupsArrayDelete(params);
1059       }
1060       else
1061       {
1062         snprintf(bufptr, bufend - bufptr + 1, "%%%%BeginFeature: *%s %s\n",
1063                  choices[i]->option->keyword, choices[i]->choice);
1064         bufptr += strlen(bufptr);
1065       }
1066
1067       if (choices[i]->code && choices[i]->code[0])
1068       {
1069         j = (int)strlen(choices[i]->code);
1070         memcpy(bufptr, choices[i]->code, j);
1071         bufptr += j;
1072
1073         if (choices[i]->code[j - 1] != '\n')
1074           *bufptr++ = '\n';
1075       }
1076
1077       strlcpy(bufptr, "%%EndFeature\n"
1078                       "} stopped cleartomark\n", bufend - bufptr + 1);
1079       bufptr += strlen(bufptr);
1080
1081       DEBUG_printf(("2ppdEmitString: Offset in string is %d...",
1082                     (int)(bufptr - buffer)));
1083     }
1084     else
1085     {
1086       strlcpy(bufptr, choices[i]->code, bufend - bufptr + 1);
1087       bufptr += strlen(bufptr);
1088     }
1089
1090  /*
1091   * Nul-terminate, free, and return...
1092   */
1093
1094   *bufptr = '\0';
1095
1096   free(choices);
1097
1098   return (buffer);
1099 }
1100
1101
1102 /*
1103  * 'ppd_compare_cparams()' - Compare the order of two custom parameters.
1104  */
1105
1106 static int                              /* O - Result of comparison */
1107 ppd_compare_cparams(ppd_cparam_t *a,    /* I - First parameter */
1108                     ppd_cparam_t *b)    /* I - Second parameter */
1109 {
1110   return (a->order - b->order);
1111 }
1112
1113
1114 /*
1115  * 'ppd_handle_media()' - Handle media selection...
1116  */
1117
1118 static void
1119 ppd_handle_media(ppd_file_t *ppd)       /* I - PPD file */
1120 {
1121   ppd_choice_t  *manual_feed,           /* ManualFeed choice, if any */
1122                 *input_slot;            /* InputSlot choice, if any */
1123   ppd_size_t    *size;                  /* Current media size */
1124   ppd_attr_t    *rpr;                   /* RequiresPageRegion value */
1125
1126
1127  /*
1128   * This function determines what page size code to use, if any, for the
1129   * current media size, InputSlot, and ManualFeed selections.
1130   *
1131   * We use the PageSize code if:
1132   *
1133   * 1. A custom media size is selected.
1134   * 2. ManualFeed and InputSlot are not selected (or do not exist).
1135   * 3. ManualFeed is selected but is False and InputSlot is not selected or
1136   *    the selection has no code - the latter check done to support "auto" or
1137   *    "printer default" InputSlot options.
1138   *
1139   * We use the PageRegion code if:
1140   *
1141   * 4. RequiresPageRegion does not exist and the PPD contains cupsFilter
1142   *    keywords, indicating this is a CUPS-based driver.
1143   * 5. RequiresPageRegion exists for the selected InputSlot (or "All" for any
1144   *    InputSlot or ManualFeed selection) and is True.
1145   *
1146   * If none of the 5 conditions are true, no page size code is used and we
1147   * unmark any existing PageSize or PageRegion choices.
1148   */
1149
1150   if ((size = ppdPageSize(ppd, NULL)) == NULL)
1151     return;
1152
1153   manual_feed = ppdFindMarkedChoice(ppd, "ManualFeed");
1154   input_slot  = ppdFindMarkedChoice(ppd, "InputSlot");
1155
1156   if (input_slot != NULL)
1157     rpr = ppdFindAttr(ppd, "RequiresPageRegion", input_slot->choice);
1158   else
1159     rpr = NULL;
1160
1161   if (!rpr)
1162     rpr = ppdFindAttr(ppd, "RequiresPageRegion", "All");
1163
1164   if (!_cups_strcasecmp(size->name, "Custom") ||
1165       (!manual_feed && !input_slot) ||
1166       (manual_feed && !_cups_strcasecmp(manual_feed->choice, "False") &&
1167        (!input_slot || (input_slot->code && !input_slot->code[0]))) ||
1168       (!rpr && ppd->num_filters > 0))
1169   {
1170    /*
1171     * Use PageSize code...
1172     */
1173
1174     ppdMarkOption(ppd, "PageSize", size->name);
1175   }
1176   else if (rpr && rpr->value && !_cups_strcasecmp(rpr->value, "True"))
1177   {
1178    /*
1179     * Use PageRegion code...
1180     */
1181
1182     ppdMarkOption(ppd, "PageRegion", size->name);
1183   }
1184   else
1185   {
1186    /*
1187     * Do not use PageSize or PageRegion code...
1188     */
1189
1190     ppd_choice_t        *page;          /* PageSize/Region choice, if any */
1191
1192     if ((page = ppdFindMarkedChoice(ppd, "PageSize")) != NULL)
1193     {
1194      /*
1195       * Unmark PageSize...
1196       */
1197
1198       page->marked = 0;
1199       cupsArrayRemove(ppd->marked, page);
1200     }
1201
1202     if ((page = ppdFindMarkedChoice(ppd, "PageRegion")) != NULL)
1203     {
1204      /*
1205       * Unmark PageRegion...
1206       */
1207
1208       page->marked = 0;
1209       cupsArrayRemove(ppd->marked, page);
1210     }
1211   }
1212 }
1213
1214
1215 /*
1216  * End of "$Id: emit.c 9993 2011-09-09 21:55:11Z mike $".
1217  */