1 /*
2  * CUPS raster to PWG raster format filter for CUPS.
3  *
4  * Copyright © 2011, 2014-2017 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 <cups/cups-private.h>
15 #include <cups/ppd-private.h>
16 #include <cups/raster.h>
17 #include <unistd.h>
18 #include <fcntl.h>
19 
20 
21 /*
22  * 'main()' - Main entry for filter.
23  */
24 
25 int					/* O - Exit status */
main(int argc,char * argv[])26 main(int  argc,				/* I - Number of command-line args */
27      char *argv[])			/* I - Command-line arguments */
28 {
29   const char		*final_content_type;
30 					/* FINAL_CONTENT_TYPE env var */
31   int			fd;		/* Raster file */
32   cups_raster_t		*inras,		/* Input raster stream */
33 			*outras;	/* Output raster stream */
34   cups_page_header2_t	inheader,	/* Input raster page header */
35 			outheader;	/* Output raster page header */
36   unsigned		y;		/* Current line */
37   unsigned char		*line;		/* Line buffer */
38   unsigned		page = 0,	/* Current page */
39 			page_width,	/* Actual page width */
40 			page_height,	/* Actual page height */
41 			page_top,	/* Top margin */
42 			page_bottom,	/* Bottom margin */
43 			page_left,	/* Left margin */
44 			linesize,	/* Bytes per line */
45 			lineoffset;	/* Offset into line */
46   unsigned char		white;		/* White pixel */
47   ppd_file_t		*ppd;		/* PPD file */
48   ppd_attr_t		*back;		/* cupsBackSide attribute */
49   _ppd_cache_t		*cache;		/* PPD cache */
50   pwg_size_t		*pwg_size;	/* PWG media size */
51   pwg_media_t		*pwg_media;	/* PWG media name */
52   int	 		num_options;	/* Number of options */
53   cups_option_t		*options = NULL;/* Options */
54   const char		*val;		/* Option value */
55 
56 
57   if (argc < 6 || argc > 7)
58   {
59     puts("Usage: rastertopwg job user title copies options [filename]");
60     return (1);
61   }
62   else if (argc == 7)
63   {
64     if ((fd = open(argv[6], O_RDONLY)) < 0)
65     {
66       perror("ERROR: Unable to open print file");
67       return (1);
68     }
69   }
70   else
71     fd = 0;
72 
73   if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
74     final_content_type = "image/pwg-raster";
75 
76   inras  = cupsRasterOpen(fd, CUPS_RASTER_READ);
77   outras = cupsRasterOpen(1, !strcmp(final_content_type, "image/pwg-raster") ? CUPS_RASTER_WRITE_PWG : CUPS_RASTER_WRITE_APPLE);
78 
79   ppd   = ppdOpenFile(getenv("PPD"));
80   back  = ppdFindAttr(ppd, "cupsBackSide", NULL);
81 
82   num_options = cupsParseOptions(argv[5], 0, &options);
83 
84   ppdMarkDefaults(ppd);
85   cupsMarkOptions(ppd, num_options, options);
86 
87   cache = ppd ? ppd->cache : NULL;
88 
89   while (cupsRasterReadHeader2(inras, &inheader))
90   {
91    /*
92     * Show page device dictionary...
93     */
94 
95     fprintf(stderr, "DEBUG: Duplex = %d\n", inheader.Duplex);
96     fprintf(stderr, "DEBUG: HWResolution = [ %d %d ]\n", inheader.HWResolution[0], inheader.HWResolution[1]);
97     fprintf(stderr, "DEBUG: ImagingBoundingBox = [ %d %d %d %d ]\n", inheader.ImagingBoundingBox[0], inheader.ImagingBoundingBox[1], inheader.ImagingBoundingBox[2], inheader.ImagingBoundingBox[3]);
98     fprintf(stderr, "DEBUG: Margins = [ %d %d ]\n", inheader.Margins[0], inheader.Margins[1]);
99     fprintf(stderr, "DEBUG: ManualFeed = %d\n", inheader.ManualFeed);
100     fprintf(stderr, "DEBUG: MediaPosition = %d\n", inheader.MediaPosition);
101     fprintf(stderr, "DEBUG: NumCopies = %d\n", inheader.NumCopies);
102     fprintf(stderr, "DEBUG: Orientation = %d\n", inheader.Orientation);
103     fprintf(stderr, "DEBUG: PageSize = [ %d %d ]\n", inheader.PageSize[0], inheader.PageSize[1]);
104     fprintf(stderr, "DEBUG: cupsWidth = %d\n", inheader.cupsWidth);
105     fprintf(stderr, "DEBUG: cupsHeight = %d\n", inheader.cupsHeight);
106     fprintf(stderr, "DEBUG: cupsMediaType = %d\n", inheader.cupsMediaType);
107     fprintf(stderr, "DEBUG: cupsBitsPerColor = %d\n", inheader.cupsBitsPerColor);
108     fprintf(stderr, "DEBUG: cupsBitsPerPixel = %d\n", inheader.cupsBitsPerPixel);
109     fprintf(stderr, "DEBUG: cupsBytesPerLine = %d\n", inheader.cupsBytesPerLine);
110     fprintf(stderr, "DEBUG: cupsColorOrder = %d\n", inheader.cupsColorOrder);
111     fprintf(stderr, "DEBUG: cupsColorSpace = %d\n", inheader.cupsColorSpace);
112     fprintf(stderr, "DEBUG: cupsCompression = %d\n", inheader.cupsCompression);
113 
114    /*
115     * Compute the real raster size...
116     */
117 
118     page ++;
119 
120     fprintf(stderr, "PAGE: %d %d\n", page, inheader.NumCopies);
121 
122     page_width  = (unsigned)(inheader.cupsPageSize[0] * inheader.HWResolution[0] / 72.0);
123     page_height = (unsigned)(inheader.cupsPageSize[1] * inheader.HWResolution[1] / 72.0);
124     page_left   = (unsigned)(inheader.cupsImagingBBox[0] * inheader.HWResolution[0] / 72.0);
125     page_bottom = (unsigned)(inheader.cupsImagingBBox[1] * inheader.HWResolution[1] / 72.0);
126     page_top    = page_height - page_bottom - inheader.cupsHeight;
127     linesize    = (page_width * inheader.cupsBitsPerPixel + 7) / 8;
128     lineoffset  = page_left * inheader.cupsBitsPerPixel / 8; /* Round down */
129 
130     if (page_left > page_width || page_top > page_height || page_bottom > page_height)
131     {
132       _cupsLangPrintFilter(stderr, "ERROR", _("Unsupported raster data."));
133       fprintf(stderr, "DEBUG: Bad bottom/left/top margin on page %d.\n", page);
134       return (1);
135     }
136 
137     switch (inheader.cupsColorSpace)
138     {
139       case CUPS_CSPACE_W :
140       case CUPS_CSPACE_RGB :
141       case CUPS_CSPACE_SW :
142       case CUPS_CSPACE_SRGB :
143       case CUPS_CSPACE_ADOBERGB :
144           white = 255;
145 	  break;
146 
147       case CUPS_CSPACE_K :
148       case CUPS_CSPACE_CMYK :
149       case CUPS_CSPACE_DEVICE1 :
150       case CUPS_CSPACE_DEVICE2 :
151       case CUPS_CSPACE_DEVICE3 :
152       case CUPS_CSPACE_DEVICE4 :
153       case CUPS_CSPACE_DEVICE5 :
154       case CUPS_CSPACE_DEVICE6 :
155       case CUPS_CSPACE_DEVICE7 :
156       case CUPS_CSPACE_DEVICE8 :
157       case CUPS_CSPACE_DEVICE9 :
158       case CUPS_CSPACE_DEVICEA :
159       case CUPS_CSPACE_DEVICEB :
160       case CUPS_CSPACE_DEVICEC :
161       case CUPS_CSPACE_DEVICED :
162       case CUPS_CSPACE_DEVICEE :
163       case CUPS_CSPACE_DEVICEF :
164           white = 0;
165 	  break;
166 
167       default :
168 	  _cupsLangPrintFilter(stderr, "ERROR", _("Unsupported raster data."));
169 	  fprintf(stderr, "DEBUG: Unsupported cupsColorSpace %d on page %d.\n",
170 	          inheader.cupsColorSpace, page);
171 	  return (1);
172     }
173 
174     if (inheader.cupsColorOrder != CUPS_ORDER_CHUNKED)
175     {
176       _cupsLangPrintFilter(stderr, "ERROR", _("Unsupported raster data."));
177       fprintf(stderr, "DEBUG: Unsupported cupsColorOrder %d on page %d.\n",
178               inheader.cupsColorOrder, page);
179       return (1);
180     }
181 
182     if (inheader.cupsBitsPerPixel != 1 &&
183         inheader.cupsBitsPerColor != 8 && inheader.cupsBitsPerColor != 16)
184     {
185       _cupsLangPrintFilter(stderr, "ERROR", _("Unsupported raster data."));
186       fprintf(stderr, "DEBUG: Unsupported cupsBitsPerColor %d on page %d.\n",
187               inheader.cupsBitsPerColor, page);
188       return (1);
189     }
190 
191     memcpy(&outheader, &inheader, sizeof(outheader));
192     outheader.cupsWidth        = page_width;
193     outheader.cupsHeight       = page_height;
194     outheader.cupsBytesPerLine = linesize;
195 
196     outheader.cupsInteger[14]  = 0;	/* VendorIdentifier */
197     outheader.cupsInteger[15]  = 0;	/* VendorLength */
198 
199     if ((val = cupsGetOption("print-content-optimize", num_options,
200                              options)) != NULL)
201     {
202       if (!strcmp(val, "automatic"))
203         strlcpy(outheader.OutputType, "Automatic",
204                 sizeof(outheader.OutputType));
205       else if (!strcmp(val, "graphics"))
206         strlcpy(outheader.OutputType, "Graphics", sizeof(outheader.OutputType));
207       else if (!strcmp(val, "photo"))
208         strlcpy(outheader.OutputType, "Photo", sizeof(outheader.OutputType));
209       else if (!strcmp(val, "text"))
210         strlcpy(outheader.OutputType, "Text", sizeof(outheader.OutputType));
211       else if (!strcmp(val, "text-and-graphics"))
212         strlcpy(outheader.OutputType, "TextAndGraphics",
213                 sizeof(outheader.OutputType));
214       else
215       {
216         fputs("DEBUG: Unsupported print-content-optimize value.\n", stderr);
217         outheader.OutputType[0] = '\0';
218       }
219     }
220 
221     if ((val = cupsGetOption("print-quality", num_options, options)) != NULL)
222     {
223       unsigned quality = (unsigned)atoi(val);		/* print-quality value */
224 
225       if (quality >= IPP_QUALITY_DRAFT && quality <= IPP_QUALITY_HIGH)
226 	outheader.cupsInteger[8] = quality;
227       else
228       {
229 	fprintf(stderr, "DEBUG: Unsupported print-quality %d.\n", quality);
230 	outheader.cupsInteger[8] = 0;
231       }
232     }
233 
234     if ((val = cupsGetOption("print-rendering-intent", num_options,
235                              options)) != NULL)
236     {
237       if (!strcmp(val, "absolute"))
238         strlcpy(outheader.cupsRenderingIntent, "Absolute",
239                 sizeof(outheader.cupsRenderingIntent));
240       else if (!strcmp(val, "automatic"))
241         strlcpy(outheader.cupsRenderingIntent, "Automatic",
242                 sizeof(outheader.cupsRenderingIntent));
243       else if (!strcmp(val, "perceptual"))
244         strlcpy(outheader.cupsRenderingIntent, "Perceptual",
245                 sizeof(outheader.cupsRenderingIntent));
246       else if (!strcmp(val, "relative"))
247         strlcpy(outheader.cupsRenderingIntent, "Relative",
248                 sizeof(outheader.cupsRenderingIntent));
249       else if (!strcmp(val, "relative-bpc"))
250         strlcpy(outheader.cupsRenderingIntent, "RelativeBpc",
251                 sizeof(outheader.cupsRenderingIntent));
252       else if (!strcmp(val, "saturation"))
253         strlcpy(outheader.cupsRenderingIntent, "Saturation",
254                 sizeof(outheader.cupsRenderingIntent));
255       else
256       {
257         fputs("DEBUG: Unsupported print-rendering-intent value.\n", stderr);
258         outheader.cupsRenderingIntent[0] = '\0';
259       }
260     }
261 
262     if (inheader.cupsPageSizeName[0] &&
263         (pwg_size = _ppdCacheGetSize(cache, inheader.cupsPageSizeName)) != NULL)
264     {
265       strlcpy(outheader.cupsPageSizeName, pwg_size->map.pwg,
266 	      sizeof(outheader.cupsPageSizeName));
267     }
268     else
269     {
270       pwg_media = pwgMediaForSize((int)(2540.0 * inheader.cupsPageSize[0] / 72.0),
271 				  (int)(2540.0 * inheader.cupsPageSize[1] / 72.0));
272 
273       if (pwg_media)
274         strlcpy(outheader.cupsPageSizeName, pwg_media->pwg,
275                 sizeof(outheader.cupsPageSizeName));
276       else
277       {
278         fprintf(stderr, "DEBUG: Unsupported PageSize %.2fx%.2f.\n",
279                 inheader.cupsPageSize[0], inheader.cupsPageSize[1]);
280         outheader.cupsPageSizeName[0] = '\0';
281       }
282     }
283 
284     if (inheader.Duplex && !(page & 1) &&
285         back && _cups_strcasecmp(back->value, "Normal"))
286     {
287       if (_cups_strcasecmp(back->value, "Flipped"))
288       {
289         if (inheader.Tumble)
290         {
291 	  outheader.cupsInteger[1] = ~0U;/* CrossFeedTransform */
292 	  outheader.cupsInteger[2] = 1;	/* FeedTransform */
293 
294 	  outheader.cupsInteger[3] = page_width - page_left -
295 	                             inheader.cupsWidth;
296 					/* ImageBoxLeft */
297 	  outheader.cupsInteger[4] = page_top;
298 					/* ImageBoxTop */
299 	  outheader.cupsInteger[5] = page_width - page_left;
300       					/* ImageBoxRight */
301 	  outheader.cupsInteger[6] = page_height - page_bottom;
302       					/* ImageBoxBottom */
303         }
304         else
305         {
306 	  outheader.cupsInteger[1] = 1;	/* CrossFeedTransform */
307 	  outheader.cupsInteger[2] = ~0U;/* FeedTransform */
308 
309 	  outheader.cupsInteger[3] = page_left;
310 					/* ImageBoxLeft */
311 	  outheader.cupsInteger[4] = page_bottom;
312 					/* ImageBoxTop */
313 	  outheader.cupsInteger[5] = page_left + inheader.cupsWidth;
314       					/* ImageBoxRight */
315 	  outheader.cupsInteger[6] = page_height - page_top;
316       					/* ImageBoxBottom */
317         }
318       }
319       else if (_cups_strcasecmp(back->value, "ManualTumble"))
320       {
321         if (inheader.Tumble)
322         {
323 	  outheader.cupsInteger[1] = ~0U;/* CrossFeedTransform */
324 	  outheader.cupsInteger[2] = ~0U;/* FeedTransform */
325 
326 	  outheader.cupsInteger[3] = page_width - page_left -
327 	                             inheader.cupsWidth;
328 					/* ImageBoxLeft */
329 	  outheader.cupsInteger[4] = page_bottom;
330 					/* ImageBoxTop */
331 	  outheader.cupsInteger[5] = page_width - page_left;
332       					/* ImageBoxRight */
333 	  outheader.cupsInteger[6] = page_height - page_top;
334       					/* ImageBoxBottom */
335         }
336         else
337         {
338 	  outheader.cupsInteger[1] = 1;	/* CrossFeedTransform */
339 	  outheader.cupsInteger[2] = 1;	/* FeedTransform */
340 
341 	  outheader.cupsInteger[3] = page_left;
342 					/* ImageBoxLeft */
343 	  outheader.cupsInteger[4] = page_top;
344 					/* ImageBoxTop */
345 	  outheader.cupsInteger[5] = page_left + inheader.cupsWidth;
346       					/* ImageBoxRight */
347 	  outheader.cupsInteger[6] = page_height - page_bottom;
348       					/* ImageBoxBottom */
349         }
350       }
351       else if (_cups_strcasecmp(back->value, "Rotated"))
352       {
353         if (inheader.Tumble)
354         {
355 	  outheader.cupsInteger[1] = ~0U;/* CrossFeedTransform */
356 	  outheader.cupsInteger[2] = ~0U;/* FeedTransform */
357 
358 	  outheader.cupsInteger[3] = page_width - page_left -
359 	                             inheader.cupsWidth;
360 					/* ImageBoxLeft */
361 	  outheader.cupsInteger[4] = page_bottom;
362 					/* ImageBoxTop */
363 	  outheader.cupsInteger[5] = page_width - page_left;
364       					/* ImageBoxRight */
365 	  outheader.cupsInteger[6] = page_height - page_top;
366       					/* ImageBoxBottom */
367         }
368         else
369         {
370 	  outheader.cupsInteger[1] = 1;	/* CrossFeedTransform */
371 	  outheader.cupsInteger[2] = 1;	/* FeedTransform */
372 
373 	  outheader.cupsInteger[3] = page_left;
374 					/* ImageBoxLeft */
375 	  outheader.cupsInteger[4] = page_top;
376 					/* ImageBoxTop */
377 	  outheader.cupsInteger[5] = page_left + inheader.cupsWidth;
378       					/* ImageBoxRight */
379 	  outheader.cupsInteger[6] = page_height - page_bottom;
380       					/* ImageBoxBottom */
381         }
382       }
383       else
384       {
385        /*
386         * Unsupported value...
387         */
388 
389         fputs("DEBUG: Unsupported cupsBackSide value.\n", stderr);
390 
391 	outheader.cupsInteger[1] = 1;	/* CrossFeedTransform */
392 	outheader.cupsInteger[2] = 1;	/* FeedTransform */
393 
394 	outheader.cupsInteger[3] = page_left;
395 					/* ImageBoxLeft */
396 	outheader.cupsInteger[4] = page_top;
397 					/* ImageBoxTop */
398 	outheader.cupsInteger[5] = page_left + inheader.cupsWidth;
399       					/* ImageBoxRight */
400 	outheader.cupsInteger[6] = page_height - page_bottom;
401       					/* ImageBoxBottom */
402       }
403     }
404     else
405     {
406       outheader.cupsInteger[1] = 1;	/* CrossFeedTransform */
407       outheader.cupsInteger[2] = 1;	/* FeedTransform */
408 
409       outheader.cupsInteger[3] = page_left;
410 					/* ImageBoxLeft */
411       outheader.cupsInteger[4] = page_top;
412 					/* ImageBoxTop */
413       outheader.cupsInteger[5] = page_left + inheader.cupsWidth;
414       					/* ImageBoxRight */
415       outheader.cupsInteger[6] = page_height - page_bottom;
416       					/* ImageBoxBottom */
417     }
418 
419     if (!cupsRasterWriteHeader2(outras, &outheader))
420     {
421       _cupsLangPrintFilter(stderr, "ERROR", _("Error sending raster data."));
422       fprintf(stderr, "DEBUG: Unable to write header for page %d.\n", page);
423       return (1);
424     }
425 
426    /*
427     * Copy raster data...
428     */
429 
430     if (linesize < inheader.cupsBytesPerLine)
431       linesize = inheader.cupsBytesPerLine;
432 
433     if ((lineoffset + inheader.cupsBytesPerLine) > linesize)
434       lineoffset = linesize - inheader.cupsBytesPerLine;
435 
436     line = malloc(linesize);
437 
438     memset(line, white, linesize);
439     for (y = page_top; y > 0; y --)
440       if (!cupsRasterWritePixels(outras, line, outheader.cupsBytesPerLine))
441       {
442 	_cupsLangPrintFilter(stderr, "ERROR", _("Error sending raster data."));
443 	fprintf(stderr, "DEBUG: Unable to write line %d for page %d.\n",
444 	        page_top - y + 1, page);
445 	return (1);
446       }
447 
448     for (y = inheader.cupsHeight; y > 0; y --)
449     {
450       if (cupsRasterReadPixels(inras, line + lineoffset, inheader.cupsBytesPerLine) != inheader.cupsBytesPerLine)
451       {
452 	_cupsLangPrintFilter(stderr, "ERROR", _("Error reading raster data."));
453 	fprintf(stderr, "DEBUG: Unable to read line %d for page %d.\n",
454 	        inheader.cupsHeight - y + page_top + 1, page);
455 	return (1);
456       }
457 
458       if (!cupsRasterWritePixels(outras, line, outheader.cupsBytesPerLine))
459       {
460 	_cupsLangPrintFilter(stderr, "ERROR", _("Error sending raster data."));
461 	fprintf(stderr, "DEBUG: Unable to write line %d for page %d.\n",
462 	        inheader.cupsHeight - y + page_top + 1, page);
463 	return (1);
464       }
465     }
466 
467     memset(line, white, linesize);
468     for (y = page_bottom; y > 0; y --)
469       if (!cupsRasterWritePixels(outras, line, outheader.cupsBytesPerLine))
470       {
471 	_cupsLangPrintFilter(stderr, "ERROR", _("Error sending raster data."));
472 	fprintf(stderr, "DEBUG: Unable to write line %d for page %d.\n",
473 	        page_bottom - y + page_top + inheader.cupsHeight + 1, page);
474 	return (1);
475       }
476 
477     free(line);
478   }
479 
480   cupsRasterClose(inras);
481   if (fd)
482     close(fd);
483 
484   cupsRasterClose(outras);
485 
486   return (0);
487 }
488