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