1/*
2 * Page size functions for CUPS.
3 *
4 * Copyright 2007-2015 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
6 *
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file.  If this file is
11 * missing or damaged, see the license at "http://www.cups.org/".
12 *
13 * PostScript is a trademark of Adobe Systems, Inc.
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 */
17
18/*
19 * Include necessary headers...
20 */
21
22#include "string-private.h"
23#include "debug-private.h"
24#include "ppd.h"
25
26
27/*
28 * 'ppdPageSize()' - Get the page size record for the named size.
29 */
30
31ppd_size_t *				/* O - Size record for page or NULL */
32ppdPageSize(ppd_file_t *ppd,		/* I - PPD file record */
33            const char *name)		/* I - Size name */
34{
35  int		i;			/* Looping var */
36  ppd_size_t	*size;			/* Current page size */
37  double	w, l;			/* Width and length of page */
38  char		*nameptr;		/* Pointer into name */
39  struct lconv	*loc;			/* Locale data */
40  ppd_coption_t	*coption;		/* Custom option for page size */
41  ppd_cparam_t	*cparam;		/* Custom option parameter */
42
43
44  DEBUG_printf(("2ppdPageSize(ppd=%p, name=\"%s\")", ppd, name));
45
46  if (!ppd)
47  {
48    DEBUG_puts("3ppdPageSize: Bad PPD pointer, returning NULL...");
49    return (NULL);
50  }
51
52  if (name)
53  {
54    if (!strncmp(name, "Custom.", 7) && ppd->variable_sizes)
55    {
56     /*
57      * Find the custom page size...
58      */
59
60      for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
61	if (!strcmp("Custom", size->name))
62          break;
63
64      if (!i)
65      {
66	DEBUG_puts("3ppdPageSize: No custom sizes, returning NULL...");
67        return (NULL);
68      }
69
70     /*
71      * Variable size; size name can be one of the following:
72      *
73      *    Custom.WIDTHxLENGTHin    - Size in inches
74      *    Custom.WIDTHxLENGTHft    - Size in feet
75      *    Custom.WIDTHxLENGTHcm    - Size in centimeters
76      *    Custom.WIDTHxLENGTHmm    - Size in millimeters
77      *    Custom.WIDTHxLENGTHm     - Size in meters
78      *    Custom.WIDTHxLENGTH[pt]  - Size in points
79      */
80
81      loc = localeconv();
82      w   = _cupsStrScand(name + 7, &nameptr, loc);
83      if (!nameptr || *nameptr != 'x')
84        return (NULL);
85
86      l = _cupsStrScand(nameptr + 1, &nameptr, loc);
87      if (!nameptr)
88        return (NULL);
89
90      if (!_cups_strcasecmp(nameptr, "in"))
91      {
92        w *= 72.0;
93	l *= 72.0;
94      }
95      else if (!_cups_strcasecmp(nameptr, "ft"))
96      {
97        w *= 12.0 * 72.0;
98	l *= 12.0 * 72.0;
99      }
100      else if (!_cups_strcasecmp(nameptr, "mm"))
101      {
102        w *= 72.0 / 25.4;
103        l *= 72.0 / 25.4;
104      }
105      else if (!_cups_strcasecmp(nameptr, "cm"))
106      {
107        w *= 72.0 / 2.54;
108        l *= 72.0 / 2.54;
109      }
110      else if (!_cups_strcasecmp(nameptr, "m"))
111      {
112        w *= 72.0 / 0.0254;
113        l *= 72.0 / 0.0254;
114      }
115
116      size->width  = (float)w;
117      size->length = (float)l;
118      size->left   = ppd->custom_margins[0];
119      size->bottom = ppd->custom_margins[1];
120      size->right  = (float)(w - ppd->custom_margins[2]);
121      size->top    = (float)(l - ppd->custom_margins[3]);
122
123     /*
124      * Update the custom option records for the page size, too...
125      */
126
127      if ((coption = ppdFindCustomOption(ppd, "PageSize")) != NULL)
128      {
129        if ((cparam = ppdFindCustomParam(coption, "Width")) != NULL)
130	  cparam->current.custom_points = (float)w;
131
132        if ((cparam = ppdFindCustomParam(coption, "Height")) != NULL)
133	  cparam->current.custom_points = (float)l;
134      }
135
136     /*
137      * Return the page size...
138      */
139
140      DEBUG_printf(("3ppdPageSize: Returning %p (\"%s\", %gx%g)", size,
141                    size->name, size->width, size->length));
142
143      return (size);
144    }
145    else
146    {
147     /*
148      * Lookup by name...
149      */
150
151      for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
152	if (!_cups_strcasecmp(name, size->name))
153	{
154	  DEBUG_printf(("3ppdPageSize: Returning %p (\"%s\", %gx%g)", size,
155			size->name, size->width, size->length));
156
157          return (size);
158	}
159    }
160  }
161  else
162  {
163   /*
164    * Find default...
165    */
166
167    for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
168      if (size->marked)
169      {
170	DEBUG_printf(("3ppdPageSize: Returning %p (\"%s\", %gx%g)", size,
171		      size->name, size->width, size->length));
172
173        return (size);
174      }
175  }
176
177  DEBUG_puts("3ppdPageSize: Size not found, returning NULL");
178
179  return (NULL);
180}
181
182
183/*
184 * 'ppdPageSizeLimits()' - Return the custom page size limits.
185 *
186 * This function returns the minimum and maximum custom page sizes and printable
187 * areas based on the currently-marked (selected) options.
188 *
189 * If the specified PPD file does not support custom page sizes, both
190 * "minimum" and "maximum" are filled with zeroes.
191 *
192 * @since CUPS 1.4/macOS 10.6@
193 */
194
195int					/* O - 1 if custom sizes are supported, 0 otherwise */
196ppdPageSizeLimits(ppd_file_t *ppd,	/* I - PPD file record */
197                  ppd_size_t *minimum,	/* O - Minimum custom size */
198		  ppd_size_t *maximum)	/* O - Maximum custom size */
199{
200  ppd_choice_t	*qualifier2,		/* Second media qualifier */
201		*qualifier3;		/* Third media qualifier */
202  ppd_attr_t	*attr;			/* Attribute */
203  float		width,			/* Min/max width */
204		length;			/* Min/max length */
205  char		spec[PPD_MAX_NAME];	/* Selector for min/max */
206
207
208 /*
209  * Range check input...
210  */
211
212  if (!ppd || !ppd->variable_sizes || !minimum || !maximum)
213  {
214    if (minimum)
215      memset(minimum, 0, sizeof(ppd_size_t));
216
217    if (maximum)
218      memset(maximum, 0, sizeof(ppd_size_t));
219
220    return (0);
221  }
222
223 /*
224  * See if we have the cupsMediaQualifier2 and cupsMediaQualifier3 attributes...
225  */
226
227  cupsArraySave(ppd->sorted_attrs);
228
229  if ((attr = ppdFindAttr(ppd, "cupsMediaQualifier2", NULL)) != NULL &&
230      attr->value)
231    qualifier2 = ppdFindMarkedChoice(ppd, attr->value);
232  else
233    qualifier2 = NULL;
234
235  if ((attr = ppdFindAttr(ppd, "cupsMediaQualifier3", NULL)) != NULL &&
236      attr->value)
237    qualifier3 = ppdFindMarkedChoice(ppd, attr->value);
238  else
239    qualifier3 = NULL;
240
241 /*
242  * Figure out the current minimum width and length...
243  */
244
245  width  = ppd->custom_min[0];
246  length = ppd->custom_min[1];
247
248  if (qualifier2)
249  {
250   /*
251    * Try getting cupsMinSize...
252    */
253
254    if (qualifier3)
255    {
256      snprintf(spec, sizeof(spec), ".%s.%s", qualifier2->choice,
257	       qualifier3->choice);
258      attr = ppdFindAttr(ppd, "cupsMinSize", spec);
259    }
260    else
261      attr = NULL;
262
263    if (!attr)
264    {
265      snprintf(spec, sizeof(spec), ".%s.", qualifier2->choice);
266      attr = ppdFindAttr(ppd, "cupsMinSize", spec);
267    }
268
269    if (!attr && qualifier3)
270    {
271      snprintf(spec, sizeof(spec), "..%s", qualifier3->choice);
272      attr = ppdFindAttr(ppd, "cupsMinSize", spec);
273    }
274
275    if ((attr && attr->value &&
276         sscanf(attr->value, "%f%f", &width, &length) != 2) || !attr)
277    {
278      width  = ppd->custom_min[0];
279      length = ppd->custom_min[1];
280    }
281  }
282
283  minimum->width  = width;
284  minimum->length = length;
285  minimum->left   = ppd->custom_margins[0];
286  minimum->bottom = ppd->custom_margins[1];
287  minimum->right  = width - ppd->custom_margins[2];
288  minimum->top    = length - ppd->custom_margins[3];
289
290 /*
291  * Figure out the current maximum width and length...
292  */
293
294  width  = ppd->custom_max[0];
295  length = ppd->custom_max[1];
296
297  if (qualifier2)
298  {
299   /*
300    * Try getting cupsMaxSize...
301    */
302
303    if (qualifier3)
304    {
305      snprintf(spec, sizeof(spec), ".%s.%s", qualifier2->choice,
306	       qualifier3->choice);
307      attr = ppdFindAttr(ppd, "cupsMaxSize", spec);
308    }
309    else
310      attr = NULL;
311
312    if (!attr)
313    {
314      snprintf(spec, sizeof(spec), ".%s.", qualifier2->choice);
315      attr = ppdFindAttr(ppd, "cupsMaxSize", spec);
316    }
317
318    if (!attr && qualifier3)
319    {
320      snprintf(spec, sizeof(spec), "..%s", qualifier3->choice);
321      attr = ppdFindAttr(ppd, "cupsMaxSize", spec);
322    }
323
324    if (!attr ||
325        (attr->value && sscanf(attr->value, "%f%f", &width, &length) != 2))
326    {
327      width  = ppd->custom_max[0];
328      length = ppd->custom_max[1];
329    }
330  }
331
332  maximum->width  = width;
333  maximum->length = length;
334  maximum->left   = ppd->custom_margins[0];
335  maximum->bottom = ppd->custom_margins[1];
336  maximum->right  = width - ppd->custom_margins[2];
337  maximum->top    = length - ppd->custom_margins[3];
338
339 /*
340  * Return the min and max...
341  */
342
343  cupsArrayRestore(ppd->sorted_attrs);
344
345  return (1);
346}
347
348
349/*
350 * 'ppdPageWidth()' - Get the page width for the given size.
351 */
352
353float				/* O - Width of page in points or 0.0 */
354ppdPageWidth(ppd_file_t *ppd,	/* I - PPD file record */
355             const char *name)	/* I - Size name */
356{
357  ppd_size_t	*size;		/* Page size */
358
359
360  if ((size = ppdPageSize(ppd, name)) == NULL)
361    return (0.0);
362  else
363    return (size->width);
364}
365
366
367/*
368 * 'ppdPageLength()' - Get the page length for the given size.
369 */
370
371float				/* O - Length of page in points or 0.0 */
372ppdPageLength(ppd_file_t *ppd,	/* I - PPD file */
373              const char *name)	/* I - Size name */
374{
375  ppd_size_t	*size;		/* Page size */
376
377
378  if ((size = ppdPageSize(ppd, name)) == NULL)
379    return (0.0);
380  else
381    return (size->length);
382}
383