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 */ 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