mirror of https://gitee.com/openkylin/cups.git
1152 lines
27 KiB
C
1152 lines
27 KiB
C
/*
|
|
* Generic Adobe PostScript printer command for ippeveprinter/CUPS.
|
|
*
|
|
* Copyright © 2021-2023 by OpenPrinting.
|
|
* 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>
|
|
#include <sys/wait.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)
|
|
{
|
|
if ((copies = atoi(ipp_copies)) < 1)
|
|
copies = 1;
|
|
}
|
|
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] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]);
|
|
break;
|
|
case 1 :
|
|
b = (unsigned)((leftdata[0] << 24) | (data[0] << 16) | (data[1] << 8) | data[2]);
|
|
break;
|
|
case 2 :
|
|
b = (unsigned)((leftdata[0] << 24) | (leftdata[1] << 16) | (data[0] << 8) | data[1]);
|
|
break;
|
|
case 3 :
|
|
b = (unsigned)((leftdata[0] << 24) | (leftdata[1] << 16) | (leftdata[2] << 8) | data[0]);
|
|
break;
|
|
}
|
|
|
|
if (col >= 76)
|
|
{
|
|
col = 0;
|
|
putchar('\n');
|
|
}
|
|
|
|
if (b == 0)
|
|
{
|
|
putchar('z');
|
|
col ++;
|
|
}
|
|
else
|
|
{
|
|
c[4] = (unsigned char) ((b % 85) + '!');
|
|
b /= 85;
|
|
c[3] = (unsigned char) ((b % 85) + '!');
|
|
b /= 85;
|
|
c[2] = (unsigned char) ((b % 85) + '!');
|
|
b /= 85;
|
|
c[1] = (unsigned char) ((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)
|
|
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] << 24) | (leftdata[1] << 16) | (leftdata[2] << 8) | leftdata[3]);
|
|
|
|
c[4] = (unsigned char) ((b % 85) + '!');
|
|
b /= 85;
|
|
c[3] = (unsigned char) ((b % 85) + '!');
|
|
b /= 85;
|
|
c[2] = (unsigned char) ((b % 85) + '!');
|
|
b /= 85;
|
|
c[1] = (unsigned char) ((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 number (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)) != NULL)
|
|
{
|
|
/* 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 */
|
|
long 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 */
|
|
ssize_t 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 height 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 = (ssize_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; /* 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;
|
|
|
|
if ((first_pos = ftell(fp)) < 0)
|
|
{
|
|
perror("DEBUG: ftell failed");
|
|
first_pos = 0;
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (fseek(fp, first_pos, SEEK_SET) < 0)
|
|
{
|
|
perror("ERROR: Unable to seek within PostScript file");
|
|
break;
|
|
}
|
|
}
|
|
|
|
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. An 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=%u 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);
|
|
}
|
|
|
|
|