Coverage Report

Created: 2025-11-09 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cups/cups/ppd-cache.c
Line
Count
Source
1
/*
2
 * PPD cache implementation for CUPS.
3
 *
4
 * Copyright © 2022-2025 by OpenPrinting.
5
 * Copyright © 2010-2021 by Apple Inc.
6
 *
7
 * Licensed under Apache License v2.0.  See the file "LICENSE" for more
8
 * information.
9
 */
10
11
#include "cups-private.h"
12
#include "ppd-private.h"
13
#include "debug-internal.h"
14
#include <math.h>
15
#include <ctype.h>
16
17
18
/*
19
 * Macro to test for two almost-equal PWG measurements.
20
 */
21
22
43.1k
#define _PWG_EQUIVALENT(x, y) (abs((x)-(y)) < 2)
23
24
25
/*
26
 * Local functions...
27
 */
28
29
static int  cups_connect(http_t **http, const char *url, char *resource, size_t ressize);
30
static cups_lang_t *cups_get_strings(http_t **http, const char *printer_uri, const char *language);
31
static int  cups_get_url(http_t **http, const char *url, const char *suffix, char *name, size_t namesize);
32
static const char *ppd_get_string(cups_lang_t *base, cups_lang_t *printer, const char *msgid, char *buffer, size_t bufsize);
33
static void ppd_get_strings(ppd_file_t *ppd, cups_lang_t *langs, const char *option, ppd_choice_t *choice, const char *pwg_msgid);
34
static const char *ppd_inputslot_for_keyword(_ppd_cache_t *pc, const char *keyword);
35
static void ppd_put_strings(cups_file_t *fp, cups_lang_t *langs, const char *ppd_option, const char *ppd_choice, const char *pwg_msgid);
36
static void pwg_add_finishing(cups_array_t *finishings, ipp_finishings_t template, const char *name, const char *value);
37
static int  pwg_compare_finishings(_pwg_finishings_t *a, _pwg_finishings_t *b, void *data);
38
static int  pwg_compare_sizes(cups_size_t *a, cups_size_t *b, void *data);
39
static cups_size_t *pwg_copy_size(cups_size_t *size, void *data);
40
static void pwg_free_finishings(_pwg_finishings_t *f, void *data);
41
static void pwg_ppdize_name(const char *ipp, char *name, size_t namesize);
42
static void pwg_ppdize_resolution(ipp_attribute_t *attr, int element, int *xres, int *yres, char *name, size_t namesize);
43
static void pwg_unppdize_name(const char *ppd, char *name, size_t namesize,
44
                      const char *dashchars);
45
46
47
/*
48
 * '_cupsConvertOptions()' - Convert printer options to standard IPP attributes.
49
 *
50
 * This functions converts PPD and CUPS-specific options to their standard IPP
51
 * attributes and values and adds them to the specified IPP request.
52
 */
53
54
int         /* O - New number of copies */
55
_cupsConvertOptions(
56
    ipp_t           *request,   /* I - IPP request */
57
    ppd_file_t      *ppd,   /* I - PPD file */
58
    _ppd_cache_t    *pc,    /* I - PPD cache info */
59
    ipp_attribute_t *media_col_sup, /* I - media-col-supported values */
60
    ipp_attribute_t *doc_handling_sup,  /* I - multiple-document-handling-supported values */
61
    ipp_attribute_t *print_color_mode_sup,
62
                                  /* I - Printer supports print-color-mode */
63
    const char    *user,    /* I - User info */
64
    const char    *format,    /* I - document-format value */
65
    int           copies,   /* I - Number of copies */
66
    int           num_options,    /* I - Number of options */
67
    cups_option_t *options)   /* I - Options */
68
580
{
69
580
  int   i;      /* Looping var */
70
580
  const char  *keyword,   /* PWG keyword */
71
580
    *password;    /* Password string */
72
580
  pwg_size_t  *size;      /* PWG media size */
73
580
  ipp_t   *media_col,   /* media-col value */
74
580
    *media_size;    /* media-size value */
75
580
  const char  *media_source,    /* media-source value */
76
580
    *media_type,    /* media-type value */
77
580
    *collate_str,   /* multiple-document-handling value */
78
580
    *color_attr_name, /* Supported color attribute */
79
580
    *mandatory,   /* Mandatory attributes */
80
580
    *finishing_template;  /* Finishing template */
81
580
  int   num_finishings = 0, /* Number of finishing values */
82
580
    finishings[10];   /* Finishing enum values */
83
580
  ppd_choice_t  *choice;    /* Marked choice */
84
580
  int           finishings_copies = copies,
85
                                        /* Number of copies for finishings */
86
580
                job_pages = 0,    /* job-pages value */
87
580
    number_up = 1;    /* number-up value */
88
580
  const char  *value;     /* Option value */
89
90
91
 /*
92
  * Send standard IPP attributes...
93
  */
94
95
580
  if (pc->password && (password = cupsGetOption("job-password", num_options, options)) != NULL && ippGetOperation(request) != IPP_OP_VALIDATE_JOB)
96
0
  {
97
0
    ipp_attribute_t *attr = NULL; /* job-password attribute */
98
99
0
    if ((keyword = cupsGetOption("job-password-encryption", num_options, options)) == NULL)
100
0
      keyword = "none";
101
102
0
    if (!strcmp(keyword, "none"))
103
0
    {
104
     /*
105
      * Add plain-text job-password...
106
      */
107
108
0
      attr = ippAddOctetString(request, IPP_TAG_OPERATION, "job-password", password, (int)strlen(password));
109
0
    }
110
0
    else
111
0
    {
112
     /*
113
      * Add hashed job-password...
114
      */
115
116
0
      unsigned char hash[64]; /* Hash of password */
117
0
      ssize_t   hashlen;  /* Length of hash */
118
119
0
      if ((hashlen = cupsHashData(keyword, password, strlen(password), hash, sizeof(hash))) > 0)
120
0
        attr = ippAddOctetString(request, IPP_TAG_OPERATION, "job-password", hash, (int)hashlen);
121
0
    }
122
123
0
    if (attr)
124
0
      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "job-password-encryption", NULL, keyword);
125
0
  }
126
127
580
  if (pc->account_id)
128
0
  {
129
0
    if ((keyword = cupsGetOption("job-account-id", num_options, options)) == NULL)
130
0
      keyword = cupsGetOption("job-billing", num_options, options);
131
132
0
    if (keyword)
133
0
      ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-account-id", NULL, keyword);
134
0
  }
135
136
580
  if (pc->accounting_user_id)
137
0
  {
138
0
    if ((keyword = cupsGetOption("job-accounting-user-id", num_options, options)) == NULL)
139
0
      keyword = user;
140
141
0
    if (keyword)
142
0
      ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-accounting-user-id", NULL, keyword);
143
0
  }
144
145
580
  for (mandatory = (const char *)cupsArrayFirst(pc->mandatory); mandatory; mandatory = (const char *)cupsArrayNext(pc->mandatory))
146
0
  {
147
0
    if (strcmp(mandatory, "copies") &&
148
0
  strcmp(mandatory, "destination-uris") &&
149
0
  strcmp(mandatory, "finishings") &&
150
0
  strcmp(mandatory, "finishings-col") &&
151
0
  strcmp(mandatory, "finishing-template") &&
152
0
  strcmp(mandatory, "job-account-id") &&
153
0
  strcmp(mandatory, "job-accounting-user-id") &&
154
0
  strcmp(mandatory, "job-password") &&
155
0
  strcmp(mandatory, "job-password-encryption") &&
156
0
  strcmp(mandatory, "media") &&
157
0
  strncmp(mandatory, "media-col", 9) &&
158
0
  strcmp(mandatory, "multiple-document-handling") &&
159
0
  strcmp(mandatory, "output-bin") &&
160
0
  strcmp(mandatory, "print-color-mode") &&
161
0
  strcmp(mandatory, "print-quality") &&
162
0
  strcmp(mandatory, "sides") &&
163
0
  (keyword = cupsGetOption(mandatory, num_options, options)) != NULL)
164
0
    {
165
0
      _ipp_option_t *opt = _ippFindOption(mandatory);
166
            /* Option type */
167
0
      ipp_tag_t value_tag = opt ? opt->value_tag : IPP_TAG_NAME;
168
            /* Value type */
169
170
0
      switch (value_tag)
171
0
      {
172
0
  case IPP_TAG_INTEGER :
173
0
  case IPP_TAG_ENUM :
174
0
      ippAddInteger(request, IPP_TAG_JOB, value_tag, mandatory, atoi(keyword));
175
0
      break;
176
0
  case IPP_TAG_BOOLEAN :
177
0
      ippAddBoolean(request, IPP_TAG_JOB, mandatory, !_cups_strcasecmp(keyword, "true"));
178
0
      break;
179
0
  case IPP_TAG_RANGE :
180
0
      {
181
0
        int lower, upper; /* Range */
182
183
0
        if (sscanf(keyword, "%d-%d", &lower, &upper) != 2)
184
0
    lower = upper = atoi(keyword);
185
186
0
        ippAddRange(request, IPP_TAG_JOB, mandatory, lower, upper);
187
0
      }
188
0
      break;
189
0
  case IPP_TAG_STRING :
190
0
      ippAddOctetString(request, IPP_TAG_JOB, mandatory, keyword, (int)strlen(keyword));
191
0
      break;
192
0
  default :
193
0
      if (!strcmp(mandatory, "print-color-mode") && !strcmp(keyword, "monochrome"))
194
0
      {
195
0
        if (ippContainsString(print_color_mode_sup, "auto-monochrome"))
196
0
    keyword = "auto-monochrome";
197
0
        else if (ippContainsString(print_color_mode_sup, "process-monochrome") && !ippContainsString(print_color_mode_sup, "monochrome"))
198
0
    keyword = "process-monochrome";
199
0
      }
200
201
0
      ippAddString(request, IPP_TAG_JOB, value_tag, mandatory, NULL, keyword);
202
0
      break;
203
0
      }
204
0
    }
205
0
  }
206
207
580
  if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
208
526
    keyword = cupsGetOption("media", num_options, options);
209
210
580
  media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot", num_options, options));
211
580
  media_type   = _ppdCacheGetType(pc, cupsGetOption("MediaType", num_options, options));
212
580
  size         = _ppdCacheGetSize(pc, keyword, /*ppd_size*/NULL);
213
214
580
  if (media_col_sup && (size || media_source || media_type))
215
0
  {
216
   /*
217
    * Add a media-col value...
218
    */
219
220
0
    media_col = ippNew();
221
222
0
    if (size)
223
0
    {
224
0
      media_size = ippNew();
225
0
      ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
226
0
                    "x-dimension", size->width);
227
0
      ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
228
0
                    "y-dimension", size->length);
229
230
0
      ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size);
231
0
      ippDelete(media_size);
232
0
    }
233
234
0
    for (i = 0; i < media_col_sup->num_values; i ++)
235
0
    {
236
0
      if (size && !strcmp(media_col_sup->values[i].string.text, "media-left-margin"))
237
0
  ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-left-margin", size->left);
238
0
      else if (size && !strcmp(media_col_sup->values[i].string.text, "media-bottom-margin"))
239
0
  ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-bottom-margin", size->bottom);
240
0
      else if (size && !strcmp(media_col_sup->values[i].string.text, "media-right-margin"))
241
0
  ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-right-margin", size->right);
242
0
      else if (size && !strcmp(media_col_sup->values[i].string.text, "media-top-margin"))
243
0
  ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-top-margin", size->top);
244
0
      else if (media_source && !strcmp(media_col_sup->values[i].string.text, "media-source"))
245
0
  ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-source", NULL, media_source);
246
0
      else if (media_type && !strcmp(media_col_sup->values[i].string.text, "media-type"))
247
0
  ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-type", NULL, media_type);
248
0
    }
249
250
0
    ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col);
251
0
    ippDelete(media_col);
252
0
  }
253
254
580
  if ((keyword = cupsGetOption("output-bin", num_options, options)) == NULL)
255
580
  {
256
580
    if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL)
257
0
      keyword = _ppdCacheGetBin(pc, choice->choice);
258
580
  }
259
260
580
  if (keyword)
261
0
    ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin", NULL, keyword);
262
263
580
  color_attr_name = print_color_mode_sup ? "print-color-mode" : "output-mode";
264
265
 /*
266
  * If we use PPD with standardized PPD option for color support - ColorModel,
267
  * prefer it to don't break color/grayscale support for PPDs, either classic
268
  * or the ones generated from IPP Get-Printer-Attributes response.
269
  */
270
271
580
  if ((keyword = cupsGetOption("ColorModel", num_options, options)) == NULL)
272
459
  {
273
   /*
274
    * No ColorModel in options...
275
    */
276
277
459
    if ((choice = ppdFindMarkedChoice(ppd, "ColorModel")) != NULL)
278
0
    {
279
     /*
280
      * ColorModel is taken from PPD as its default option.
281
      */
282
283
0
      if (!strcmp(choice->choice, "Gray") || !strcmp(choice->choice, "FastGray") || !strcmp(choice->choice, "DeviceGray"))
284
0
        keyword = "monochrome";
285
0
      else
286
0
        keyword = "color";
287
0
    }
288
459
    else
289
     /*
290
      * print-color-mode is a default option since 2.4.1, use it as a fallback if there is no
291
      * ColorModel in options or PPD...
292
      */
293
459
      keyword = cupsGetOption("print-color-mode", num_options, options);
294
459
  }
295
121
  else
296
121
  {
297
   /*
298
    * ColorModel found in options...
299
    */
300
301
121
    if (!strcmp(keyword, "Gray") || !strcmp(keyword, "FastGray") || !strcmp(keyword, "DeviceGray"))
302
3
      keyword = "monochrome";
303
118
    else
304
118
      keyword = "color";
305
121
  }
306
307
580
  if (keyword && !strcmp(keyword, "monochrome"))
308
3
  {
309
3
    if (ippContainsString(print_color_mode_sup, "auto-monochrome"))
310
0
      keyword = "auto-monochrome";
311
3
    else if (ippContainsString(print_color_mode_sup, "process-monochrome") && !ippContainsString(print_color_mode_sup, "monochrome"))
312
0
      keyword = "process-monochrome";
313
3
  }
314
315
580
  if (keyword)
316
121
    ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, color_attr_name, NULL, keyword);
317
318
580
  if ((keyword = cupsGetOption("print-quality", num_options, options)) != NULL)
319
13
    ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", atoi(keyword));
320
567
  else if ((choice = ppdFindMarkedChoice(ppd, "cupsPrintQuality")) != NULL)
321
0
  {
322
0
    if (!_cups_strcasecmp(choice->choice, "draft"))
323
0
      ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_DRAFT);
324
0
    else if (!_cups_strcasecmp(choice->choice, "normal"))
325
0
      ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_NORMAL);
326
0
    else if (!_cups_strcasecmp(choice->choice, "high"))
327
0
      ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_HIGH);
328
0
  }
329
330
580
  if ((keyword = cupsGetOption("sides", num_options, options)) != NULL)
331
2
    ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, keyword);
332
578
  else if (pc->sides_option && (choice = ppdFindMarkedChoice(ppd, pc->sides_option)) != NULL)
333
0
  {
334
0
    if (pc->sides_1sided && !_cups_strcasecmp(choice->choice, pc->sides_1sided))
335
0
      ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "one-sided");
336
0
    else if (pc->sides_2sided_long && !_cups_strcasecmp(choice->choice, pc->sides_2sided_long))
337
0
      ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "two-sided-long-edge");
338
0
    else if (pc->sides_2sided_short && !_cups_strcasecmp(choice->choice, pc->sides_2sided_short))
339
0
      ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "two-sided-short-edge");
340
0
  }
341
342
 /*
343
  * Copies...
344
  */
345
346
580
  if ((keyword = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
347
0
  {
348
0
    if (strstr(keyword, "uncollated"))
349
0
      keyword = "false";
350
0
    else
351
0
      keyword = "true";
352
0
  }
353
580
  else if ((keyword = cupsGetOption("collate", num_options, options)) == NULL)
354
580
    keyword = "true";
355
356
580
  if (format)
357
580
  {
358
580
    if (!_cups_strcasecmp(format, "image/gif") ||
359
580
  !_cups_strcasecmp(format, "image/jp2") ||
360
580
  !_cups_strcasecmp(format, "image/jpeg") ||
361
580
  !_cups_strcasecmp(format, "image/png") ||
362
580
  !_cups_strcasecmp(format, "image/tiff") ||
363
580
  !_cups_strncasecmp(format, "image/x-", 8))
364
0
    {
365
     /*
366
      * Collation makes no sense for single page image formats...
367
      */
368
369
0
      keyword = "false";
370
0
    }
371
580
    else if (!_cups_strncasecmp(format, "image/", 6) ||
372
580
       !_cups_strcasecmp(format, "application/vnd.cups-raster"))
373
0
    {
374
     /*
375
      * Multi-page image formats will have copies applied by the upstream
376
      * filters...
377
      */
378
379
0
      copies = 1;
380
0
    }
381
580
  }
382
383
580
  if (doc_handling_sup)
384
0
  {
385
0
    if (!_cups_strcasecmp(keyword, "true"))
386
0
      collate_str = "separate-documents-collated-copies";
387
0
    else
388
0
      collate_str = "separate-documents-uncollated-copies";
389
390
0
    for (i = 0; i < doc_handling_sup->num_values; i ++)
391
0
    {
392
0
      if (!strcmp(doc_handling_sup->values[i].string.text, collate_str))
393
0
      {
394
0
  ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "multiple-document-handling", NULL, collate_str);
395
0
  break;
396
0
      }
397
0
    }
398
399
0
    if (i >= doc_handling_sup->num_values)
400
0
      copies = 1;
401
0
  }
402
403
 /*
404
  * Map finishing options...
405
  */
406
407
580
  if (copies != finishings_copies)
408
0
  {
409
    // Figure out the proper job-pages-per-set value...
410
0
    if ((value = cupsGetOption("job-pages", num_options, options)) == NULL)
411
0
      value = cupsGetOption("com.apple.print.PrintSettings.PMTotalBeginPages..n.", num_options, options);
412
413
0
    if (value)
414
0
    {
415
0
      if ((job_pages = atoi(value)) < 1)
416
0
        job_pages = 1;
417
0
    }
418
419
    // Adjust for number-up
420
0
    if ((value = cupsGetOption("number-up", num_options, options)) != NULL)
421
0
    {
422
0
      if ((number_up = atoi(value)) < 1)
423
0
        number_up = 1;
424
0
    }
425
426
0
    job_pages = (job_pages + number_up - 1) / number_up;
427
428
    // When duplex printing, raster data will include an extra (blank) page to
429
    // make the total number of pages even.  Make sure this is reflected in the
430
    // page count...
431
0
    if ((job_pages & 1) && (keyword = cupsGetOption("sides", num_options, options)) != NULL && strcmp(keyword, "one-sided"))
432
0
      job_pages ++;
433
0
  }
434
435
580
  if ((finishing_template = cupsGetOption("cupsFinishingTemplate", num_options, options)) == NULL)
436
580
    finishing_template = cupsGetOption("finishing-template", num_options, options);
437
438
580
  if (finishing_template && strcmp(finishing_template, "none"))
439
0
  {
440
0
    ipp_t *fin_col = ippNew();    /* finishings-col value */
441
442
0
    ippAddString(fin_col, IPP_TAG_JOB, IPP_TAG_KEYWORD, "finishing-template", NULL, finishing_template);
443
0
    ippAddCollection(request, IPP_TAG_JOB, "finishings-col", fin_col);
444
0
    ippDelete(fin_col);
445
446
0
    if (copies != finishings_copies && job_pages > 0)
447
0
    {
448
     /*
449
      * Send job-pages-per-set attribute to apply finishings correctly...
450
      */
451
452
0
      ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", job_pages);
453
0
    }
454
0
  }
455
580
  else
456
580
  {
457
580
    num_finishings = _ppdCacheGetFinishingValues(ppd, pc, (int)(sizeof(finishings) / sizeof(finishings[0])), finishings);
458
580
    if (num_finishings > 0)
459
0
    {
460
0
      ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings", num_finishings, finishings);
461
462
0
      if (copies != finishings_copies && job_pages > 0)
463
0
      {
464
       /*
465
  * Send job-pages-per-set attribute to apply finishings correctly...
466
  */
467
468
0
  ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", job_pages);
469
0
      }
470
0
    }
471
580
  }
472
473
580
  return (copies);
474
580
}
475
476
477
/*
478
 * '_ppdCacheCreateWithFile()' - Create PPD cache and mapping data from a
479
 *                               written file.
480
 *
481
 * Use the @link _ppdCacheWriteFile@ function to write PWG mapping data to a
482
 * file.
483
 */
484
485
_ppd_cache_t *        /* O  - PPD cache and mapping data */
486
_ppdCacheCreateWithFile(
487
    const char *filename,   /* I  - File to read */
488
    ipp_t      **attrs)     /* IO - IPP attributes, if any */
489
7.36k
{
490
7.36k
  cups_file_t *fp;      /* File */
491
7.36k
  _ppd_cache_t  *pc;      /* PWG mapping data */
492
7.36k
  pwg_size_t  *size;      /* Current size */
493
7.36k
  pwg_map_t *map;     /* Current map */
494
7.36k
  _pwg_finishings_t *finishings;  /* Current finishings option */
495
7.36k
  int   linenum,    /* Current line number */
496
7.36k
    num_bins,   /* Number of bins in file */
497
7.36k
    num_sizes,    /* Number of sizes in file */
498
7.36k
    num_sources,    /* Number of sources in file */
499
7.36k
    num_types;    /* Number of types in file */
500
7.36k
  char    line[2048],   /* Current line */
501
7.36k
    *value,     /* Pointer to value in line */
502
7.36k
    *valueptr,    /* Pointer into value */
503
7.36k
    pwg_keyword[128], /* PWG keyword */
504
7.36k
    ppd_keyword[PPD_MAX_NAME];
505
          /* PPD keyword */
506
7.36k
  _pwg_print_color_mode_t print_color_mode;
507
          /* Print color mode for preset */
508
7.36k
  _pwg_print_quality_t print_quality; /* Print quality for preset */
509
510
511
7.36k
  DEBUG_printf("_ppdCacheCreateWithFile(filename=\"%s\")", filename);
512
513
 /*
514
  * Range check input...
515
  */
516
517
7.36k
  if (attrs)
518
7.36k
    *attrs = NULL;
519
520
7.36k
  if (!filename)
521
0
  {
522
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
523
0
    return (NULL);
524
0
  }
525
526
 /*
527
  * Open the file...
528
  */
529
530
7.36k
  if ((fp = cupsFileOpen(filename, "r")) == NULL)
531
0
  {
532
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
533
0
    return (NULL);
534
0
  }
535
536
 /*
537
  * Read the first line and make sure it has "#CUPS-PPD-CACHE-version" in it...
538
  */
539
540
7.36k
  if (!cupsFileGets(fp, line, sizeof(line)))
541
0
  {
542
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
543
0
    DEBUG_puts("_ppdCacheCreateWithFile: Unable to read first line.");
544
0
    cupsFileClose(fp);
545
0
    return (NULL);
546
0
  }
547
548
7.36k
  if (strncmp(line, "#CUPS-PPD-CACHE-", 16))
549
0
  {
550
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
551
0
    DEBUG_printf("_ppdCacheCreateWithFile: Wrong first line \"%s\".", line);
552
0
    cupsFileClose(fp);
553
0
    return (NULL);
554
0
  }
555
556
7.36k
  if (atoi(line + 16) != _PPD_CACHE_VERSION)
557
0
  {
558
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Out of date PPD cache file."), 1);
559
0
    DEBUG_printf("_ppdCacheCreateWithFile: Cache file has version %s, expected %d.", line + 16, _PPD_CACHE_VERSION);
560
0
    cupsFileClose(fp);
561
0
    return (NULL);
562
0
  }
563
564
 /*
565
  * Allocate the mapping data structure...
566
  */
567
568
7.36k
  if ((pc = calloc(1, sizeof(_ppd_cache_t))) == NULL)
569
0
  {
570
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
571
0
    DEBUG_puts("_ppdCacheCreateWithFile: Unable to allocate _ppd_cache_t.");
572
0
    goto create_error;
573
0
  }
574
575
7.36k
  pc->max_copies = 9999;
576
577
 /*
578
  * Read the file...
579
  */
580
581
7.36k
  linenum     = 0;
582
7.36k
  num_bins    = 0;
583
7.36k
  num_sizes   = 0;
584
7.36k
  num_sources = 0;
585
7.36k
  num_types   = 0;
586
587
91.9k
  while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
588
85.6k
  {
589
85.6k
    DEBUG_printf("_ppdCacheCreateWithFile: line=\"%s\", value=\"%s\", linenum=%d", line, value, linenum);
590
591
85.6k
    if (!value)
592
293
    {
593
293
      DEBUG_printf("_ppdCacheCreateWithFile: Missing value on line %d.", linenum);
594
293
      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
595
293
      goto create_error;
596
293
    }
597
85.3k
    else if (!_cups_strcasecmp(line, "Filter"))
598
23.9k
    {
599
23.9k
      if (!pc->filters)
600
6.41k
        pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
601
602
23.9k
      cupsArrayAdd(pc->filters, value);
603
23.9k
    }
604
61.4k
    else if (!_cups_strcasecmp(line, "PreFilter"))
605
218
    {
606
218
      if (!pc->prefilters)
607
15
        pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
608
609
218
      cupsArrayAdd(pc->prefilters, value);
610
218
    }
611
61.2k
    else if (!_cups_strcasecmp(line, "Product"))
612
744
    {
613
744
      pc->product = strdup(value);
614
744
    }
615
60.5k
    else if (!_cups_strcasecmp(line, "SingleFile"))
616
6.28k
    {
617
6.28k
      pc->single_file = !_cups_strcasecmp(value, "true");
618
6.28k
    }
619
54.2k
    else if (!_cups_strcasecmp(line, "IPP"))
620
484
    {
621
484
      off_t pos = cupsFileTell(fp), /* Position in file */
622
484
    length = strtol(value, NULL, 10);
623
          /* Length of IPP attributes */
624
625
484
      if (attrs && *attrs)
626
1
      {
627
1
        DEBUG_puts("_ppdCacheCreateWithFile: IPP listed multiple times.");
628
1
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
629
1
  goto create_error;
630
1
      }
631
483
      else if (length <= 0)
632
61
      {
633
61
        DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP length.");
634
61
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
635
61
  goto create_error;
636
61
      }
637
638
422
      if (attrs)
639
422
      {
640
       /*
641
        * Read IPP attributes into the provided variable...
642
  */
643
644
422
        *attrs = ippNew();
645
646
422
        if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL,
647
422
          *attrs) != IPP_STATE_DATA)
648
344
  {
649
344
    DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
650
344
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
651
344
    goto create_error;
652
344
  }
653
422
      }
654
0
      else
655
0
      {
656
       /*
657
        * Skip the IPP data entirely...
658
  */
659
660
0
        cupsFileSeek(fp, pos + length);
661
0
      }
662
663
78
      if (cupsFileTell(fp) != (pos + length))
664
76
      {
665
76
        DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
666
76
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
667
76
  goto create_error;
668
76
      }
669
78
    }
670
53.7k
    else if (!_cups_strcasecmp(line, "NumBins"))
671
0
    {
672
0
      if (num_bins > 0)
673
0
      {
674
0
        DEBUG_puts("_ppdCacheCreateWithFile: NumBins listed multiple times.");
675
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
676
0
  goto create_error;
677
0
      }
678
679
0
      if ((num_bins = atoi(value)) <= 0 || num_bins > 65536)
680
0
      {
681
0
        DEBUG_printf("_ppdCacheCreateWithFile: Bad NumBins value %d on line %d.", num_sizes, linenum);
682
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
683
0
  goto create_error;
684
0
      }
685
686
0
      if ((pc->bins = calloc((size_t)num_bins, sizeof(pwg_map_t))) == NULL)
687
0
      {
688
0
        DEBUG_printf("_ppdCacheCreateWithFile: Unable to allocate %d bins.", num_sizes);
689
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
690
0
  goto create_error;
691
0
      }
692
0
    }
693
53.7k
    else if (!_cups_strcasecmp(line, "Bin"))
694
2
    {
695
2
      if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
696
1
      {
697
1
        DEBUG_printf("_ppdCacheCreateWithFile: Bad Bin on line %d.", linenum);
698
1
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
699
1
  goto create_error;
700
1
      }
701
702
1
      if (pc->num_bins >= num_bins)
703
1
      {
704
1
        DEBUG_printf("_ppdCacheCreateWithFile: Too many Bin's on line %d.", linenum);
705
1
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
706
1
  goto create_error;
707
1
      }
708
709
0
      map      = pc->bins + pc->num_bins;
710
0
      map->pwg = strdup(pwg_keyword);
711
0
      map->ppd = strdup(ppd_keyword);
712
713
0
      pc->num_bins ++;
714
0
    }
715
53.7k
    else if (!_cups_strcasecmp(line, "NumSizes"))
716
7.36k
    {
717
7.36k
      if (num_sizes > 0)
718
0
      {
719
0
        DEBUG_puts("_ppdCacheCreateWithFile: NumSizes listed multiple times.");
720
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
721
0
  goto create_error;
722
0
      }
723
724
7.36k
      if ((num_sizes = atoi(value)) < 0 || num_sizes > 65536)
725
0
      {
726
0
        DEBUG_printf("_ppdCacheCreateWithFile: Bad NumSizes value %d on line %d.", num_sizes, linenum);
727
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
728
0
  goto create_error;
729
0
      }
730
731
7.36k
      if (num_sizes > 0)
732
774
      {
733
774
  if ((pc->sizes = calloc((size_t)num_sizes, sizeof(pwg_size_t))) == NULL)
734
0
  {
735
0
    DEBUG_printf("_ppdCacheCreateWithFile: Unable to allocate %d sizes.", num_sizes);
736
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
737
0
    goto create_error;
738
0
  }
739
774
      }
740
7.36k
    }
741
46.3k
    else if (!_cups_strcasecmp(line, "Size"))
742
1.83k
    {
743
1.83k
      if (pc->num_sizes >= num_sizes)
744
1
      {
745
1
        DEBUG_printf("_ppdCacheCreateWithFile: Too many Size's on line %d.", linenum);
746
1
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
747
1
  goto create_error;
748
1
      }
749
750
1.83k
      size = pc->sizes + pc->num_sizes;
751
752
1.83k
      if (sscanf(value, "%127s%40s%d%d%d%d%d%d", pwg_keyword, ppd_keyword,
753
1.83k
     &(size->width), &(size->length), &(size->left),
754
1.83k
     &(size->bottom), &(size->right), &(size->top)) != 8)
755
241
      {
756
241
        DEBUG_printf("_ppdCacheCreateWithFile: Bad Size on line %d.", linenum);
757
241
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
758
241
  goto create_error;
759
241
      }
760
761
1.59k
      size->map.pwg = strdup(pwg_keyword);
762
1.59k
      size->map.ppd = strdup(ppd_keyword);
763
764
1.59k
      pc->num_sizes ++;
765
1.59k
    }
766
44.5k
    else if (!_cups_strcasecmp(line, "CustomSize"))
767
1
    {
768
1
      if (pc->custom_max_width > 0)
769
0
      {
770
0
        DEBUG_printf("_ppdCacheCreateWithFile: Too many CustomSize's on line %d.", linenum);
771
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
772
0
  goto create_error;
773
0
      }
774
775
1
      if (sscanf(value, "%d%d%d%d%d%d%d%d", &(pc->custom_max_width),
776
1
                 &(pc->custom_max_length), &(pc->custom_min_width),
777
1
     &(pc->custom_min_length), &(pc->custom_size.left),
778
1
     &(pc->custom_size.bottom), &(pc->custom_size.right),
779
1
     &(pc->custom_size.top)) != 8)
780
1
      {
781
1
        DEBUG_printf("_ppdCacheCreateWithFile: Bad CustomSize on line %d.", linenum);
782
1
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
783
1
  goto create_error;
784
1
      }
785
786
0
      pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
787
0
            pc->custom_max_width, pc->custom_max_length, NULL);
788
0
      pc->custom_max_keyword = strdup(pwg_keyword);
789
790
0
      pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
791
0
            pc->custom_min_width, pc->custom_min_length, NULL);
792
0
      pc->custom_min_keyword = strdup(pwg_keyword);
793
0
    }
794
44.5k
    else if (!_cups_strcasecmp(line, "SourceOption"))
795
275
    {
796
275
      pc->source_option = strdup(value);
797
275
    }
798
44.2k
    else if (!_cups_strcasecmp(line, "NumSources"))
799
234
    {
800
234
      if (num_sources > 0)
801
0
      {
802
0
        DEBUG_puts("_ppdCacheCreateWithFile: NumSources listed multiple "
803
0
             "times.");
804
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
805
0
  goto create_error;
806
0
      }
807
808
234
      if ((num_sources = atoi(value)) <= 0 || num_sources > 65536)
809
0
      {
810
0
        DEBUG_printf("_ppdCacheCreateWithFile: Bad NumSources value %d on line %d.", num_sources, linenum);
811
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
812
0
  goto create_error;
813
0
      }
814
815
234
      if ((pc->sources = calloc((size_t)num_sources, sizeof(pwg_map_t))) == NULL)
816
0
      {
817
0
        DEBUG_printf("_ppdCacheCreateWithFile: Unable to allocate %d sources.", num_sources);
818
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
819
0
  goto create_error;
820
0
      }
821
234
    }
822
44.0k
    else if (!_cups_strcasecmp(line, "Source"))
823
13.9k
    {
824
13.9k
      if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
825
26
      {
826
26
        DEBUG_printf("_ppdCacheCreateWithFile: Bad Source on line %d.", linenum);
827
26
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
828
26
  goto create_error;
829
26
      }
830
831
13.9k
      if (pc->num_sources >= num_sources)
832
0
      {
833
0
        DEBUG_printf("_ppdCacheCreateWithFile: Too many Source's on line %d.", linenum);
834
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
835
0
  goto create_error;
836
0
      }
837
838
13.9k
      map      = pc->sources + pc->num_sources;
839
13.9k
      map->pwg = strdup(pwg_keyword);
840
13.9k
      map->ppd = strdup(ppd_keyword);
841
842
13.9k
      pc->num_sources ++;
843
13.9k
    }
844
30.0k
    else if (!_cups_strcasecmp(line, "NumTypes"))
845
174
    {
846
174
      if (num_types > 0)
847
0
      {
848
0
        DEBUG_puts("_ppdCacheCreateWithFile: NumTypes listed multiple times.");
849
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
850
0
  goto create_error;
851
0
      }
852
853
174
      if ((num_types = atoi(value)) <= 0 || num_types > 65536)
854
0
      {
855
0
        DEBUG_printf("_ppdCacheCreateWithFile: Bad NumTypes value %d on line %d.", num_types, linenum);
856
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
857
0
  goto create_error;
858
0
      }
859
860
174
      if ((pc->types = calloc((size_t)num_types, sizeof(pwg_map_t))) == NULL)
861
0
      {
862
0
        DEBUG_printf("_ppdCacheCreateWithFile: Unable to allocate %d types.", num_types);
863
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
864
0
  goto create_error;
865
0
      }
866
174
    }
867
29.8k
    else if (!_cups_strcasecmp(line, "Type"))
868
2.49k
    {
869
2.49k
      if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
870
41
      {
871
41
        DEBUG_printf("_ppdCacheCreateWithFile: Bad Type on line %d.", linenum);
872
41
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
873
41
  goto create_error;
874
41
      }
875
876
2.44k
      if (pc->num_types >= num_types)
877
1
      {
878
1
        DEBUG_printf("_ppdCacheCreateWithFile: Too many Type's on line %d.", linenum);
879
1
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
880
1
  goto create_error;
881
1
      }
882
883
2.44k
      map      = pc->types + pc->num_types;
884
2.44k
      map->pwg = strdup(pwg_keyword);
885
2.44k
      map->ppd = strdup(ppd_keyword);
886
887
2.44k
      pc->num_types ++;
888
2.44k
    }
889
27.3k
    else if (!_cups_strcasecmp(line, "Preset"))
890
0
    {
891
     /*
892
      * Preset output-mode print-quality name=value ...
893
      */
894
895
0
      print_color_mode = (_pwg_print_color_mode_t)strtol(value, &valueptr, 10);
896
0
      print_quality    = (_pwg_print_quality_t)strtol(valueptr, &valueptr, 10);
897
898
0
      if (print_color_mode < _PWG_PRINT_COLOR_MODE_MONOCHROME ||
899
0
          print_color_mode >= _PWG_PRINT_COLOR_MODE_MAX ||
900
0
    print_quality < _PWG_PRINT_QUALITY_DRAFT ||
901
0
    print_quality >= _PWG_PRINT_QUALITY_MAX ||
902
0
    valueptr == value || !*valueptr)
903
0
      {
904
0
        DEBUG_printf("_ppdCacheCreateWithFile: Bad Preset on line %d.", linenum);
905
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
906
0
  goto create_error;
907
0
      }
908
909
0
      pc->num_presets[print_color_mode][print_quality] =
910
0
          cupsParseOptions(valueptr, 0,
911
0
                     pc->presets[print_color_mode] + print_quality);
912
0
    }
913
27.3k
    else if (!_cups_strcasecmp(line, "SidesOption"))
914
29
      pc->sides_option = strdup(value);
915
27.3k
    else if (!_cups_strcasecmp(line, "Sides1Sided"))
916
15
      pc->sides_1sided = strdup(value);
917
27.3k
    else if (!_cups_strcasecmp(line, "Sides2SidedLong"))
918
0
      pc->sides_2sided_long = strdup(value);
919
27.3k
    else if (!_cups_strcasecmp(line, "Sides2SidedShort"))
920
0
      pc->sides_2sided_short = strdup(value);
921
27.3k
    else if (!_cups_strcasecmp(line, "Finishings"))
922
0
    {
923
0
      if (!pc->finishings)
924
0
  pc->finishings =
925
0
      cupsArrayNew3((cups_array_func_t)pwg_compare_finishings,
926
0
        NULL, NULL, 0, NULL,
927
0
        (cups_afree_func_t)pwg_free_finishings);
928
929
0
      if ((finishings = calloc(1, sizeof(_pwg_finishings_t))) == NULL)
930
0
        goto create_error;
931
932
0
      finishings->value       = (ipp_finishings_t)strtol(value, &valueptr, 10);
933
0
      finishings->num_options = cupsParseOptions(valueptr, 0,
934
0
                                                 &(finishings->options));
935
936
0
      cupsArrayAdd(pc->finishings, finishings);
937
0
    }
938
27.3k
    else if (!_cups_strcasecmp(line, "FinishingTemplate"))
939
0
    {
940
0
      if (!pc->templates)
941
0
        pc->templates = cupsArrayNew3((cups_array_func_t)_cupsArrayStrcmp, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
942
943
0
      cupsArrayAdd(pc->templates, value);
944
0
    }
945
27.3k
    else if (!_cups_strcasecmp(line, "MaxCopies"))
946
6.28k
      pc->max_copies = atoi(value);
947
21.0k
    else if (!_cups_strcasecmp(line, "ChargeInfoURI"))
948
0
      pc->charge_info_uri = strdup(value);
949
21.0k
    else if (!_cups_strcasecmp(line, "JobAccountId"))
950
6.28k
      pc->account_id = !_cups_strcasecmp(value, "true");
951
14.7k
    else if (!_cups_strcasecmp(line, "JobAccountingUserId"))
952
6.28k
      pc->accounting_user_id = !_cups_strcasecmp(value, "true");
953
8.51k
    else if (!_cups_strcasecmp(line, "JobPassword"))
954
0
      pc->password = strdup(value);
955
8.51k
    else if (!_cups_strcasecmp(line, "Mandatory"))
956
0
    {
957
0
      if (pc->mandatory)
958
0
        cupsArrayAddStrings(pc->mandatory, value, ' ');
959
0
      else
960
0
        pc->mandatory = cupsArrayNewStrings(value, ' ');
961
0
    }
962
8.51k
    else if (!_cups_strcasecmp(line, "SupportFile"))
963
0
    {
964
0
      if (!pc->support_files)
965
0
        pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
966
967
0
      cupsArrayAdd(pc->support_files, value);
968
0
    }
969
8.51k
    else
970
8.51k
    {
971
8.51k
      DEBUG_printf("_ppdCacheCreateWithFile: Unknown %s on line %d.", line, linenum);
972
8.51k
    }
973
85.6k
  }
974
975
6.28k
  if (pc->num_sizes < num_sizes)
976
0
  {
977
0
    DEBUG_printf("_ppdCacheCreateWithFile: Not enough sizes (%d < %d).", pc->num_sizes, num_sizes);
978
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
979
0
    goto create_error;
980
0
  }
981
982
6.28k
  if (pc->num_sources < num_sources)
983
0
  {
984
0
    DEBUG_printf("_ppdCacheCreateWithFile: Not enough sources (%d < %d).", pc->num_sources, num_sources);
985
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
986
0
    goto create_error;
987
0
  }
988
989
6.28k
  if (pc->num_types < num_types)
990
0
  {
991
0
    DEBUG_printf("_ppdCacheCreateWithFile: Not enough types (%d < %d).", pc->num_types, num_types);
992
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
993
0
    goto create_error;
994
0
  }
995
996
6.28k
  cupsFileClose(fp);
997
998
6.28k
  return (pc);
999
1000
 /*
1001
  * If we get here the file was bad - free any data and return...
1002
  */
1003
1004
1.08k
  create_error:
1005
1006
1.08k
  cupsFileClose(fp);
1007
1.08k
  _ppdCacheDestroy(pc);
1008
1009
1.08k
  if (attrs)
1010
1.08k
  {
1011
1.08k
    ippDelete(*attrs);
1012
1.08k
    *attrs = NULL;
1013
1.08k
  }
1014
1015
1.08k
  return (NULL);
1016
6.28k
}
1017
1018
1019
/*
1020
 * '_ppdCacheCreateWithPPD()' - Create PWG mapping data from a PPD file.
1021
 */
1022
1023
_ppd_cache_t *        /* O - PPD cache and mapping data */
1024
_ppdCacheCreateWithPPD(
1025
    cups_lang_t *langs,     /* I - Languages to load */
1026
    ppd_file_t  *ppd)     /* I - PPD file */
1027
7.36k
{
1028
7.36k
  int     i, j, k;  /* Looping vars */
1029
7.36k
  _ppd_cache_t    *pc;    /* PWG mapping data */
1030
7.36k
  ppd_option_t    *input_slot,  /* InputSlot option */
1031
7.36k
      *media_type,  /* MediaType option */
1032
7.36k
      *output_bin,  /* OutputBin option */
1033
7.36k
      *color_model, /* ColorModel option */
1034
7.36k
      *duplex,  /* Duplex option */
1035
7.36k
      *ppd_option;  /* Other PPD option */
1036
7.36k
  ppd_choice_t    *choice;  /* Current InputSlot/MediaType */
1037
7.36k
  pwg_map_t   *map;   /* Current source/type map */
1038
7.36k
  ppd_attr_t    *ppd_attr;  /* Current PPD preset attribute */
1039
7.36k
  int     num_options;  /* Number of preset options and props */
1040
7.36k
  cups_option_t   *options; /* Preset options and properties */
1041
7.36k
  ppd_size_t    *ppd_size;  /* Current PPD size */
1042
7.36k
  pwg_size_t    *pwg_size;  /* Current PWG size */
1043
7.36k
  char      pwg_keyword[3 + PPD_MAX_NAME + 1 + 12 + 1 + 12 + 3],
1044
          /* PWG keyword string */
1045
7.36k
      ppd_name[PPD_MAX_NAME];
1046
          /* Normalized PPD name */
1047
7.36k
  const char    *pwg_name;  /* Standard PWG media name */
1048
7.36k
  pwg_media_t   *pwg_media, /* PWG media data */
1049
7.36k
      pwg_mediatemp;  /* PWG media data buffer */
1050
7.36k
  _pwg_print_color_mode_t pwg_print_color_mode;
1051
          /* print-color-mode index */
1052
7.36k
  _pwg_print_quality_t  pwg_print_quality;
1053
          /* print-quality index */
1054
7.36k
  int     similar;  /* Are the old and new size similar? */
1055
7.36k
  pwg_size_t    *old_size;  /* Current old size */
1056
7.36k
  int     old_imageable,  /* Old imageable length in 2540ths */
1057
7.36k
      old_borderless, /* Old borderless state */
1058
7.36k
      old_known_pwg;  /* Old PWG name is well-known */
1059
7.36k
  int     new_width,  /* New width in 2540ths */
1060
7.36k
      new_length, /* New length in 2540ths */
1061
7.36k
      new_left, /* New left margin in 2540ths */
1062
7.36k
      new_bottom, /* New bottom margin in 2540ths */
1063
7.36k
      new_right,  /* New right margin in 2540ths */
1064
7.36k
      new_top,  /* New top margin in 2540ths */
1065
7.36k
      new_imageable,  /* New imageable length in 2540ths */
1066
7.36k
      new_borderless, /* New borderless state */
1067
7.36k
      new_known_pwg;  /* New PWG name is well-known */
1068
7.36k
  pwg_size_t    *new_size;  /* New size to add, if any */
1069
7.36k
  const char    *filter;  /* Current filter */
1070
7.36k
  _pwg_finishings_t *finishings;  /* Current finishings value */
1071
7.36k
  char      msg_id[256];  /* Message identifier */
1072
7.36k
  cups_lang_t   *lang;    /* Current language */
1073
7.36k
  _cups_message_t msg;    /* Message */
1074
1075
1076
7.36k
  DEBUG_printf("_ppdCacheCreateWithPPD(ppd=%p)", (void *)ppd);
1077
1078
 /*
1079
  * Range check input...
1080
  */
1081
1082
7.36k
  if (!ppd)
1083
0
    return (NULL);
1084
1085
 /*
1086
  * Allocate memory...
1087
  */
1088
1089
7.36k
  if ((pc = calloc(1, sizeof(_ppd_cache_t))) == NULL)
1090
0
  {
1091
0
    DEBUG_puts("_ppdCacheCreateWithPPD: Unable to allocate _ppd_cache_t.");
1092
0
    goto create_error;
1093
0
  }
1094
1095
 /*
1096
  * Copy and convert size data...
1097
  */
1098
1099
7.36k
  if (ppd->num_sizes > 0)
1100
1.41k
  {
1101
1.41k
    if ((pc->sizes = calloc((size_t)ppd->num_sizes, sizeof(pwg_size_t))) == NULL)
1102
0
    {
1103
0
      DEBUG_printf("_ppdCacheCreateWithPPD: Unable to allocate %d pwg_size_t's.", ppd->num_sizes);
1104
0
      goto create_error;
1105
0
    }
1106
1107
1.41k
    for (i = ppd->num_sizes, pwg_size = pc->sizes, ppd_size = ppd->sizes;
1108
8.12k
   i > 0;
1109
6.71k
   i --, ppd_size ++)
1110
6.71k
    {
1111
     /*
1112
      * Don't copy over custom size...
1113
      */
1114
1115
6.71k
      if (!_cups_strcasecmp(ppd_size->name, "Custom"))
1116
1.10k
  continue;
1117
1118
     /*
1119
      * Convert the PPD size name to the corresponding PWG keyword name.
1120
      */
1121
1122
5.61k
      if ((pwg_media = pwgMediaForSize(PWG_FROM_POINTS(ppd_size->width), PWG_FROM_POINTS(ppd_size->length))) != NULL)
1123
2.99k
      {
1124
       /*
1125
  * Standard name, do we have conflicts?
1126
  */
1127
1128
15.5k
  for (j = 0; j < pc->num_sizes; j ++)
1129
13.3k
    if (!strcmp(pc->sizes[j].map.pwg, pwg_media->pwg))
1130
783
    {
1131
783
      pwg_media = NULL;
1132
783
      break;
1133
783
    }
1134
2.99k
      }
1135
1136
5.61k
      if (pwg_media)
1137
2.20k
      {
1138
       /*
1139
  * Standard name and no conflicts, use it!
1140
  */
1141
1142
2.20k
  pwg_name      = pwg_media->pwg;
1143
2.20k
  new_known_pwg = 1;
1144
2.20k
      }
1145
3.40k
      else
1146
3.40k
      {
1147
       /*
1148
  * Not a standard name; convert it to a PWG vendor name of the form:
1149
  *
1150
  *     pp_lowerppd_WIDTHxHEIGHTuu
1151
  */
1152
1153
3.40k
  pwg_name      = pwg_keyword;
1154
3.40k
  new_known_pwg = 0;
1155
1156
3.40k
  pwg_unppdize_name(ppd_size->name, ppd_name, sizeof(ppd_name), "_.");
1157
3.40k
  pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), NULL, ppd_name,
1158
3.40k
        PWG_FROM_POINTS(ppd_size->width),
1159
3.40k
        PWG_FROM_POINTS(ppd_size->length), NULL);
1160
3.40k
      }
1161
1162
     /*
1163
      * If we have a similar paper with non-zero margins then we only want to
1164
      * keep it if it has a larger imageable area length.  The NULL check is for
1165
      * dimensions that are <= 0...
1166
      */
1167
1168
5.61k
      if ((pwg_media = _pwgMediaNearSize(&pwg_mediatemp, /*keyword*/NULL, /*keysize*/0, /*ppdname*/NULL, /*ppdsize*/0, PWG_FROM_POINTS(ppd_size->width), PWG_FROM_POINTS(ppd_size->length), /*epsilon*/0)) == NULL)
1169
2.62k
  continue;
1170
1171
2.99k
      new_width      = pwg_media->width;
1172
2.99k
      new_length     = pwg_media->length;
1173
2.99k
      new_left       = PWG_FROM_POINTS(ppd_size->left);
1174
2.99k
      new_bottom     = PWG_FROM_POINTS(ppd_size->bottom);
1175
2.99k
      new_right      = PWG_FROM_POINTS(ppd_size->width - ppd_size->right);
1176
2.99k
      new_top        = PWG_FROM_POINTS(ppd_size->length - ppd_size->top);
1177
2.99k
      new_imageable  = new_length - new_top - new_bottom;
1178
2.99k
      new_borderless = new_bottom == 0 && new_top == 0 &&
1179
99
           new_left == 0 && new_right == 0;
1180
1181
2.99k
      for (k = pc->num_sizes, similar = 0, old_size = pc->sizes, new_size = NULL;
1182
16.7k
     k > 0 && !similar;
1183
13.7k
     k --, old_size ++)
1184
13.7k
      {
1185
13.7k
  old_imageable  = old_size->length - old_size->top - old_size->bottom;
1186
13.7k
  old_borderless = old_size->left == 0 && old_size->bottom == 0 &&
1187
12.9k
       old_size->right == 0 && old_size->top == 0;
1188
13.7k
  old_known_pwg  = strncmp(old_size->map.pwg, "oe_", 3) &&
1189
13.5k
       strncmp(old_size->map.pwg, "om_", 3);
1190
1191
13.7k
  similar = old_borderless == new_borderless &&
1192
13.6k
      _PWG_EQUIVALENT(old_size->width, new_width) &&
1193
1.94k
      _PWG_EQUIVALENT(old_size->length, new_length);
1194
1195
13.7k
  if (similar &&
1196
705
      (new_known_pwg || (!old_known_pwg && new_imageable > old_imageable)))
1197
55
  {
1198
   /*
1199
    * The new paper has a larger imageable area so it could replace
1200
    * the older paper.  Regardless of the imageable area, we always
1201
    * prefer the size with a well-known PWG name.
1202
    */
1203
1204
55
    new_size = old_size;
1205
55
    free(old_size->map.ppd);
1206
55
    free(old_size->map.pwg);
1207
55
  }
1208
13.7k
      }
1209
1210
2.99k
      if (!similar)
1211
2.28k
      {
1212
       /*
1213
  * The paper was unique enough to deserve its own entry so add it to the
1214
  * end.
1215
  */
1216
1217
2.28k
  new_size = pwg_size ++;
1218
2.28k
  pc->num_sizes ++;
1219
2.28k
      }
1220
1221
2.99k
      if (new_size)
1222
2.34k
      {
1223
       /*
1224
  * Save this size...
1225
  */
1226
1227
2.34k
  new_size->map.ppd = strdup(ppd_size->name);
1228
2.34k
  new_size->map.pwg = strdup(pwg_name);
1229
2.34k
  new_size->width   = new_width;
1230
2.34k
  new_size->length  = new_length;
1231
2.34k
  new_size->left    = new_left;
1232
2.34k
  new_size->bottom  = new_bottom;
1233
2.34k
  new_size->right   = new_right;
1234
2.34k
  new_size->top     = new_top;
1235
2.34k
      }
1236
2.99k
    }
1237
1.41k
  }
1238
1239
7.36k
  if (ppd->variable_sizes)
1240
84
  {
1241
   /*
1242
    * Generate custom size data...
1243
    */
1244
1245
84
    pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
1246
84
          PWG_FROM_POINTS(ppd->custom_max[0]),
1247
84
          PWG_FROM_POINTS(ppd->custom_max[1]), NULL);
1248
84
    pc->custom_max_keyword = strdup(pwg_keyword);
1249
84
    pc->custom_max_width   = PWG_FROM_POINTS(ppd->custom_max[0]);
1250
84
    pc->custom_max_length  = PWG_FROM_POINTS(ppd->custom_max[1]);
1251
1252
84
    pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
1253
84
          PWG_FROM_POINTS(ppd->custom_min[0]),
1254
84
          PWG_FROM_POINTS(ppd->custom_min[1]), NULL);
1255
84
    pc->custom_min_keyword = strdup(pwg_keyword);
1256
84
    pc->custom_min_width   = PWG_FROM_POINTS(ppd->custom_min[0]);
1257
84
    pc->custom_min_length  = PWG_FROM_POINTS(ppd->custom_min[1]);
1258
1259
84
    pc->custom_size.left   = PWG_FROM_POINTS(ppd->custom_margins[0]);
1260
84
    pc->custom_size.bottom = PWG_FROM_POINTS(ppd->custom_margins[1]);
1261
84
    pc->custom_size.right  = PWG_FROM_POINTS(ppd->custom_margins[2]);
1262
84
    pc->custom_size.top    = PWG_FROM_POINTS(ppd->custom_margins[3]);
1263
84
  }
1264
1265
 /*
1266
  * Copy and convert InputSlot data...
1267
  */
1268
1269
7.36k
  if ((input_slot = ppdFindOption(ppd, "InputSlot")) == NULL)
1270
7.07k
    input_slot = ppdFindOption(ppd, "HPPaperSource");
1271
1272
7.36k
  if (input_slot)
1273
292
  {
1274
292
    pc->source_option = strdup(input_slot->keyword);
1275
1276
292
    if ((pc->sources = calloc((size_t)input_slot->num_choices, sizeof(pwg_map_t))) == NULL)
1277
0
    {
1278
0
      DEBUG_printf("_ppdCacheCreateWithPPD: Unable to allocate %d pwg_map_t's for InputSlot.", input_slot->num_choices);
1279
0
      goto create_error;
1280
0
    }
1281
1282
292
    pc->num_sources = input_slot->num_choices;
1283
1284
292
    for (i = input_slot->num_choices, choice = input_slot->choices,
1285
292
             map = pc->sources;
1286
18.7k
   i > 0;
1287
18.4k
   i --, choice ++, map ++)
1288
18.4k
    {
1289
18.4k
      if (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
1290
18.2k
          !_cups_strncasecmp(choice->text, "Auto", 4) ||
1291
17.9k
          !_cups_strcasecmp(choice->choice, "Default") ||
1292
17.8k
          !_cups_strcasecmp(choice->text, "Default"))
1293
661
        pwg_name = "auto";
1294
17.7k
      else if (!_cups_strcasecmp(choice->choice, "Cassette"))
1295
0
        pwg_name = "main";
1296
17.7k
      else if (!_cups_strcasecmp(choice->choice, "PhotoTray"))
1297
0
        pwg_name = "photo";
1298
17.7k
      else if (!_cups_strcasecmp(choice->choice, "CDTray"))
1299
0
        pwg_name = "disc";
1300
17.7k
      else if (!_cups_strncasecmp(choice->choice, "Multipurpose", 12) ||
1301
17.7k
               !_cups_strcasecmp(choice->choice, "MP") ||
1302
17.7k
               !_cups_strcasecmp(choice->choice, "MPTray"))
1303
77
        pwg_name = "by-pass-tray";
1304
17.7k
      else if (!_cups_strcasecmp(choice->choice, "LargeCapacity"))
1305
0
        pwg_name = "large-capacity";
1306
17.7k
      else if (!_cups_strncasecmp(choice->choice, "Lower", 5))
1307
0
        pwg_name = "bottom";
1308
17.7k
      else if (!_cups_strncasecmp(choice->choice, "Middle", 6))
1309
0
        pwg_name = "middle";
1310
17.7k
      else if (!_cups_strncasecmp(choice->choice, "Upper", 5))
1311
0
        pwg_name = "top";
1312
17.7k
      else if (!_cups_strncasecmp(choice->choice, "Side", 4))
1313
91
        pwg_name = "side";
1314
17.6k
      else if (!_cups_strcasecmp(choice->choice, "Roll"))
1315
0
        pwg_name = "main-roll";
1316
17.6k
      else if (!_cups_strcasecmp(choice->choice, "0"))
1317
461
        pwg_name = "tray-1";
1318
17.1k
      else if (!_cups_strcasecmp(choice->choice, "1"))
1319
262
        pwg_name = "tray-2";
1320
16.9k
      else if (!_cups_strcasecmp(choice->choice, "2"))
1321
4.37k
        pwg_name = "tray-3";
1322
12.5k
      else if (!_cups_strcasecmp(choice->choice, "3"))
1323
622
        pwg_name = "tray-4";
1324
11.9k
      else if (!_cups_strcasecmp(choice->choice, "4"))
1325
821
        pwg_name = "tray-5";
1326
11.0k
      else if (!_cups_strcasecmp(choice->choice, "5"))
1327
6.07k
        pwg_name = "tray-6";
1328
5.02k
      else if (!_cups_strcasecmp(choice->choice, "6"))
1329
1.40k
        pwg_name = "tray-7";
1330
3.61k
      else if (!_cups_strcasecmp(choice->choice, "7"))
1331
678
        pwg_name = "tray-8";
1332
2.93k
      else if (!_cups_strcasecmp(choice->choice, "8"))
1333
284
        pwg_name = "tray-9";
1334
2.65k
      else if (!_cups_strcasecmp(choice->choice, "9"))
1335
237
        pwg_name = "tray-10";
1336
2.41k
      else
1337
2.41k
      {
1338
       /*
1339
        * Convert PPD name to lowercase...
1340
  */
1341
1342
2.41k
        pwg_name = pwg_keyword;
1343
2.41k
  pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword),
1344
2.41k
                    "_");
1345
2.41k
      }
1346
1347
18.4k
      map->pwg = strdup(pwg_name);
1348
18.4k
      map->ppd = strdup(choice->choice);
1349
1350
     /*
1351
      * Add localized text for PWG keyword to message catalog...
1352
      */
1353
1354
18.4k
      snprintf(msg_id, sizeof(msg_id), "media-source.%s", pwg_name);
1355
18.4k
      ppd_get_strings(ppd, langs, "InputSlot", choice, msg_id);
1356
18.4k
    }
1357
292
  }
1358
1359
 /*
1360
  * Copy and convert MediaType data...
1361
  */
1362
1363
7.36k
  if ((media_type = ppdFindOption(ppd, "MediaType")) != NULL)
1364
197
  {
1365
197
    static const struct
1366
197
    {
1367
197
      const char *ppd_name;   /* PPD MediaType name or prefix to match */
1368
197
      int        match_length;    /* Length of prefix, or -1 to match entire string */
1369
197
      const char *pwg_name;   /* Registered PWG media-type name to use */
1370
197
    } standard_types[] = {
1371
197
      {"Auto", 4, "auto"},
1372
197
      {"Any", -1, "auto"},
1373
197
      {"Default", -1, "auto"},
1374
197
      {"Card", 4, "cardstock"},
1375
197
      {"Env", 3, "envelope"},
1376
197
      {"Gloss", 5, "photographic-glossy"},
1377
197
      {"HighGloss", -1, "photographic-high-gloss"},
1378
197
      {"Matte", -1, "photographic-matte"},
1379
197
      {"Plain", 5, "stationery"},
1380
197
      {"Coated", 6, "stationery-coated"},
1381
197
      {"Inkjet", -1, "stationery-inkjet"},
1382
197
      {"Letterhead", -1, "stationery-letterhead"},
1383
197
      {"Preprint", 8, "stationery-preprinted"},
1384
197
      {"Recycled", -1, "stationery-recycled"},
1385
197
      {"Transparen", 10, "transparency"},
1386
197
    };
1387
197
    const int num_standard_types = (int)(sizeof(standard_types) / sizeof(standard_types[0]));
1388
          /* Length of the standard_types array */
1389
197
    int match_counts[sizeof(standard_types) / sizeof(standard_types[0])] = {0};
1390
          /* Number of matches for each standard type */
1391
1392
197
    if ((pc->types = calloc((size_t)media_type->num_choices, sizeof(pwg_map_t))) == NULL)
1393
0
    {
1394
0
      DEBUG_printf("_ppdCacheCreateWithPPD: Unable to allocate %d pwg_map_t's for MediaType.", media_type->num_choices);
1395
0
      goto create_error;
1396
0
    }
1397
1398
197
    pc->num_types = media_type->num_choices;
1399
1400
197
    for (i = media_type->num_choices, choice = media_type->choices,
1401
197
             map = pc->types;
1402
4.63k
   i > 0;
1403
4.43k
   i --, choice ++, map ++)
1404
4.43k
    {
1405
4.43k
      pwg_name = NULL;
1406
1407
70.9k
      for (j = 0; j < num_standard_types; j ++)
1408
66.5k
      {
1409
66.5k
        if (standard_types[j].match_length <= 0)
1410
31.0k
        {
1411
31.0k
          if (!_cups_strcasecmp(choice->choice, standard_types[j].ppd_name))
1412
308
          {
1413
308
            pwg_name = standard_types[j].pwg_name;
1414
308
            match_counts[j] ++;
1415
308
          }
1416
31.0k
        }
1417
35.4k
        else if (!_cups_strncasecmp(choice->choice, standard_types[j].ppd_name, (size_t)standard_types[j].match_length))
1418
1.46k
        {
1419
1.46k
          pwg_name = standard_types[j].pwg_name;
1420
1.46k
          match_counts[j] ++;
1421
1.46k
        }
1422
66.5k
      }
1423
1424
4.43k
      if (!pwg_name)
1425
2.66k
      {
1426
       /*
1427
        * Convert PPD name to lowercase...
1428
  */
1429
1430
2.66k
        pwg_name = pwg_keyword;
1431
2.66k
  pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword),
1432
2.66k
                    "_");
1433
2.66k
      }
1434
1435
4.43k
      map->pwg = strdup(pwg_name);
1436
4.43k
      map->ppd = strdup(choice->choice);
1437
4.43k
    }
1438
1439
   /*
1440
    * Since three PPD name patterns can map to "auto", their match counts
1441
    * should each be the count of all three combined.
1442
    */
1443
1444
197
    i = match_counts[0] + match_counts[1] + match_counts[2];
1445
197
    match_counts[0] = match_counts[1] = match_counts[2] = i;
1446
1447
197
    for (i = 0, choice = media_type->choices, map = pc->types;
1448
4.63k
      i < media_type->num_choices;
1449
4.43k
      i ++, choice ++, map ++)
1450
4.43k
    {
1451
     /*
1452
      * If there are two matches for any standard PWG media type, don't give
1453
      * the PWG name to either one.
1454
      */
1455
1456
70.9k
      for (j = 0; j < num_standard_types; j ++)
1457
66.5k
      {
1458
66.5k
        if (match_counts[j] > 1 && !strcmp(map->pwg, standard_types[j].pwg_name))
1459
1.76k
        {
1460
1.76k
          free(map->pwg);
1461
1.76k
          pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword), "_");
1462
1.76k
          map->pwg = strdup(pwg_keyword);
1463
1.76k
        }
1464
66.5k
      }
1465
1466
     /*
1467
      * Add localized text for PWG keyword to message catalog...
1468
      */
1469
1470
4.43k
      snprintf(msg_id, sizeof(msg_id), "media-type.%s", map->pwg);
1471
4.43k
      ppd_get_strings(ppd, langs, "MediaType", choice, msg_id);
1472
4.43k
    }
1473
197
  }
1474
1475
 /*
1476
  * Copy and convert OutputBin data...
1477
  */
1478
1479
7.36k
  if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL)
1480
0
  {
1481
0
    if ((pc->bins = calloc((size_t)output_bin->num_choices, sizeof(pwg_map_t))) == NULL)
1482
0
    {
1483
0
      DEBUG_printf("_ppdCacheCreateWithPPD: Unable to allocate %d pwg_map_t's for OutputBin.", output_bin->num_choices);
1484
0
      goto create_error;
1485
0
    }
1486
1487
0
    pc->num_bins = output_bin->num_choices;
1488
1489
0
    for (i = output_bin->num_choices, choice = output_bin->choices,
1490
0
             map = pc->bins;
1491
0
   i > 0;
1492
0
   i --, choice ++, map ++)
1493
0
    {
1494
0
      pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword), "_");
1495
1496
0
      map->pwg = strdup(pwg_keyword);
1497
0
      map->ppd = strdup(choice->choice);
1498
1499
     /*
1500
      * Add localized text for PWG keyword to message catalog...
1501
      */
1502
1503
0
      snprintf(msg_id, sizeof(msg_id), "output-bin.%s", pwg_keyword);
1504
0
      ppd_get_strings(ppd, langs, "OutputBin", choice, msg_id);
1505
0
    }
1506
0
  }
1507
1508
7.36k
  if ((ppd_attr = ppdFindAttr(ppd, "APPrinterPreset", NULL)) != NULL)
1509
0
  {
1510
   /*
1511
    * Copy and convert APPrinterPreset (output-mode + print-quality) data...
1512
    */
1513
1514
0
    const char  *quality,   /* com.apple.print.preset.quality value */
1515
0
    *output_mode,   /* com.apple.print.preset.output-mode value */
1516
0
    *color_model_val, /* ColorModel choice */
1517
0
    *graphicsType,    /* com.apple.print.preset.graphicsType value */
1518
0
    *media_front_coating; /* com.apple.print.preset.media-front-coating value */
1519
1520
0
    do
1521
0
    {
1522
#if 0
1523
     /*
1524
      * Add localized text for PWG keyword to message catalog...
1525
      */
1526
1527
      snprintf(msg_id, sizeof(msg_id), "preset-name.%s", ppd_attr->spec);
1528
      pwg_add_message(pc->strings, msg_id, ppd_attr->text);
1529
#endif // 0
1530
1531
     /*
1532
      * Get the options for this preset...
1533
      */
1534
1535
0
      num_options = _ppdParseOptions(ppd_attr->value, 0, &options,
1536
0
                                     _PPD_PARSE_ALL);
1537
1538
0
      if ((quality = cupsGetOption("com.apple.print.preset.quality",
1539
0
                                   num_options, options)) != NULL)
1540
0
      {
1541
       /*
1542
        * Get the print-quality for this preset...
1543
  */
1544
1545
0
  if (!strcmp(quality, "low"))
1546
0
    pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
1547
0
  else if (!strcmp(quality, "high"))
1548
0
    pwg_print_quality = _PWG_PRINT_QUALITY_HIGH;
1549
0
  else
1550
0
    pwg_print_quality = _PWG_PRINT_QUALITY_NORMAL;
1551
1552
       /*
1553
  * Ignore graphicsType "Photo" presets that are not high quality.
1554
  */
1555
1556
0
  graphicsType = cupsGetOption("com.apple.print.preset.graphicsType",
1557
0
              num_options, options);
1558
1559
0
  if (pwg_print_quality != _PWG_PRINT_QUALITY_HIGH && graphicsType &&
1560
0
      !strcmp(graphicsType, "Photo"))
1561
0
    continue;
1562
1563
       /*
1564
  * Ignore presets for normal and draft quality where the coating
1565
  * isn't "none" or "autodetect".
1566
  */
1567
1568
0
  media_front_coating = cupsGetOption(
1569
0
                            "com.apple.print.preset.media-front-coating",
1570
0
                num_options, options);
1571
1572
0
        if (pwg_print_quality != _PWG_PRINT_QUALITY_HIGH &&
1573
0
      media_front_coating &&
1574
0
      strcmp(media_front_coating, "none") &&
1575
0
      strcmp(media_front_coating, "autodetect"))
1576
0
    continue;
1577
1578
       /*
1579
        * Get the output mode for this preset...
1580
  */
1581
1582
0
        output_mode     = cupsGetOption("com.apple.print.preset.output-mode",
1583
0
                                  num_options, options);
1584
0
        color_model_val = cupsGetOption("ColorModel", num_options, options);
1585
1586
0
        if (output_mode)
1587
0
  {
1588
0
    if (!strcmp(output_mode, "monochrome"))
1589
0
      pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
1590
0
    else
1591
0
      pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1592
0
  }
1593
0
  else if (color_model_val)
1594
0
  {
1595
0
    if (!_cups_strcasecmp(color_model_val, "Gray"))
1596
0
      pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
1597
0
    else
1598
0
      pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1599
0
  }
1600
0
  else
1601
0
    pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
1602
1603
       /*
1604
        * Save the options for this combination as needed...
1605
  */
1606
1607
0
        if (!pc->num_presets[pwg_print_color_mode][pwg_print_quality])
1608
0
    pc->num_presets[pwg_print_color_mode][pwg_print_quality] =
1609
0
        _ppdParseOptions(ppd_attr->value, 0,
1610
0
                         pc->presets[pwg_print_color_mode] +
1611
0
                 pwg_print_quality, _PPD_PARSE_OPTIONS);
1612
0
      }
1613
1614
0
      cupsFreeOptions(num_options, options);
1615
0
    }
1616
0
    while ((ppd_attr = ppdFindNextAttr(ppd, "APPrinterPreset", NULL)) != NULL);
1617
0
  }
1618
1619
7.36k
  if (!pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_DRAFT] &&
1620
7.36k
      !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_NORMAL] &&
1621
7.36k
      !pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][_PWG_PRINT_QUALITY_HIGH])
1622
7.36k
  {
1623
   /*
1624
    * Try adding some common color options to create grayscale presets.  These
1625
    * are listed in order of popularity...
1626
    */
1627
1628
7.36k
    const char  *color_option = NULL, /* Color control option */
1629
7.36k
    *gray_choice = NULL; /* Choice to select grayscale */
1630
1631
7.36k
    if ((color_model = ppdFindOption(ppd, "ColorModel")) != NULL &&
1632
53
        ppdFindChoice(color_model, "Gray"))
1633
0
    {
1634
0
      color_option = "ColorModel";
1635
0
      gray_choice  = "Gray";
1636
0
    }
1637
7.36k
    else if ((color_model = ppdFindOption(ppd, "HPColorMode")) != NULL &&
1638
0
             ppdFindChoice(color_model, "grayscale"))
1639
0
    {
1640
0
      color_option = "HPColorMode";
1641
0
      gray_choice  = "grayscale";
1642
0
    }
1643
7.36k
    else if ((color_model = ppdFindOption(ppd, "BRMonoColor")) != NULL &&
1644
0
             ppdFindChoice(color_model, "Mono"))
1645
0
    {
1646
0
      color_option = "BRMonoColor";
1647
0
      gray_choice  = "Mono";
1648
0
    }
1649
7.36k
    else if ((color_model = ppdFindOption(ppd, "CNIJSGrayScale")) != NULL &&
1650
0
             ppdFindChoice(color_model, "1"))
1651
0
    {
1652
0
      color_option = "CNIJSGrayScale";
1653
0
      gray_choice  = "1";
1654
0
    }
1655
7.36k
    else if ((color_model = ppdFindOption(ppd, "HPColorAsGray")) != NULL &&
1656
0
             ppdFindChoice(color_model, "True"))
1657
0
    {
1658
0
      color_option = "HPColorAsGray";
1659
0
      gray_choice  = "True";
1660
0
    }
1661
1662
7.36k
    if (color_option && gray_choice)
1663
0
    {
1664
     /*
1665
      * Copy and convert ColorModel (output-mode) data...
1666
      */
1667
1668
0
      cups_option_t *coption, /* Color option */
1669
0
        *moption; /* Monochrome option */
1670
1671
0
      for (pwg_print_quality = _PWG_PRINT_QUALITY_DRAFT;
1672
0
     pwg_print_quality < _PWG_PRINT_QUALITY_MAX;
1673
0
     pwg_print_quality ++)
1674
0
      {
1675
0
  if (pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_print_quality])
1676
0
  {
1677
   /*
1678
    * Copy the color options...
1679
    */
1680
1681
0
    num_options = pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR]
1682
0
          [pwg_print_quality];
1683
0
    options     = calloc((size_t)num_options, sizeof(cups_option_t));
1684
1685
0
    if (options)
1686
0
    {
1687
0
      for (i = num_options, moption = options,
1688
0
         coption = pc->presets[_PWG_PRINT_COLOR_MODE_COLOR]
1689
0
             [pwg_print_quality];
1690
0
     i > 0;
1691
0
     i --, moption ++, coption ++)
1692
0
      {
1693
0
        moption->name  = _cupsStrRetain(coption->name);
1694
0
        moption->value = _cupsStrRetain(coption->value);
1695
0
      }
1696
1697
0
      pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1698
0
    num_options;
1699
0
      pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1700
0
    options;
1701
0
    }
1702
0
  }
1703
0
  else if (pwg_print_quality != _PWG_PRINT_QUALITY_NORMAL)
1704
0
    continue;
1705
1706
       /*
1707
  * Add the grayscale option to the preset...
1708
  */
1709
1710
0
  pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME][pwg_print_quality] =
1711
0
      cupsAddOption(color_option, gray_choice,
1712
0
        pc->num_presets[_PWG_PRINT_COLOR_MODE_MONOCHROME]
1713
0
            [pwg_print_quality],
1714
0
        pc->presets[_PWG_PRINT_COLOR_MODE_MONOCHROME] +
1715
0
            pwg_print_quality);
1716
0
      }
1717
0
    }
1718
7.36k
  }
1719
1720
 /*
1721
  * Copy and convert Duplex (sides) data...
1722
  */
1723
1724
7.36k
  if ((duplex = ppdFindOption(ppd, "Duplex")) == NULL)
1725
7.33k
    if ((duplex = ppdFindOption(ppd, "JCLDuplex")) == NULL)
1726
7.33k
      if ((duplex = ppdFindOption(ppd, "EFDuplex")) == NULL)
1727
7.33k
        if ((duplex = ppdFindOption(ppd, "EFDuplexing")) == NULL)
1728
7.33k
    duplex = ppdFindOption(ppd, "KD03Duplex");
1729
1730
7.36k
  if (duplex)
1731
33
  {
1732
33
    pc->sides_option = strdup(duplex->keyword);
1733
1734
33
    for (i = duplex->num_choices, choice = duplex->choices;
1735
828
         i > 0;
1736
795
   i --, choice ++)
1737
795
    {
1738
795
      if ((!_cups_strcasecmp(choice->choice, "None") ||
1739
795
     !_cups_strcasecmp(choice->choice, "False")) && !pc->sides_1sided)
1740
19
        pc->sides_1sided = strdup(choice->choice);
1741
776
      else if ((!_cups_strcasecmp(choice->choice, "DuplexNoTumble") ||
1742
776
          !_cups_strcasecmp(choice->choice, "LongEdge") ||
1743
776
          !_cups_strcasecmp(choice->choice, "Top")) && !pc->sides_2sided_long)
1744
0
        pc->sides_2sided_long = strdup(choice->choice);
1745
776
      else if ((!_cups_strcasecmp(choice->choice, "DuplexTumble") ||
1746
776
          !_cups_strcasecmp(choice->choice, "ShortEdge") ||
1747
776
          !_cups_strcasecmp(choice->choice, "Bottom")) &&
1748
0
         !pc->sides_2sided_short)
1749
0
        pc->sides_2sided_short = strdup(choice->choice);
1750
795
    }
1751
33
  }
1752
1753
 /*
1754
  * Copy filters and pre-filters...
1755
  */
1756
1757
7.36k
  pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
1758
1759
7.36k
  cupsArrayAdd(pc->filters,
1760
7.36k
               "application/vnd.cups-raw application/octet-stream 0 -");
1761
1762
7.36k
  if ((ppd_attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) != NULL)
1763
145
  {
1764
145
    do
1765
818
    {
1766
818
      cupsArrayAdd(pc->filters, ppd_attr->value);
1767
818
    }
1768
818
    while ((ppd_attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
1769
145
  }
1770
7.22k
  else if (ppd->num_filters > 0)
1771
313
  {
1772
8.94k
    for (i = 0; i < ppd->num_filters; i ++)
1773
8.62k
      cupsArrayAdd(pc->filters, ppd->filters[i]);
1774
313
  }
1775
6.91k
  else
1776
6.91k
    cupsArrayAdd(pc->filters, "application/vnd.cups-postscript 0 -");
1777
1778
 /*
1779
  * See if we have a command filter...
1780
  */
1781
1782
7.36k
  for (filter = (const char *)cupsArrayFirst(pc->filters);
1783
31.0k
       filter;
1784
23.7k
       filter = (const char *)cupsArrayNext(pc->filters))
1785
23.7k
    if (!_cups_strncasecmp(filter, "application/vnd.cups-command", 28) &&
1786
0
        _cups_isspace(filter[28]))
1787
0
      break;
1788
1789
7.36k
  if (!filter &&
1790
7.36k
      ((ppd_attr = ppdFindAttr(ppd, "cupsCommands", NULL)) == NULL ||
1791
0
       _cups_strcasecmp(ppd_attr->value, "none")))
1792
7.36k
  {
1793
   /*
1794
    * No command filter and no cupsCommands keyword telling us not to use one.
1795
    * See if this is a PostScript printer, and if so add a PostScript command
1796
    * filter...
1797
    */
1798
1799
7.36k
    for (filter = (const char *)cupsArrayFirst(pc->filters);
1800
24.1k
   filter;
1801
16.8k
   filter = (const char *)cupsArrayNext(pc->filters))
1802
23.7k
      if (!_cups_strncasecmp(filter, "application/vnd.cups-postscript", 31) &&
1803
6.91k
    _cups_isspace(filter[31]))
1804
6.91k
  break;
1805
1806
7.36k
    if (filter)
1807
6.91k
      cupsArrayAdd(pc->filters,
1808
6.91k
                   "application/vnd.cups-command application/postscript 100 "
1809
6.91k
                   "commandtops");
1810
7.36k
  }
1811
1812
7.36k
  if ((ppd_attr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL)
1813
17
  {
1814
17
    pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
1815
1816
17
    do
1817
228
    {
1818
228
      cupsArrayAdd(pc->prefilters, ppd_attr->value);
1819
228
    }
1820
228
    while ((ppd_attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL)) != NULL);
1821
17
  }
1822
1823
7.36k
  if ((ppd_attr = ppdFindAttr(ppd, "cupsSingleFile", NULL)) != NULL)
1824
0
    pc->single_file = !_cups_strcasecmp(ppd_attr->value, "true");
1825
1826
 /*
1827
  * Copy the product string, if any...
1828
  */
1829
1830
7.36k
  if (ppd->product)
1831
725
    pc->product = strdup(ppd->product);
1832
1833
 /*
1834
  * Copy finishings mapping data...
1835
  */
1836
1837
7.36k
  if ((ppd_attr = ppdFindAttr(ppd, "cupsIPPFinishings", NULL)) != NULL)
1838
0
  {
1839
   /*
1840
    * Have proper vendor mapping of IPP finishings values to PPD options...
1841
    */
1842
1843
0
    pc->finishings = cupsArrayNew3((cups_array_func_t)pwg_compare_finishings,
1844
0
                                   NULL, NULL, 0, NULL,
1845
0
                                   (cups_afree_func_t)pwg_free_finishings);
1846
1847
0
    do
1848
0
    {
1849
0
      if ((finishings = calloc(1, sizeof(_pwg_finishings_t))) == NULL)
1850
0
        goto create_error;
1851
1852
0
      finishings->value       = (ipp_finishings_t)atoi(ppd_attr->spec);
1853
0
      finishings->num_options = _ppdParseOptions(ppd_attr->value, 0,
1854
0
                                                 &(finishings->options),
1855
0
                                                 _PPD_PARSE_OPTIONS);
1856
1857
0
      cupsArrayAdd(pc->finishings, finishings);
1858
0
    }
1859
0
    while ((ppd_attr = ppdFindNextAttr(ppd, "cupsIPPFinishings",
1860
0
                                       NULL)) != NULL);
1861
0
  }
1862
7.36k
  else
1863
7.36k
  {
1864
   /*
1865
    * No IPP mapping data, try to map common/standard PPD keywords...
1866
    */
1867
1868
7.36k
    pc->finishings = cupsArrayNew3((cups_array_func_t)pwg_compare_finishings, NULL, NULL, 0, NULL, (cups_afree_func_t)pwg_free_finishings);
1869
1870
7.36k
    if ((ppd_option = ppdFindOption(ppd, "StapleLocation")) != NULL)
1871
0
    {
1872
     /*
1873
      * Add staple finishings...
1874
      */
1875
1876
0
      if (ppdFindChoice(ppd_option, "SinglePortrait"))
1877
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_LEFT, "StapleLocation", "SinglePortrait");
1878
0
      if (ppdFindChoice(ppd_option, "UpperLeft")) /* Ricoh extension */
1879
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_LEFT, "StapleLocation", "UpperLeft");
1880
0
      if (ppdFindChoice(ppd_option, "UpperRight")) /* Ricoh extension */
1881
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_TOP_RIGHT, "StapleLocation", "UpperRight");
1882
0
      if (ppdFindChoice(ppd_option, "SingleLandscape"))
1883
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_BOTTOM_LEFT, "StapleLocation", "SingleLandscape");
1884
0
      if (ppdFindChoice(ppd_option, "DualLandscape"))
1885
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_STAPLE_DUAL_LEFT, "StapleLocation", "DualLandscape");
1886
0
    }
1887
1888
7.36k
    if ((ppd_option = ppdFindOption(ppd, "RIPunch")) != NULL)
1889
0
    {
1890
     /*
1891
      * Add (Ricoh) punch finishings...
1892
      */
1893
1894
0
      if (ppdFindChoice(ppd_option, "Left2"))
1895
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_DUAL_LEFT, "RIPunch", "Left2");
1896
0
      if (ppdFindChoice(ppd_option, "Left3"))
1897
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_TRIPLE_LEFT, "RIPunch", "Left3");
1898
0
      if (ppdFindChoice(ppd_option, "Left4"))
1899
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_QUAD_LEFT, "RIPunch", "Left4");
1900
0
      if (ppdFindChoice(ppd_option, "Right2"))
1901
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_DUAL_RIGHT, "RIPunch", "Right2");
1902
0
      if (ppdFindChoice(ppd_option, "Right3"))
1903
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_TRIPLE_RIGHT, "RIPunch", "Right3");
1904
0
      if (ppdFindChoice(ppd_option, "Right4"))
1905
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_QUAD_RIGHT, "RIPunch", "Right4");
1906
0
      if (ppdFindChoice(ppd_option, "Upper2"))
1907
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_DUAL_TOP, "RIPunch", "Upper2");
1908
0
      if (ppdFindChoice(ppd_option, "Upper3"))
1909
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_TRIPLE_TOP, "RIPunch", "Upper3");
1910
0
      if (ppdFindChoice(ppd_option, "Upper4"))
1911
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_PUNCH_QUAD_TOP, "RIPunch", "Upper4");
1912
0
    }
1913
1914
7.36k
    if ((ppd_option = ppdFindOption(ppd, "BindEdge")) != NULL)
1915
0
    {
1916
     /*
1917
      * Add bind finishings...
1918
      */
1919
1920
0
      if (ppdFindChoice(ppd_option, "Left"))
1921
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_LEFT, "BindEdge", "Left");
1922
0
      if (ppdFindChoice(ppd_option, "Right"))
1923
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_RIGHT, "BindEdge", "Right");
1924
0
      if (ppdFindChoice(ppd_option, "Top"))
1925
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_TOP, "BindEdge", "Top");
1926
0
      if (ppdFindChoice(ppd_option, "Bottom"))
1927
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_BIND_BOTTOM, "BindEdge", "Bottom");
1928
0
    }
1929
1930
7.36k
    if ((ppd_option = ppdFindOption(ppd, "FoldType")) != NULL)
1931
0
    {
1932
     /*
1933
      * Add (Adobe) fold finishings...
1934
      */
1935
1936
0
      if (ppdFindChoice(ppd_option, "ZFold"))
1937
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_Z, "FoldType", "ZFold");
1938
0
      if (ppdFindChoice(ppd_option, "Saddle"))
1939
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_HALF, "FoldType", "Saddle");
1940
0
      if (ppdFindChoice(ppd_option, "DoubleGate"))
1941
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_DOUBLE_GATE, "FoldType", "DoubleGate");
1942
0
      if (ppdFindChoice(ppd_option, "LeftGate"))
1943
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LEFT_GATE, "FoldType", "LeftGate");
1944
0
      if (ppdFindChoice(ppd_option, "RightGate"))
1945
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_RIGHT_GATE, "FoldType", "RightGate");
1946
0
      if (ppdFindChoice(ppd_option, "Letter"))
1947
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LETTER, "FoldType", "Letter");
1948
0
      if (ppdFindChoice(ppd_option, "XFold"))
1949
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_POSTER, "FoldType", "XFold");
1950
0
    }
1951
1952
7.36k
    if ((ppd_option = ppdFindOption(ppd, "RIFoldType")) != NULL)
1953
0
    {
1954
     /*
1955
      * Add (Ricoh) fold finishings...
1956
      */
1957
1958
0
      if (ppdFindChoice(ppd_option, "OutsideTwoFold"))
1959
0
        pwg_add_finishing(pc->finishings, IPP_FINISHINGS_FOLD_LETTER, "RIFoldType", "OutsideTwoFold");
1960
0
    }
1961
1962
7.36k
    if (cupsArrayCount(pc->finishings) == 0)
1963
7.36k
    {
1964
7.36k
      cupsArrayDelete(pc->finishings);
1965
7.36k
      pc->finishings = NULL;
1966
7.36k
    }
1967
7.36k
  }
1968
1969
7.36k
  if ((ppd_option = ppdFindOption(ppd, "cupsFinishingTemplate")) != NULL)
1970
0
  {
1971
0
    pc->templates = cupsArrayNew3((cups_array_func_t)_cupsArrayStrcmp, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
1972
1973
0
    for (choice = ppd_option->choices, i = ppd_option->num_choices; i > 0; choice ++, i --)
1974
0
    {
1975
0
      cupsArrayAdd(pc->templates, (void *)choice->choice);
1976
1977
     /*
1978
      * Add localized text for PWG keyword to message catalog...
1979
      */
1980
1981
0
      snprintf(msg_id, sizeof(msg_id), "finishing-template.%s", choice->choice);
1982
0
      msg.msg = msg_id;
1983
1984
0
      for (lang = langs; lang; lang = lang->next)
1985
0
      {
1986
        // See if the string is already localized...
1987
0
        if (cupsArrayFind(lang->strings, &msg))
1988
0
          continue;     // Yes
1989
1990
        // Otherwise add the text...
1991
0
        if (!strcmp(lang->language, "en"))
1992
0
        {
1993
          // English
1994
0
          msg.str = choice->text;
1995
0
  }
1996
0
  else
1997
0
  {
1998
    // Other languauge...
1999
0
          snprintf(ppd_name, sizeof(ppd_name), "%s.cupsFinishingTemplate", lang->language);
2000
0
          if ((ppd_attr = ppdFindAttr(ppd, ppd_name, choice->choice)) != NULL)
2001
0
            msg.str = ppd_attr->text;
2002
0
    else
2003
0
      continue;
2004
0
  }
2005
2006
0
  cupsArrayAdd(lang->strings, &msg);
2007
0
      }
2008
0
    }
2009
0
  }
2010
2011
 /*
2012
  * Max copies...
2013
  */
2014
2015
7.36k
  if ((ppd_attr = ppdFindAttr(ppd, "cupsMaxCopies", NULL)) != NULL)
2016
0
    pc->max_copies = atoi(ppd_attr->value);
2017
7.36k
  else if (ppd->manual_copies)
2018
1
    pc->max_copies = 1;
2019
7.36k
  else
2020
7.36k
    pc->max_copies = 9999;
2021
2022
 /*
2023
  * cupsChargeInfoURI, cupsJobAccountId, cupsJobAccountingUserId,
2024
  * cupsJobPassword, and cupsMandatory.
2025
  */
2026
2027
7.36k
  if ((ppd_attr = ppdFindAttr(ppd, "cupsChargeInfoURI", NULL)) != NULL)
2028
0
    pc->charge_info_uri = strdup(ppd_attr->value);
2029
2030
7.36k
  if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountId", NULL)) != NULL)
2031
0
    pc->account_id = !_cups_strcasecmp(ppd_attr->value, "true");
2032
2033
7.36k
  if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountingUserId", NULL)) != NULL)
2034
0
    pc->accounting_user_id = !_cups_strcasecmp(ppd_attr->value, "true");
2035
2036
7.36k
  if ((ppd_attr = ppdFindAttr(ppd, "cupsJobPassword", NULL)) != NULL)
2037
0
    pc->password = strdup(ppd_attr->value);
2038
2039
7.36k
  if ((ppd_attr = ppdFindAttr(ppd, "cupsMandatory", NULL)) != NULL)
2040
0
    pc->mandatory = cupsArrayNewStrings(ppd_attr->value, ' ');
2041
2042
 /*
2043
  * Support files...
2044
  */
2045
2046
7.36k
  pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
2047
2048
7.36k
  for (ppd_attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
2049
7.36k
       ppd_attr;
2050
7.36k
       ppd_attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
2051
0
    cupsArrayAdd(pc->support_files, ppd_attr->value);
2052
2053
#ifdef HAVE_APPLICATIONSERVICES_H
2054
  if ((ppd_attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL)
2055
    cupsArrayAdd(pc->support_files, ppd_attr->value);
2056
#endif
2057
2058
 /*
2059
  * Return the cache data...
2060
  */
2061
2062
7.36k
  return (pc);
2063
2064
 /*
2065
  * If we get here we need to destroy the PWG mapping data and return NULL...
2066
  */
2067
2068
0
  create_error:
2069
2070
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Out of memory."), 1);
2071
0
  _ppdCacheDestroy(pc);
2072
2073
0
  return (NULL);
2074
7.36k
}
2075
2076
2077
/*
2078
 * '_ppdCacheDestroy()' - Free all memory used for PWG mapping data.
2079
 */
2080
2081
void
2082
_ppdCacheDestroy(_ppd_cache_t *pc)  /* I - PPD cache and mapping data */
2083
14.7k
{
2084
14.7k
  int   i;      /* Looping var */
2085
14.7k
  pwg_map_t *map;     /* Current map */
2086
14.7k
  pwg_size_t  *size;      /* Current size */
2087
2088
2089
 /*
2090
  * Range check input...
2091
  */
2092
2093
14.7k
  if (!pc)
2094
0
    return;
2095
2096
 /*
2097
  * Free memory as needed...
2098
  */
2099
2100
14.7k
  if (pc->bins)
2101
0
  {
2102
0
    for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
2103
0
    {
2104
0
      free(map->pwg);
2105
0
      free(map->ppd);
2106
0
    }
2107
2108
0
    free(pc->bins);
2109
0
  }
2110
2111
14.7k
  if (pc->sizes)
2112
2.18k
  {
2113
6.06k
    for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2114
3.88k
    {
2115
3.88k
      free(size->map.pwg);
2116
3.88k
      free(size->map.ppd);
2117
3.88k
    }
2118
2119
2.18k
    free(pc->sizes);
2120
2.18k
  }
2121
2122
14.7k
  free(pc->source_option);
2123
2124
14.7k
  if (pc->sources)
2125
526
  {
2126
32.9k
    for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
2127
32.4k
    {
2128
32.4k
      free(map->pwg);
2129
32.4k
      free(map->ppd);
2130
32.4k
    }
2131
2132
526
    free(pc->sources);
2133
526
  }
2134
2135
14.7k
  if (pc->types)
2136
371
  {
2137
7.25k
    for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
2138
6.88k
    {
2139
6.88k
      free(map->pwg);
2140
6.88k
      free(map->ppd);
2141
6.88k
    }
2142
2143
371
    free(pc->types);
2144
371
  }
2145
2146
14.7k
  free(pc->custom_max_keyword);
2147
14.7k
  free(pc->custom_min_keyword);
2148
2149
14.7k
  free(pc->product);
2150
14.7k
  cupsArrayDelete(pc->filters);
2151
14.7k
  cupsArrayDelete(pc->prefilters);
2152
14.7k
  cupsArrayDelete(pc->finishings);
2153
2154
14.7k
  free(pc->charge_info_uri);
2155
14.7k
  free(pc->password);
2156
2157
14.7k
  free(pc->sides_option);
2158
14.7k
  free(pc->sides_1sided);
2159
14.7k
  free(pc->sides_2sided_long);
2160
14.7k
  free(pc->sides_2sided_short);
2161
2162
14.7k
  cupsArrayDelete(pc->mandatory);
2163
2164
14.7k
  cupsArrayDelete(pc->support_files);
2165
2166
14.7k
  free(pc);
2167
14.7k
}
2168
2169
2170
/*
2171
 * '_ppdCacheGetBin()' - Get the PWG output-bin keyword associated with a PPD
2172
 *                  OutputBin.
2173
 */
2174
2175
const char *        /* O - output-bin or NULL */
2176
_ppdCacheGetBin(
2177
    _ppd_cache_t *pc,     /* I - PPD cache and mapping data */
2178
    const char   *output_bin)   /* I - PPD OutputBin string */
2179
75
{
2180
75
  int i;        /* Looping var */
2181
2182
2183
 /*
2184
  * Range check input...
2185
  */
2186
2187
75
  if (!pc || !output_bin)
2188
0
    return (NULL);
2189
2190
 /*
2191
  * Look up the OutputBin string...
2192
  */
2193
2194
2195
75
  for (i = 0; i < pc->num_bins; i ++)
2196
0
    if (!_cups_strcasecmp(output_bin, pc->bins[i].ppd) || !_cups_strcasecmp(output_bin, pc->bins[i].pwg))
2197
0
      return (pc->bins[i].pwg);
2198
2199
75
  return (NULL);
2200
75
}
2201
2202
2203
/*
2204
 * '_ppdCacheGetFinishingOptions()' - Get PPD finishing options for the given
2205
 *                                    IPP finishings value(s).
2206
 */
2207
2208
int         /* O  - New number of options */
2209
_ppdCacheGetFinishingOptions(
2210
    _ppd_cache_t     *pc,   /* I  - PPD cache and mapping data */
2211
    ipp_t            *job,    /* I  - Job attributes or NULL */
2212
    ipp_finishings_t value,   /* I  - IPP finishings value of IPP_FINISHINGS_NONE */
2213
    int              num_options, /* I  - Number of options */
2214
    cups_option_t    **options)   /* IO - Options */
2215
191
{
2216
191
  int     i;    /* Looping var */
2217
191
  _pwg_finishings_t *f,   /* PWG finishings options */
2218
191
      key;    /* Search key */
2219
191
  ipp_attribute_t *attr;    /* Finishings attribute */
2220
191
  cups_option_t   *option;  /* Current finishings option */
2221
2222
2223
 /*
2224
  * Range check input...
2225
  */
2226
2227
191
  if (!pc || cupsArrayCount(pc->finishings) == 0 || !options ||
2228
0
      (!job && value == IPP_FINISHINGS_NONE))
2229
191
    return (num_options);
2230
2231
 /*
2232
  * Apply finishing options...
2233
  */
2234
2235
0
  if (job && (attr = ippFindAttribute(job, "finishings", IPP_TAG_ENUM)) != NULL)
2236
0
  {
2237
0
    int num_values = ippGetCount(attr); /* Number of values */
2238
2239
0
    for (i = 0; i < num_values; i ++)
2240
0
    {
2241
0
      key.value = (ipp_finishings_t)ippGetInteger(attr, i);
2242
2243
0
      if ((f = cupsArrayFind(pc->finishings, &key)) != NULL)
2244
0
      {
2245
0
        int j;      /* Another looping var */
2246
2247
0
        for (j = f->num_options, option = f->options; j > 0; j --, option ++)
2248
0
          num_options = cupsAddOption(option->name, option->value,
2249
0
                                      num_options, options);
2250
0
      }
2251
0
    }
2252
0
  }
2253
0
  else if (value != IPP_FINISHINGS_NONE)
2254
0
  {
2255
0
    key.value = value;
2256
2257
0
    if ((f = cupsArrayFind(pc->finishings, &key)) != NULL)
2258
0
    {
2259
0
      int j;      /* Another looping var */
2260
2261
0
      for (j = f->num_options, option = f->options; j > 0; j --, option ++)
2262
0
  num_options = cupsAddOption(option->name, option->value,
2263
0
            num_options, options);
2264
0
    }
2265
0
  }
2266
2267
0
  return (num_options);
2268
191
}
2269
2270
2271
/*
2272
 * '_ppdCacheGetFinishingValues()' - Get IPP finishings value(s) from the given
2273
 *                                   PPD options.
2274
 */
2275
2276
int         /* O - Number of finishings values */
2277
_ppdCacheGetFinishingValues(
2278
    ppd_file_t    *ppd,     /* I - Marked PPD file */
2279
    _ppd_cache_t  *pc,      /* I - PPD cache and mapping data */
2280
    int           max_values,   /* I - Maximum number of finishings values */
2281
    int           *values)    /* O - Finishings values */
2282
14.2k
{
2283
14.2k
  int     i,    /* Looping var */
2284
14.2k
      num_values = 0; /* Number of values */
2285
14.2k
  _pwg_finishings_t *f;   /* Current finishings option */
2286
14.2k
  cups_option_t   *option;  /* Current option */
2287
14.2k
  ppd_choice_t    *choice;  /* Marked PPD choice */
2288
2289
2290
 /*
2291
  * Range check input...
2292
  */
2293
2294
14.2k
  DEBUG_printf("_ppdCacheGetFinishingValues(ppd=%p, pc=%p, max_values=%d, values=%p)", (void *)ppd, (void *)pc, max_values, (void *)values);
2295
2296
14.2k
  if (!ppd || !pc || max_values < 1 || !values)
2297
0
  {
2298
0
    DEBUG_puts("_ppdCacheGetFinishingValues: Bad arguments, returning 0.");
2299
0
    return (0);
2300
0
  }
2301
14.2k
  else if (!pc->finishings)
2302
14.2k
  {
2303
14.2k
    DEBUG_puts("_ppdCacheGetFinishingValues: No finishings support, returning 0.");
2304
14.2k
    return (0);
2305
14.2k
  }
2306
2307
 /*
2308
  * Go through the finishings options and see what is set...
2309
  */
2310
2311
0
  for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
2312
0
       f;
2313
0
       f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
2314
0
  {
2315
0
    DEBUG_printf("_ppdCacheGetFinishingValues: Checking %d (%s)", (int)f->value, ippEnumString("finishings", (int)f->value));
2316
2317
0
    for (i = f->num_options, option = f->options; i > 0; i --, option ++)
2318
0
    {
2319
0
      DEBUG_printf("_ppdCacheGetFinishingValues: %s=%s?", option->name, option->value);
2320
2321
0
      if ((choice = ppdFindMarkedChoice(ppd, option->name)) == NULL || _cups_strcasecmp(option->value, choice->choice))
2322
0
      {
2323
0
        DEBUG_puts("_ppdCacheGetFinishingValues: NO");
2324
0
        break;
2325
0
      }
2326
0
    }
2327
2328
0
    if (i == 0)
2329
0
    {
2330
0
      DEBUG_printf("_ppdCacheGetFinishingValues: Adding %d (%s)", (int)f->value, ippEnumString("finishings", (int)f->value));
2331
2332
0
      values[num_values ++] = (int)f->value;
2333
2334
0
      if (num_values >= max_values)
2335
0
        break;
2336
0
    }
2337
0
  }
2338
2339
0
  if (num_values == 0)
2340
0
  {
2341
   /*
2342
    * Always have at least "finishings" = 'none'...
2343
    */
2344
2345
0
    DEBUG_puts("_ppdCacheGetFinishingValues: Adding 3 (none).");
2346
0
    values[0] = IPP_FINISHINGS_NONE;
2347
0
    num_values ++;
2348
0
  }
2349
2350
0
  DEBUG_printf("_ppdCacheGetFinishingValues: Returning %d.", num_values);
2351
2352
0
  return (num_values);
2353
14.2k
}
2354
2355
2356
/*
2357
 * 'ppd_inputslot_for_keyword()' - Return the PPD InputSlot associated
2358
 *                                a keyword string, or NULL if no mapping
2359
 *                                exists.
2360
 */
2361
static const char *     /* O - PPD InputSlot or NULL */
2362
ppd_inputslot_for_keyword(
2363
    _ppd_cache_t *pc,     /* I - PPD cache and mapping data */
2364
    const char   *keyword)    /* I - Keyword string */
2365
25
{
2366
25
  int i;        /* Looping var */
2367
2368
25
  if (!pc || !keyword)
2369
25
    return (NULL);
2370
2371
0
  for (i = 0; i < pc->num_sources; i ++)
2372
0
    if (!_cups_strcasecmp(keyword, pc->sources[i].pwg) || !_cups_strcasecmp(keyword, pc->sources[i].ppd))
2373
0
      return (pc->sources[i].ppd);
2374
2375
0
  return (NULL);
2376
0
}
2377
2378
2379
/*
2380
 * '_ppdCacheGetInputSlot()' - Get the PPD InputSlot associated with the job
2381
 *                        attributes or a keyword string.
2382
 */
2383
2384
const char *        /* O - PPD InputSlot or NULL */
2385
_ppdCacheGetInputSlot(
2386
    _ppd_cache_t *pc,     /* I - PPD cache and mapping data */
2387
    ipp_t        *job,      /* I - Job attributes or NULL */
2388
    const char   *keyword)    /* I - Keyword string or NULL */
2389
191
{
2390
 /*
2391
  * Range check input...
2392
  */
2393
2394
191
  if (!pc || pc->num_sources == 0 || (!job && !keyword))
2395
166
    return (NULL);
2396
2397
25
  if (job && !keyword)
2398
25
  {
2399
   /*
2400
    * Lookup the media-col attribute and any media-source found there...
2401
    */
2402
2403
25
    ipp_attribute_t *media_col, /* media-col attribute */
2404
25
      *media_source;  /* media-source attribute */
2405
25
    pwg_size_t    size;   /* Dimensional size */
2406
25
    int     margins_set;  /* Were the margins set? */
2407
2408
25
    media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
2409
25
    if (media_col &&
2410
0
        (media_source = ippFindAttribute(ippGetCollection(media_col, 0),
2411
0
                                         "media-source",
2412
0
                                   IPP_TAG_KEYWORD)) != NULL)
2413
0
    {
2414
     /*
2415
      * Use the media-source value from media-col...
2416
      */
2417
2418
0
      keyword = ippGetString(media_source, 0, NULL);
2419
0
    }
2420
25
    else if (pwgInitSize(&size, job, &margins_set))
2421
0
    {
2422
     /*
2423
      * For media <= 5x7, try to ask for automatic selection so the printer can
2424
      * pick the photo tray.  If auto isn't available, fall back to explicitly
2425
      * asking for the photo tray.
2426
      */
2427
2428
0
      if (size.width <= (5 * 2540) && size.length <= (7 * 2540)) {
2429
0
        const char* match;
2430
0
        if ((match = ppd_inputslot_for_keyword(pc, "auto")) != NULL)
2431
0
          return (match);
2432
0
        keyword = "photo";
2433
0
      }
2434
0
    }
2435
25
  }
2436
2437
25
  return (ppd_inputslot_for_keyword(pc, keyword));
2438
25
}
2439
2440
2441
/*
2442
 * '_ppdCacheGetMediaType()' - Get the PPD MediaType associated with the job
2443
 *                        attributes or a keyword string.
2444
 */
2445
2446
const char *        /* O - PPD MediaType or NULL */
2447
_ppdCacheGetMediaType(
2448
    _ppd_cache_t *pc,     /* I - PPD cache and mapping data */
2449
    ipp_t        *job,      /* I - Job attributes or NULL */
2450
    const char   *keyword)    /* I - Keyword string or NULL */
2451
191
{
2452
 /*
2453
  * Range check input...
2454
  */
2455
2456
191
  if (!pc || pc->num_types == 0 || (!job && !keyword))
2457
175
    return (NULL);
2458
2459
16
  if (job && !keyword)
2460
16
  {
2461
   /*
2462
    * Lookup the media-col attribute and any media-source found there...
2463
    */
2464
2465
16
    ipp_attribute_t *media_col, /* media-col attribute */
2466
16
      *media_type;  /* media-type attribute */
2467
2468
16
    media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
2469
16
    if (media_col)
2470
0
    {
2471
0
      if ((media_type = ippFindAttribute(media_col->values[0].collection,
2472
0
                                         "media-type",
2473
0
                                   IPP_TAG_KEYWORD)) == NULL)
2474
0
  media_type = ippFindAttribute(media_col->values[0].collection,
2475
0
              "media-type", IPP_TAG_NAME);
2476
2477
0
      if (media_type)
2478
0
  keyword = media_type->values[0].string.text;
2479
0
    }
2480
16
  }
2481
2482
16
  if (keyword)
2483
0
  {
2484
0
    int i;        /* Looping var */
2485
2486
0
    for (i = 0; i < pc->num_types; i ++)
2487
0
      if (!_cups_strcasecmp(keyword, pc->types[i].pwg) || !_cups_strcasecmp(keyword, pc->types[i].ppd))
2488
0
        return (pc->types[i].ppd);
2489
0
  }
2490
2491
16
  return (NULL);
2492
16
}
2493
2494
2495
/*
2496
 * '_ppdCacheGetOutputBin()' - Get the PPD OutputBin associated with the keyword
2497
 *                        string.
2498
 */
2499
2500
const char *        /* O - PPD OutputBin or NULL */
2501
_ppdCacheGetOutputBin(
2502
    _ppd_cache_t *pc,     /* I - PPD cache and mapping data */
2503
    const char   *output_bin)   /* I - Keyword string */
2504
75
{
2505
75
  int i;        /* Looping var */
2506
2507
2508
 /*
2509
  * Range check input...
2510
  */
2511
2512
75
  if (!pc || !output_bin)
2513
0
    return (NULL);
2514
2515
 /*
2516
  * Look up the OutputBin string...
2517
  */
2518
2519
2520
75
  for (i = 0; i < pc->num_bins; i ++)
2521
0
    if (!_cups_strcasecmp(output_bin, pc->bins[i].pwg) || !_cups_strcasecmp(output_bin, pc->bins[i].ppd))
2522
0
      return (pc->bins[i].ppd);
2523
2524
75
  return (NULL);
2525
75
}
2526
2527
2528
/*
2529
 * '_ppdCacheGetPageSize()' - Get the PPD PageSize associated with the job
2530
 *                       attributes or a keyword string.
2531
 */
2532
2533
const char *        /* O - PPD PageSize or NULL */
2534
_ppdCacheGetPageSize(
2535
    _ppd_cache_t *pc,     /* I - PPD cache and mapping data */
2536
    ipp_t        *job,      /* I - Job attributes or NULL */
2537
    const char   *keyword,    /* I - Keyword string or NULL */
2538
    int          *exact)    /* O - 1 if exact match, 0 otherwise */
2539
1.38k
{
2540
1.38k
  int   i;      /* Looping var */
2541
1.38k
  pwg_size_t  *size,      /* Current size */
2542
1.38k
    *closest,   /* Closest size */
2543
1.38k
    jobsize;    /* Size data from job */
2544
1.38k
  int   margins_set,    /* Were the margins set? */
2545
1.38k
    dwidth,     /* Difference in width */
2546
1.38k
    dlength,    /* Difference in length */
2547
1.38k
    dleft,      /* Difference in left margins */
2548
1.38k
    dright,     /* Difference in right margins */
2549
1.38k
    dbottom,    /* Difference in bottom margins */
2550
1.38k
    dtop,     /* Difference in top margins */
2551
1.38k
    dmin,     /* Minimum difference */
2552
1.38k
    dclosest;   /* Closest difference */
2553
1.38k
  const char  *ppd_name;    /* PPD media name */
2554
2555
2556
1.38k
  DEBUG_printf("_ppdCacheGetPageSize(pc=%p, job=%p, keyword=\"%s\", exact=%p)", (void *)pc, (void *)job, keyword, (void *)exact);
2557
2558
 /*
2559
  * Range check input...
2560
  */
2561
2562
1.38k
  if (!pc || (!job && !keyword))
2563
0
    return (NULL);
2564
2565
1.38k
  if (exact)
2566
1.38k
    *exact = 0;
2567
2568
1.38k
  ppd_name = keyword;
2569
2570
1.38k
  if (job)
2571
191
  {
2572
   /*
2573
    * Try getting the PPD media name from the job attributes...
2574
    */
2575
2576
191
    ipp_attribute_t *attr;    /* Job attribute */
2577
2578
191
    if ((attr = ippFindAttribute(job, "PageSize", IPP_TAG_ZERO)) == NULL)
2579
191
      if ((attr = ippFindAttribute(job, "PageRegion", IPP_TAG_ZERO)) == NULL)
2580
191
        attr = ippFindAttribute(job, "media", IPP_TAG_ZERO);
2581
2582
#ifdef DEBUG
2583
    if (attr)
2584
      DEBUG_printf("1_ppdCacheGetPageSize: Found attribute %s (%s)", attr->name, ippTagString(attr->value_tag));
2585
    else
2586
      DEBUG_puts("1_ppdCacheGetPageSize: Did not find media attribute.");
2587
#endif /* DEBUG */
2588
2589
191
    if (attr && (attr->value_tag == IPP_TAG_NAME ||
2590
158
                 attr->value_tag == IPP_TAG_KEYWORD))
2591
158
      ppd_name = attr->values[0].string.text;
2592
191
  }
2593
2594
1.38k
  DEBUG_printf("1_ppdCacheGetPageSize: ppd_name=\"%s\"", ppd_name);
2595
2596
1.38k
  if (ppd_name)
2597
1.35k
  {
2598
   /*
2599
    * Try looking up the named PPD size first...
2600
    */
2601
2602
1.79k
    for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2603
453
    {
2604
453
      DEBUG_printf("2_ppdCacheGetPageSize: size[%d]=[\"%s\" \"%s\"]", (int)(size - pc->sizes), size->map.pwg, size->map.ppd);
2605
2606
453
      if (!_cups_strcasecmp(ppd_name, size->map.ppd) ||
2607
450
          !_cups_strcasecmp(ppd_name, size->map.pwg))
2608
4
      {
2609
4
  if (exact)
2610
4
    *exact = 1;
2611
2612
4
        DEBUG_printf("1_ppdCacheGetPageSize: Returning \"%s\"", ppd_name);
2613
2614
4
        return (size->map.ppd);
2615
4
      }
2616
453
    }
2617
1.35k
  }
2618
2619
1.37k
  if (job && !keyword)
2620
191
  {
2621
   /*
2622
    * Get the size using media-col or media, with the preference being
2623
    * media-col.
2624
    */
2625
2626
191
    if (!pwgInitSize(&jobsize, job, &margins_set))
2627
191
      return (NULL);
2628
191
  }
2629
1.18k
  else
2630
1.18k
  {
2631
   /*
2632
    * Get the size using a media keyword...
2633
    */
2634
2635
1.18k
    pwg_media_t *media;   /* Media definition */
2636
2637
2638
1.18k
    if ((media = pwgMediaForPWG(keyword)) == NULL)
2639
839
      if ((media = pwgMediaForLegacy(keyword)) == NULL)
2640
817
        if ((media = pwgMediaForPPD(keyword)) == NULL)
2641
383
    return (NULL);
2642
2643
805
    jobsize.width  = media->width;
2644
805
    jobsize.length = media->length;
2645
805
    margins_set    = 0;
2646
805
  }
2647
2648
 /*
2649
  * Now that we have the dimensions and possibly the margins, look at the
2650
  * available sizes and find the match...
2651
  */
2652
2653
805
  closest  = NULL;
2654
805
  dclosest = 999999999;
2655
2656
805
  if (!ppd_name || _cups_strncasecmp(ppd_name, "Custom.", 7) ||
2657
226
      _cups_strncasecmp(ppd_name, "custom_", 7))
2658
805
  {
2659
1.12k
    for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2660
332
    {
2661
     /*
2662
      * Adobe uses a size matching algorithm with an epsilon of 5 points, which
2663
      * is just about 176/2540ths...
2664
      */
2665
2666
332
      dwidth  = size->width - jobsize.width;
2667
332
      dlength = size->length - jobsize.length;
2668
2669
332
      if (dwidth <= -176 || dwidth >= 176 || dlength <= -176 || dlength >= 176)
2670
319
  continue;
2671
2672
13
      if (margins_set)
2673
0
      {
2674
       /*
2675
  * Use a tighter epsilon of 1 point (35/2540ths) for margins...
2676
  */
2677
2678
0
  dleft   = size->left - jobsize.left;
2679
0
  dright  = size->right - jobsize.right;
2680
0
  dtop    = size->top - jobsize.top;
2681
0
  dbottom = size->bottom - jobsize.bottom;
2682
2683
0
  if (dleft <= -35 || dleft >= 35 || dright <= -35 || dright >= 35 ||
2684
0
      dtop <= -35 || dtop >= 35 || dbottom <= -35 || dbottom >= 35)
2685
0
  {
2686
0
    dleft   = dleft < 0 ? -dleft : dleft;
2687
0
    dright  = dright < 0 ? -dright : dright;
2688
0
    dbottom = dbottom < 0 ? -dbottom : dbottom;
2689
0
    dtop    = dtop < 0 ? -dtop : dtop;
2690
0
    dmin    = dleft + dright + dbottom + dtop;
2691
2692
0
    if (dmin < dclosest)
2693
0
    {
2694
0
      dclosest = dmin;
2695
0
      closest  = size;
2696
0
    }
2697
2698
0
    continue;
2699
0
  }
2700
0
      }
2701
2702
13
      if (exact)
2703
13
  *exact = 1;
2704
2705
13
      DEBUG_printf("1_ppdCacheGetPageSize: Returning \"%s\"", size->map.ppd);
2706
2707
13
      return (size->map.ppd);
2708
13
    }
2709
805
  }
2710
2711
792
  if (closest)
2712
0
  {
2713
0
    DEBUG_printf("1_ppdCacheGetPageSize: Returning \"%s\" (closest)", closest->map.ppd);
2714
2715
0
    return (closest->map.ppd);
2716
0
  }
2717
2718
 /*
2719
  * If we get here we need to check for custom page size support...
2720
  */
2721
2722
792
  if (jobsize.width >= pc->custom_min_width &&
2723
721
      jobsize.width <= pc->custom_max_width &&
2724
182
      jobsize.length >= pc->custom_min_length &&
2725
158
      jobsize.length <= pc->custom_max_length)
2726
31
  {
2727
   /*
2728
    * In range, format as Custom.WWWWxLLLL (points).
2729
    */
2730
2731
31
    snprintf(pc->custom_ppd_size, sizeof(pc->custom_ppd_size), "Custom.%dx%d",
2732
31
             (int)PWG_TO_POINTS(jobsize.width), (int)PWG_TO_POINTS(jobsize.length));
2733
2734
31
    if (margins_set && exact)
2735
0
    {
2736
0
      dleft   = pc->custom_size.left - jobsize.left;
2737
0
      dright  = pc->custom_size.right - jobsize.right;
2738
0
      dtop    = pc->custom_size.top - jobsize.top;
2739
0
      dbottom = pc->custom_size.bottom - jobsize.bottom;
2740
2741
0
      if (dleft > -35 && dleft < 35 && dright > -35 && dright < 35 &&
2742
0
          dtop > -35 && dtop < 35 && dbottom > -35 && dbottom < 35)
2743
0
  *exact = 1;
2744
0
    }
2745
31
    else if (exact)
2746
31
      *exact = 1;
2747
2748
31
    DEBUG_printf("1_ppdCacheGetPageSize: Returning \"%s\" (custom)", pc->custom_ppd_size);
2749
2750
31
    return (pc->custom_ppd_size);
2751
31
  }
2752
2753
 /*
2754
  * No custom page size support or the size is out of range - return NULL.
2755
  */
2756
2757
761
  DEBUG_puts("1_ppdCacheGetPageSize: Returning NULL");
2758
2759
761
  return (NULL);
2760
792
}
2761
2762
2763
/*
2764
 * '_ppdCacheGetSize()' - Get the PWG size associated with a PPD PageSize.
2765
 */
2766
2767
pwg_size_t *        /* O - PWG size or NULL */
2768
_ppdCacheGetSize(
2769
    _ppd_cache_t *pc,     /* I - PPD cache and mapping data */
2770
    const char   *page_size,    /* I - PPD PageSize */
2771
    ppd_size_t   *ppd_size)   /* I - PPD page size information */
2772
1.77k
{
2773
1.77k
  int   i;      /* Looping var */
2774
1.77k
  pwg_media_t *media;     /* Media */
2775
1.77k
  pwg_size_t  *size;      /* Current size */
2776
2777
2778
 /*
2779
  * Range check input...
2780
  */
2781
2782
1.77k
  if (!pc || !page_size)
2783
520
    return (NULL);
2784
2785
1.25k
  if (!_cups_strcasecmp(page_size, "Custom") || !_cups_strncasecmp(page_size, "Custom.", 7))
2786
311
  {
2787
   /*
2788
    * Custom size; size name can be one of the following:
2789
    *
2790
    *    Custom.WIDTHxLENGTHin    - Size in inches
2791
    *    Custom.WIDTHxLENGTHft    - Size in feet
2792
    *    Custom.WIDTHxLENGTHcm    - Size in centimeters
2793
    *    Custom.WIDTHxLENGTHmm    - Size in millimeters
2794
    *    Custom.WIDTHxLENGTHm     - Size in meters
2795
    *    Custom.WIDTHxLENGTH[pt]  - Size in points
2796
    */
2797
2798
311
    double    w, l;   /* Width and length of page */
2799
311
    char    *ptr;   /* Pointer into PageSize */
2800
311
    struct lconv  *loc;   /* Locale data */
2801
2802
311
    if (page_size[6])
2803
305
    {
2804
305
      loc = localeconv();
2805
305
      w   = (float)_cupsStrScand(page_size + 7, &ptr, loc);
2806
305
      if (!ptr || *ptr != 'x')
2807
61
  return (NULL);
2808
2809
244
      l = (float)_cupsStrScand(ptr + 1, &ptr, loc);
2810
244
      if (!ptr)
2811
0
  return (NULL);
2812
2813
244
      if (!_cups_strcasecmp(ptr, "in"))
2814
4
      {
2815
4
  w *= 2540.0;
2816
4
  l *= 2540.0;
2817
4
      }
2818
240
      else if (!_cups_strcasecmp(ptr, "ft"))
2819
4
      {
2820
4
  w *= 12.0 * 2540.0;
2821
4
  l *= 12.0 * 2540.0;
2822
4
      }
2823
236
      else if (!_cups_strcasecmp(ptr, "mm"))
2824
3
      {
2825
3
  w *= 100.0;
2826
3
  l *= 100.0;
2827
3
      }
2828
233
      else if (!_cups_strcasecmp(ptr, "cm"))
2829
3
      {
2830
3
  w *= 1000.0;
2831
3
  l *= 1000.0;
2832
3
      }
2833
230
      else if (!_cups_strcasecmp(ptr, "m"))
2834
10
      {
2835
10
  w *= 100000.0;
2836
10
  l *= 100000.0;
2837
10
      }
2838
220
      else
2839
220
      {
2840
220
  w *= 2540.0 / 72.0;
2841
220
  l *= 2540.0 / 72.0;
2842
220
      }
2843
244
    }
2844
6
    else if (ppd_size)
2845
0
    {
2846
0
      w = ppd_size->width * 2540.0 / 72.0;
2847
0
      l = ppd_size->length * 2540.0 / 72.0;
2848
0
    }
2849
6
    else
2850
6
    {
2851
      // No custom size information...
2852
6
      return (NULL);
2853
6
    }
2854
2855
244
    pc->custom_size.map.ppd = (char *)page_size;
2856
244
    pc->custom_size.width   = (int)w;
2857
244
    pc->custom_size.length  = (int)l;
2858
2859
244
    if ((media = pwgMediaForSize((int)w, (int)l)) != NULL)
2860
126
      pc->custom_size.map.pwg = (char *)media->pwg;
2861
2862
244
    return (&(pc->custom_size));
2863
311
  }
2864
2865
 /*
2866
  * Not a custom size - look it up...
2867
  */
2868
2869
1.34k
  for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
2870
408
  {
2871
408
    if (!_cups_strcasecmp(page_size, size->map.ppd) ||
2872
405
        !_cups_strcasecmp(page_size, size->map.pwg))
2873
4
      return (size);
2874
408
  }
2875
2876
 /*
2877
  * Look up standard sizes...
2878
  */
2879
2880
937
  if ((media = pwgMediaForPPD(page_size)) == NULL)
2881
668
  {
2882
668
    if ((media = pwgMediaForLegacy(page_size)) == NULL)
2883
643
      media = pwgMediaForPWG(page_size);
2884
668
  }
2885
2886
937
  if (media)
2887
601
  {
2888
601
    pc->custom_size.map.ppd = (char *)page_size;
2889
601
    pc->custom_size.map.pwg = (char *)media->pwg;
2890
601
    pc->custom_size.width   = media->width;
2891
601
    pc->custom_size.length  = media->length;
2892
2893
601
    return (&(pc->custom_size));
2894
601
  }
2895
2896
336
  return (NULL);
2897
937
}
2898
2899
2900
/*
2901
 * '_ppdCacheGetSource()' - Get the PWG media-source associated with a PPD
2902
 *                          InputSlot.
2903
 */
2904
2905
const char *        /* O - PWG media-source keyword */
2906
_ppdCacheGetSource(
2907
    _ppd_cache_t *pc,     /* I - PPD cache and mapping data */
2908
    const char   *input_slot)   /* I - PPD InputSlot */
2909
666
{
2910
666
  int   i;      /* Looping var */
2911
666
  pwg_map_t *source;    /* Current source */
2912
2913
2914
 /*
2915
  * Range check input...
2916
  */
2917
2918
666
  if (!pc || !input_slot)
2919
568
    return (NULL);
2920
2921
447
  for (i = pc->num_sources, source = pc->sources; i > 0; i --, source ++)
2922
357
    if (!_cups_strcasecmp(input_slot, source->ppd) || !_cups_strcasecmp(input_slot, source->pwg))
2923
8
      return (source->pwg);
2924
2925
90
  return (NULL);
2926
98
}
2927
2928
2929
/*
2930
 * '_ppdCacheGetType()' - Get the PWG media-type associated with a PPD
2931
 *                        MediaType.
2932
 */
2933
2934
const char *        /* O - PWG media-type keyword */
2935
_ppdCacheGetType(
2936
    _ppd_cache_t *pc,     /* I - PPD cache and mapping data */
2937
    const char   *media_type)   /* I - PPD MediaType */
2938
682
{
2939
682
  int   i;      /* Looping var */
2940
682
  pwg_map_t *type;      /* Current type */
2941
2942
2943
 /*
2944
  * Range check input...
2945
  */
2946
2947
682
  if (!pc || !media_type)
2948
564
    return (NULL);
2949
2950
475
  for (i = pc->num_types, type = pc->types; i > 0; i --, type ++)
2951
365
    if (!_cups_strcasecmp(media_type, type->ppd) || !_cups_strcasecmp(media_type, type->pwg))
2952
8
      return (type->pwg);
2953
2954
110
  return (NULL);
2955
118
}
2956
2957
2958
/*
2959
 * '_ppdCacheWriteFile()' - Write PWG mapping data to a file.
2960
 */
2961
2962
int         /* O - 1 on success, 0 on failure */
2963
_ppdCacheWriteFile(
2964
    _ppd_cache_t *pc,     /* I - PPD cache and mapping data */
2965
    const char   *filename,   /* I - File to write */
2966
    ipp_t        *attrs)    /* I - Attributes to write, if any */
2967
7.36k
{
2968
7.36k
  int     i, j, k;  /* Looping vars */
2969
7.36k
  cups_file_t   *fp;    /* Output file */
2970
7.36k
  pwg_size_t    *size;    /* Current size */
2971
7.36k
  pwg_map_t   *map;   /* Current map */
2972
7.36k
  _pwg_finishings_t *f;   /* Current finishing option */
2973
7.36k
  cups_option_t   *option;  /* Current option */
2974
7.36k
  const char    *value;   /* String value */
2975
7.36k
  char      newfile[1024];  /* New filename */
2976
2977
2978
 /*
2979
  * Range check input...
2980
  */
2981
2982
7.36k
  if (!pc || !filename)
2983
0
  {
2984
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
2985
0
    return (0);
2986
0
  }
2987
2988
 /*
2989
  * Open the file and write with compression...
2990
  */
2991
2992
7.36k
  snprintf(newfile, sizeof(newfile), "%s.N", filename);
2993
7.36k
  if ((fp = cupsFileOpen(newfile, "w9")) == NULL)
2994
0
  {
2995
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
2996
0
    return (0);
2997
0
  }
2998
2999
 /*
3000
  * Standard header...
3001
  */
3002
3003
7.36k
  cupsFilePrintf(fp, "#CUPS-PPD-CACHE-%d\n", _PPD_CACHE_VERSION);
3004
3005
 /*
3006
  * Output bins...
3007
  */
3008
3009
7.36k
  if (pc->num_bins > 0)
3010
0
  {
3011
0
    cupsFilePrintf(fp, "NumBins %d\n", pc->num_bins);
3012
0
    for (i = pc->num_bins, map = pc->bins; i > 0; i --, map ++)
3013
0
      cupsFilePrintf(fp, "Bin %s %s\n", map->pwg, map->ppd);
3014
0
  }
3015
3016
 /*
3017
  * Media sizes...
3018
  */
3019
3020
7.36k
  cupsFilePrintf(fp, "NumSizes %d\n", pc->num_sizes);
3021
9.65k
  for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
3022
2.28k
    cupsFilePrintf(fp, "Size %s %s %d %d %d %d %d %d\n", size->map.pwg,
3023
2.28k
       size->map.ppd, size->width, size->length, size->left,
3024
2.28k
       size->bottom, size->right, size->top);
3025
7.36k
  if (pc->custom_max_width > 0)
3026
0
    cupsFilePrintf(fp, "CustomSize %d %d %d %d %d %d %d %d\n",
3027
0
                   pc->custom_max_width, pc->custom_max_length,
3028
0
       pc->custom_min_width, pc->custom_min_length,
3029
0
       pc->custom_size.left, pc->custom_size.bottom,
3030
0
       pc->custom_size.right, pc->custom_size.top);
3031
3032
 /*
3033
  * Media sources...
3034
  */
3035
3036
7.36k
  if (pc->source_option)
3037
292
    cupsFilePrintf(fp, "SourceOption %s\n", pc->source_option);
3038
3039
7.36k
  if (pc->num_sources > 0)
3040
250
  {
3041
250
    cupsFilePrintf(fp, "NumSources %d\n", pc->num_sources);
3042
18.7k
    for (i = pc->num_sources, map = pc->sources; i > 0; i --, map ++)
3043
18.4k
      cupsFilePrintf(fp, "Source %s %s\n", map->pwg, map->ppd);
3044
250
  }
3045
3046
 /*
3047
  * Media types...
3048
  */
3049
3050
7.36k
  if (pc->num_types > 0)
3051
175
  {
3052
175
    cupsFilePrintf(fp, "NumTypes %d\n", pc->num_types);
3053
4.61k
    for (i = pc->num_types, map = pc->types; i > 0; i --, map ++)
3054
4.43k
      cupsFilePrintf(fp, "Type %s %s\n", map->pwg, map->ppd);
3055
175
  }
3056
3057
 /*
3058
  * Presets...
3059
  */
3060
3061
22.1k
  for (i = _PWG_PRINT_COLOR_MODE_MONOCHROME; i < _PWG_PRINT_COLOR_MODE_MAX; i ++)
3062
14.7k
  {
3063
58.9k
    for (j = _PWG_PRINT_QUALITY_DRAFT; j < _PWG_PRINT_QUALITY_MAX; j ++)
3064
44.2k
    {
3065
44.2k
      if (pc->num_presets[i][j])
3066
0
      {
3067
0
  cupsFilePrintf(fp, "Preset %d %d", i, j);
3068
0
  for (k = pc->num_presets[i][j], option = pc->presets[i][j];
3069
0
       k > 0;
3070
0
       k --, option ++)
3071
0
    cupsFilePrintf(fp, " %s=%s", option->name, option->value);
3072
0
  cupsFilePutChar(fp, '\n');
3073
0
      }
3074
44.2k
    }
3075
14.7k
  }
3076
3077
 /*
3078
  * Duplex/sides...
3079
  */
3080
3081
7.36k
  if (pc->sides_option)
3082
33
    cupsFilePrintf(fp, "SidesOption %s\n", pc->sides_option);
3083
3084
7.36k
  if (pc->sides_1sided)
3085
19
    cupsFilePrintf(fp, "Sides1Sided %s\n", pc->sides_1sided);
3086
3087
7.36k
  if (pc->sides_2sided_long)
3088
0
    cupsFilePrintf(fp, "Sides2SidedLong %s\n", pc->sides_2sided_long);
3089
3090
7.36k
  if (pc->sides_2sided_short)
3091
0
    cupsFilePrintf(fp, "Sides2SidedShort %s\n", pc->sides_2sided_short);
3092
3093
 /*
3094
  * Product, cupsFilter, cupsFilter2, and cupsPreFilter...
3095
  */
3096
3097
7.36k
  if (pc->product)
3098
725
    cupsFilePutConf(fp, "Product", pc->product);
3099
3100
7.36k
  for (value = (const char *)cupsArrayFirst(pc->filters);
3101
38.0k
       value;
3102
30.6k
       value = (const char *)cupsArrayNext(pc->filters))
3103
30.6k
    cupsFilePutConf(fp, "Filter", value);
3104
3105
7.36k
  for (value = (const char *)cupsArrayFirst(pc->prefilters);
3106
7.59k
       value;
3107
7.36k
       value = (const char *)cupsArrayNext(pc->prefilters))
3108
228
    cupsFilePutConf(fp, "PreFilter", value);
3109
3110
7.36k
  cupsFilePrintf(fp, "SingleFile %s\n", pc->single_file ? "true" : "false");
3111
3112
 /*
3113
  * Finishing options...
3114
  */
3115
3116
7.36k
  for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
3117
7.36k
       f;
3118
7.36k
       f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
3119
0
  {
3120
0
    cupsFilePrintf(fp, "Finishings %d", f->value);
3121
0
    for (i = f->num_options, option = f->options; i > 0; i --, option ++)
3122
0
      cupsFilePrintf(fp, " %s=%s", option->name, option->value);
3123
0
    cupsFilePutChar(fp, '\n');
3124
0
  }
3125
3126
7.36k
  for (value = (const char *)cupsArrayFirst(pc->templates); value; value = (const char *)cupsArrayNext(pc->templates))
3127
0
    cupsFilePutConf(fp, "FinishingTemplate", value);
3128
3129
 /*
3130
  * Max copies...
3131
  */
3132
3133
7.36k
  cupsFilePrintf(fp, "MaxCopies %d\n", pc->max_copies);
3134
3135
 /*
3136
  * Accounting/quota/PIN/managed printing values...
3137
  */
3138
3139
7.36k
  if (pc->charge_info_uri)
3140
0
    cupsFilePutConf(fp, "ChargeInfoURI", pc->charge_info_uri);
3141
3142
7.36k
  cupsFilePrintf(fp, "JobAccountId %s\n", pc->account_id ? "true" : "false");
3143
7.36k
  cupsFilePrintf(fp, "JobAccountingUserId %s\n",
3144
7.36k
                 pc->accounting_user_id ? "true" : "false");
3145
3146
7.36k
  if (pc->password)
3147
0
    cupsFilePutConf(fp, "JobPassword", pc->password);
3148
3149
7.36k
  for (value = (char *)cupsArrayFirst(pc->mandatory);
3150
7.36k
       value;
3151
7.36k
       value = (char *)cupsArrayNext(pc->mandatory))
3152
0
    cupsFilePutConf(fp, "Mandatory", value);
3153
3154
 /*
3155
  * Support files...
3156
  */
3157
3158
7.36k
  for (value = (char *)cupsArrayFirst(pc->support_files);
3159
7.36k
       value;
3160
7.36k
       value = (char *)cupsArrayNext(pc->support_files))
3161
0
    cupsFilePutConf(fp, "SupportFile", value);
3162
3163
 /*
3164
  * IPP attributes, if any...
3165
  */
3166
3167
7.36k
  if (attrs)
3168
0
  {
3169
0
    cupsFilePrintf(fp, "IPP " CUPS_LLFMT "\n", CUPS_LLCAST ippLength(attrs));
3170
3171
0
    attrs->state = IPP_STATE_IDLE;
3172
0
    ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, attrs);
3173
0
  }
3174
3175
 /*
3176
  * Close and return...
3177
  */
3178
3179
7.36k
  if (cupsFileClose(fp))
3180
0
  {
3181
0
    unlink(newfile);
3182
0
    return (0);
3183
0
  }
3184
3185
7.36k
  unlink(filename);
3186
7.36k
  return (!rename(newfile, filename));
3187
7.36k
}
3188
3189
3190
/*
3191
 * '_ppdCreateFromIPP()' - Create a PPD file describing the capabilities
3192
 *                         of an IPP printer.
3193
 */
3194
3195
char *          /* O - PPD filename or `NULL` on error */
3196
_ppdCreateFromIPP(char   *buffer, /* I - Filename buffer */
3197
                  size_t bufsize, /* I - Size of filename buffer */
3198
      ipp_t  *supported)  /* I - Get-Printer-Attributes response */
3199
0
{
3200
0
  const char    *printer_uri; /* Printer URI */
3201
0
  http_t    *http = NULL; /* HTTP connection */
3202
0
  cups_lang_t   *base_lang, /* Base English language */
3203
0
      *lang,    /* Current language information */
3204
0
      *langs = NULL; /* Language (strings) files */
3205
0
  const char    *prefix;  /* Prefix string */
3206
0
  cups_file_t   *fp;    /* PPD file */
3207
0
  cups_array_t    *sizes;   /* Media sizes supported by printer */
3208
0
  cups_size_t   *size;    /* Current media size */
3209
0
  ipp_attribute_t *attr,    /* xxx-supported */
3210
0
      *lang_supp, /* printer-strings-languages-supported */
3211
0
      *defattr, /* xxx-default */
3212
0
                        *quality, /* print-quality-supported */
3213
0
      *x_dim, *y_dim; /* Media dimensions */
3214
0
  ipp_t     *media_col, /* Media collection */
3215
0
      *media_size;  /* Media size collection */
3216
0
  char      make[256],  /* Make and model */
3217
0
      *mptr,    /* Pointer into make and model */
3218
0
      ppdname[PPD_MAX_NAME],
3219
              /* PPD keyword */
3220
0
          ppdtext[PPD_MAX_TEXT];
3221
          /* PPD English text */
3222
0
  const char    *model;   /* Model name */
3223
0
  int     i, j,   /* Looping vars */
3224
0
      count,    /* Number of values */
3225
0
      bottom,   /* Largest bottom margin */
3226
0
      left,   /* Largest left margin */
3227
0
      right,    /* Largest right margin */
3228
0
      top,    /* Largest top margin */
3229
0
      max_length = 0, /* Maximum custom size */
3230
0
      max_width = 0,
3231
0
      min_length = INT_MAX,
3232
          /* Minimum custom size */
3233
0
      min_width = INT_MAX,
3234
0
      is_apple = 0, /* Does the printer support Apple raster? */
3235
0
      is_pdf = 0, /* Does the printer support PDF? */
3236
0
      is_pwg = 0; /* Does the printer support PWG Raster? */
3237
0
  pwg_media_t   *pwg;   /* PWG media size */
3238
0
  int     xres, yres; /* Resolution values */
3239
0
  int                   resolutions[1000];
3240
                                        /* Array of resolution indices */
3241
0
  int     have_qdraft = 0,/* Have draft quality? */
3242
0
      have_qhigh = 0; /* Have high quality? */
3243
0
  char      msgid[256]; /* PWG message identifier (attr.value) */
3244
0
  const char    *keyword; /* Keyword value */
3245
0
  struct lconv    *loc = localeconv();
3246
          /* Locale data */
3247
0
  cups_array_t    *fin_options = NULL;
3248
          /* Finishing options */
3249
3250
3251
 /*
3252
  * Range check input...
3253
  */
3254
3255
0
  if (buffer)
3256
0
    *buffer = '\0';
3257
3258
0
  if (!buffer || bufsize < 1)
3259
0
  {
3260
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
3261
0
    return (NULL);
3262
0
  }
3263
3264
0
  if (!supported)
3265
0
  {
3266
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No IPP attributes."), 1);
3267
0
    return (NULL);
3268
0
  }
3269
3270
0
  if ((printer_uri = ippGetString(ippFindAttribute(supported, "printer-uri-supported", IPP_TAG_URI), 0, NULL)) == NULL)
3271
0
  {
3272
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No printer-uri-supported attribute."), 1);
3273
0
    return (NULL);
3274
0
  }
3275
3276
 /*
3277
  * Open a temporary file for the PPD...
3278
  */
3279
3280
0
  if ((fp = cupsCreateTempFile("ippeve", ".ppd", buffer, bufsize)) == NULL)
3281
0
  {
3282
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
3283
0
    return (NULL);
3284
0
  }
3285
3286
 /*
3287
  * Get a sanitized make and model...
3288
  */
3289
3290
0
  if ((attr = ippFindAttribute(supported, "printer-make-and-model", IPP_TAG_TEXT)) != NULL && ippValidateAttribute(attr))
3291
0
  {
3292
   /*
3293
    * Sanitize the model name to only contain PPD-safe characters.
3294
    */
3295
3296
0
    cupsCopyString(make, ippGetString(attr, 0, NULL), sizeof(make));
3297
3298
0
    for (mptr = make; *mptr; mptr ++)
3299
0
    {
3300
0
      if (*mptr < ' ' || *mptr >= 127 || *mptr == '\"')
3301
0
      {
3302
       /*
3303
  * Truncate the make and model on the first bad character...
3304
  */
3305
3306
0
  *mptr = '\0';
3307
0
  break;
3308
0
      }
3309
0
    }
3310
3311
0
    while (mptr > make)
3312
0
    {
3313
     /*
3314
      * Strip trailing whitespace...
3315
      */
3316
3317
0
      mptr --;
3318
0
      if (*mptr == ' ')
3319
0
  *mptr = '\0';
3320
0
      else
3321
0
        break;
3322
0
    }
3323
3324
0
    if (!make[0])
3325
0
    {
3326
     /*
3327
      * Use a default make and model if nothing remains...
3328
      */
3329
3330
0
      cupsCopyString(make, "Unknown", sizeof(make));
3331
0
    }
3332
0
  }
3333
0
  else
3334
0
  {
3335
   /*
3336
    * Use a default make and model...
3337
    */
3338
3339
0
    cupsCopyString(make, "Unknown", sizeof(make));
3340
0
  }
3341
3342
0
  if (!_cups_strncasecmp(make, "Hewlett Packard ", 16) || !_cups_strncasecmp(make, "Hewlett-Packard ", 16))
3343
0
  {
3344
   /*
3345
    * Normalize HP printer make and model...
3346
    */
3347
3348
0
    model = make + 16;
3349
0
    cupsCopyString(make, "HP", sizeof(make));
3350
3351
0
    if (!_cups_strncasecmp(model, "HP ", 3))
3352
0
      model += 3;
3353
0
  }
3354
0
  else if ((mptr = strchr(make, ' ')) != NULL)
3355
0
  {
3356
   /*
3357
    * Separate "MAKE MODEL"...
3358
    */
3359
3360
0
    while (*mptr && *mptr == ' ')
3361
0
      *mptr++ = '\0';
3362
3363
0
    model = mptr;
3364
0
  }
3365
0
  else
3366
0
  {
3367
   /*
3368
    * No separate model name...
3369
    */
3370
3371
0
    model = "Printer";
3372
0
  }
3373
3374
 /*
3375
  * Standard stuff for PPD file...
3376
  */
3377
3378
0
  cupsFilePuts(fp, "*PPD-Adobe: \"4.3\"\n");
3379
0
  cupsFilePuts(fp, "*FormatVersion: \"4.3\"\n");
3380
0
  cupsFilePrintf(fp, "*FileVersion: \"%d.%d\"\n", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR);
3381
0
  cupsFilePuts(fp, "*LanguageVersion: English\n");
3382
0
  cupsFilePuts(fp, "*LanguageEncoding: ISOLatin1\n");
3383
0
  cupsFilePuts(fp, "*PSVersion: \"(3010.000) 0\"\n");
3384
0
  cupsFilePuts(fp, "*LanguageLevel: \"3\"\n");
3385
0
  cupsFilePuts(fp, "*FileSystem: False\n");
3386
0
  cupsFilePuts(fp, "*PCFileName: \"ippeve.ppd\"\n");
3387
0
  cupsFilePrintf(fp, "*Manufacturer: \"%s\"\n", make);
3388
0
  cupsFilePrintf(fp, "*ModelName: \"%s\"\n", model);
3389
0
  cupsFilePrintf(fp, "*Product: \"(%s)\"\n", model);
3390
0
  cupsFilePrintf(fp, "*NickName: \"%s - IPP Everywhere\"\n", model);
3391
0
  cupsFilePrintf(fp, "*ShortNickName: \"%s - IPP Everywhere\"\n", model);
3392
3393
0
  if (ippGetBoolean(ippFindAttribute(supported, "color-supported", IPP_TAG_BOOLEAN), 0))
3394
0
    cupsFilePuts(fp, "*ColorDevice: True\n");
3395
0
  else
3396
0
    cupsFilePuts(fp, "*ColorDevice: False\n");
3397
3398
0
  cupsFilePrintf(fp, "*cupsVersion: %d.%d\n", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR);
3399
#ifdef __APPLE__
3400
  cupsFilePrintf(fp, "*APAirPrint: True\n");
3401
#endif // __APPLE__
3402
0
  cupsFilePuts(fp, "*cupsSNMPSupplies: False\n");
3403
3404
0
  if ((lang_supp = ippFindAttribute(supported, "printer-strings-languages-supported", IPP_TAG_LANGUAGE)) != NULL)
3405
0
  {
3406
   /*
3407
    * List supported languages...
3408
    */
3409
3410
0
    for (i = 0, count = ippGetCount(lang_supp), prefix = "*cupsLanguages: \""; i < count; i ++)
3411
0
    {
3412
0
      keyword = ippGetString(lang_supp, i, NULL);
3413
3414
0
      if ((lang = cups_get_strings(&http, printer_uri, keyword)) != NULL)
3415
0
      {
3416
       /*
3417
        * Add language...
3418
        */
3419
3420
0
        lang->next = langs;
3421
0
        langs      = lang;
3422
3423
0
        cupsFilePrintf(fp, "%s%s", prefix, keyword);
3424
0
        prefix = " ";
3425
0
      }
3426
0
    }
3427
3428
0
    if (!strcmp(prefix, " "))
3429
0
      cupsFilePuts(fp, "\"\n");
3430
0
  }
3431
3432
0
  if (http)
3433
0
  {
3434
0
    httpClose(http);
3435
0
    http = NULL;
3436
0
  }
3437
3438
  // Find the English localization...
3439
0
  for (lang = langs; lang; lang = lang->next)
3440
0
  {
3441
0
    if (!strcmp(lang->language, "en"))
3442
0
      break;
3443
0
  }
3444
3445
0
  base_lang = cupsLangGet("en");
3446
3447
0
  if ((attr = ippFindAttribute(supported, "printer-more-info", IPP_TAG_URI)) != NULL && ippValidateAttribute(attr))
3448
0
    cupsFilePrintf(fp, "*APSupplies: \"%s\"\n", ippGetString(attr, 0, NULL));
3449
3450
0
  if ((attr = ippFindAttribute(supported, "printer-charge-info-uri", IPP_TAG_URI)) != NULL && ippValidateAttribute(attr))
3451
0
    cupsFilePrintf(fp, "*cupsChargeInfoURI: \"%s\"\n", ippGetString(attr, 0, NULL));
3452
3453
 /*
3454
  * Accounting...
3455
  */
3456
3457
0
  if (ippGetBoolean(ippFindAttribute(supported, "job-account-id-supported", IPP_TAG_BOOLEAN), 0))
3458
0
    cupsFilePuts(fp, "*cupsJobAccountId: True\n");
3459
3460
0
  if (ippGetBoolean(ippFindAttribute(supported, "job-accounting-user-id-supported", IPP_TAG_BOOLEAN), 0))
3461
0
    cupsFilePuts(fp, "*cupsJobAccountingUserId: True\n");
3462
3463
0
  if ((attr = ippFindAttribute(supported, "printer-privacy-policy-uri", IPP_TAG_URI)) != NULL && ippValidateAttribute(attr))
3464
0
    cupsFilePrintf(fp, "*cupsPrivacyURI: \"%s\"\n", ippGetString(attr, 0, NULL));
3465
3466
0
  if ((attr = ippFindAttribute(supported, "printer-mandatory-job-attributes", IPP_TAG_KEYWORD)) != NULL && ippValidateAttribute(attr))
3467
0
  {
3468
0
    for (i = 0, count = ippGetCount(attr), prefix = "*cupsMandatory: \""; i < count; i ++)
3469
0
    {
3470
0
      keyword = ippGetString(attr, i, NULL);
3471
3472
0
      if (strcmp(keyword, "attributes-charset") && strcmp(keyword, "attributes-natural-language") && strcmp(keyword, "printer-uri"))
3473
0
      {
3474
0
        cupsFilePrintf(fp, "%s%s", prefix, keyword);
3475
0
        prefix = " ";
3476
0
      }
3477
0
    }
3478
3479
0
    if (!strcmp(prefix, " "))
3480
0
      cupsFilePuts(fp, "\"\n");
3481
0
  }
3482
3483
0
  if ((attr = ippFindAttribute(supported, "printer-requested-job-attributes", IPP_TAG_KEYWORD)) != NULL && ippValidateAttribute(attr))
3484
0
  {
3485
0
    for (i = 0, count = ippGetCount(attr), prefix = "*cupsRequested: \""; i < count; i ++)
3486
0
    {
3487
0
      keyword = ippGetString(attr, i, NULL);
3488
3489
0
      if (strcmp(keyword, "attributes-charset") && strcmp(keyword, "attributes-natural-language") && strcmp(keyword, "printer-uri"))
3490
0
      {
3491
0
        cupsFilePrintf(fp, "%s%s", prefix, keyword);
3492
0
        prefix = " ";
3493
0
      }
3494
0
    }
3495
3496
0
    if (!strcmp(prefix, " "))
3497
0
      cupsFilePuts(fp, "\"\n");
3498
0
  }
3499
3500
 /*
3501
  * Password/PIN printing...
3502
  */
3503
3504
0
  if ((attr = ippFindAttribute(supported, "job-password-supported", IPP_TAG_INTEGER)) != NULL)
3505
0
  {
3506
0
    char  pattern[33];    /* Password pattern */
3507
0
    int   maxlen = ippGetInteger(attr, 0);
3508
          /* Maximum length */
3509
0
    const char  *repertoire = ippGetString(ippFindAttribute(supported, "job-password-repertoire-configured", IPP_TAG_KEYWORD), 0, NULL);
3510
          /* Type of password */
3511
3512
0
    if (maxlen > (int)(sizeof(pattern) - 1))
3513
0
      maxlen = (int)sizeof(pattern) - 1;
3514
3515
0
    if (!repertoire || !strcmp(repertoire, "iana_us-ascii_digits"))
3516
0
      memset(pattern, '1', (size_t)maxlen);
3517
0
    else if (!strcmp(repertoire, "iana_us-ascii_letters"))
3518
0
      memset(pattern, 'A', (size_t)maxlen);
3519
0
    else if (!strcmp(repertoire, "iana_us-ascii_complex"))
3520
0
      memset(pattern, 'C', (size_t)maxlen);
3521
0
    else if (!strcmp(repertoire, "iana_us-ascii_any"))
3522
0
      memset(pattern, '.', (size_t)maxlen);
3523
0
    else if (!strcmp(repertoire, "iana_utf-8_digits"))
3524
0
      memset(pattern, 'N', (size_t)maxlen);
3525
0
    else if (!strcmp(repertoire, "iana_utf-8_letters"))
3526
0
      memset(pattern, 'U', (size_t)maxlen);
3527
0
    else
3528
0
      memset(pattern, '*', (size_t)maxlen);
3529
3530
0
    pattern[maxlen] = '\0';
3531
3532
0
    cupsFilePrintf(fp, "*cupsJobPassword: \"%s\"\n", pattern);
3533
0
  }
3534
3535
 /*
3536
  * Filters...
3537
  */
3538
3539
0
  if ((attr = ippFindAttribute(supported, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL)
3540
0
  {
3541
0
    is_apple = ippContainsString(attr, "image/urf") && (ippFindAttribute(supported, "urf-supported", IPP_TAG_KEYWORD) != NULL);
3542
0
    is_pdf   = ippContainsString(attr, "application/pdf");
3543
0
    is_pwg   = ippContainsString(attr, "image/pwg-raster") && !is_apple &&
3544
0
         (ippFindAttribute(supported, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION) != NULL) &&
3545
0
         (ippFindAttribute(supported, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD) != NULL);
3546
3547
0
    if (ippContainsString(attr, "image/jpeg"))
3548
0
      cupsFilePuts(fp, "*cupsFilter2: \"image/jpeg image/jpeg 0 -\"\n");
3549
0
    if (ippContainsString(attr, "image/png"))
3550
0
      cupsFilePuts(fp, "*cupsFilter2: \"image/png image/png 0 -\"\n");
3551
0
    if (is_pdf)
3552
0
    {
3553
     /*
3554
      * Don't locally filter PDF content when printing to a CUPS shared
3555
      * printer, otherwise the options will be applied twice...
3556
      */
3557
3558
0
      if (ippContainsString(attr, "application/vnd.cups-pdf"))
3559
0
        cupsFilePuts(fp, "*cupsFilter2: \"application/pdf application/pdf 0 -\"\n");
3560
0
      else
3561
0
        cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-pdf application/pdf 10 -\"\n");
3562
0
    }
3563
0
    else
3564
0
      cupsFilePuts(fp, "*cupsManualCopies: True\n");
3565
0
    if (is_apple)
3566
0
      cupsFilePuts(fp, "*cupsFilter2: \"image/urf image/urf 100 -\"\n");
3567
0
    if (is_pwg)
3568
0
      cupsFilePuts(fp, "*cupsFilter2: \"image/pwg-raster image/pwg-raster 100 -\"\n");
3569
0
  }
3570
3571
0
  if (!is_apple && !is_pdf && !is_pwg)
3572
0
    goto bad_ppd;
3573
3574
 /*
3575
  * PageSize/PageRegion/ImageableArea/PaperDimension
3576
  */
3577
3578
0
  if ((attr = ippFindAttribute(supported, "media-bottom-margin-supported", IPP_TAG_INTEGER)) != NULL)
3579
0
  {
3580
0
    for (i = 1, bottom = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3581
0
      if (ippGetInteger(attr, i) > bottom)
3582
0
        bottom = ippGetInteger(attr, i);
3583
0
  }
3584
0
  else
3585
0
    bottom = 1270;
3586
3587
0
  if ((attr = ippFindAttribute(supported, "media-left-margin-supported", IPP_TAG_INTEGER)) != NULL)
3588
0
  {
3589
0
    for (i = 1, left = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3590
0
      if (ippGetInteger(attr, i) > left)
3591
0
        left = ippGetInteger(attr, i);
3592
0
  }
3593
0
  else
3594
0
    left = 635;
3595
3596
0
  if ((attr = ippFindAttribute(supported, "media-right-margin-supported", IPP_TAG_INTEGER)) != NULL)
3597
0
  {
3598
0
    for (i = 1, right = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3599
0
      if (ippGetInteger(attr, i) > right)
3600
0
        right = ippGetInteger(attr, i);
3601
0
  }
3602
0
  else
3603
0
    right = 635;
3604
3605
0
  if ((attr = ippFindAttribute(supported, "media-top-margin-supported", IPP_TAG_INTEGER)) != NULL)
3606
0
  {
3607
0
    for (i = 1, top = ippGetInteger(attr, 0), count = ippGetCount(attr); i < count; i ++)
3608
0
      if (ippGetInteger(attr, i) > top)
3609
0
        top = ippGetInteger(attr, i);
3610
0
  }
3611
0
  else
3612
0
    top = 1270;
3613
3614
0
  if ((defattr = ippFindAttribute(supported, "media-col-default", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3615
0
  {
3616
0
    if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3617
0
    {
3618
0
      media_size = ippGetCollection(attr, 0);
3619
0
      x_dim      = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
3620
0
      y_dim      = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
3621
3622
0
      if (x_dim && y_dim && (pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0))) != NULL)
3623
0
  cupsCopyString(ppdname, pwg->ppd, sizeof(ppdname));
3624
0
      else
3625
0
  cupsCopyString(ppdname, "Unknown", sizeof(ppdname));
3626
0
    }
3627
0
    else
3628
0
      cupsCopyString(ppdname, "Unknown", sizeof(ppdname));
3629
0
  }
3630
0
  else if ((pwg = pwgMediaForPWG(ippGetString(ippFindAttribute(supported, "media-default", IPP_TAG_ZERO), 0, NULL))) != NULL)
3631
0
    cupsCopyString(ppdname, pwg->ppd, sizeof(ppdname));
3632
0
  else
3633
0
    cupsCopyString(ppdname, "Unknown", sizeof(ppdname));
3634
3635
0
  sizes = cupsArrayNew3((cups_array_func_t)pwg_compare_sizes, NULL, NULL, 0, (cups_acopy_func_t)pwg_copy_size, _cupsArrayFree);
3636
3637
0
  if ((attr = ippFindAttribute(supported, "media-col-database", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3638
0
  {
3639
0
    for (i = 0, count = ippGetCount(attr); i < count; i ++)
3640
0
    {
3641
0
      cups_size_t temp;   /* Current size */
3642
0
      ipp_attribute_t *margin;  /* media-xxx-margin attribute */
3643
3644
0
      media_col   = ippGetCollection(attr, i);
3645
0
      media_size  = ippGetCollection(ippFindAttribute(media_col, "media-size", IPP_TAG_BEGIN_COLLECTION), 0);
3646
0
      x_dim       = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
3647
0
      y_dim       = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
3648
0
      pwg         = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0));
3649
3650
0
      if (pwg)
3651
0
      {
3652
0
  temp.width  = pwg->width;
3653
0
  temp.length = pwg->length;
3654
3655
0
  if ((margin = ippFindAttribute(media_col, "media-bottom-margin", IPP_TAG_INTEGER)) != NULL)
3656
0
    temp.bottom = ippGetInteger(margin, 0);
3657
0
  else
3658
0
    temp.bottom = bottom;
3659
3660
0
  if ((margin = ippFindAttribute(media_col, "media-left-margin", IPP_TAG_INTEGER)) != NULL)
3661
0
    temp.left = ippGetInteger(margin, 0);
3662
0
  else
3663
0
    temp.left = left;
3664
3665
0
  if ((margin = ippFindAttribute(media_col, "media-right-margin", IPP_TAG_INTEGER)) != NULL)
3666
0
    temp.right = ippGetInteger(margin, 0);
3667
0
  else
3668
0
    temp.right = right;
3669
3670
0
  if ((margin = ippFindAttribute(media_col, "media-top-margin", IPP_TAG_INTEGER)) != NULL)
3671
0
    temp.top = ippGetInteger(margin, 0);
3672
0
  else
3673
0
    temp.top = top;
3674
3675
0
  if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && temp.top == 0)
3676
0
    snprintf(temp.media, sizeof(temp.media), "%s.Borderless", pwg->ppd);
3677
0
  else
3678
0
    cupsCopyString(temp.media, pwg->ppd, sizeof(temp.media));
3679
3680
0
  if (!cupsArrayFind(sizes, &temp))
3681
0
    cupsArrayAdd(sizes, &temp);
3682
0
      }
3683
0
      else if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3684
0
      {
3685
       /*
3686
  * Custom size - record the min/max values...
3687
  */
3688
3689
0
  int lower, upper;   /* Range values */
3690
3691
0
  if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
3692
0
    lower = ippGetRange(x_dim, 0, &upper);
3693
0
  else
3694
0
    lower = upper = ippGetInteger(x_dim, 0);
3695
3696
0
  if (lower < min_width)
3697
0
    min_width = lower;
3698
0
  if (upper > max_width)
3699
0
    max_width = upper;
3700
3701
0
  if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3702
0
    lower = ippGetRange(y_dim, 0, &upper);
3703
0
  else
3704
0
    lower = upper = ippGetInteger(y_dim, 0);
3705
3706
0
  if (lower < min_length)
3707
0
    min_length = lower;
3708
0
  if (upper > max_length)
3709
0
    max_length = upper;
3710
0
      }
3711
0
    }
3712
3713
0
    if ((max_width == 0 || max_length == 0) && (attr = ippFindAttribute(supported, "media-size-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3714
0
    {
3715
     /*
3716
      * Some printers don't list custom size support in media-col-database...
3717
      */
3718
3719
0
      for (i = 0, count = ippGetCount(attr); i < count; i ++)
3720
0
      {
3721
0
  media_size  = ippGetCollection(attr, i);
3722
0
  x_dim       = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
3723
0
  y_dim       = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
3724
3725
0
  if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3726
0
  {
3727
   /*
3728
    * Custom size - record the min/max values...
3729
    */
3730
3731
0
    int lower, upper;   /* Range values */
3732
3733
0
    if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
3734
0
      lower = ippGetRange(x_dim, 0, &upper);
3735
0
    else
3736
0
      lower = upper = ippGetInteger(x_dim, 0);
3737
3738
0
    if (lower < min_width)
3739
0
      min_width = lower;
3740
0
    if (upper > max_width)
3741
0
      max_width = upper;
3742
3743
0
    if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3744
0
      lower = ippGetRange(y_dim, 0, &upper);
3745
0
    else
3746
0
      lower = upper = ippGetInteger(y_dim, 0);
3747
3748
0
    if (lower < min_length)
3749
0
      min_length = lower;
3750
0
    if (upper > max_length)
3751
0
      max_length = upper;
3752
0
  }
3753
0
      }
3754
0
    }
3755
0
  }
3756
0
  else if ((attr = ippFindAttribute(supported, "media-size-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
3757
0
  {
3758
0
    for (i = 0, count = ippGetCount(attr); i < count; i ++)
3759
0
    {
3760
0
      cups_size_t temp;   /* Current size */
3761
3762
0
      media_size  = ippGetCollection(attr, i);
3763
0
      x_dim       = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
3764
0
      y_dim       = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
3765
0
      pwg         = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0));
3766
3767
0
      if (pwg)
3768
0
      {
3769
0
  temp.width  = pwg->width;
3770
0
  temp.length = pwg->length;
3771
0
  temp.bottom = bottom;
3772
0
  temp.left   = left;
3773
0
  temp.right  = right;
3774
0
  temp.top    = top;
3775
3776
0
  if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && temp.top == 0)
3777
0
    snprintf(temp.media, sizeof(temp.media), "%s.Borderless", pwg->ppd);
3778
0
  else
3779
0
    cupsCopyString(temp.media, pwg->ppd, sizeof(temp.media));
3780
3781
0
  if (!cupsArrayFind(sizes, &temp))
3782
0
    cupsArrayAdd(sizes, &temp);
3783
0
      }
3784
0
      else if (ippGetValueTag(x_dim) == IPP_TAG_RANGE || ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3785
0
      {
3786
       /*
3787
  * Custom size - record the min/max values...
3788
  */
3789
3790
0
  int lower, upper;   /* Range values */
3791
3792
0
  if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
3793
0
    lower = ippGetRange(x_dim, 0, &upper);
3794
0
  else
3795
0
    lower = upper = ippGetInteger(x_dim, 0);
3796
3797
0
  if (lower < min_width)
3798
0
    min_width = lower;
3799
0
  if (upper > max_width)
3800
0
    max_width = upper;
3801
3802
0
  if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
3803
0
    lower = ippGetRange(y_dim, 0, &upper);
3804
0
  else
3805
0
    lower = upper = ippGetInteger(y_dim, 0);
3806
3807
0
  if (lower < min_length)
3808
0
    min_length = lower;
3809
0
  if (upper > max_length)
3810
0
    max_length = upper;
3811
0
      }
3812
0
    }
3813
0
  }
3814
0
  else if ((attr = ippFindAttribute(supported, "media-supported", IPP_TAG_ZERO)) != NULL)
3815
0
  {
3816
0
    for (i = 0, count = ippGetCount(attr); i < count; i ++)
3817
0
    {
3818
0
      const char  *pwg_size = ippGetString(attr, i, NULL);
3819
              /* PWG size name */
3820
0
      cups_size_t temp;   /* Current size */
3821
3822
0
      if ((pwg = pwgMediaForPWG(pwg_size)) != NULL)
3823
0
      {
3824
0
        if (strstr(pwg_size, "_max_") || strstr(pwg_size, "_max."))
3825
0
        {
3826
0
          if (pwg->width > max_width)
3827
0
            max_width = pwg->width;
3828
0
          if (pwg->length > max_length)
3829
0
            max_length = pwg->length;
3830
0
        }
3831
0
        else if (strstr(pwg_size, "_min_") || strstr(pwg_size, "_min."))
3832
0
        {
3833
0
          if (pwg->width < min_width)
3834
0
            min_width = pwg->width;
3835
0
          if (pwg->length < min_length)
3836
0
            min_length = pwg->length;
3837
0
        }
3838
0
        else
3839
0
        {
3840
0
    temp.width  = pwg->width;
3841
0
    temp.length = pwg->length;
3842
0
    temp.bottom = bottom;
3843
0
    temp.left   = left;
3844
0
    temp.right  = right;
3845
0
    temp.top    = top;
3846
3847
0
    if (temp.bottom == 0 && temp.left == 0 && temp.right == 0 && temp.top == 0)
3848
0
      snprintf(temp.media, sizeof(temp.media), "%s.Borderless", pwg->ppd);
3849
0
    else
3850
0
      cupsCopyString(temp.media, pwg->ppd, sizeof(temp.media));
3851
3852
0
    if (!cupsArrayFind(sizes, &temp))
3853
0
      cupsArrayAdd(sizes, &temp);
3854
0
  }
3855
0
      }
3856
0
    }
3857
0
  }
3858
3859
0
  if (cupsArrayCount(sizes) > 0)
3860
0
  {
3861
   /*
3862
    * List all of the standard sizes...
3863
    */
3864
3865
0
    char  tleft[256],   /* Left string */
3866
0
    tbottom[256],   /* Bottom string */
3867
0
    tright[256],    /* Right string */
3868
0
    ttop[256],    /* Top string */
3869
0
    twidth[256],    /* Width string */
3870
0
    tlength[256];   /* Length string */
3871
3872
0
    cupsFilePrintf(fp, "*OpenUI *PageSize: PickOne\n"
3873
0
           "*OrderDependency: 10 AnySetup *PageSize\n"
3874
0
                       "*DefaultPageSize: %s\n", ppdname);
3875
0
    for (size = (cups_size_t *)cupsArrayFirst(sizes); size; size = (cups_size_t *)cupsArrayNext(sizes))
3876
0
    {
3877
0
      _cupsStrFormatd(twidth, twidth + sizeof(twidth), size->width * 72.0 / 2540.0, loc);
3878
0
      _cupsStrFormatd(tlength, tlength + sizeof(tlength), size->length * 72.0 / 2540.0, loc);
3879
3880
0
      cupsFilePrintf(fp, "*PageSize %s: \"<</PageSize[%s %s]>>setpagedevice\"\n", size->media, twidth, tlength);
3881
0
    }
3882
0
    cupsFilePuts(fp, "*CloseUI: *PageSize\n");
3883
3884
0
    cupsFilePrintf(fp, "*OpenUI *PageRegion: PickOne\n"
3885
0
                       "*OrderDependency: 10 AnySetup *PageRegion\n"
3886
0
                       "*DefaultPageRegion: %s\n", ppdname);
3887
0
    for (size = (cups_size_t *)cupsArrayFirst(sizes); size; size = (cups_size_t *)cupsArrayNext(sizes))
3888
0
    {
3889
0
      _cupsStrFormatd(twidth, twidth + sizeof(twidth), size->width * 72.0 / 2540.0, loc);
3890
0
      _cupsStrFormatd(tlength, tlength + sizeof(tlength), size->length * 72.0 / 2540.0, loc);
3891
3892
0
      cupsFilePrintf(fp, "*PageRegion %s: \"<</PageSize[%s %s]>>setpagedevice\"\n", size->media, twidth, tlength);
3893
0
    }
3894
0
    cupsFilePuts(fp, "*CloseUI: *PageRegion\n");
3895
3896
0
    cupsFilePrintf(fp, "*DefaultImageableArea: %s\n"
3897
0
           "*DefaultPaperDimension: %s\n", ppdname, ppdname);
3898
3899
0
    for (size = (cups_size_t *)cupsArrayFirst(sizes); size; size = (cups_size_t *)cupsArrayNext(sizes))
3900
0
    {
3901
0
      _cupsStrFormatd(tleft, tleft + sizeof(tleft), size->left * 72.0 / 2540.0, loc);
3902
0
      _cupsStrFormatd(tbottom, tbottom + sizeof(tbottom), size->bottom * 72.0 / 2540.0, loc);
3903
0
      _cupsStrFormatd(tright, tright + sizeof(tright), (size->width - size->right) * 72.0 / 2540.0, loc);
3904
0
      _cupsStrFormatd(ttop, ttop + sizeof(ttop), (size->length - size->top) * 72.0 / 2540.0, loc);
3905
0
      _cupsStrFormatd(twidth, twidth + sizeof(twidth), size->width * 72.0 / 2540.0, loc);
3906
0
      _cupsStrFormatd(tlength, tlength + sizeof(tlength), size->length * 72.0 / 2540.0, loc);
3907
3908
0
      cupsFilePrintf(fp, "*ImageableArea %s: \"%s %s %s %s\"\n", size->media, tleft, tbottom, tright, ttop);
3909
0
      cupsFilePrintf(fp, "*PaperDimension %s: \"%s %s\"\n", size->media, twidth, tlength);
3910
0
    }
3911
3912
0
    cupsArrayDelete(sizes);
3913
3914
   /*
3915
    * Custom size support...
3916
    */
3917
3918
0
    if (max_width > 0 && min_width < INT_MAX && max_length > 0 && min_length < INT_MAX)
3919
0
    {
3920
0
      char  tmax[256], tmin[256]; /* Min/max values */
3921
3922
0
      _cupsStrFormatd(tleft, tleft + sizeof(tleft), left * 72.0 / 2540.0, loc);
3923
0
      _cupsStrFormatd(tbottom, tbottom + sizeof(tbottom), bottom * 72.0 / 2540.0, loc);
3924
0
      _cupsStrFormatd(tright, tright + sizeof(tright), right * 72.0 / 2540.0, loc);
3925
0
      _cupsStrFormatd(ttop, ttop + sizeof(ttop), top * 72.0 / 2540.0, loc);
3926
3927
0
      cupsFilePrintf(fp, "*HWMargins: \"%s %s %s %s\"\n", tleft, tbottom, tright, ttop);
3928
3929
0
      _cupsStrFormatd(tmax, tmax + sizeof(tmax), max_width * 72.0 / 2540.0, loc);
3930
0
      _cupsStrFormatd(tmin, tmin + sizeof(tmin), min_width * 72.0 / 2540.0, loc);
3931
0
      cupsFilePrintf(fp, "*ParamCustomPageSize Width: 1 points %s %s\n", tmin, tmax);
3932
3933
0
      _cupsStrFormatd(tmax, tmax + sizeof(tmax), max_length * 72.0 / 2540.0, loc);
3934
0
      _cupsStrFormatd(tmin, tmin + sizeof(tmin), min_length * 72.0 / 2540.0, loc);
3935
0
      cupsFilePrintf(fp, "*ParamCustomPageSize Height: 2 points %s %s\n", tmin, tmax);
3936
3937
0
      cupsFilePuts(fp, "*ParamCustomPageSize WidthOffset: 3 points 0 0\n");
3938
0
      cupsFilePuts(fp, "*ParamCustomPageSize HeightOffset: 4 points 0 0\n");
3939
0
      cupsFilePuts(fp, "*ParamCustomPageSize Orientation: 5 int 0 3\n");
3940
0
      cupsFilePuts(fp, "*CustomPageSize True: \"pop pop pop <</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice\"\n");
3941
0
    }
3942
0
  }
3943
0
  else
3944
0
  {
3945
0
    cupsArrayDelete(sizes);
3946
0
    goto bad_ppd;
3947
0
  }
3948
3949
 /*
3950
  * InputSlot...
3951
  */
3952
3953
0
  if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-source", IPP_TAG_ZERO)) != NULL)
3954
0
    pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
3955
0
  else
3956
0
    ppdname[0] = '\0';
3957
3958
0
  if ((attr = ippFindAttribute(supported, "media-source-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
3959
0
  {
3960
0
    int have_default = ppdname[0] != '\0';
3961
          /* Do we have a default InputSlot? */
3962
0
    static const char * const sources[] =
3963
0
    {         /* Standard "media-source" strings */
3964
0
      "auto",
3965
0
      "main",
3966
0
      "alternate",
3967
0
      "large-capacity",
3968
0
      "manual",
3969
0
      "envelope",
3970
0
      "disc",
3971
0
      "photo",
3972
0
      "hagaki",
3973
0
      "main-roll",
3974
0
      "alternate-roll",
3975
0
      "top",
3976
0
      "middle",
3977
0
      "bottom",
3978
0
      "side",
3979
0
      "left",
3980
0
      "right",
3981
0
      "center",
3982
0
      "rear",
3983
0
      "by-pass-tray",
3984
0
      "tray-1",
3985
0
      "tray-2",
3986
0
      "tray-3",
3987
0
      "tray-4",
3988
0
      "tray-5",
3989
0
      "tray-6",
3990
0
      "tray-7",
3991
0
      "tray-8",
3992
0
      "tray-9",
3993
0
      "tray-10",
3994
0
      "tray-11",
3995
0
      "tray-12",
3996
0
      "tray-13",
3997
0
      "tray-14",
3998
0
      "tray-15",
3999
0
      "tray-16",
4000
0
      "tray-17",
4001
0
      "tray-18",
4002
0
      "tray-19",
4003
0
      "tray-20",
4004
0
      "roll-1",
4005
0
      "roll-2",
4006
0
      "roll-3",
4007
0
      "roll-4",
4008
0
      "roll-5",
4009
0
      "roll-6",
4010
0
      "roll-7",
4011
0
      "roll-8",
4012
0
      "roll-9",
4013
0
      "roll-10"
4014
0
    };
4015
4016
0
    cupsFilePuts(fp, "*OpenUI *InputSlot: PickOne\n"
4017
0
                     "*OrderDependency: 10 AnySetup *InputSlot\n");
4018
0
    if (have_default)
4019
0
      cupsFilePrintf(fp, "*DefaultInputSlot: %s\n", ppdname);
4020
4021
0
    for (i = 0; i < count; i ++)
4022
0
    {
4023
0
      keyword = ippGetString(attr, i, NULL);
4024
4025
0
      pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4026
4027
0
      if (i == 0 && !have_default)
4028
0
  cupsFilePrintf(fp, "*DefaultInputSlot: %s\n", ppdname);
4029
4030
0
      for (j = 0; j < (int)(sizeof(sources) / sizeof(sources[0])); j ++)
4031
0
      {
4032
0
        if (!strcmp(sources[j], keyword))
4033
0
  {
4034
0
    snprintf(msgid, sizeof(msgid), "media-source.%s", keyword);
4035
4036
0
    cupsFilePrintf(fp, "*InputSlot %s/%s: \"<</MediaPosition %d>>setpagedevice\"\n", ppdname, ppd_get_string(base_lang, lang, msgid, ppdtext, sizeof(ppdtext)), j);
4037
0
    ppd_put_strings(fp, langs, "InputSlot", ppdname, msgid);
4038
0
    break;
4039
0
  }
4040
0
      }
4041
0
    }
4042
0
    cupsFilePuts(fp, "*CloseUI: *InputSlot\n");
4043
0
  }
4044
4045
 /*
4046
  * MediaType...
4047
  */
4048
4049
0
  if ((attr = ippFindAttribute(ippGetCollection(defattr, 0), "media-type", IPP_TAG_ZERO)) != NULL)
4050
0
    pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
4051
0
  else
4052
0
    cupsCopyString(ppdname, "Unknown", sizeof(ppdname));
4053
4054
0
  if ((attr = ippFindAttribute(supported, "media-type-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
4055
0
  {
4056
0
    cupsFilePrintf(fp, "*OpenUI *MediaType: PickOne\n"
4057
0
                       "*OrderDependency: 10 AnySetup *MediaType\n"
4058
0
                       "*DefaultMediaType: %s\n", ppdname);
4059
0
    for (i = 0; i < count; i ++)
4060
0
    {
4061
0
      keyword = ippGetString(attr, i, NULL);
4062
4063
0
      pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4064
4065
0
      snprintf(msgid, sizeof(msgid), "media-type.%s", keyword);
4066
4067
0
      cupsFilePrintf(fp, "*MediaType %s/%s: \"<</MediaType(%s)>>setpagedevice\"\n", ppdname, ppd_get_string(base_lang, lang, msgid, ppdtext, sizeof(ppdtext)), keyword);
4068
0
      ppd_put_strings(fp, langs, "MediaType", ppdname, msgid);
4069
0
    }
4070
0
    cupsFilePuts(fp, "*CloseUI: *MediaType\n");
4071
0
  }
4072
4073
 /*
4074
  * cupsPrintQuality and DefaultResolution...
4075
  */
4076
4077
0
  quality = ippFindAttribute(supported, "print-quality-supported", IPP_TAG_ENUM);
4078
4079
0
  if ((attr = ippFindAttribute(supported, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
4080
0
  {
4081
0
    int lowdpi = 0, hidpi = 0;    /* Lower and higher resolution */
4082
4083
0
    for (i = 0, count = ippGetCount(attr); i < count; i ++)
4084
0
    {
4085
0
      const char *rs = ippGetString(attr, i, NULL);
4086
          /* RS value */
4087
4088
0
      if (_cups_strncasecmp(rs, "RS", 2))
4089
0
        continue;
4090
4091
0
      lowdpi = atoi(rs + 2);
4092
0
      if ((rs = strrchr(rs, '-')) != NULL)
4093
0
        hidpi = atoi(rs + 1);
4094
0
      else
4095
0
        hidpi = lowdpi;
4096
0
      break;
4097
0
    }
4098
4099
0
    if (lowdpi == 0)
4100
0
    {
4101
     /*
4102
      * Invalid "urf-supported" value...
4103
      */
4104
4105
0
      goto bad_ppd;
4106
0
    }
4107
0
    else
4108
0
    {
4109
     /*
4110
      * Generate print qualities based on low and high DPIs...
4111
      */
4112
4113
0
      cupsFilePrintf(fp, "*DefaultResolution: %ddpi\n", lowdpi);
4114
4115
0
      cupsFilePuts(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4116
0
           "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4117
0
           "*DefaultcupsPrintQuality: Normal\n");
4118
0
      if ((lowdpi & 1) == 0)
4119
0
      {
4120
0
  cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n", lowdpi, lowdpi / 2);
4121
0
  have_qdraft = 1;
4122
0
      }
4123
0
      else if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4124
0
      {
4125
0
  cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*", lowdpi, lowdpi);
4126
0
  have_qdraft = 1;
4127
0
      }
4128
4129
0
      cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n", lowdpi, lowdpi);
4130
4131
0
      if (hidpi > lowdpi || ippContainsInteger(quality, IPP_QUALITY_HIGH))
4132
0
      {
4133
0
  cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n", hidpi, hidpi);
4134
0
  have_qhigh = 1;
4135
0
      }
4136
4137
0
      cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4138
0
    }
4139
0
  }
4140
0
  else if ((attr = ippFindAttribute(supported, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
4141
0
  {
4142
   /*
4143
    * Make a sorted list of resolutions.
4144
    */
4145
4146
0
    count = ippGetCount(attr);
4147
0
    if (count > (int)(sizeof(resolutions) / sizeof(resolutions[0])))
4148
0
      count = (int)(sizeof(resolutions) / sizeof(resolutions[0]));
4149
4150
0
    resolutions[0] = 0; /* Not in loop to silence Clang static analyzer... */
4151
0
    for (i = 1; i < count; i ++)
4152
0
      resolutions[i] = i;
4153
4154
0
    for (i = 0; i < (count - 1); i ++)
4155
0
    {
4156
0
      for (j = i + 1; j < count; j ++)
4157
0
      {
4158
0
        int       ix, iy,               /* First X and Y resolution */
4159
0
                  jx, jy,               /* Second X and Y resolution */
4160
0
                  temp;                 /* Swap variable */
4161
0
        ipp_res_t units;                /* Resolution units */
4162
4163
0
        ix = ippGetResolution(attr, resolutions[i], &iy, &units);
4164
0
        jx = ippGetResolution(attr, resolutions[j], &jy, &units);
4165
4166
0
        if (ix > jx || (ix == jx && iy > jy))
4167
0
        {
4168
         /*
4169
          * Swap these two resolutions...
4170
          */
4171
4172
0
          temp           = resolutions[i];
4173
0
          resolutions[i] = resolutions[j];
4174
0
          resolutions[j] = temp;
4175
0
        }
4176
0
      }
4177
0
    }
4178
4179
   /*
4180
    * Generate print quality options...
4181
    */
4182
4183
0
    pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, ppdname, sizeof(ppdname));
4184
0
    cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
4185
4186
0
    cupsFilePuts(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4187
0
         "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4188
0
         "*DefaultcupsPrintQuality: Normal\n");
4189
0
    if (count > 2 || ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4190
0
    {
4191
0
      pwg_ppdize_resolution(attr, resolutions[0], &xres, &yres, NULL, 0);
4192
0
      cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4193
0
      have_qdraft = 1;
4194
0
    }
4195
4196
0
    pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, NULL, 0);
4197
0
    cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4198
4199
0
    if (count > 1 || ippContainsInteger(quality, IPP_QUALITY_HIGH))
4200
0
    {
4201
0
      pwg_ppdize_resolution(attr, resolutions[count - 1], &xres, &yres, NULL, 0);
4202
0
      cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4203
0
      have_qhigh = 1;
4204
0
    }
4205
4206
0
    cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4207
0
  }
4208
0
  else if (is_apple || is_pwg)
4209
0
  {
4210
0
    goto bad_ppd;
4211
0
  }
4212
0
  else
4213
0
  {
4214
0
    if ((attr = ippFindAttribute(supported, "printer-resolution-default", IPP_TAG_RESOLUTION)) != NULL)
4215
0
    {
4216
0
      pwg_ppdize_resolution(attr, 0, &xres, &yres, ppdname, sizeof(ppdname));
4217
0
    }
4218
0
    else
4219
0
    {
4220
0
      xres = yres = 300;
4221
0
      cupsCopyString(ppdname, "300dpi", sizeof(ppdname));
4222
0
    }
4223
4224
0
    cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
4225
4226
0
    cupsFilePuts(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
4227
0
                     "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
4228
0
                     "*DefaultcupsPrintQuality: Normal\n");
4229
0
    if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
4230
0
    {
4231
0
      cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*", xres, yres);
4232
0
      have_qdraft = 1;
4233
0
    }
4234
4235
0
    cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4236
4237
0
    if (ippContainsInteger(quality, IPP_QUALITY_HIGH))
4238
0
    {
4239
0
      cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
4240
0
      have_qhigh = 1;
4241
0
    }
4242
0
    cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
4243
0
  }
4244
4245
 /*
4246
  * ColorModel...
4247
  */
4248
4249
0
  if ((defattr = ippFindAttribute(supported, "print-color-mode-default", IPP_TAG_KEYWORD)) == NULL)
4250
0
    defattr = ippFindAttribute(supported, "output-mode-default", IPP_TAG_KEYWORD);
4251
4252
0
  if ((attr = ippFindAttribute(supported, "urf-supported", IPP_TAG_KEYWORD)) == NULL)
4253
0
    if ((attr = ippFindAttribute(supported, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) == NULL)
4254
0
      if ((attr = ippFindAttribute(supported, "print-color-mode-supported", IPP_TAG_KEYWORD)) == NULL)
4255
0
        attr = ippFindAttribute(supported, "output-mode-supported", IPP_TAG_KEYWORD);
4256
4257
0
  if (attr)
4258
0
  {
4259
0
    int wrote_color = 0;
4260
0
    const char *default_color = NULL; /* Default */
4261
4262
0
    if ((keyword = ippGetString(defattr, 0, NULL)) != NULL &&
4263
0
  strcmp(keyword, "auto"))
4264
0
    {
4265
0
      if (!strcmp(keyword, "bi-level"))
4266
0
        default_color = "FastGray";
4267
0
      else if (!strcmp(keyword, "monochrome") || !strcmp(keyword, "auto-monochrome"))
4268
0
        default_color = "Gray";
4269
0
      else
4270
0
        default_color = "RGB";
4271
0
    }
4272
4273
0
    cupsFilePrintf(fp, "*%% ColorModel from %s\n", ippGetName(attr));
4274
4275
0
    for (i = 0, count = ippGetCount(attr); i < count; i ++)
4276
0
    {
4277
0
      keyword = ippGetString(attr, i, NULL);
4278
4279
0
#define PRINTF_COLORMODEL if (!wrote_color) { cupsFilePuts(fp, "*OpenUI *ColorModel: PickOne\n*OrderDependency: 10 AnySetup *ColorModel\n"); wrote_color = 1; }
4280
0
#define PRINTF_COLOROPTION(name,text,cspace,bpp) cupsFilePrintf(fp, "*ColorModel %s/%s: \"<</cupsColorSpace %d/cupsBitsPerColor %d/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n", name, text, cspace, bpp);
4281
4282
0
      if (!strcasecmp(keyword, "black_1") || !strcmp(keyword, "bi-level") || !strcmp(keyword, "process-bi-level"))
4283
0
      {
4284
0
  PRINTF_COLORMODEL
4285
4286
0
  PRINTF_COLOROPTION("FastGray", _("Fast Grayscale"), CUPS_CSPACE_K, 1)
4287
4288
0
  if (!default_color)
4289
0
    default_color = "FastGray";
4290
0
      }
4291
0
      else if (!strcasecmp(keyword, "sgray_8") || !strcmp(keyword, "W8") || !strcmp(keyword, "monochrome") || !strcmp(keyword, "process-monochrome"))
4292
0
      {
4293
0
  PRINTF_COLORMODEL
4294
4295
0
  PRINTF_COLOROPTION("Gray", _("Grayscale"), CUPS_CSPACE_SW, 8)
4296
4297
0
  if (!default_color || (!defattr && !strcmp(default_color, "FastGray")))
4298
0
    default_color = "Gray";
4299
0
      }
4300
0
      else if (!strcasecmp(keyword, "sgray_16") || !strcmp(keyword, "W8-16"))
4301
0
      {
4302
0
  PRINTF_COLORMODEL
4303
4304
0
  if (!strcmp(keyword, "W8-16"))
4305
0
  {
4306
0
    PRINTF_COLOROPTION("Gray", _("Grayscale"), CUPS_CSPACE_SW, 8)
4307
4308
0
    if (!default_color || (!defattr && !strcmp(default_color, "FastGray")))
4309
0
      default_color = "Gray";
4310
0
  }
4311
4312
0
  PRINTF_COLOROPTION("Gray16", _("Deep Gray"), CUPS_CSPACE_SW, 16)
4313
0
      }
4314
0
      else if (!strcasecmp(keyword, "srgb_8") || !strncmp(keyword, "SRGB24", 6) || !strcmp(keyword, "color"))
4315
0
      {
4316
0
  PRINTF_COLORMODEL
4317
4318
0
  PRINTF_COLOROPTION("RGB", _("Color"), CUPS_CSPACE_SRGB, 8)
4319
4320
0
        if (!default_color)
4321
0
    default_color = "RGB";
4322
4323
        // Apparently some printers only advertise color support, so make sure
4324
        // we also do grayscale for these printers...
4325
0
  if (!ippContainsString(attr, "sgray_8") && !ippContainsString(attr, "black_1") && !ippContainsString(attr, "black_8") && !ippContainsString(attr, "W8") && !ippContainsString(attr, "W8-16"))
4326
0
    PRINTF_COLOROPTION("Gray", _("GrayScale"), CUPS_CSPACE_SW, 8)
4327
0
      }
4328
0
      else if (!strcasecmp(keyword, "adobe-rgb_16") || !strcmp(keyword, "ADOBERGB48") || !strcmp(keyword, "ADOBERGB24-48"))
4329
0
      {
4330
0
  PRINTF_COLORMODEL
4331
4332
0
  PRINTF_COLOROPTION("AdobeRGB", _("Deep Color"), CUPS_CSPACE_ADOBERGB, 16)
4333
4334
0
  if (!default_color)
4335
0
    default_color = "AdobeRGB";
4336
0
      }
4337
0
      else if ((!strcasecmp(keyword, "adobe-rgb_8") && !ippContainsString(attr, "adobe-rgb_16")) || !strcmp(keyword, "ADOBERGB24"))
4338
0
      {
4339
0
  PRINTF_COLORMODEL
4340
4341
0
  PRINTF_COLOROPTION("AdobeRGB", _("Deep Color"), CUPS_CSPACE_ADOBERGB, 8)
4342
4343
0
  if (!default_color)
4344
0
    default_color = "AdobeRGB";
4345
0
      }
4346
0
      else if ((!strcasecmp(keyword, "black_8") && !ippContainsString(attr, "black_16")) || !strcmp(keyword, "DEVW8"))
4347
0
      {
4348
0
  PRINTF_COLORMODEL
4349
4350
0
  PRINTF_COLOROPTION("DeviceGray", _("Device Gray"), CUPS_CSPACE_W, 8)
4351
0
      }
4352
0
      else if (!strcasecmp(keyword, "black_16") || !strcmp(keyword, "DEVW16") || !strcmp(keyword, "DEVW8-16"))
4353
0
      {
4354
0
  PRINTF_COLORMODEL
4355
4356
0
  PRINTF_COLOROPTION("DeviceGray", _("Device Gray"), CUPS_CSPACE_W, 16)
4357
0
      }
4358
0
      else if ((!strcasecmp(keyword, "cmyk_8") && !ippContainsString(attr, "cmyk_16")) || !strcmp(keyword, "DEVCMYK32"))
4359
0
      {
4360
0
  PRINTF_COLORMODEL
4361
4362
0
  PRINTF_COLOROPTION("CMYK", _("Device CMYK"), CUPS_CSPACE_CMYK, 8)
4363
0
      }
4364
0
      else if (!strcasecmp(keyword, "cmyk_16") || !strcmp(keyword, "DEVCMYK32-64") || !strcmp(keyword, "DEVCMYK64"))
4365
0
      {
4366
0
  PRINTF_COLORMODEL
4367
4368
0
  PRINTF_COLOROPTION("CMYK", _("Device CMYK"), CUPS_CSPACE_CMYK, 16)
4369
0
      }
4370
0
      else if ((!strcasecmp(keyword, "rgb_8") && ippContainsString(attr, "rgb_16")) || !strcmp(keyword, "DEVRGB24"))
4371
0
      {
4372
0
  PRINTF_COLORMODEL
4373
4374
0
  PRINTF_COLOROPTION("DeviceRGB", _("Device RGB"), CUPS_CSPACE_RGB, 8)
4375
0
      }
4376
0
      else if (!strcasecmp(keyword, "rgb_16") || !strcmp(keyword, "DEVRGB24-48") || !strcmp(keyword, "DEVRGB48"))
4377
0
      {
4378
0
  PRINTF_COLORMODEL
4379
4380
0
  PRINTF_COLOROPTION("DeviceRGB", _("Device RGB"), CUPS_CSPACE_RGB, 16)
4381
0
      }
4382
0
    }
4383
4384
0
    if (default_color)
4385
0
      cupsFilePrintf(fp, "*DefaultColorModel: %s\n", default_color);
4386
0
    if (wrote_color)
4387
0
      cupsFilePuts(fp, "*CloseUI: *ColorModel\n");
4388
4389
0
    if (default_color)
4390
0
    {
4391
      // Standard presets for color mode and quality...
4392
0
      if (have_qdraft)
4393
0
  cupsFilePuts(fp,
4394
0
         "*APPrinterPreset Gray_with_Paper_Auto-Detect_-_Draft/Draft B&W: \"\n"
4395
0
         "  *cupsPrintQuality Draft *ColorModel Gray\n"
4396
0
         "  com.apple.print.preset.graphicsType General\n"
4397
0
         "  com.apple.print.preset.quality low\n"
4398
0
         "  com.apple.print.preset.media-front-coating autodetect\n"
4399
0
         "  com.apple.print.preset.output-mode monochrome\"\n"
4400
0
         "*End\n");
4401
0
      cupsFilePuts(fp,
4402
0
                   "*APPrinterPreset Gray_with_Paper_Auto-Detect/Black and White: \"\n"
4403
0
       "  *cupsPrintQuality Normal *ColorModel Gray\n"
4404
0
       "  com.apple.print.preset.graphicsType General\n"
4405
0
       "  com.apple.print.preset.quality mid\n"
4406
0
       "  com.apple.print.preset.media-front-coating autodetect\n"
4407
0
       "  com.apple.print.preset.output-mode monochrome\"\n"
4408
0
       "*End\n");
4409
0
      if (strcmp(default_color, "Gray"))
4410
0
  cupsFilePuts(fp,
4411
0
         "*APPrinterPreset Color_with_Paper_Auto-Detect/Color: \"\n"
4412
0
         "  *cupsPrintQuality Normal *ColorModel RGB\n"
4413
0
         "  com.apple.print.preset.graphicsType General\n"
4414
0
         "  com.apple.print.preset.quality mid\n"
4415
0
         "  com.apple.print.preset.media-front-coating autodetect\n"
4416
0
         "  com.apple.print.preset.output-mode color\"\n"
4417
0
         "*End\n");
4418
0
      if (!strcmp(default_color, "AdobeRGB") || have_qhigh)
4419
0
  cupsFilePrintf(fp,
4420
0
           "*APPrinterPreset Photo_with_Paper_Auto-Detect/Photo: \"\n"
4421
0
           "  *cupsPrintQuality %s *ColorModel %s\n"
4422
0
           "  com.apple.print.preset.graphicsType Photo\n"
4423
0
           "  com.apple.print.preset.quality %s\n"
4424
0
           "  com.apple.print.preset.media-front-coating autodetect\n"
4425
0
           "  com.apple.print.preset.output-mode color\"\n"
4426
0
           "*End\n", have_qhigh ? "High" : "Normal", default_color, have_qhigh ? "high" : "mid");
4427
0
    }
4428
0
  }
4429
4430
 /*
4431
  * Duplex...
4432
  */
4433
4434
0
  if ((attr = ippFindAttribute(supported, "sides-supported", IPP_TAG_KEYWORD)) != NULL && ippContainsString(attr, "two-sided-long-edge"))
4435
0
  {
4436
0
    cupsFilePuts(fp, "*OpenUI *Duplex: PickOne\n"
4437
0
         "*OrderDependency: 10 AnySetup *Duplex\n"
4438
0
         "*DefaultDuplex: None\n"
4439
0
         "*Duplex None: \"<</Duplex false>>setpagedevice\"\n"
4440
0
         "*Duplex DuplexNoTumble: \"<</Duplex true/Tumble false>>setpagedevice\"\n"
4441
0
         "*Duplex DuplexTumble: \"<</Duplex true/Tumble true>>setpagedevice\"\n"
4442
0
         "*CloseUI: *Duplex\n");
4443
4444
0
    if ((attr = ippFindAttribute(supported, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
4445
0
    {
4446
0
      for (i = 0, count = ippGetCount(attr); i < count; i ++)
4447
0
      {
4448
0
        const char *dm = ippGetString(attr, i, NULL);
4449
                                        /* DM value */
4450
4451
0
        if (!_cups_strcasecmp(dm, "DM1"))
4452
0
        {
4453
0
          cupsFilePuts(fp, "*cupsBackSide: Normal\n");
4454
0
          break;
4455
0
        }
4456
0
        else if (!_cups_strcasecmp(dm, "DM2"))
4457
0
        {
4458
0
          cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
4459
0
          break;
4460
0
        }
4461
0
        else if (!_cups_strcasecmp(dm, "DM3"))
4462
0
        {
4463
0
          cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
4464
0
          break;
4465
0
        }
4466
0
        else if (!_cups_strcasecmp(dm, "DM4"))
4467
0
        {
4468
0
          cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
4469
0
          break;
4470
0
        }
4471
0
      }
4472
0
    }
4473
0
    else if ((attr = ippFindAttribute(supported, "pwg-raster-document-sheet-back", IPP_TAG_KEYWORD)) != NULL)
4474
0
    {
4475
0
      keyword = ippGetString(attr, 0, NULL);
4476
4477
0
      if (!strcmp(keyword, "flipped"))
4478
0
        cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
4479
0
      else if (!strcmp(keyword, "manual-tumble"))
4480
0
        cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
4481
0
      else if (!strcmp(keyword, "normal"))
4482
0
        cupsFilePuts(fp, "*cupsBackSide: Normal\n");
4483
0
      else
4484
0
        cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
4485
0
    }
4486
0
  }
4487
4488
 /*
4489
  * Output bin...
4490
  */
4491
4492
0
  if ((attr = ippFindAttribute(supported, "output-bin-default", IPP_TAG_ZERO)) != NULL)
4493
0
    pwg_ppdize_name(ippGetString(attr, 0, NULL), ppdname, sizeof(ppdname));
4494
0
  else
4495
0
    cupsCopyString(ppdname, "Unknown", sizeof(ppdname));
4496
4497
0
  if ((attr = ippFindAttribute(supported, "output-bin-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 0)
4498
0
  {
4499
0
    ipp_attribute_t *trays = ippFindAttribute(supported, "printer-output-tray", IPP_TAG_STRING);
4500
          /* printer-output-tray attribute, if any */
4501
0
    const char    *tray_ptr;  /* printer-output-tray value */
4502
0
    int     tray_len; /* Len of printer-output-tray value */
4503
0
    char    tray[IPP_MAX_OCTETSTRING];
4504
          /* printer-output-tray string value */
4505
4506
0
    cupsFilePrintf(fp, "*OpenUI *OutputBin: PickOne\n"
4507
0
                       "*OrderDependency: 10 AnySetup *OutputBin\n"
4508
0
                       "*DefaultOutputBin: %s\n", ppdname);
4509
0
    if (!strcmp(ppdname, "FaceUp"))
4510
0
      cupsFilePuts(fp, "*DefaultOutputOrder: Reverse\n");
4511
0
    else
4512
0
      cupsFilePuts(fp, "*DefaultOutputOrder: Normal\n");
4513
4514
0
    for (i = 0; i < count; i ++)
4515
0
    {
4516
0
      keyword = ippGetString(attr, i, NULL);
4517
4518
0
      pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4519
4520
0
      snprintf(msgid, sizeof(msgid), "output-bin.%s", keyword);
4521
4522
0
      cupsFilePrintf(fp, "*OutputBin %s/%s: \"\"\n", ppdname, ppd_get_string(base_lang, lang, msgid, ppdtext, sizeof(ppdtext)));
4523
0
      ppd_put_strings(fp, langs, "OutputBin", ppdname, msgid);
4524
4525
0
      if ((tray_ptr = ippGetOctetString(trays, i, &tray_len)) != NULL)
4526
0
      {
4527
0
        if (tray_len >= (int)sizeof(tray))
4528
0
          tray_len = (int)sizeof(tray) - 1;
4529
4530
0
        memcpy(tray, tray_ptr, (size_t)tray_len);
4531
0
        tray[tray_len] = '\0';
4532
4533
0
        if (strstr(tray, "stackingorder=lastToFirst;"))
4534
0
          cupsFilePrintf(fp, "*PageStackOrder %s: Reverse\n", ppdname);
4535
0
        else
4536
0
          cupsFilePrintf(fp, "*PageStackOrder %s: Normal\n", ppdname);
4537
0
      }
4538
0
      else if (!strcmp(ppdname, "FaceUp"))
4539
0
  cupsFilePrintf(fp, "*PageStackOrder %s: Reverse\n", ppdname);
4540
0
      else
4541
0
  cupsFilePrintf(fp, "*PageStackOrder %s: Normal\n", ppdname);
4542
0
    }
4543
0
    cupsFilePuts(fp, "*CloseUI: *OutputBin\n");
4544
0
  }
4545
4546
 /*
4547
  * Finishing options...
4548
  */
4549
4550
0
  if ((attr = ippFindAttribute(supported, "finishings-supported", IPP_TAG_ENUM)) != NULL)
4551
0
  {
4552
0
    int     value;    /* Enum value */
4553
0
    const char    *ppd_keyword; /* PPD keyword for enum */
4554
0
    cups_array_t  *names;   /* Names we've added */
4555
0
    static const char * const base_keywords[] =
4556
0
    {         /* Base STD 92 keywords */
4557
0
      NULL,       /* none */
4558
0
      "SingleAuto",     /* staple */
4559
0
      "SingleAuto",     /* punch */
4560
0
      NULL,       /* cover */
4561
0
      "BindAuto",     /* bind */
4562
0
      "SaddleStitch",     /* saddle-stitch */
4563
0
      "EdgeStitchAuto",     /* edge-stitch */
4564
0
      "Auto",       /* fold */
4565
0
      NULL,       /* trim */
4566
0
      NULL,       /* bale */
4567
0
      NULL,       /* booklet-maker */
4568
0
      NULL,       /* jog-offset */
4569
0
      NULL,       /* coat */
4570
      NULL        /* laminate */
4571
0
    };
4572
4573
0
    count       = ippGetCount(attr);
4574
0
    names       = cupsArrayNew3((cups_array_func_t)_cupsArrayStrcmp, NULL, NULL, 0, (cups_acopy_func_t)_cupsArrayStrdup, _cupsArrayFree);
4575
0
    fin_options = cupsArrayNew((cups_array_func_t)_cupsArrayStrcmp, NULL);
4576
4577
   /*
4578
    * Staple/Bind/Stitch
4579
    */
4580
4581
0
    for (i = 0; i < count; i ++)
4582
0
    {
4583
0
      value   = ippGetInteger(attr, i);
4584
0
      keyword = ippEnumString("finishings", value);
4585
4586
0
      if (!strncmp(keyword, "staple-", 7) || !strncmp(keyword, "bind-", 5) || !strncmp(keyword, "edge-stitch-", 12) || !strcmp(keyword, "saddle-stitch") || !strcmp(keyword, "staple") || !strcmp(keyword, "bind"))
4587
0
        break;
4588
0
    }
4589
4590
0
    if (i < count)
4591
0
    {
4592
0
      static const char * const staple_keywords[] =
4593
0
      {         /* StapleLocation keywords */
4594
0
  "SinglePortrait",
4595
0
  "SingleRevLandscape",
4596
0
  "SingleLandscape",
4597
0
  "SingleRevPortrait",
4598
0
  "EdgeStitchPortrait",
4599
0
  "EdgeStitchLandscape",
4600
0
  "EdgeStitchRevPortrait",
4601
0
  "EdgeStitchRevLandscape",
4602
0
  "DualPortrait",
4603
0
  "DualLandscape",
4604
0
  "DualRevPortrait",
4605
0
  "DualRevLandscape",
4606
0
  "TriplePortrait",
4607
0
  "TripleLandscape",
4608
0
  "TripleRevPortrait",
4609
0
  "TripleRevLandscape"
4610
0
      };
4611
0
      static const char * const bind_keywords[] =
4612
0
      {         /* StapleLocation binding keywords */
4613
0
  "BindPortrait",
4614
0
  "BindLandscape",
4615
0
  "BindRevPortrait",
4616
0
  "BindRevLandscape"
4617
0
      };
4618
4619
0
      cupsArrayAdd(fin_options, "*StapleLocation");
4620
4621
0
      cupsFilePrintf(fp, "*OpenUI *StapleLocation/%s: PickOne\n", _cupsLangString(base_lang, "finishings.4"));
4622
0
      cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *StapleLocation\n");
4623
0
      ppd_put_strings(fp, langs, "Translation", "StapleLocation", "finishings.4");
4624
0
      cupsFilePuts(fp, "*DefaultStapleLocation: None\n");
4625
0
      cupsFilePrintf(fp, "*StapleLocation None/%s: \"\"\n", _cupsLangString(base_lang, "finishings.3"));
4626
0
      ppd_put_strings(fp, langs, "StapleLocation", "None", "finishings.3");
4627
4628
0
      for (; i < count; i ++)
4629
0
      {
4630
0
        value   = ippGetInteger(attr, i);
4631
0
        keyword = ippEnumString("finishings", value);
4632
4633
0
        if (strncmp(keyword, "staple-", 7) && strncmp(keyword, "bind-", 5) && strncmp(keyword, "edge-stitch-", 12) && strcmp(keyword, "saddle-stitch") && strcmp(keyword, "staple") && strcmp(keyword, "bind"))
4634
0
          continue;
4635
4636
0
        if (cupsArrayFind(names, (char *)keyword))
4637
0
          continue;     /* Already did this finishing template */
4638
4639
0
        cupsArrayAdd(names, (char *)keyword);
4640
4641
0
  snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4642
4643
0
        if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
4644
0
          ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
4645
0
        else if (value >= IPP_FINISHINGS_STAPLE_TOP_LEFT && value <= IPP_FINISHINGS_STAPLE_TRIPLE_BOTTOM)
4646
0
          ppd_keyword = staple_keywords[value - IPP_FINISHINGS_STAPLE_TOP_LEFT];
4647
0
        else if (value >= IPP_FINISHINGS_BIND_LEFT && value <= IPP_FINISHINGS_BIND_BOTTOM)
4648
0
          ppd_keyword = bind_keywords[value - IPP_FINISHINGS_BIND_LEFT];
4649
0
        else
4650
0
          ppd_keyword = NULL;
4651
4652
0
        if (!ppd_keyword)
4653
0
          continue;
4654
4655
0
  cupsFilePrintf(fp, "*StapleLocation %s/%s: \"\"\n", ppd_keyword, _cupsLangString(base_lang, msgid));
4656
0
  ppd_put_strings(fp, langs, "StapleLocation", ppd_keyword, msgid);
4657
0
  cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*StapleLocation %s\"\n", value, keyword, ppd_keyword);
4658
0
      }
4659
4660
0
      cupsFilePuts(fp, "*CloseUI: *StapleLocation\n");
4661
0
    }
4662
4663
   /*
4664
    * Fold
4665
    */
4666
4667
0
    for (i = 0; i < count; i ++)
4668
0
    {
4669
0
      value   = ippGetInteger(attr, i);
4670
0
      keyword = ippEnumString("finishings", value);
4671
4672
0
      if (!strncmp(keyword, "cups-fold-", 10) || !strcmp(keyword, "fold") || !strncmp(keyword, "fold-", 5))
4673
0
        break;
4674
0
    }
4675
4676
0
    if (i < count)
4677
0
    {
4678
0
      static const char * const fold_keywords[] =
4679
0
      {         /* FoldType keywords */
4680
0
  "Accordion",
4681
0
  "DoubleGate",
4682
0
  "Gate",
4683
0
  "Half",
4684
0
  "HalfZ",
4685
0
  "LeftGate",
4686
0
  "Letter",
4687
0
  "Parallel",
4688
0
  "XFold",
4689
0
  "RightGate",
4690
0
  "ZFold",
4691
0
  "EngineeringZ"
4692
0
      };
4693
4694
0
      cupsArrayAdd(fin_options, "*FoldType");
4695
4696
0
      cupsFilePrintf(fp, "*OpenUI *FoldType/%s: PickOne\n", _cupsLangString(base_lang, "finishings.10"));
4697
0
      cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *FoldType\n");
4698
0
      ppd_put_strings(fp, langs, "Translation", "FoldType", "finishings.10");
4699
0
      cupsFilePuts(fp, "*DefaultFoldType: None\n");
4700
0
      cupsFilePrintf(fp, "*FoldType None/%s: \"\"\n", _cupsLangString(base_lang, "finishings.3"));
4701
0
      ppd_put_strings(fp, langs, "FoldType", "None", "finishings.3");
4702
4703
0
      for (; i < count; i ++)
4704
0
      {
4705
0
        value   = ippGetInteger(attr, i);
4706
0
        keyword = ippEnumString("finishings", value);
4707
4708
0
        if (!strncmp(keyword, "cups-fold-", 10))
4709
0
          keyword += 5;
4710
0
        else if (strcmp(keyword, "fold") && strncmp(keyword, "fold-", 5))
4711
0
          continue;
4712
4713
0
        if (cupsArrayFind(names, (char *)keyword))
4714
0
          continue;     /* Already did this finishing template */
4715
4716
0
        cupsArrayAdd(names, (char *)keyword);
4717
4718
0
  snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4719
4720
0
        if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
4721
0
          ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
4722
0
        else if (value >= IPP_FINISHINGS_FOLD_ACCORDION && value <= IPP_FINISHINGS_FOLD_ENGINEERING_Z)
4723
0
          ppd_keyword = fold_keywords[value - IPP_FINISHINGS_FOLD_ACCORDION];
4724
0
        else if (value >= IPP_FINISHINGS_CUPS_FOLD_ACCORDION && value <= IPP_FINISHINGS_CUPS_FOLD_Z)
4725
0
          ppd_keyword = fold_keywords[value - IPP_FINISHINGS_CUPS_FOLD_ACCORDION];
4726
0
        else
4727
0
          ppd_keyword = NULL;
4728
4729
0
        if (!ppd_keyword)
4730
0
          continue;
4731
4732
0
  cupsFilePrintf(fp, "*FoldType %s/%s: \"\"\n", ppd_keyword, _cupsLangString(base_lang, msgid));
4733
0
  ppd_put_strings(fp, langs, "FoldType", ppd_keyword, msgid);
4734
0
  cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*FoldType %s\"\n", value, keyword, ppd_keyword);
4735
0
      }
4736
4737
0
      cupsFilePuts(fp, "*CloseUI: *FoldType\n");
4738
0
    }
4739
4740
   /*
4741
    * Punch
4742
    */
4743
4744
0
    for (i = 0; i < count; i ++)
4745
0
    {
4746
0
      value   = ippGetInteger(attr, i);
4747
0
      keyword = ippEnumString("finishings", value);
4748
4749
0
      if (!strcmp(keyword, "punch") || !strncmp(keyword, "cups-punch-", 11) || !strncmp(keyword, "punch-", 6))
4750
0
        break;
4751
0
    }
4752
4753
0
    if (i < count)
4754
0
    {
4755
0
      static const char * const punch_keywords[] =
4756
0
      {         /* PunchMedia keywords */
4757
0
  "SinglePortrait",
4758
0
  "SingleRevLandscape",
4759
0
  "SingleLandscape",
4760
0
  "SingleRevPortrait",
4761
0
  "DualPortrait",
4762
0
  "DualLandscape",
4763
0
  "DualRevPortrait",
4764
0
  "DualRevLandscape",
4765
0
  "TriplePortrait",
4766
0
  "TripleLandscape",
4767
0
  "TripleRevPortrait",
4768
0
  "TripleRevLandscape",
4769
0
  "QuadPortrait",
4770
0
  "QuadLandscape",
4771
0
  "QuadRevPortrait",
4772
0
  "QuadRevLandscape",
4773
0
  "MultiplePortrait",
4774
0
  "MultipleLandscape",
4775
0
  "MultipleRevPortrait",
4776
0
  "MultipleRevLandscape"
4777
0
      };
4778
4779
0
      cupsArrayAdd(fin_options, "*PunchMedia");
4780
4781
0
      cupsFilePrintf(fp, "*OpenUI *PunchMedia/%s: PickOne\n", _cupsLangString(base_lang, "finishings.5"));
4782
0
      cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *PunchMedia\n");
4783
0
      ppd_put_strings(fp, langs, "Translation", "PunchMedia", "finishings.5");
4784
0
      cupsFilePuts(fp, "*DefaultPunchMedia: None\n");
4785
0
      cupsFilePrintf(fp, "*PunchMedia None/%s: \"\"\n", _cupsLangString(base_lang, "finishings.3"));
4786
0
      ppd_put_strings(fp, langs, "PunchMedia", "None", "finishings.3");
4787
4788
0
      for (i = 0; i < count; i ++)
4789
0
      {
4790
0
        value   = ippGetInteger(attr, i);
4791
0
        keyword = ippEnumString("finishings", value);
4792
4793
0
        if (!strncmp(keyword, "cups-punch-", 11))
4794
0
          keyword += 5;
4795
0
        else if (strcmp(keyword, "punch") && strncmp(keyword, "punch-", 6))
4796
0
          continue;
4797
4798
0
        if (cupsArrayFind(names, (char *)keyword))
4799
0
          continue;     /* Already did this finishing template */
4800
4801
0
        cupsArrayAdd(names, (char *)keyword);
4802
4803
0
  snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4804
4805
0
        if (value >= IPP_FINISHINGS_NONE && value <= IPP_FINISHINGS_LAMINATE)
4806
0
          ppd_keyword = base_keywords[value - IPP_FINISHINGS_NONE];
4807
0
        else if (value >= IPP_FINISHINGS_PUNCH_TOP_LEFT && value <= IPP_FINISHINGS_PUNCH_MULTIPLE_BOTTOM)
4808
0
          ppd_keyword = punch_keywords[value - IPP_FINISHINGS_PUNCH_TOP_LEFT];
4809
0
        else if (value >= IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT && value <= IPP_FINISHINGS_CUPS_PUNCH_QUAD_BOTTOM)
4810
0
          ppd_keyword = punch_keywords[value - IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT];
4811
0
        else
4812
0
          ppd_keyword = NULL;
4813
4814
0
        if (!ppd_keyword)
4815
0
          continue;
4816
4817
0
  cupsFilePrintf(fp, "*PunchMedia %s/%s: \"\"\n", ppd_keyword, _cupsLangString(base_lang, msgid));
4818
0
  ppd_put_strings(fp, langs, "PunchMedia", ppd_keyword, msgid);
4819
0
  cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*PunchMedia %s\"\n", value, keyword, ppd_keyword);
4820
0
      }
4821
4822
0
      cupsFilePuts(fp, "*CloseUI: *PunchMedia\n");
4823
0
    }
4824
4825
   /*
4826
    * Booklet
4827
    */
4828
4829
0
    if (ippContainsInteger(attr, IPP_FINISHINGS_BOOKLET_MAKER))
4830
0
    {
4831
0
      cupsArrayAdd(fin_options, "*Booklet");
4832
4833
0
      cupsFilePrintf(fp, "*OpenUI *Booklet/%s: Boolean\n", _cupsLangString(base_lang, "finishings.13"));
4834
0
      cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *Booklet\n");
4835
0
      ppd_put_strings(fp, langs, "Translation", "StapleLocation", "finishings.13");
4836
0
      cupsFilePuts(fp, "*DefaultBooklet: False\n");
4837
0
      cupsFilePuts(fp, "*Booklet False: \"\"\n");
4838
0
      cupsFilePuts(fp, "*Booklet True: \"\"\n");
4839
0
      cupsFilePrintf(fp, "*cupsIPPFinishings %d/booklet-maker: \"*Booklet True\"\n", IPP_FINISHINGS_BOOKLET_MAKER);
4840
0
      cupsFilePuts(fp, "*CloseUI: *Booklet\n");
4841
0
    }
4842
4843
   /*
4844
    * CutMedia
4845
    */
4846
4847
0
    for (i = 0; i < count; i ++)
4848
0
    {
4849
0
      value   = ippGetInteger(attr, i);
4850
0
      keyword = ippEnumString("finishings", value);
4851
4852
0
      if (!strcmp(keyword, "trim") || !strncmp(keyword, "trim-", 5))
4853
0
        break;
4854
0
    }
4855
4856
0
    if (i < count)
4857
0
    {
4858
0
      static const char * const trim_keywords[] =
4859
0
      {       /* CutMedia keywords */
4860
0
        "EndOfPage",
4861
0
        "EndOfDoc",
4862
0
        "EndOfSet",
4863
0
        "EndOfJob"
4864
0
      };
4865
4866
0
      cupsArrayAdd(fin_options, "*CutMedia");
4867
4868
0
      cupsFilePrintf(fp, "*OpenUI *CutMedia/%s: PickOne\n", _cupsLangString(base_lang, "finishings.11"));
4869
0
      cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *CutMedia\n");
4870
0
      ppd_put_strings(fp, langs, "Translation", "CutMedia", "finishings.11");
4871
0
      cupsFilePuts(fp, "*DefaultCutMedia: None\n");
4872
0
      cupsFilePrintf(fp, "*CutMedia None/%s: \"\"\n", _cupsLangString(base_lang, "finishings.3"));
4873
0
      ppd_put_strings(fp, langs, "CutMedia", "None", "finishings.3");
4874
4875
0
      for (i = 0; i < count; i ++)
4876
0
      {
4877
0
        value   = ippGetInteger(attr, i);
4878
0
        keyword = ippEnumString("finishings", value);
4879
4880
0
  if (strcmp(keyword, "trim") && strncmp(keyword, "trim-", 5))
4881
0
          continue;
4882
4883
0
        if (cupsArrayFind(names, (char *)keyword))
4884
0
          continue;     /* Already did this finishing template */
4885
4886
0
        cupsArrayAdd(names, (char *)keyword);
4887
4888
0
  snprintf(msgid, sizeof(msgid), "finishings.%d", value);
4889
4890
0
        if (value == IPP_FINISHINGS_TRIM)
4891
0
          ppd_keyword = "Auto";
4892
0
  else
4893
0
    ppd_keyword = trim_keywords[value - IPP_FINISHINGS_TRIM_AFTER_PAGES];
4894
4895
0
  cupsFilePrintf(fp, "*CutMedia %s/%s: \"\"\n", ppd_keyword, _cupsLangString(base_lang, msgid));
4896
0
  ppd_put_strings(fp, langs, "CutMedia", ppd_keyword, msgid);
4897
0
  cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*CutMedia %s\"\n", value, keyword, ppd_keyword);
4898
0
      }
4899
4900
0
      cupsFilePuts(fp, "*CloseUI: *CutMedia\n");
4901
0
    }
4902
4903
0
    cupsArrayDelete(names);
4904
0
  }
4905
4906
0
  if ((attr = ippFindAttribute(supported, "finishings-col-database", IPP_TAG_BEGIN_COLLECTION)) != NULL)
4907
0
  {
4908
0
    ipp_t *finishing_col;   /* Current finishing collection */
4909
0
    ipp_attribute_t *finishing_attr;  /* Current finishing member attribute */
4910
0
    cups_array_t *templates;    /* Finishing templates */
4911
4912
0
    cupsFilePrintf(fp, "*OpenUI *cupsFinishingTemplate/%s: PickOne\n", ppd_get_string(base_lang, lang, "finishing-template", ppdtext, sizeof(ppdtext)));
4913
0
    cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *cupsFinishingTemplate\n");
4914
0
    ppd_put_strings(fp, langs, "Translation", "cupsFinishingTemplate", "finishing-template");
4915
0
    cupsFilePuts(fp, "*DefaultcupsFinishingTemplate: none\n");
4916
0
    cupsFilePrintf(fp, "*cupsFinishingTemplate none/%s: \"\"\n", _cupsLangString(lang, "finishings.3"));
4917
0
    ppd_put_strings(fp, langs, "cupsFinishingTemplate", "none", "finishings.3");
4918
4919
0
    templates = cupsArrayNew((cups_array_func_t)_cupsArrayStrcmp, NULL);
4920
0
    count     = ippGetCount(attr);
4921
4922
0
    for (i = 0; i < count; i ++)
4923
0
    {
4924
0
      finishing_col = ippGetCollection(attr, i);
4925
0
      keyword       = ippGetString(ippFindAttribute(finishing_col, "finishing-template", IPP_TAG_ZERO), 0, NULL);
4926
4927
0
      if (!keyword || cupsArrayFind(templates, (void *)keyword))
4928
0
        continue;
4929
4930
0
      if (!strcmp(keyword, "none"))
4931
0
        continue;
4932
4933
0
      cupsArrayAdd(templates, (void *)keyword);
4934
4935
0
      pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
4936
4937
0
      snprintf(msgid, sizeof(msgid), "finishing-template.%s", keyword);
4938
4939
0
      cupsFilePrintf(fp, "*cupsFinishingTemplate %s/%s: \"\n", ppdname, ppd_get_string(base_lang, lang, msgid, ppdtext, sizeof(ppdtext)));
4940
0
      for (finishing_attr = ippFirstAttribute(finishing_col); finishing_attr; finishing_attr = ippNextAttribute(finishing_col))
4941
0
      {
4942
0
        if (ippGetValueTag(finishing_attr) == IPP_TAG_BEGIN_COLLECTION)
4943
0
        {
4944
0
    const char *name = ippGetName(finishing_attr);
4945
          /* Member attribute name */
4946
4947
0
          if (strcmp(name, "media-size"))
4948
0
            cupsFilePrintf(fp, "%% %s\n", name);
4949
0
  }
4950
0
      }
4951
0
      cupsFilePuts(fp, "\"\n");
4952
0
      ppd_put_strings(fp, langs, "cupsFinishingTemplate", ppdname, msgid);
4953
0
      cupsFilePuts(fp, "*End\n");
4954
0
    }
4955
4956
0
    cupsFilePuts(fp, "*CloseUI: *cupsFinishingTemplate\n");
4957
4958
0
    if (cupsArrayCount(fin_options))
4959
0
    {
4960
0
      const char  *fin_option;  /* Current finishing option */
4961
4962
0
      cupsFilePuts(fp, "*cupsUIConstraint finishing-template: \"*cupsFinishingTemplate");
4963
0
      for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option; fin_option = (const char *)cupsArrayNext(fin_options))
4964
0
        cupsFilePrintf(fp, " %s", fin_option);
4965
0
      cupsFilePuts(fp, "\"\n");
4966
4967
0
      cupsFilePuts(fp, "*cupsUIResolver finishing-template: \"*cupsFinishingTemplate none");
4968
0
      for (fin_option = (const char *)cupsArrayFirst(fin_options); fin_option; fin_option = (const char *)cupsArrayNext(fin_options))
4969
0
        cupsFilePrintf(fp, " %s None", fin_option);
4970
0
      cupsFilePuts(fp, "\"\n");
4971
0
    }
4972
4973
0
    cupsArrayDelete(templates);
4974
0
  }
4975
4976
0
  cupsArrayDelete(fin_options);
4977
4978
 /*
4979
  * Presets...
4980
  */
4981
4982
0
  if ((attr = ippFindAttribute(supported, "job-presets-supported", IPP_TAG_BEGIN_COLLECTION)) != NULL)
4983
0
  {
4984
0
    for (i = 0, count = ippGetCount(attr); i < count; i ++)
4985
0
    {
4986
0
      ipp_t *preset = ippGetCollection(attr, i);
4987
          /* Preset collection */
4988
0
      const char *preset_name = ippGetString(ippFindAttribute(preset, "preset-name", IPP_TAG_ZERO), 0, NULL);
4989
          /* Preset name */
4990
0
      ipp_attribute_t *member;    /* Member attribute in preset */
4991
0
      const char *member_name;    /* Member attribute name */
4992
0
      char      member_value[256];  /* Member attribute value */
4993
4994
0
      if (!preset || !preset_name)
4995
0
        continue;
4996
4997
0
      pwg_ppdize_name(preset_name, ppdname, sizeof(ppdname));
4998
4999
0
      cupsFilePrintf(fp, "*APPrinterPreset %s: \"\n", ppdname);
5000
0
      for (member = ippFirstAttribute(preset); member; member = ippNextAttribute(preset))
5001
0
      {
5002
0
        member_name = ippGetName(member);
5003
5004
0
        if (!member_name || !strcmp(member_name, "preset-name"))
5005
0
          continue;
5006
5007
0
        if (!strcmp(member_name, "finishings"))
5008
0
        {
5009
0
    for (i = 0, count = ippGetCount(member); i < count; i ++)
5010
0
    {
5011
0
      const char *option = NULL; /* PPD option name */
5012
5013
0
      keyword = ippEnumString("finishings", ippGetInteger(member, i));
5014
5015
0
      if (!strcmp(keyword, "booklet-maker"))
5016
0
      {
5017
0
        option  = "Booklet";
5018
0
        keyword = "True";
5019
0
      }
5020
0
      else if (!strncmp(keyword, "fold-", 5))
5021
0
        option = "FoldType";
5022
0
      else if (!strncmp(keyword, "punch-", 6))
5023
0
        option = "PunchMedia";
5024
0
      else if (!strncmp(keyword, "bind-", 5) || !strncmp(keyword, "edge-stitch-", 12) || !strcmp(keyword, "saddle-stitch") || !strncmp(keyword, "staple-", 7) || !strcmp(keyword, "staple") || !strcmp(keyword, "bind"))
5025
0
        option = "StapleLocation";
5026
5027
0
      if (option && keyword)
5028
0
        cupsFilePrintf(fp, "*%s %s\n", option, keyword);
5029
0
    }
5030
0
        }
5031
0
        else if (!strcmp(member_name, "finishings-col"))
5032
0
        {
5033
0
          ipp_t *fin_col;   /* finishings-col value */
5034
5035
0
          for (i = 0, count = ippGetCount(member); i < count; i ++)
5036
0
          {
5037
0
            fin_col = ippGetCollection(member, i);
5038
5039
0
            if ((keyword = ippGetString(ippFindAttribute(fin_col, "finishing-template", IPP_TAG_ZERO), 0, NULL)) != NULL)
5040
0
            {
5041
0
        pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
5042
0
              cupsFilePrintf(fp, "*cupsFinishingTemplate %s\n", ppdname);
5043
0
            }
5044
0
          }
5045
0
        }
5046
0
        else if (!strcmp(member_name, "media"))
5047
0
        {
5048
         /*
5049
          * Map media to PageSize...
5050
          */
5051
5052
0
          if ((pwg = pwgMediaForPWG(ippGetString(member, 0, NULL))) != NULL && pwg->ppd)
5053
0
            cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd);
5054
0
        }
5055
0
        else if (!strcmp(member_name, "media-col"))
5056
0
        {
5057
0
          media_col = ippGetCollection(member, 0);
5058
5059
0
          if ((media_size = ippGetCollection(ippFindAttribute(media_col, "media-size", IPP_TAG_BEGIN_COLLECTION), 0)) != NULL)
5060
0
          {
5061
0
            x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_INTEGER);
5062
0
            y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_INTEGER);
5063
0
            if ((pwg = pwgMediaForSize(ippGetInteger(x_dim, 0), ippGetInteger(y_dim, 0))) != NULL && pwg->ppd)
5064
0
        cupsFilePrintf(fp, "*PageSize %s\n", pwg->ppd);
5065
0
          }
5066
5067
0
          if ((keyword = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL)) != NULL)
5068
0
          {
5069
0
            pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
5070
0
            cupsFilePrintf(fp, "*InputSlot %s\n", ppdname);
5071
0
    }
5072
5073
0
          if ((keyword = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL)) != NULL)
5074
0
          {
5075
0
            pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
5076
0
            cupsFilePrintf(fp, "*MediaType %s\n", ppdname);
5077
0
    }
5078
0
        }
5079
0
        else if (!strcmp(member_name, "print-quality"))
5080
0
        {
5081
   /*
5082
    * Map print-quality to cupsPrintQuality...
5083
    */
5084
5085
0
          int qval = ippGetInteger(member, 0);
5086
          /* print-quality value */
5087
0
    static const char * const qualities[] = { "Draft", "Normal", "High" };
5088
          /* cupsPrintQuality values */
5089
5090
0
          if (qval >= IPP_QUALITY_DRAFT && qval <= IPP_QUALITY_HIGH)
5091
0
            cupsFilePrintf(fp, "*cupsPrintQuality %s\n", qualities[qval - IPP_QUALITY_DRAFT]);
5092
0
        }
5093
0
        else if (!strcmp(member_name, "output-bin"))
5094
0
        {
5095
0
          pwg_ppdize_name(ippGetString(member, 0, NULL), ppdname, sizeof(ppdname));
5096
0
          cupsFilePrintf(fp, "*OutputBin %s\n", ppdname);
5097
0
        }
5098
0
        else if (!strcmp(member_name, "sides"))
5099
0
        {
5100
0
          keyword = ippGetString(member, 0, NULL);
5101
0
          if (keyword && !strcmp(keyword, "one-sided"))
5102
0
            cupsFilePuts(fp, "*Duplex None\n");
5103
0
    else if (keyword && !strcmp(keyword, "two-sided-long-edge"))
5104
0
      cupsFilePuts(fp, "*Duplex DuplexNoTumble\n");
5105
0
    else if (keyword && !strcmp(keyword, "two-sided-short-edge"))
5106
0
      cupsFilePuts(fp, "*Duplex DuplexTumble\n");
5107
0
        }
5108
0
        else
5109
0
        {
5110
         /*
5111
          * Add attribute name and value as-is...
5112
          */
5113
5114
0
          ippAttributeString(member, member_value, sizeof(member_value));
5115
0
          cupsFilePrintf(fp, "*%s %s\n", member_name, member_value);
5116
0
  }
5117
0
      }
5118
5119
0
      cupsFilePuts(fp, "\"\n*End\n");
5120
5121
0
      pwg_ppdize_name(preset_name, ppdname, sizeof(ppdname));
5122
0
      snprintf(msgid, sizeof(msgid), "preset-name.%s", preset_name);
5123
0
      ppd_put_strings(fp, langs, "APPrinterPreset", ppdname, msgid);
5124
0
    }
5125
0
  }
5126
5127
 /*
5128
  * Add cupsSingleFile to support multiple files printing on printers
5129
  * which don't support multiple files in its firmware...
5130
  *
5131
  * Adding the keyword degrades printing performance (there is 1-2 seconds
5132
  * pause between files).
5133
  */
5134
5135
0
  cupsFilePuts(fp, "*cupsSingleFile: true\n");
5136
5137
 /*
5138
  * Add localized printer-state-reasons keywords as needed...
5139
  */
5140
5141
0
  for (lang = langs; lang; lang = lang->next)
5142
0
  {
5143
0
    _cups_message_t *msg;   // Current message
5144
5145
0
    for (msg = (_cups_message_t *)cupsArrayGetFirst(lang->strings); msg; msg = (_cups_message_t *)cupsArrayGetNext(lang->strings))
5146
0
    {
5147
0
      if (!strncmp(msg->msg, "printer-state-reasons.", 22))
5148
0
      {
5149
0
        if (!strcmp(lang->language, "en"))
5150
0
          cupsFilePrintf(fp, "*cupsIPPReason %s/%s: \"\"\n", msg->msg + 22, msg->str);
5151
0
        else
5152
0
          cupsFilePrintf(fp, "*%s.cupsIPPReason %s/%s: \"\"\n", lang->language, msg->msg + 22, msg->str);
5153
0
      }
5154
0
    }
5155
0
  }
5156
5157
 /*
5158
  * Close up and return...
5159
  */
5160
5161
0
  cupsFileClose(fp);
5162
5163
0
  for (lang = langs; lang;)
5164
0
  {
5165
0
    cups_lang_t *next = lang->next;
5166
5167
0
    cupsArrayDelete(lang->strings);
5168
0
    free(lang);
5169
5170
0
    lang = next;
5171
0
  }
5172
5173
0
  return (buffer);
5174
5175
 /*
5176
  * If we get here then there was a problem creating the PPD...
5177
  */
5178
5179
0
  bad_ppd:
5180
5181
0
  cupsFileClose(fp);
5182
5183
0
  for (lang = langs; lang;)
5184
0
  {
5185
0
    cups_lang_t *next = lang->next;
5186
5187
0
    cupsArrayDelete(lang->strings);
5188
0
    free(lang);
5189
5190
0
    lang = next;
5191
0
  }
5192
5193
0
  unlink(buffer);
5194
0
  *buffer = '\0';
5195
5196
0
  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Printer does not support required IPP attributes or document formats."), 1);
5197
5198
0
  return (NULL);
5199
0
}
5200
5201
5202
/*
5203
 * '_pwgInputSlotForSource()' - Get the InputSlot name for the given PWG
5204
 *                              media-source.
5205
 */
5206
5207
const char *        /* O - InputSlot name */
5208
_pwgInputSlotForSource(
5209
    const char *media_source,   /* I - PWG media-source */
5210
    char       *name,     /* I - Name buffer */
5211
    size_t     namesize)    /* I - Size of name buffer */
5212
0
{
5213
 /*
5214
  * Range check input...
5215
  */
5216
5217
0
  if (!media_source || !name || namesize < PPD_MAX_NAME)
5218
0
    return (NULL);
5219
5220
0
  if (_cups_strcasecmp(media_source, "main"))
5221
0
    cupsCopyString(name, "Cassette", namesize);
5222
0
  else if (_cups_strcasecmp(media_source, "alternate"))
5223
0
    cupsCopyString(name, "Multipurpose", namesize);
5224
0
  else if (_cups_strcasecmp(media_source, "large-capacity"))
5225
0
    cupsCopyString(name, "LargeCapacity", namesize);
5226
0
  else if (_cups_strcasecmp(media_source, "bottom"))
5227
0
    cupsCopyString(name, "Lower", namesize);
5228
0
  else if (_cups_strcasecmp(media_source, "middle"))
5229
0
    cupsCopyString(name, "Middle", namesize);
5230
0
  else if (_cups_strcasecmp(media_source, "top"))
5231
0
    cupsCopyString(name, "Upper", namesize);
5232
0
  else if (_cups_strcasecmp(media_source, "rear"))
5233
0
    cupsCopyString(name, "Rear", namesize);
5234
0
  else if (_cups_strcasecmp(media_source, "side"))
5235
0
    cupsCopyString(name, "Side", namesize);
5236
0
  else if (_cups_strcasecmp(media_source, "envelope"))
5237
0
    cupsCopyString(name, "Envelope", namesize);
5238
0
  else if (_cups_strcasecmp(media_source, "main-roll"))
5239
0
    cupsCopyString(name, "Roll", namesize);
5240
0
  else if (_cups_strcasecmp(media_source, "alternate-roll"))
5241
0
    cupsCopyString(name, "Roll2", namesize);
5242
0
  else
5243
0
    pwg_ppdize_name(media_source, name, namesize);
5244
5245
0
  return (name);
5246
0
}
5247
5248
5249
/*
5250
 * '_pwgMediaTypeForType()' - Get the MediaType name for the given PWG
5251
 *                            media-type.
5252
 */
5253
5254
const char *        /* O - MediaType name */
5255
_pwgMediaTypeForType(
5256
    const char *media_type,   /* I - PWG media-type */
5257
    char       *name,     /* I - Name buffer */
5258
    size_t     namesize)    /* I - Size of name buffer */
5259
0
{
5260
 /*
5261
  * Range check input...
5262
  */
5263
5264
0
  if (!media_type || !name || namesize < PPD_MAX_NAME)
5265
0
    return (NULL);
5266
5267
0
  if (_cups_strcasecmp(media_type, "auto"))
5268
0
    cupsCopyString(name, "Auto", namesize);
5269
0
  else if (_cups_strcasecmp(media_type, "cardstock"))
5270
0
    cupsCopyString(name, "Cardstock", namesize);
5271
0
  else if (_cups_strcasecmp(media_type, "envelope"))
5272
0
    cupsCopyString(name, "Envelope", namesize);
5273
0
  else if (_cups_strcasecmp(media_type, "photographic-glossy"))
5274
0
    cupsCopyString(name, "Glossy", namesize);
5275
0
  else if (_cups_strcasecmp(media_type, "photographic-high-gloss"))
5276
0
    cupsCopyString(name, "HighGloss", namesize);
5277
0
  else if (_cups_strcasecmp(media_type, "photographic-matte"))
5278
0
    cupsCopyString(name, "Matte", namesize);
5279
0
  else if (_cups_strcasecmp(media_type, "stationery"))
5280
0
    cupsCopyString(name, "Plain", namesize);
5281
0
  else if (_cups_strcasecmp(media_type, "stationery-coated"))
5282
0
    cupsCopyString(name, "Coated", namesize);
5283
0
  else if (_cups_strcasecmp(media_type, "stationery-inkjet"))
5284
0
    cupsCopyString(name, "Inkjet", namesize);
5285
0
  else if (_cups_strcasecmp(media_type, "stationery-letterhead"))
5286
0
    cupsCopyString(name, "Letterhead", namesize);
5287
0
  else if (_cups_strcasecmp(media_type, "stationery-preprinted"))
5288
0
    cupsCopyString(name, "Preprinted", namesize);
5289
0
  else if (_cups_strcasecmp(media_type, "transparency"))
5290
0
    cupsCopyString(name, "Transparency", namesize);
5291
0
  else
5292
0
    pwg_ppdize_name(media_type, name, namesize);
5293
5294
0
  return (name);
5295
0
}
5296
5297
5298
/*
5299
 * '_pwgPageSizeForMedia()' - Get the PageSize name for the given media.
5300
 */
5301
5302
const char *        /* O - PageSize name */
5303
_pwgPageSizeForMedia(
5304
    pwg_media_t *media,     /* I - Media */
5305
    char        *name,      /* I - PageSize name buffer */
5306
    size_t      namesize)   /* I - Size of name buffer */
5307
0
{
5308
0
  const char  *sizeptr,   /* Pointer to size in PWG name */
5309
0
    *dimptr;    /* Pointer to dimensions in PWG name */
5310
5311
5312
 /*
5313
  * Range check input...
5314
  */
5315
5316
0
  if (!media || !name || namesize < PPD_MAX_NAME)
5317
0
    return (NULL);
5318
5319
 /*
5320
  * Copy or generate a PageSize name...
5321
  */
5322
5323
0
  if (media->ppd)
5324
0
  {
5325
   /*
5326
    * Use a standard Adobe name...
5327
    */
5328
5329
0
    cupsCopyString(name, media->ppd, namesize);
5330
0
  }
5331
0
  else if (!media->pwg || !strncmp(media->pwg, "custom_", 7) ||
5332
0
           (sizeptr = strchr(media->pwg, '_')) == NULL ||
5333
0
     (dimptr = strchr(sizeptr + 1, '_')) == NULL ||
5334
0
     (size_t)(dimptr - sizeptr) > namesize)
5335
0
  {
5336
   /*
5337
    * Use a name of the form "wNNNhNNN"...
5338
    */
5339
5340
0
    snprintf(name, namesize, "w%dh%d", (int)PWG_TO_POINTS(media->width),
5341
0
             (int)PWG_TO_POINTS(media->length));
5342
0
  }
5343
0
  else
5344
0
  {
5345
   /*
5346
    * Copy the size name from class_sizename_dimensions...
5347
    */
5348
5349
0
    memcpy(name, sizeptr + 1, (size_t)(dimptr - sizeptr - 1));
5350
0
    name[dimptr - sizeptr - 1] = '\0';
5351
0
  }
5352
5353
0
  return (name);
5354
0
}
5355
5356
5357
/*
5358
 * 'cups_connect()' - Connect to a URL and get the resource path.
5359
 */
5360
5361
static int        /* O  - 1 on success, 0 on failure */
5362
cups_connect(http_t     **http,   /* IO - Current HTTP connection */
5363
             const char *url,   /* I  - URL to connect */
5364
             char       *resource,  /* I  - Resource path buffer */
5365
             size_t     ressize)  /* I  - Size of resource path buffer */
5366
0
{
5367
0
  char      scheme[32], /* URL scheme */
5368
0
      userpass[256],  /* URL username:password */
5369
0
      host[256],  /* URL host */
5370
0
      curhost[256]; /* Current host */
5371
0
  int     port;   /* URL port */
5372
0
  http_encryption_t encryption; /* Type of encryption to use */
5373
5374
5375
  // Separate the URI...
5376
0
  if (httpSeparateURI(HTTP_URI_CODING_ALL, url, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, ressize) < HTTP_URI_STATUS_OK)
5377
0
    return (0);
5378
5379
  // Use encryption as needed..
5380
0
  if (port == 443 || !strcmp(scheme, "https") || !strcmp(scheme, "ipps"))
5381
0
    encryption = HTTP_ENCRYPTION_ALWAYS;
5382
0
  else
5383
0
    encryption = HTTP_ENCRYPTION_IF_REQUESTED;
5384
5385
0
  if (!*http || strcasecmp(host, httpGetHostname(*http, curhost, sizeof(curhost))) || httpAddrPort(httpGetAddress(*http)) != port || httpIsEncrypted(*http) != (encryption == HTTP_ENCRYPTION_ALWAYS))
5386
0
  {
5387
0
    httpClose(*http);
5388
0
    *http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 5000, NULL);
5389
0
  }
5390
5391
0
  return (*http != NULL);
5392
0
}
5393
5394
5395
/*
5396
 * 'cups_get_strings()' - Get the strings for the specified language.
5397
 */
5398
5399
static cups_lang_t *      /* O  - Language data */
5400
cups_get_strings(
5401
    http_t     **http,      /* IO - Current HTTP connection */
5402
    const char *printer_uri,    /* I  - Printer URI */
5403
    const char *language)   /* I  - Language name */
5404
0
{
5405
0
  cups_lang_t *lang = NULL;   /* Language data */
5406
0
  char    resource[256];    /* Resource path for printer */
5407
0
  ipp_t   *request,   /* IPP request */
5408
0
    *response = NULL; /* IPP response */
5409
0
  const char  *strings_uri;   /* "printer-strings-uri" value */
5410
0
  char    strings_file[1024] = "";/* Strings filename */
5411
0
  static int  request_id = 1000;  // Request ID
5412
5413
5414
 /*
5415
  * Connect to the printer...
5416
  */
5417
5418
0
  if (!cups_connect(http, printer_uri, resource, sizeof(resource)))
5419
0
    goto done;
5420
5421
 /*
5422
  * Get the URL for the corresponding language strings...
5423
  */
5424
5425
0
  request = ippNew();
5426
0
  ippSetOperation(request, IPP_OP_GET_PRINTER_ATTRIBUTES);
5427
0
  ippSetRequestId(request, request_id ++);
5428
0
  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", /*language*/NULL, "utf-8");
5429
0
  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", /*language*/NULL, language);
5430
0
  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", /*language*/NULL, printer_uri);
5431
5432
0
  response = cupsDoRequest(*http, request, resource);
5433
5434
0
  if ((strings_uri = ippGetString(ippFindAttribute(response, "printer-strings-uri", IPP_TAG_URI), 0, NULL)) != NULL)
5435
0
  {
5436
   /*
5437
    * Download the strings file...
5438
    */
5439
5440
0
    if (!cups_get_url(http, strings_uri, ".strings", strings_file, sizeof(strings_file)))
5441
0
      goto done;
5442
5443
   /*
5444
    * Allocate memory...
5445
    */
5446
5447
0
    if ((lang = (cups_lang_t *)calloc(1, sizeof(cups_lang_t))) == NULL)
5448
0
      goto done;
5449
5450
   /*
5451
    * Load the strings...
5452
    */
5453
5454
0
    cupsCopyString(lang->language, language, sizeof(lang->language));
5455
0
    lang->strings = _cupsMessageLoad(NULL, strings_file, _CUPS_MESSAGE_STRINGS);
5456
0
    unlink(strings_file);
5457
0
  }
5458
#if 0
5459
  else
5460
  {
5461
    ipp_attribute_t *attr;
5462
    ipp_tag_t group_tag = IPP_TAG_ZERO, value_tag;
5463
    const char *name;
5464
    char value[2048];
5465
5466
    puts("No printer-strings-uri in response.");
5467
    printf("Get-Printer-Attributes: %s\n", ippErrorString(ippGetStatusCode(response)));
5468
    for (attr = ippGetFirstAttribute(response); attr; attr = ippGetNextAttribute(response))
5469
    {
5470
      if (ippGetGroupTag(attr) != group_tag)
5471
      {
5472
        group_tag = ippGetGroupTag(attr);
5473
        puts(ippTagString(group_tag));
5474
      }
5475
5476
      name      = ippGetName(attr);
5477
      value_tag = ippGetValueTag(attr);
5478
5479
      ippAttributeString(attr, value, sizeof(value));
5480
5481
      printf("  %s %s = %s\n", name, ippTagString(value_tag), value);
5482
    }
5483
  }
5484
#endif // 0
5485
5486
0
  done:
5487
5488
0
  ippDelete(response);
5489
5490
0
  return (lang);
5491
0
}
5492
5493
5494
/*
5495
 * 'cups_get_url()' - Get a copy of the file at the given URL.
5496
 */
5497
5498
static int        /* O  - 1 on success, 0 on failure */
5499
cups_get_url(http_t     **http,   /* IO - Current HTTP connection */
5500
             const char *url,   /* I  - URL to get */
5501
             const char *suffix,  /* I  - Filename suffix */
5502
             char       *name,    /* I  - Temporary filename */
5503
             size_t     namesize) /* I  - Size of temporary filename buffer */
5504
0
{
5505
0
  char      resource[256];  /* URL resource */
5506
0
  http_status_t   status;   /* Status of GET request */
5507
0
  int     fd;   /* Temporary file */
5508
5509
5510
0
  if (!cups_connect(http, url, resource, sizeof(resource)))
5511
0
    return (0);
5512
5513
0
  if ((fd = cupsCreateTempFd("ippeve", suffix, name, namesize)) < 0)
5514
0
    return (0);
5515
5516
0
  status = cupsGetFd(*http, resource, fd);
5517
5518
0
  close(fd);
5519
5520
0
  if (status != HTTP_STATUS_OK)
5521
0
  {
5522
0
    unlink(name);
5523
0
    *name = '\0';
5524
0
    return (0);
5525
0
  }
5526
5527
0
  return (1);
5528
0
}
5529
5530
5531
/*
5532
 * 'ppd_get_string()' - Get a localized string.
5533
 *
5534
 * This function looks up the msgid first in the CUPS English localization
5535
 * catalog, then the printer's English localization, and finally falls back on
5536
 * converting the keyword from "word-like-this" to "Words Like This".
5537
 */
5538
5539
static const char *     // O - Localized text
5540
ppd_get_string(cups_lang_t *base, // I - Base (CUPS) localization
5541
               cups_lang_t *printer,  // I - Printer localization
5542
               const char  *msgid,  // I - Message ID
5543
               char        *buffer, // I - Temporary string buffer
5544
               size_t      bufsize) // I - Size of temporary string buffer
5545
0
{
5546
0
  const char  *text,      // Localized text
5547
0
    *msgptr;    // Pointer into message ID
5548
0
  char    *bufptr;    // Pointer into string buffer
5549
5550
5551
0
  if ((text = _cupsLangString(base, msgid)) == msgid)
5552
0
  {
5553
0
    if ((text = _cupsLangString(printer, msgid)) == msgid)
5554
0
    {
5555
0
      if ((msgptr = strrchr(msgid, '.')) != NULL)
5556
0
        msgptr ++;
5557
0
      else
5558
0
        msgptr = msgid;
5559
5560
0
      if (!*msgptr)
5561
0
        return ("");
5562
5563
0
      cupsCopyString(buffer, msgptr, bufsize);
5564
0
      buffer[0] = (char)toupper(buffer[0] & 255);
5565
0
      for (bufptr = buffer + 1; *bufptr; bufptr ++)
5566
0
      {
5567
0
        if (*bufptr == '-')
5568
0
        {
5569
0
          *bufptr   = ' ';
5570
0
          bufptr[1] = (char)toupper(bufptr[1] & 255);
5571
0
        }
5572
0
      }
5573
5574
0
      text = buffer;
5575
0
    }
5576
0
  }
5577
5578
0
  return (text);
5579
0
}
5580
5581
5582
//
5583
// 'ppd_get_strings()' - Get the strings for a given option and choice.
5584
//
5585
5586
static void
5587
ppd_get_strings(
5588
    ppd_file_t   *ppd,      // I - PPD file
5589
    cups_lang_t  *langs,    // I - Languages
5590
    const char   *option,   // I - PPD option keyword
5591
    ppd_choice_t *choice,   // I - PPD choice
5592
    const char   *pwg_msgid)    // I - PWG message ID
5593
22.8k
{
5594
22.8k
  cups_lang_t *lang;      // Current language
5595
22.8k
  char    ppd_name[PPD_MAX_NAME]; // PPD main keyword
5596
22.8k
  ppd_attr_t  *ppd_attr;    // PPD attribute
5597
22.8k
  _cups_message_t msg;      // Message
5598
5599
5600
22.8k
  msg.msg = (char *)pwg_msgid;
5601
5602
22.8k
  for (lang = langs; lang; lang = lang->next)
5603
0
  {
5604
    // See if the string is already localized...
5605
0
    if (cupsArrayFind(lang->strings, &msg))
5606
0
      continue;     // Yes
5607
5608
    // Otherwise add the text...
5609
0
    if (!strcmp(lang->language, "en"))
5610
0
    {
5611
      // English
5612
0
      msg.str = choice->text;
5613
0
    }
5614
0
    else
5615
0
    {
5616
      // Other languauge...
5617
0
      snprintf(ppd_name, sizeof(ppd_name), "%s.%s", lang->language, option);
5618
0
      if ((ppd_attr = ppdFindAttr(ppd, ppd_name, choice->choice)) != NULL)
5619
0
  msg.str = ppd_attr->text;
5620
0
      else
5621
0
  continue;
5622
0
    }
5623
5624
0
    cupsArrayAdd(lang->strings, &msg);
5625
0
  }
5626
22.8k
}
5627
5628
5629
/*
5630
 * 'ppd_put_strings()' - Write localization attributes to a PPD file.
5631
 */
5632
5633
static void
5634
ppd_put_strings(cups_file_t *fp,  /* I - PPD file */
5635
                cups_lang_t *langs, /* I - Languages */
5636
                const char  *ppd_option,/* I - PPD option */
5637
                const char  *ppd_choice,/* I - PPD choice */
5638
                const char  *pwg_msgid) /* I - PWG message ID */
5639
0
{
5640
0
  cups_lang_t *lang;      /* Current language */
5641
0
  const char  *text;      /* Localized text */
5642
5643
5644
0
  for (lang = langs; lang; lang = lang->next)
5645
0
  {
5646
0
    if (strcmp(lang->language, "en") && (text = _cupsLangString(lang, pwg_msgid)) != pwg_msgid)
5647
0
    {
5648
      // Add the first line of localized text...
5649
0
      cupsFilePrintf(fp, "*%s.%s %s/", lang->language, ppd_option, ppd_choice);
5650
0
      while (*text && *text != '\n')
5651
0
      {
5652
        // Escape ":" and "<"...
5653
0
        if (*text == ':' || *text == '<')
5654
0
          cupsFilePrintf(fp, "<%02X>", *text);
5655
0
        else
5656
0
          cupsFilePutChar(fp, *text);
5657
5658
0
        text ++;
5659
0
      }
5660
0
      cupsFilePuts(fp, ": \"\"\n");
5661
0
    }
5662
0
  }
5663
0
}
5664
5665
5666
/*
5667
 * 'pwg_add_finishing()' - Add a finishings value.
5668
 */
5669
5670
static void
5671
pwg_add_finishing(
5672
    cups_array_t     *finishings, /* I - Finishings array */
5673
    ipp_finishings_t template,    /* I - Finishing template */
5674
    const char       *name,   /* I - PPD option */
5675
    const char       *value)    /* I - PPD choice */
5676
0
{
5677
0
  _pwg_finishings_t *f;   /* New finishings value */
5678
5679
5680
0
  if ((f = (_pwg_finishings_t *)calloc(1, sizeof(_pwg_finishings_t))) != NULL)
5681
0
  {
5682
0
    f->value       = template;
5683
0
    f->num_options = cupsAddOption(name, value, 0, &f->options);
5684
5685
0
    cupsArrayAdd(finishings, f);
5686
0
  }
5687
0
}
5688
5689
5690
/*
5691
 * 'pwg_compare_finishings()' - Compare two finishings values.
5692
 */
5693
5694
static int /* O - Result of comparison */
5695
pwg_compare_finishings(
5696
    _pwg_finishings_t *a, /* I - First finishings value */
5697
    _pwg_finishings_t *b, /* I - Second finishings value */
5698
    void *data)           /* Unused */
5699
0
{
5700
0
  (void)data;
5701
0
  return ((int)b->value - (int)a->value);
5702
0
}
5703
5704
5705
/*
5706
 * 'pwg_compare_sizes()' - Compare two media sizes...
5707
 */
5708
5709
static int                        /* O - Result of comparison */
5710
pwg_compare_sizes(cups_size_t *a, /* I - First media size */
5711
                  cups_size_t *b, /* I - Second media size */
5712
                  void *data)     /* Unused */
5713
0
{
5714
0
  (void)data;
5715
0
  return (strcmp(a->media, b->media));
5716
0
}
5717
5718
5719
/*
5720
 * 'pwg_copy_size()' - Copy a media size.
5721
 */
5722
5723
static cups_size_t *             /* O - New media size */
5724
pwg_copy_size(cups_size_t *size, /* I - Media size to copy */
5725
              void *data)        /* Unused*/
5726
0
{
5727
0
  cups_size_t *newsize = (cups_size_t *)calloc(1, sizeof(cups_size_t));
5728
  /* New media size */
5729
0
  (void)data;
5730
0
  if (newsize)
5731
0
    memcpy(newsize, size, sizeof(cups_size_t));
5732
5733
0
  return (newsize);
5734
0
}
5735
5736
5737
/*
5738
 * 'pwg_free_finishings()' - Free a finishings value.
5739
 */
5740
5741
static void
5742
pwg_free_finishings(_pwg_finishings_t *f, /* I - Finishings value */
5743
                    void *data)           /* Unused */
5744
0
{
5745
0
  (void)data;
5746
0
  cupsFreeOptions(f->num_options, f->options);
5747
0
  free(f);
5748
0
}
5749
5750
5751
/*
5752
 * 'pwg_ppdize_name()' - Convert an IPP keyword to a PPD keyword.
5753
 */
5754
5755
static void
5756
pwg_ppdize_name(const char *ipp,  /* I - IPP keyword */
5757
                char       *name, /* I - Name buffer */
5758
    size_t     namesize)  /* I - Size of name buffer */
5759
0
{
5760
0
  char  *ptr,       /* Pointer into name buffer */
5761
0
  *end;       /* End of name buffer */
5762
5763
5764
0
  if (!ipp || !_cups_isalnum(*ipp))
5765
0
  {
5766
0
    *name = '\0';
5767
0
    return;
5768
0
  }
5769
5770
0
  *name = (char)toupper(*ipp++);
5771
5772
0
  for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;)
5773
0
  {
5774
0
    if (*ipp == '-' && _cups_isalnum(ipp[1]))
5775
0
    {
5776
0
      ipp ++;
5777
0
      *ptr++ = (char)toupper(*ipp++ & 255);
5778
0
    }
5779
0
    else if (*ipp == '_' || *ipp == '.' || *ipp == '-' || _cups_isalnum(*ipp))
5780
0
    {
5781
0
      *ptr++ = *ipp++;
5782
0
    }
5783
0
    else
5784
0
    {
5785
0
      ipp ++;
5786
0
    }
5787
0
  }
5788
5789
0
  *ptr = '\0';
5790
0
}
5791
5792
5793
/*
5794
 * 'pwg_ppdize_resolution()' - Convert PWG resolution values to PPD values.
5795
 */
5796
5797
static void
5798
pwg_ppdize_resolution(
5799
    ipp_attribute_t *attr,    /* I - Attribute to convert */
5800
    int             element,    /* I - Element to convert */
5801
    int             *xres,    /* O - X resolution in DPI */
5802
    int             *yres,    /* O - Y resolution in DPI */
5803
    char            *name,    /* I - Name buffer */
5804
    size_t          namesize)   /* I - Size of name buffer */
5805
0
{
5806
0
  ipp_res_t units;      /* Units for resolution */
5807
5808
5809
0
  *xres = ippGetResolution(attr, element, yres, &units);
5810
5811
0
  if (units == IPP_RES_PER_CM)
5812
0
  {
5813
0
    *xres = (int)(*xres * 2.54);
5814
0
    *yres = (int)(*yres * 2.54);
5815
0
  }
5816
5817
0
  if (name && namesize > 4)
5818
0
  {
5819
0
    if (*xres == *yres)
5820
0
      snprintf(name, namesize, "%ddpi", *xres);
5821
0
    else
5822
0
      snprintf(name, namesize, "%dx%ddpi", *xres, *yres);
5823
0
  }
5824
0
}
5825
5826
5827
/*
5828
 * 'pwg_unppdize_name()' - Convert a PPD keyword to a lowercase IPP keyword.
5829
 */
5830
5831
static void
5832
pwg_unppdize_name(const char *ppd,  /* I - PPD keyword */
5833
      char       *name, /* I - Name buffer */
5834
                  size_t     namesize,  /* I - Size of name buffer */
5835
                  const char *dashchars)/* I - Characters to be replaced by dashes */
5836
10.2k
{
5837
10.2k
  char  *ptr,       /* Pointer into name buffer */
5838
10.2k
  *end;       /* End of name buffer */
5839
10.2k
  int   nodash = 1;                     /* Next char in IPP name cannot be a
5840
                                           dash (first char or after a dash) */
5841
5842
5843
10.2k
  if (_cups_islower(*ppd))
5844
3.13k
  {
5845
   /*
5846
    * Already lowercase name, use as-is?
5847
    */
5848
5849
3.13k
    const char *ppdptr;     /* Pointer into PPD keyword */
5850
5851
9.05k
    for (ppdptr = ppd + 1; *ppdptr; ppdptr ++)
5852
7.30k
      if (_cups_isupper(*ppdptr) || strchr(dashchars, *ppdptr) ||
5853
6.65k
    (*ppdptr == '-' && *(ppdptr - 1) == '-') ||
5854
6.57k
    (*ppdptr == '-' && *(ppdptr + 1) == '\0'))
5855
1.38k
        break;
5856
5857
3.13k
    if (!*ppdptr)
5858
1.74k
    {
5859
1.74k
      cupsCopyString(name, ppd, namesize);
5860
1.74k
      return;
5861
1.74k
    }
5862
3.13k
  }
5863
5864
70.0k
  for (ptr = name, end = name + namesize - 1; *ppd && ptr < end; ppd ++)
5865
61.5k
  {
5866
61.5k
    if (_cups_isalnum(*ppd) || *ppd == '.' || *ppd == '_')
5867
54.3k
    {
5868
54.3k
      *ptr++ = (char)tolower(*ppd & 255);
5869
54.3k
      nodash = 0;
5870
54.3k
    }
5871
7.13k
    else if (*ppd == '-' || strchr(dashchars, *ppd))
5872
2.15k
    {
5873
2.15k
      if (nodash == 0)
5874
1.74k
      {
5875
1.74k
  *ptr++ = '-';
5876
1.74k
  nodash = 1;
5877
1.74k
      }
5878
2.15k
    }
5879
5880
61.5k
    if (nodash == 0)
5881
57.5k
    {
5882
57.5k
      if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) &&
5883
39.9k
    _cups_isupper(ppd[1]) && ptr < end)
5884
3.83k
      {
5885
3.83k
  *ptr++ = '-';
5886
3.83k
  nodash = 1;
5887
3.83k
      }
5888
53.7k
      else if (!isdigit(*ppd & 255) && isdigit(ppd[1] & 255))
5889
4.93k
      {
5890
4.93k
  *ptr++ = '-';
5891
4.93k
  nodash = 1;
5892
4.93k
      }
5893
57.5k
    }
5894
61.5k
  }
5895
5896
  /* Remove trailing dashes */
5897
9.42k
  while (ptr > name && *(ptr - 1) == '-')
5898
925
    ptr --;
5899
5900
8.50k
  *ptr = '\0';
5901
8.50k
}