• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // PPD file compiler definitions for the CUPS PPD Compiler.
3 //
4 // Copyright © 2020-2024 by OpenPrinting.
5 // Copyright © 2007-2019 by Apple Inc.
6 // Copyright © 2002-2006 by Easy Software Products.
7 //
8 // Licensed under Apache License v2.0.  See the file "LICENSE" for more
9 // information.
10 //
11 
12 //
13 // Include necessary headers...
14 //
15 
16 #include "ppdc-private.h"
17 
18 
19 //
20 // 'ppdcDriver::ppdcDriver()' - Create a new printer driver.
21 //
22 
ppdcDriver(ppdcDriver * d)23 ppdcDriver::ppdcDriver(ppdcDriver *d)	// I - Printer driver template
24   : ppdcShared()
25 {
26   ppdcGroup	*g;			// Current group
27 
28 
29   PPDC_NEW;
30 
31   if (d)
32   {
33     // Bump the use count of any strings we inherit...
34     if (d->manufacturer)
35       d->manufacturer->retain();
36     if (d->version)
37       d->version->retain();
38     if (d->default_font)
39       d->default_font->retain();
40     if (d->default_size)
41       d->default_size->retain();
42     if (d->custom_size_code)
43       d->custom_size_code->retain();
44 
45     // Copy all of the data from the driver template...
46     copyright           = new ppdcArray(d->copyright);
47     manufacturer        = d->manufacturer;
48     model_name          = 0;
49     file_name           = 0;
50     pc_file_name        = 0;
51     type                = d->type;
52     version             = d->version;
53     model_number        = d->model_number;
54     manual_copies       = d->manual_copies;
55     color_device        = d->color_device;
56     throughput          = d->throughput;
57     attrs               = new ppdcArray(d->attrs);
58     constraints         = new ppdcArray(d->constraints);
59     filters             = new ppdcArray(d->filters);
60     fonts               = new ppdcArray(d->fonts);
61     profiles            = new ppdcArray(d->profiles);
62     sizes               = new ppdcArray(d->sizes);
63     default_font        = d->default_font;
64     default_size        = d->default_size;
65     variable_paper_size = d->variable_paper_size;
66     custom_size_code    = d->custom_size_code;
67     left_margin         = d->left_margin;
68     bottom_margin       = d->bottom_margin;
69     right_margin        = d->right_margin;
70     top_margin          = d->top_margin;
71     max_width           = d->max_width;
72     max_length          = d->max_length;
73     min_width           = d->min_width;
74     min_length          = d->min_length;
75 
76     // Then copy the groups manually, since we want separate copies
77     // of the groups and options...
78     groups = new ppdcArray();
79 
80     for (g = (ppdcGroup *)d->groups->first(); g; g = (ppdcGroup *)d->groups->next())
81       groups->add(new ppdcGroup(g));
82   }
83   else
84   {
85     // Zero all of the data in the driver...
86     copyright           = new ppdcArray();
87     manufacturer        = 0;
88     model_name          = 0;
89     file_name           = 0;
90     pc_file_name        = 0;
91     version             = 0;
92     type                = PPDC_DRIVER_CUSTOM;
93     model_number        = 0;
94     manual_copies       = 0;
95     color_device        = 0;
96     throughput          = 1;
97     attrs               = new ppdcArray();
98     constraints         = new ppdcArray();
99     fonts               = new ppdcArray();
100     filters             = new ppdcArray();
101     groups              = new ppdcArray();
102     profiles            = new ppdcArray();
103     sizes               = new ppdcArray();
104     default_font        = 0;
105     default_size        = 0;
106     variable_paper_size = 0;
107     custom_size_code    = 0;
108     left_margin         = 0;
109     bottom_margin       = 0;
110     right_margin        = 0;
111     top_margin          = 0;
112     max_width           = 0;
113     max_length          = 0;
114     min_width           = 0;
115     min_length          = 0;
116   }
117 }
118 
119 
120 //
121 // 'ppdcDriver::~ppdcDriver()' - Destroy a printer driver.
122 //
123 
~ppdcDriver()124 ppdcDriver::~ppdcDriver()
125 {
126   PPDC_DELETE;
127 
128   copyright->release();
129 
130   if (manufacturer)
131     manufacturer->release();
132   if (model_name)
133     model_name->release();
134   if (file_name)
135     file_name->release();
136   if (pc_file_name)
137     pc_file_name->release();
138   if (version)
139     version->release();
140   if (default_font)
141     default_font->release();
142   if (default_size)
143     default_size->release();
144   if (custom_size_code)
145     custom_size_code->release();
146 
147   attrs->release();
148   constraints->release();
149   filters->release();
150   fonts->release();
151   groups->release();
152   profiles->release();
153   sizes->release();
154 }
155 
156 
157 //
158 // 'ppdcDriver::find_attr()' - Find an attribute.
159 //
160 
161 ppdcAttr *				// O - Attribute or NULL
find_attr(const char * k,const char * s)162 ppdcDriver::find_attr(const char *k,	// I - Keyword string
163                       const char *s)	// I - Spec string
164 {
165   ppdcAttr	*a;			// Current attribute
166 
167 
168   for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next())
169     if (!strcmp(a->name->value, k) &&
170         ((!s && (!a->selector->value || !a->selector->value[0])) ||
171 	 (s && a->selector->value && !strcmp(a->selector->value, s))))
172       return (a);
173 
174   return (NULL);
175 }
176 
177 
178 //
179 // 'ppdcDriver::find_group()' - Find a group.
180 //
181 
182 ppdcGroup *				// O - Matching group or NULL
find_group(const char * n)183 ppdcDriver::find_group(const char *n)	// I - Group name
184 {
185   ppdcGroup	*g;			// Current group
186 
187 
188   for (g = (ppdcGroup *)groups->first(); g; g = (ppdcGroup *)groups->next())
189     if (!_cups_strcasecmp(n, g->name->value))
190       return (g);
191 
192   return (0);
193 }
194 
195 
196 //
197 // 'ppdcDriver::find_option()' - Find an option.
198 //
199 
200 ppdcOption *				// O - Matching option or NULL
find_option(const char * n)201 ppdcDriver::find_option(const char *n)	// I - Option name
202 {
203   return (find_option_group(n, (ppdcGroup **)0));
204 }
205 
206 
207 //
208 // 'ppdcDriver::find_option_group()' - Find an option and its group.
209 //
210 
211 ppdcOption *				// O - Matching option or NULL
find_option_group(const char * n,ppdcGroup ** mg)212 ppdcDriver::find_option_group(
213     const char *n,			// I - Option name
214     ppdcGroup  **mg)			// O - Matching group or NULL
215 {
216   ppdcGroup	*g;			// Current group
217   ppdcOption	*o;			// Current option
218 
219 
220   for (g = (ppdcGroup *)groups->first(); g; g = (ppdcGroup *)groups->next())
221     for (o = (ppdcOption *)g->options->first(); o; o = (ppdcOption *)g->options->next())
222       if (!_cups_strcasecmp(n, o->name->value))
223       {
224         if (mg)
225 	  *mg = g;
226 
227         return (o);
228       }
229 
230   if (mg)
231     *mg = (ppdcGroup *)0;
232 
233   return (0);
234 }
235 
236 
237 //
238 // 'ppdcDriver::set_custom_size_code()' - Set the custom page size code.
239 //
240 
241 void
set_custom_size_code(const char * c)242 ppdcDriver::set_custom_size_code(
243     const char *c)			// I - CustomPageSize code
244 {
245   if (custom_size_code)
246     custom_size_code->release();
247 
248   custom_size_code = new ppdcString(c);
249 }
250 
251 
252 //
253 // 'ppdcDriver::set_default_font()' - Set the default font name.
254 //
255 
256 void
set_default_font(ppdcFont * f)257 ppdcDriver::set_default_font(
258     ppdcFont *f)			// I - Font
259 {
260   if (default_font)
261     default_font->release();
262 
263   if (f)
264   {
265     f->name->retain();
266     default_font = f->name;
267   }
268   else
269     default_font = 0;
270 }
271 
272 
273 //
274 // 'ppdcDriver::set_default_size()' - Set the default size name.
275 //
276 
277 void
set_default_size(ppdcMediaSize * m)278 ppdcDriver::set_default_size(
279     ppdcMediaSize *m)			// I - Media size
280 {
281   if (default_size)
282     default_size->release();
283 
284   if (m)
285   {
286     m->name->retain();
287     default_size = m->name;
288   }
289   else
290     default_size = 0;
291 }
292 
293 
294 //
295 // 'ppdcDriver::set_file_name()' - Set the full filename.
296 //
297 
298 void
set_file_name(const char * f)299 ppdcDriver::set_file_name(const char *f)// I - Filename
300 {
301   if (file_name)
302     file_name->release();
303 
304   file_name = new ppdcString(f);
305 }
306 
307 
308 //
309 // 'ppdcDriver::set_manufacturer()' - Set the manufacturer name.
310 //
311 
312 void
set_manufacturer(const char * m)313 ppdcDriver::set_manufacturer(
314     const char *m)			// I - Model name
315 {
316   if (manufacturer)
317     manufacturer->release();
318 
319   manufacturer = new ppdcString(m);
320 }
321 
322 
323 //
324 // 'ppdcDriver::set_model_name()' - Set the model name.
325 //
326 
327 void
set_model_name(const char * m)328 ppdcDriver::set_model_name(
329     const char *m)			// I - Model name
330 {
331   if (model_name)
332     model_name->release();
333 
334   model_name = new ppdcString(m);
335 }
336 
337 
338 //
339 // 'ppdcDriver::set_pc_file_name()' - Set the PC filename.
340 //
341 
342 void
set_pc_file_name(const char * f)343 ppdcDriver::set_pc_file_name(
344     const char *f)			// I - Filename
345 {
346   if (pc_file_name)
347     pc_file_name->release();
348 
349   pc_file_name = new ppdcString(f);
350 }
351 
352 
353 //
354 // 'ppdcDriver::set_version()' - Set the version string.
355 //
356 
357 void
set_version(const char * v)358 ppdcDriver::set_version(const char *v)	// I - Version
359 {
360   if (version)
361     version->release();
362 
363   version = new ppdcString(v);
364 }
365 
366 
367 //
368 // 'ppdcDriver::write_ppd_file()' - Write a PPD file...
369 //
370 
371 int					// O - 0 on success, -1 on failure
write_ppd_file(cups_file_t * fp,ppdcCatalog * catalog,ppdcArray * locales,ppdcSource * src,ppdcLineEnding le)372 ppdcDriver::write_ppd_file(
373     cups_file_t    *fp,			// I - PPD file
374     ppdcCatalog    *catalog,		// I - Message catalog
375     ppdcArray      *locales,		// I - Additional languages to add
376     ppdcSource     *src,		// I - Driver source
377     ppdcLineEnding le)			// I - Line endings to use
378 {
379   bool			delete_cat;	// Delete the catalog when we are done?
380   char			query[42],	// Query attribute
381 			custom[42];	// Custom attribute
382   ppdcString		*s;		// Copyright string
383   ppdcGroup		*g;		// Current group
384   ppdcOption		*o;		// Current option
385   ppdcChoice		*c;		// Current choice
386   ppdcMediaSize		*m;		// Current media size
387   ppdcProfile		*p;		// Current color profile
388   ppdcFilter		*f;		// Current filter
389   ppdcFont		*fn,		// Current font
390 			*bfn;		// Current base font
391   ppdcConstraint	*cn;		// Current constraint
392   ppdcAttr		*a;		// Current attribute
393   const char		*lf;		// Linefeed character to use
394 
395 
396   // If we don't have a message catalog, use an empty (English) one...
397   if (!catalog)
398   {
399     catalog    = new ppdcCatalog(NULL);
400     delete_cat = true;
401   }
402   else
403     delete_cat = false;
404 
405   // Figure out the end-of-line string...
406   if (le == PPDC_LFONLY)
407     lf = "\n";
408   else if (le == PPDC_CRONLY)
409     lf = "\r";
410   else
411     lf = "\r\n";
412 
413   // Write the standard header stuff...
414   cupsFilePrintf(fp, "*PPD-Adobe: \"4.3\"%s", lf);
415   cupsFilePrintf(fp, "*%%%%%%%% PPD file for %s with CUPS.%s",
416                  model_name->value, lf);
417   cupsFilePrintf(fp,
418                  "*%%%%%%%% Created by the CUPS PPD Compiler " CUPS_SVERSION
419 		 ".%s", lf);
420   for (s = (ppdcString *)copyright->first();
421        s;
422        s = (ppdcString *)copyright->next())
423     cupsFilePrintf(fp, "*%% %s%s", catalog->find_message(s->value), lf);
424   cupsFilePrintf(fp, "*FormatVersion: \"4.3\"%s", lf);
425   cupsFilePrintf(fp, "*FileVersion: \"%s\"%s", version->value, lf);
426 
427   a = find_attr("LanguageVersion", NULL);
428   cupsFilePrintf(fp, "*LanguageVersion: %s%s",
429         	 catalog->find_message(a ? a->value->value : "English"), lf);
430 
431   a = find_attr("LanguageEncoding", NULL);
432   cupsFilePrintf(fp, "*LanguageEncoding: %s%s",
433         	 catalog->find_message(a ? a->value->value : "ISOLatin1"), lf);
434 
435   cupsFilePrintf(fp, "*PCFileName: \"%s\"%s", pc_file_name->value, lf);
436 
437   for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next())
438     if (!strcmp(a->name->value, "Product"))
439       break;
440 
441   if (a)
442   {
443     for (; a; a = (ppdcAttr *)attrs->next())
444       if (!strcmp(a->name->value, "Product"))
445 	cupsFilePrintf(fp, "*Product: \"%s\"%s", a->value->value, lf);
446   }
447   else
448     cupsFilePrintf(fp, "*Product: \"(%s)\"%s", model_name->value, lf);
449 
450   cupsFilePrintf(fp, "*Manufacturer: \"%s\"%s",
451         	 catalog->find_message(manufacturer->value), lf);
452 
453   if ((a = find_attr("ModelName", NULL)) != NULL)
454     cupsFilePrintf(fp, "*ModelName: \"%s\"%s",
455         	   catalog->find_message(a->value->value), lf);
456   else if (_cups_strncasecmp(model_name->value, manufacturer->value,
457                        strlen(manufacturer->value)))
458     cupsFilePrintf(fp, "*ModelName: \"%s %s\"%s",
459         	   catalog->find_message(manufacturer->value),
460         	   catalog->find_message(model_name->value), lf);
461   else
462     cupsFilePrintf(fp, "*ModelName: \"%s\"%s",
463         	   catalog->find_message(model_name->value), lf);
464 
465   if ((a = find_attr("ShortNickName", NULL)) != NULL)
466     cupsFilePrintf(fp, "*ShortNickName: \"%s\"%s",
467         	   catalog->find_message(a->value->value), lf);
468   else if (_cups_strncasecmp(model_name->value, manufacturer->value,
469                        strlen(manufacturer->value)))
470     cupsFilePrintf(fp, "*ShortNickName: \"%s %s\"%s",
471         	   catalog->find_message(manufacturer->value),
472         	   catalog->find_message(model_name->value), lf);
473   else
474     cupsFilePrintf(fp, "*ShortNickName: \"%s\"%s",
475         	   catalog->find_message(model_name->value), lf);
476 
477   if ((a = find_attr("NickName", NULL)) != NULL)
478     cupsFilePrintf(fp, "*NickName: \"%s\"%s",
479         	   catalog->find_message(a->value->value), lf);
480   else if (_cups_strncasecmp(model_name->value, manufacturer->value,
481                        strlen(manufacturer->value)))
482     cupsFilePrintf(fp, "*NickName: \"%s %s, %s\"%s",
483         	   catalog->find_message(manufacturer->value),
484         	   catalog->find_message(model_name->value), version->value,
485 		   lf);
486   else
487     cupsFilePrintf(fp, "*NickName: \"%s, %s\"%s",
488         	   catalog->find_message(model_name->value), version->value,
489 		   lf);
490 
491   for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next())
492     if (!strcmp(a->name->value, "PSVersion"))
493       break;
494 
495   if (a)
496   {
497     for (; a; a = (ppdcAttr *)attrs->next())
498       if (!strcmp(a->name->value, "PSVersion"))
499 	cupsFilePrintf(fp, "*PSVersion: \"%s\"%s", a->value->value, lf);
500   }
501   else
502     cupsFilePrintf(fp, "*PSVersion: \"(3010.000) 0\"%s", lf);
503 
504   if ((a = find_attr("LanguageLevel", NULL)) != NULL)
505     cupsFilePrintf(fp, "*LanguageLevel: \"%s\"%s", a->value->value, lf);
506   else
507     cupsFilePrintf(fp, "*LanguageLevel: \"3\"%s", lf);
508 
509   cupsFilePrintf(fp, "*ColorDevice: %s%s", color_device ? "True" : "False", lf);
510 
511   if ((a = find_attr("DefaultColorSpace", NULL)) != NULL)
512     cupsFilePrintf(fp, "*DefaultColorSpace: %s%s", a->value->value, lf);
513   else
514     cupsFilePrintf(fp, "*DefaultColorSpace: %s%s",
515                    color_device ? "RGB" : "Gray", lf);
516 
517   if ((a = find_attr("FileSystem", NULL)) != NULL)
518     cupsFilePrintf(fp, "*FileSystem: %s%s", a->value->value, lf);
519   else
520     cupsFilePrintf(fp, "*FileSystem: False%s", lf);
521 
522   cupsFilePrintf(fp, "*Throughput: \"%d\"%s", throughput, lf);
523 
524   if ((a = find_attr("LandscapeOrientation", NULL)) != NULL)
525     cupsFilePrintf(fp, "*LandscapeOrientation: %s%s", a->value->value, lf);
526   else
527     cupsFilePrintf(fp, "*LandscapeOrientation: Plus90%s", lf);
528 
529   if ((a = find_attr("TTRasterizer", NULL)) != NULL)
530     cupsFilePrintf(fp, "*TTRasterizer: %s%s", a->value->value, lf);
531   else if (type != PPDC_DRIVER_PS)
532     cupsFilePrintf(fp, "*TTRasterizer: Type42%s", lf);
533 
534   struct lconv *loc = localeconv();
535 
536   if (attrs->count)
537   {
538     // Write driver-defined attributes...
539     cupsFilePrintf(fp, "*%% Driver-defined attributes...%s", lf);
540     for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next())
541     {
542       if (!strcmp(a->name->value, "Product") ||
543           !strcmp(a->name->value, "PSVersion") ||
544           !strcmp(a->name->value, "LanguageLevel") ||
545           !strcmp(a->name->value, "DefaultColorSpace") ||
546           !strcmp(a->name->value, "FileSystem") ||
547           !strcmp(a->name->value, "LandscapeOrientation") ||
548           !strcmp(a->name->value, "TTRasterizer") ||
549           !strcmp(a->name->value, "LanguageVersion") ||
550           !strcmp(a->name->value, "LanguageEncoding") ||
551           !strcmp(a->name->value, "ModelName") ||
552           !strcmp(a->name->value, "NickName") ||
553           !strcmp(a->name->value, "ShortNickName") ||
554 	  !strcmp(a->name->value, "cupsVersion"))
555 	continue;
556 
557       if (a->name->value[0] == '?' &&
558           (find_option(a->name->value + 1) ||
559 	   !strcmp(a->name->value, "?ImageableArea") ||
560 	   !strcmp(a->name->value, "?PageRegion") ||
561 	   !strcmp(a->name->value, "?PageSize") ||
562 	   !strcmp(a->name->value, "?PaperDimension")))
563         continue;
564 
565       if (!strncmp(a->name->value, "Custom", 6) &&
566           find_option(a->name->value + 6))
567 	continue;
568 
569       if (!strncmp(a->name->value, "ParamCustom", 11) &&
570           find_option(a->name->value + 11))
571 	continue;
572 
573       if (!a->selector->value || !a->selector->value[0])
574 	cupsFilePrintf(fp, "*%s", a->name->value);
575       else if (!a->text->value || !a->text->value[0])
576 	cupsFilePrintf(fp, "*%s %s", a->name->value, a->selector->value);
577       else
578 	cupsFilePrintf(fp, "*%s %s/%s", a->name->value, a->selector->value,
579         	       a->text->value);
580 
581       if (strcmp(a->value->value, "False") &&
582           strcmp(a->value->value, "True") &&
583 	  strcmp(a->name->value, "1284Modes") &&
584 	  strcmp(a->name->value, "InkName") &&
585 	  strcmp(a->name->value, "PageStackOrder") &&
586 	  strncmp(a->name->value, "ParamCustom", 11) &&
587 	  strcmp(a->name->value, "Protocols") &&
588 	  strcmp(a->name->value, "ReferencePunch") &&
589 	  strncmp(a->name->value, "Default", 7))
590       {
591 	cupsFilePrintf(fp, ": \"%s\"%s", a->value->value, lf);
592 
593 	if (strchr(a->value->value, '\n') || strchr(a->value->value, '\r'))
594           cupsFilePrintf(fp, "*End%s", lf);
595       }
596       else
597 	cupsFilePrintf(fp, ": %s%s", a->value->value, lf);
598     }
599   }
600 
601   if (type != PPDC_DRIVER_PS || filters->count)
602   {
603     if ((a = find_attr("cupsVersion", NULL)) != NULL)
604       cupsFilePrintf(fp, "*cupsVersion: %s%s", a->value->value, lf);
605     else
606       cupsFilePrintf(fp, "*cupsVersion: %d.%d%s", CUPS_VERSION_MAJOR,
607 		     CUPS_VERSION_MINOR, lf);
608     cupsFilePrintf(fp, "*cupsModelNumber: %d%s", model_number, lf);
609     cupsFilePrintf(fp, "*cupsManualCopies: %s%s",
610                    manual_copies ? "True" : "False", lf);
611 
612     if (filters->count)
613     {
614       for (f = (ppdcFilter *)filters->first();
615            f;
616 	   f = (ppdcFilter *)filters->next())
617 	cupsFilePrintf(fp, "*cupsFilter: \"%s %d %s\"%s", f->mime_type->value,
618 	               f->cost, f->program->value, lf);
619     }
620     else
621     {
622       switch (type)
623       {
624         case PPDC_DRIVER_LABEL :
625 	    cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-raster 50 "
626 	        	     "rastertolabel\"%s", lf);
627 	    break;
628 
629         case PPDC_DRIVER_EPSON :
630 	    cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-raster 50 "
631 	        	     "rastertoepson\"%s", lf);
632 	    break;
633 
634         case PPDC_DRIVER_ESCP :
635 	    cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-command 50 "
636 	        	     "commandtoescpx\"%s", lf);
637 	    cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-raster 50 "
638 	        	     "rastertoescpx\"%s", lf);
639 	    break;
640 
641         case PPDC_DRIVER_HP :
642 	    cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-raster 50 "
643 	        	     "rastertohp\"%s", lf);
644 	    break;
645 
646         case PPDC_DRIVER_PCL :
647 	    cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-command 50 "
648 	        	     "commandtopclx\"%s", lf);
649 	    cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-raster 50 "
650 	        	     "rastertopclx\"%s", lf);
651 	    break;
652 
653 	default :
654 	    break;
655       }
656     }
657 
658     for (p = (ppdcProfile *)profiles->first();
659          p;
660 	 p = (ppdcProfile *)profiles->next())
661     {
662       char density[255], gamma[255], profile[9][255];
663 
664       _cupsStrFormatd(density, density + sizeof(density), p->density, loc);
665       _cupsStrFormatd(gamma, gamma + sizeof(gamma), p->gamma, loc);
666 
667       for (int i = 0; i < 9; i ++)
668 	_cupsStrFormatd(profile[i], profile[i] + sizeof(profile[0]),
669 	                p->profile[i], loc);
670 
671       cupsFilePrintf(fp,
672                      "*cupsColorProfile %s/%s: \"%s %s %s %s %s %s %s %s %s %s "
673 		     "%s\"%s", p->resolution->value, p->media_type->value,
674 		     density, gamma, profile[0], profile[1], profile[2],
675 		     profile[3], profile[4], profile[5], profile[6], profile[7],
676 		     profile[8], lf);
677     }
678   }
679 
680   if (locales)
681   {
682     // Add localizations for additional languages...
683     ppdcString	*locale;		// Locale name
684     ppdcCatalog	*locatalog;		// Message catalog for locale
685 
686 
687     // Write the list of languages...
688     cupsFilePrintf(fp, "*cupsLanguages: \"en");
689 
690     for (locale = (ppdcString *)locales->first();
691          locale;
692 	 locale = (ppdcString *)locales->next())
693     {
694       // Skip (US) English...
695       if (!strcmp(locale->value, "en") || !strcmp(locale->value, "en_US"))
696         continue;
697 
698       // See if we have a po file for this language...
699       if (!src->find_po(locale->value))
700       {
701         // No, see if we can use the base file?
702         locatalog = new ppdcCatalog(locale->value);
703 
704 	if (locatalog->messages->count == 0)
705 	{
706 	  // No, skip this one...
707           _cupsLangPrintf(stderr,
708 	                  _("ppdc: No message catalog provided for locale "
709 			    "%s."), locale->value);
710           delete locatalog;
711           continue;
712 	}
713 
714         // Add the base file to the list...
715 	src->po_files->add(locatalog);
716       }
717 
718       cupsFilePrintf(fp, " %s", locale->value);
719     }
720 
721     cupsFilePrintf(fp, "\"%s", lf);
722   }
723 
724   for (cn = (ppdcConstraint *)constraints->first();
725        cn;
726        cn = (ppdcConstraint *)constraints->next())
727   {
728     // First constrain 1 against 2...
729     if (!strncmp(cn->option1->value, "*Custom", 7) ||
730         !strncmp(cn->option2->value, "*Custom", 7))
731       cupsFilePuts(fp, "*NonUIConstraints: ");
732     else
733       cupsFilePuts(fp, "*UIConstraints: ");
734 
735     if (cn->option1->value[0] != '*')
736       cupsFilePutChar(fp, '*');
737 
738     cupsFilePuts(fp, cn->option1->value);
739 
740     if (cn->choice1->value)
741       cupsFilePrintf(fp, " %s", cn->choice1->value);
742 
743     cupsFilePutChar(fp, ' ');
744 
745     if (cn->option2->value[0] != '*')
746       cupsFilePutChar(fp, '*');
747 
748     cupsFilePuts(fp, cn->option2->value);
749 
750     if (cn->choice2->value)
751       cupsFilePrintf(fp, " %s", cn->choice2->value);
752 
753     cupsFilePuts(fp, lf);
754 
755     // Then constrain 2 against 1...
756     if (!strncmp(cn->option1->value, "*Custom", 7) ||
757         !strncmp(cn->option2->value, "*Custom", 7))
758       cupsFilePuts(fp, "*NonUIConstraints: ");
759     else
760       cupsFilePuts(fp, "*UIConstraints: ");
761 
762     if (cn->option2->value[0] != '*')
763       cupsFilePutChar(fp, '*');
764 
765     cupsFilePuts(fp, cn->option2->value);
766 
767     if (cn->choice2->value)
768       cupsFilePrintf(fp, " %s", cn->choice2->value);
769 
770     cupsFilePutChar(fp, ' ');
771 
772     if (cn->option1->value[0] != '*')
773       cupsFilePutChar(fp, '*');
774 
775     cupsFilePuts(fp, cn->option1->value);
776 
777     if (cn->choice1->value)
778       cupsFilePrintf(fp, " %s", cn->choice1->value);
779 
780     cupsFilePuts(fp, lf);
781   }
782 
783   // PageSize option...
784   cupsFilePrintf(fp, "*OpenUI *PageSize/Media Size: PickOne%s", lf);
785   cupsFilePrintf(fp, "*OrderDependency: 10 AnySetup *PageSize%s", lf);
786   cupsFilePrintf(fp, "*DefaultPageSize: %s%s",
787                  default_size ? default_size->value : "Letter", lf);
788 
789   for (m = (ppdcMediaSize *)sizes->first();
790        m;
791        m = (ppdcMediaSize *)sizes->next())
792     if (m->size_code->value)
793     {
794       cupsFilePrintf(fp, "*PageSize %s/%s: \"%s\"%s",
795         	     m->name->value, catalog->find_message(m->text->value),
796 		     m->size_code->value, lf);
797 
798       if (strchr(m->size_code->value, '\n') ||
799           strchr(m->size_code->value, '\r'))
800         cupsFilePrintf(fp, "*End%s", lf);
801     }
802     else
803       cupsFilePrintf(fp,
804                      "*PageSize %s/%s: \"<</PageSize[%.0f %.0f]"
805 		     "/ImagingBBox null>>setpagedevice\"%s",
806         	     m->name->value, catalog->find_message(m->text->value),
807 		     m->width, m->length, lf);
808 
809   if ((a = find_attr("?PageSize", NULL)) != NULL)
810   {
811     cupsFilePrintf(fp, "*?PageSize: \"%s\"%s", a->value->value, lf);
812 
813     if (strchr(a->value->value, '\n') ||
814         strchr(a->value->value, '\r'))
815       cupsFilePrintf(fp, "*End%s", lf);
816   }
817 
818   cupsFilePrintf(fp, "*CloseUI: *PageSize%s", lf);
819 
820   // PageRegion option...
821   cupsFilePrintf(fp, "*OpenUI *PageRegion/Media Size: PickOne%s", lf);
822   cupsFilePrintf(fp, "*OrderDependency: 10 AnySetup *PageRegion%s", lf);
823   cupsFilePrintf(fp, "*DefaultPageRegion: %s%s",
824                  default_size ? default_size->value : "Letter", lf);
825 
826   for (m = (ppdcMediaSize *)sizes->first();
827        m;
828        m = (ppdcMediaSize *)sizes->next())
829     if (m->region_code->value)
830     {
831       cupsFilePrintf(fp, "*PageRegion %s/%s: \"%s\"%s",
832         	     m->name->value, catalog->find_message(m->text->value),
833 		     m->region_code->value, lf);
834 
835       if (strchr(m->region_code->value, '\n') ||
836           strchr(m->region_code->value, '\r'))
837         cupsFilePrintf(fp, "*End%s", lf);
838     }
839     else
840       cupsFilePrintf(fp,
841                      "*PageRegion %s/%s: \"<</PageSize[%.0f %.0f]"
842 		     "/ImagingBBox null>>setpagedevice\"%s",
843         	     m->name->value, catalog->find_message(m->text->value),
844 		     m->width, m->length, lf);
845 
846   if ((a = find_attr("?PageRegion", NULL)) != NULL)
847   {
848     cupsFilePrintf(fp, "*?PageRegion: \"%s\"%s", a->value->value, lf);
849 
850     if (strchr(a->value->value, '\n') ||
851         strchr(a->value->value, '\r'))
852       cupsFilePrintf(fp, "*End%s", lf);
853   }
854 
855   cupsFilePrintf(fp, "*CloseUI: *PageRegion%s", lf);
856 
857   // ImageableArea info...
858   cupsFilePrintf(fp, "*DefaultImageableArea: %s%s",
859                  default_size ? default_size->value : "Letter", lf);
860 
861   char left[255], right[255], bottom[255], top[255];
862 
863   for (m = (ppdcMediaSize *)sizes->first();
864        m;
865        m = (ppdcMediaSize *)sizes->next())
866   {
867     _cupsStrFormatd(left, left + sizeof(left), m->left, loc);
868     _cupsStrFormatd(bottom, bottom + sizeof(bottom), m->bottom, loc);
869     _cupsStrFormatd(right, right + sizeof(right), m->width - m->right, loc);
870     _cupsStrFormatd(top, top + sizeof(top), m->length - m->top, loc);
871 
872     cupsFilePrintf(fp, "*ImageableArea %s/%s: \"%s %s %s %s\"%s",
873                    m->name->value, catalog->find_message(m->text->value),
874 		   left, bottom, right, top, lf);
875   }
876 
877   if ((a = find_attr("?ImageableArea", NULL)) != NULL)
878   {
879     cupsFilePrintf(fp, "*?ImageableArea: \"%s\"%s", a->value->value, lf);
880 
881     if (strchr(a->value->value, '\n') ||
882         strchr(a->value->value, '\r'))
883       cupsFilePrintf(fp, "*End%s", lf);
884   }
885 
886   // PaperDimension info...
887   cupsFilePrintf(fp, "*DefaultPaperDimension: %s%s",
888                  default_size ? default_size->value : "Letter", lf);
889 
890   char width[255], length[255];
891 
892   for (m = (ppdcMediaSize *)sizes->first();
893        m;
894        m = (ppdcMediaSize *)sizes->next())
895   {
896     _cupsStrFormatd(width, width + sizeof(width), m->width, loc);
897     _cupsStrFormatd(length, length + sizeof(length), m->length, loc);
898 
899     cupsFilePrintf(fp, "*PaperDimension %s/%s: \"%s %s\"%s",
900                    m->name->value, catalog->find_message(m->text->value),
901 		   width, length, lf);
902   }
903 
904   if ((a = find_attr("?PaperDimension", NULL)) != NULL)
905   {
906     cupsFilePrintf(fp, "*?PaperDimension: \"%s\"%s", a->value->value, lf);
907 
908     if (strchr(a->value->value, '\n') ||
909         strchr(a->value->value, '\r'))
910       cupsFilePrintf(fp, "*End%s", lf);
911   }
912 
913   // Custom size support...
914   if (variable_paper_size)
915   {
916     _cupsStrFormatd(width, width + sizeof(width), max_width, loc);
917     _cupsStrFormatd(length, length + sizeof(length), max_length, loc);
918 
919     _cupsStrFormatd(left, left + sizeof(left), left_margin, loc);
920     _cupsStrFormatd(bottom, bottom + sizeof(bottom), bottom_margin, loc);
921     _cupsStrFormatd(right, right + sizeof(right), right_margin, loc);
922     _cupsStrFormatd(top, top + sizeof(top), top_margin, loc);
923 
924     cupsFilePrintf(fp, "*MaxMediaWidth: \"%s\"%s", width, lf);
925     cupsFilePrintf(fp, "*MaxMediaHeight: \"%s\"%s", length, lf);
926     cupsFilePrintf(fp, "*HWMargins: %s %s %s %s%s", left, bottom, right, top,
927                    lf);
928 
929     if (custom_size_code && custom_size_code->value)
930     {
931       cupsFilePrintf(fp, "*CustomPageSize True: \"%s\"%s",
932                      custom_size_code->value, lf);
933 
934       if (strchr(custom_size_code->value, '\n') ||
935           strchr(custom_size_code->value, '\r'))
936         cupsFilePrintf(fp, "*End%s", lf);
937     }
938     else
939       cupsFilePrintf(fp,
940 		     "*CustomPageSize True: \"pop pop pop <</PageSize[5 -2 roll]"
941 		     "/ImagingBBox null>>setpagedevice\"%s", lf);
942 
943     if ((a = find_attr("ParamCustomPageSize", "Width")) != NULL)
944       cupsFilePrintf(fp, "*ParamCustomPageSize Width: %s%s", a->value->value,
945 		     lf);
946     else
947     {
948       char width0[255];
949 
950       _cupsStrFormatd(width0, width0 + sizeof(width0), min_width, loc);
951       _cupsStrFormatd(width, width + sizeof(width), max_width, loc);
952 
953       cupsFilePrintf(fp, "*ParamCustomPageSize Width: 1 points %s %s%s",
954                      width0, width, lf);
955     }
956 
957     if ((a = find_attr("ParamCustomPageSize", "Height")) != NULL)
958       cupsFilePrintf(fp, "*ParamCustomPageSize Height: %s%s", a->value->value,
959 		     lf);
960     else
961     {
962       char length0[255];
963 
964       _cupsStrFormatd(length0, length0 + sizeof(length0), min_length, loc);
965       _cupsStrFormatd(length, length + sizeof(length), max_length, loc);
966 
967       cupsFilePrintf(fp, "*ParamCustomPageSize Height: 2 points %s %s%s",
968                      length0, length, lf);
969     }
970 
971     if ((a = find_attr("ParamCustomPageSize", "WidthOffset")) != NULL)
972       cupsFilePrintf(fp, "*ParamCustomPageSize WidthOffset: %s%s",
973                      a->value->value, lf);
974     else
975       cupsFilePrintf(fp, "*ParamCustomPageSize WidthOffset: 3 points 0 0%s", lf);
976 
977     if ((a = find_attr("ParamCustomPageSize", "HeightOffset")) != NULL)
978       cupsFilePrintf(fp, "*ParamCustomPageSize HeightOffset: %s%s",
979                      a->value->value, lf);
980     else
981       cupsFilePrintf(fp, "*ParamCustomPageSize HeightOffset: 4 points 0 0%s", lf);
982 
983     if ((a = find_attr("ParamCustomPageSize", "Orientation")) != NULL)
984       cupsFilePrintf(fp, "*ParamCustomPageSize Orientation: %s%s",
985                      a->value->value, lf);
986     else
987       cupsFilePrintf(fp, "*ParamCustomPageSize Orientation: 5 int 0 0%s", lf);
988   }
989 
990   // All other options...
991   for (g = (ppdcGroup *)groups->first(); g; g = (ppdcGroup *)groups->next())
992   {
993     if (!g->options->count)
994       continue;
995 
996     if (_cups_strcasecmp(g->name->value, "General"))
997       cupsFilePrintf(fp, "*OpenGroup: %s/%s%s", g->name->value,
998                      catalog->find_message(g->text->value), lf);
999 
1000     for (o = (ppdcOption *)g->options->first();
1001          o;
1002 	 o = (ppdcOption *)g->options->next())
1003     {
1004       if (!o->choices->count)
1005         continue;
1006 
1007       if (o->section == PPDC_SECTION_JCL)
1008       {
1009 	if (!o->text->value)
1010 	  cupsFilePrintf(fp, "*JCLOpenUI *%s/%s: ", o->name->value,
1011 			 catalog->find_message(o->name->value));
1012 	else
1013 	  cupsFilePrintf(fp, "*JCLOpenUI *%s/%s: ", o->name->value,
1014 			 catalog->find_message(o->text->value));
1015       }
1016       else if (!o->text->value)
1017 	cupsFilePrintf(fp, "*OpenUI *%s/%s: ", o->name->value,
1018 	               catalog->find_message(o->name->value));
1019       else
1020 	cupsFilePrintf(fp, "*OpenUI *%s/%s: ", o->name->value,
1021 	               catalog->find_message(o->text->value));
1022 
1023       switch (o->type)
1024       {
1025         case PPDC_BOOLEAN :
1026 	    cupsFilePrintf(fp, "Boolean%s", lf);
1027 	    break;
1028         default :
1029 	    cupsFilePrintf(fp, "PickOne%s", lf);
1030 	    break;
1031         case PPDC_PICKMANY :
1032 	    cupsFilePrintf(fp, "PickMany%s", lf);
1033 	    break;
1034       }
1035 
1036       char order[255];
1037       _cupsStrFormatd(order, order + sizeof(order), o->order, loc);
1038 
1039       cupsFilePrintf(fp, "*OrderDependency: %s ", order);
1040       switch (o->section)
1041       {
1042         default :
1043 	    cupsFilePrintf(fp, "AnySetup");
1044 	    break;
1045         case PPDC_SECTION_DOCUMENT :
1046 	    cupsFilePrintf(fp, "DocumentSetup");
1047 	    break;
1048         case PPDC_SECTION_EXIT :
1049 	    cupsFilePrintf(fp, "ExitServer");
1050 	    break;
1051         case PPDC_SECTION_JCL :
1052 	    cupsFilePrintf(fp, "JCLSetup");
1053 	    break;
1054         case PPDC_SECTION_PAGE :
1055 	    cupsFilePrintf(fp, "PageSetup");
1056 	    break;
1057         case PPDC_SECTION_PROLOG :
1058 	    cupsFilePrintf(fp, "Prolog");
1059 	    break;
1060       }
1061 
1062       cupsFilePrintf(fp, " *%s%s", o->name->value, lf);
1063 
1064       if (o->defchoice)
1065       {
1066         // Use the programmer-supplied default...
1067         cupsFilePrintf(fp, "*Default%s: %s%s", o->name->value,
1068 	               o->defchoice->value, lf);
1069       }
1070       else
1071       {
1072         // Make the first choice the default...
1073         c = (ppdcChoice *)o->choices->first();
1074         cupsFilePrintf(fp, "*Default%s: %s%s", o->name->value, c->name->value,
1075 		       lf);
1076       }
1077 
1078       for (c = (ppdcChoice *)o->choices->first();
1079            c;
1080 	   c = (ppdcChoice *)o->choices->next())
1081       {
1082         // Write this choice...
1083 	if (!c->text->value)
1084           cupsFilePrintf(fp, "*%s %s/%s: \"%s\"%s", o->name->value,
1085                          c->name->value, catalog->find_message(c->name->value),
1086 	        	 c->code->value, lf);
1087         else
1088           cupsFilePrintf(fp, "*%s %s/%s: \"%s\"%s", o->name->value,
1089 	                 c->name->value, catalog->find_message(c->text->value),
1090 			 c->code->value, lf);
1091 
1092 	// Multi-line commands need a *End line to terminate them.
1093         if (strchr(c->code->value, '\n') ||
1094 	    strchr(c->code->value, '\r'))
1095 	  cupsFilePrintf(fp, "*End%s", lf);
1096       }
1097 
1098       snprintf(query, sizeof(query), "?%s", o->name->value);
1099 
1100       if ((a = find_attr(query, NULL)) != NULL)
1101       {
1102 	cupsFilePrintf(fp, "*%s: \"%s\"%s", query, a->value->value, lf);
1103 
1104 	if (strchr(a->value->value, '\n') ||
1105             strchr(a->value->value, '\r'))
1106 	  cupsFilePrintf(fp, "*End%s", lf);
1107       }
1108 
1109       if (o->section == PPDC_SECTION_JCL)
1110 	cupsFilePrintf(fp, "*JCLCloseUI: *%s%s", o->name->value, lf);
1111       else
1112 	cupsFilePrintf(fp, "*CloseUI: *%s%s", o->name->value, lf);
1113 
1114       snprintf(custom, sizeof(custom), "Custom%s", o->name->value);
1115       if ((a = find_attr(custom, "True")) != NULL)
1116       {
1117         // Output custom option information...
1118         cupsFilePrintf(fp, "*%s True: \"%s\"%s", custom, a->value->value, lf);
1119 	if (strchr(a->value->value, '\n') || strchr(a->value->value, '\r'))
1120 	  cupsFilePrintf(fp, "*End%s", lf);
1121 
1122         snprintf(custom, sizeof(custom), "ParamCustom%s", o->name->value);
1123 	for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next())
1124 	{
1125 	  if (strcmp(a->name->value, custom))
1126 	    continue;
1127 
1128 	  if (!a->selector->value || !a->selector->value[0])
1129 	    cupsFilePrintf(fp, "*%s", a->name->value);
1130 	  else if (!a->text->value || !a->text->value[0])
1131 	    cupsFilePrintf(fp, "*%s %s/%s", a->name->value, a->selector->value,
1132 	                   catalog->find_message(a->selector->value));
1133 	  else
1134 	    cupsFilePrintf(fp, "*%s %s/%s", a->name->value, a->selector->value,
1135 			   catalog->find_message(a->text->value));
1136 
1137           cupsFilePrintf(fp, ": %s%s", a->value->value, lf);
1138 	}
1139       }
1140     }
1141 
1142     if (_cups_strcasecmp(g->name->value, "General"))
1143       cupsFilePrintf(fp, "*CloseGroup: %s%s", g->name->value, lf);
1144   }
1145 
1146   if (locales)
1147   {
1148     // Add localizations for additional languages...
1149     ppdcString	*locale;		// Locale name
1150     ppdcCatalog	*locatalog;		// Message catalog for locale
1151 
1152 
1153     // Write the translation strings for each language...
1154     for (locale = (ppdcString *)locales->first();
1155          locale;
1156 	 locale = (ppdcString *)locales->next())
1157     {
1158       // Skip (US) English...
1159       if (!strcmp(locale->value, "en") || !strcmp(locale->value, "en_US"))
1160         continue;
1161 
1162       // Skip missing languages...
1163       if ((locatalog = src->find_po(locale->value)) == NULL)
1164         continue;
1165 
1166       // Do the core stuff first...
1167       cupsFilePrintf(fp, "*%s.Translation Manufacturer/%s: \"\"%s",
1168                      locale->value,
1169         	     locatalog->find_message(manufacturer->value), lf);
1170 
1171       if ((a = find_attr("ModelName", NULL)) != NULL)
1172 	cupsFilePrintf(fp, "*%s.Translation ModelName/%s: \"\"%s",
1173                        locale->value,
1174         	       locatalog->find_message(a->value->value), lf);
1175       else if (_cups_strncasecmp(model_name->value, manufacturer->value,
1176                 	   strlen(manufacturer->value)))
1177 	cupsFilePrintf(fp, "*%s.Translation ModelName/%s %s: \"\"%s",
1178                        locale->value,
1179         	       locatalog->find_message(manufacturer->value),
1180         	       locatalog->find_message(model_name->value), lf);
1181       else
1182 	cupsFilePrintf(fp, "*%s.Translation ModelName/%s: \"\"%s",
1183                        locale->value,
1184         	       locatalog->find_message(model_name->value), lf);
1185 
1186       if ((a = find_attr("ShortNickName", NULL)) != NULL)
1187 	cupsFilePrintf(fp, "*%s.Translation ShortNickName/%s: \"\"%s",
1188                        locale->value,
1189         	       locatalog->find_message(a->value->value), lf);
1190       else if (_cups_strncasecmp(model_name->value, manufacturer->value,
1191                 	   strlen(manufacturer->value)))
1192 	cupsFilePrintf(fp, "*%s.Translation ShortNickName/%s %s: \"\"%s",
1193                        locale->value,
1194         	       locatalog->find_message(manufacturer->value),
1195         	       locatalog->find_message(model_name->value), lf);
1196       else
1197 	cupsFilePrintf(fp, "*%s.Translation ShortNickName/%s: \"\"%s",
1198                        locale->value,
1199         	       locatalog->find_message(model_name->value), lf);
1200 
1201       if ((a = find_attr("NickName", NULL)) != NULL)
1202 	cupsFilePrintf(fp, "*%s.Translation NickName/%s: \"\"%s",
1203                        locale->value,
1204         	       locatalog->find_message(a->value->value), lf);
1205       else if (_cups_strncasecmp(model_name->value, manufacturer->value,
1206                 	   strlen(manufacturer->value)))
1207 	cupsFilePrintf(fp, "*%s.Translation NickName/%s %s, %s: \"\"%s",
1208                        locale->value,
1209         	       locatalog->find_message(manufacturer->value),
1210         	       locatalog->find_message(model_name->value),
1211 		       version->value, lf);
1212       else
1213 	cupsFilePrintf(fp, "*%s.Translation NickName/%s, %s: \"\"%s",
1214                        locale->value,
1215         	       locatalog->find_message(model_name->value),
1216 		       version->value, lf);
1217 
1218       // Then the page sizes...
1219       cupsFilePrintf(fp, "*%s.Translation PageSize/%s: \"\"%s", locale->value,
1220                      locatalog->find_message("Media Size"), lf);
1221 
1222       for (m = (ppdcMediaSize *)sizes->first();
1223 	   m;
1224 	   m = (ppdcMediaSize *)sizes->next())
1225       {
1226         cupsFilePrintf(fp, "*%s.PageSize %s/%s: \"\"%s", locale->value,
1227         	       m->name->value, locatalog->find_message(m->text->value),
1228 		       lf);
1229       }
1230 
1231       // Next the groups and options...
1232       for (g = (ppdcGroup *)groups->first(); g; g = (ppdcGroup *)groups->next())
1233       {
1234 	if (!g->options->count)
1235 	  continue;
1236 
1237 	if (_cups_strcasecmp(g->name->value, "General"))
1238 	  cupsFilePrintf(fp, "*%s.Translation %s/%s: \"\"%s", locale->value,
1239 	                 g->name->value,
1240                 	 locatalog->find_message(g->text->value), lf);
1241 
1242 	for (o = (ppdcOption *)g->options->first();
1243              o;
1244 	     o = (ppdcOption *)g->options->next())
1245 	{
1246 	  if (!o->choices->count)
1247             continue;
1248 
1249           cupsFilePrintf(fp, "*%s.Translation %s/%s: \"\"%s", locale->value,
1250 	                 o->name->value,
1251 			 locatalog->find_message(o->text->value ?
1252 			                         o->text->value :
1253 						 o->name->value), lf);
1254 
1255 	  for (c = (ppdcChoice *)o->choices->first();
1256                c;
1257 	       c = (ppdcChoice *)o->choices->next())
1258 	  {
1259             // Write this choice...
1260             cupsFilePrintf(fp, "*%s.%s %s/%s: \"\"%s", locale->value,
1261 	                   o->name->value, c->name->value,
1262 			   locatalog->find_message(c->text->value ?
1263 			                           c->text->value :
1264 						   c->name->value), lf);
1265 	  }
1266 	}
1267       }
1268 
1269       // Finally the localizable attributes...
1270       for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next())
1271       {
1272         if (!a->localizable &&
1273 	    (!a->text || !a->text->value || !a->text->value[0]) &&
1274 	    strcmp(a->name->value, "APCustomColorMatchingName") &&
1275 	    strcmp(a->name->value, "APPrinterPreset") &&
1276 	    strcmp(a->name->value, "cupsICCProfile") &&
1277 	    strcmp(a->name->value, "cupsIPPReason") &&
1278 	    strcmp(a->name->value, "cupsMarkerName") &&
1279 	    strncmp(a->name->value, "Custom", 6) &&
1280 	    strncmp(a->name->value, "ParamCustom", 11))
1281 	  continue;
1282 
1283         cupsFilePrintf(fp, "*%s.%s %s/%s: \"%s\"%s", locale->value,
1284 	               a->name->value, a->selector->value,
1285 		       locatalog->find_message(a->text && a->text->value ?
1286 		                               a->text->value : a->name->value),
1287 		       ((a->localizable && a->value->value[0]) ||
1288 		        !strcmp(a->name->value, "cupsIPPReason")) ?
1289 		           locatalog->find_message(a->value->value) : "",
1290 		       lf);
1291       }
1292     }
1293   }
1294 
1295   if (default_font && default_font->value)
1296     cupsFilePrintf(fp, "*DefaultFont: %s%s", default_font->value, lf);
1297   else
1298     cupsFilePrintf(fp, "*DefaultFont: Courier%s", lf);
1299 
1300   for (fn = (ppdcFont *)fonts->first(); fn; fn = (ppdcFont *)fonts->next())
1301     if (!strcmp(fn->name->value, "*"))
1302     {
1303       for (bfn = (ppdcFont *)src->base_fonts->first();
1304 	   bfn;
1305 	   bfn = (ppdcFont *)src->base_fonts->next())
1306 	cupsFilePrintf(fp, "*Font %s: %s \"%s\" %s %s%s",
1307 		       bfn->name->value, bfn->encoding->value,
1308 		       bfn->version->value, bfn->charset->value,
1309 		       bfn->status == PPDC_FONT_ROM ? "ROM" : "Disk", lf);
1310     }
1311     else
1312       cupsFilePrintf(fp, "*Font %s: %s \"%s\" %s %s%s",
1313         	     fn->name->value, fn->encoding->value, fn->version->value,
1314 		     fn->charset->value,
1315 		     fn->status == PPDC_FONT_ROM ? "ROM" : "Disk", lf);
1316 
1317   cupsFilePrintf(fp, "*%% End of %s, %05d bytes.%s", pc_file_name->value,
1318         	 (int)((size_t)cupsFileTell(fp) + 25 + strlen(pc_file_name->value)),
1319 		 lf);
1320 
1321   if (delete_cat)
1322     catalog->release();
1323 
1324   return (0);
1325 }
1326