1/*
2 * Common filter routines for CUPS.
3 *
4 * Copyright 2007-2014 by Apple Inc.
5 * Copyright 1997-2006 by Easy Software Products.
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 * This file is subject to the Apple OS-Developed Software exception.
14 */
15
16/*
17 * Include necessary headers...
18 */
19
20#include "common.h"
21#include <locale.h>
22
23
24/*
25 * Globals...
26 */
27
28int	Orientation = 0,		/* 0 = portrait, 1 = landscape, etc. */
29	Duplex = 0,			/* Duplexed? */
30	LanguageLevel = 1,		/* Language level of printer */
31	ColorDevice = 1;		/* Do color text? */
32float	PageLeft = 18.0f,		/* Left margin */
33	PageRight = 594.0f,		/* Right margin */
34	PageBottom = 36.0f,		/* Bottom margin */
35	PageTop = 756.0f,		/* Top margin */
36	PageWidth = 612.0f,		/* Total page width */
37	PageLength = 792.0f;		/* Total page length */
38
39
40/*
41 * 'SetCommonOptions()' - Set common filter options for media size, etc.
42 */
43
44ppd_file_t *				/* O - PPD file */
45SetCommonOptions(
46    int           num_options,		/* I - Number of options */
47    cups_option_t *options,		/* I - Options */
48    int           change_size)		/* I - Change page size? */
49{
50  ppd_file_t	*ppd;			/* PPD file */
51  ppd_size_t	*pagesize;		/* Current page size */
52  const char	*val;			/* Option value */
53
54
55#ifdef LC_TIME
56  setlocale(LC_TIME, "");
57#endif /* LC_TIME */
58
59  ppd = ppdOpenFile(getenv("PPD"));
60
61  ppdMarkDefaults(ppd);
62  cupsMarkOptions(ppd, num_options, options);
63
64  if ((pagesize = ppdPageSize(ppd, NULL)) != NULL)
65  {
66    PageWidth  = pagesize->width;
67    PageLength = pagesize->length;
68    PageTop    = pagesize->top;
69    PageBottom = pagesize->bottom;
70    PageLeft   = pagesize->left;
71    PageRight  = pagesize->right;
72
73    fprintf(stderr, "DEBUG: Page = %.0fx%.0f; %.0f,%.0f to %.0f,%.0f\n",
74            PageWidth, PageLength, PageLeft, PageBottom, PageRight, PageTop);
75  }
76
77  if (ppd != NULL)
78  {
79    ColorDevice   = ppd->color_device;
80    LanguageLevel = ppd->language_level;
81  }
82
83  if ((val = cupsGetOption("landscape", num_options, options)) != NULL)
84  {
85    if (_cups_strcasecmp(val, "no") != 0 && _cups_strcasecmp(val, "off") != 0 &&
86        _cups_strcasecmp(val, "false") != 0)
87    {
88      if (ppd && ppd->landscape > 0)
89        Orientation = 1;
90      else
91        Orientation = 3;
92    }
93  }
94  else if ((val = cupsGetOption("orientation-requested", num_options, options)) != NULL)
95  {
96   /*
97    * Map IPP orientation values to 0 to 3:
98    *
99    *   3 = 0 degrees   = 0
100    *   4 = 90 degrees  = 1
101    *   5 = -90 degrees = 3
102    *   6 = 180 degrees = 2
103    */
104
105    Orientation = atoi(val) - 3;
106    if (Orientation >= 2)
107      Orientation ^= 1;
108  }
109
110  if ((val = cupsGetOption("page-left", num_options, options)) != NULL)
111  {
112    switch (Orientation & 3)
113    {
114      case 0 :
115          PageLeft = (float)atof(val);
116	  break;
117      case 1 :
118          PageBottom = (float)atof(val);
119	  break;
120      case 2 :
121          PageRight = PageWidth - (float)atof(val);
122	  break;
123      case 3 :
124          PageTop = PageLength - (float)atof(val);
125	  break;
126    }
127  }
128
129  if ((val = cupsGetOption("page-right", num_options, options)) != NULL)
130  {
131    switch (Orientation & 3)
132    {
133      case 0 :
134          PageRight = PageWidth - (float)atof(val);
135	  break;
136      case 1 :
137          PageTop = PageLength - (float)atof(val);
138	  break;
139      case 2 :
140          PageLeft = (float)atof(val);
141	  break;
142      case 3 :
143          PageBottom = (float)atof(val);
144	  break;
145    }
146  }
147
148  if ((val = cupsGetOption("page-bottom", num_options, options)) != NULL)
149  {
150    switch (Orientation & 3)
151    {
152      case 0 :
153          PageBottom = (float)atof(val);
154	  break;
155      case 1 :
156          PageLeft = (float)atof(val);
157	  break;
158      case 2 :
159          PageTop = PageLength - (float)atof(val);
160	  break;
161      case 3 :
162          PageRight = PageWidth - (float)atof(val);
163	  break;
164    }
165  }
166
167  if ((val = cupsGetOption("page-top", num_options, options)) != NULL)
168  {
169    switch (Orientation & 3)
170    {
171      case 0 :
172          PageTop = PageLength - (float)atof(val);
173	  break;
174      case 1 :
175          PageRight = PageWidth - (float)atof(val);
176	  break;
177      case 2 :
178          PageBottom = (float)atof(val);
179	  break;
180      case 3 :
181          PageLeft = (float)atof(val);
182	  break;
183    }
184  }
185
186  if (change_size)
187    UpdatePageVars();
188
189  if (ppdIsMarked(ppd, "Duplex", "DuplexNoTumble") ||
190      ppdIsMarked(ppd, "Duplex", "DuplexTumble") ||
191      ppdIsMarked(ppd, "JCLDuplex", "DuplexNoTumble") ||
192      ppdIsMarked(ppd, "JCLDuplex", "DuplexTumble") ||
193      ppdIsMarked(ppd, "EFDuplex", "DuplexNoTumble") ||
194      ppdIsMarked(ppd, "EFDuplex", "DuplexTumble") ||
195      ppdIsMarked(ppd, "KD03Duplex", "DuplexNoTumble") ||
196      ppdIsMarked(ppd, "KD03Duplex", "DuplexTumble"))
197    Duplex = 1;
198
199  return (ppd);
200}
201
202
203/*
204 * 'UpdatePageVars()' - Update the page variables for the orientation.
205 */
206
207void
208UpdatePageVars(void)
209{
210  float		temp;			/* Swapping variable */
211
212
213  switch (Orientation & 3)
214  {
215    case 0 : /* Portait */
216        break;
217
218    case 1 : /* Landscape */
219	temp       = PageLeft;
220	PageLeft   = PageBottom;
221	PageBottom = temp;
222
223	temp       = PageRight;
224	PageRight  = PageTop;
225	PageTop    = temp;
226
227	temp       = PageWidth;
228	PageWidth  = PageLength;
229	PageLength = temp;
230	break;
231
232    case 2 : /* Reverse Portrait */
233	temp       = PageWidth - PageLeft;
234	PageLeft   = PageWidth - PageRight;
235	PageRight  = temp;
236
237	temp       = PageLength - PageBottom;
238	PageBottom = PageLength - PageTop;
239	PageTop    = temp;
240        break;
241
242    case 3 : /* Reverse Landscape */
243	temp       = PageWidth - PageLeft;
244	PageLeft   = PageWidth - PageRight;
245	PageRight  = temp;
246
247	temp       = PageLength - PageBottom;
248	PageBottom = PageLength - PageTop;
249	PageTop    = temp;
250
251	temp       = PageLeft;
252	PageLeft   = PageBottom;
253	PageBottom = temp;
254
255	temp       = PageRight;
256	PageRight  = PageTop;
257	PageTop    = temp;
258
259	temp       = PageWidth;
260	PageWidth  = PageLength;
261	PageLength = temp;
262	break;
263  }
264}
265
266
267/*
268 * 'WriteCommon()' - Write common procedures...
269 */
270
271void
272WriteCommon(void)
273{
274  puts("% x y w h ESPrc - Clip to a rectangle.\n"
275       "userdict/ESPrc/rectclip where{pop/rectclip load}\n"
276       "{{newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
277       "neg 0 rlineto closepath clip newpath}bind}ifelse put");
278  puts("% x y w h ESPrf - Fill a rectangle.\n"
279       "userdict/ESPrf/rectfill where{pop/rectfill load}\n"
280       "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
281       "neg 0 rlineto closepath fill grestore}bind}ifelse put");
282  puts("% x y w h ESPrs - Stroke a rectangle.\n"
283       "userdict/ESPrs/rectstroke where{pop/rectstroke load}\n"
284       "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
285       "neg 0 rlineto closepath stroke grestore}bind}ifelse put");
286}
287
288
289/*
290 * 'WriteLabelProlog()' - Write the prolog with the classification
291 *                        and page label.
292 */
293
294void
295WriteLabelProlog(const char *label,	/* I - Page label */
296		 float      bottom,	/* I - Bottom position in points */
297		 float      top,	/* I - Top position in points */
298		 float      width)	/* I - Width in points */
299{
300  const char	*classification;	/* CLASSIFICATION environment variable */
301  const char	*ptr;			/* Temporary string pointer */
302
303
304 /*
305  * First get the current classification...
306  */
307
308  if ((classification = getenv("CLASSIFICATION")) == NULL)
309    classification = "";
310  if (strcmp(classification, "none") == 0)
311    classification = "";
312
313 /*
314  * If there is nothing to show, bind an empty 'write labels' procedure
315  * and return...
316  */
317
318  if (!classification[0] && (label == NULL || !label[0]))
319  {
320    puts("userdict/ESPwl{}bind put");
321    return;
322  }
323
324 /*
325  * Set the classification + page label string...
326  */
327
328  printf("userdict");
329  if (strcmp(classification, "confidential") == 0)
330    printf("/ESPpl(CONFIDENTIAL");
331  else if (strcmp(classification, "classified") == 0)
332    printf("/ESPpl(CLASSIFIED");
333  else if (strcmp(classification, "secret") == 0)
334    printf("/ESPpl(SECRET");
335  else if (strcmp(classification, "topsecret") == 0)
336    printf("/ESPpl(TOP SECRET");
337  else if (strcmp(classification, "unclassified") == 0)
338    printf("/ESPpl(UNCLASSIFIED");
339  else
340  {
341    printf("/ESPpl(");
342
343    for (ptr = classification; *ptr; ptr ++)
344      if (*ptr < 32 || *ptr > 126)
345        printf("\\%03o", *ptr);
346      else if (*ptr == '_')
347        putchar(' ');
348      else
349      {
350	if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
351	  putchar('\\');
352
353	putchar(*ptr);
354      }
355  }
356
357  if (label)
358  {
359    if (classification[0])
360      printf(" - ");
361
362   /*
363    * Quote the label string as needed...
364    */
365
366    for (ptr = label; *ptr; ptr ++)
367      if (*ptr < 32 || *ptr > 126)
368        printf("\\%03o", *ptr);
369      else
370      {
371	if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
372	  putchar('\\');
373
374	putchar(*ptr);
375      }
376  }
377
378  puts(")put");
379
380 /*
381  * Then get a 14 point Helvetica-Bold font...
382  */
383
384  puts("userdict/ESPpf /Helvetica-Bold findfont 14 scalefont put");
385
386 /*
387  * Finally, the procedure to write the labels on the page...
388  */
389
390  puts("userdict/ESPwl{");
391  puts("  ESPpf setfont");
392  printf("  ESPpl stringwidth pop dup 12 add exch -0.5 mul %.0f add\n",
393         width * 0.5f);
394  puts("  1 setgray");
395  printf("  dup 6 sub %.0f 3 index 20 ESPrf\n", bottom - 2.0);
396  printf("  dup 6 sub %.0f 3 index 20 ESPrf\n", top - 18.0);
397  puts("  0 setgray");
398  printf("  dup 6 sub %.0f 3 index 20 ESPrs\n", bottom - 2.0);
399  printf("  dup 6 sub %.0f 3 index 20 ESPrs\n", top - 18.0);
400  printf("  dup %.0f moveto ESPpl show\n", bottom + 2.0);
401  printf("  %.0f moveto ESPpl show\n", top - 14.0);
402  puts("pop");
403  puts("}bind put");
404}
405
406
407/*
408 * 'WriteLabels()' - Write the actual page labels.
409 */
410
411void
412WriteLabels(int orient)	/* I - Orientation of the page */
413{
414  float	width,		/* Width of page */
415	length;		/* Length of page */
416
417
418  puts("gsave");
419
420  if ((orient ^ Orientation) & 1)
421  {
422    width  = PageLength;
423    length = PageWidth;
424  }
425  else
426  {
427    width  = PageWidth;
428    length = PageLength;
429  }
430
431  switch (orient & 3)
432  {
433    case 1 : /* Landscape */
434        printf("%.1f 0.0 translate 90 rotate\n", length);
435        break;
436    case 2 : /* Reverse Portrait */
437        printf("%.1f %.1f translate 180 rotate\n", width, length);
438        break;
439    case 3 : /* Reverse Landscape */
440        printf("0.0 %.1f translate -90 rotate\n", width);
441        break;
442  }
443
444  puts("ESPwl");
445  puts("grestore");
446}
447
448
449/*
450 * 'WriteTextComment()' - Write a DSC text comment.
451 */
452
453void
454WriteTextComment(const char *name,	/* I - Comment name ("Title", etc.) */
455                 const char *value)	/* I - Comment value */
456{
457  int	len;				/* Current line length */
458
459
460 /*
461  * DSC comments are of the form:
462  *
463  *   %%name: value
464  *
465  * The name and value must be limited to 7-bit ASCII for most printers,
466  * so we escape all non-ASCII and ASCII control characters as described
467  * in the Adobe Document Structuring Conventions specification.
468  */
469
470  printf("%%%%%s: (", name);
471  len = 5 + (int)strlen(name);
472
473  while (*value)
474  {
475    if (*value < ' ' || *value >= 127)
476    {
477     /*
478      * Escape this character value...
479      */
480
481      if (len >= 251)			/* Keep line < 254 chars */
482        break;
483
484      printf("\\%03o", *value & 255);
485      len += 4;
486    }
487    else if (*value == '\\')
488    {
489     /*
490      * Escape the backslash...
491      */
492
493      if (len >= 253)			/* Keep line < 254 chars */
494        break;
495
496      putchar('\\');
497      putchar('\\');
498      len += 2;
499    }
500    else
501    {
502     /*
503      * Put this character literally...
504      */
505
506      if (len >= 254)			/* Keep line < 254 chars */
507        break;
508
509      putchar(*value);
510      len ++;
511    }
512
513    value ++;
514  }
515
516  puts(")");
517}
518