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