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