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