1 /*
2 * Generic Adobe PostScript printer command for ippeveprinter/CUPS.
3 *
4 * Copyright © 2019 by Apple Inc.
5 *
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
7 * information.
8 */
9
10 /*
11 * Include necessary headers...
12 */
13
14 #include "ippevecommon.h"
15 #if !CUPS_LITE
16 # include <cups/ppd-private.h>
17 #endif /* !CUPS_LITE */
18 #include <limits.h>
19 #include <sys/wait.h>
20
21 #ifdef __APPLE__
22 # define PDFTOPS CUPS_SERVERBIN "/filter/cgpdftops"
23 #else
24 # define PDFTOPS CUPS_SERVERBIN "/filter/pdftops"
25 #endif /* __APPLE__ */
26
27
28 /*
29 * Local globals...
30 */
31
32 #if !CUPS_LITE
33 static ppd_file_t *ppd = NULL; /* PPD file data */
34 static _ppd_cache_t *ppd_cache = NULL;
35 /* IPP to PPD cache data */
36 #endif /* !CUPS_LITE */
37
38
39 /*
40 * Local functions...
41 */
42
43 static void ascii85(const unsigned char *data, int length, int eod);
44 static void dsc_header(int num_pages);
45 static void dsc_page(int page);
46 static void dsc_trailer(int num_pages);
47 static int get_options(cups_option_t **options);
48 static int jpeg_to_ps(const char *filename, int copies);
49 static int pdf_to_ps(const char *filename, int copies, int num_options, cups_option_t *options);
50 static int ps_to_ps(const char *filename, int copies);
51 static int raster_to_ps(const char *filename);
52
53
54 /*
55 * 'main()' - Main entry for PostScript printer command.
56 */
57
58 int /* O - Exit status */
main(int argc,char * argv[])59 main(int argc, /* I - Number of command-line arguments */
60 char *argv[]) /* I - Command-line arguments */
61 {
62 const char *content_type, /* Content type to print */
63 *ipp_copies; /* IPP_COPIES value */
64 int copies; /* Number of copies */
65 int num_options; /* Number of options */
66 cups_option_t *options; /* Options */
67
68
69 /*
70 * Get print options...
71 */
72
73 num_options = get_options(&options);
74 if ((ipp_copies = getenv("IPP_COPIES")) != NULL)
75 copies = atoi(ipp_copies);
76 else
77 copies = 1;
78
79 /*
80 * Print it...
81 */
82
83 if (argc > 2)
84 {
85 fputs("ERROR: Too many arguments supplied, aborting.\n", stderr);
86 return (1);
87 }
88 else if ((content_type = getenv("CONTENT_TYPE")) == NULL)
89 {
90 fputs("ERROR: CONTENT_TYPE environment variable not set, aborting.\n", stderr);
91 return (1);
92 }
93 else if (!strcasecmp(content_type, "application/pdf"))
94 {
95 return (pdf_to_ps(argv[1], copies, num_options, options));
96 }
97 else if (!strcasecmp(content_type, "application/postscript"))
98 {
99 return (ps_to_ps(argv[1], copies));
100 }
101 else if (!strcasecmp(content_type, "image/jpeg"))
102 {
103 return (jpeg_to_ps(argv[1], copies));
104 }
105 else if (!strcasecmp(content_type, "image/pwg-raster") || !strcasecmp(content_type, "image/urf"))
106 {
107 return (raster_to_ps(argv[1]));
108 }
109 else
110 {
111 fprintf(stderr, "ERROR: CONTENT_TYPE %s not supported.\n", content_type);
112 return (1);
113 }
114 }
115
116
117 /*
118 * 'ascii85()' - Write binary data using a Base85 encoding...
119 */
120
121 static void
ascii85(const unsigned char * data,int length,int eod)122 ascii85(const unsigned char *data, /* I - Data to write */
123 int length, /* I - Number of bytes to write */
124 int eod) /* I - 1 if this is the end, 0 otherwise */
125 {
126 unsigned b = 0; /* Current 32-bit word */
127 unsigned char c[5]; /* Base-85 encoded characters */
128 static int col = 0; /* Column */
129 static unsigned char leftdata[4]; /* Leftover data at the end */
130 static int leftcount = 0; /* Size of leftover data */
131
132
133 length += leftcount;
134
135 while (length > 3)
136 {
137 switch (leftcount)
138 {
139 case 0 :
140 b = (unsigned)((((((data[0] << 8) | data[1]) << 8) | data[2]) << 8) | data[3]);
141 break;
142 case 1 :
143 b = (unsigned)((((((leftdata[0] << 8) | data[0]) << 8) | data[1]) << 8) | data[2]);
144 break;
145 case 2 :
146 b = (unsigned)((((((leftdata[0] << 8) | leftdata[1]) << 8) | data[0]) << 8) | data[1]);
147 break;
148 case 3 :
149 b = (unsigned)((((((leftdata[0] << 8) | leftdata[1]) << 8) | leftdata[2]) << 8) | data[0]);
150 break;
151 }
152
153 if (col >= 76)
154 {
155 col = 0;
156 putchar('\n');
157 }
158
159 if (b == 0)
160 {
161 putchar('z');
162 col ++;
163 }
164 else
165 {
166 c[4] = (b % 85) + '!';
167 b /= 85;
168 c[3] = (b % 85) + '!';
169 b /= 85;
170 c[2] = (b % 85) + '!';
171 b /= 85;
172 c[1] = (b % 85) + '!';
173 b /= 85;
174 c[0] = (unsigned char)(b + '!');
175
176 fwrite(c, 1, 5, stdout);
177 col += 5;
178 }
179
180 data += 4 - leftcount;
181 length -= 4 - leftcount;
182 leftcount = 0;
183 }
184
185 if (length > 0)
186 {
187 // Copy any remainder into the leftdata array...
188 if ((length - leftcount) > 0)
189 memcpy(leftdata + leftcount, data, (size_t)(length - leftcount));
190
191 memset(leftdata + length, 0, (size_t)(4 - length));
192
193 leftcount = length;
194 }
195
196 if (eod)
197 {
198 // Do the end-of-data dance...
199 if (col >= 76)
200 {
201 col = 0;
202 putchar('\n');
203 }
204
205 if (leftcount > 0)
206 {
207 // Write the remaining bytes as needed...
208 b = (unsigned)((((((leftdata[0] << 8) | leftdata[1]) << 8) | leftdata[2]) << 8) | leftdata[3]);
209
210 c[4] = (b % 85) + '!';
211 b /= 85;
212 c[3] = (b % 85) + '!';
213 b /= 85;
214 c[2] = (b % 85) + '!';
215 b /= 85;
216 c[1] = (b % 85) + '!';
217 b /= 85;
218 c[0] = (unsigned char)(b + '!');
219
220 fwrite(c, (size_t)(leftcount + 1), 1, stdout);
221
222 leftcount = 0;
223 }
224
225 puts("~>");
226 col = 0;
227 }
228 }
229
230
231 /*
232 * 'dsc_header()' - Write out a standard Document Structuring Conventions
233 * PostScript header.
234 */
235
236 static void
dsc_header(int num_pages)237 dsc_header(int num_pages) /* I - Number of pages or 0 if not known */
238 {
239 const char *job_name = getenv("IPP_JOB_NAME");
240 /* job-name value */
241
242
243 #if !CUPS_LITE
244 const char *job_id = getenv("IPP_JOB_ID");
245 /* job-id value */
246
247 ppdEmitJCL(ppd, stdout, job_id ? atoi(job_id) : 0, cupsUser(), job_name ? job_name : "Unknown");
248 #endif /* !CUPS_LITE */
249
250 puts("%!PS-Adobe-3.0");
251 puts("%%LanguageLevel: 2");
252 printf("%%%%Creator: ippeveps/%d.%d.%d\n", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR, CUPS_VERSION_PATCH);
253 if (job_name)
254 {
255 fputs("%%Title: ", stdout);
256 while (*job_name)
257 {
258 if (*job_name >= 0x20 && *job_name < 0x7f)
259 putchar(*job_name);
260 else
261 putchar('?');
262
263 job_name ++;
264 }
265 putchar('\n');
266 }
267 if (num_pages > 0)
268 printf("%%%%Pages: %d\n", num_pages);
269 else
270 puts("%%Pages: (atend)");
271 puts("%%EndComments");
272
273 #if !CUPS_LITE
274 if (ppd)
275 {
276 puts("%%BeginProlog");
277 if (ppd->patches)
278 {
279 puts("%%BeginFeature: *JobPatchFile 1");
280 puts(ppd->patches);
281 puts("%%EndFeature");
282 }
283 ppdEmit(ppd, stdout, PPD_ORDER_PROLOG);
284 puts("%%EndProlog");
285
286 puts("%%BeginSetup");
287 ppdEmit(ppd, stdout, PPD_ORDER_DOCUMENT);
288 ppdEmit(ppd, stdout, PPD_ORDER_ANY);
289 puts("%%EndSetup");
290 }
291 #endif /* !CUPS_LITE */
292 }
293
294
295 /*
296 * 'dsc_page()' - Mark the start of a page.
297 */
298
299 static void
dsc_page(int page)300 dsc_page(int page) /* I - Page numebr (1-based) */
301 {
302 printf("%%%%Page: (%d) %d\n", page, page);
303
304 fprintf(stderr, "ATTR: job-impressions-completed=%d\n", page);
305
306 #if !CUPS_LITE
307 if (ppd)
308 {
309 puts("%%BeginPageSetup");
310 ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
311 puts("%%EndPageSetup");
312 }
313 #endif /* !CUPS_LITE */
314 }
315
316
317 /*
318 * 'dsc_trailer()' - Mark the end of the document.
319 */
320
321 static void
dsc_trailer(int num_pages)322 dsc_trailer(int num_pages) /* I - Number of pages */
323 {
324 if (num_pages > 0)
325 {
326 puts("%%Trailer");
327 printf("%%%%Pages: %d\n", num_pages);
328 puts("%%EOF");
329 }
330
331 #if !CUPS_LITE
332 if (ppd && ppd->jcl_end)
333 ppdEmitJCLEnd(ppd, stdout);
334 else
335 #endif /* !CUPS_LITE */
336 putchar(0x04);
337 }
338
339
340 /*
341 * 'get_options()' - Get the PPD options corresponding to the IPP Job Template
342 * attributes.
343 */
344
345 static int /* O - Number of options */
get_options(cups_option_t ** options)346 get_options(cups_option_t **options) /* O - Options */
347 {
348 int num_options = 0; /* Number of options */
349 const char *value; /* Option value */
350 pwg_media_t *media = NULL; /* Media mapping */
351 int num_media_col = 0; /* Number of media-col values */
352 cups_option_t *media_col = NULL; /* media-col values */
353 #if !CUPS_LITE
354 const char *choice; /* PPD choice */
355 #endif /* !CUPS_LITE */
356
357
358 /*
359 * No options to start...
360 */
361
362 *options = NULL;
363
364 /*
365 * Media...
366 */
367
368 if ((value = getenv("IPP_MEDIA")) == NULL)
369 if ((value = getenv("IPP_MEDIA_COL")) == NULL)
370 if ((value = getenv("IPP_MEDIA_DEFAULT")) == NULL)
371 value = getenv("IPP_MEDIA_COL_DEFAULT");
372
373 if (value)
374 {
375 if (*value == '{')
376 {
377 /*
378 * media-col value...
379 */
380
381 num_media_col = cupsParseOptions(value, 0, &media_col);
382 }
383 else
384 {
385 /*
386 * media value - map to media-col.media-size-name...
387 */
388
389 num_media_col = cupsAddOption("media-size-name", value, 0, &media_col);
390 }
391 }
392
393 if ((value = cupsGetOption("media-size-name", num_media_col, media_col)) != NULL)
394 {
395 media = pwgMediaForPWG(value);
396 }
397 else if ((value = cupsGetOption("media-size", num_media_col, media_col)) != NULL)
398 {
399 int num_media_size; /* Number of media-size values */
400 cups_option_t *media_size; /* media-size values */
401 const char *x_dimension, /* x-dimension value */
402 *y_dimension; /* y-dimension value */
403
404 num_media_size = cupsParseOptions(value, 0, &media_size);
405
406 if ((x_dimension = cupsGetOption("x-dimension", num_media_size, media_size)) != NULL && (y_dimension = cupsGetOption("y-dimension", num_media_size, media_size)) != NULL)
407 media = pwgMediaForSize(atoi(x_dimension), atoi(y_dimension));
408
409 cupsFreeOptions(num_media_size, media_size);
410 }
411
412 if (media)
413 num_options = cupsAddOption("PageSize", media->ppd, num_options, options);
414
415 #if !CUPS_LITE
416 /*
417 * Load PPD file and the corresponding IPP <-> PPD cache data...
418 */
419
420 if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL)
421 {
422 ppd_cache = _ppdCacheCreateWithPPD(ppd);
423
424 /* TODO: Fix me - values are names, not numbers... Also need to support finishings-col */
425 if ((value = getenv("IPP_FINISHINGS")) == NULL)
426 value = getenv("IPP_FINISHINGS_DEFAULT");
427
428 if (value)
429 {
430 char *ptr; /* Pointer into value */
431 int fin; /* Current value */
432
433 for (fin = strtol(value, &ptr, 10); fin > 0; fin = strtol(ptr + 1, &ptr, 10))
434 {
435 num_options = _ppdCacheGetFinishingOptions(ppd_cache, NULL, (ipp_finishings_t)fin, num_options, options);
436
437 if (*ptr != ',')
438 break;
439 }
440 }
441
442 if ((value = cupsGetOption("media-source", num_media_col, media_col)) != NULL)
443 {
444 if ((choice = _ppdCacheGetInputSlot(ppd_cache, NULL, value)) != NULL)
445 num_options = cupsAddOption("InputSlot", choice, num_options, options);
446 }
447
448 if ((value = cupsGetOption("media-type", num_media_col, media_col)) != NULL)
449 {
450 if ((choice = _ppdCacheGetMediaType(ppd_cache, NULL, value)) != NULL)
451 num_options = cupsAddOption("MediaType", choice, num_options, options);
452 }
453
454 if ((value = getenv("IPP_OUTPUT_BIN")) == NULL)
455 value = getenv("IPP_OUTPUT_BIN_DEFAULT");
456
457 if (value)
458 {
459 if ((choice = _ppdCacheGetOutputBin(ppd_cache, value)) != NULL)
460 num_options = cupsAddOption("OutputBin", choice, num_options, options);
461 }
462
463 if ((value = getenv("IPP_SIDES")) == NULL)
464 value = getenv("IPP_SIDES_DEFAULT");
465
466 if (value && ppd_cache->sides_option)
467 {
468 if (!strcmp(value, "one-sided") && ppd_cache->sides_1sided)
469 num_options = cupsAddOption(ppd_cache->sides_option, ppd_cache->sides_1sided, num_options, options);
470 else if (!strcmp(value, "two-sided-long-edge") && ppd_cache->sides_2sided_long)
471 num_options = cupsAddOption(ppd_cache->sides_option, ppd_cache->sides_2sided_long, num_options, options);
472 else if (!strcmp(value, "two-sided-short-edge") && ppd_cache->sides_2sided_short)
473 num_options = cupsAddOption(ppd_cache->sides_option, ppd_cache->sides_2sided_short, num_options, options);
474 }
475
476 if ((value = getenv("IPP_PRINT_QUALITY")) == NULL)
477 value = getenv("IPP_PRINT_QUALITY_DEFAULT");
478
479 if (value)
480 {
481 int i; /* Looping var */
482 int pq; /* Print quality (0-2) */
483 int pcm = 1; /* Print color model (0 = mono, 1 = color) */
484 int num_presets; /* Number of presets */
485 cups_option_t *presets; /* Presets */
486
487 if (!strcmp(value, "draft"))
488 pq = 0;
489 else if (!strcmp(value, "high"))
490 pq = 2;
491 else
492 pq = 1;
493
494 if ((value = getenv("IPP_PRINT_COLOR_MODE")) == NULL)
495 value = getenv("IPP_PRINT_COLOR_MODE_DEFAULT");
496
497 if (value && !strcmp(value, "monochrome"))
498 pcm = 0;
499
500 num_presets = ppd_cache->num_presets[pcm][pq];
501 presets = ppd_cache->presets[pcm][pq];
502
503 for (i = 0; i < num_presets; i ++)
504 num_options = cupsAddOption(presets[i].name, presets[i].value, num_options, options);
505 }
506
507 /*
508 * Mark the PPD with the options...
509 */
510
511 ppdMarkDefaults(ppd);
512 cupsMarkOptions(ppd, num_options, *options);
513 }
514 #endif /* !CUPS_LITE */
515
516 cupsFreeOptions(num_media_col, media_col);
517
518 return (num_options);
519 }
520
521
522 /*
523 * 'jpeg_to_ps()' - Convert a JPEG file to PostScript.
524 */
525
526 static int /* O - Exit status */
jpeg_to_ps(const char * filename,int copies)527 jpeg_to_ps(const char *filename, /* I - Filename */
528 int copies) /* I - Number of copies */
529 {
530 int fd; /* JPEG file descriptor */
531 int copy; /* Current copy */
532 int width = 0, /* Width */
533 height = 0, /* Height */
534 depth = 0, /* Number of colors */
535 length; /* Length of marker */
536 unsigned char buffer[65536], /* Copy buffer */
537 *bufptr, /* Pointer info buffer */
538 *bufend; /* End of buffer */
539 ssize_t bytes; /* Bytes in buffer */
540 const char *decode; /* Decode array */
541 float page_left, /* Left margin */
542 page_top, /* Top margin */
543 page_width, /* Page width in points */
544 page_height, /* Page heigth in points */
545 x_factor, /* X image scaling factor */
546 y_factor, /* Y image scaling factor */
547 page_scaling; /* Image scaling factor */
548 #if !CUPS_LITE
549 ppd_size_t *page_size; /* Current page size */
550 #endif /* !CUPS_LITE */
551
552
553 /*
554 * Open the input file...
555 */
556
557 if (filename)
558 {
559 if ((fd = open(filename, O_RDONLY)) < 0)
560 {
561 fprintf(stderr, "ERROR: Unable to open \"%s\": %s\n", filename, strerror(errno));
562 return (1);
563 }
564 }
565 else
566 {
567 fd = 0;
568 copies = 1;
569 }
570
571 /*
572 * Read the JPEG dimensions...
573 */
574
575 bytes = read(fd, buffer, sizeof(buffer));
576
577 if (bytes < 3 || memcmp(buffer, "\377\330\377", 3))
578 {
579 fputs("ERROR: Not a JPEG image.\n", stderr);
580
581 if (fd > 0)
582 close(fd);
583
584 return (1);
585 }
586
587 for (bufptr = buffer + 2, bufend = buffer + bytes; bufptr < bufend;)
588 {
589 /*
590 * Scan the file for a SOFn marker, then we can get the dimensions...
591 */
592
593 if (*bufptr == 0xff)
594 {
595 bufptr ++;
596
597 if (bufptr >= bufend)
598 {
599 /*
600 * If we are at the end of the current buffer, re-fill and continue...
601 */
602
603 if ((bytes = read(fd, buffer, sizeof(buffer))) <= 0)
604 break;
605
606 bufptr = buffer;
607 bufend = buffer + bytes;
608 }
609
610 if (*bufptr == 0xff)
611 continue;
612
613 if ((bufptr + 16) >= bufend)
614 {
615 /*
616 * Read more of the marker...
617 */
618
619 bytes = bufend - bufptr;
620
621 memmove(buffer, bufptr, (size_t)bytes);
622 bufptr = buffer;
623 bufend = buffer + bytes;
624
625 if ((bytes = read(fd, bufend, sizeof(buffer) - (size_t)bytes)) <= 0)
626 break;
627
628 bufend += bytes;
629 }
630
631 length = (size_t)((bufptr[1] << 8) | bufptr[2]);
632
633 if ((*bufptr >= 0xc0 && *bufptr <= 0xc3) || (*bufptr >= 0xc5 && *bufptr <= 0xc7) || (*bufptr >= 0xc9 && *bufptr <= 0xcb) || (*bufptr >= 0xcd && *bufptr <= 0xcf))
634 {
635 /*
636 * SOFn marker, look for dimensions...
637 */
638
639 width = (bufptr[6] << 8) | bufptr[7];
640 height = (bufptr[4] << 8) | bufptr[5];
641 depth = bufptr[8];
642 break;
643 }
644
645 /*
646 * Skip past this marker...
647 */
648
649 bufptr ++;
650 bytes = bufend - bufptr;
651
652 while (length >= bytes)
653 {
654 length -= bytes;
655
656 if ((bytes = read(fd, buffer, sizeof(buffer))) <= 0)
657 break;
658
659 bufptr = buffer;
660 bufend = buffer + bytes;
661 }
662
663 if (length > bytes)
664 break;
665
666 bufptr += length;
667 }
668 }
669
670 fprintf(stderr, "DEBUG: JPEG dimensions are %dx%dx%d\n", width, height, depth);
671
672 if (width <= 0 || height <= 0 || depth <= 0)
673 {
674 fputs("ERROR: No valid image data in JPEG file.\n", stderr);
675
676 if (fd > 0)
677 close(fd);
678
679 return (1);
680 }
681
682 fputs("ATTR: job-impressions=1\n", stderr);
683
684 /*
685 * Figure out the dimensions/scaling of the final image...
686 */
687
688 #if CUPS_LITE
689 page_left = 18.0f;
690 page_top = 756.0f;
691 page_width = 576.0f;
692 page_height = 720.0f;
693
694 #else
695 if ((page_size = ppdPageSize(ppd, NULL)) != NULL)
696 {
697 page_left = page_size->left;
698 page_top = page_size->top;
699 page_width = page_size->right - page_left;
700 page_height = page_top - page_size->bottom;
701 }
702 else
703 {
704 page_left = 18.0f;
705 page_top = 756.0f;
706 page_width = 576.0f;
707 page_height = 720.0f;
708 }
709 #endif /* CUPS_LITE */
710
711 fprintf(stderr, "DEBUG: page_left=%.2f, page_top=%.2f, page_width=%.2f, page_height=%.2f\n", page_left, page_top, page_width, page_height);
712
713 /* TODO: Support orientation/rotation, different print-scaling modes */
714 x_factor = page_width / width;
715 y_factor = page_height / height;
716
717 if (x_factor > y_factor && (height * x_factor) <= page_height)
718 page_scaling = x_factor;
719 else
720 page_scaling = y_factor;
721
722 fprintf(stderr, "DEBUG: Scaled dimensions are %.2fx%.2f\n", width * page_scaling, height * page_scaling);
723
724 /*
725 * Write pages...
726 */
727
728 dsc_header(copies);
729
730 for (copy = 1; copy <= copies; copy ++)
731 {
732 dsc_page(copy);
733
734 if (depth == 1)
735 {
736 puts("/DeviceGray setcolorspace");
737 decode = "0 1";
738 }
739 else if (depth == 3)
740 {
741 puts("/DeviceRGB setcolorspace");
742 decode = "0 1 0 1 0 1";
743 }
744 else
745 {
746 puts("/DeviceCMYK setcolorspace");
747 decode = "0 1 0 1 0 1 0 1";
748 }
749
750 printf("gsave %.3f %.3f translate %.3f %.3f scale\n", page_left + 0.5f * (page_width - width * page_scaling), page_top - 0.5f * (page_height - height * page_scaling), page_scaling, page_scaling);
751 printf("<</ImageType 1/Width %d/Height %d/BitsPerComponent 8/ImageMatrix[1 0 0 -1 0 1]/Decode[%s]/DataSource currentfile/ASCII85Decode filter/DCTDecode filter/Interpolate true>>image\n", width, height, decode);
752
753 if (fd > 0)
754 lseek(fd, 0, SEEK_SET);
755
756 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
757 ascii85(buffer, (int)bytes, 0);
758
759 ascii85(buffer, 0, 1);
760
761 puts("grestore showpage");
762 }
763
764 dsc_trailer(0);
765
766 return (0);
767 }
768
769
770 /*
771 * 'pdf_to_ps()' - Convert a PDF file to PostScript.
772 */
773
774 static int /* O - Exit status */
pdf_to_ps(const char * filename,int copies,int num_options,cups_option_t * options)775 pdf_to_ps(const char *filename, /* I - Filename */
776 int copies, /* I - Number of copies */
777 int num_options, /* I - Number of options */
778 cups_option_t *options) /* I - options */
779 {
780 int status; /* Exit status */
781 char tempfile[1024]; /* Temporary file */
782 int tempfd; /* Temporary file descriptor */
783 int pid; /* Process ID */
784 const char *pdf_argv[8]; /* Command-line arguments */
785 char pdf_options[1024]; /* Options */
786 const char *value; /* Option value */
787 const char *job_id, /* job-id value */
788 *job_name; /* job-name value */
789
790
791 /*
792 * Create a temporary file for the PostScript version...
793 */
794
795 if ((tempfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
796 {
797 fprintf(stderr, "ERROR: Unable to create temporary file: %s\n", strerror(errno));
798 return (1);
799 }
800
801 /*
802 * Run cgpdftops or pdftops in the filter directory...
803 */
804
805 if ((value = cupsGetOption("PageSize", num_options, options)) != NULL)
806 snprintf(pdf_options, sizeof(pdf_options), "PageSize=%s", value);
807 else
808 pdf_options[0] = '\0';
809
810 if ((job_id = getenv("IPP_JOB_ID")) == NULL)
811 job_id = "42";
812 if ((job_name = getenv("IPP_JOB_NAME")) == NULL)
813 job_name = "untitled";
814
815 pdf_argv[0] = "printer";
816 pdf_argv[1] = job_id;
817 pdf_argv[2] = cupsUser();
818 pdf_argv[3] = job_name;
819 pdf_argv[4] = "1";
820 pdf_argv[5] = pdf_options;
821 pdf_argv[6] = filename;
822 pdf_argv[7] = NULL;
823
824 if ((pid = fork()) == 0)
825 {
826 /*
827 * Child comes here...
828 */
829
830 close(1);
831 dup2(tempfd, 1);
832 close(tempfd);
833
834 execv(PDFTOPS, (char * const *)pdf_argv);
835 exit(errno);
836 }
837 else if (pid < 0)
838 {
839 /*
840 * Unable to fork process...
841 */
842
843 perror("ERROR: Unable to start PDF filter");
844
845 close(tempfd);
846 unlink(tempfile);
847
848 return (1);
849 }
850 else
851 {
852 /*
853 * Wait for the filter to complete...
854 */
855
856 close(tempfd);
857
858 # ifdef HAVE_WAITPID
859 while (waitpid(pid, &status, 0) < 0);
860 # else
861 while (wait(&status) < 0);
862 # endif /* HAVE_WAITPID */
863
864 if (status)
865 {
866 if (WIFEXITED(status))
867 fprintf(stderr, "ERROR: " PDFTOPS " exited with status %d.\n", WEXITSTATUS(status));
868 else
869 fprintf(stderr, "ERROR: " PDFTOPS " terminated with signal %d.\n", WTERMSIG(status));
870
871 unlink(tempfile);
872 return (1);
873 }
874 }
875
876 /*
877 * Copy the PostScript output from the command...
878 */
879
880 status = ps_to_ps(tempfile, copies);
881
882 unlink(tempfile);
883
884 return (status);
885 }
886
887
888 /*
889 * 'ps_to_ps()' - Copy PostScript to the standard output.
890 */
891
892 static int /* O - Exit status */
ps_to_ps(const char * filename,int copies)893 ps_to_ps(const char *filename, /* I - Filename */
894 int copies) /* I - Number of copies */
895 {
896 FILE *fp; /* File to read from */
897 int copy, /* Current copy */
898 page, /* Current page number */
899 num_pages = 0, /* Total number of pages */
900 first_page, /* First page */
901 last_page; /* Last page */
902 const char *page_ranges; /* page-ranges option */
903 long first_pos = -1; /* Offset for first page */
904 char line[1024]; /* Line from file */
905
906
907 /*
908 * Open the print file...
909 */
910
911 if (filename)
912 {
913 if ((fp = fopen(filename, "rb")) == NULL)
914 {
915 fprintf(stderr, "ERROR: Unable to open \"%s\": %s\n", filename, strerror(errno));
916 return (1);
917 }
918 }
919 else
920 {
921 copies = 1;
922 fp = stdin;
923 }
924
925 /*
926 * Check page ranges...
927 */
928
929 if ((page_ranges = getenv("IPP_PAGE_RANGES")) != NULL)
930 {
931 if (sscanf(page_ranges, "%d-%d", &first_page, &last_page) != 2)
932 {
933 first_page = 1;
934 last_page = INT_MAX;
935 }
936 }
937 else
938 {
939 first_page = 1;
940 last_page = INT_MAX;
941 }
942
943 /*
944 * Write the PostScript header for the document...
945 */
946
947 dsc_header(0);
948
949 first_pos = 0;
950
951 while (fgets(line, sizeof(line), fp))
952 {
953 if (!strncmp(line, "%%Page:", 7))
954 break;
955
956 first_pos = ftell(fp);
957
958 if (line[0] != '%')
959 fputs(line, stdout);
960 }
961
962 if (!strncmp(line, "%%Page:", 7))
963 {
964 for (copy = 0; copy < copies; copy ++)
965 {
966 int copy_page = 0; /* Do we copy the page data? */
967
968 if (fp != stdin)
969 fseek(fp, first_pos, SEEK_SET);
970
971 page = 0;
972 while (fgets(line, sizeof(line), fp))
973 {
974 if (!strncmp(line, "%%Page:", 7))
975 {
976 page ++;
977 copy_page = page >= first_page && page <= last_page;
978
979 if (copy_page)
980 {
981 num_pages ++;
982 dsc_page(num_pages);
983 }
984 }
985 else if (copy_page)
986 fputs(line, stdout);
987 }
988 }
989 }
990
991 dsc_trailer(num_pages);
992
993 fprintf(stderr, "ATTR: job-impressions=%d\n", num_pages / copies);
994
995 if (fp != stdin)
996 fclose(fp);
997
998 return (0);
999 }
1000
1001
1002 /*
1003 * 'raster_to_ps()' - Convert PWG Raster/Apple Raster to PostScript.
1004 *
1005 * The current implementation locally-decodes the raster data and then writes
1006 * whole, non-blank lines as 1-line high images with base-85 encoding, resulting
1007 * in between 10 and 20 times larger output. A alternate implementation (if it
1008 * is deemed necessary) would be to implement a PostScript decode procedure that
1009 * handles the modified packbits decompression so that we just have the base-85
1010 * encoding overhead (25%). Furthermore, Level 3 PostScript printers also
1011 * support Flate compression.
1012 *
1013 * That said, the most efficient path with the highest quality is for Clients
1014 * to supply PDF files and us to use the existing PDF to PostScript conversion
1015 * filters.
1016 */
1017
1018 static int /* O - Exit status */
raster_to_ps(const char * filename)1019 raster_to_ps(const char *filename) /* I - Filename */
1020 {
1021 int fd; /* Input file */
1022 cups_raster_t *ras; /* Raster stream */
1023 cups_page_header2_t header; /* Page header */
1024 int page = 0; /* Current page */
1025 unsigned y; /* Current line */
1026 unsigned char *line; /* Line buffer */
1027 unsigned char white; /* White color */
1028 const char *decode; /* Image decode array */
1029
1030
1031 /*
1032 * Open the input file...
1033 */
1034
1035 if (filename)
1036 {
1037 if ((fd = open(filename, O_RDONLY)) < 0)
1038 {
1039 fprintf(stderr, "ERROR: Unable to open \"%s\": %s\n", filename, strerror(errno));
1040 return (1);
1041 }
1042 }
1043 else
1044 {
1045 fd = 0;
1046 }
1047
1048 /*
1049 * Open the raster stream and send pages...
1050 */
1051
1052 if ((ras = cupsRasterOpen(fd, CUPS_RASTER_READ)) == NULL)
1053 {
1054 fputs("ERROR: Unable to read raster data, aborting.\n", stderr);
1055 return (1);
1056 }
1057
1058 dsc_header(0);
1059
1060 while (cupsRasterReadHeader2(ras, &header))
1061 {
1062 page ++;
1063
1064 fprintf(stderr, "DEBUG: Page %d: %ux%ux%u\n", page, header.cupsWidth, header.cupsHeight, header.cupsBitsPerPixel);
1065
1066 if (header.cupsColorSpace != CUPS_CSPACE_W && header.cupsColorSpace != CUPS_CSPACE_SW && header.cupsColorSpace != CUPS_CSPACE_K && header.cupsColorSpace != CUPS_CSPACE_RGB && header.cupsColorSpace != CUPS_CSPACE_SRGB)
1067 {
1068 fputs("ERROR: Unsupported color space, aborting.\n", stderr);
1069 break;
1070 }
1071 else if (header.cupsBitsPerColor != 1 && header.cupsBitsPerColor != 8)
1072 {
1073 fputs("ERROR: Unsupported bit depth, aborting.\n", stderr);
1074 break;
1075 }
1076
1077 line = malloc(header.cupsBytesPerLine);
1078
1079 dsc_page(page);
1080
1081 puts("gsave");
1082 printf("%.6f %.6f scale\n", 72.0f / header.HWResolution[0], 72.0f / header.HWResolution[1]);
1083
1084 switch (header.cupsColorSpace)
1085 {
1086 case CUPS_CSPACE_W :
1087 case CUPS_CSPACE_SW :
1088 decode = "0 1";
1089 puts("/DeviceGray setcolorspace");
1090 white = 255;
1091 break;
1092
1093 case CUPS_CSPACE_K :
1094 decode = "0 1";
1095 puts("/DeviceGray setcolorspace");
1096 white = 0;
1097 break;
1098
1099 default :
1100 decode = "0 1 0 1 0 1";
1101 puts("/DeviceRGB setcolorspace");
1102 white = 255;
1103 break;
1104 }
1105
1106 printf("gsave /L{grestore gsave 0 exch translate <</ImageType 1/Width %u/Height 1/BitsPerComponent %u/ImageMatrix[1 0 0 -1 0 1]/DataSource currentfile/ASCII85Decode filter/Decode[%s]>>image}bind def\n", header.cupsWidth, header.cupsBitsPerColor, decode);
1107
1108 for (y = header.cupsHeight; y > 0; y --)
1109 {
1110 if (cupsRasterReadPixels(ras, line, header.cupsBytesPerLine))
1111 {
1112 if (line[0] != white || memcmp(line, line + 1, header.cupsBytesPerLine - 1))
1113 {
1114 printf("%d L\n", y - 1);
1115 ascii85(line, (int)header.cupsBytesPerLine, 1);
1116 }
1117 }
1118 else
1119 break;
1120 }
1121
1122 fprintf(stderr, "DEBUG: y=%d at end...\n", y);
1123
1124 puts("grestore grestore");
1125 puts("showpage");
1126
1127 free(line);
1128 }
1129
1130 cupsRasterClose(ras);
1131
1132 dsc_trailer(page);
1133
1134 fprintf(stderr, "ATTR: job-impressions=%d\n", page);
1135
1136 return (0);
1137 }
1138
1139
1140