]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/ppd-emit.c
Move debug printfs to internal usage only.
[thirdparty/cups.git] / cups / ppd-emit.c
CommitLineData
ef416fc2 1/*
7e86f2f6 2 * PPD code emission routines for CUPS.
ef416fc2 3 *
f787e1e3 4 * Copyright 2007-2015 by Apple Inc.
7e86f2f6 5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
ef416fc2 6 *
4b042bf6
MS
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
ef416fc2 9 *
7e86f2f6 10 * PostScript is a trademark of Adobe Systems, Inc.
ef416fc2 11 */
12
13/*
14 * Include necessary headers...
15 */
16
71e16022 17#include "cups-private.h"
fb863569 18#include "debug-internal.h"
f787e1e3 19#include "ppd.h"
19dc16f7 20#if defined(_WIN32) || defined(__EMX__)
ef416fc2 21# include <io.h>
22#else
23# include <unistd.h>
19dc16f7 24#endif /* _WIN32 || __EMX__ */
ef416fc2 25
26
27/*
28 * Local functions...
29 */
30
ee571f26 31static int ppd_compare_cparams(ppd_cparam_t *a, ppd_cparam_t *b);
ef416fc2 32static void ppd_handle_media(ppd_file_t *ppd);
ef416fc2 33
34
35/*
36 * Local globals...
37 */
38
39static const char ppd_custom_code[] =
40 "pop pop pop\n"
41 "<</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice\n";
42
43
44/*
45 * 'ppdCollect()' - Collect all marked options that reside in the specified
46 * section.
5a738aea
MS
47 *
48 * The choices array should be freed using @code free@ when you are
49 * finished with it.
ef416fc2 50 */
51
52int /* O - Number of options marked */
53ppdCollect(ppd_file_t *ppd, /* I - PPD file data */
54 ppd_section_t section, /* I - Section to collect */
55 ppd_choice_t ***choices) /* O - Pointers to choices */
fa73b229 56{
57 return (ppdCollect2(ppd, section, 0.0, choices));
58}
59
60
61/*
62 * 'ppdCollect2()' - Collect all marked options that reside in the
63 * specified section and minimum order.
64 *
5a738aea
MS
65 * The choices array should be freed using @code free@ when you are
66 * finished with it.
67 *
8072030b 68 * @since CUPS 1.2/macOS 10.5@
fa73b229 69 */
70
71int /* O - Number of options marked */
72ppdCollect2(ppd_file_t *ppd, /* I - PPD file data */
73 ppd_section_t section, /* I - Section to collect */
74 float min_order, /* I - Minimum OrderDependency value */
75 ppd_choice_t ***choices) /* O - Pointers to choices */
ef416fc2 76{
ef416fc2 77 ppd_choice_t *c; /* Current choice */
0a682745
MS
78 ppd_section_t csection; /* Current section */
79 float corder; /* Current OrderDependency value */
ef416fc2 80 int count; /* Number of choices collected */
81 ppd_choice_t **collect; /* Collected choices */
0a682745 82 float *orders; /* Collected order values */
ef416fc2 83
84
e07d4801 85 DEBUG_printf(("ppdCollect2(ppd=%p, section=%d, min_order=%f, choices=%p)",
8ca02f3c 86 ppd, section, min_order, choices));
87
91c84a35
MS
88 if (!ppd || !choices)
89 {
90 if (choices)
91 *choices = NULL;
92
ef416fc2 93 return (0);
91c84a35 94 }
ef416fc2 95
96 /*
0a682745 97 * Allocate memory for up to N selected choices...
ef416fc2 98 */
99
91c84a35
MS
100 count = 0;
101 if ((collect = calloc(sizeof(ppd_choice_t *),
7e86f2f6 102 (size_t)cupsArrayCount(ppd->marked))) == NULL)
91c84a35
MS
103 {
104 *choices = NULL;
105 return (0);
106 }
107
7e86f2f6 108 if ((orders = calloc(sizeof(float), (size_t)cupsArrayCount(ppd->marked))) == NULL)
91c84a35
MS
109 {
110 *choices = NULL;
111 free(collect);
112 return (0);
113 }
ef416fc2 114
115 /*
116 * Loop through all options and add choices as needed...
117 */
118
0a682745
MS
119 for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
120 c;
121 c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
ef416fc2 122 {
0a682745
MS
123 csection = c->option->section;
124 corder = c->option->order;
125
126 if (!strcmp(c->choice, "Custom"))
127 {
128 ppd_attr_t *attr; /* NonUIOrderDependency value */
129 float aorder; /* Order value */
130 char asection[17], /* Section name */
131 amain[PPD_MAX_NAME + 1],
132 aoption[PPD_MAX_NAME];
133 /* *CustomFoo and True */
134
135
136 for (attr = ppdFindAttr(ppd, "NonUIOrderDependency", NULL);
137 attr;
138 attr = ppdFindNextAttr(ppd, "NonUIOrderDependency", NULL))
139 if (attr->value &&
140 sscanf(attr->value, "%f%16s%41s%40s", &aorder, asection, amain,
141 aoption) == 4 &&
142 !strncmp(amain, "*Custom", 7) &&
143 !strcmp(amain + 7, c->option->keyword) && !strcmp(aoption, "True"))
144 {
145 /*
146 * Use this NonUIOrderDependency...
147 */
ef416fc2 148
0a682745
MS
149 corder = aorder;
150
151 if (!strcmp(asection, "DocumentSetup"))
152 csection = PPD_ORDER_DOCUMENT;
153 else if (!strcmp(asection, "ExitServer"))
154 csection = PPD_ORDER_EXIT;
155 else if (!strcmp(asection, "JCLSetup"))
156 csection = PPD_ORDER_JCL;
157 else if (!strcmp(asection, "PageSetup"))
158 csection = PPD_ORDER_PAGE;
159 else if (!strcmp(asection, "Prolog"))
160 csection = PPD_ORDER_PROLOG;
161 else
162 csection = PPD_ORDER_ANY;
163
164 break;
165 }
166 }
167
168 if (csection == section && corder >= min_order)
169 {
170 collect[count] = c;
171 orders[count] = corder;
172 count ++;
173 }
ef416fc2 174 }
175
176 /*
177 * If we have more than 1 marked choice, sort them...
178 */
179
180 if (count > 1)
0a682745
MS
181 {
182 int i, j; /* Looping vars */
183
184 for (i = 0; i < (count - 1); i ++)
185 for (j = i + 1; j < count; j ++)
186 if (orders[i] > orders[j])
187 {
188 c = collect[i];
189 corder = orders[i];
190 collect[i] = collect[j];
191 orders[i] = orders[j];
192 collect[j] = c;
193 orders[j] = corder;
194 }
195 }
196
197 free(orders);
ef416fc2 198
e07d4801 199 DEBUG_printf(("2ppdCollect2: %d marked choices...", count));
8ca02f3c 200
ef416fc2 201 /*
202 * Return the array and number of choices; if 0, free the array since
203 * it isn't needed.
204 */
205
206 if (count > 0)
207 {
208 *choices = collect;
209 return (count);
210 }
211 else
212 {
213 *choices = NULL;
214 free(collect);
215 return (0);
216 }
217}
218
219
220/*
221 * 'ppdEmit()' - Emit code for marked options to a file.
222 */
223
224int /* O - 0 on success, -1 on failure */
225ppdEmit(ppd_file_t *ppd, /* I - PPD file record */
226 FILE *fp, /* I - File to write to */
227 ppd_section_t section) /* I - Section to write */
fa73b229 228{
229 return (ppdEmitAfterOrder(ppd, fp, section, 0, 0.0));
230}
231
232
233/*
234 * 'ppdEmitAfterOrder()' - Emit a subset of the code for marked options to a file.
235 *
236 * When "limit" is non-zero, this function only emits options whose
237 * OrderDependency value is greater than or equal to "min_order".
238 *
239 * When "limit" is zero, this function is identical to ppdEmit().
240 *
8072030b 241 * @since CUPS 1.2/macOS 10.5@
fa73b229 242 */
243
244int /* O - 0 on success, -1 on failure */
245ppdEmitAfterOrder(
246 ppd_file_t *ppd, /* I - PPD file record */
247 FILE *fp, /* I - File to write to */
248 ppd_section_t section, /* I - Section to write */
757d2cad 249 int limit, /* I - Non-zero to use min_order */
250 float min_order) /* I - Lowest OrderDependency */
ef416fc2 251{
757d2cad 252 char *buffer; /* Option code */
253 int status; /* Return status */
ef416fc2 254
255
256 /*
757d2cad 257 * Range check input...
ef416fc2 258 */
259
757d2cad 260 if (!ppd || !fp)
261 return (-1);
ef416fc2 262
263 /*
757d2cad 264 * Get the string...
ef416fc2 265 */
266
83e08001 267 buffer = ppdEmitString(ppd, section, limit ? min_order : 0.0f);
ef416fc2 268
757d2cad 269 /*
270 * Write it as needed and return...
271 */
ef416fc2 272
757d2cad 273 if (buffer)
274 {
275 status = fputs(buffer, fp) < 0 ? -1 : 0;
ef416fc2 276
757d2cad 277 free(buffer);
278 }
279 else
280 status = 0;
ef416fc2 281
757d2cad 282 return (status);
ef416fc2 283}
284
285
286/*
287 * 'ppdEmitFd()' - Emit code for marked options to a file.
288 */
289
290int /* O - 0 on success, -1 on failure */
291ppdEmitFd(ppd_file_t *ppd, /* I - PPD file record */
292 int fd, /* I - File to write to */
293 ppd_section_t section) /* I - Section to write */
294{
757d2cad 295 char *buffer, /* Option code */
296 *bufptr; /* Pointer into code */
297 size_t buflength; /* Length of option code */
298 ssize_t bytes; /* Bytes written */
299 int status; /* Return status */
ef416fc2 300
301
302 /*
757d2cad 303 * Range check input...
ef416fc2 304 */
305
757d2cad 306 if (!ppd || fd < 0)
307 return (-1);
ef416fc2 308
309 /*
757d2cad 310 * Get the string...
ef416fc2 311 */
312
757d2cad 313 buffer = ppdEmitString(ppd, section, 0.0);
ef416fc2 314
757d2cad 315 /*
316 * Write it as needed and return...
317 */
ef416fc2 318
757d2cad 319 if (buffer)
320 {
321 buflength = strlen(buffer);
322 bufptr = buffer;
323 bytes = 0;
ef416fc2 324
757d2cad 325 while (buflength > 0)
326 {
19dc16f7 327#ifdef _WIN32
b86bc4cf 328 if ((bytes = (ssize_t)write(fd, bufptr, (unsigned)buflength)) < 0)
329#else
757d2cad 330 if ((bytes = write(fd, bufptr, buflength)) < 0)
19dc16f7 331#endif /* _WIN32 */
ef416fc2 332 {
757d2cad 333 if (errno == EAGAIN || errno == EINTR)
334 continue;
ef416fc2 335
757d2cad 336 break;
ef416fc2 337 }
757d2cad 338
7e86f2f6 339 buflength -= (size_t)bytes;
757d2cad 340 bufptr += bytes;
ef416fc2 341 }
342
757d2cad 343 status = bytes < 0 ? -1 : 0;
344
345 free(buffer);
346 }
347 else
348 status = 0;
349
350 return (status);
ef416fc2 351}
352
353
354/*
355 * 'ppdEmitJCL()' - Emit code for JCL options to a file.
356 */
357
358int /* O - 0 on success, -1 on failure */
359ppdEmitJCL(ppd_file_t *ppd, /* I - PPD file record */
360 FILE *fp, /* I - File to write to */
361 int job_id, /* I - Job ID */
362 const char *user, /* I - Username */
363 const char *title) /* I - Title */
364{
365 char *ptr; /* Pointer into JCL string */
8b116e60
MS
366 char temp[65], /* Local title string */
367 displaymsg[33]; /* Local display string */
ef416fc2 368
369
370 /*
371 * Range check the input...
372 */
373
757d2cad 374 if (!ppd || !ppd->jcl_begin || !ppd->jcl_ps)
ef416fc2 375 return (0);
376
377 /*
378 * See if the printer supports HP PJL...
379 */
380
381 if (!strncmp(ppd->jcl_begin, "\033%-12345X@", 10))
382 {
383 /*
384 * This printer uses HP PJL commands for output; filter the output
385 * so that we only have a single "@PJL JOB" command in the header...
386 *
387 * To avoid bugs in the PJL implementation of certain vendors' products
388 * (Xerox in particular), we add a dummy "@PJL" command at the beginning
389 * of the PJL commands to initialize PJL processing.
390 */
391
f7deaa1a 392 ppd_attr_t *charset; /* PJL charset */
634763e8 393 ppd_attr_t *display; /* PJL display command */
f7deaa1a 394
395
396 if ((charset = ppdFindAttr(ppd, "cupsPJLCharset", NULL)) != NULL)
397 {
88f9aafc 398 if (!charset->value || _cups_strcasecmp(charset->value, "UTF-8"))
f7deaa1a 399 charset = NULL;
400 }
401
634763e8
MS
402 if ((display = ppdFindAttr(ppd, "cupsPJLDisplay", NULL)) != NULL)
403 {
404 if (!display->value)
405 display = NULL;
406 }
407
ef416fc2 408 fputs("\033%-12345X@PJL\n", fp);
409 for (ptr = ppd->jcl_begin + 9; *ptr;)
410 if (!strncmp(ptr, "@PJL JOB", 8))
411 {
412 /*
413 * Skip job command...
414 */
415
416 for (;*ptr; ptr ++)
417 if (*ptr == '\n')
418 break;
419
420 if (*ptr)
421 ptr ++;
422 }
423 else
424 {
425 /*
426 * Copy line...
427 */
428
429 for (;*ptr; ptr ++)
430 {
431 putc(*ptr, fp);
432 if (*ptr == '\n')
433 break;
434 }
435
436 if (*ptr)
437 ptr ++;
438 }
439
440 /*
8b116e60 441 * Clean up the job title...
ef416fc2 442 */
443
444 if ((ptr = strrchr(title, '/')) != NULL)
8b116e60
MS
445 {
446 /*
447 * Only show basename of file path...
448 */
449
ef416fc2 450 title = ptr + 1;
8b116e60
MS
451 }
452
453 if (!strncmp(title, "smbprn.", 7))
454 {
455 /*
456 * Skip leading smbprn.######## from Samba jobs...
457 */
458
459 for (title += 7; *title && isdigit(*title & 255); title ++);
7cf5915e
MS
460 while (_cups_isspace(*title))
461 title ++;
8b116e60
MS
462
463 if ((ptr = strstr(title, " - ")) != NULL)
464 {
465 /*
466 * Skip application name in "Some Application - Title of job"...
467 */
468
469 title = ptr + 3;
470 }
471 }
ef416fc2 472
473 /*
8b116e60 474 * Replace double quotes with single quotes and UTF-8 characters with
07725fee 475 * question marks so that the title does not cause a PJL syntax error.
ef416fc2 476 */
477
478 strlcpy(temp, title, sizeof(temp));
479
480 for (ptr = temp; *ptr; ptr ++)
481 if (*ptr == '\"')
482 *ptr = '\'';
8b116e60 483 else if (!charset && (*ptr & 128))
07725fee 484 *ptr = '?';
ef416fc2 485
8b116e60
MS
486 /*
487 * CUPS STR #3125: Long PJL JOB NAME causes problems with some printers
488 *
489 * Generate the display message, truncating at 32 characters + nul to avoid
490 * issues with some printer's PJL implementations...
491 */
492
493 snprintf(displaymsg, sizeof(displaymsg), "%d %s %s", job_id, user, temp);
494
ef416fc2 495 /*
496 * Send PJL JOB and PJL RDYMSG commands before we enter PostScript mode...
497 */
498
634763e8 499 if (display && strcmp(display->value, "job"))
634763e8 500 fprintf(fp, "@PJL JOB NAME = \"%s\"\n", temp);
3e7fe0ca
MS
501 else if (display && !strcmp(display->value, "rdymsg"))
502 fprintf(fp, "@PJL RDYMSG DISPLAY = \"%s\"\n", displaymsg);
634763e8 503 else
8b116e60
MS
504 fprintf(fp, "@PJL JOB NAME = \"%s\" DISPLAY = \"%s\"\n", temp,
505 displaymsg);
3e7fe0ca
MS
506
507 /*
508 * Replace double quotes with single quotes and UTF-8 characters with
509 * question marks so that the user does not cause a PJL syntax error.
510 */
511
512 strlcpy(temp, user, sizeof(temp));
513
514 for (ptr = temp; *ptr; ptr ++)
515 if (*ptr == '\"')
516 *ptr = '\'';
517 else if (!charset && (*ptr & 128))
518 *ptr = '?';
519
520 fprintf(fp, "@PJL SET USERNAME = \"%s\"\n", temp);
ef416fc2 521 }
522 else
523 fputs(ppd->jcl_begin, fp);
524
525 ppdEmit(ppd, fp, PPD_ORDER_JCL);
526 fputs(ppd->jcl_ps, fp);
527
528 return (0);
529}
530
531
532/*
533 * 'ppdEmitJCLEnd()' - Emit JCLEnd code to a file.
534 *
8072030b 535 * @since CUPS 1.2/macOS 10.5@
ef416fc2 536 */
537
538int /* O - 0 on success, -1 on failure */
539ppdEmitJCLEnd(ppd_file_t *ppd, /* I - PPD file record */
540 FILE *fp) /* I - File to write to */
541{
ef416fc2 542 /*
543 * Range check the input...
544 */
545
757d2cad 546 if (!ppd)
ef416fc2 547 return (0);
548
757d2cad 549 if (!ppd->jcl_end)
ef416fc2 550 {
551 if (ppd->num_filters == 0)
757d2cad 552 putc(0x04, fp);
ef416fc2 553
554 return (0);
555 }
556
557 /*
558 * See if the printer supports HP PJL...
559 */
560
561 if (!strncmp(ppd->jcl_end, "\033%-12345X@", 10))
562 {
563 /*
564 * This printer uses HP PJL commands for output; filter the output
565 * so that we only have a single "@PJL JOB" command in the header...
566 *
567 * To avoid bugs in the PJL implementation of certain vendors' products
568 * (Xerox in particular), we add a dummy "@PJL" command at the beginning
569 * of the PJL commands to initialize PJL processing.
570 */
571
572 fputs("\033%-12345X@PJL\n", fp);
06d4e77b 573 fputs("@PJL RDYMSG DISPLAY = \"\"\n", fp);
ef416fc2 574 fputs(ppd->jcl_end + 9, fp);
575 }
576 else
577 fputs(ppd->jcl_end, fp);
578
579 return (0);
580}
581
582
757d2cad 583/*
584 * 'ppdEmitString()' - Get a string containing the code for marked options.
585 *
586 * When "min_order" is greater than zero, this function only includes options
587 * whose OrderDependency value is greater than or equal to "min_order".
588 * Otherwise, all options in the specified section are included in the
589 * returned string.
590 *
591 * The return string is allocated on the heap and should be freed using
5a738aea 592 * @code free@ when you are done with it.
757d2cad 593 *
8072030b 594 * @since CUPS 1.2/macOS 10.5@
757d2cad 595 */
596
5a738aea 597char * /* O - String containing option code or @code NULL@ if there is no option code */
757d2cad 598ppdEmitString(ppd_file_t *ppd, /* I - PPD file record */
599 ppd_section_t section, /* I - Section to write */
600 float min_order) /* I - Lowest OrderDependency */
601{
602 int i, j, /* Looping vars */
603 count; /* Number of choices */
604 ppd_choice_t **choices; /* Choices */
605 ppd_size_t *size; /* Custom page size */
606 ppd_coption_t *coption; /* Custom option */
607 ppd_cparam_t *cparam; /* Custom parameter */
608 size_t bufsize; /* Size of string buffer needed */
609 char *buffer, /* String buffer */
610 *bufptr, /* Pointer into buffer */
611 *bufend; /* End of buffer */
612 struct lconv *loc; /* Locale data */
613
614
e07d4801 615 DEBUG_printf(("ppdEmitString(ppd=%p, section=%d, min_order=%f)",
8ca02f3c 616 ppd, section, min_order));
617
757d2cad 618 /*
619 * Range check input...
620 */
621
622 if (!ppd)
623 return (NULL);
624
625 /*
626 * Use PageSize or PageRegion as required...
627 */
628
629 ppd_handle_media(ppd);
630
631 /*
632 * Collect the options we need to emit...
633 */
634
635 if ((count = ppdCollect2(ppd, section, min_order, &choices)) == 0)
636 return (NULL);
637
638 /*
639 * Count the number of bytes that are required to hold all of the
640 * option code...
641 */
642
643 for (i = 0, bufsize = 1; i < count; i ++)
644 {
005dd1eb
MS
645 if (section == PPD_ORDER_JCL)
646 {
88f9aafc 647 if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
005dd1eb
MS
648 (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
649 != NULL)
650 {
651 /*
652 * Add space to account for custom parameter substitution...
653 */
654
655 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
656 cparam;
657 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
658 {
659 switch (cparam->type)
660 {
661 case PPD_CUSTOM_CURVE :
662 case PPD_CUSTOM_INVCURVE :
663 case PPD_CUSTOM_POINTS :
664 case PPD_CUSTOM_REAL :
665 case PPD_CUSTOM_INT :
666 bufsize += 10;
667 break;
668
669 case PPD_CUSTOM_PASSCODE :
670 case PPD_CUSTOM_PASSWORD :
671 case PPD_CUSTOM_STRING :
4220952d
MS
672 if (cparam->current.custom_string)
673 bufsize += strlen(cparam->current.custom_string);
005dd1eb
MS
674 break;
675 }
676 }
677 }
678 }
679 else if (section != PPD_ORDER_EXIT)
757d2cad 680 {
681 bufsize += 3; /* [{\n */
682
88f9aafc
MS
683 if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") ||
684 !_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
685 !_cups_strcasecmp(choices[i]->choice, "Custom"))
757d2cad 686 {
e07d4801 687 DEBUG_puts("2ppdEmitString: Custom size set!");
8ca02f3c 688
e1d6a774 689 bufsize += 37; /* %%BeginFeature: *CustomPageSize True\n */
757d2cad 690 bufsize += 50; /* Five 9-digit numbers + newline */
691 }
88f9aafc 692 else if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
757d2cad 693 (coption = ppdFindCustomOption(ppd,
694 choices[i]->option->keyword))
695 != NULL)
696 {
238c3832
MS
697 bufsize += 23 + strlen(choices[i]->option->keyword) + 6;
698 /* %%BeginFeature: *Customkeyword True\n */
757d2cad 699
88f9aafc 700
757d2cad 701 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
702 cparam;
703 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
704 {
705 switch (cparam->type)
706 {
707 case PPD_CUSTOM_CURVE :
708 case PPD_CUSTOM_INVCURVE :
709 case PPD_CUSTOM_POINTS :
710 case PPD_CUSTOM_REAL :
711 case PPD_CUSTOM_INT :
712 bufsize += 10;
713 break;
714
715 case PPD_CUSTOM_PASSCODE :
716 case PPD_CUSTOM_PASSWORD :
717 case PPD_CUSTOM_STRING :
4220952d
MS
718 bufsize += 3;
719 if (cparam->current.custom_string)
720 bufsize += 4 * strlen(cparam->current.custom_string);
757d2cad 721 break;
722 }
723 }
724 }
725 else
e1d6a774 726 bufsize += 17 + strlen(choices[i]->option->keyword) + 1 +
727 strlen(choices[i]->choice) + 1;
728 /* %%BeginFeature: *keyword choice\n */
757d2cad 729
730 bufsize += 13; /* %%EndFeature\n */
731 bufsize += 22; /* } stopped cleartomark\n */
732 }
733
734 if (choices[i]->code)
e1d6a774 735 bufsize += strlen(choices[i]->code) + 1;
757d2cad 736 else
737 bufsize += strlen(ppd_custom_code);
738 }
739
740 /*
741 * Allocate memory...
742 */
743
e07d4801 744 DEBUG_printf(("2ppdEmitString: Allocating %d bytes for string...",
ae71f5de 745 (int)bufsize));
8ca02f3c 746
757d2cad 747 if ((buffer = calloc(1, bufsize)) == NULL)
748 {
749 free(choices);
750 return (NULL);
751 }
752
753 bufend = buffer + bufsize - 1;
754 loc = localeconv();
755
756 /*
757 * Copy the option code to the buffer...
758 */
759
760 for (i = 0, bufptr = buffer; i < count; i ++, bufptr += strlen(bufptr))
005dd1eb
MS
761 if (section == PPD_ORDER_JCL)
762 {
88f9aafc 763 if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
41681883 764 choices[i]->code &&
005dd1eb
MS
765 (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
766 != NULL)
767 {
768 /*
769 * Handle substitutions in custom JCL options...
770 */
771
772 char *cptr; /* Pointer into code */
773 int pnum; /* Parameter number */
774
775
776 for (cptr = choices[i]->code; *cptr && bufptr < bufend;)
777 {
778 if (*cptr == '\\')
779 {
780 cptr ++;
781
782 if (isdigit(*cptr & 255))
783 {
784 /*
785 * Substitute parameter...
786 */
787
788 pnum = *cptr++ - '0';
4220952d
MS
789 while (isdigit(*cptr & 255))
790 pnum = pnum * 10 + *cptr++ - '0';
005dd1eb
MS
791
792 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
793 cparam;
794 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
795 if (cparam->order == pnum)
796 break;
797
798 if (cparam)
799 {
800 switch (cparam->type)
801 {
802 case PPD_CUSTOM_CURVE :
803 case PPD_CUSTOM_INVCURVE :
804 case PPD_CUSTOM_POINTS :
805 case PPD_CUSTOM_REAL :
806 bufptr = _cupsStrFormatd(bufptr, bufend,
807 cparam->current.custom_real,
808 loc);
809 break;
810
811 case PPD_CUSTOM_INT :
07623986 812 snprintf(bufptr, (size_t)(bufend - bufptr), "%d", cparam->current.custom_int);
005dd1eb
MS
813 bufptr += strlen(bufptr);
814 break;
815
816 case PPD_CUSTOM_PASSCODE :
817 case PPD_CUSTOM_PASSWORD :
818 case PPD_CUSTOM_STRING :
4220952d
MS
819 if (cparam->current.custom_string)
820 {
07623986 821 strlcpy(bufptr, cparam->current.custom_string, (size_t)(bufend - bufptr));
4220952d
MS
822 bufptr += strlen(bufptr);
823 }
005dd1eb
MS
824 break;
825 }
826 }
827 }
828 else if (*cptr)
829 *bufptr++ = *cptr++;
830 }
831 else
832 *bufptr++ = *cptr++;
833 }
834 }
835 else
836 {
837 /*
838 * Otherwise just copy the option code directly...
839 */
840
07623986 841 strlcpy(bufptr, choices[i]->code, (size_t)(bufend - bufptr + 1));
005dd1eb
MS
842 bufptr += strlen(bufptr);
843 }
844 }
845 else if (section != PPD_ORDER_EXIT)
757d2cad 846 {
847 /*
848 * Add wrapper commands to prevent printer errors for unsupported
849 * options...
850 */
851
07623986 852 strlcpy(bufptr, "[{\n", (size_t)(bufend - bufptr + 1));
757d2cad 853 bufptr += 3;
854
855 /*
856 * Send DSC comments with option...
857 */
858
e07d4801
MS
859 DEBUG_printf(("2ppdEmitString: Adding code for %s=%s...",
860 choices[i]->option->keyword, choices[i]->choice));
8ca02f3c 861
88f9aafc
MS
862 if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") ||
863 !_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
864 !_cups_strcasecmp(choices[i]->choice, "Custom"))
757d2cad 865 {
866 /*
867 * Variable size; write out standard size options, using the
868 * parameter positions defined in the PPD file...
869 */
870
871 ppd_attr_t *attr; /* PPD attribute */
872 int pos, /* Position of custom value */
873 orientation; /* Orientation to use */
874 float values[5]; /* Values for custom command */
875
876
07623986 877 strlcpy(bufptr, "%%BeginFeature: *CustomPageSize True\n", (size_t)(bufend - bufptr + 1));
757d2cad 878 bufptr += 37;
879
880 size = ppdPageSize(ppd, "Custom");
881
882 memset(values, 0, sizeof(values));
883
884 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Width")) != NULL)
885 {
886 pos = atoi(attr->value) - 1;
887
888 if (pos < 0 || pos > 4)
889 pos = 0;
890 }
891 else
892 pos = 0;
893
894 values[pos] = size->width;
895
896 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Height")) != NULL)
897 {
898 pos = atoi(attr->value) - 1;
899
900 if (pos < 0 || pos > 4)
901 pos = 1;
902 }
903 else
904 pos = 1;
905
906 values[pos] = size->length;
907
908 /*
909 * According to the Adobe PPD specification, an orientation of 1
910 * will produce a print that comes out upside-down with the X
911 * axis perpendicular to the direction of feed, which is exactly
912 * what we want to be consistent with non-PS printers.
913 *
914 * We could also use an orientation of 3 to produce output that
915 * comes out rightside-up (this is the default for many large format
916 * printer PPDs), however for consistency we will stick with the
917 * value 1.
918 *
919 * If we wanted to get fancy, we could use orientations of 0 or
920 * 2 and swap the width and length, however we don't want to get
921 * fancy, we just want it to work consistently.
922 *
923 * The orientation value is range limited by the Orientation
924 * parameter definition, so certain non-PS printer drivers that
925 * only support an Orientation of 0 will get the value 0 as
926 * expected.
927 */
928
929 orientation = 1;
930
931 if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize",
932 "Orientation")) != NULL)
933 {
934 int min_orient, max_orient; /* Minimum and maximum orientations */
935
936
937 if (sscanf(attr->value, "%d%*s%d%d", &pos, &min_orient,
938 &max_orient) != 3)
939 pos = 4;
940 else
941 {
942 pos --;
943
944 if (pos < 0 || pos > 4)
945 pos = 4;
946
947 if (orientation > max_orient)
948 orientation = max_orient;
949 else if (orientation < min_orient)
950 orientation = min_orient;
951 }
952 }
953 else
954 pos = 4;
955
b86bc4cf 956 values[pos] = (float)orientation;
757d2cad 957
958 for (pos = 0; pos < 5; pos ++)
959 {
960 bufptr = _cupsStrFormatd(bufptr, bufend, values[pos], loc);
961 *bufptr++ = '\n';
962 }
963
964 if (!choices[i]->code)
965 {
966 /*
967 * This can happen with certain buggy PPD files that don't include
968 * a CustomPageSize command sequence... We just use a generic
969 * Level 2 command sequence...
970 */
971
07623986 972 strlcpy(bufptr, ppd_custom_code, (size_t)(bufend - bufptr + 1));
757d2cad 973 bufptr += strlen(bufptr);
974 }
975 }
88f9aafc 976 else if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
005dd1eb 977 (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
757d2cad 978 != NULL)
979 {
980 /*
981 * Custom option...
982 */
983
984 const char *s; /* Pointer into string value */
ee571f26 985 cups_array_t *params; /* Parameters in the correct output order */
757d2cad 986
987
ee571f26
MS
988 params = cupsArrayNew((cups_array_func_t)ppd_compare_cparams, NULL);
989
990 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
991 cparam;
992 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
993 cupsArrayAdd(params, cparam);
994
07623986 995 snprintf(bufptr, (size_t)(bufend - bufptr + 1), "%%%%BeginFeature: *Custom%s True\n", coption->keyword);
757d2cad 996 bufptr += strlen(bufptr);
997
ee571f26 998 for (cparam = (ppd_cparam_t *)cupsArrayFirst(params);
757d2cad 999 cparam;
ee571f26 1000 cparam = (ppd_cparam_t *)cupsArrayNext(params))
757d2cad 1001 {
1002 switch (cparam->type)
1003 {
1004 case PPD_CUSTOM_CURVE :
1005 case PPD_CUSTOM_INVCURVE :
1006 case PPD_CUSTOM_POINTS :
1007 case PPD_CUSTOM_REAL :
1008 bufptr = _cupsStrFormatd(bufptr, bufend,
1009 cparam->current.custom_real, loc);
1010 *bufptr++ = '\n';
1011 break;
1012
1013 case PPD_CUSTOM_INT :
07623986 1014 snprintf(bufptr, (size_t)(bufend - bufptr + 1), "%d\n", cparam->current.custom_int);
757d2cad 1015 bufptr += strlen(bufptr);
1016 break;
1017
1018 case PPD_CUSTOM_PASSCODE :
1019 case PPD_CUSTOM_PASSWORD :
1020 case PPD_CUSTOM_STRING :
1021 *bufptr++ = '(';
1022
4220952d
MS
1023 if (cparam->current.custom_string)
1024 {
1025 for (s = cparam->current.custom_string; *s; s ++)
757d2cad 1026 {
4220952d
MS
1027 if (*s < ' ' || *s == '(' || *s == ')' || *s >= 127)
1028 {
07623986 1029 snprintf(bufptr, (size_t)(bufend - bufptr + 1), "\\%03o", *s & 255);
4220952d
MS
1030 bufptr += strlen(bufptr);
1031 }
1032 else
1033 *bufptr++ = *s;
757d2cad 1034 }
4220952d 1035 }
757d2cad 1036
1037 *bufptr++ = ')';
1038 *bufptr++ = '\n';
1039 break;
1040 }
1041 }
ee571f26
MS
1042
1043 cupsArrayDelete(params);
757d2cad 1044 }
1045 else
1046 {
07623986 1047 snprintf(bufptr, (size_t)(bufend - bufptr + 1), "%%%%BeginFeature: *%s %s\n", choices[i]->option->keyword, choices[i]->choice);
757d2cad 1048 bufptr += strlen(bufptr);
1049 }
1050
1051 if (choices[i]->code && choices[i]->code[0])
1052 {
b86bc4cf 1053 j = (int)strlen(choices[i]->code);
07623986 1054 memcpy(bufptr, choices[i]->code, (size_t)j);
757d2cad 1055 bufptr += j;
1056
1057 if (choices[i]->code[j - 1] != '\n')
1058 *bufptr++ = '\n';
1059 }
1060
1061 strlcpy(bufptr, "%%EndFeature\n"
07623986 1062 "} stopped cleartomark\n", (size_t)(bufend - bufptr + 1));
757d2cad 1063 bufptr += strlen(bufptr);
8ca02f3c 1064
e07d4801 1065 DEBUG_printf(("2ppdEmitString: Offset in string is %d...",
ae71f5de 1066 (int)(bufptr - buffer)));
757d2cad 1067 }
1068 else
1069 {
07623986 1070 strlcpy(bufptr, choices[i]->code, (size_t)(bufend - bufptr + 1));
757d2cad 1071 bufptr += strlen(bufptr);
1072 }
1073
1074 /*
1075 * Nul-terminate, free, and return...
1076 */
1077
1078 *bufptr = '\0';
1079
1080 free(choices);
1081
1082 return (buffer);
1083}
1084
1085
ee571f26
MS
1086/*
1087 * 'ppd_compare_cparams()' - Compare the order of two custom parameters.
1088 */
1089
1090static int /* O - Result of comparison */
1091ppd_compare_cparams(ppd_cparam_t *a, /* I - First parameter */
1092 ppd_cparam_t *b) /* I - Second parameter */
1093{
1094 return (a->order - b->order);
1095}
1096
1097
ef416fc2 1098/*
1099 * 'ppd_handle_media()' - Handle media selection...
1100 */
1101
1102static void
ee571f26 1103ppd_handle_media(ppd_file_t *ppd) /* I - PPD file */
ef416fc2 1104{
1105 ppd_choice_t *manual_feed, /* ManualFeed choice, if any */
f11a948a 1106 *input_slot; /* InputSlot choice, if any */
ef416fc2 1107 ppd_size_t *size; /* Current media size */
1108 ppd_attr_t *rpr; /* RequiresPageRegion value */
1109
1110
1111 /*
f11a948a
MS
1112 * This function determines what page size code to use, if any, for the
1113 * current media size, InputSlot, and ManualFeed selections.
1114 *
1115 * We use the PageSize code if:
1116 *
1117 * 1. A custom media size is selected.
1118 * 2. ManualFeed and InputSlot are not selected (or do not exist).
1119 * 3. ManualFeed is selected but is False and InputSlot is not selected or
1120 * the selection has no code - the latter check done to support "auto" or
1121 * "printer default" InputSlot options.
1122 *
1123 * We use the PageRegion code if:
1124 *
1125 * 4. RequiresPageRegion does not exist and the PPD contains cupsFilter
1126 * keywords, indicating this is a CUPS-based driver.
1127 * 5. RequiresPageRegion exists for the selected InputSlot (or "All" for any
1128 * InputSlot or ManualFeed selection) and is True.
1129 *
1130 * If none of the 5 conditions are true, no page size code is used and we
1131 * unmark any existing PageSize or PageRegion choices.
ef416fc2 1132 */
1133
1134 if ((size = ppdPageSize(ppd, NULL)) == NULL)
1135 return;
1136
1137 manual_feed = ppdFindMarkedChoice(ppd, "ManualFeed");
1138 input_slot = ppdFindMarkedChoice(ppd, "InputSlot");
1139
1140 if (input_slot != NULL)
1141 rpr = ppdFindAttr(ppd, "RequiresPageRegion", input_slot->choice);
1142 else
1143 rpr = NULL;
1144
1145 if (!rpr)
1146 rpr = ppdFindAttr(ppd, "RequiresPageRegion", "All");
1147
88f9aafc 1148 if (!_cups_strcasecmp(size->name, "Custom") ||
f11a948a 1149 (!manual_feed && !input_slot) ||
88f9aafc 1150 (manual_feed && !_cups_strcasecmp(manual_feed->choice, "False") &&
178cb736
MS
1151 (!input_slot || (input_slot->code && !input_slot->code[0]))) ||
1152 (!rpr && ppd->num_filters > 0))
ef416fc2 1153 {
1154 /*
f11a948a 1155 * Use PageSize code...
ef416fc2 1156 */
1157
1158 ppdMarkOption(ppd, "PageSize", size->name);
1159 }
88f9aafc 1160 else if (rpr && rpr->value && !_cups_strcasecmp(rpr->value, "True"))
ef416fc2 1161 {
1162 /*
f11a948a 1163 * Use PageRegion code...
ef416fc2 1164 */
1165
1166 ppdMarkOption(ppd, "PageRegion", size->name);
f11a948a
MS
1167 }
1168 else
1169 {
ef416fc2 1170 /*
f11a948a 1171 * Do not use PageSize or PageRegion code...
ef416fc2 1172 */
1173
f11a948a
MS
1174 ppd_choice_t *page; /* PageSize/Region choice, if any */
1175
1176 if ((page = ppdFindMarkedChoice(ppd, "PageSize")) != NULL)
ef416fc2 1177 {
1178 /*
f11a948a 1179 * Unmark PageSize...
ef416fc2 1180 */
1181
f11a948a
MS
1182 page->marked = 0;
1183 cupsArrayRemove(ppd->marked, page);
1184 }
1185
1186 if ((page = ppdFindMarkedChoice(ppd, "PageRegion")) != NULL)
1187 {
1188 /*
1189 * Unmark PageRegion...
1190 */
ef416fc2 1191
f11a948a
MS
1192 page->marked = 0;
1193 cupsArrayRemove(ppd->marked, page);
ef416fc2 1194 }
1195 }
1196}