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