]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/emit.c
Add support for "ll" as well as "ll_CC" for PPD translation strings.
[thirdparty/cups.git] / cups / emit.c
CommitLineData
ea930040 1/*
b2e10895 2 * "$Id$"
ea930040 3 *
3a193f5e 4 * PPD code emission routines for the Common UNIX Printing System (CUPS).
ea930040 5 *
b2e10895 6 * Copyright 1997-2005 by Easy Software Products, all rights reserved.
ea930040 7 *
3a193f5e 8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
ea930040 16 * Easy Software Products
58ec2a95 17 * 44141 Airport View Drive, Suite 204
b2e10895 18 * Hollywood, Maryland 20636 USA
ea930040 19 *
c4dcf3cc 20 * Voice: (301) 373-9600
ea930040 21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
2b85e375 24 * PostScript is a trademark of Adobe Systems, Inc.
25 *
dab1a4d8 26 * This file is subject to the Apple OS-Developed Software exception.
27 *
ea930040 28 * Contents:
29 *
ed6078a4 30 * ppdCollect() - Collect all marked options that reside in the
31 * specified section.
32 * ppdEmit() - Emit code for marked options to a file.
33 * ppdEmitFd() - Emit code for marked options to a file.
34 * ppdEmitJCL() - Emit code for JCL options to a file.
35 * ppd_handle_media() - Handle media selection...
36 * ppd_sort() - Sort options by ordering numbers...
ea930040 37 */
38
39/*
2b85e375 40 * Include necessary headers...
41 */
42
43#include "ppd.h"
3b960317 44#include <stdlib.h>
aaa5608c 45#include "string.h"
3b960317 46
47#if defined(WIN32) || defined(__EMX__)
2456b740 48# include <io.h>
49#else
50# include <unistd.h>
3b960317 51#endif /* WIN32 || __EMX__ */
2b85e375 52
53
8c1333e2 54/*
55 * Local functions...
56 */
57
ed6078a4 58static void ppd_handle_media(ppd_file_t *ppd);
8c1333e2 59static int ppd_sort(ppd_choice_t **c1, ppd_choice_t **c2);
cbcf8057 60
61
ed6078a4 62/*
63 * Local globals...
64 */
65
66static const char *ppd_custom_code =
67 "pop pop pop\n"
68 "<</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice\n";
69
70
cbcf8057 71/*
72 * 'ppdCollect()' - Collect all marked options that reside in the specified
73 * section.
74 */
75
76int /* O - Number of options marked */
77ppdCollect(ppd_file_t *ppd, /* I - PPD file data */
78 ppd_section_t section, /* I - Section to collect */
79 ppd_choice_t ***choices) /* O - Pointers to choices */
80{
81 int i, j, k, m; /* Looping vars */
82 ppd_group_t *g, /* Current group */
83 *sg; /* Current sub-group */
84 ppd_option_t *o; /* Current option */
85 ppd_choice_t *c; /* Current choice */
86 int count; /* Number of choices collected */
87 ppd_choice_t **collect; /* Collected choices */
88
89
90 if (ppd == NULL)
91 return (0);
92
93 /*
94 * Allocate memory for up to 1000 selected choices...
95 */
96
97 count = 0;
98 collect = calloc(sizeof(ppd_choice_t *), 1000);
99
100 /*
101 * Loop through all options and add choices as needed...
102 */
103
104 for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
105 {
106 for (j = g->num_options, o = g->options; j > 0; j --, o ++)
107 if (o->section == section)
108 for (k = o->num_choices, c = o->choices; k > 0; k --, c ++)
109 if (c->marked && count < 1000)
110 {
111 collect[count] = c;
112 count ++;
113 }
114
115 for (j = g->num_subgroups, sg = g->subgroups; j > 0; j --, sg ++)
116 for (k = sg->num_options, o = sg->options; k > 0; k --, o ++)
117 if (o->section == section)
118 for (m = o->num_choices, c = o->choices; m > 0; m --, c ++)
119 if (c->marked && count < 1000)
120 {
121 collect[count] = c;
122 count ++;
123 }
124 }
125
126 /*
127 * If we have more than 1 marked choice, sort them...
128 */
129
130 if (count > 1)
131 qsort(collect, count, sizeof(ppd_choice_t *),
132 (int (*)(const void *, const void *))ppd_sort);
133
134 /*
135 * Return the array and number of choices; if 0, free the array since
136 * it isn't needed.
137 */
138
139 if (count > 0)
140 {
141 *choices = collect;
142 return (count);
143 }
144 else
145 {
146 *choices = NULL;
147 free(collect);
148 return (0);
149 }
150}
8c1333e2 151
152
2b85e375 153/*
154 * 'ppdEmit()' - Emit code for marked options to a file.
155 */
156
157int /* O - 0 on success, -1 on failure */
158ppdEmit(ppd_file_t *ppd, /* I - PPD file record */
159 FILE *fp, /* I - File to write to */
160 ppd_section_t section) /* I - Section to write */
161{
8c1333e2 162 int i, /* Looping var */
163 count; /* Number of choices */
164 ppd_choice_t **choices; /* Choices */
6fdf969a 165 ppd_size_t *size; /* Custom page size */
8c1333e2 166
167
ed6078a4 168 /*
169 * Use PageSize or PageRegion as required...
170 */
171
172 ppd_handle_media(ppd);
173
174 /*
175 * Collect the options we need to emit and emit them!
176 */
177
cbcf8057 178 if ((count = ppdCollect(ppd, section, &choices)) == 0)
8c1333e2 179 return (0);
180
181 for (i = 0; i < count; i ++)
182 if (section != PPD_ORDER_EXIT && section != PPD_ORDER_JCL)
183 {
96831d7f 184 /*
185 * Send wrapper commands to prevent printer errors for unsupported
186 * options...
187 */
188
189 if (fputs("[{\n", fp) < 0)
190 {
191 free(choices);
192 return (-1);
193 }
194
8c1333e2 195 /*
196 * Send DSC comments with option...
197 */
198
ed6078a4 199 if ((strcasecmp(((ppd_option_t *)choices[i]->option)->keyword, "PageSize") == 0 ||
200 strcasecmp(((ppd_option_t *)choices[i]->option)->keyword, "PageRegion") == 0) &&
a7b3e92e 201 strcasecmp(choices[i]->choice, "Custom") == 0)
6fdf969a 202 {
203 /*
8c5f90ff 204 * Variable size; write out standard size options, using the
205 * parameter positions defined in the PPD file...
6fdf969a 206 */
207
8c5f90ff 208 ppd_attr_t *attr; /* PPD attribute */
209 int pos, /* Position of custom value */
ae90e335 210 values[5], /* Values for custom command */
211 orientation; /* Orientation to use */
8c5f90ff 212
213
214 fputs("%%BeginFeature: *CustomPageSize True\n", fp);
215
6fdf969a 216 size = ppdPageSize(ppd, "Custom");
8c5f90ff 217
218 memset(values, 0, sizeof(values));
219
220 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Width")) != NULL)
221 {
222 pos = atoi(attr->value) - 1;
223
224 if (pos < 0 || pos > 4)
225 pos = 0;
226 }
227 else
228 pos = 0;
229
230 values[pos] = (int)size->width;
231
232 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Height")) != NULL)
233 {
234 pos = atoi(attr->value) - 1;
235
236 if (pos < 0 || pos > 4)
237 pos = 1;
238 }
239 else
240 pos = 1;
241
242 values[pos] = (int)size->length;
243
7161ef54 244 /*
245 * According to the Adobe PPD specification, an orientation of 1
246 * will produce a print that comes out upside-down with the X
247 * axis perpendicular to the direction of feed, which is exactly
248 * what we want to be consistent with non-PS printers.
249 *
250 * We could also use an orientation of 3 to produce output that
251 * comes out rightside-up (this is the default for many large format
252 * printer PPDs), however for consistency we will stick with the
253 * value 1.
254 *
255 * If we wanted to get fancy, we could use orientations of 0 or
256 * 2 and swap the width and length, however we don't want to get
257 * fancy, we just want it to work consistently.
258 *
259 * The orientation value is range limited by the Orientation
260 * parameter definition, so certain non-PS printer drivers that
261 * only support an Orientation of 0 will get the value 0 as
262 * expected.
263 */
264
265 orientation = 1;
ae90e335 266
267 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize",
268 "Orientation")) != NULL)
269 {
270 int min_orient, max_orient; /* Minimum and maximum orientations */
271
272
273 if (sscanf(attr->value, "%d%*s%d%d", &pos, &min_orient,
274 &max_orient) != 3)
275 pos = 4;
276 else
277 {
5edf6841 278 pos --;
279
ae90e335 280 if (pos < 0 || pos > 4)
281 pos = 4;
282
283 if (orientation > max_orient)
284 orientation = max_orient;
285 else if (orientation < min_orient)
286 orientation = min_orient;
287 }
288 }
289 else
290 pos = 4;
291
292 values[pos] = orientation;
293
8c5f90ff 294 fprintf(fp, "%d %d %d %d %d\n", values[0], values[1],
295 values[2], values[3], values[4]);
6fdf969a 296
297 if (choices[i]->code == NULL)
298 {
299 /*
300 * This can happen with certain buggy PPD files that don't include
301 * a CustomPageSize command sequence... We just use a generic
302 * Level 2 command sequence...
303 */
304
ed6078a4 305 fputs(ppd_custom_code, fp);
6fdf969a 306 }
307 }
8c5f90ff 308 else if (fprintf(fp, "%%%%BeginFeature: *%s %s\n",
309 ((ppd_option_t *)choices[i]->option)->keyword,
310 choices[i]->choice) < 0)
311 {
312 free(choices);
313 return (-1);
314 }
6fdf969a 315
316 if (choices[i]->code != NULL && choices[i]->code[0] != '\0')
8c1333e2 317 {
d23a857a 318 if (fputs(choices[i]->code, fp) < 0)
58ec2a95 319 {
320 free(choices);
321 return (-1);
322 }
323
d23a857a 324 if (choices[i]->code[strlen(choices[i]->code) - 1] != '\n')
58ec2a95 325 putc('\n', fp);
8c1333e2 326 }
327
58ec2a95 328 if (fputs("%%EndFeature\n", fp) < 0)
8c1333e2 329 {
330 free(choices);
331 return (-1);
332 }
96831d7f 333
334 if (fputs("} stopped cleartomark\n", fp) < 0)
335 {
336 free(choices);
337 return (-1);
338 }
8c1333e2 339 }
d23a857a 340 else if (fputs(choices[i]->code, fp) < 0)
8c1333e2 341 {
342 free(choices);
343 return (-1);
344 }
345
346 free(choices);
2b85e375 347 return (0);
348}
349
350
351/*
352 * 'ppdEmitFd()' - Emit code for marked options to a file.
ea930040 353 */
354
2b85e375 355int /* O - 0 on success, -1 on failure */
356ppdEmitFd(ppd_file_t *ppd, /* I - PPD file record */
357 int fd, /* I - File to write to */
358 ppd_section_t section) /* I - Section to write */
359{
8c1333e2 360 int i, /* Looping var */
8c5f90ff 361 count, /* Number of choices */
362 custom_size; /* Non-zero if this option is a custom size */
8c1333e2 363 ppd_choice_t **choices; /* Choices */
ed6078a4 364 ppd_size_t *size; /* Custom page size */
8c1333e2 365 char buf[1024]; /* Output buffer for feature */
366
367
ed6078a4 368 /*
369 * Use PageSize or PageRegion as required...
370 */
371
372 ppd_handle_media(ppd);
373
374 /*
375 * Collect the options we need to emit and emit them!
376 */
377
cbcf8057 378 if ((count = ppdCollect(ppd, section, &choices)) == 0)
8c1333e2 379 return (0);
380
381 for (i = 0; i < count; i ++)
382 if (section != PPD_ORDER_EXIT && section != PPD_ORDER_JCL)
383 {
96831d7f 384 /*
385 * Send wrapper commands to prevent printer errors for unsupported
386 * options...
387 */
388
389 if (write(fd, "[{\n", 3) < 1)
390 {
391 free(choices);
392 return (-1);
393 }
394
8c1333e2 395 /*
396 * Send DSC comments with option...
397 */
398
8c5f90ff 399 if ((strcasecmp(((ppd_option_t *)choices[i]->option)->keyword, "PageSize") == 0 ||
400 strcasecmp(((ppd_option_t *)choices[i]->option)->keyword, "PageRegion") == 0) &&
401 strcasecmp(choices[i]->choice, "Custom") == 0)
402 {
403 custom_size = 1;
404
405 strcpy(buf, "%%BeginFeature: *CustomPageSize True\n");
406 }
407 else
408 {
409 custom_size = 0;
410
411 snprintf(buf, sizeof(buf), "%%%%BeginFeature: *%s %s\n",
412 ((ppd_option_t *)choices[i]->option)->keyword,
413 choices[i]->choice);
414 }
8c1333e2 415
416 if (write(fd, buf, strlen(buf)) < 1)
417 {
418 free(choices);
419 return (-1);
420 }
421
8c5f90ff 422 if (custom_size)
8c1333e2 423 {
ed6078a4 424 /*
8c5f90ff 425 * Variable size; write out standard size options, using the
426 * parameter positions defined in the PPD file...
ed6078a4 427 */
428
8c5f90ff 429 ppd_attr_t *attr; /* PPD attribute */
430 int pos, /* Position of custom value */
ae90e335 431 values[5], /* Values for custom command */
432 orientation; /* Orientation to use */
8c5f90ff 433
434
ed6078a4 435 size = ppdPageSize(ppd, "Custom");
8c5f90ff 436
437 memset(values, 0, sizeof(values));
438
439 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Width")) != NULL)
440 {
441 pos = atoi(attr->value) - 1;
442
443 if (pos < 0 || pos > 4)
444 pos = 0;
445 }
446 else
447 pos = 0;
448
449 values[pos] = (int)size->width;
450
451 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Height")) != NULL)
452 {
453 pos = atoi(attr->value) - 1;
454
455 if (pos < 0 || pos > 4)
456 pos = 1;
457 }
458 else
459 pos = 1;
460
461 values[pos] = (int)size->length;
462
ae90e335 463 if (size->width < size->length)
464 orientation = 1;
465 else
466 orientation = 0;
467
468 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize",
469 "Orientation")) != NULL)
470 {
471 int min_orient, max_orient; /* Minimum and maximum orientations */
472
473
474 if (sscanf(attr->value, "%d%*s%d%d", &pos, &min_orient,
475 &max_orient) != 3)
476 pos = 4;
477 else
478 {
5edf6841 479 pos --;
480
ae90e335 481 if (pos < 0 || pos > 4)
482 pos = 4;
483
484 if (orientation > max_orient)
485 orientation = max_orient;
486 else if (orientation < min_orient)
487 orientation = min_orient;
488 }
489 }
490 else
491 pos = 4;
492
493 values[pos] = orientation;
494
8c5f90ff 495 snprintf(buf, sizeof(buf), "%d %d %d %d %d\n", values[0], values[1],
496 values[2], values[3], values[4]);
ed6078a4 497
498 if (write(fd, buf, strlen(buf)) < 1)
499 {
500 free(choices);
501 return (-1);
502 }
503
504 if (choices[i]->code == NULL)
505 {
506 /*
507 * This can happen with certain buggy PPD files that don't include
508 * a CustomPageSize command sequence... We just use a generic
509 * Level 2 command sequence...
510 */
511
512 if (write(fd, ppd_custom_code, strlen(ppd_custom_code)) < 1)
513 {
514 free(choices);
515 return (-1);
516 }
517 }
518 }
519
520 if (choices[i]->code != NULL && choices[i]->code[0] != '\0')
521 {
522 if (write(fd, choices[i]->code, strlen(choices[i]->code)) < 1)
523 {
524 free(choices);
525 return (-1);
526 }
8c1333e2 527 }
528
529 if (write(fd, "%%EndFeature\n", 13) < 1)
530 {
531 free(choices);
532 return (-1);
533 }
96831d7f 534
535 if (write(fd, "} stopped cleartomark\n", 22) < 1)
536 {
537 free(choices);
538 return (-1);
539 }
8c1333e2 540 }
d23a857a 541 else if (write(fd, choices[i]->code, strlen(choices[i]->code)) < 1)
8c1333e2 542 {
543 free(choices);
544 return (-1);
545 }
546
547 free(choices);
2b85e375 548 return (0);
549}
ea930040 550
551
ee6a18b5 552/*
553 * 'ppdEmitJCL()' - Emit code for JCL options to a file.
554 */
555
556int /* O - 0 on success, -1 on failure */
557ppdEmitJCL(ppd_file_t *ppd, /* I - PPD file record */
558 FILE *fp, /* I - File to write to */
559 int job_id, /* I - Job ID */
560 const char *user, /* I - Username */
561 const char *title) /* I - Title */
562{
33b8a82d 563 char *ptr; /* Pointer into JCL string */
ae90e335 564 char temp[81]; /* Local title string */
ee6a18b5 565
566
b2e10895 567 /*
568 * Range check the input...
569 */
570
ee6a18b5 571 if (ppd == NULL || ppd->jcl_begin == NULL || ppd->jcl_ps == NULL)
572 return (0);
573
b2e10895 574 /*
575 * See if the printer supports HP PJL...
576 */
577
ee6a18b5 578 if (strncmp(ppd->jcl_begin, "\033%-12345X@", 10) == 0)
579 {
580 /*
581 * This printer uses HP PJL commands for output; filter the output
582 * so that we only have a single "@PJL JOB" command in the header...
ae90e335 583 *
584 * To avoid bugs in the PJL implementation of certain vendors' products
585 * (Xerox in particular), we add a dummy "@PJL" command at the beginning
586 * of the PJL commands to initialize PJL processing.
ee6a18b5 587 */
588
ae90e335 589 fputs("\033%-12345X@PJL\n", fp);
ee6a18b5 590 for (ptr = ppd->jcl_begin + 9; *ptr;)
591 if (strncmp(ptr, "@PJL JOB", 8) == 0)
592 {
593 /*
594 * Skip job command...
595 */
596
597 for (;*ptr; ptr ++)
598 if (*ptr == '\n')
599 break;
600
601 if (*ptr)
602 ptr ++;
603 }
604 else
605 {
606 /*
607 * Copy line...
608 */
609
610 for (;*ptr; ptr ++)
611 {
612 putc(*ptr, fp);
613 if (*ptr == '\n')
614 break;
615 }
616
617 if (*ptr)
618 ptr ++;
619 }
620
b2e10895 621 /*
622 * Eliminate any path info from the job title...
623 */
624
625 if ((ptr = strrchr(title, '/')) != NULL)
626 title = ptr + 1;
627
ae90e335 628 /*
629 * Replace double quotes with single quotes so that the title
630 * does not cause a PJL syntax error.
631 */
632
633 strlcpy(temp, title, sizeof(temp));
634
635 for (ptr = temp; *ptr; ptr ++)
636 if (*ptr == '\"')
637 *ptr = '\'';
638
ee6a18b5 639 /*
640 * Send PJL JOB command before we enter PostScript mode...
641 */
642
ae90e335 643 fprintf(fp, "@PJL JOB NAME = \"%s\" DISPLAY = \"%d %s %s\"\n", temp,
644 job_id, user, temp);
ee6a18b5 645 }
646 else
af50faa7 647 fputs(ppd->jcl_begin, fp);
ee6a18b5 648
af50faa7 649 ppdEmit(ppd, fp, PPD_ORDER_JCL);
650 fputs(ppd->jcl_ps, fp);
37ac5c5e 651
652 return (0);
ee6a18b5 653}
654
655
ed6078a4 656/*
657 * 'ppd_handle_media()' - Handle media selection...
658 */
659
660static void
661ppd_handle_media(ppd_file_t *ppd)
662{
663 ppd_choice_t *manual_feed, /* ManualFeed choice, if any */
b3291c5a 664 *input_slot, /* InputSlot choice, if any */
665 *page; /* PageSize/PageRegion */
ed6078a4 666 ppd_size_t *size; /* Current media size */
1b98ab76 667 ppd_attr_t *rpr; /* RequiresPageRegion value */
ed6078a4 668
669
670 /*
671 * This function determines if the user has selected a media source
672 * via the InputSlot or ManualFeed options; if so, it marks the
673 * PageRegion option corresponding to the current media size.
674 * Otherwise it marks the PageSize option.
675 */
676
677 if ((size = ppdPageSize(ppd, NULL)) == NULL)
678 return;
679
680 manual_feed = ppdFindMarkedChoice(ppd, "ManualFeed");
681 input_slot = ppdFindMarkedChoice(ppd, "InputSlot");
682
b3291c5a 683 if (input_slot != NULL)
684 rpr = ppdFindAttr(ppd, "RequiresPageRegion", input_slot->choice);
685 else
686 rpr = NULL;
687
688 if (!rpr)
689 rpr = ppdFindAttr(ppd, "RequiresPageRegion", "All");
690
ed6078a4 691 if (strcasecmp(size->name, "Custom") == 0 ||
692 (manual_feed == NULL && input_slot == NULL) ||
693 (manual_feed != NULL && strcasecmp(manual_feed->choice, "False") == 0) ||
694 (input_slot != NULL && (input_slot->code == NULL || !input_slot->code[0])))
695 {
696 /*
697 * Manual feed was not selected and/or the input slot selection does
698 * not contain any PostScript code. Use the PageSize option...
699 */
700
701 ppdMarkOption(ppd, "PageSize", size->name);
702 }
703 else
704 {
705 /*
706 * Manual feed was selected and/or the input slot selection contains
707 * PostScript code. Use the PageRegion option...
708 */
709
710 ppdMarkOption(ppd, "PageRegion", size->name);
b3291c5a 711
6f83172d 712 /*
713 * RequiresPageRegion does not apply to manual feed so we need to
714 * check that we are not doing manual feed before unmarking PageRegion.
715 */
716
717 if (!(manual_feed && !strcasecmp(manual_feed->choice, "True")) &&
718 ((rpr && rpr->value && !strcmp(rpr->value, "False")) ||
719 (!rpr && !ppd->num_filters)))
b3291c5a 720 {
721 /*
722 * Either the PPD file specifies no PageRegion code or the PPD file
723 * not for a CUPS raster driver and thus defaults to no PageRegion
724 * code... Unmark the PageRegion choice so that we don't output the
725 * code...
726 */
727
728 page = ppdFindMarkedChoice(ppd, "PageRegion");
729
730 if (page)
731 page->marked = 0;
732 }
ed6078a4 733 }
734}
735
736
ea930040 737/*
8c1333e2 738 * 'ppd_sort()' - Sort options by ordering numbers...
739 */
740
741static int /* O - -1 if c1 < c2, 0 if equal, 1 otherwise */
742ppd_sort(ppd_choice_t **c1, /* I - First choice */
743 ppd_choice_t **c2) /* I - Second choice */
744{
745 if (((ppd_option_t *)(*c1)->option)->order < ((ppd_option_t *)(*c2)->option)->order)
746 return (-1);
747 else if (((ppd_option_t *)(*c1)->option)->order > ((ppd_option_t *)(*c2)->option)->order)
748 return (1);
749 else
750 return (0);
751}
752
753
754/*
b2e10895 755 * End of "$Id$".
ea930040 756 */