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