| /* |
| * CUPS raster to PWG raster format filter for CUPS. |
| * |
| * Copyright © 2011, 2014-2017 Apple Inc. |
| * |
| * Licensed under Apache License v2.0. See the file "LICENSE" for more |
| * information. |
| */ |
| |
| /* |
| * Include necessary headers... |
| */ |
| |
| #include <cups/cups-private.h> |
| #include <cups/ppd-private.h> |
| #include <cups/raster.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| |
| |
| /* |
| * 'main()' - Main entry for filter. |
| */ |
| |
| int /* O - Exit status */ |
| main(int argc, /* I - Number of command-line args */ |
| char *argv[]) /* I - Command-line arguments */ |
| { |
| const char *final_content_type; |
| /* FINAL_CONTENT_TYPE env var */ |
| int fd; /* Raster file */ |
| cups_raster_t *inras, /* Input raster stream */ |
| *outras; /* Output raster stream */ |
| cups_page_header2_t inheader, /* Input raster page header */ |
| outheader; /* Output raster page header */ |
| unsigned y; /* Current line */ |
| unsigned char *line; /* Line buffer */ |
| unsigned page = 0, /* Current page */ |
| page_width, /* Actual page width */ |
| page_height, /* Actual page height */ |
| page_top, /* Top margin */ |
| page_bottom, /* Bottom margin */ |
| page_left, /* Left margin */ |
| linesize, /* Bytes per line */ |
| lineoffset; /* Offset into line */ |
| unsigned char white; /* White pixel */ |
| ppd_file_t *ppd; /* PPD file */ |
| ppd_attr_t *back; /* cupsBackSide attribute */ |
| _ppd_cache_t *cache; /* PPD cache */ |
| pwg_size_t *pwg_size; /* PWG media size */ |
| pwg_media_t *pwg_media; /* PWG media name */ |
| int num_options; /* Number of options */ |
| cups_option_t *options = NULL;/* Options */ |
| const char *val; /* Option value */ |
| |
| |
| if (argc < 6 || argc > 7) |
| { |
| puts("Usage: rastertopwg job user title copies options [filename]"); |
| return (1); |
| } |
| else if (argc == 7) |
| { |
| if ((fd = open(argv[6], O_RDONLY)) < 0) |
| { |
| perror("ERROR: Unable to open print file"); |
| return (1); |
| } |
| } |
| else |
| fd = 0; |
| |
| if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL) |
| final_content_type = "image/pwg-raster"; |
| |
| inras = cupsRasterOpen(fd, CUPS_RASTER_READ); |
| outras = cupsRasterOpen(1, !strcmp(final_content_type, "image/pwg-raster") ? CUPS_RASTER_WRITE_PWG : CUPS_RASTER_WRITE_APPLE); |
| |
| ppd = ppdOpenFile(getenv("PPD")); |
| back = ppdFindAttr(ppd, "cupsBackSide", NULL); |
| |
| num_options = cupsParseOptions(argv[5], 0, &options); |
| |
| ppdMarkDefaults(ppd); |
| cupsMarkOptions(ppd, num_options, options); |
| |
| cache = ppd ? ppd->cache : NULL; |
| |
| while (cupsRasterReadHeader2(inras, &inheader)) |
| { |
| /* |
| * Show page device dictionary... |
| */ |
| |
| fprintf(stderr, "DEBUG: Duplex = %d\n", inheader.Duplex); |
| fprintf(stderr, "DEBUG: HWResolution = [ %d %d ]\n", inheader.HWResolution[0], inheader.HWResolution[1]); |
| fprintf(stderr, "DEBUG: ImagingBoundingBox = [ %d %d %d %d ]\n", inheader.ImagingBoundingBox[0], inheader.ImagingBoundingBox[1], inheader.ImagingBoundingBox[2], inheader.ImagingBoundingBox[3]); |
| fprintf(stderr, "DEBUG: Margins = [ %d %d ]\n", inheader.Margins[0], inheader.Margins[1]); |
| fprintf(stderr, "DEBUG: ManualFeed = %d\n", inheader.ManualFeed); |
| fprintf(stderr, "DEBUG: MediaPosition = %d\n", inheader.MediaPosition); |
| fprintf(stderr, "DEBUG: NumCopies = %d\n", inheader.NumCopies); |
| fprintf(stderr, "DEBUG: Orientation = %d\n", inheader.Orientation); |
| fprintf(stderr, "DEBUG: PageSize = [ %d %d ]\n", inheader.PageSize[0], inheader.PageSize[1]); |
| fprintf(stderr, "DEBUG: cupsWidth = %d\n", inheader.cupsWidth); |
| fprintf(stderr, "DEBUG: cupsHeight = %d\n", inheader.cupsHeight); |
| fprintf(stderr, "DEBUG: cupsMediaType = %d\n", inheader.cupsMediaType); |
| fprintf(stderr, "DEBUG: cupsBitsPerColor = %d\n", inheader.cupsBitsPerColor); |
| fprintf(stderr, "DEBUG: cupsBitsPerPixel = %d\n", inheader.cupsBitsPerPixel); |
| fprintf(stderr, "DEBUG: cupsBytesPerLine = %d\n", inheader.cupsBytesPerLine); |
| fprintf(stderr, "DEBUG: cupsColorOrder = %d\n", inheader.cupsColorOrder); |
| fprintf(stderr, "DEBUG: cupsColorSpace = %d\n", inheader.cupsColorSpace); |
| fprintf(stderr, "DEBUG: cupsCompression = %d\n", inheader.cupsCompression); |
| |
| /* |
| * Compute the real raster size... |
| */ |
| |
| page ++; |
| |
| fprintf(stderr, "PAGE: %d %d\n", page, inheader.NumCopies); |
| |
| page_width = (unsigned)(inheader.cupsPageSize[0] * inheader.HWResolution[0] / 72.0); |
| page_height = (unsigned)(inheader.cupsPageSize[1] * inheader.HWResolution[1] / 72.0); |
| page_left = (unsigned)(inheader.cupsImagingBBox[0] * inheader.HWResolution[0] / 72.0); |
| page_bottom = (unsigned)(inheader.cupsImagingBBox[1] * inheader.HWResolution[1] / 72.0); |
| page_top = page_height - page_bottom - inheader.cupsHeight; |
| linesize = (page_width * inheader.cupsBitsPerPixel + 7) / 8; |
| lineoffset = page_left * inheader.cupsBitsPerPixel / 8; /* Round down */ |
| |
| if (page_left > page_width || page_top > page_height || page_bottom > page_height) |
| { |
| _cupsLangPrintFilter(stderr, "ERROR", _("Unsupported raster data.")); |
| fprintf(stderr, "DEBUG: Bad bottom/left/top margin on page %d.\n", page); |
| return (1); |
| } |
| |
| switch (inheader.cupsColorSpace) |
| { |
| case CUPS_CSPACE_W : |
| case CUPS_CSPACE_RGB : |
| case CUPS_CSPACE_SW : |
| case CUPS_CSPACE_SRGB : |
| case CUPS_CSPACE_ADOBERGB : |
| white = 255; |
| break; |
| |
| case CUPS_CSPACE_K : |
| case CUPS_CSPACE_CMYK : |
| case CUPS_CSPACE_DEVICE1 : |
| case CUPS_CSPACE_DEVICE2 : |
| case CUPS_CSPACE_DEVICE3 : |
| case CUPS_CSPACE_DEVICE4 : |
| case CUPS_CSPACE_DEVICE5 : |
| case CUPS_CSPACE_DEVICE6 : |
| case CUPS_CSPACE_DEVICE7 : |
| case CUPS_CSPACE_DEVICE8 : |
| case CUPS_CSPACE_DEVICE9 : |
| case CUPS_CSPACE_DEVICEA : |
| case CUPS_CSPACE_DEVICEB : |
| case CUPS_CSPACE_DEVICEC : |
| case CUPS_CSPACE_DEVICED : |
| case CUPS_CSPACE_DEVICEE : |
| case CUPS_CSPACE_DEVICEF : |
| white = 0; |
| break; |
| |
| default : |
| _cupsLangPrintFilter(stderr, "ERROR", _("Unsupported raster data.")); |
| fprintf(stderr, "DEBUG: Unsupported cupsColorSpace %d on page %d.\n", |
| inheader.cupsColorSpace, page); |
| return (1); |
| } |
| |
| if (inheader.cupsColorOrder != CUPS_ORDER_CHUNKED) |
| { |
| _cupsLangPrintFilter(stderr, "ERROR", _("Unsupported raster data.")); |
| fprintf(stderr, "DEBUG: Unsupported cupsColorOrder %d on page %d.\n", |
| inheader.cupsColorOrder, page); |
| return (1); |
| } |
| |
| if (inheader.cupsBitsPerPixel != 1 && |
| inheader.cupsBitsPerColor != 8 && inheader.cupsBitsPerColor != 16) |
| { |
| _cupsLangPrintFilter(stderr, "ERROR", _("Unsupported raster data.")); |
| fprintf(stderr, "DEBUG: Unsupported cupsBitsPerColor %d on page %d.\n", |
| inheader.cupsBitsPerColor, page); |
| return (1); |
| } |
| |
| memcpy(&outheader, &inheader, sizeof(outheader)); |
| outheader.cupsWidth = page_width; |
| outheader.cupsHeight = page_height; |
| outheader.cupsBytesPerLine = linesize; |
| |
| outheader.cupsInteger[14] = 0; /* VendorIdentifier */ |
| outheader.cupsInteger[15] = 0; /* VendorLength */ |
| |
| if ((val = cupsGetOption("print-content-optimize", num_options, |
| options)) != NULL) |
| { |
| if (!strcmp(val, "automatic")) |
| strlcpy(outheader.OutputType, "Automatic", |
| sizeof(outheader.OutputType)); |
| else if (!strcmp(val, "graphics")) |
| strlcpy(outheader.OutputType, "Graphics", sizeof(outheader.OutputType)); |
| else if (!strcmp(val, "photo")) |
| strlcpy(outheader.OutputType, "Photo", sizeof(outheader.OutputType)); |
| else if (!strcmp(val, "text")) |
| strlcpy(outheader.OutputType, "Text", sizeof(outheader.OutputType)); |
| else if (!strcmp(val, "text-and-graphics")) |
| strlcpy(outheader.OutputType, "TextAndGraphics", |
| sizeof(outheader.OutputType)); |
| else |
| { |
| fputs("DEBUG: Unsupported print-content-optimize value.\n", stderr); |
| outheader.OutputType[0] = '\0'; |
| } |
| } |
| |
| if ((val = cupsGetOption("print-quality", num_options, options)) != NULL) |
| { |
| unsigned quality = (unsigned)atoi(val); /* print-quality value */ |
| |
| if (quality >= IPP_QUALITY_DRAFT && quality <= IPP_QUALITY_HIGH) |
| outheader.cupsInteger[8] = quality; |
| else |
| { |
| fprintf(stderr, "DEBUG: Unsupported print-quality %d.\n", quality); |
| outheader.cupsInteger[8] = 0; |
| } |
| } |
| |
| if ((val = cupsGetOption("print-rendering-intent", num_options, |
| options)) != NULL) |
| { |
| if (!strcmp(val, "absolute")) |
| strlcpy(outheader.cupsRenderingIntent, "Absolute", |
| sizeof(outheader.cupsRenderingIntent)); |
| else if (!strcmp(val, "automatic")) |
| strlcpy(outheader.cupsRenderingIntent, "Automatic", |
| sizeof(outheader.cupsRenderingIntent)); |
| else if (!strcmp(val, "perceptual")) |
| strlcpy(outheader.cupsRenderingIntent, "Perceptual", |
| sizeof(outheader.cupsRenderingIntent)); |
| else if (!strcmp(val, "relative")) |
| strlcpy(outheader.cupsRenderingIntent, "Relative", |
| sizeof(outheader.cupsRenderingIntent)); |
| else if (!strcmp(val, "relative-bpc")) |
| strlcpy(outheader.cupsRenderingIntent, "RelativeBpc", |
| sizeof(outheader.cupsRenderingIntent)); |
| else if (!strcmp(val, "saturation")) |
| strlcpy(outheader.cupsRenderingIntent, "Saturation", |
| sizeof(outheader.cupsRenderingIntent)); |
| else |
| { |
| fputs("DEBUG: Unsupported print-rendering-intent value.\n", stderr); |
| outheader.cupsRenderingIntent[0] = '\0'; |
| } |
| } |
| |
| if (inheader.cupsPageSizeName[0] && |
| (pwg_size = _ppdCacheGetSize(cache, inheader.cupsPageSizeName)) != NULL) |
| { |
| strlcpy(outheader.cupsPageSizeName, pwg_size->map.pwg, |
| sizeof(outheader.cupsPageSizeName)); |
| } |
| else |
| { |
| pwg_media = pwgMediaForSize((int)(2540.0 * inheader.cupsPageSize[0] / 72.0), |
| (int)(2540.0 * inheader.cupsPageSize[1] / 72.0)); |
| |
| if (pwg_media) |
| strlcpy(outheader.cupsPageSizeName, pwg_media->pwg, |
| sizeof(outheader.cupsPageSizeName)); |
| else |
| { |
| fprintf(stderr, "DEBUG: Unsupported PageSize %.2fx%.2f.\n", |
| inheader.cupsPageSize[0], inheader.cupsPageSize[1]); |
| outheader.cupsPageSizeName[0] = '\0'; |
| } |
| } |
| |
| if (inheader.Duplex && !(page & 1) && |
| back && _cups_strcasecmp(back->value, "Normal")) |
| { |
| if (_cups_strcasecmp(back->value, "Flipped")) |
| { |
| if (inheader.Tumble) |
| { |
| outheader.cupsInteger[1] = ~0U;/* CrossFeedTransform */ |
| outheader.cupsInteger[2] = 1; /* FeedTransform */ |
| |
| outheader.cupsInteger[3] = page_width - page_left - |
| inheader.cupsWidth; |
| /* ImageBoxLeft */ |
| outheader.cupsInteger[4] = page_top; |
| /* ImageBoxTop */ |
| outheader.cupsInteger[5] = page_width - page_left; |
| /* ImageBoxRight */ |
| outheader.cupsInteger[6] = page_height - page_bottom; |
| /* ImageBoxBottom */ |
| } |
| else |
| { |
| outheader.cupsInteger[1] = 1; /* CrossFeedTransform */ |
| outheader.cupsInteger[2] = ~0U;/* FeedTransform */ |
| |
| outheader.cupsInteger[3] = page_left; |
| /* ImageBoxLeft */ |
| outheader.cupsInteger[4] = page_bottom; |
| /* ImageBoxTop */ |
| outheader.cupsInteger[5] = page_left + inheader.cupsWidth; |
| /* ImageBoxRight */ |
| outheader.cupsInteger[6] = page_height - page_top; |
| /* ImageBoxBottom */ |
| } |
| } |
| else if (_cups_strcasecmp(back->value, "ManualTumble")) |
| { |
| if (inheader.Tumble) |
| { |
| outheader.cupsInteger[1] = ~0U;/* CrossFeedTransform */ |
| outheader.cupsInteger[2] = ~0U;/* FeedTransform */ |
| |
| outheader.cupsInteger[3] = page_width - page_left - |
| inheader.cupsWidth; |
| /* ImageBoxLeft */ |
| outheader.cupsInteger[4] = page_bottom; |
| /* ImageBoxTop */ |
| outheader.cupsInteger[5] = page_width - page_left; |
| /* ImageBoxRight */ |
| outheader.cupsInteger[6] = page_height - page_top; |
| /* ImageBoxBottom */ |
| } |
| else |
| { |
| outheader.cupsInteger[1] = 1; /* CrossFeedTransform */ |
| outheader.cupsInteger[2] = 1; /* FeedTransform */ |
| |
| outheader.cupsInteger[3] = page_left; |
| /* ImageBoxLeft */ |
| outheader.cupsInteger[4] = page_top; |
| /* ImageBoxTop */ |
| outheader.cupsInteger[5] = page_left + inheader.cupsWidth; |
| /* ImageBoxRight */ |
| outheader.cupsInteger[6] = page_height - page_bottom; |
| /* ImageBoxBottom */ |
| } |
| } |
| else if (_cups_strcasecmp(back->value, "Rotated")) |
| { |
| if (inheader.Tumble) |
| { |
| outheader.cupsInteger[1] = ~0U;/* CrossFeedTransform */ |
| outheader.cupsInteger[2] = ~0U;/* FeedTransform */ |
| |
| outheader.cupsInteger[3] = page_width - page_left - |
| inheader.cupsWidth; |
| /* ImageBoxLeft */ |
| outheader.cupsInteger[4] = page_bottom; |
| /* ImageBoxTop */ |
| outheader.cupsInteger[5] = page_width - page_left; |
| /* ImageBoxRight */ |
| outheader.cupsInteger[6] = page_height - page_top; |
| /* ImageBoxBottom */ |
| } |
| else |
| { |
| outheader.cupsInteger[1] = 1; /* CrossFeedTransform */ |
| outheader.cupsInteger[2] = 1; /* FeedTransform */ |
| |
| outheader.cupsInteger[3] = page_left; |
| /* ImageBoxLeft */ |
| outheader.cupsInteger[4] = page_top; |
| /* ImageBoxTop */ |
| outheader.cupsInteger[5] = page_left + inheader.cupsWidth; |
| /* ImageBoxRight */ |
| outheader.cupsInteger[6] = page_height - page_bottom; |
| /* ImageBoxBottom */ |
| } |
| } |
| else |
| { |
| /* |
| * Unsupported value... |
| */ |
| |
| fputs("DEBUG: Unsupported cupsBackSide value.\n", stderr); |
| |
| outheader.cupsInteger[1] = 1; /* CrossFeedTransform */ |
| outheader.cupsInteger[2] = 1; /* FeedTransform */ |
| |
| outheader.cupsInteger[3] = page_left; |
| /* ImageBoxLeft */ |
| outheader.cupsInteger[4] = page_top; |
| /* ImageBoxTop */ |
| outheader.cupsInteger[5] = page_left + inheader.cupsWidth; |
| /* ImageBoxRight */ |
| outheader.cupsInteger[6] = page_height - page_bottom; |
| /* ImageBoxBottom */ |
| } |
| } |
| else |
| { |
| outheader.cupsInteger[1] = 1; /* CrossFeedTransform */ |
| outheader.cupsInteger[2] = 1; /* FeedTransform */ |
| |
| outheader.cupsInteger[3] = page_left; |
| /* ImageBoxLeft */ |
| outheader.cupsInteger[4] = page_top; |
| /* ImageBoxTop */ |
| outheader.cupsInteger[5] = page_left + inheader.cupsWidth; |
| /* ImageBoxRight */ |
| outheader.cupsInteger[6] = page_height - page_bottom; |
| /* ImageBoxBottom */ |
| } |
| |
| if (!cupsRasterWriteHeader2(outras, &outheader)) |
| { |
| _cupsLangPrintFilter(stderr, "ERROR", _("Error sending raster data.")); |
| fprintf(stderr, "DEBUG: Unable to write header for page %d.\n", page); |
| return (1); |
| } |
| |
| /* |
| * Copy raster data... |
| */ |
| |
| if (linesize < inheader.cupsBytesPerLine) |
| linesize = inheader.cupsBytesPerLine; |
| |
| if ((lineoffset + inheader.cupsBytesPerLine) > linesize) |
| lineoffset = linesize - inheader.cupsBytesPerLine; |
| |
| line = malloc(linesize); |
| |
| memset(line, white, linesize); |
| for (y = page_top; y > 0; y --) |
| if (!cupsRasterWritePixels(outras, line, outheader.cupsBytesPerLine)) |
| { |
| _cupsLangPrintFilter(stderr, "ERROR", _("Error sending raster data.")); |
| fprintf(stderr, "DEBUG: Unable to write line %d for page %d.\n", |
| page_top - y + 1, page); |
| return (1); |
| } |
| |
| for (y = inheader.cupsHeight; y > 0; y --) |
| { |
| if (cupsRasterReadPixels(inras, line + lineoffset, inheader.cupsBytesPerLine) != inheader.cupsBytesPerLine) |
| { |
| _cupsLangPrintFilter(stderr, "ERROR", _("Error reading raster data.")); |
| fprintf(stderr, "DEBUG: Unable to read line %d for page %d.\n", |
| inheader.cupsHeight - y + page_top + 1, page); |
| return (1); |
| } |
| |
| if (!cupsRasterWritePixels(outras, line, outheader.cupsBytesPerLine)) |
| { |
| _cupsLangPrintFilter(stderr, "ERROR", _("Error sending raster data.")); |
| fprintf(stderr, "DEBUG: Unable to write line %d for page %d.\n", |
| inheader.cupsHeight - y + page_top + 1, page); |
| return (1); |
| } |
| } |
| |
| memset(line, white, linesize); |
| for (y = page_bottom; y > 0; y --) |
| if (!cupsRasterWritePixels(outras, line, outheader.cupsBytesPerLine)) |
| { |
| _cupsLangPrintFilter(stderr, "ERROR", _("Error sending raster data.")); |
| fprintf(stderr, "DEBUG: Unable to write line %d for page %d.\n", |
| page_bottom - y + page_top + inheader.cupsHeight + 1, page); |
| return (1); |
| } |
| |
| free(line); |
| } |
| |
| cupsRasterClose(inras); |
| if (fd) |
| close(fd); |
| |
| cupsRasterClose(outras); |
| |
| return (0); |
| } |