diff options
Diffstat (limited to 'tools/ippeveps.c')
-rw-r--r-- | tools/ippeveps.c | 1138 |
1 files changed, 1138 insertions, 0 deletions
diff --git a/tools/ippeveps.c b/tools/ippeveps.c new file mode 100644 index 000000000..2bfc14eb4 --- /dev/null +++ b/tools/ippeveps.c @@ -0,0 +1,1138 @@ +/* + * Generic Adobe PostScript printer command for ippeveprinter/CUPS. + * + * Copyright © 2019 by Apple Inc. + * + * Licensed under Apache License v2.0. See the file "LICENSE" for more + * information. + */ + +/* + * Include necessary headers... + */ + +#include "ippevecommon.h" +#if !CUPS_LITE +# include <cups/ppd-private.h> +#endif /* !CUPS_LITE */ +#include <limits.h> + +#ifdef __APPLE__ +# define PDFTOPS CUPS_SERVERBIN "/filter/cgpdftops" +#else +# define PDFTOPS CUPS_SERVERBIN "/filter/pdftops" +#endif /* __APPLE__ */ + + +/* + * Local globals... + */ + +#if !CUPS_LITE +static ppd_file_t *ppd = NULL; /* PPD file data */ +static _ppd_cache_t *ppd_cache = NULL; + /* IPP to PPD cache data */ +#endif /* !CUPS_LITE */ + + +/* + * Local functions... + */ + +static void ascii85(const unsigned char *data, int length, int eod); +static void dsc_header(int num_pages); +static void dsc_page(int page); +static void dsc_trailer(int num_pages); +static int get_options(cups_option_t **options); +static int jpeg_to_ps(const char *filename, int copies); +static int pdf_to_ps(const char *filename, int copies, int num_options, cups_option_t *options); +static int ps_to_ps(const char *filename, int copies); +static int raster_to_ps(const char *filename); + + +/* + * 'main()' - Main entry for PostScript printer command. + */ + +int /* O - Exit status */ +main(int argc, /* I - Number of command-line arguments */ + char *argv[]) /* I - Command-line arguments */ +{ + const char *content_type, /* Content type to print */ + *ipp_copies; /* IPP_COPIES value */ + int copies; /* Number of copies */ + int num_options; /* Number of options */ + cups_option_t *options; /* Options */ + + + /* + * Get print options... + */ + + num_options = get_options(&options); + if ((ipp_copies = getenv("IPP_COPIES")) != NULL) + copies = atoi(ipp_copies); + else + copies = 1; + + /* + * Print it... + */ + + if (argc > 2) + { + fputs("ERROR: Too many arguments supplied, aborting.\n", stderr); + return (1); + } + else if ((content_type = getenv("CONTENT_TYPE")) == NULL) + { + fputs("ERROR: CONTENT_TYPE environment variable not set, aborting.\n", stderr); + return (1); + } + else if (!strcasecmp(content_type, "application/pdf")) + { + return (pdf_to_ps(argv[1], copies, num_options, options)); + } + else if (!strcasecmp(content_type, "application/postscript")) + { + return (ps_to_ps(argv[1], copies)); + } + else if (!strcasecmp(content_type, "image/jpeg")) + { + return (jpeg_to_ps(argv[1], copies)); + } + else if (!strcasecmp(content_type, "image/pwg-raster") || !strcasecmp(content_type, "image/urf")) + { + return (raster_to_ps(argv[1])); + } + else + { + fprintf(stderr, "ERROR: CONTENT_TYPE %s not supported.\n", content_type); + return (1); + } +} + + +/* + * 'ascii85()' - Write binary data using a Base85 encoding... + */ + +static void +ascii85(const unsigned char *data, /* I - Data to write */ + int length, /* I - Number of bytes to write */ + int eod) /* I - 1 if this is the end, 0 otherwise */ +{ + unsigned b = 0; /* Current 32-bit word */ + unsigned char c[5]; /* Base-85 encoded characters */ + static int col = 0; /* Column */ + static unsigned char leftdata[4]; /* Leftover data at the end */ + static int leftcount = 0; /* Size of leftover data */ + + + length += leftcount; + + while (length > 3) + { + switch (leftcount) + { + case 0 : + b = (unsigned)((((((data[0] << 8) | data[1]) << 8) | data[2]) << 8) | data[3]); + break; + case 1 : + b = (unsigned)((((((leftdata[0] << 8) | data[0]) << 8) | data[1]) << 8) | data[2]); + break; + case 2 : + b = (unsigned)((((((leftdata[0] << 8) | leftdata[1]) << 8) | data[0]) << 8) | data[1]); + break; + case 3 : + b = (unsigned)((((((leftdata[0] << 8) | leftdata[1]) << 8) | leftdata[2]) << 8) | data[0]); + break; + } + + if (col >= 76) + { + col = 0; + putchar('\n'); + } + + if (b == 0) + { + putchar('z'); + col ++; + } + else + { + c[4] = (b % 85) + '!'; + b /= 85; + c[3] = (b % 85) + '!'; + b /= 85; + c[2] = (b % 85) + '!'; + b /= 85; + c[1] = (b % 85) + '!'; + b /= 85; + c[0] = (unsigned char)(b + '!'); + + fwrite(c, 1, 5, stdout); + col += 5; + } + + data += 4 - leftcount; + length -= 4 - leftcount; + leftcount = 0; + } + + if (length > 0) + { + // Copy any remainder into the leftdata array... + if ((length - leftcount) > 0) + memcpy(leftdata + leftcount, data, (size_t)(length - leftcount)); + + memset(leftdata + length, 0, (size_t)(4 - length)); + + leftcount = length; + } + + if (eod) + { + // Do the end-of-data dance... + if (col >= 76) + { + col = 0; + putchar('\n'); + } + + if (leftcount > 0) + { + // Write the remaining bytes as needed... + b = (unsigned)((((((leftdata[0] << 8) | leftdata[1]) << 8) | leftdata[2]) << 8) | leftdata[3]); + + c[4] = (b % 85) + '!'; + b /= 85; + c[3] = (b % 85) + '!'; + b /= 85; + c[2] = (b % 85) + '!'; + b /= 85; + c[1] = (b % 85) + '!'; + b /= 85; + c[0] = (unsigned char)(b + '!'); + + fwrite(c, (size_t)(leftcount + 1), 1, stdout); + + leftcount = 0; + } + + puts("~>"); + col = 0; + } +} + + +/* + * 'dsc_header()' - Write out a standard Document Structuring Conventions + * PostScript header. + */ + +static void +dsc_header(int num_pages) /* I - Number of pages or 0 if not known */ +{ + const char *job_name = getenv("IPP_JOB_NAME"); + /* job-name value */ + + +#if !CUPS_LITE + const char *job_id = getenv("IPP_JOB_ID"); + /* job-id value */ + + ppdEmitJCL(ppd, stdout, job_id ? atoi(job_id) : 0, cupsUser(), job_name ? job_name : "Unknown"); +#endif /* !CUPS_LITE */ + + puts("%!PS-Adobe-3.0"); + puts("%%LanguageLevel: 2"); + printf("%%%%Creator: ippeveps/%d.%d.%d\n", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR, CUPS_VERSION_PATCH); + if (job_name) + { + fputs("%%Title: ", stdout); + while (*job_name) + { + if (*job_name >= 0x20 && *job_name < 0x7f) + putchar(*job_name); + else + putchar('?'); + + job_name ++; + } + putchar('\n'); + } + if (num_pages > 0) + printf("%%%%Pages: %d\n", num_pages); + else + puts("%%Pages: (atend)"); + puts("%%EndComments"); + +#if !CUPS_LITE + if (ppd) + { + puts("%%BeginProlog"); + if (ppd->patches) + { + puts("%%BeginFeature: *JobPatchFile 1"); + puts(ppd->patches); + puts("%%EndFeature"); + } + ppdEmit(ppd, stdout, PPD_ORDER_PROLOG); + puts("%%EndProlog"); + + puts("%%BeginSetup"); + ppdEmit(ppd, stdout, PPD_ORDER_DOCUMENT); + ppdEmit(ppd, stdout, PPD_ORDER_ANY); + puts("%%EndSetup"); + } +#endif /* !CUPS_LITE */ +} + + +/* + * 'dsc_page()' - Mark the start of a page. + */ + +static void +dsc_page(int page) /* I - Page numebr (1-based) */ +{ + printf("%%%%Page: (%d) %d\n", page, page); + + fprintf(stderr, "ATTR: job-impressions-completed=%d\n", page); + +#if !CUPS_LITE + if (ppd) + { + puts("%%BeginPageSetup"); + ppdEmit(ppd, stdout, PPD_ORDER_PAGE); + puts("%%EndPageSetup"); + } +#endif /* !CUPS_LITE */ +} + + +/* + * 'dsc_trailer()' - Mark the end of the document. + */ + +static void +dsc_trailer(int num_pages) /* I - Number of pages */ +{ + if (num_pages > 0) + { + puts("%%Trailer"); + printf("%%%%Pages: %d\n", num_pages); + puts("%%EOF"); + } + +#if !CUPS_LITE + if (ppd && ppd->jcl_end) + ppdEmitJCLEnd(ppd, stdout); + else +#endif /* !CUPS_LITE */ + putchar(0x04); +} + + +/* + * 'get_options()' - Get the PPD options corresponding to the IPP Job Template + * attributes. + */ + +static int /* O - Number of options */ +get_options(cups_option_t **options) /* O - Options */ +{ + int num_options = 0; /* Number of options */ + const char *value; /* Option value */ + pwg_media_t *media = NULL; /* Media mapping */ + int num_media_col = 0; /* Number of media-col values */ + cups_option_t *media_col = NULL; /* media-col values */ +#if !CUPS_LITE + const char *choice; /* PPD choice */ +#endif /* !CUPS_LITE */ + + + /* + * No options to start... + */ + + *options = NULL; + + /* + * Media... + */ + + if ((value = getenv("IPP_MEDIA")) == NULL) + if ((value = getenv("IPP_MEDIA_COL")) == NULL) + if ((value = getenv("IPP_MEDIA_DEFAULT")) == NULL) + value = getenv("IPP_MEDIA_COL_DEFAULT"); + + if (value) + { + if (*value == '{') + { + /* + * media-col value... + */ + + num_media_col = cupsParseOptions(value, 0, &media_col); + } + else + { + /* + * media value - map to media-col.media-size-name... + */ + + num_media_col = cupsAddOption("media-size-name", value, 0, &media_col); + } + } + + if ((value = cupsGetOption("media-size-name", num_media_col, media_col)) != NULL) + { + media = pwgMediaForPWG(value); + } + else if ((value = cupsGetOption("media-size", num_media_col, media_col)) != NULL) + { + int num_media_size; /* Number of media-size values */ + cups_option_t *media_size; /* media-size values */ + const char *x_dimension, /* x-dimension value */ + *y_dimension; /* y-dimension value */ + + num_media_size = cupsParseOptions(value, 0, &media_size); + + if ((x_dimension = cupsGetOption("x-dimension", num_media_size, media_size)) != NULL && (y_dimension = cupsGetOption("y-dimension", num_media_size, media_size)) != NULL) + media = pwgMediaForSize(atoi(x_dimension), atoi(y_dimension)); + + cupsFreeOptions(num_media_size, media_size); + } + + if (media) + num_options = cupsAddOption("PageSize", media->ppd, num_options, options); + +#if !CUPS_LITE + /* + * Load PPD file and the corresponding IPP <-> PPD cache data... + */ + + if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL) + { + ppd_cache = _ppdCacheCreateWithPPD(ppd); + + /* TODO: Fix me - values are names, not numbers... Also need to support finishings-col */ + if ((value = getenv("IPP_FINISHINGS")) == NULL) + value = getenv("IPP_FINISHINGS_DEFAULT"); + + if (value) + { + char *ptr; /* Pointer into value */ + int fin; /* Current value */ + + for (fin = strtol(value, &ptr, 10); fin > 0; fin = strtol(ptr + 1, &ptr, 10)) + { + num_options = _ppdCacheGetFinishingOptions(ppd_cache, NULL, (ipp_finishings_t)fin, num_options, options); + + if (*ptr != ',') + break; + } + } + + if ((value = cupsGetOption("media-source", num_media_col, media_col)) != NULL) + { + if ((choice = _ppdCacheGetInputSlot(ppd_cache, NULL, value)) != NULL) + num_options = cupsAddOption("InputSlot", choice, num_options, options); + } + + if ((value = cupsGetOption("media-type", num_media_col, media_col)) != NULL) + { + if ((choice = _ppdCacheGetMediaType(ppd_cache, NULL, value)) != NULL) + num_options = cupsAddOption("MediaType", choice, num_options, options); + } + + if ((value = getenv("IPP_OUTPUT_BIN")) == NULL) + value = getenv("IPP_OUTPUT_BIN_DEFAULT"); + + if (value) + { + if ((choice = _ppdCacheGetOutputBin(ppd_cache, value)) != NULL) + num_options = cupsAddOption("OutputBin", choice, num_options, options); + } + + if ((value = getenv("IPP_SIDES")) == NULL) + value = getenv("IPP_SIDES_DEFAULT"); + + if (value && ppd_cache->sides_option) + { + if (!strcmp(value, "one-sided") && ppd_cache->sides_1sided) + num_options = cupsAddOption(ppd_cache->sides_option, ppd_cache->sides_1sided, num_options, options); + else if (!strcmp(value, "two-sided-long-edge") && ppd_cache->sides_2sided_long) + num_options = cupsAddOption(ppd_cache->sides_option, ppd_cache->sides_2sided_long, num_options, options); + else if (!strcmp(value, "two-sided-short-edge") && ppd_cache->sides_2sided_short) + num_options = cupsAddOption(ppd_cache->sides_option, ppd_cache->sides_2sided_short, num_options, options); + } + + if ((value = getenv("IPP_PRINT_QUALITY")) == NULL) + value = getenv("IPP_PRINT_QUALITY_DEFAULT"); + + if (value) + { + int i; /* Looping var */ + int pq; /* Print quality (0-2) */ + int pcm = 1; /* Print color model (0 = mono, 1 = color) */ + int num_presets; /* Number of presets */ + cups_option_t *presets; /* Presets */ + + if (!strcmp(value, "draft")) + pq = 0; + else if (!strcmp(value, "high")) + pq = 2; + else + pq = 1; + + if ((value = getenv("IPP_PRINT_COLOR_MODE")) == NULL) + value = getenv("IPP_PRINT_COLOR_MODE_DEFAULT"); + + if (value && !strcmp(value, "monochrome")) + pcm = 0; + + num_presets = ppd_cache->num_presets[pcm][pq]; + presets = ppd_cache->presets[pcm][pq]; + + for (i = 0; i < num_presets; i ++) + num_options = cupsAddOption(presets[i].name, presets[i].value, num_options, options); + } + + /* + * Mark the PPD with the options... + */ + + ppdMarkDefaults(ppd); + cupsMarkOptions(ppd, num_options, *options); + } +#endif /* !CUPS_LITE */ + + cupsFreeOptions(num_media_col, media_col); + + return (num_options); +} + + +/* + * 'jpeg_to_ps()' - Convert a JPEG file to PostScript. + */ + +static int /* O - Exit status */ +jpeg_to_ps(const char *filename, /* I - Filename */ + int copies) /* I - Number of copies */ +{ + int fd; /* JPEG file descriptor */ + int copy; /* Current copy */ + int width = 0, /* Width */ + height = 0, /* Height */ + depth = 0, /* Number of colors */ + length; /* Length of marker */ + unsigned char buffer[65536], /* Copy buffer */ + *bufptr, /* Pointer info buffer */ + *bufend; /* End of buffer */ + ssize_t bytes; /* Bytes in buffer */ + const char *decode; /* Decode array */ + float page_left, /* Left margin */ + page_top, /* Top margin */ + page_width, /* Page width in points */ + page_height, /* Page heigth in points */ + x_factor, /* X image scaling factor */ + y_factor, /* Y image scaling factor */ + page_scaling; /* Image scaling factor */ +#if !CUPS_LITE + ppd_size_t *page_size; /* Current page size */ +#endif /* !CUPS_LITE */ + + + /* + * Open the input file... + */ + + if (filename) + { + if ((fd = open(filename, O_RDONLY)) < 0) + { + fprintf(stderr, "ERROR: Unable to open \"%s\": %s\n", filename, strerror(errno)); + return (1); + } + } + else + { + fd = 0; + copies = 1; + } + + /* + * Read the JPEG dimensions... + */ + + bytes = read(fd, buffer, sizeof(buffer)); + + if (bytes < 3 || memcmp(buffer, "\377\330\377", 3)) + { + fputs("ERROR: Not a JPEG image.\n", stderr); + + if (fd > 0) + close(fd); + + return (1); + } + + for (bufptr = buffer + 2, bufend = buffer + bytes; bufptr < bufend;) + { + /* + * Scan the file for a SOFn marker, then we can get the dimensions... + */ + + if (*bufptr == 0xff) + { + bufptr ++; + + if (bufptr >= bufend) + { + /* + * If we are at the end of the current buffer, re-fill and continue... + */ + + if ((bytes = read(fd, buffer, sizeof(buffer))) <= 0) + break; + + bufptr = buffer; + bufend = buffer + bytes; + } + + if (*bufptr == 0xff) + continue; + + if ((bufptr + 16) >= bufend) + { + /* + * Read more of the marker... + */ + + bytes = bufend - bufptr; + + memmove(buffer, bufptr, (size_t)bytes); + bufptr = buffer; + bufend = buffer + bytes; + + if ((bytes = read(fd, bufend, sizeof(buffer) - (size_t)bytes)) <= 0) + break; + + bufend += bytes; + } + + length = (size_t)((bufptr[1] << 8) | bufptr[2]); + + if ((*bufptr >= 0xc0 && *bufptr <= 0xc3) || (*bufptr >= 0xc5 && *bufptr <= 0xc7) || (*bufptr >= 0xc9 && *bufptr <= 0xcb) || (*bufptr >= 0xcd && *bufptr <= 0xcf)) + { + /* + * SOFn marker, look for dimensions... + */ + + width = (bufptr[6] << 8) | bufptr[7]; + height = (bufptr[4] << 8) | bufptr[5]; + depth = bufptr[8]; + break; + } + + /* + * Skip past this marker... + */ + + bufptr ++; + bytes = bufend - bufptr; + + while (length >= bytes) + { + length -= bytes; + + if ((bytes = read(fd, buffer, sizeof(buffer))) <= 0) + break; + + bufptr = buffer; + bufend = buffer + bytes; + } + + if (length > bytes) + break; + + bufptr += length; + } + } + + fprintf(stderr, "DEBUG: JPEG dimensions are %dx%dx%d\n", width, height, depth); + + if (width <= 0 || height <= 0 || depth <= 0) + { + fputs("ERROR: No valid image data in JPEG file.\n", stderr); + + if (fd > 0) + close(fd); + + return (1); + } + + fputs("ATTR: job-impressions=1\n", stderr); + + /* + * Figure out the dimensions/scaling of the final image... + */ + +#if CUPS_LITE + page_left = 18.0f; + page_top = 756.0f; + page_width = 576.0f; + page_height = 720.0f; + +#else + if ((page_size = ppdPageSize(ppd, NULL)) != NULL) + { + page_left = page_size->left; + page_top = page_size->top; + page_width = page_size->right - page_left; + page_height = page_top - page_size->bottom; + } + else + { + page_left = 18.0f; + page_top = 756.0f; + page_width = 576.0f; + page_height = 720.0f; + } +#endif /* CUPS_LITE */ + + fprintf(stderr, "DEBUG: page_left=%.2f, page_top=%.2f, page_width=%.2f, page_height=%.2f\n", page_left, page_top, page_width, page_height); + + /* TODO: Support orientation/rotation, different print-scaling modes */ + x_factor = page_width / width; + y_factor = page_height / height; + + if (x_factor > y_factor && (height * x_factor) <= page_height) + page_scaling = x_factor; + else + page_scaling = y_factor; + + fprintf(stderr, "DEBUG: Scaled dimensions are %.2fx%.2f\n", width * page_scaling, height * page_scaling); + + /* + * Write pages... + */ + + dsc_header(copies); + + for (copy = 1; copy <= copies; copy ++) + { + dsc_page(copy); + + if (depth == 1) + { + puts("/DeviceGray setcolorspace"); + decode = "0 1"; + } + else if (depth == 3) + { + puts("/DeviceRGB setcolorspace"); + decode = "0 1 0 1 0 1"; + } + else + { + puts("/DeviceCMYK setcolorspace"); + decode = "0 1 0 1 0 1 0 1"; + } + + printf("gsave %.3f %.3f translate %.3f %.3f scale\n", page_left + 0.5f * (page_width - width * page_scaling), page_top - 0.5f * (page_height - height * page_scaling), page_scaling, page_scaling); + printf("<</ImageType 1/Width %d/Height %d/BitsPerComponent 8/ImageMatrix[1 0 0 -1 0 1]/Decode[%s]/DataSource currentfile/ASCII85Decode filter/DCTDecode filter/Interpolate true>>image\n", width, height, decode); + + if (fd > 0) + lseek(fd, 0, SEEK_SET); + + while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) + ascii85(buffer, (int)bytes, 0); + + ascii85(buffer, 0, 1); + + puts("grestore showpage"); + } + + dsc_trailer(0); + + return (0); +} + + +/* + * 'pdf_to_ps()' - Convert a PDF file to PostScript. + */ + +static int /* O - Exit status */ +pdf_to_ps(const char *filename, /* I - Filename */ + int copies, /* I - Number of copies */ + int num_options, /* I - Number of options */ + cups_option_t *options) /* I - options */ +{ + int status; /* Exit status */ + char tempfile[1024]; /* Temporary file */ + int tempfd; /* Temporary file descriptor */ + int pid; /* Process ID */ + const char *pdf_argv[8]; /* Command-line arguments */ + char pdf_options[1024]; /* Options */ + const char *value; /* Option value */ + const char *job_id, /* job-id value */ + *job_name; /* job-name value */ + + + /* + * Create a temporary file for the PostScript version... + */ + + if ((tempfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) + { + fprintf(stderr, "ERROR: Unable to create temporary file: %s\n", strerror(errno)); + return (1); + } + + /* + * Run cgpdftops or pdftops in the filter directory... + */ + + if ((value = cupsGetOption("PageSize", num_options, options)) != NULL) + snprintf(pdf_options, sizeof(pdf_options), "PageSize=%s", value); + else + pdf_options[0] = '\0'; + + if ((job_id = getenv("IPP_JOB_ID")) == NULL) + job_id = "42"; + if ((job_name = getenv("IPP_JOB_NAME")) == NULL) + job_name = "untitled"; + + pdf_argv[0] = "printer"; + pdf_argv[1] = job_id; + pdf_argv[2] = cupsUser(); + pdf_argv[3] = job_name; + pdf_argv[4] = "1"; + pdf_argv[5] = pdf_options; + pdf_argv[6] = filename; + pdf_argv[7] = NULL; + + if ((pid = fork()) == 0) + { + /* + * Child comes here... + */ + + close(1); + dup2(tempfd, 1); + close(tempfd); + + execv(PDFTOPS, (char * const *)pdf_argv); + exit(errno); + } + else if (pid < 0) + { + /* + * Unable to fork process... + */ + + perror("ERROR: Unable to start PDF filter"); + + close(tempfd); + unlink(tempfile); + + return (1); + } + else + { + /* + * Wait for the filter to complete... + */ + + close(tempfd); + +# ifdef HAVE_WAITPID + while (waitpid(pid, &status, 0) < 0); +# else + while (wait(&status) < 0); +# endif /* HAVE_WAITPID */ + + if (status) + { + if (WIFEXITED(status)) + fprintf(stderr, "ERROR: " PDFTOPS " exited with status %d.\n", WEXITSTATUS(status)); + else + fprintf(stderr, "ERROR: " PDFTOPS " terminated with signal %d.\n", WTERMSIG(status)); + + unlink(tempfile); + return (1); + } + } + + /* + * Copy the PostScript output from the command... + */ + + status = ps_to_ps(tempfile, copies); + + unlink(tempfile); + + return (status); +} + + +/* + * 'ps_to_ps()' - Copy PostScript to the standard output. + */ + +static int /* O - Exit status */ +ps_to_ps(const char *filename, /* I - Filename */ + int copies) /* I - Number of copies */ +{ + FILE *fp; /* File to read from */ + int copy, /* Current copy */ + page, /* Current page number */ + num_pages = 0, /* Total number of pages */ + first_page, /* First page */ + last_page; /* Last page */ + const char *page_ranges; /* page-ranges option */ + long first_pos = -1; /* Offset for first page */ + char line[1024]; /* Line from file */ + + + /* + * Open the print file... + */ + + if (filename) + { + if ((fp = fopen(filename, "rb")) == NULL) + { + fprintf(stderr, "ERROR: Unable to open \"%s\": %s\n", filename, strerror(errno)); + return (1); + } + } + else + { + copies = 1; + fp = stdin; + } + + /* + * Check page ranges... + */ + + if ((page_ranges = getenv("IPP_PAGE_RANGES")) != NULL) + { + if (sscanf(page_ranges, "%d-%d", &first_page, &last_page) != 2) + { + first_page = 1; + last_page = INT_MAX; + } + } + else + { + first_page = 1; + last_page = INT_MAX; + } + + /* + * Write the PostScript header for the document... + */ + + dsc_header(0); + + first_pos = 0; + + while (fgets(line, sizeof(line), fp)) + { + if (!strncmp(line, "%%Page:", 7)) + break; + + first_pos = ftell(fp); + + if (line[0] != '%') + fputs(line, stdout); + } + + if (!strncmp(line, "%%Page:", 7)) + { + for (copy = 0; copy < copies; copy ++) + { + int copy_page = 0; /* Do we copy the page data? */ + + if (fp != stdin) + fseek(fp, first_pos, SEEK_SET); + + page = 0; + while (fgets(line, sizeof(line), fp)) + { + if (!strncmp(line, "%%Page:", 7)) + { + page ++; + copy_page = page >= first_page && page <= last_page; + + if (copy_page) + { + num_pages ++; + dsc_page(num_pages); + } + } + else if (copy_page) + fputs(line, stdout); + } + } + } + + dsc_trailer(num_pages); + + fprintf(stderr, "ATTR: job-impressions=%d\n", num_pages / copies); + + if (fp != stdin) + fclose(fp); + + return (0); +} + + +/* + * 'raster_to_ps()' - Convert PWG Raster/Apple Raster to PostScript. + * + * The current implementation locally-decodes the raster data and then writes + * whole, non-blank lines as 1-line high images with base-85 encoding, resulting + * in between 10 and 20 times larger output. A alternate implementation (if it + * is deemed necessary) would be to implement a PostScript decode procedure that + * handles the modified packbits decompression so that we just have the base-85 + * encoding overhead (25%). Furthermore, Level 3 PostScript printers also + * support Flate compression. + * + * That said, the most efficient path with the highest quality is for Clients + * to supply PDF files and us to use the existing PDF to PostScript conversion + * filters. + */ + +static int /* O - Exit status */ +raster_to_ps(const char *filename) /* I - Filename */ +{ + int fd; /* Input file */ + cups_raster_t *ras; /* Raster stream */ + cups_page_header2_t header; /* Page header */ + int page = 0; /* Current page */ + unsigned y; /* Current line */ + unsigned char *line; /* Line buffer */ + unsigned char white; /* White color */ + const char *decode; /* Image decode array */ + + + /* + * Open the input file... + */ + + if (filename) + { + if ((fd = open(filename, O_RDONLY)) < 0) + { + fprintf(stderr, "ERROR: Unable to open \"%s\": %s\n", filename, strerror(errno)); + return (1); + } + } + else + { + fd = 0; + } + + /* + * Open the raster stream and send pages... + */ + + if ((ras = cupsRasterOpen(fd, CUPS_RASTER_READ)) == NULL) + { + fputs("ERROR: Unable to read raster data, aborting.\n", stderr); + return (1); + } + + dsc_header(0); + + while (cupsRasterReadHeader2(ras, &header)) + { + page ++; + + fprintf(stderr, "DEBUG: Page %d: %ux%ux%u\n", page, header.cupsWidth, header.cupsHeight, header.cupsBitsPerPixel); + + if (header.cupsColorSpace != CUPS_CSPACE_W && header.cupsColorSpace != CUPS_CSPACE_SW && header.cupsColorSpace != CUPS_CSPACE_K && header.cupsColorSpace != CUPS_CSPACE_RGB && header.cupsColorSpace != CUPS_CSPACE_SRGB) + { + fputs("ERROR: Unsupported color space, aborting.\n", stderr); + break; + } + else if (header.cupsBitsPerColor != 1 && header.cupsBitsPerColor != 8) + { + fputs("ERROR: Unsupported bit depth, aborting.\n", stderr); + break; + } + + line = malloc(header.cupsBytesPerLine); + + dsc_page(page); + + puts("gsave"); + printf("%.6f %.6f scale\n", 72.0f / header.HWResolution[0], 72.0f / header.HWResolution[1]); + + switch (header.cupsColorSpace) + { + case CUPS_CSPACE_W : + case CUPS_CSPACE_SW : + decode = "0 1"; + puts("/DeviceGray setcolorspace"); + white = 255; + break; + + case CUPS_CSPACE_K : + decode = "0 1"; + puts("/DeviceGray setcolorspace"); + white = 0; + break; + + default : + decode = "0 1 0 1 0 1"; + puts("/DeviceRGB setcolorspace"); + white = 255; + break; + } + + printf("gsave /L{grestore gsave 0 exch translate <</ImageType 1/Width %u/Height 1/BitsPerComponent %u/ImageMatrix[1 0 0 -1 0 1]/DataSource currentfile/ASCII85Decode filter/Decode[%s]>>image}bind def\n", header.cupsWidth, header.cupsBitsPerColor, decode); + + for (y = header.cupsHeight; y > 0; y --) + { + if (cupsRasterReadPixels(ras, line, header.cupsBytesPerLine)) + { + if (line[0] != white || memcmp(line, line + 1, header.cupsBytesPerLine - 1)) + { + printf("%d L\n", y - 1); + ascii85(line, (int)header.cupsBytesPerLine, 1); + } + } + else + break; + } + + fprintf(stderr, "DEBUG: y=%d at end...\n", y); + + puts("grestore grestore"); + puts("showpage"); + + free(line); + } + + cupsRasterClose(ras); + + dsc_trailer(page); + + fprintf(stderr, "ATTR: job-impressions=%d\n", page); + + return (0); +} + + |