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