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