You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
531 lines
10 KiB
531 lines
10 KiB
/*
|
|
* Generic HP PCL 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"
|
|
#include "dither.h"
|
|
|
|
|
|
/*
|
|
* Local globals...
|
|
*/
|
|
|
|
static unsigned pcl_bottom, /* Bottom line */
|
|
pcl_left, /* Left offset in line */
|
|
pcl_right, /* Right offset in line */
|
|
pcl_top, /* Top line */
|
|
pcl_blanks; /* Number of blank lines to skip */
|
|
static unsigned char pcl_white, /* White color */
|
|
*pcl_line, /* Line buffer */
|
|
*pcl_comp; /* Compression buffer */
|
|
|
|
/*
|
|
* Local functions...
|
|
*/
|
|
|
|
static void pcl_end_page(cups_page_header2_t *header, unsigned page);
|
|
static void pcl_start_page(cups_page_header2_t *header, unsigned page);
|
|
static int pcl_to_pcl(const char *filename);
|
|
static void pcl_write_line(cups_page_header2_t *header, unsigned y, const unsigned char *line);
|
|
static int raster_to_pcl(const char *filename);
|
|
|
|
|
|
/*
|
|
* 'main()' - Main entry for PCL 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 */
|
|
|
|
|
|
/*
|
|
* 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/vnd.hp-pcl"))
|
|
{
|
|
return (pcl_to_pcl(argv[1]));
|
|
}
|
|
else if (!strcasecmp(content_type, "image/pwg-raster") || !strcasecmp(content_type, "image/urf"))
|
|
{
|
|
return (raster_to_pcl(argv[1]));
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "ERROR: CONTENT_TYPE %s not supported.\n", content_type);
|
|
return (1);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* 'pcl_end_page()' - End of PCL page.
|
|
*/
|
|
|
|
static void
|
|
pcl_end_page(
|
|
cups_page_header2_t *header, /* I - Page header */
|
|
unsigned page) /* I - Current page */
|
|
{
|
|
/*
|
|
* End graphics...
|
|
*/
|
|
|
|
fputs("\033*r0B", stdout);
|
|
|
|
/*
|
|
* Formfeed as needed...
|
|
*/
|
|
|
|
if (!(header->Duplex && (page & 1)))
|
|
putchar('\f');
|
|
|
|
/*
|
|
* Free the output buffers...
|
|
*/
|
|
|
|
free(pcl_line);
|
|
free(pcl_comp);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'pcl_start_page()' - Start a PCL page.
|
|
*/
|
|
|
|
static void
|
|
pcl_start_page(
|
|
cups_page_header2_t *header, /* I - Page header */
|
|
unsigned page) /* I - Current page */
|
|
{
|
|
/*
|
|
* Setup margins to be 1/6" top and bottom and 1/4" or .135" on the
|
|
* left and right.
|
|
*/
|
|
|
|
pcl_top = header->HWResolution[1] / 6;
|
|
pcl_bottom = header->cupsHeight - header->HWResolution[1] / 6 - 1;
|
|
|
|
if (header->PageSize[1] == 842)
|
|
{
|
|
/* A4 gets special side margins to expose an 8" print area */
|
|
pcl_left = (header->cupsWidth - 8 * header->HWResolution[0]) / 2;
|
|
pcl_right = pcl_left + 8 * header->HWResolution[0] - 1;
|
|
}
|
|
else
|
|
{
|
|
/* All other sizes get 1/4" margins */
|
|
pcl_left = header->HWResolution[0] / 4;
|
|
pcl_right = header->cupsWidth - header->HWResolution[0] / 4 - 1;
|
|
}
|
|
|
|
if (!header->Duplex || (page & 1))
|
|
{
|
|
/*
|
|
* Set the media size...
|
|
*/
|
|
|
|
printf("\033&l12D\033&k12H"); /* Set 12 LPI, 10 CPI */
|
|
printf("\033&l0O"); /* Set portrait orientation */
|
|
|
|
switch (header->PageSize[1])
|
|
{
|
|
case 540 : /* Monarch Envelope */
|
|
printf("\033&l80A");
|
|
break;
|
|
|
|
case 595 : /* A5 */
|
|
printf("\033&l25A");
|
|
break;
|
|
|
|
case 624 : /* DL Envelope */
|
|
printf("\033&l90A");
|
|
break;
|
|
|
|
case 649 : /* C5 Envelope */
|
|
printf("\033&l91A");
|
|
break;
|
|
|
|
case 684 : /* COM-10 Envelope */
|
|
printf("\033&l81A");
|
|
break;
|
|
|
|
case 709 : /* B5 Envelope */
|
|
printf("\033&l100A");
|
|
break;
|
|
|
|
case 756 : /* Executive */
|
|
printf("\033&l1A");
|
|
break;
|
|
|
|
case 792 : /* Letter */
|
|
printf("\033&l2A");
|
|
break;
|
|
|
|
case 842 : /* A4 */
|
|
printf("\033&l26A");
|
|
break;
|
|
|
|
case 1008 : /* Legal */
|
|
printf("\033&l3A");
|
|
break;
|
|
|
|
case 1191 : /* A3 */
|
|
printf("\033&l27A");
|
|
break;
|
|
|
|
case 1224 : /* Tabloid */
|
|
printf("\033&l6A");
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Set top margin and turn off perforation skip...
|
|
*/
|
|
|
|
printf("\033&l%uE\033&l0L", 12 * pcl_top / header->HWResolution[1]);
|
|
|
|
if (header->Duplex)
|
|
{
|
|
int mode = header->Duplex ? 1 + header->Tumble != 0 : 0;
|
|
|
|
printf("\033&l%dS", mode); /* Set duplex mode */
|
|
}
|
|
}
|
|
else if (header->Duplex)
|
|
printf("\033&a2G"); /* Print on back side */
|
|
|
|
/*
|
|
* Set graphics mode...
|
|
*/
|
|
|
|
printf("\033*t%uR", header->HWResolution[0]);
|
|
/* Set resolution */
|
|
printf("\033*r%uS", pcl_right - pcl_left + 1);
|
|
/* Set width */
|
|
printf("\033*r%uT", pcl_bottom - pcl_top + 1);
|
|
/* Set height */
|
|
printf("\033&a0H\033&a%uV", 720 * pcl_top / header->HWResolution[1]);
|
|
/* Set position */
|
|
|
|
printf("\033*b2M"); /* Use PackBits compression */
|
|
printf("\033*r1A"); /* Start graphics */
|
|
|
|
/*
|
|
* Allocate the output buffers...
|
|
*/
|
|
|
|
pcl_white = header->cupsBitsPerColor == 1 ? 0 : 255;
|
|
pcl_blanks = 0;
|
|
pcl_line = malloc(header->cupsWidth / 8 + 1);
|
|
pcl_comp = malloc(2 * header->cupsBytesPerLine + 2);
|
|
|
|
fprintf(stderr, "ATTR: job-impressions-completed=%d\n", page);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'pcl_to_pcl()' - Pass through PCL data.
|
|
*/
|
|
|
|
static int /* O - Exit status */
|
|
pcl_to_pcl(const char *filename) /* I - File to print or NULL for stdin */
|
|
{
|
|
int fd; /* File to read from */
|
|
char buffer[65536]; /* Copy buffer */
|
|
ssize_t bytes; /* Bytes to write */
|
|
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
fputs("ATTR: job-impressions=unknown\n", stderr);
|
|
|
|
/*
|
|
* Copy to stdout...
|
|
*/
|
|
|
|
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
|
|
write(1, buffer, (size_t)bytes);
|
|
|
|
/*
|
|
* Close the input file...
|
|
*/
|
|
|
|
if (fd > 0)
|
|
close(fd);
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'pcl_write_line()' - Write a line of raster data.
|
|
*/
|
|
|
|
static void
|
|
pcl_write_line(
|
|
cups_page_header2_t *header, /* I - Raster information */
|
|
unsigned y, /* I - Line number */
|
|
const unsigned char *line) /* I - Pixels on line */
|
|
{
|
|
unsigned x; /* Column number */
|
|
unsigned char bit, /* Current bit */
|
|
byte, /* Current byte */
|
|
*outptr, /* Pointer into output buffer */
|
|
*outend, /* End of output buffer */
|
|
*start, /* Start of sequence */
|
|
*compptr; /* Pointer into compression buffer */
|
|
unsigned count; /* Count of bytes for output */
|
|
const unsigned char *ditherline; /* Pointer into dither table */
|
|
|
|
|
|
if (line[0] == pcl_white && !memcmp(line, line + 1, header->cupsBytesPerLine - 1))
|
|
{
|
|
/*
|
|
* Skip blank line...
|
|
*/
|
|
|
|
pcl_blanks ++;
|
|
return;
|
|
}
|
|
|
|
if (header->cupsBitsPerPixel == 1)
|
|
{
|
|
/*
|
|
* B&W bitmap data can be used directly...
|
|
*/
|
|
|
|
outend = (unsigned char *)line + (pcl_right + 7) / 8;
|
|
outptr = (unsigned char *)line + pcl_left / 8;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Dither 8-bit grayscale to B&W...
|
|
*/
|
|
|
|
y &= 63;
|
|
ditherline = threshold[y];
|
|
|
|
for (x = pcl_left, bit = 128, byte = 0, outptr = pcl_line; x <= pcl_right; x ++, line ++)
|
|
{
|
|
if (*line <= ditherline[x & 63])
|
|
byte |= bit;
|
|
|
|
if (bit == 1)
|
|
{
|
|
*outptr++ = byte;
|
|
byte = 0;
|
|
bit = 128;
|
|
}
|
|
else
|
|
bit >>= 1;
|
|
}
|
|
|
|
if (bit != 128)
|
|
*outptr++ = byte;
|
|
|
|
outend = outptr;
|
|
outptr = pcl_line;
|
|
}
|
|
|
|
/*
|
|
* Apply compression...
|
|
*/
|
|
|
|
compptr = pcl_comp;
|
|
|
|
while (outptr < outend)
|
|
{
|
|
if ((outptr + 1) >= outend)
|
|
{
|
|
/*
|
|
* Single byte on the end...
|
|
*/
|
|
|
|
*compptr++ = 0x00;
|
|
*compptr++ = *outptr++;
|
|
}
|
|
else if (outptr[0] == outptr[1])
|
|
{
|
|
/*
|
|
* Repeated sequence...
|
|
*/
|
|
|
|
outptr ++;
|
|
count = 2;
|
|
|
|
while (outptr < (outend - 1) &&
|
|
outptr[0] == outptr[1] &&
|
|
count < 127)
|
|
{
|
|
outptr ++;
|
|
count ++;
|
|
}
|
|
|
|
*compptr++ = (unsigned char)(257 - count);
|
|
*compptr++ = *outptr++;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Non-repeated sequence...
|
|
*/
|
|
|
|
start = outptr;
|
|
outptr ++;
|
|
count = 1;
|
|
|
|
while (outptr < (outend - 1) &&
|
|
outptr[0] != outptr[1] &&
|
|
count < 127)
|
|
{
|
|
outptr ++;
|
|
count ++;
|
|
}
|
|
|
|
*compptr++ = (unsigned char)(count - 1);
|
|
|
|
memcpy(compptr, start, count);
|
|
compptr += count;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Output the line...
|
|
*/
|
|
|
|
if (pcl_blanks > 0)
|
|
{
|
|
/*
|
|
* Skip blank lines first...
|
|
*/
|
|
|
|
printf("\033*b%dY", pcl_blanks);
|
|
pcl_blanks = 0;
|
|
}
|
|
|
|
printf("\033*b%dW", (int)(compptr - pcl_comp));
|
|
fwrite(pcl_comp, 1, (size_t)(compptr - pcl_comp), stdout);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'raster_to_pcl()' - Convert raster data to PCL.
|
|
*/
|
|
|
|
static int /* O - Exit status */
|
|
raster_to_pcl(const char *filename) /* I - File to print (NULL for stdin) */
|
|
{
|
|
int fd; /* Input file */
|
|
cups_raster_t *ras; /* Raster stream */
|
|
cups_page_header2_t header; /* Page header */
|
|
unsigned page = 0, /* Current page */
|
|
y; /* Current line */
|
|
unsigned char *line; /* Line buffer */
|
|
|
|
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
fputs("\033E", stdout);
|
|
|
|
while (cupsRasterReadHeader2(ras, &header))
|
|
{
|
|
page ++;
|
|
|
|
if (header.cupsColorSpace != CUPS_CSPACE_W && header.cupsColorSpace != CUPS_CSPACE_SW && header.cupsColorSpace != CUPS_CSPACE_K)
|
|
{
|
|
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);
|
|
|
|
pcl_start_page(&header, page);
|
|
for (y = 0; y < header.cupsHeight; y ++)
|
|
{
|
|
if (cupsRasterReadPixels(ras, line, header.cupsBytesPerLine))
|
|
pcl_write_line(&header, y, line);
|
|
else
|
|
break;
|
|
}
|
|
pcl_end_page(&header, page);
|
|
|
|
free(line);
|
|
}
|
|
|
|
cupsRasterClose(ras);
|
|
|
|
fprintf(stderr, "ATTR: job-impressions=%d\n", page);
|
|
|
|
return (0);
|
|
}
|