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