• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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