]> git.ipfire.org Git - thirdparty/cups.git/blame - tools/ippeveps.c
Greatly simplify the man page handling.
[thirdparty/cups.git] / tools / ippeveps.c
CommitLineData
1562b9a1
MS
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"
de4912b2
MS
15#if !CUPS_LITE
16# include <cups/ppd-private.h>
17#endif /* !CUPS_LITE */
7331c1b6 18#include <limits.h>
de4912b2
MS
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
32static ppd_file_t *ppd = NULL; /* PPD file data */
33static _ppd_cache_t *ppd_cache = NULL;
34 /* IPP to PPD cache data */
35#endif /* !CUPS_LITE */
36
37
38/*
39 * Local functions...
40 */
41
7331c1b6
MS
42static void ascii85(const unsigned char *data, int length, int eod);
43static void dsc_header(int num_pages);
de4912b2
MS
44static void dsc_page(int page);
45static void dsc_trailer(int num_pages);
46static int get_options(cups_option_t **options);
7331c1b6 47static int jpeg_to_ps(const char *filename, int copies);
de4912b2 48static int pdf_to_ps(const char *filename, int copies, int num_options, cups_option_t *options);
7331c1b6
MS
49static int ps_to_ps(const char *filename, int copies);
50static int raster_to_ps(const char *filename);
6641bd0d
MS
51
52
53/*
54 * 'main()' - Main entry for PostScript printer command.
55 */
56
57int /* O - Exit status */
58main(int argc, /* I - Number of command-line arguments */
59 char *argv[]) /* I - Command-line arguments */
60{
de4912b2
MS
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 {
7331c1b6 98 return (ps_to_ps(argv[1], copies));
de4912b2
MS
99 }
100 else if (!strcasecmp(content_type, "image/jpeg"))
101 {
7331c1b6 102 return (jpeg_to_ps(argv[1], copies));
de4912b2
MS
103 }
104 else if (!strcasecmp(content_type, "image/pwg-raster") || !strcasecmp(content_type, "image/urf"))
105 {
7331c1b6 106 return (raster_to_ps(argv[1]));
de4912b2
MS
107 }
108 else
109 {
110 fprintf(stderr, "ERROR: CONTENT_TYPE %s not supported.\n", content_type);
111 return (1);
112 }
113}
114
115
de4912b2
MS
116/*
117 * 'ascii85()' - Write binary data using a Base85 encoding...
118 */
119
120static void
121ascii85(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}
de4912b2
MS
228
229
230/*
231 * 'dsc_header()' - Write out a standard Document Structuring Conventions
232 * PostScript header.
233 */
234
235static void
7331c1b6 236dsc_header(int num_pages) /* I - Number of pages or 0 if not known */
de4912b2
MS
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");
10ce4df1 244 /* job-id value */
de4912b2 245
75c93cc2 246 ppdEmitJCL(ppd, stdout, job_id ? atoi(job_id) : 0, cupsUser(), job_name ? job_name : "Unknown");
de4912b2
MS
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);
75c93cc2 252 if (job_name)
de4912b2
MS
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
298static void
299dsc_page(int page) /* I - Page numebr (1-based) */
300{
301 printf("%%%%Page: (%d) %d\n", page, page);
302
58b64171
MS
303 fprintf(stderr, "ATTR: job-impressions-completed=%d\n", page);
304
de4912b2
MS
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
320static void
321dsc_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
344static int /* O - Number of options */
345get_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
de4912b2
MS
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
dc84a5a4 423 /* TODO: Fix me - values are names, not numbers... Also need to support finishings-col */
de4912b2
MS
424 if ((value = getenv("IPP_FINISHINGS")) == NULL)
425 value = getenv("IPP_FINISHINGS_DEFAULT");
426
427 if (value)
428 {
429 char *ptr; /* Pointer into value */
430 int fin; /* Current value */
431
432 for (fin = strtol(value, &ptr, 10); fin > 0; fin = strtol(ptr + 1, &ptr, 10))
433 {
434 num_options = _ppdCacheGetFinishingOptions(ppd_cache, NULL, (ipp_finishings_t)fin, num_options, options);
435
436 if (*ptr != ',')
437 break;
438 }
439 }
440
441 if ((value = cupsGetOption("media-source", num_media_col, media_col)) != NULL)
442 {
443 if ((choice = _ppdCacheGetInputSlot(ppd_cache, NULL, value)) != NULL)
444 num_options = cupsAddOption("InputSlot", choice, num_options, options);
445 }
446
447 if ((value = cupsGetOption("media-type", num_media_col, media_col)) != NULL)
448 {
449 if ((choice = _ppdCacheGetMediaType(ppd_cache, NULL, value)) != NULL)
450 num_options = cupsAddOption("MediaType", choice, num_options, options);
451 }
452
453 if ((value = getenv("IPP_OUTPUT_BIN")) == NULL)
454 value = getenv("IPP_OUTPUT_BIN_DEFAULT");
455
456 if (value)
457 {
458 if ((choice = _ppdCacheGetOutputBin(ppd_cache, value)) != NULL)
459 num_options = cupsAddOption("OutputBin", choice, num_options, options);
460 }
461
462 if ((value = getenv("IPP_SIDES")) == NULL)
463 value = getenv("IPP_SIDES_DEFAULT");
464
465 if (value && ppd_cache->sides_option)
466 {
467 if (!strcmp(value, "one-sided") && ppd_cache->sides_1sided)
468 num_options = cupsAddOption(ppd_cache->sides_option, ppd_cache->sides_1sided, num_options, options);
469 else if (!strcmp(value, "two-sided-long-edge") && ppd_cache->sides_2sided_long)
470 num_options = cupsAddOption(ppd_cache->sides_option, ppd_cache->sides_2sided_long, num_options, options);
471 else if (!strcmp(value, "two-sided-short-edge") && ppd_cache->sides_2sided_short)
472 num_options = cupsAddOption(ppd_cache->sides_option, ppd_cache->sides_2sided_short, num_options, options);
473 }
474
475 if ((value = getenv("IPP_PRINT_QUALITY")) == NULL)
476 value = getenv("IPP_PRINT_QUALITY_DEFAULT");
477
478 if (value)
479 {
480 int i; /* Looping var */
481 int pq; /* Print quality (0-2) */
482 int pcm = 1; /* Print color model (0 = mono, 1 = color) */
483 int num_presets; /* Number of presets */
484 cups_option_t *presets; /* Presets */
485
dc84a5a4
MS
486 if (!strcmp(value, "draft"))
487 pq = 0;
488 else if (!strcmp(value, "high"))
489 pq = 2;
490 else
491 pq = 1;
de4912b2
MS
492
493 if ((value = getenv("IPP_PRINT_COLOR_MODE")) == NULL)
494 value = getenv("IPP_PRINT_COLOR_MODE_DEFAULT");
495
496 if (value && !strcmp(value, "monochrome"))
497 pcm = 0;
498
499 num_presets = ppd_cache->num_presets[pcm][pq];
500 presets = ppd_cache->presets[pcm][pq];
501
502 for (i = 0; i < num_presets; i ++)
503 num_options = cupsAddOption(presets[i].name, presets[i].value, num_options, options);
504 }
505
506 /*
507 * Mark the PPD with the options...
508 */
509
510 ppdMarkDefaults(ppd);
511 cupsMarkOptions(ppd, num_options, *options);
512 }
513#endif /* !CUPS_LITE */
514
515 cupsFreeOptions(num_media_col, media_col);
516
517 return (num_options);
518}
519
520
521/*
522 * 'jpeg_to_ps()' - Convert a JPEG file to PostScript.
523 */
524
525static int /* O - Exit status */
526jpeg_to_ps(const char *filename, /* I - Filename */
7331c1b6 527 int copies) /* I - Number of copies */
de4912b2 528{
7331c1b6
MS
529 int fd; /* JPEG file descriptor */
530 int copy; /* Current copy */
531 int width = 0, /* Width */
532 height = 0, /* Height */
533 depth = 0, /* Number of colors */
534 length; /* Length of marker */
535 unsigned char buffer[65536], /* Copy buffer */
536 *bufptr, /* Pointer info buffer */
537 *bufend; /* End of buffer */
538 ssize_t bytes; /* Bytes in buffer */
539 const char *decode; /* Decode array */
540 float page_left, /* Left margin */
541 page_top, /* Top margin */
542 page_width, /* Page width in points */
543 page_height, /* Page heigth in points */
544 x_factor, /* X image scaling factor */
545 y_factor, /* Y image scaling factor */
546 page_scaling; /* Image scaling factor */
547#if !CUPS_LITE
548 ppd_size_t *page_size; /* Current page size */
549#endif /* !CUPS_LITE */
550
551
552 /*
553 * Open the input file...
554 */
555
556 if (filename)
557 {
558 if ((fd = open(filename, O_RDONLY)) < 0)
559 {
560 fprintf(stderr, "ERROR: Unable to open \"%s\": %s\n", filename, strerror(errno));
561 return (1);
562 }
563 }
564 else
565 {
566 fd = 0;
567 copies = 1;
568 }
569
570 /*
571 * Read the JPEG dimensions...
572 */
573
574 bytes = read(fd, buffer, sizeof(buffer));
575
576 if (bytes < 3 || memcmp(buffer, "\377\330\377", 3))
577 {
578 fputs("ERROR: Not a JPEG image.\n", stderr);
579
580 if (fd > 0)
581 close(fd);
582
583 return (1);
584 }
585
586 for (bufptr = buffer + 2, bufend = buffer + bytes; bufptr < bufend;)
587 {
588 /*
589 * Scan the file for a SOFn marker, then we can get the dimensions...
590 */
591
592 if (*bufptr == 0xff)
593 {
594 bufptr ++;
595
596 if (bufptr >= bufend)
597 {
598 /*
599 * If we are at the end of the current buffer, re-fill and continue...
600 */
601
602 if ((bytes = read(fd, buffer, sizeof(buffer))) <= 0)
603 break;
604
605 bufptr = buffer;
606 bufend = buffer + bytes;
607 }
608
609 if (*bufptr == 0xff)
610 continue;
611
612 if ((bufptr + 16) >= bufend)
613 {
614 /*
615 * Read more of the marker...
616 */
617
618 bytes = bufend - bufptr;
619
620 memmove(buffer, bufptr, (size_t)bytes);
621 bufptr = buffer;
622 bufend = buffer + bytes;
623
624 if ((bytes = read(fd, bufend, sizeof(buffer) - (size_t)bytes)) <= 0)
625 break;
626
627 bufend += bytes;
628 }
629
630 length = (size_t)((bufptr[1] << 8) | bufptr[2]);
631
632 if ((*bufptr >= 0xc0 && *bufptr <= 0xc3) || (*bufptr >= 0xc5 && *bufptr <= 0xc7) || (*bufptr >= 0xc9 && *bufptr <= 0xcb) || (*bufptr >= 0xcd && *bufptr <= 0xcf))
633 {
634 /*
635 * SOFn marker, look for dimensions...
636 */
637
638 width = (bufptr[6] << 8) | bufptr[7];
639 height = (bufptr[4] << 8) | bufptr[5];
640 depth = bufptr[8];
641 break;
642 }
643
644 /*
645 * Skip past this marker...
646 */
647
648 bufptr ++;
649 bytes = bufend - bufptr;
650
651 while (length >= bytes)
652 {
653 length -= bytes;
654
655 if ((bytes = read(fd, buffer, sizeof(buffer))) <= 0)
656 break;
657
658 bufptr = buffer;
659 bufend = buffer + bytes;
660 }
661
662 if (length > bytes)
663 break;
664
665 bufptr += length;
666 }
667 }
668
669 fprintf(stderr, "DEBUG: JPEG dimensions are %dx%dx%d\n", width, height, depth);
670
671 if (width <= 0 || height <= 0 || depth <= 0)
672 {
673 fputs("ERROR: No valid image data in JPEG file.\n", stderr);
674
675 if (fd > 0)
676 close(fd);
677
678 return (1);
679 }
680
58b64171
MS
681 fputs("ATTR: job-impressions=1\n", stderr);
682
7331c1b6
MS
683 /*
684 * Figure out the dimensions/scaling of the final image...
685 */
686
687#if CUPS_LITE
688 page_left = 18.0f;
689 page_top = 756.0f;
690 page_width = 576.0f;
691 page_height = 720.0f;
692
693#else
694 if ((page_size = ppdPageSize(ppd, NULL)) != NULL)
695 {
696 page_left = page_size->left;
697 page_top = page_size->top;
698 page_width = page_size->right - page_left;
699 page_height = page_top - page_size->bottom;
700 }
701 else
702 {
703 page_left = 18.0f;
704 page_top = 756.0f;
705 page_width = 576.0f;
706 page_height = 720.0f;
707 }
708#endif /* CUPS_LITE */
709
710 fprintf(stderr, "DEBUG: page_left=%.2f, page_top=%.2f, page_width=%.2f, page_height=%.2f\n", page_left, page_top, page_width, page_height);
711
712 /* TODO: Support orientation/rotation, different print-scaling modes */
713 x_factor = page_width / width;
714 y_factor = page_height / height;
715
716 if (x_factor > y_factor && (height * x_factor) <= page_height)
717 page_scaling = x_factor;
718 else
719 page_scaling = y_factor;
720
721 fprintf(stderr, "DEBUG: Scaled dimensions are %.2fx%.2f\n", width * page_scaling, height * page_scaling);
722
723 /*
724 * Write pages...
725 */
726
727 dsc_header(copies);
728
729 for (copy = 1; copy <= copies; copy ++)
730 {
731 dsc_page(copy);
de4912b2 732
7331c1b6
MS
733 if (depth == 1)
734 {
735 puts("/DeviceGray setcolorspace");
736 decode = "0 1";
737 }
738 else if (depth == 3)
739 {
740 puts("/DeviceRGB setcolorspace");
741 decode = "0 1 0 1 0 1";
742 }
743 else
744 {
745 puts("/DeviceCMYK setcolorspace");
746 decode = "0 1 0 1 0 1 0 1";
747 }
de4912b2 748
7331c1b6
MS
749 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);
750 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);
751
752 if (fd > 0)
753 lseek(fd, 0, SEEK_SET);
754
755 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
756 ascii85(buffer, (int)bytes, 0);
757
758 ascii85(buffer, 0, 1);
759
760 puts("grestore showpage");
761 }
de4912b2
MS
762
763 dsc_trailer(0);
764
765 return (0);
766}
767
768
769/*
770 * 'pdf_to_ps()' - Convert a PDF file to PostScript.
771 */
772
773static int /* O - Exit status */
774pdf_to_ps(const char *filename, /* I - Filename */
775 int copies, /* I - Number of copies */
776 int num_options, /* I - Number of options */
777 cups_option_t *options) /* I - options */
778{
779 int status; /* Exit status */
780 char tempfile[1024]; /* Temporary file */
781 int tempfd; /* Temporary file descriptor */
782 int pid; /* Process ID */
783 const char *pdf_argv[8]; /* Command-line arguments */
784 char pdf_options[1024]; /* Options */
785 const char *value; /* Option value */
b13a9a96
MS
786 const char *job_id, /* job-id value */
787 *job_name; /* job-name value */
de4912b2
MS
788
789
790 /*
791 * Create a temporary file for the PostScript version...
792 */
793
794 if ((tempfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
795 {
796 fprintf(stderr, "ERROR: Unable to create temporary file: %s\n", strerror(errno));
797 return (1);
798 }
799
800 /*
801 * Run cgpdftops or pdftops in the filter directory...
802 */
803
804 if ((value = cupsGetOption("PageSize", num_options, options)) != NULL)
805 snprintf(pdf_options, sizeof(pdf_options), "PageSize=%s", value);
806 else
807 pdf_options[0] = '\0';
808
b13a9a96
MS
809 if ((job_id = getenv("IPP_JOB_ID")) == NULL)
810 job_id = "42";
811 if ((job_name = getenv("IPP_JOB_NAME")) == NULL)
812 job_name = "untitled";
813
de4912b2 814 pdf_argv[0] = "printer";
b13a9a96 815 pdf_argv[1] = job_id;
de4912b2 816 pdf_argv[2] = cupsUser();
b13a9a96 817 pdf_argv[3] = job_name;
de4912b2
MS
818 pdf_argv[4] = "1";
819 pdf_argv[5] = pdf_options;
820 pdf_argv[6] = filename;
821 pdf_argv[7] = NULL;
822
823 if ((pid = fork()) == 0)
824 {
825 /*
826 * Child comes here...
827 */
828
829 close(1);
830 dup2(tempfd, 1);
831 close(tempfd);
832
833 execv(PDFTOPS, (char * const *)pdf_argv);
834 exit(errno);
835 }
836 else if (pid < 0)
837 {
838 /*
839 * Unable to fork process...
840 */
841
842 perror("ERROR: Unable to start PDF filter");
843
844 close(tempfd);
845 unlink(tempfile);
846
847 return (1);
848 }
849 else
850 {
851 /*
852 * Wait for the filter to complete...
853 */
854
855 close(tempfd);
856
857# ifdef HAVE_WAITPID
858 while (waitpid(pid, &status, 0) < 0);
859# else
860 while (wait(&status) < 0);
861# endif /* HAVE_WAITPID */
862
863 if (status)
864 {
865 if (WIFEXITED(status))
866 fprintf(stderr, "ERROR: " PDFTOPS " exited with status %d.\n", WEXITSTATUS(status));
867 else
868 fprintf(stderr, "ERROR: " PDFTOPS " terminated with signal %d.\n", WTERMSIG(status));
869
870 unlink(tempfile);
871 return (1);
872 }
873 }
874
875 /*
876 * Copy the PostScript output from the command...
877 */
878
7331c1b6 879 status = ps_to_ps(tempfile, copies);
de4912b2
MS
880
881 unlink(tempfile);
882
883 return (status);
884}
885
886
887/*
888 * 'ps_to_ps()' - Copy PostScript to the standard output.
889 */
890
891static int /* O - Exit status */
892ps_to_ps(const char *filename, /* I - Filename */
7331c1b6 893 int copies) /* I - Number of copies */
de4912b2 894{
7331c1b6
MS
895 FILE *fp; /* File to read from */
896 int copy, /* Current copy */
897 page, /* Current page number */
898 num_pages = 0, /* Total number of pages */
899 first_page, /* First page */
900 last_page; /* Last page */
901 const char *page_ranges; /* page-ranges option */
902 long first_pos = -1; /* Offset for first page */
903 char line[1024]; /* Line from file */
de4912b2 904
de4912b2 905
7331c1b6
MS
906 /*
907 * Open the print file...
908 */
de4912b2 909
7331c1b6
MS
910 if (filename)
911 {
912 if ((fp = fopen(filename, "rb")) == NULL)
913 {
914 fprintf(stderr, "ERROR: Unable to open \"%s\": %s\n", filename, strerror(errno));
915 return (1);
916 }
917 }
918 else
919 {
920 copies = 1;
921 fp = stdin;
922 }
923
924 /*
925 * Check page ranges...
926 */
927
928 if ((page_ranges = getenv("IPP_PAGE_RANGES")) != NULL)
929 {
930 if (sscanf(page_ranges, "%d-%d", &first_page, &last_page) != 2)
931 {
932 first_page = 1;
933 last_page = INT_MAX;
934 }
935 }
936 else
937 {
938 first_page = 1;
939 last_page = INT_MAX;
940 }
941
942 /*
943 * Write the PostScript header for the document...
944 */
945
946 dsc_header(0);
947
b13a9a96
MS
948 first_pos = 0;
949
7331c1b6
MS
950 while (fgets(line, sizeof(line), fp))
951 {
952 if (!strncmp(line, "%%Page:", 7))
953 break;
954
b13a9a96
MS
955 first_pos = ftell(fp);
956
7331c1b6
MS
957 if (line[0] != '%')
958 fputs(line, stdout);
959 }
960
961 if (!strncmp(line, "%%Page:", 7))
962 {
7331c1b6
MS
963 for (copy = 0; copy < copies; copy ++)
964 {
965 int copy_page = 0; /* Do we copy the page data? */
966
b13a9a96 967 if (fp != stdin)
7331c1b6
MS
968 fseek(fp, first_pos, SEEK_SET);
969
970 page = 0;
971 while (fgets(line, sizeof(line), fp))
972 {
973 if (!strncmp(line, "%%Page:", 7))
974 {
975 page ++;
976 copy_page = page >= first_page && page <= last_page;
977
978 if (copy_page)
979 {
980 num_pages ++;
981 dsc_page(num_pages);
982 }
983 }
984 else if (copy_page)
985 fputs(line, stdout);
986 }
987 }
988 }
989
990 dsc_trailer(num_pages);
991
58b64171
MS
992 fprintf(stderr, "ATTR: job-impressions=%d\n", num_pages / copies);
993
7331c1b6
MS
994 if (fp != stdin)
995 fclose(fp);
de4912b2
MS
996
997 return (0);
998}
999
1000
1001/*
1002 * 'raster_to_ps()' - Convert PWG Raster/Apple Raster to PostScript.
75c93cc2
MS
1003 *
1004 * The current implementation locally-decodes the raster data and then writes
1005 * whole, non-blank lines as 1-line high images with base-85 encoding, resulting
1006 * in between 10 and 20 times larger output. A alternate implementation (if it
1007 * is deemed necessary) would be to implement a PostScript decode procedure that
1008 * handles the modified packbits decompression so that we just have the base-85
1009 * encoding overhead (25%). Furthermore, Level 3 PostScript printers also
1010 * support Flate compression.
1011 *
1012 * That said, the most efficient path with the highest quality is for Clients
1013 * to supply PDF files and us to use the existing PDF to PostScript conversion
1014 * filters.
de4912b2
MS
1015 */
1016
1017static int /* O - Exit status */
7331c1b6 1018raster_to_ps(const char *filename) /* I - Filename */
de4912b2 1019{
7331c1b6
MS
1020 int fd; /* Input file */
1021 cups_raster_t *ras; /* Raster stream */
1022 cups_page_header2_t header; /* Page header */
1023 int page = 0; /* Current page */
1024 unsigned y; /* Current line */
1025 unsigned char *line; /* Line buffer */
1026 unsigned char white; /* White color */
75c93cc2 1027 const char *decode; /* Image decode array */
de4912b2 1028
de4912b2 1029
7331c1b6
MS
1030 /*
1031 * Open the input file...
1032 */
de4912b2 1033
7331c1b6
MS
1034 if (filename)
1035 {
1036 if ((fd = open(filename, O_RDONLY)) < 0)
1037 {
1038 fprintf(stderr, "ERROR: Unable to open \"%s\": %s\n", filename, strerror(errno));
1039 return (1);
1040 }
1041 }
1042 else
1043 {
1044 fd = 0;
1045 }
1046
1047 /*
1048 * Open the raster stream and send pages...
1049 */
1050
1051 if ((ras = cupsRasterOpen(fd, CUPS_RASTER_READ)) == NULL)
1052 {
1053 fputs("ERROR: Unable to read raster data, aborting.\n", stderr);
1054 return (1);
1055 }
1056
7331c1b6
MS
1057 dsc_header(0);
1058
1059 while (cupsRasterReadHeader2(ras, &header))
1060 {
1061 page ++;
1062
75c93cc2
MS
1063 fprintf(stderr, "DEBUG: Page %d: %ux%ux%u\n", page, header.cupsWidth, header.cupsHeight, header.cupsBitsPerPixel);
1064
7331c1b6
MS
1065 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)
1066 {
1067 fputs("ERROR: Unsupported color space, aborting.\n", stderr);
1068 break;
1069 }
1070 else if (header.cupsBitsPerColor != 1 && header.cupsBitsPerColor != 8)
1071 {
1072 fputs("ERROR: Unsupported bit depth, aborting.\n", stderr);
1073 break;
1074 }
1075
1076 line = malloc(header.cupsBytesPerLine);
1077
1078 dsc_page(page);
1079
1080 puts("gsave");
1081 printf("%.6f %.6f scale\n", 72.0f / header.HWResolution[0], 72.0f / header.HWResolution[1]);
1082
7331c1b6
MS
1083 switch (header.cupsColorSpace)
1084 {
1085 case CUPS_CSPACE_W :
1086 case CUPS_CSPACE_SW :
75c93cc2 1087 decode = "0 1";
7331c1b6
MS
1088 puts("/DeviceGray setcolorspace");
1089 white = 255;
1090 break;
1091
1092 case CUPS_CSPACE_K :
75c93cc2 1093 decode = "0 1";
7331c1b6
MS
1094 puts("/DeviceGray setcolorspace");
1095 white = 0;
1096 break;
1097
1098 default :
75c93cc2 1099 decode = "0 1 0 1 0 1";
7331c1b6
MS
1100 puts("/DeviceRGB setcolorspace");
1101 white = 255;
1102 break;
1103 }
1104
75c93cc2
MS
1105 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);
1106
1107 for (y = header.cupsHeight; y > 0; y --)
7331c1b6
MS
1108 {
1109 if (cupsRasterReadPixels(ras, line, header.cupsBytesPerLine))
1110 {
1111 if (line[0] != white || memcmp(line, line + 1, header.cupsBytesPerLine - 1))
1112 {
75c93cc2 1113 printf("%d L\n", y - 1);
7331c1b6
MS
1114 ascii85(line, (int)header.cupsBytesPerLine, 1);
1115 }
1116 }
1117 else
1118 break;
1119 }
1120
75c93cc2
MS
1121 fprintf(stderr, "DEBUG: y=%d at end...\n", y);
1122
1123 puts("grestore grestore");
7331c1b6
MS
1124 puts("showpage");
1125
1126 free(line);
1127 }
1128
1129 cupsRasterClose(ras);
1130
1131 dsc_trailer(page);
6641bd0d 1132
58b64171
MS
1133 fprintf(stderr, "ATTR: job-impressions=%d\n", page);
1134
6641bd0d
MS
1135 return (0);
1136}
de4912b2
MS
1137
1138