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