#include "ippevecommon.h"
#if !CUPS_LITE
# include <cups/ppd-private.h>
#endif
#include <limits.h>
#include <sys/wait.h>
#ifdef __APPLE__
# define PDFTOPS CUPS_SERVERBIN "/filter/cgpdftops"
#else
# define PDFTOPS CUPS_SERVERBIN "/filter/pdftops"
#endif
#if !CUPS_LITE
static ppd_file_t *ppd = NULL;
static _ppd_cache_t *ppd_cache = NULL;
#endif
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);
int
main(int argc,
char *argv[])
{
const char *content_type,
*ipp_copies;
int copies;
int num_options;
cups_option_t *options;
num_options = get_options(&options);
if ((ipp_copies = getenv("IPP_COPIES")) != NULL)
copies = atoi(ipp_copies);
else
copies = 1;
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);
}
}
static void
ascii85(const unsigned char *data,
int length,
int eod)
{
unsigned b = 0;
unsigned char c[5];
static int col = 0;
static unsigned char leftdata[4];
static int leftcount = 0;
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)
{
if ((length - leftcount) > 0)
memcpy(leftdata + leftcount, data, (size_t)(length - leftcount));
memset(leftdata + length, 0, (size_t)(4 - length));
leftcount = length;
}
if (eod)
{
if (col >= 76)
{
col = 0;
putchar('\n');
}
if (leftcount > 0)
{
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;
}
}
static void
dsc_header(int num_pages)
{
const char *job_name = getenv("IPP_JOB_NAME");
#if !CUPS_LITE
const char *job_id = getenv("IPP_JOB_ID");
ppdEmitJCL(ppd, stdout, job_id ? atoi(job_id) : 0, cupsUser(), job_name ? job_name : "Unknown");
#endif
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
}
static void
dsc_page(int page)
{
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
}
static void
dsc_trailer(int num_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
putchar(0x04);
}
static int
get_options(cups_option_t **options)
{
int num_options = 0;
const char *value;
pwg_media_t *media = NULL;
int num_media_col = 0;
cups_option_t *media_col = NULL;
#if !CUPS_LITE
const char *choice;
#endif
*options = NULL;
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 == '{')
{
num_media_col = cupsParseOptions(value, 0, &media_col);
}
else
{
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;
cups_option_t *media_size;
const char *x_dimension,
*y_dimension;
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
if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL)
{
ppd_cache = _ppdCacheCreateWithPPD(ppd);
if ((value = getenv("IPP_FINISHINGS")) == NULL)
value = getenv("IPP_FINISHINGS_DEFAULT");
if (value)
{
char *ptr;
int fin;
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;
int pq;
int pcm = 1;
int num_presets;
cups_option_t *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);
}
ppdMarkDefaults(ppd);
cupsMarkOptions(ppd, num_options, *options);
}
#endif
cupsFreeOptions(num_media_col, media_col);
return (num_options);
}
static int
jpeg_to_ps(const char *filename,
int copies)
{
int fd;
int copy;
int width = 0,
height = 0,
depth = 0,
length;
unsigned char buffer[65536],
*bufptr,
*bufend;
ssize_t bytes;
const char *decode;
float page_left,
page_top,
page_width,
page_height,
x_factor,
y_factor,
page_scaling;
#if !CUPS_LITE
ppd_size_t *page_size;
#endif
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;
}
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;)
{
if (*bufptr == 0xff)
{
bufptr ++;
if (bufptr >= bufend)
{
if ((bytes = read(fd, buffer, sizeof(buffer))) <= 0)
break;
bufptr = buffer;
bufend = buffer + bytes;
}
if (*bufptr == 0xff)
continue;
if ((bufptr + 16) >= bufend)
{
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))
{
width = (bufptr[6] << 8) | bufptr[7];
height = (bufptr[4] << 8) | bufptr[5];
depth = bufptr[8];
break;
}
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);
#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
fprintf(stderr, "DEBUG: page_left=%.2f, page_top=%.2f, page_width=%.2f, page_height=%.2f\n", page_left, page_top, page_width, page_height);
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);
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);
}
static int
pdf_to_ps(const char *filename,
int copies,
int num_options,
cups_option_t *options)
{
int status;
char tempfile[1024];
int tempfd;
int pid;
const char *pdf_argv[8];
char pdf_options[1024];
const char *value;
const char *job_id,
*job_name;
if ((tempfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
{
fprintf(stderr, "ERROR: Unable to create temporary file: %s\n", strerror(errno));
return (1);
}
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)
{
close(1);
dup2(tempfd, 1);
close(tempfd);
execv(PDFTOPS, (char * const *)pdf_argv);
exit(errno);
}
else if (pid < 0)
{
perror("ERROR: Unable to start PDF filter");
close(tempfd);
unlink(tempfile);
return (1);
}
else
{
close(tempfd);
# ifdef HAVE_WAITPID
while (waitpid(pid, &status, 0) < 0);
# else
while (wait(&status) < 0);
# endif
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);
}
}
status = ps_to_ps(tempfile, copies);
unlink(tempfile);
return (status);
}
static int
ps_to_ps(const char *filename,
int copies)
{
FILE *fp;
int copy,
page,
num_pages = 0,
first_page,
last_page;
const char *page_ranges;
long first_pos = -1;
char line[1024];
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;
}
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;
}
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;
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);
}
static int
raster_to_ps(const char *filename)
{
int fd;
cups_raster_t *ras;
cups_page_header2_t header;
int page = 0;
unsigned y;
unsigned char *line;
unsigned char white;
const char *decode;
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;
}
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);
}