1/*
2 * "$Id: genppd.c,v 1.186 2011/03/13 19:28:50 rlk Exp $"
3 *
4 *   PPD file generation program for the CUPS drivers.
5 *
6 *   Copyright 1993-2008 by Mike Sweet and Robert Krawitz.
7 *
8 *   This program is free software; you can redistribute it and/or modify it
9 *   under the terms of the GNU General Public License as published by the Free
10 *   Software Foundation; either version 2 of the License, or (at your option)
11 *   any later version.
12 *
13 *   This program is distributed in the hope that it will be useful, but
14 *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 *   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 *   for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 *
22 * Contents:
23 *
24 *   main()              - Process files on the command-line...
25 *   cat_ppd()           - Copy the named PPD to stdout.
26 *   generate_ppd()      - Generate a PPD file.
27 *   getlangs()          - Get a list of available translations.
28 *   help()              - Show detailed help.
29 *   is_special_option() - Determine if an option should be grouped.
30 *   list_ppds()         - List the available drivers.
31 *   print_group_close() - Close a UI group.
32 *   print_group_open()  - Open a new UI group.
33 *   printlangs()        - Print list of available translations.
34 *   printmodels()       - Print a list of available models.
35 *   usage()             - Show program usage.
36 *   write_ppd()         - Write a PPD file.
37 */
38
39/*
40 * Include necessary headers...
41 */
42
43#ifdef HAVE_CONFIG_H
44#include <config.h>
45#endif
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <dirent.h>
50#include <unistd.h>
51#include <fcntl.h>
52#include <sys/types.h>
53#include <sys/stat.h>
54#include <string.h>
55#include <ctype.h>
56#include <errno.h>
57#include <libgen.h>
58
59#ifdef CUPS_DRIVER_INTERFACE
60#  ifdef HAVE_LIBZ
61#    undef HAVE_LIBZ
62#  endif /* HAVE_LIBZ */
63#endif /* CUPS_DRIVER_INTERFACE */
64
65static const char *cups_modeldir = CUPS_MODELDIR;
66
67#ifdef HAVE_LIBZ
68#include <zlib.h>
69static const char *gzext = ".gz";
70#else
71static const char *gzext = "";
72#  define gzopen fopen
73#  define gzclose fclose
74#  define gzFile FILE *
75#  define gzprintf fprintf
76#  define gzputs(f,s) fputs((s),(f))
77#endif
78
79#include <cups/cups.h>
80#include <cups/raster.h>
81
82#include "i18n.h"
83
84/*
85 * Some applications use the XxYdpi tags rather than the actual
86 * hardware resolutions to decide what resolution to print at.  Some
87 * applications get very unhappy if the vertical resolution exceeds
88 * a certain amount.  Some of those applications even get very unhappy if
89 * the PPD file even contains a resolution that exceeds that limit.
90 * And they're not even free source applications.
91 * Feh.
92 */
93#define MAXIMUM_SAFE_PPD_Y_RESOLUTION (720)
94#define MAXIMUM_SAFE_PPD_X_RESOLUTION (1500)
95
96typedef enum
97{
98  PPD_STANDARD = 0,
99  PPD_SIMPLIFIED = 1,
100  PPD_NO_COLOR_OPTS = 2
101} ppd_type_t;
102
103/*
104 * Note:
105 *
106 * The current release of ESP Ghostscript is fully Level 3 compliant,
107 * so we can report Level 3 support by default...
108 */
109
110int cups_ppd_ps_level = CUPS_PPD_PS_LEVEL;
111int localize_numbers = 0;
112
113/*
114 * File handling stuff...
115 */
116
117static const char *ppdext = ".ppd";
118
119typedef struct				/**** Media size values ****/
120{
121  const char	*name,			/* Media size name */
122		*text;			/* Media size text */
123  int		width,			/* Media width */
124		height,			/* Media height */
125		left,			/* Media left margin */
126		right,			/* Media right margin */
127		bottom,			/* Media bottom margin */
128		top;			/* Media top margin */
129} paper_t;
130
131const char *special_options[] =
132{
133  "PageSize",
134  "MediaType",
135  "InputSlot",
136  "Resolution",
137  "OutputOrder",
138  "Quality",
139  "Duplex",
140  NULL
141};
142
143/*
144 * TRANSLATORS:
145 * Please keep these translated names SHORT.  The number of bytes in
146 * the parameter class name plus the number of bytes in the parameter
147 * name must not exceed 38 BYTES (not characters!)
148 */
149
150const char *parameter_class_names[] =
151{
152  _("Printer Features"),
153  _("Output Control")
154};
155
156const char *parameter_level_names[] =
157{
158  _("Common"),
159  _("Extra 1"),
160  _("Extra 2"),
161  _("Extra 3"),
162  _("Extra 4"),
163  _("Extra 5")
164};
165
166
167/*
168 * Local functions...
169 */
170
171#ifdef CUPS_DRIVER_INTERFACE
172static int	cat_ppd(const char *uri);
173static int	list_ppds(const char *argv0);
174#else  /* !CUPS_DRIVER_INTERFACE */
175static int	generate_ppd(const char *prefix, int verbose,
176		             const stp_printer_t *p, const char *language,
177			     ppd_type_t ppd_type);
178static int	generate_model_ppds(const char *prefix, int verbose,
179				    const stp_printer_t *printer,
180				    const char *language, int which_ppds);
181static void	help(void);
182static void	printlangs(char** langs);
183static void	printmodels(int verbose);
184static void	usage(void);
185#endif /* !CUPS_DRIVER_INTERFACE */
186static char	**getlangs(void);
187static int	is_special_option(const char *name);
188static void	print_group_close(gzFile fp, stp_parameter_class_t p_class,
189				  stp_parameter_level_t p_level,
190				  const char *language,
191				  const stp_string_list_t *po);
192static void	print_group_open(gzFile fp, stp_parameter_class_t p_class,
193				 stp_parameter_level_t p_level,
194				 const char *language,
195				 const stp_string_list_t *po);
196static int	write_ppd(gzFile fp, const stp_printer_t *p,
197		          const char *language, const char *ppd_location,
198			  ppd_type_t ppd_type, const char *filename);
199
200
201/*
202 * Global variables...
203 */
204
205
206#ifdef CUPS_DRIVER_INTERFACE
207
208/*
209 * 'main()' - Process files on the command-line...
210 */
211
212const char slang_c[] = "LANG=C";
213const char slcall_c[] = "LC_ALL=C";
214const char slcnumeric_c[] = "LC_NUMERIC=C";
215char lang_c[sizeof(slang_c) + 1];
216char lcall_c[sizeof(slcall_c) + 1];
217char lcnumeric_c[sizeof(slcnumeric_c) + 1];
218
219int				    /* O - Exit status */
220main(int  argc,			    /* I - Number of command-line arguments */
221     char *argv[])		    /* I - Command-line arguments */
222{
223 /*
224  * Force POSIX locale, since stp_init incorrectly calls setlocale...
225  */
226
227  strcpy(lang_c, slang_c);
228  strcpy(lcall_c, slcall_c);
229  strcpy(lcnumeric_c, slcnumeric_c);
230  putenv(lang_c);
231  putenv(lcall_c);
232  putenv(lcnumeric_c);
233
234 /*
235  * Initialise libgutenprint
236  */
237
238  stp_init();
239
240 /*
241  * Process command-line...
242  */
243
244  if (argc == 2 && !strcmp(argv[1], "list"))
245    return (list_ppds(argv[0]));
246  else if (argc == 3 && !strcmp(argv[1], "cat"))
247    return (cat_ppd(argv[2]));
248  else if (argc == 2 && !strcmp(argv[1], "org.gutenprint.multicat"))
249    {
250      char buf[1024];
251      int status = 0;
252      while (status == 0 && fgets(buf, sizeof(buf) - 1, stdin))
253	{
254	  size_t len = strlen(buf);
255	  if (len == 0)
256	    continue;
257	  if (buf[len - 1] == '\n')
258	    buf[len - 1] = '\0';
259	  status = cat_ppd(buf);
260	  fputs("*%*%EOFEOF\n", stdout);
261	  (void) fflush(stdout);
262	}
263      return status;
264    }
265  else if (argc == 2 && !strcmp(argv[1], "VERSION"))
266    {
267      printf("%s\n", VERSION);
268      return (0);
269    }
270  else if (argc == 2 && !strcasecmp(argv[1], "org.gutenprint.extensions"))
271    {
272      printf("org.gutenprint.multicat");
273      return (0);
274    }
275  else
276  {
277    fprintf(stderr, "Usage: %s list\n", argv[0]);
278    fprintf(stderr, "       %s cat URI\n", argv[0]);
279    return (1);
280  }
281}
282
283
284/*
285 * 'cat_ppd()' - Copy the named PPD to stdout.
286 */
287
288static int				/* O - Exit status */
289cat_ppd(const char *uri)	/* I - Driver URI */
290{
291  char			scheme[64],	/* URI scheme */
292			userpass[32],	/* URI user/pass (unused) */
293			hostname[32],	/* URI hostname */
294			resource[1024];	/* URI resource */
295  int			port;		/* URI port (unused) */
296  http_uri_status_t	status;		/* URI decode status */
297  const stp_printer_t	*p;		/* Printer driver */
298  const char		*lang = NULL;
299  char			*s;
300  char			filename[1024],		/* Filename */
301			ppd_location[1024];	/* Installed location */
302  const char 		*infix = "";
303  ppd_type_t 		ppd_type = PPD_STANDARD;
304
305  if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, uri,
306                                scheme, sizeof(scheme),
307                                userpass, sizeof(userpass),
308				hostname, sizeof(hostname),
309		                &port, resource, sizeof(resource)))
310				    < HTTP_URI_OK)
311  {
312    fprintf(stderr, "ERROR: Bad ppd-name \"%s\" (%d)!\n", uri, status);
313    return (1);
314  }
315
316  if (strcmp(scheme, "gutenprint." GUTENPRINT_RELEASE_VERSION) != 0)
317    {
318      fprintf(stderr, "ERROR: Gutenprint version mismatch!\n");
319      return(1);
320    }
321
322  s = strchr(resource + 1, '/');
323  if (s)
324    {
325      lang = s + 1;
326      *s = '\0';
327    }
328
329  if ((p = stp_get_printer_by_driver(hostname)) == NULL)
330  {
331    fprintf(stderr, "ERROR: Unable to find driver \"%s\"!\n", hostname);
332    return (1);
333  }
334
335  if (strcmp(resource + 1, "simple") == 0)
336    {
337      infix = ".sim";
338      ppd_type = PPD_SIMPLIFIED;
339    }
340  else if (strcmp(resource + 1, "nocolor") == 0)
341    {
342      infix = ".nc";
343      ppd_type = PPD_NO_COLOR_OPTS;
344    }
345
346  /*
347   * This isn't really the right thing to do.  We really shouldn't
348   * be embedding filenames in automatically generated PPD files, but
349   * if the user ever decides to go back from generated PPD files to
350   * static PPD files we'll need to have this for genppdupdate to work.
351   */
352  snprintf(filename, sizeof(filename) - 1, "stp-%s.%s%s%s",
353	   hostname, GUTENPRINT_RELEASE_VERSION, infix, ppdext);
354  snprintf(ppd_location, sizeof(ppd_location) - 1, "%s%s%s/ppd/%s%s",
355	   cups_modeldir,
356	   cups_modeldir[strlen(cups_modeldir) - 1] == '/' ? "" : "/",
357	   lang ? lang : "C",
358	   filename, gzext);
359
360  return (write_ppd(stdout, p, lang, ppd_location, ppd_type, filename));
361}
362
363/*
364 * 'list_ppds()' - List the available drivers.
365 */
366
367static int				/* O - Exit status */
368list_ppds(const char *argv0)		/* I - Name of program */
369{
370  const char		*scheme;	/* URI scheme */
371  int			i;		/* Looping var */
372  const stp_printer_t	*printer;	/* Pointer to printer driver */
373
374  if ((scheme = strrchr(argv0, '/')) != NULL)
375    scheme ++;
376  else
377    scheme = argv0;
378
379  for (i = 0; i < stp_printer_model_count(); i++)
380    if ((printer = stp_get_printer_by_index(i)) != NULL)
381    {
382      const char *device_id;
383      if (!strcmp(stp_printer_get_family(printer), "ps") ||
384	  !strcmp(stp_printer_get_family(printer), "raw"))
385        continue;
386
387      device_id = stp_printer_get_device_id(printer);
388      printf("\"%s://%s/expert\" "
389             "%s "
390	     "\"%s\" "
391             "\"%s" CUPS_PPD_NICKNAME_STRING VERSION "\" "
392	     "\"%s\"\n",
393             scheme, stp_printer_get_driver(printer),
394	     "en",
395	     stp_printer_get_manufacturer(printer),
396	     stp_printer_get_long_name(printer),
397	     device_id ? device_id : "");
398
399#ifdef GENERATE_SIMPLIFIED_PPDS
400      printf("\"%s://%s/simple\" "
401             "%s "
402	     "\"%s\" "
403             "\"%s" CUPS_PPD_NICKNAME_STRING VERSION " Simplified\" "
404	     "\"%s\"\n",
405             scheme, stp_printer_get_driver(printer),
406	     "en",
407	     stp_printer_get_manufacturer(printer),
408	     stp_printer_get_long_name(printer),
409	     device_id ? device_id : "");
410#endif
411
412#ifdef GENERATE_NOCOLOR_PPDS
413      printf("\"%s://%s/nocolor\" "
414             "%s "
415	     "\"%s\" "
416             "\"%s" CUPS_PPD_NICKNAME_STRING VERSION " No color options\" "
417	     "\"%s\"\n",
418             scheme, stp_printer_get_driver(printer),
419	     "en",
420	     stp_printer_get_manufacturer(printer),
421	     stp_printer_get_long_name(printer),
422	     device_id ? device_id : "");
423#endif
424    }
425
426  return (0);
427}
428#endif /* CUPS_DRIVER_INTERFACE */
429
430#ifndef CUPS_DRIVER_INTERFACE
431
432/*
433 * 'main()' - Process files on the command-line...
434 */
435
436int				    /* O - Exit status */
437main(int  argc,			    /* I - Number of command-line arguments */
438     char *argv[])		    /* I - Command-line arguments */
439{
440  int		i;		    /* Looping var */
441  const char	*prefix;	    /* Directory prefix for output */
442  const char	*language = NULL;   /* Language */
443  const stp_printer_t *printer;	    /* Pointer to printer driver */
444  int           verbose = 0;        /* Verbose messages */
445  char          **langs = NULL;     /* Available translations */
446  char          **models = NULL;    /* Models to output, all if NULL */
447  int           opt_printlangs = 0; /* Print available translations */
448  int           opt_printmodels = 0;/* Print available models */
449  int           which_ppds = 2;	    /* Simplified PPD's = 1, full = 2,
450				       no color opts = 4 */
451
452 /*
453  * Parse command-line args...
454  */
455
456  prefix   = CUPS_MODELDIR;
457
458  for (;;)
459  {
460    if ((i = getopt(argc, argv, "23hvqc:p:l:LMVd:saNC")) == -1)
461      break;
462
463    switch (i)
464    {
465    case '2':
466      cups_ppd_ps_level = 2;
467      break;
468    case '3':
469      cups_ppd_ps_level = 3;
470      break;
471    case 'h':
472      help();
473      exit(EXIT_SUCCESS);
474      break;
475    case 'v':
476      verbose = 1;
477      break;
478    case 'q':
479      verbose = 0;
480      break;
481    case 'c':
482      fputs("ERROR: -c option no longer supported!\n", stderr);
483      break;
484    case 'p':
485      prefix = optarg;
486#  ifdef DEBUG
487      fprintf(stderr, "DEBUG: prefix: %s\n", prefix);
488#  endif
489      break;
490    case 'l':
491      language = optarg;
492      break;
493    case 'L':
494      opt_printlangs = 1;
495      break;
496    case 'M':
497      opt_printmodels = 1;
498      break;
499    case 'd':
500      cups_modeldir = optarg;
501      break;
502    case 's':
503      which_ppds = 1;
504      break;
505    case 'a':
506      which_ppds = 3;
507      break;
508    case 'C':
509      which_ppds |= 4;
510      break;
511    case 'N':
512      localize_numbers = !localize_numbers;
513      break;
514    case 'V':
515      printf("cups-genppd version %s, "
516	     "Copyright 1993-2008 by Michael R Sweet and Robert Krawitz.\n\n",
517	     VERSION);
518      printf("Default CUPS PPD PostScript Level: %d\n", cups_ppd_ps_level);
519      printf("Default PPD location (prefix):     %s\n", CUPS_MODELDIR);
520      printf("Default base locale directory:     %s\n\n", PACKAGE_LOCALE_DIR);
521      puts("This program is free software; you can redistribute it and/or\n"
522	   "modify it under the terms of the GNU General Public License,\n"
523	   "version 2, as published by the Free Software Foundation.\n"
524	   "\n"
525	   "This program is distributed in the hope that it will be useful,\n"
526	   "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
527	   "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
528	   "GNU General Public License for more details.\n");
529      exit(EXIT_SUCCESS);
530      break;
531    default:
532      usage();
533      exit(EXIT_FAILURE);
534      break;
535    }
536  }
537  if (optind < argc) {
538    int n, numargs;
539    numargs = argc-optind;
540    models = stp_malloc((numargs+1) * sizeof(char*));
541    for (n=0; n<numargs; n++)
542      {
543	models[n] = argv[optind+n];
544      }
545    models[numargs] = (char*)NULL;
546
547    n=0;
548  }
549
550/*
551 * Initialise libgutenprint
552 */
553
554  stp_init();
555
556  langs = getlangs();
557
558 /*
559  * Print lists
560  */
561
562  if (opt_printlangs)
563    {
564      printlangs(langs);
565      exit(EXIT_SUCCESS);
566    }
567
568  if (opt_printmodels)
569    {
570      printmodels(verbose);
571      exit(EXIT_SUCCESS);
572    }
573
574 /*
575  * Write PPD files...
576  */
577
578  if (models)
579    {
580      int n;
581      for (n=0; models[n]; n++)
582	{
583	  printer = stp_get_printer_by_driver(models[n]);
584	  if (!printer)
585	    printer = stp_get_printer_by_long_name(models[n]);
586
587	  if (printer)
588	    {
589	      if (generate_model_ppds(prefix, verbose, printer, language,
590				      which_ppds))
591		return 1;
592	    }
593	  else
594	    {
595	      printf("Driver not found: %s\n", models[n]);
596	      return (1);
597	    }
598	}
599      stp_free(models);
600    }
601  else
602    {
603      for (i = 0; i < stp_printer_model_count(); i++)
604	{
605	  printer = stp_get_printer_by_index(i);
606
607	  if (printer)
608	    {
609	      if (generate_model_ppds(prefix, verbose, printer, language,
610				      which_ppds))
611		return 1;
612	    }
613	}
614    }
615  if (!verbose)
616    fprintf(stderr, " done.\n");
617
618  return (0);
619}
620
621static int
622generate_model_ppds(const char *prefix, int verbose,
623		    const stp_printer_t *printer, const char *language,
624		    int which_ppds)
625{
626  if ((which_ppds & 1) &&
627      generate_ppd(prefix, verbose, printer, language, PPD_SIMPLIFIED))
628    return (1);
629  if ((which_ppds & 2) &&
630      generate_ppd(prefix, verbose, printer, language, PPD_STANDARD))
631    return (1);
632  if ((which_ppds & 4) &&
633      generate_ppd(prefix, verbose, printer, language, PPD_NO_COLOR_OPTS))
634    return (1);
635  return 0;
636}
637
638/*
639 * 'generate_ppd()' - Generate a PPD file.
640 */
641
642static int				/* O - Exit status */
643generate_ppd(
644    const char          *prefix,	/* I - PPD directory prefix */
645    int                 verbose,	/* I - Verbosity level */
646    const stp_printer_t *p,		/* I - Driver */
647    const char          *language,	/* I - Primary language */
648    ppd_type_t          ppd_type)	/* I - full, simplified, no color */
649{
650  int		status;			/* Exit status */
651  gzFile	fp;			/* File to write to */
652  char		filename[1024],		/* Filename */
653		ppd_location[1024];	/* Installed location */
654  struct stat   dir;                    /* Prefix dir status */
655  const char    *ppd_infix;
656
657 /*
658  * Skip the PostScript drivers...
659  */
660
661  if (!strcmp(stp_printer_get_family(p), "ps") ||
662      !strcmp(stp_printer_get_family(p), "raw"))
663    return (0);
664
665 /*
666  * Make sure the destination directory exists...
667  */
668
669  if (stat(prefix, &dir) && !S_ISDIR(dir.st_mode))
670  {
671    if (mkdir(prefix, 0777))
672    {
673      printf("cups-genppd: Cannot create directory %s: %s\n",
674	     prefix, strerror(errno));
675      exit(EXIT_FAILURE);
676    }
677  }
678
679 /*
680  * The files will be named stp-<driver>.<major>.<minor>.ppd, for
681  * example:
682  *
683  * stp-escp2-ex.5.0.ppd
684  *
685  * or
686  *
687  * stp-escp2-ex.5.0.ppd.gz
688  */
689
690  switch (ppd_type)
691    {
692    case PPD_SIMPLIFIED:
693      ppd_infix = ".sim";
694      break;
695    case PPD_NO_COLOR_OPTS:
696      ppd_infix = ".nc";
697      break;
698    default:
699      ppd_infix = "";
700    }
701
702  snprintf(filename, sizeof(filename) - 1, "%s/stp-%s.%s%s%s%s",
703	   prefix, stp_printer_get_driver(p), GUTENPRINT_RELEASE_VERSION,
704	   ppd_infix, ppdext, gzext);
705
706 /*
707  * Open the PPD file...
708  */
709
710  if ((fp = gzopen(filename, "wb")) == NULL)
711  {
712    fprintf(stderr, "cups-genppd: Unable to create file \"%s\" - %s.\n",
713            filename, strerror(errno));
714    return (2);
715  }
716
717  if (verbose)
718    fprintf(stderr, "Writing %s...\n", filename);
719  else
720    fprintf(stderr, ".");
721
722  snprintf(ppd_location, sizeof(ppd_location), "%s%s%s/%s",
723	   cups_modeldir,
724	   cups_modeldir[strlen(cups_modeldir) - 1] == '/' ? "" : "/",
725	   language ? language : "C",
726	   basename(filename));
727
728  snprintf(filename, sizeof(filename) - 1, "stp-%s.%s%s%s",
729	   stp_printer_get_driver(p), GUTENPRINT_RELEASE_VERSION,
730	   ppd_infix, ppdext);
731
732  status = write_ppd(fp, p, language, ppd_location, ppd_type,
733		     basename(filename));
734
735  gzclose(fp);
736
737  return (status);
738}
739
740/*
741 * 'help()' - Show detailed help.
742 */
743
744void
745help(void)
746{
747  puts("Generate Gutenprint PPD files for use with CUPS\n\n");
748  usage();
749  puts("\nExamples: LANG=de_DE cups-genppd -p ppd -c /usr/share/locale\n"
750       "          cups-genppd -L -c /usr/share/locale\n"
751       "          cups-genppd -M -v\n\n"
752       "Commands:\n"
753       "  -h            Show this help message.\n"
754       "  -L            List available translations (message catalogs).\n"
755       "  -M            List available printer models.\n"
756       "  -V            Show version information and defaults.\n"
757       "  The default is to output PPDs.\n");
758  puts("Options:\n"
759       "  -l locale     Output PPDs translated with messages for locale.\n"
760       "  -p prefix     Output PPDs in directory prefix.\n"
761       "  -d prefix     Embed directory prefix in PPD file.\n"
762       "  -s            Generate simplified PPD files.\n"
763       "  -a            Generate all (simplified and full) PPD files.\n"
764       "  -q            Quiet mode.\n"
765       "  -v            Verbose mode.\n"
766       "models:\n"
767       "  A list of printer models, either the driver or quoted full name.\n");
768}
769
770/*
771 * 'usage()' - Show program usage.
772 */
773
774void
775usage(void)
776{
777  puts("Usage: cups-genppd "
778        "[-l locale] [-p prefix] [-s | -a] [-q] [-v] models...\n"
779        "       cups-genppd -L\n"
780	"       cups-genppd -M [-v]\n"
781	"       cups-genppd -h\n"
782	"       cups-genppd -V\n");
783}
784
785/*
786 * 'printlangs()' - Print list of available translations.
787 */
788
789void
790printlangs(char **langs)		/* I - Languages */
791{
792  if (langs)
793    {
794      int n = 0;
795      while (langs && langs[n])
796	{
797	  puts(langs[n]);
798	  n++;
799	}
800    }
801  exit(EXIT_SUCCESS);
802}
803
804
805/*
806 * 'printmodels()' - Print a list of available models.
807 */
808
809void
810printmodels(int verbose)		/* I - Verbosity level */
811{
812  const stp_printer_t *p;
813  int i;
814
815  for (i = 0; i < stp_printer_model_count(); i++)
816    {
817      p = stp_get_printer_by_index(i);
818      if (p &&
819	  strcmp(stp_printer_get_family(p), "ps") != 0 &&
820	  strcmp(stp_printer_get_family(p), "raw") != 0)
821	{
822	  if(verbose)
823	    printf("%-20s%s\n", stp_printer_get_driver(p),
824		   stp_printer_get_long_name(p));
825	  else
826	    printf("%s\n", stp_printer_get_driver(p));
827	}
828    }
829  exit(EXIT_SUCCESS);
830}
831
832#endif /* !CUPS_DRIVER_INTERFACE */
833
834
835/*
836 * 'getlangs()' - Get a list of available translations.
837 */
838
839char **					/* O - Array of languages */
840getlangs(void)
841{
842  int		i;			/* Looping var */
843  char		*ptr;			/* Pointer into string */
844  static char	all_linguas[] = ALL_LINGUAS;
845					/* List of languages from configure.ac */
846  static char **langs = NULL;		/* Array of languages */
847
848
849  if (!langs)
850  {
851   /*
852    * Create the langs array...
853    */
854
855    for (i = 1, ptr = strchr(all_linguas, ' '); ptr; ptr = strchr(ptr + 1, ' '))
856      i ++;
857
858    langs = calloc(i + 1, sizeof(char *));
859
860    langs[0] = all_linguas;
861    for (i = 1, ptr = strchr(all_linguas, ' '); ptr; ptr = strchr(ptr + 1, ' '))
862    {
863      *ptr     = '\0';
864      langs[i] = ptr + 1;
865      i ++;
866    }
867  }
868
869  return (langs);
870}
871
872
873/*
874 * 'is_special_option()' - Determine if an option should be grouped.
875 */
876
877static int				/* O - 1 if non-grouped, 0 otherwise */
878is_special_option(const char *name)	/* I - Option name */
879{
880  int i = 0;
881  while (special_options[i])
882    {
883      if (strcmp(name, special_options[i]) == 0)
884	return 1;
885      i++;
886    }
887  return 0;
888}
889
890/*
891 * strlen returns the number of characters.  PPD file limitations are
892 * defined in bytes.  So we need something to count bytes, not merely
893 * characters.
894 */
895
896static size_t
897bytelen(const char *buffer)
898{
899  size_t answer = 0;
900  while (*buffer++ != '\0')
901    answer++;
902  return answer;
903}
904
905/*
906 * Use our localization routine to correctly do localization on all
907 * systems.  The standard lookup routine has trouble creating multi-locale
908 * files on many systems, and on some systems there's not even a reliable
909 * way to use something other than the system locale.
910 */
911#ifdef _
912#undef _
913#endif
914#define _(x) stp_i18n_lookup(po, x)
915
916#define PPD_MAX_SHORT_NICKNAME (31)
917
918static void
919print_ppd_header(gzFile fp, ppd_type_t ppd_type, int model, const char *driver,
920		 const char *family, const char *long_name,
921		 const char *manufacturer, const char *device_id,
922		 const char *ppd_location,
923		 const char *language, const stp_string_list_t *po,
924		 char **all_langs)
925{
926  char short_long_name[(PPD_MAX_SHORT_NICKNAME) + 1];
927 /*
928  * Write a standard header...
929  */
930  gzputs(fp, "*PPD-Adobe: \"4.3\"\n");
931  gzputs(fp, "*% PPD file for CUPS/Gutenprint.\n");
932  gzputs(fp, "*% Copyright 1993-2008 by Mike Sweet and Robert Krawitz.\n");
933  gzputs(fp, "*% This program is free software; you can redistribute it and/or\n");
934  gzputs(fp, "*% modify it under the terms of the GNU General Public License,\n");
935  gzputs(fp, "*% version 2, as published by the Free Software Foundation.\n");
936  gzputs(fp, "*%\n");
937  gzputs(fp, "*% This program is distributed in the hope that it will be useful, but\n");
938  gzputs(fp, "*% WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n");
939  gzputs(fp, "*% or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n");
940  gzputs(fp, "*% for more details.\n");
941  gzputs(fp, "*%\n");
942  gzputs(fp, "*% You should have received a copy of the GNU General Public License\n");
943  gzputs(fp, "*% along with this program; if not, write to the Free Software\n");
944  gzputs(fp, "*% Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n");
945  gzputs(fp, "*%\n");
946  gzputs(fp, "*FormatVersion:	\"4.3\"\n");
947  gzputs(fp, "*FileVersion:	\"" VERSION "\"\n");
948  /* Specify language of PPD translation */
949  /* TRANSLATORS: Specify the language of the PPD translation.
950   * Use the English name of your language here, e.g. "Swedish" instead of
951   * "Svenska". */
952  gzprintf(fp, "*LanguageVersion: %s\n", _("English"));
953  if (language)
954    gzputs(fp, "*LanguageEncoding: UTF-8\n");
955  else
956    gzputs(fp, "*LanguageEncoding: ISOLatin1\n");
957 /*
958  * Strictly speaking, the PCFileName attribute should be a 12 character
959  * max (12345678.ppd) filename, as a requirement of the old PPD spec.
960  * The following code generates a (hopefully unique) 8.3 filename from
961  * the driver name, and makes the filename all UPPERCASE as well...
962  */
963
964  gzprintf(fp, "*PCFileName:	\"STP%05d.PPD\"\n",
965	   stp_get_printer_index_by_driver(driver) +
966	   ((int) ppd_type * stp_printer_model_count()));
967  gzprintf(fp, "*Manufacturer:	\"%s\"\n", manufacturer);
968
969 /*
970  * The Product attribute specifies the string returned by the PostScript
971  * interpreter.  The last one will appear in the CUPS "product" field,
972  * while all instances are available as attributes.  Rather than listing
973  * the PostScript interpreters we might encounter, we instead just list
974  * a single product line with the "long name" to be compatible with other
975  * CUPS-based drivers. (This is a change from Gutenprint 5.0 and earlier)
976  */
977
978  gzprintf(fp, "*Product:	\"(%s)\"\n", long_name);
979
980 /*
981  * The ModelName attribute now provides the long name rather than the
982  * short driver name...  The rastertoprinter driver looks up both...
983  */
984
985  gzprintf(fp, "*ModelName:     \"%s\"\n", long_name);
986  strncpy(short_long_name, long_name, PPD_MAX_SHORT_NICKNAME);
987  short_long_name[PPD_MAX_SHORT_NICKNAME] = '\0';
988  gzprintf(fp, "*ShortNickName: \"%s\"\n", short_long_name);
989
990 /*
991  * The Windows driver download stuff has problems with NickName fields
992  * with commas.  Now use a dash instead...
993  */
994
995 /*
996  * NOTE - code in rastertoprinter looks for this version string.
997  * If this is changed, the corresponding change must be made in
998  * rastertoprinter.c.  Look for "ppd->nickname"
999  */
1000  gzprintf(fp, "*NickName:      \"%s%s%s%s\"\n",
1001	   long_name, CUPS_PPD_NICKNAME_STRING, VERSION,
1002	   (ppd_type == PPD_SIMPLIFIED ? " Simplified" :
1003	    ppd_type == PPD_NO_COLOR_OPTS ? " No Color Options" : ""));
1004  if (cups_ppd_ps_level == 2)
1005    gzputs(fp, "*PSVersion:	\"(2017.000) 550\"\n");
1006  else
1007    gzputs(fp, "*PSVersion:	\"(3010.000) 0\"\n");
1008  gzprintf(fp, "*LanguageLevel:	\"%d\"\n", cups_ppd_ps_level);
1009}
1010
1011static void
1012print_ppd_header_3(gzFile fp, ppd_type_t ppd_type, int model, const char *driver,
1013		 const char *family, const char *long_name,
1014		 const char *manufacturer, const char *device_id,
1015		 const char *ppd_location,
1016		 const char *language, const stp_string_list_t *po,
1017		 char **all_langs)
1018{
1019  int i;
1020  gzputs(fp, "*FileSystem:	False\n");
1021  gzputs(fp, "*LandscapeOrientation: Plus90\n");
1022  gzputs(fp, "*TTRasterizer:	Type42\n");
1023
1024  gzputs(fp, "*cupsVersion:	1.2\n");
1025  gzputs(fp, "*cupsManualCopies: True\n");
1026  gzprintf(fp, "*cupsFilter:	\"application/vnd.cups-raster 100 rastertogutenprint.%s\"\n", GUTENPRINT_RELEASE_VERSION);
1027  if (strcasecmp(manufacturer, "EPSON") == 0)
1028    gzputs(fp, "*cupsFilter:	\"application/vnd.cups-command 33 commandtoepson\"\n");
1029  if (device_id)
1030    gzprintf(fp, "*1284DeviceID: \"%s\"\n", device_id);
1031  if (!language)
1032  {
1033   /*
1034    * Generate globalized PPDs when POSIX language is requested...
1035    */
1036
1037    const char *prefix = "*cupsLanguages: \"";
1038
1039    for (i = 0; all_langs[i]; i ++)
1040    {
1041      if (!strcmp(all_langs[i], "C") || !strcmp(all_langs[i], "en"))
1042        continue;
1043
1044      gzprintf(fp, "%s%s", prefix, all_langs[i]);
1045      prefix = " ";
1046    }
1047
1048    if (!strcmp(prefix, " "))
1049      gzputs(fp, "\"\n");
1050  }
1051}
1052
1053static void
1054print_ppd_header_2(gzFile fp, ppd_type_t ppd_type, int model, const char *driver,
1055		   const char *family, const char *long_name,
1056		   const char *manufacturer, const char *device_id,
1057		   const char *ppd_location,
1058		   const char *language, const stp_string_list_t *po,
1059		   char **all_langs)
1060{
1061  gzprintf(fp, "*StpDriverName:	\"%s\"\n", driver);
1062  gzprintf(fp, "*StpDriverModelFamily:	\"%d_%s\"\n", model, family);
1063  gzprintf(fp, "*StpPPDLocation: \"%s\"\n", ppd_location);
1064  gzprintf(fp, "*StpLocale:	\"%s\"\n", language ? language : "C");
1065}
1066
1067static void
1068print_page_sizes(gzFile fp, stp_vars_t *v, int simplified,
1069		 const stp_string_list_t *po)
1070{
1071  int variable_sizes = 0;
1072  stp_parameter_t desc;
1073  int num_opts;
1074  paper_t *the_papers;
1075  int i;
1076  int		width, height,		/* Page information */
1077		bottom, left,
1078		top, right;
1079  int		min_width,		/* Min/max custom size */
1080		min_height,
1081		max_width,
1082		max_height;
1083  const stp_param_string_t *opt;
1084  int cur_opt = 0;
1085
1086  stp_describe_parameter(v, "PageSize", &desc);
1087  num_opts = stp_string_list_count(desc.bounds.str);
1088  the_papers = stp_malloc(sizeof(paper_t) * num_opts);
1089  for (i = 0; i < num_opts; i++)
1090    {
1091      const stp_papersize_t *papersize;
1092      opt = stp_string_list_param(desc.bounds.str, i);
1093      papersize = stp_get_papersize_by_name(opt->name);
1094
1095      if (!papersize)
1096	{
1097	  printf("Unable to lookup size %s!\n", opt->name);
1098	  continue;
1099	}
1100
1101      if (strcmp(opt->name, "Custom") == 0)
1102	{
1103	  variable_sizes = 1;
1104	  continue;
1105	}
1106      if (simplified && num_opts >= 10 &&
1107	  (papersize->paper_unit == PAPERSIZE_ENGLISH_EXTENDED ||
1108	   papersize->paper_unit == PAPERSIZE_METRIC_EXTENDED))
1109	continue;
1110
1111      width  = papersize->width;
1112      height = papersize->height;
1113
1114      if (width <= 0 || height <= 0)
1115	continue;
1116
1117      stp_set_string_parameter(v, "PageSize", opt->name);
1118
1119      stp_get_media_size(v, &width, &height);
1120      stp_get_maximum_imageable_area(v, &left, &right, &bottom, &top);
1121
1122      if (left < 0)
1123	left = 0;
1124      if (right > width)
1125	right = width;
1126      if (bottom > height)
1127	bottom = height;
1128      if (top < 0)
1129	top = 0;
1130
1131      the_papers[cur_opt].name   = opt->name;
1132      the_papers[cur_opt].text   = stp_i18n_lookup(po, opt->text);
1133      the_papers[cur_opt].width  = width;
1134      the_papers[cur_opt].height = height;
1135      the_papers[cur_opt].left   = left;
1136      the_papers[cur_opt].right  = right;
1137      the_papers[cur_opt].bottom = height - bottom;
1138      the_papers[cur_opt].top    = height - top;
1139
1140      cur_opt++;
1141      stp_clear_string_parameter(v, "PageSize");
1142    }
1143
1144  /*
1145   * The VariablePaperSize attribute is obsolete, however some popular
1146   * applications still look for it to provide custom page size support.
1147   */
1148
1149  gzprintf(fp, "*VariablePaperSize: %s\n\n", variable_sizes ? "true" : "false");
1150
1151  if (stp_parameter_has_category_value(v, &desc, "Color", "Yes"))
1152    gzputs(fp, "*ColorKeyWords: \"PageSize\"\n");
1153  gzprintf(fp, "*OpenUI *PageSize/%s: PickOne\n", _("Media Size"));
1154  gzputs(fp, "*OPOptionHints PageSize: \"dropdown\"\n");
1155  gzputs(fp, "*OrderDependency: 10 AnySetup *PageSize\n");
1156  gzprintf(fp, "*StpStp%s: %d %d %d %d %d %.3f %.3f %.3f\n",
1157	   desc.name, desc.p_type, desc.is_mandatory,
1158	   desc.p_class, desc.p_level, desc.channel, 0.0, 0.0, 0.0);
1159  gzprintf(fp, "*DefaultPageSize: %s\n", desc.deflt.str);
1160  gzprintf(fp, "*StpDefaultPageSize: %s\n", desc.deflt.str);
1161  for (i = 0; i < cur_opt; i ++)
1162    {
1163      gzprintf(fp,  "*PageSize %s", the_papers[i].name);
1164      gzprintf(fp, "/%s:\t\"<</PageSize[%d %d]/ImagingBBox null>>setpagedevice\"\n",
1165	       the_papers[i].text, the_papers[i].width, the_papers[i].height);
1166    }
1167  gzputs(fp, "*CloseUI: *PageSize\n\n");
1168
1169  if (stp_parameter_has_category_value(v, &desc, "Color", "Yes"))
1170    gzputs(fp, "*ColorKeyWords: \"PageRegion\"\n");
1171  gzprintf(fp, "*OpenUI *PageRegion/%s: PickOne\n", _("Media Size"));
1172  gzputs(fp, "*OPOptionHints PageRegion: \"dropdown\"\n");
1173  gzputs(fp, "*OrderDependency: 10 AnySetup *PageRegion\n");
1174  gzprintf(fp, "*DefaultPageRegion: %s\n", desc.deflt.str);
1175  gzprintf(fp, "*StpDefaultPageRegion: %s\n", desc.deflt.str);
1176  for (i = 0; i < cur_opt; i ++)
1177    {
1178      gzprintf(fp,  "*PageRegion %s", the_papers[i].name);
1179      gzprintf(fp, "/%s:\t\"<</PageSize[%d %d]/ImagingBBox null>>setpagedevice\"\n",
1180	       the_papers[i].text, the_papers[i].width, the_papers[i].height);
1181    }
1182  gzputs(fp, "*CloseUI: *PageRegion\n\n");
1183
1184  gzprintf(fp, "*DefaultImageableArea: %s\n", desc.deflt.str);
1185  gzprintf(fp, "*StpDefaultImageableArea: %s\n", desc.deflt.str);
1186  for (i = 0; i < cur_opt; i ++)
1187    {
1188      gzprintf(fp,  "*ImageableArea %s", the_papers[i].name);
1189      gzprintf(fp, "/%s:\t\"%d %d %d %d\"\n", the_papers[i].text,
1190	       the_papers[i].left, the_papers[i].bottom,
1191	       the_papers[i].right, the_papers[i].top);
1192    }
1193  gzputs(fp, "\n");
1194
1195  gzprintf(fp, "*DefaultPaperDimension: %s\n", desc.deflt.str);
1196  gzprintf(fp, "*StpDefaultPaperDimension: %s\n", desc.deflt.str);
1197
1198  for (i = 0; i < cur_opt; i ++)
1199    {
1200      gzprintf(fp, "*PaperDimension %s", the_papers[i].name);
1201      gzprintf(fp, "/%s:\t\"%d %d\"\n",
1202	       the_papers[i].text, the_papers[i].width, the_papers[i].height);
1203    }
1204  gzputs(fp, "\n");
1205
1206  if (variable_sizes)
1207    {
1208      stp_get_size_limit(v, &max_width, &max_height, &min_width, &min_height);
1209      stp_set_string_parameter(v, "PageSize", "Custom");
1210      stp_get_media_size(v, &width, &height);
1211      stp_get_maximum_imageable_area(v, &left, &right, &bottom, &top);
1212      if (left < 0)
1213	left = 0;
1214      if (top < 0)
1215	top = 0;
1216      if (bottom > height)
1217	bottom = height;
1218      if (right > width)
1219	width = right;
1220
1221      gzprintf(fp, "*MaxMediaWidth:  \"%d\"\n", max_width);
1222      gzprintf(fp, "*MaxMediaHeight: \"%d\"\n", max_height);
1223      gzprintf(fp, "*HWMargins:      %d %d %d %d\n",
1224	       left, height - bottom, width - right, top);
1225      gzputs(fp, "*CustomPageSize True: \"pop pop pop <</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice\"\n");
1226      gzprintf(fp, "*ParamCustomPageSize Width:        1 points %d %d\n",
1227	       min_width, max_width);
1228      gzprintf(fp, "*ParamCustomPageSize Height:       2 points %d %d\n",
1229	       min_height, max_height);
1230      gzputs(fp, "*ParamCustomPageSize WidthOffset:  3 points 0 0\n");
1231      gzputs(fp, "*ParamCustomPageSize HeightOffset: 4 points 0 0\n");
1232      gzputs(fp, "*ParamCustomPageSize Orientation:  5 int 0 0\n\n");
1233      stp_clear_string_parameter(v, "PageSize");
1234    }
1235
1236  stp_parameter_description_destroy(&desc);
1237  if (the_papers)
1238    stp_free(the_papers);
1239}
1240
1241static void
1242print_color_setup(gzFile fp, int simplified, int printer_is_color,
1243		  const stp_string_list_t *po)
1244{
1245  gzputs(fp, "*ColorKeyWords: \"ColorModel\"\n");
1246  gzprintf(fp, "*OpenUI *ColorModel/%s: PickOne\n", _("Color Model"));
1247  gzputs(fp, "*OPOptionHints ColorModel: \"radiobuttons\"\n");
1248  gzputs(fp, "*OrderDependency: 10 AnySetup *ColorModel\n");
1249
1250  if (printer_is_color)
1251    {
1252      gzputs(fp, "*DefaultColorModel: RGB\n");
1253      gzputs(fp, "*StpDefaultColorModel: RGB\n");
1254    }
1255  else
1256    {
1257      gzputs(fp, "*DefaultColorModel: Gray\n");
1258      gzputs(fp, "*StpDefaultColorModel: Gray\n");
1259    }
1260
1261  gzprintf(fp, "*ColorModel Gray/%s:\t\"<<"
1262               "/cupsColorSpace %d"
1263	       "/cupsColorOrder %d"
1264	       "%s"
1265	       ">>setpagedevice\"\n",
1266           _("Grayscale"), CUPS_CSPACE_W, CUPS_ORDER_CHUNKED,
1267	   simplified ? "/cupsBitsPerColor 8/cupsPreferredBitsPerColor 16" : "");
1268  gzprintf(fp, "*ColorModel Black/%s:\t\"<<"
1269               "/cupsColorSpace %d"
1270	       "/cupsColorOrder %d"
1271	       "%s"
1272	       ">>setpagedevice\"\n",
1273           _("Inverted Grayscale"), CUPS_CSPACE_K, CUPS_ORDER_CHUNKED,
1274	   simplified ? "/cupsBitsPerColor 8/cupsPreferredBitsPerColor 16" : "");
1275
1276  if (printer_is_color)
1277  {
1278    gzprintf(fp, "*ColorModel RGB/%s:\t\"<<"
1279                 "/cupsColorSpace %d"
1280		 "/cupsColorOrder %d"
1281	         "%s"
1282		 ">>setpagedevice\"\n",
1283             _("RGB Color"), CUPS_CSPACE_RGB, CUPS_ORDER_CHUNKED,
1284	     simplified ? "/cupsBitsPerColor 8/cupsPreferredBitsPerColor 16" : "");
1285    gzprintf(fp, "*ColorModel CMY/%s:\t\"<<"
1286                 "/cupsColorSpace %d"
1287		 "/cupsColorOrder %d"
1288	         "%s"
1289		 ">>setpagedevice\"\n",
1290             _("CMY Color"), CUPS_CSPACE_CMY, CUPS_ORDER_CHUNKED,
1291	     simplified ? "/cupsBitsPerColor 8/cupsPreferredBitsPerColor 16" : "");
1292    gzprintf(fp, "*ColorModel CMYK/%s:\t\"<<"
1293                 "/cupsColorSpace %d"
1294		 "/cupsColorOrder %d"
1295	         "%s"
1296		 ">>setpagedevice\"\n",
1297             _("CMYK"), CUPS_CSPACE_CMYK, CUPS_ORDER_CHUNKED,
1298	     simplified ? "/cupsBitsPerColor 8/cupsPreferredBitsPerColor 16" : "");
1299    gzprintf(fp, "*ColorModel KCMY/%s:\t\"<<"
1300                 "/cupsColorSpace %d"
1301		 "/cupsColorOrder %d"
1302	         "%s"
1303		 ">>setpagedevice\"\n",
1304             _("KCMY"), CUPS_CSPACE_KCMY, CUPS_ORDER_CHUNKED,
1305	     simplified ? "/cupsBitsPerColor 8/cupsPreferredBitsPerColor 16" : "");
1306  }
1307
1308  gzputs(fp, "*CloseUI: *ColorModel\n\n");
1309  if (!simplified)
1310    {
1311      /*
1312       * 8 or 16 bit color (16 bit is slower)
1313       */
1314      gzputs(fp, "*ColorKeyWords: \"StpColorPrecision\"\n");
1315      gzprintf(fp, "*OpenUI *StpColorPrecision/%s: PickOne\n", _("Color Precision"));
1316      gzputs(fp, "*OPOptionHints StpColorPrecision: \"radiobuttons\"\n");
1317      gzputs(fp, "*OrderDependency: 10 AnySetup *StpColorPrecision\n");
1318      gzputs(fp, "*DefaultStpColorPrecision: Normal\n");
1319      gzputs(fp, "*StpDefaultStpColorPrecision: Normal\n");
1320      gzprintf(fp, "*StpColorPrecision Normal/%s:\t\"<<"
1321	           "/cupsBitsPerColor 8>>setpagedevice\"\n", _("Normal"));
1322      gzprintf(fp, "*StpColorPrecision Best/%s:\t\"<<"
1323		   "/cupsBitsPerColor 8"
1324		   "/cupsPreferredBitsPerColor 16>>setpagedevice\"\n", _("Best"));
1325      gzputs(fp, "*CloseUI: *StpColorPrecision\n\n");
1326    }
1327}
1328
1329static void
1330print_group(
1331    gzFile                fp,		/* I - File to write to */
1332    const char		  *what,
1333    stp_parameter_class_t p_class,	/* I - Option class */
1334    stp_parameter_level_t p_level,	/* I - Option level */
1335    const char		  *language,	/* I - Language */
1336    const stp_string_list_t     *po)		/* I - Message catalog */
1337{
1338  char buf[64];
1339  const char *class = stp_i18n_lookup(po, parameter_class_names[p_class]);
1340  const char *level = stp_i18n_lookup(po, parameter_level_names[p_level]);
1341  size_t bytes = bytelen(class) + bytelen(level);
1342  snprintf(buf, 40, "%s%s%s", class, bytes < 39 ? " " : "", level);
1343  gzprintf(fp, "*%sGroup: C%dL%d/%s\n", what, p_class, p_level, buf);
1344  if (language && !strcmp(language, "C") && !strcmp(what, "Open"))
1345    {
1346      char		**all_langs = getlangs();/* All languages */
1347      const char *lang;
1348      int langnum;
1349
1350      for (langnum = 0; all_langs[langnum]; langnum ++)
1351	{
1352	  const stp_string_list_t *altpo;
1353
1354	  lang = all_langs[langnum];
1355
1356	  if (!strcmp(lang, "C") || !strcmp(lang, "en"))
1357	    continue;
1358	  if ((altpo = stp_i18n_load(lang)) != NULL)
1359	    {
1360	      class = stp_i18n_lookup(altpo, parameter_class_names[p_class]);
1361	      level = stp_i18n_lookup(altpo, parameter_level_names[p_level]);
1362	      bytes = bytelen(class) + bytelen(level);
1363	      snprintf(buf, 40, "%s%s%s", class, bytes < 39 ? " " : "", level);
1364	      gzprintf(fp, "*%s.Translation C%dL%d/%s: \"\"\n",
1365		       lang, p_class, p_level, buf);
1366            }
1367	}
1368    }
1369  gzputs(fp, "\n");
1370}
1371
1372/*
1373 * 'print_group_close()' - Close a UI group.
1374 */
1375
1376static void
1377print_group_close(
1378    gzFile                fp,		/* I - File to write to */
1379    stp_parameter_class_t p_class,	/* I - Option class */
1380    stp_parameter_level_t p_level,	/* I - Option level */
1381    const char		 *language,	/* I - language */
1382    const stp_string_list_t    *po)		/* I - Message catalog */
1383{
1384  print_group(fp, "Close", p_class, p_level, NULL, NULL);
1385}
1386
1387
1388/*
1389 * 'print_group_open()' - Open a new UI group.
1390 */
1391
1392static void
1393print_group_open(
1394    gzFile                fp,		/* I - File to write to */
1395    stp_parameter_class_t p_class,	/* I - Option class */
1396    stp_parameter_level_t p_level,	/* I - Option level */
1397    const char		 *language,	/* I - language */
1398    const stp_string_list_t    *po)		/* I - Message catalog */
1399{
1400  print_group(fp, "Open", p_class, p_level, language ? language : "C", po);
1401}
1402
1403static void
1404print_one_option(gzFile fp, stp_vars_t *v, const stp_string_list_t *po,
1405		 ppd_type_t ppd_type, const stp_parameter_t *lparam,
1406		 const stp_parameter_t *desc)
1407{
1408  int num_opts;
1409  int i;
1410  const stp_param_string_t *opt;
1411  int printed_default_value = 0;
1412  int simplified = ppd_type == PPD_SIMPLIFIED;
1413  char		dimstr[255];		/* Dimension string */
1414  int print_close_ui = 1;
1415  int is_color_opt = stp_parameter_has_category_value(v, desc, "Color", "Yes");
1416  int skip_color = (ppd_type == PPD_NO_COLOR_OPTS && is_color_opt);
1417  if (is_color_opt)
1418    gzprintf(fp, "*ColorKeyWords: \"Stp%s\"\n", desc->name);
1419  gzprintf(fp, "*OpenUI *Stp%s/%s: PickOne\n",
1420	   desc->name, stp_i18n_lookup(po, desc->text));
1421  gzprintf(fp, "*OrderDependency: 10 AnySetup *Stp%s\n", desc->name);
1422  switch (desc->p_type)
1423    {
1424    case STP_PARAMETER_TYPE_STRING_LIST:
1425      num_opts = stp_string_list_count(desc->bounds.str);
1426      if (! skip_color)
1427	{
1428	  if (num_opts > 3)
1429	    gzprintf(fp, "*OPOptionHints Stp%s: \"dropdown\"\n", lparam->name);
1430	  else
1431	    gzprintf(fp, "*OPOptionHints Stp%s: \"radiobuttons\"\n", lparam->name);
1432	}
1433      gzprintf(fp, "*StpStp%s: %d %d %d %d %d %.3f %.3f %.3f\n",
1434	       desc->name, desc->p_type, desc->is_mandatory, desc->p_class,
1435	       desc->p_level, desc->channel, 0.0, 0.0, 0.0);
1436      if (desc->is_mandatory)
1437	{
1438	  gzprintf(fp, "*DefaultStp%s: %s\n", desc->name, desc->deflt.str);
1439	  gzprintf(fp, "*StpDefaultStp%s: %s\n", desc->name, desc->deflt.str);
1440	}
1441      else
1442	{
1443	  gzprintf(fp, "*DefaultStp%s: None\n", desc->name);
1444	  gzprintf(fp, "*StpDefaultStp%s: None\n", desc->name);
1445	  gzprintf(fp, "*Stp%s %s/%s: \"\"\n", desc->name, "None", _("None"));
1446	}
1447      for (i = 0; i < num_opts; i++)
1448	{
1449	  opt = stp_string_list_param(desc->bounds.str, i);
1450	  if (skip_color && strcmp(opt->name, desc->deflt.str) != 0)
1451	    gzprintf(fp, "*?Stp%s %s/%s: \"\"\n",
1452		     desc->name, opt->name, stp_i18n_lookup(po, opt->text));
1453	  else
1454	    gzprintf(fp, "*Stp%s %s/%s: \"\"\n",
1455		     desc->name, opt->name, stp_i18n_lookup(po, opt->text));
1456	}
1457      break;
1458    case STP_PARAMETER_TYPE_BOOLEAN:
1459      gzprintf(fp, "*OPOptionHints Stp%s: \"checkbox\"\n", lparam->name);
1460      gzprintf(fp, "*StpStp%s: %d %d %d %d %d %.3f %.3f %.3f\n",
1461	       desc->name, desc->p_type, desc->is_mandatory, desc->p_class,
1462	       desc->p_level, desc->channel, 0.0, 0.0,
1463	       desc->deflt.boolean ? 1.0 : 0.0);
1464      if (desc->is_mandatory)
1465	{
1466	  gzprintf(fp, "*DefaultStp%s: %s\n", desc->name,
1467		   desc->deflt.boolean ? "True" : "False");
1468	  gzprintf(fp, "*StpDefaultStp%s: %s\n", desc->name,
1469		   desc->deflt.boolean ? "True" : "False");
1470	  if (skip_color)
1471	    gzprintf(fp, "*Stp%s %s/%s: \"\"\n",
1472		     desc->name, desc->deflt.boolean ? "True" : "False",
1473		     desc->deflt.boolean ? _("Yes") : _("No"));
1474	}
1475      else
1476	{
1477	  gzprintf(fp, "*DefaultStp%s: None\n", desc->name);
1478	  gzprintf(fp, "*StpDefaultStp%s: None\n", desc->name);
1479	  gzprintf(fp, "*Stp%s %s/%s: \"\"\n", desc->name, "None", _("None"));
1480	}
1481      gzprintf(fp, "*%sStp%s %s/%s: \"\"\n",
1482	       (skip_color ? "?" : ""), desc->name, "False", _("No"));
1483      gzprintf(fp, "*%sStp%s %s/%s: \"\"\n",
1484	       (skip_color ? "?" : ""), desc->name, "True", _("Yes"));
1485      break;
1486    case STP_PARAMETER_TYPE_DOUBLE:
1487      gzprintf(fp, "*OPOptionHints Stp%s: \"slider input spinbox\"\n",
1488	       lparam->name);
1489      gzprintf(fp, "*StpStp%s: %d %d %d %d %d %.3f %.3f %.3f\n",
1490	       desc->name, desc->p_type, desc->is_mandatory, desc->p_class,
1491	       desc->p_level, desc->channel, desc->bounds.dbl.lower,
1492	       desc->bounds.dbl.upper, desc->deflt.dbl);
1493      gzprintf(fp, "*DefaultStp%s: None\n", desc->name);
1494      gzprintf(fp, "*StpDefaultStp%s: None\n", desc->name);
1495      if (!skip_color)
1496	{
1497	  for (i = desc->bounds.dbl.lower * 1000;
1498	       i <= desc->bounds.dbl.upper * 1000 ; i += 100)
1499	    {
1500	      if (desc->deflt.dbl * 1000 == i && desc->is_mandatory)
1501		{
1502		  gzprintf(fp, "*Stp%s None/%.3f: \"\"\n",
1503			   desc->name, ((double) i) * .001);
1504		  printed_default_value = 1;
1505		}
1506	      else
1507		gzprintf(fp, "*Stp%s %d/%.3f: \"\"\n",
1508			 desc->name, i, ((double) i) * .001);
1509	    }
1510	}
1511      if (!desc->is_mandatory)
1512	gzprintf(fp, "*Stp%s None/%s: \"\"\n", desc->name, _("None"));
1513      else if (! printed_default_value)
1514	gzprintf(fp, "*Stp%s None/%.3f: \"\"\n", desc->name, desc->deflt.dbl);
1515      gzprintf(fp, "*CloseUI: *Stp%s\n\n", desc->name);
1516
1517      /*
1518       * Add custom option code and value parameter...
1519       */
1520
1521      gzprintf(fp, "*CustomStp%s True: \"pop\"\n", desc->name);
1522      gzprintf(fp, "*ParamCustomStp%s Value/%s: 1 real %.3f %.3f\n\n",
1523	       desc->name, _("Value"),  desc->bounds.dbl.lower,
1524	       desc->bounds.dbl.upper);
1525      if (!simplified && !skip_color)
1526	{
1527	  if (is_color_opt)
1528	    gzprintf(fp, "*ColorKeyWords: \"StpFine%s\"\n", desc->name);
1529	  gzprintf(fp, "*OpenUI *StpFine%s/%s %s: PickOne\n",
1530		   desc->name, stp_i18n_lookup(po, desc->text),
1531		   _("Fine Adjustment"));
1532	  gzprintf(fp, "*OPOptionHints StpFine%s: \"hide\"\n", lparam->name);
1533	  gzprintf(fp, "*StpStpFine%s: %d %d %d %d %d %.3f %.3f %.3f\n",
1534		   desc->name, STP_PARAMETER_TYPE_INVALID, 0,
1535		   0, 0, -1, 0.0, 0.0, 0.0);
1536	  gzprintf(fp, "*DefaultStpFine%s: None\n", desc->name);
1537	  gzprintf(fp, "*StpDefaultStpFine%s: None\n", desc->name);
1538	  gzprintf(fp, "*StpFine%s None/0.000: \"\"\n", desc->name);
1539	  for (i = 0; i < 100; i += 5)
1540	    gzprintf(fp, "*StpFine%s %d/%.3f: \"\"\n",
1541		     desc->name, i, ((double) i) * .001);
1542	  gzprintf(fp, "*CloseUI: *StpFine%s\n\n", desc->name);
1543	}
1544      print_close_ui = 0;
1545
1546      break;
1547    case STP_PARAMETER_TYPE_DIMENSION:
1548      gzprintf(fp, "*OPOptionHints Stp%s: \"length slider input spinbox\"\n",
1549	       lparam->name);
1550      gzprintf(fp, "*StpStp%s: %d %d %d %d %d %.3f %.3f %.3f\n",
1551	       desc->name, desc->p_type, desc->is_mandatory,
1552	       desc->p_class, desc->p_level, desc->channel,
1553	       (double) desc->bounds.dimension.lower,
1554	       (double) desc->bounds.dimension.upper,
1555	       (double) desc->deflt.dimension);
1556      if (desc->is_mandatory)
1557	{
1558	  gzprintf(fp, "*DefaultStp%s: %d\n",
1559		   desc->name, desc->deflt.dimension);
1560	  gzprintf(fp, "*StpDefaultStp%s: %d\n",
1561		   desc->name, desc->deflt.dimension);
1562	}
1563      else
1564	{
1565	  gzprintf(fp, "*DefaultStp%s: None\n", desc->name);
1566	  gzprintf(fp, "*StpDefaultStp%s: None\n", desc->name);
1567	  gzprintf(fp, "*Stp%s %s/%s: \"\"\n", desc->name, "None", _("None"));
1568	}
1569      if (!skip_color)
1570	{
1571	  for (i = desc->bounds.dimension.lower;
1572	       i <= desc->bounds.dimension.upper; i++)
1573	    {
1574	      snprintf(dimstr, sizeof(dimstr), _("%.1f mm"),
1575		       (double)i * 25.4 / 72.0);
1576	      gzprintf(fp, "*Stp%s %d/%s: \"\"\n", desc->name, i, dimstr);
1577	    }
1578	}
1579
1580      print_close_ui = 0;
1581      gzprintf(fp, "*CloseUI: *Stp%s\n\n", desc->name);
1582
1583      /*
1584       * Add custom option code and value parameter...
1585       */
1586
1587      gzprintf(fp, "*CustomStp%s True: \"pop\"\n", desc->name);
1588      gzprintf(fp, "*ParamCustomStp%s Value/%s: 1 points %d %d\n\n",
1589	       desc->name, _("Value"), desc->bounds.dimension.lower,
1590	       desc->bounds.dimension.upper);
1591
1592      break;
1593    case STP_PARAMETER_TYPE_INT:
1594      gzprintf(fp, "*OPOptionHints Stp%s: \"input spinbox\"\n", lparam->name);
1595      gzprintf(fp, "*StpStp%s: %d %d %d %d %d %.3f %.3f %.3f\n",
1596	       desc->name, desc->p_type, desc->is_mandatory, desc->p_class,
1597	       desc->p_level, desc->channel,
1598	       (double) desc->bounds.integer.lower,
1599	       (double) desc->bounds.integer.upper,
1600	       (double) desc->deflt.integer);
1601      if (desc->is_mandatory)
1602	{
1603	  gzprintf(fp, "*DefaultStp%s: %d\n", desc->name, desc->deflt.integer);
1604	  gzprintf(fp, "*StpDefaultStp%s: %d\n", desc->name, desc->deflt.integer);
1605	  if (skip_color)
1606	    gzprintf(fp, "*Stp%s %d/%d: \"\"\n", desc->name,
1607		     desc->deflt.integer, desc->deflt.integer);
1608	}
1609      else
1610	{
1611	  gzprintf(fp, "*DefaultStp%s: None\n", desc->name);
1612	  gzprintf(fp, "*StpDefaultStp%s: None\n", desc->name);
1613	  gzprintf(fp, "*Stp%s %s/%s: \"\"\n", desc->name, "None", _("None"));
1614	}
1615      for (i = desc->bounds.integer.lower; i <= desc->bounds.integer.upper; i++)
1616	{
1617	  gzprintf(fp, "*%sStp%s %d/%d: \"\"\n",
1618		   (skip_color ? "?" : ""), desc->name, i, i);
1619	}
1620
1621      print_close_ui = 0;
1622      gzprintf(fp, "*CloseUI: *Stp%s\n\n", desc->name);
1623
1624      /*
1625       * Add custom option code and value parameter...
1626       */
1627
1628      gzprintf(fp, "*CustomStp%s True: \"pop\"\n", desc->name);
1629      gzprintf(fp, "*ParamCustomStp%s Value/%s: 1 int %d %d\n\n", desc->name,
1630	       _("Value"), desc->bounds.dimension.lower,
1631	       desc->bounds.dimension.upper);
1632
1633      break;
1634    default:
1635      break;
1636    }
1637  if (print_close_ui)
1638    gzprintf(fp, "*CloseUI: *Stp%s\n\n", desc->name);
1639}
1640
1641static void
1642print_one_localization(gzFile fp, const stp_string_list_t *po,
1643		       int simplified, const char *lang,
1644		       const stp_parameter_t *lparam,
1645		       const stp_parameter_t *desc)
1646{
1647  int num_opts;
1648  int i;
1649  const stp_param_string_t *opt;
1650  char		dimstr[255];		/* Dimension string */
1651
1652  gzprintf(fp, "*%s.Translation Stp%s/%s: \"\"\n", lang,
1653	   desc->name, stp_i18n_lookup(po, desc->text));
1654  switch (desc->p_type)
1655    {
1656    case STP_PARAMETER_TYPE_STRING_LIST:
1657      if (!desc->is_mandatory)
1658	gzprintf(fp, "*%s.Stp%s %s/%s: \"\"\n", lang, desc->name,
1659		 "None", _("None"));
1660      num_opts = stp_string_list_count(desc->bounds.str);
1661      for (i = 0; i < num_opts; i++)
1662	{
1663	  opt = stp_string_list_param(desc->bounds.str, i);
1664	  gzprintf(fp, "*%s.Stp%s %s/%s: \"\"\n", lang,
1665		   desc->name, opt->name, stp_i18n_lookup(po, opt->text));
1666	}
1667      break;
1668
1669    case STP_PARAMETER_TYPE_BOOLEAN:
1670      if (!desc->is_mandatory)
1671	gzprintf(fp, "*%s.Stp%s %s/%s: \"\"\n", lang, desc->name,
1672		 "None", _("None"));
1673      gzprintf(fp, "*%s.Stp%s %s/%s: \"\"\n", lang, desc->name, "False", _("No"));
1674      gzprintf(fp, "*%s.Stp%s %s/%s: \"\"\n", lang, desc->name, "True", _("Yes"));
1675      break;
1676
1677    case STP_PARAMETER_TYPE_DOUBLE:
1678      if (localize_numbers)
1679	{
1680	  for (i = desc->bounds.dbl.lower * 1000;
1681	       i <= desc->bounds.dbl.upper * 1000; i += 100)
1682	    {
1683	      if (desc->deflt.dbl * 1000 == i && desc->is_mandatory)
1684		gzprintf(fp, "*%s.Stp%s None/%.3f: \"\"\n", lang,
1685			 desc->name, ((double) i) * .001);
1686	      else
1687		gzprintf(fp, "*%s.Stp%s %d/%.3f: \"\"\n", lang,
1688			 desc->name, i, ((double) i) * .001);
1689	    }
1690	}
1691      if (!desc->is_mandatory)
1692	gzprintf(fp, "*%s.Stp%s None/%s: \"\"\n", lang, desc->name, _("None"));
1693      gzprintf(fp, "*%s.ParamCustomStp%s Value/%s: \"\"\n", lang,
1694	       desc->name, _("Value"));
1695      if (!simplified)
1696	{
1697	  gzprintf(fp, "*%s.Translation StpFine%s/%s %s: \"\"\n", lang,
1698		   desc->name, stp_i18n_lookup(po, desc->text),
1699		   _("Fine Adjustment"));
1700	  gzprintf(fp, "*%s.StpFine%s None/%.3f: \"\"\n", lang,
1701		   desc->name, 0.0);
1702	  if (localize_numbers)
1703	    {
1704	      for (i = 0; i < 100; i += 5)
1705		gzprintf(fp, "*%s.StpFine%s %d/%.3f: \"\"\n", lang,
1706			 desc->name, i, ((double) i) * .001);
1707	    }
1708	}
1709      break;
1710
1711    case STP_PARAMETER_TYPE_DIMENSION:
1712      if (!desc->is_mandatory)
1713	gzprintf(fp, "*%s.Stp%s %s/%s: \"\"\n", lang, desc->name,
1714		 "None", _("None"));
1715      /* Unlike the other fields, dimensions are not strictly numbers */
1716      for (i = desc->bounds.dimension.lower;
1717	   i <= desc->bounds.dimension.upper; i++)
1718	{
1719	  snprintf(dimstr, sizeof(dimstr), _("%.1f mm"),
1720		   (double)i * 25.4 / 72.0);
1721	  gzprintf(fp, "*%s.Stp%s %d/%s: \"\"\n", lang,
1722		   desc->name, i, dimstr);
1723	}
1724      gzprintf(fp, "*%s.ParamCustomStp%s Value/%s: \"\"\n", lang,
1725	       desc->name, _("Value"));
1726      break;
1727
1728    case STP_PARAMETER_TYPE_INT:
1729      if (!desc->is_mandatory)
1730	gzprintf(fp, "*%s.Stp%s %s/%s: \"\"\n", lang, desc->name,
1731		 "None", _("None"));
1732      if (localize_numbers)
1733	{
1734	  for (i = desc->bounds.integer.lower;
1735	       i <= desc->bounds.integer.upper; i++)
1736	    {
1737	      gzprintf(fp, "*%s.Stp%s %d/%d: \"\"\n", lang, desc->name, i, i);
1738	    }
1739	}
1740      gzprintf(fp, "*%s.ParamCustomStp%s Value/%s: \"\"\n", lang,
1741	       desc->name, _("Value"));
1742      break;
1743
1744    default:
1745      break;
1746    }
1747}
1748
1749static void
1750print_standard_fonts(gzFile fp)
1751{
1752  gzputs(fp, "\n*DefaultFont: Courier\n");
1753  gzputs(fp, "*Font AvantGarde-Book: Standard \"(001.006S)\" Standard ROM\n");
1754  gzputs(fp, "*Font AvantGarde-BookOblique: Standard \"(001.006S)\" Standard ROM\n");
1755  gzputs(fp, "*Font AvantGarde-Demi: Standard \"(001.007S)\" Standard ROM\n");
1756  gzputs(fp, "*Font AvantGarde-DemiOblique: Standard \"(001.007S)\" Standard ROM\n");
1757  gzputs(fp, "*Font Bookman-Demi: Standard \"(001.004S)\" Standard ROM\n");
1758  gzputs(fp, "*Font Bookman-DemiItalic: Standard \"(001.004S)\" Standard ROM\n");
1759  gzputs(fp, "*Font Bookman-Light: Standard \"(001.004S)\" Standard ROM\n");
1760  gzputs(fp, "*Font Bookman-LightItalic: Standard \"(001.004S)\" Standard ROM\n");
1761  gzputs(fp, "*Font Courier: Standard \"(002.004S)\" Standard ROM\n");
1762  gzputs(fp, "*Font Courier-Bold: Standard \"(002.004S)\" Standard ROM\n");
1763  gzputs(fp, "*Font Courier-BoldOblique: Standard \"(002.004S)\" Standard ROM\n");
1764  gzputs(fp, "*Font Courier-Oblique: Standard \"(002.004S)\" Standard ROM\n");
1765  gzputs(fp, "*Font Helvetica: Standard \"(001.006S)\" Standard ROM\n");
1766  gzputs(fp, "*Font Helvetica-Bold: Standard \"(001.007S)\" Standard ROM\n");
1767  gzputs(fp, "*Font Helvetica-BoldOblique: Standard \"(001.007S)\" Standard ROM\n");
1768  gzputs(fp, "*Font Helvetica-Narrow: Standard \"(001.006S)\" Standard ROM\n");
1769  gzputs(fp, "*Font Helvetica-Narrow-Bold: Standard \"(001.007S)\" Standard ROM\n");
1770  gzputs(fp, "*Font Helvetica-Narrow-BoldOblique: Standard \"(001.007S)\" Standard ROM\n");
1771  gzputs(fp, "*Font Helvetica-Narrow-Oblique: Standard \"(001.006S)\" Standard ROM\n");
1772  gzputs(fp, "*Font Helvetica-Oblique: Standard \"(001.006S)\" Standard ROM\n");
1773  gzputs(fp, "*Font NewCenturySchlbk-Bold: Standard \"(001.009S)\" Standard ROM\n");
1774  gzputs(fp, "*Font NewCenturySchlbk-BoldItalic: Standard \"(001.007S)\" Standard ROM\n");
1775  gzputs(fp, "*Font NewCenturySchlbk-Italic: Standard \"(001.006S)\" Standard ROM\n");
1776  gzputs(fp, "*Font NewCenturySchlbk-Roman: Standard \"(001.007S)\" Standard ROM\n");
1777  gzputs(fp, "*Font Palatino-Bold: Standard \"(001.005S)\" Standard ROM\n");
1778  gzputs(fp, "*Font Palatino-BoldItalic: Standard \"(001.005S)\" Standard ROM\n");
1779  gzputs(fp, "*Font Palatino-Italic: Standard \"(001.005S)\" Standard ROM\n");
1780  gzputs(fp, "*Font Palatino-Roman: Standard \"(001.005S)\" Standard ROM\n");
1781  gzputs(fp, "*Font Symbol: Special \"(001.007S)\" Special ROM\n");
1782  gzputs(fp, "*Font Times-Bold: Standard \"(001.007S)\" Standard ROM\n");
1783  gzputs(fp, "*Font Times-BoldItalic: Standard \"(001.009S)\" Standard ROM\n");
1784  gzputs(fp, "*Font Times-Italic: Standard \"(001.007S)\" Standard ROM\n");
1785  gzputs(fp, "*Font Times-Roman: Standard \"(001.007S)\" Standard ROM\n");
1786  gzputs(fp, "*Font ZapfChancery-MediumItalic: Standard \"(001.007S)\" Standard ROM\n");
1787  gzputs(fp, "*Font ZapfDingbats: Special \"(001.004S)\" Standard ROM\n");
1788}
1789
1790/*
1791 * 'write_ppd()' - Write a PPD file.
1792 */
1793
1794static int				/* O - Exit status */
1795write_ppd(
1796    gzFile              fp,		/* I - File to write to */
1797    const stp_printer_t *p,		/* I - Printer driver */
1798    const char          *language,	/* I - Primary language */
1799    const char		*ppd_location,	/* I - Location of PPD file */
1800    ppd_type_t          ppd_type,	/* I - 1 = simplified options */
1801    const char		*filename)	/* I - input filename */
1802{
1803  int		i, j, k, l;		/* Looping vars */
1804  int		num_opts;		/* Number of printer options */
1805  int		xdpi, ydpi;		/* Resolution info */
1806  stp_vars_t	*v;			/* Variable info */
1807  const char	*driver;		/* Driver name */
1808  const char	*family;		/* Printer family */
1809  int		model;			/* Internal model ID */
1810  const char	*long_name;		/* Driver long name */
1811  const char	*manufacturer;		/* Manufacturer of printer */
1812  const char	*device_id;		/* IEEE1284 device ID */
1813  const stp_vars_t *printvars;		/* Printer option names */
1814  stp_parameter_t desc;
1815  stp_parameter_list_t param_list;
1816  const stp_param_string_t *opt;
1817  int has_quality_parameter = 0;
1818  int printer_is_color = 0;
1819  int simplified = ppd_type == PPD_SIMPLIFIED;
1820  int skip_color = ppd_type == PPD_NO_COLOR_OPTS;
1821  int maximum_level = simplified ?
1822    STP_PARAMETER_LEVEL_BASIC : STP_PARAMETER_LEVEL_ADVANCED4;
1823  char		*default_resolution = NULL;  /* Default resolution mapped name */
1824  stp_string_list_t *resolutions = stp_string_list_create();
1825  char		**all_langs = getlangs();/* All languages */
1826  const stp_string_list_t	*po = stp_i18n_load(language);
1827					/* Message catalog */
1828
1829
1830 /*
1831  * Initialize driver-specific variables...
1832  */
1833
1834  driver     = stp_printer_get_driver(p);
1835  family     = stp_printer_get_family(p);
1836  model      = stp_printer_get_model(p);
1837  long_name  = stp_printer_get_long_name(p);
1838  manufacturer = stp_printer_get_manufacturer(p);
1839  device_id  = stp_printer_get_device_id(p);
1840  printvars  = stp_printer_get_defaults(p);
1841
1842  print_ppd_header(fp, ppd_type, model, driver, family, long_name,
1843		   manufacturer, device_id, ppd_location, language, po,
1844		   all_langs);
1845
1846
1847  /* Set Job Mode to "Job" as this enables the Duplex option */
1848  v = stp_vars_create_copy(printvars);
1849  stp_set_string_parameter(v, "JobMode", "Job");
1850
1851  /* Assume that color printers are inkjets and should have pages reversed */
1852  stp_describe_parameter(v, "PrintingMode", &desc);
1853  if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST)
1854    {
1855      if (stp_string_list_is_present(desc.bounds.str, "Color"))
1856	{
1857	  printer_is_color = 1;
1858	  gzputs(fp, "*ColorDevice:	True\n");
1859	}
1860      else
1861	{
1862	  printer_is_color = 0;
1863	  gzputs(fp, "*ColorDevice:	False\n");
1864	}
1865      if (strcmp(desc.deflt.str, "Color") == 0)
1866	gzputs(fp, "*DefaultColorSpace:	RGB\n");
1867      else
1868	gzputs(fp, "*DefaultColorSpace:	Gray\n");
1869    }
1870  stp_parameter_description_destroy(&desc);
1871
1872  print_ppd_header_3(fp, ppd_type, model, driver, family, long_name,
1873		   manufacturer, device_id, ppd_location, language, po,
1874		   all_langs);
1875
1876  /* Macintosh color management */
1877  gzputs(fp, "*cupsICCProfile Gray../Grayscale:	\"/System/Library/ColorSync/Profiles/sRGB Profile.icc\"\n");
1878  gzputs(fp, "*cupsICCProfile RGB../Color:	\"/System/Library/ColorSync/Profiles/sRGB Profile.icc\"\n");
1879  gzputs(fp, "*cupsICCProfile CMYK../Color:	\"/System/Library/ColorSync/Profiles/Generic CMYK Profile.icc\"\n");
1880  gzputs(fp, "*APSupportsCustomColorMatching: true\n");
1881  gzputs(fp, "*APDefaultCustomColorMatchingProfile: sRGB\n");
1882  gzputs(fp, "*APCustomColorMatchingProfile: sRGB\n");
1883
1884  gzputs(fp, "\n");
1885
1886  print_ppd_header_2(fp, ppd_type, model, driver, family, long_name,
1887		     manufacturer, device_id, ppd_location, language, po,
1888		     all_langs);
1889
1890 /*
1891  * Get the page sizes from the driver...
1892  */
1893
1894  if (printer_is_color)
1895    stp_set_string_parameter(v, "PrintingMode", "Color");
1896  else
1897    stp_set_string_parameter(v, "PrintingMode", "BW");
1898  stp_set_string_parameter(v, "ChannelBitDepth", "8");
1899  print_page_sizes(fp, v, simplified, po);
1900
1901 /*
1902  * Do we support color?
1903  */
1904
1905  print_color_setup(fp, simplified, printer_is_color, po);
1906
1907 /*
1908  * Media types...
1909  */
1910
1911  stp_describe_parameter(v, "MediaType", &desc);
1912  num_opts = stp_string_list_count(desc.bounds.str);
1913
1914  if (num_opts > 0)
1915  {
1916    int is_color_opt =
1917      stp_parameter_has_category_value(v, &desc, "Color", "Yes");
1918    int nocolor = skip_color && is_color_opt;
1919    if (is_color_opt)
1920      gzprintf(fp, "*ColorKeyWords: \"MediaType\"\n");
1921    gzprintf(fp, "*OpenUI *MediaType/%s: PickOne\n", _("Media Type"));
1922    gzputs(fp, "*OPOptionHints MediaType: \"dropdown\"\n");
1923    gzputs(fp, "*OrderDependency: 10 AnySetup *MediaType\n");
1924    gzprintf(fp, "*StpStp%s: %d %d %d %d %d %.3f %.3f %.3f\n",
1925	     desc.name, desc.p_type, desc.is_mandatory,
1926	     desc.p_class, desc.p_level, desc.channel, 0.0, 0.0, 0.0);
1927    gzprintf(fp, "*DefaultMediaType: %s\n", desc.deflt.str);
1928    gzprintf(fp, "*StpDefaultMediaType: %s\n", desc.deflt.str);
1929
1930    for (i = 0; i < num_opts; i ++)
1931    {
1932      opt = stp_string_list_param(desc.bounds.str, i);
1933      gzprintf(fp, "*%sMediaType %s/%s:\t\"<</MediaType(%s)>>setpagedevice\"\n",
1934	       nocolor && strcmp(opt->name, desc.deflt.str) != 0 ? "?" : "",
1935               opt->name, stp_i18n_lookup(po, opt->text), opt->name);
1936    }
1937
1938    gzputs(fp, "*CloseUI: *MediaType\n\n");
1939  }
1940  stp_parameter_description_destroy(&desc);
1941
1942 /*
1943  * Input slots...
1944  */
1945
1946  stp_describe_parameter(v, "InputSlot", &desc);
1947  num_opts = stp_string_list_count(desc.bounds.str);
1948
1949  if (num_opts > 0)
1950  {
1951    int is_color_opt =
1952      stp_parameter_has_category_value(v, &desc, "Color", "Yes");
1953    int nocolor = skip_color && is_color_opt;
1954    if (is_color_opt)
1955      gzprintf(fp, "*ColorKeyWords: \"InputSlot\"\n");
1956    gzprintf(fp, "*OpenUI *InputSlot/%s: PickOne\n", _("Media Source"));
1957    gzputs(fp, "*OPOptionHints InputSlot: \"dropdown\"\n");
1958    gzputs(fp, "*OrderDependency: 10 AnySetup *InputSlot\n");
1959    gzprintf(fp, "*StpStp%s: %d %d %d %d %d %.3f %.3f %.3f\n",
1960	     desc.name, desc.p_type, desc.is_mandatory,
1961	     desc.p_class, desc.p_level, desc.channel, 0.0, 0.0, 0.0);
1962    gzprintf(fp, "*DefaultInputSlot: %s\n", desc.deflt.str);
1963    gzprintf(fp, "*StpDefaultInputSlot: %s\n", desc.deflt.str);
1964
1965    for (i = 0; i < num_opts; i ++)
1966    {
1967      opt = stp_string_list_param(desc.bounds.str, i);
1968      gzprintf(fp, "*%sInputSlot %s/%s:\t\"<</MediaClass(%s)>>setpagedevice\"\n",
1969	       nocolor && strcmp(opt->name, desc.deflt.str) != 0 ? "?" : "",
1970               opt->name, stp_i18n_lookup(po, opt->text), opt->name);
1971    }
1972
1973    gzputs(fp, "*CloseUI: *InputSlot\n\n");
1974  }
1975  stp_parameter_description_destroy(&desc);
1976
1977 /*
1978  * Quality settings
1979  */
1980
1981  stp_describe_parameter(v, "Quality", &desc);
1982  if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST && desc.is_active)
1983    {
1984      int is_color_opt =
1985	stp_parameter_has_category_value(v, &desc, "Color", "Yes");
1986      int nocolor = skip_color && is_color_opt;
1987      if (is_color_opt)
1988	gzprintf(fp, "*ColorKeyWords: \"Quality\"\n");
1989      stp_clear_string_parameter(v, "Resolution");
1990      has_quality_parameter = 1;
1991      num_opts = stp_string_list_count(desc.bounds.str);
1992      gzprintf(fp, "*OpenUI *StpQuality/%s: PickOne\n", stp_i18n_lookup(po, desc.text));
1993      if (num_opts > 3)
1994	gzputs(fp, "*OPOptionHints Quality: \"radiobuttons\"\n");
1995      else
1996	gzputs(fp, "*OPOptionHints Quality: \"dropdown\"\n");
1997      gzputs(fp, "*OrderDependency: 10 AnySetup *StpQuality\n");
1998      gzprintf(fp, "*StpStp%s: %d %d %d %d %d %.3f %.3f %.3f\n",
1999	       desc.name, desc.p_type, desc.is_mandatory,
2000	       desc.p_type, desc.p_level, desc.channel, 0.0, 0.0, 0.0);
2001      gzprintf(fp, "*DefaultStpQuality: %s\n", desc.deflt.str);
2002      gzprintf(fp, "*StpDefaultStpQuality: %s\n", desc.deflt.str);
2003      for (i = 0; i < num_opts; i++)
2004	{
2005	  opt = stp_string_list_param(desc.bounds.str, i);
2006	  stp_set_string_parameter(v, "Quality", opt->name);
2007	  stp_describe_resolution(v, &xdpi, &ydpi);
2008	  if (xdpi == -1 || ydpi == -1)
2009	    {
2010	      stp_parameter_t res_desc;
2011	      stp_clear_string_parameter(v, "Quality");
2012	      stp_describe_parameter(v, "Resolution", &res_desc);
2013	      stp_set_string_parameter(v, "Resolution", res_desc.deflt.str);
2014	      stp_describe_resolution(v, &xdpi, &ydpi);
2015	      stp_clear_string_parameter(v, "Resolution");
2016	      stp_parameter_description_destroy(&res_desc);
2017	    }
2018	  gzprintf(fp, "*%sStpQuality %s/%s:\t\"<</HWResolution[%d %d]/cupsRowFeed %d>>setpagedevice\"\n",
2019		   nocolor && strcmp(opt->name, desc.deflt.str) != 0 ? "?" : "",
2020		   opt->name, stp_i18n_lookup(po, opt->text), xdpi, ydpi, i + 1);
2021	}
2022      gzputs(fp, "*CloseUI: *StpQuality\n\n");
2023    }
2024  stp_parameter_description_destroy(&desc);
2025  stp_clear_string_parameter(v, "Quality");
2026
2027 /*
2028  * Resolutions...
2029  */
2030
2031  stp_describe_parameter(v, "Resolution", &desc);
2032  num_opts = stp_string_list_count(desc.bounds.str);
2033
2034  if (!simplified || desc.p_level == STP_PARAMETER_LEVEL_BASIC)
2035    {
2036      int is_color_opt =
2037	stp_parameter_has_category_value(v, &desc, "Color", "Yes");
2038      int nocolor = skip_color && is_color_opt;
2039      stp_string_list_t *res_list = stp_string_list_create();
2040      char res_name[64];	/* Plenty long enough for XXXxYYYdpi */
2041      int resolution_ok;
2042      int tmp_xdpi, tmp_ydpi;
2043
2044      if (is_color_opt)
2045	gzprintf(fp, "*ColorKeyWords: \"Resolution\"\n");
2046      gzprintf(fp, "*OpenUI *Resolution/%s: PickOne\n", _("Resolution"));
2047      if (num_opts > 3)
2048	gzputs(fp, "*OPOptionHints Resolution: \"resolution radiobuttons\"\n");
2049      else
2050	gzputs(fp, "*OPOptionHints Resolution: \"resolution dropdown\"\n");
2051      gzputs(fp, "*OrderDependency: 10 AnySetup *Resolution\n");
2052      gzprintf(fp, "*StpStp%s: %d %d %d %d %d %.3f %.3f %.3f\n",
2053	       desc.name, desc.p_type, desc.is_mandatory,
2054	       desc.p_class, desc.p_level, desc.channel, 0.0, 0.0, 0.0);
2055      if (has_quality_parameter)
2056	{
2057	  stp_parameter_t desc1;
2058	  stp_clear_string_parameter(v, "Resolution");
2059	  stp_describe_parameter(v, "Quality", &desc1);
2060	  stp_set_string_parameter(v, "Quality", desc1.deflt.str);
2061	  stp_parameter_description_destroy(&desc1);
2062	  stp_describe_resolution(v, &xdpi, &ydpi);
2063	  stp_clear_string_parameter(v, "Quality");
2064	  tmp_xdpi = xdpi;
2065	  while (tmp_xdpi > MAXIMUM_SAFE_PPD_X_RESOLUTION)
2066	    tmp_xdpi /= 2;
2067	  tmp_ydpi = ydpi;
2068	  while (tmp_ydpi > MAXIMUM_SAFE_PPD_Y_RESOLUTION)
2069	    tmp_ydpi /= 2;
2070	  if (tmp_ydpi < tmp_xdpi)
2071	    tmp_xdpi = tmp_ydpi;
2072	  /*
2073	     Make the default resolution look like an almost square resolution
2074	     so that applications using it will be less likely to generate
2075	     excess resolution.  However, make the hardware resolution
2076	     match the printer default.
2077	  */
2078	  (void) snprintf(res_name, 63, "%dx%ddpi", tmp_xdpi + 1, tmp_xdpi);
2079	  default_resolution = stp_strdup(res_name);
2080	  stp_string_list_add_string(res_list, res_name, res_name);
2081	  gzprintf(fp, "*DefaultResolution: %s\n", res_name);
2082	  gzprintf(fp, "*StpDefaultResolution: %s\n", res_name);
2083	  gzprintf(fp, "*Resolution %s/%s:\t\"<</HWResolution[%d %d]>>setpagedevice\"\n",
2084		   res_name, _("Automatic"), xdpi, ydpi);
2085	  gzprintf(fp, "*StpResolutionMap: %s %s\n", res_name, "None");
2086	}
2087      else
2088      {
2089	stp_set_string_parameter(v, "Resolution", desc.deflt.str);
2090	stp_describe_resolution(v, &xdpi, &ydpi);
2091
2092	if (xdpi == ydpi)
2093	  (void) snprintf(res_name, 63, "%ddpi", xdpi);
2094	else
2095	  (void) snprintf(res_name, 63, "%dx%ddpi", xdpi, ydpi);
2096	gzprintf(fp, "*DefaultResolution: %s\n", res_name);
2097	gzprintf(fp, "*StpDefaultResolution: %s\n", res_name);
2098	/*
2099	 * We need to add this to the resolution list here so that
2100	 * some non-default resolution won't wind up with the
2101	 * default resolution name
2102	 */
2103	stp_string_list_add_string(res_list, res_name, res_name);
2104      }
2105
2106      stp_clear_string_parameter(v, "Quality");
2107      for (i = 0; i < num_opts; i ++)
2108	{
2109	  /*
2110	   * Strip resolution name to its essentials...
2111	   */
2112	  opt = stp_string_list_param(desc.bounds.str, i);
2113	  stp_set_string_parameter(v, "Resolution", opt->name);
2114	  stp_describe_resolution(v, &xdpi, &ydpi);
2115
2116	  /* This should only happen with a "None" resolution */
2117	  if (xdpi == -1 || ydpi == -1)
2118	    continue;
2119
2120	  resolution_ok = 0;
2121	  tmp_xdpi = xdpi;
2122	  while (tmp_xdpi > MAXIMUM_SAFE_PPD_X_RESOLUTION)
2123	    tmp_xdpi /= 2;
2124	  tmp_ydpi = ydpi;
2125	  while (tmp_ydpi > MAXIMUM_SAFE_PPD_Y_RESOLUTION)
2126	    tmp_ydpi /= 2;
2127	  do
2128	    {
2129	      if (tmp_xdpi == tmp_ydpi)
2130		(void) snprintf(res_name, 63, "%ddpi", tmp_xdpi);
2131	      else
2132		(void) snprintf(res_name, 63, "%dx%ddpi", tmp_xdpi, tmp_ydpi);
2133	      if ((!has_quality_parameter &&
2134		   strcmp(opt->name, desc.deflt.str) == 0) ||
2135		  !stp_string_list_is_present(res_list, res_name))
2136		{
2137		  resolution_ok = 1;
2138		  stp_string_list_add_string(res_list, res_name, opt->text);
2139		}
2140	      else if (tmp_ydpi > tmp_xdpi &&
2141		       tmp_ydpi < MAXIMUM_SAFE_PPD_Y_RESOLUTION)
2142		/* Note that we're incrementing the *higher* resolution.
2143		   This will generate less aliasing, and apps that convert
2144		   down to a square resolution will do the right thing. */
2145		tmp_ydpi++;
2146	      else if (tmp_xdpi < MAXIMUM_SAFE_PPD_X_RESOLUTION)
2147		tmp_xdpi++;
2148	      else
2149		tmp_xdpi /= 2;
2150	    } while (!resolution_ok);
2151	  stp_string_list_add_string(resolutions, res_name, opt->text);
2152	  gzprintf(fp, "*%sResolution %s/%s:\t\"<</HWResolution[%d %d]/cupsCompression %d>>setpagedevice\"\n",
2153		   nocolor && strcmp(opt->name, desc.deflt.str) != 0 ? "?" : "",
2154		   res_name, stp_i18n_lookup(po, opt->text), xdpi, ydpi, i + 1);
2155	  if (strcmp(res_name, opt->name) != 0)
2156	    gzprintf(fp, "*StpResolutionMap: %s %s\n", res_name, opt->name);
2157	}
2158
2159      stp_string_list_destroy(res_list);
2160      gzputs(fp, "*CloseUI: *Resolution\n\n");
2161    }
2162
2163  stp_parameter_description_destroy(&desc);
2164
2165  stp_describe_parameter(v, "OutputOrder", &desc);
2166  if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST)
2167    {
2168      gzprintf(fp, "*OpenUI *OutputOrder/%s: PickOne\n", _("Output Order"));
2169      gzputs(fp, "*OPOptionHints OutputOrder: \"radiobuttons\"\n");
2170      gzputs(fp, "*OrderDependency: 10 AnySetup *OutputOrder\n");
2171      gzprintf(fp, "*DefaultOutputOrder: %s\n", desc.deflt.str);
2172      gzprintf(fp, "*StpDefaultOutputOrder: %s\n", desc.deflt.str);
2173      gzprintf(fp, "*OutputOrder Normal/%s: \"\"\n", _("Normal"));
2174      gzprintf(fp, "*OutputOrder Reverse/%s: \"\"\n", _("Reverse"));
2175      gzputs(fp, "*CloseUI: *OutputOrder\n\n");
2176    }
2177  stp_parameter_description_destroy(&desc);
2178
2179 /*
2180  * Duplex
2181  * Note that the opt->name strings MUST match those in the printer driver(s)
2182  * else the PPD files will not be generated correctly
2183  */
2184
2185  stp_describe_parameter(v, "Duplex", &desc);
2186  if (desc.is_active && desc.p_type == STP_PARAMETER_TYPE_STRING_LIST)
2187    {
2188      num_opts = stp_string_list_count(desc.bounds.str);
2189      if (num_opts > 0)
2190      {
2191	int is_color_opt =
2192	  stp_parameter_has_category_value(v, &desc, "Color", "Yes");
2193	if (is_color_opt)
2194	  gzprintf(fp, "*ColorKeyWords: \"InputSlot\"\n");
2195        gzprintf(fp, "*OpenUI *Duplex/%s: PickOne\n", _("2-Sided Printing"));
2196	gzputs(fp, "*OPOptionHints Duplex: \"radiobuttons\"\n");
2197        gzputs(fp, "*OrderDependency: 10 AnySetup *Duplex\n");
2198	gzprintf(fp, "*StpStp%s: %d %d %d %d %d %.3f %.3f %.3f\n",
2199		 desc.name, desc.p_type, desc.is_mandatory,
2200		 desc.p_class, desc.p_level, desc.channel, 0.0, 0.0, 0.0);
2201        gzprintf(fp, "*DefaultDuplex: %s\n", desc.deflt.str);
2202        gzprintf(fp, "*StpDefaultDuplex: %s\n", desc.deflt.str);
2203
2204        for (i = 0; i < num_opts; i++)
2205          {
2206            opt = stp_string_list_param(desc.bounds.str, i);
2207            if (strcmp(opt->name, "None") == 0)
2208              gzprintf(fp, "*Duplex %s/%s: \"<</Duplex false>>setpagedevice\"\n", opt->name, stp_i18n_lookup(po, opt->text));
2209            else if (strcmp(opt->name, "DuplexNoTumble") == 0)
2210              gzprintf(fp, "*Duplex %s/%s: \"<</Duplex true/Tumble false>>setpagedevice\"\n", opt->name, stp_i18n_lookup(po, opt->text));
2211            else if (strcmp(opt->name, "DuplexTumble") == 0)
2212              gzprintf(fp, "*Duplex %s/%s: \"<</Duplex true/Tumble true>>setpagedevice\"\n", opt->name, stp_i18n_lookup(po, opt->text));
2213           }
2214        gzputs(fp, "*CloseUI: *Duplex\n\n");
2215      }
2216    }
2217  stp_parameter_description_destroy(&desc);
2218
2219  gzprintf(fp, "*OpenUI *StpiShrinkOutput/%s: PickOne\n",
2220	   _("Shrink Page If Necessary to Fit Borders"));
2221  gzputs(fp, "*OPOptionHints StpiShrinkOutput: \"radiobuttons\"\n");
2222  gzputs(fp, "*OrderDependency: 10 AnySetup *StpiShrinkOutput\n");
2223  gzputs(fp, "*DefaultStpiShrinkOutput: Shrink\n");
2224  gzputs(fp, "*StpDefaultStpiShrinkOutput: Shrink\n");
2225  gzprintf(fp, "*StpiShrinkOutput %s/%s: \"\"\n", "Shrink", _("Shrink (print the whole page)"));
2226  gzprintf(fp, "*StpiShrinkOutput %s/%s: \"\"\n", "Crop", _("Crop (preserve dimensions)"));
2227  gzprintf(fp, "*StpiShrinkOutput %s/%s: \"\"\n", "Expand", _("Expand (use maximum page area)"));
2228  gzputs(fp, "*CloseUI: *StpiShrinkOutput\n\n");
2229
2230  param_list = stp_get_parameter_list(v);
2231
2232  for (j = 0; j <= STP_PARAMETER_CLASS_OUTPUT; j++)
2233    {
2234      for (k = 0; k <= maximum_level; k++)
2235	{
2236	  int printed_open_group = 0;
2237	  size_t param_count = stp_parameter_list_count(param_list);
2238	  for (l = 0; l < param_count; l++)
2239	    {
2240	      const stp_parameter_t *lparam =
2241		stp_parameter_list_param(param_list, l);
2242	      if (lparam->p_class != j || lparam->p_level != k ||
2243		  is_special_option(lparam->name) || lparam->read_only ||
2244		  (lparam->p_type != STP_PARAMETER_TYPE_STRING_LIST &&
2245		   lparam->p_type != STP_PARAMETER_TYPE_BOOLEAN &&
2246		   lparam->p_type != STP_PARAMETER_TYPE_DIMENSION &&
2247		   lparam->p_type != STP_PARAMETER_TYPE_INT &&
2248		   lparam->p_type != STP_PARAMETER_TYPE_DOUBLE))
2249		  continue;
2250	      stp_describe_parameter(v, lparam->name, &desc);
2251	      if (desc.is_active)
2252		{
2253		  if (!printed_open_group)
2254		    {
2255		      print_group_open(fp, j, k, language, po);
2256		      printed_open_group = 1;
2257		    }
2258		  print_one_option(fp, v, po, ppd_type, lparam, &desc);
2259		}
2260	      stp_parameter_description_destroy(&desc);
2261	    }
2262	  if (printed_open_group)
2263	    print_group_close(fp, j, k, language, po);
2264	}
2265    }
2266  stp_parameter_list_destroy(param_list);
2267  stp_describe_parameter(v, "ImageType", &desc);
2268  if (desc.is_active && desc.p_type == STP_PARAMETER_TYPE_STRING_LIST)
2269    {
2270      num_opts = stp_string_list_count(desc.bounds.str);
2271      if (num_opts > 0)
2272	{
2273	  for (i = 0; i < num_opts; i++)
2274	    {
2275	      opt = stp_string_list_param(desc.bounds.str, i);
2276	      if (strcmp(opt->name, "None") != 0)
2277		gzprintf(fp, "*APPrinterPreset %s/%s: \"*StpImageType %s\"\n",
2278			 opt->name, stp_i18n_lookup(po, opt->text), opt->name);
2279	    }
2280	  gzputs(fp, "\n");
2281	}
2282    }
2283  stp_parameter_description_destroy(&desc);
2284
2285  if (!language)
2286    {
2287      /*
2288       * Generate globalized PPDs when POSIX language is requested...
2289       */
2290
2291      const char *lang;
2292      const stp_string_list_t *savepo = po;
2293      int langnum;
2294
2295      for (langnum = 0; all_langs[langnum]; langnum ++)
2296	{
2297	  lang = all_langs[langnum];
2298
2299	  if (!strcmp(lang, "C") || !strcmp(lang, "en"))
2300	    continue;
2301
2302	  if ((po = stp_i18n_load(lang)) == NULL)
2303	    continue;
2304
2305	  /*
2306	   * Get the page sizes from the driver...
2307	   */
2308
2309	  if (printer_is_color)
2310	    stp_set_string_parameter(v, "PrintingMode", "Color");
2311	  else
2312	    stp_set_string_parameter(v, "PrintingMode", "BW");
2313	  stp_set_string_parameter(v, "ChannelBitDepth", "8");
2314	  stp_describe_parameter(v, "PageSize", &desc);
2315	  num_opts = stp_string_list_count(desc.bounds.str);
2316
2317	  gzprintf(fp, "*%s.Translation PageSize/%s: \"\"\n", lang, _("Media Size"));
2318	  gzprintf(fp, "*%s.Translation PageRegion/%s: \"\"\n", lang, _("Media Size"));
2319
2320	  for (i = 0; i < num_opts; i++)
2321	    {
2322	      const stp_papersize_t *papersize;
2323	      opt = stp_string_list_param(desc.bounds.str, i);
2324	      papersize = stp_get_papersize_by_name(opt->name);
2325
2326	      if (!papersize)
2327		continue;
2328
2329	      /*
2330		if (strcmp(opt->name, "Custom") == 0)
2331		continue;
2332	      */
2333
2334	      if (simplified && num_opts >= 10 &&
2335		  (papersize->paper_unit == PAPERSIZE_ENGLISH_EXTENDED ||
2336		   papersize->paper_unit == PAPERSIZE_METRIC_EXTENDED))
2337		continue;
2338
2339	      if ((papersize->width <= 0 || papersize->height <= 0) &&
2340		  strcmp(opt->name, "Custom") != 0)
2341		continue;
2342
2343	      gzprintf(fp, "*%s.PageSize %s/%s: \"\"\n", lang, opt->name, stp_i18n_lookup(po, opt->text));
2344	      gzprintf(fp, "*%s.PageRegion %s/%s: \"\"\n", lang, opt->name, stp_i18n_lookup(po, opt->text));
2345	    }
2346
2347	  stp_parameter_description_destroy(&desc);
2348
2349	  /*
2350	   * Do we support color?
2351	   */
2352
2353	  gzprintf(fp, "*%s.Translation ColorModel/%s: \"\"\n", lang, _("Color Model"));
2354	  gzprintf(fp, "*%s.ColorModel Gray/%s: \"\"\n", lang, _("Grayscale"));
2355	  gzprintf(fp, "*%s.ColorModel Black/%s: \"\"\n", lang, _("Inverted Grayscale"));
2356
2357	  if (printer_is_color)
2358	    {
2359	      gzprintf(fp, "*%s.ColorModel RGB/%s: \"\"\n", lang, _("RGB Color"));
2360	      gzprintf(fp, "*%s.ColorModel CMY/%s: \"\"\n", lang, _("CMY Color"));
2361	      gzprintf(fp, "*%s.ColorModel CMYK/%s: \"\"\n", lang, _("CMYK"));
2362	      gzprintf(fp, "*%s.ColorModel KCMY/%s: \"\"\n", lang, _("KCMY"));
2363	    }
2364
2365	  if (!simplified)
2366	    {
2367	      /*
2368	       * 8 or 16 bit color (16 bit is slower)
2369	       */
2370	      gzprintf(fp, "*%s.Translation StpColorPrecision/%s: \"\"\n", lang, _("Color Precision"));
2371	      gzprintf(fp, "*%s.StpColorPrecision Normal/%s: \"\"\n", lang, _("Normal"));
2372	      gzprintf(fp, "*%s.StpColorPrecision Best/%s: \"\"\n", lang, _("Best"));
2373	    }
2374
2375	  /*
2376	   * Media types...
2377	   */
2378
2379	  stp_describe_parameter(v, "MediaType", &desc);
2380	  num_opts = stp_string_list_count(desc.bounds.str);
2381
2382	  if (num_opts > 0)
2383	    {
2384	      gzprintf(fp, "*%s.Translation MediaType/%s: \"\"\n", lang, _("Media Type"));
2385
2386	      for (i = 0; i < num_opts; i ++)
2387		{
2388		  opt = stp_string_list_param(desc.bounds.str, i);
2389		  gzprintf(fp, "*%s.MediaType %s/%s: \"\"\n", lang, opt->name, stp_i18n_lookup(po, opt->text));
2390		}
2391	    }
2392	  stp_parameter_description_destroy(&desc);
2393
2394	  /*
2395	   * Input slots...
2396	   */
2397
2398	  stp_describe_parameter(v, "InputSlot", &desc);
2399	  num_opts = stp_string_list_count(desc.bounds.str);
2400
2401	  if (num_opts > 0)
2402	    {
2403	      gzprintf(fp, "*%s.Translation InputSlot/%s: \"\"\n", lang, _("Media Source"));
2404
2405	      for (i = 0; i < num_opts; i ++)
2406		{
2407		  opt = stp_string_list_param(desc.bounds.str, i);
2408		  gzprintf(fp, "*%s.InputSlot %s/%s: \"\"\n", lang, opt->name, stp_i18n_lookup(po, opt->text));
2409		}
2410	    }
2411	  stp_parameter_description_destroy(&desc);
2412
2413	  /*
2414	   * Quality settings
2415	   */
2416
2417	  stp_describe_parameter(v, "Quality", &desc);
2418	  if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST && desc.is_active)
2419	    {
2420	      gzprintf(fp, "*%s.Translation StpQuality/%s: \"\"\n", lang, stp_i18n_lookup(po, desc.text));
2421	      num_opts = stp_string_list_count(desc.bounds.str);
2422	      for (i = 0; i < num_opts; i++)
2423		{
2424		  opt = stp_string_list_param(desc.bounds.str, i);
2425		  gzprintf(fp, "*%s.StpQuality %s/%s: \"\"\n", lang, opt->name, stp_i18n_lookup(po, opt->text));
2426		}
2427	    }
2428	  stp_parameter_description_destroy(&desc);
2429
2430	  /*
2431	   * Resolution
2432	   */
2433
2434	  stp_describe_parameter(v, "Resolution", &desc);
2435	  num_opts = stp_string_list_count(resolutions);
2436
2437	  if (!simplified || desc.p_level == STP_PARAMETER_LEVEL_BASIC)
2438	    {
2439	      gzprintf(fp, "*%s.Translation Resolution/%s: \"\"\n", lang, _("Resolution"));
2440	      if (has_quality_parameter)
2441		gzprintf(fp, "*%s.Resolution %s/%s: \"\"\n", lang,
2442			 default_resolution, _("Automatic"));
2443
2444	      for (i = 0; i < num_opts; i ++)
2445		{
2446		  opt = stp_string_list_param(resolutions, i);
2447		  gzprintf(fp, "*%s.Resolution %s/%s: \"\"\n", lang,
2448			   opt->name, stp_i18n_lookup(po, opt->text));
2449		}
2450	    }
2451
2452	  stp_parameter_description_destroy(&desc);
2453
2454	  /*
2455	   * OutputOrder
2456	   */
2457
2458	  stp_describe_parameter(v, "OutputOrder", &desc);
2459	  if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST)
2460	    {
2461	      gzprintf(fp, "*%s.Translation OutputOrder/%s: \"\"\n", lang, _("Output Order"));
2462	      gzprintf(fp, "*%s.OutputOrder Normal/%s: \"\"\n", lang, _("Normal"));
2463	      gzprintf(fp, "*%s.OutputOrder Reverse/%s: \"\"\n", lang, _("Reverse"));
2464	    }
2465	  stp_parameter_description_destroy(&desc);
2466
2467	  /*
2468	   * Duplex
2469	   * Note that the opt->name strings MUST match those in the printer driver(s)
2470	   * else the PPD files will not be generated correctly
2471	   */
2472
2473	  stp_describe_parameter(v, "Duplex", &desc);
2474	  if (desc.is_active && desc.p_type == STP_PARAMETER_TYPE_STRING_LIST)
2475	    {
2476	      num_opts = stp_string_list_count(desc.bounds.str);
2477	      if (num_opts > 0)
2478		{
2479		  gzprintf(fp, "*%s.Translation Duplex/%s: \"\"\n", lang, _("2-Sided Printing"));
2480
2481		  for (i = 0; i < num_opts; i++)
2482		    {
2483		      opt = stp_string_list_param(desc.bounds.str, i);
2484		      if (strcmp(opt->name, "None") == 0)
2485			gzprintf(fp, "*%s.Duplex %s/%s: \"\"\n", lang, opt->name, stp_i18n_lookup(po, opt->text));
2486		      else if (strcmp(opt->name, "DuplexNoTumble") == 0)
2487			gzprintf(fp, "*%s.Duplex %s/%s: \"\"\n", lang, opt->name, stp_i18n_lookup(po, opt->text));
2488		      else if (strcmp(opt->name, "DuplexTumble") == 0)
2489			gzprintf(fp, "*%s.Duplex %s/%s: \"\"\n", lang, opt->name, stp_i18n_lookup(po, opt->text));
2490		    }
2491		}
2492	    }
2493	  stp_parameter_description_destroy(&desc);
2494
2495	  gzprintf(fp, "*%s.Translation StpiShrinkOutput/%s: \"\"\n", lang,
2496		   _("Shrink Page If Necessary to Fit Borders"));
2497	  gzprintf(fp, "*%s.StpiShrinkOutput %s/%s: \"\"\n", lang, "Shrink", _("Shrink (print the whole page)"));
2498	  gzprintf(fp, "*%s.StpiShrinkOutput %s/%s: \"\"\n", lang, "Crop", _("Crop (preserve dimensions)"));
2499	  gzprintf(fp, "*%s.StpiShrinkOutput %s/%s: \"\"\n", lang, "Expand", _("Expand (use maximum page area)"));
2500
2501	  param_list = stp_get_parameter_list(v);
2502
2503	  for (j = 0; j <= STP_PARAMETER_CLASS_OUTPUT; j++)
2504	    {
2505	      for (k = 0; k <= maximum_level; k++)
2506		{
2507		  size_t param_count = stp_parameter_list_count(param_list);
2508		  for (l = 0; l < param_count; l++)
2509		    {
2510		      const stp_parameter_t *lparam =
2511			stp_parameter_list_param(param_list, l);
2512		      if (lparam->p_class != j || lparam->p_level != k ||
2513			  is_special_option(lparam->name) || lparam->read_only ||
2514			  (lparam->p_type != STP_PARAMETER_TYPE_STRING_LIST &&
2515			   lparam->p_type != STP_PARAMETER_TYPE_BOOLEAN &&
2516			   lparam->p_type != STP_PARAMETER_TYPE_DIMENSION &&
2517			   lparam->p_type != STP_PARAMETER_TYPE_INT &&
2518			   lparam->p_type != STP_PARAMETER_TYPE_DOUBLE))
2519			continue;
2520		      stp_describe_parameter(v, lparam->name, &desc);
2521		      if (desc.is_active)
2522			print_one_localization(fp, po, simplified, lang,
2523					       lparam, &desc);
2524		      stp_parameter_description_destroy(&desc);
2525		    }
2526		}
2527	    }
2528	  stp_parameter_list_destroy(param_list);
2529	  stp_describe_parameter(v, "ImageType", &desc);
2530	  if (desc.is_active && desc.p_type == STP_PARAMETER_TYPE_STRING_LIST)
2531	    {
2532	      num_opts = stp_string_list_count(desc.bounds.str);
2533	      if (num_opts > 0)
2534		{
2535		  for (i = 0; i < num_opts; i++)
2536		    {
2537		      opt = stp_string_list_param(desc.bounds.str, i);
2538		      if (strcmp(opt->name, "None") != 0)
2539			gzprintf(fp, "*%s.APPrinterPreset %s/%s: \"*StpImageType %s\"\n",
2540				 lang, opt->name, opt->text, opt->name);
2541		    }
2542		}
2543	    }
2544	  stp_parameter_description_destroy(&desc);
2545	}
2546      po = savepo;
2547    }
2548  if (has_quality_parameter)
2549    stp_free(default_resolution);
2550  stp_string_list_destroy(resolutions);
2551
2552 /*
2553  * Fonts...
2554  */
2555
2556  print_standard_fonts(fp);
2557  gzprintf(fp, "\n*%% End of %s\n", filename);
2558
2559  stp_vars_destroy(v);
2560
2561  return (0);
2562}
2563
2564
2565/*
2566 * End of "$Id: genppd.c,v 1.186 2011/03/13 19:28:50 rlk Exp $".
2567 */
2568