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