mirror of https://gitee.com/openkylin/cups.git
1295 lines
26 KiB
C
1295 lines
26 KiB
C
/*
|
||
* Label printer filter for CUPS.
|
||
*
|
||
* Copyright © 2007-2019 by Apple Inc.
|
||
* Copyright © 2001-2007 by Easy Software Products.
|
||
*
|
||
* Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||
* information.
|
||
*/
|
||
|
||
/*
|
||
* Include necessary headers...
|
||
*/
|
||
|
||
#include <cups/cups.h>
|
||
#include <cups/ppd.h>
|
||
#include <cups/string-private.h>
|
||
#include <cups/language-private.h>
|
||
#include <cups/raster.h>
|
||
#include <unistd.h>
|
||
#include <fcntl.h>
|
||
#include <signal.h>
|
||
|
||
|
||
/*
|
||
* This driver filter currently supports DYMO, Intellitech, and Zebra
|
||
* label printers.
|
||
*
|
||
* The DYMO portion of the driver has been tested with the 300, 330,
|
||
* 330 Turbo, and 450 Twin Turbo label printers; it may also work with other
|
||
* models. The DYMO printers support printing at 136, 203, and 300 DPI.
|
||
*
|
||
* The Intellitech portion of the driver has been tested with the
|
||
* Intellibar 408, 412, and 808 and supports their PCL variant.
|
||
*
|
||
* The Zebra portion of the driver has been tested with the LP-2844,
|
||
* LP-2844Z, QL-320, and QL-420 label printers; it may also work with
|
||
* other models. The driver supports EPL line mode, EPL page mode,
|
||
* ZPL, and CPCL as defined in Zebra's online developer documentation.
|
||
*/
|
||
|
||
/*
|
||
* Model number constants...
|
||
*/
|
||
|
||
#define DYMO_3x0 0 /* DYMO Labelwriter 300/330/330 Turbo */
|
||
|
||
#define ZEBRA_EPL_LINE 0x10 /* Zebra EPL line mode printers */
|
||
#define ZEBRA_EPL_PAGE 0x11 /* Zebra EPL page mode printers */
|
||
#define ZEBRA_ZPL 0x12 /* Zebra ZPL-based printers */
|
||
#define ZEBRA_CPCL 0x13 /* Zebra CPCL-based printers */
|
||
|
||
#define INTELLITECH_PCL 0x20 /* Intellitech PCL-based printers */
|
||
|
||
|
||
/*
|
||
* Globals...
|
||
*/
|
||
|
||
unsigned char *Buffer; /* Output buffer */
|
||
unsigned char *CompBuffer; /* Compression buffer */
|
||
unsigned char *LastBuffer; /* Last buffer */
|
||
unsigned Feed; /* Number of lines to skip */
|
||
int LastSet; /* Number of repeat characters */
|
||
int ModelNumber, /* cupsModelNumber attribute */
|
||
Page, /* Current page */
|
||
Canceled; /* Non-zero if job is canceled */
|
||
|
||
|
||
/*
|
||
* Prototypes...
|
||
*/
|
||
|
||
void Setup(ppd_file_t *ppd);
|
||
void StartPage(ppd_file_t *ppd, cups_page_header2_t *header);
|
||
void EndPage(ppd_file_t *ppd, cups_page_header2_t *header);
|
||
void CancelJob(int sig);
|
||
void OutputLine(ppd_file_t *ppd, cups_page_header2_t *header, unsigned y);
|
||
void PCLCompress(unsigned char *line, unsigned length);
|
||
void ZPLCompress(unsigned char repeat_char, unsigned repeat_count);
|
||
|
||
|
||
/*
|
||
* 'Setup()' - Prepare the printer for printing.
|
||
*/
|
||
|
||
void
|
||
Setup(ppd_file_t *ppd) /* I - PPD file */
|
||
{
|
||
int i; /* Looping var */
|
||
|
||
|
||
/*
|
||
* Get the model number from the PPD file...
|
||
*/
|
||
|
||
if (ppd)
|
||
ModelNumber = ppd->model_number;
|
||
|
||
/*
|
||
* Initialize based on the model number...
|
||
*/
|
||
|
||
switch (ModelNumber)
|
||
{
|
||
case DYMO_3x0 :
|
||
/*
|
||
* Clear any remaining data...
|
||
*/
|
||
|
||
for (i = 0; i < 100; i ++)
|
||
putchar(0x1b);
|
||
|
||
/*
|
||
* Reset the printer...
|
||
*/
|
||
|
||
fputs("\033@", stdout);
|
||
break;
|
||
|
||
case ZEBRA_EPL_LINE :
|
||
break;
|
||
|
||
case ZEBRA_EPL_PAGE :
|
||
break;
|
||
|
||
case ZEBRA_ZPL :
|
||
break;
|
||
|
||
case ZEBRA_CPCL :
|
||
break;
|
||
|
||
case INTELLITECH_PCL :
|
||
/*
|
||
* Send a PCL reset sequence.
|
||
*/
|
||
|
||
putchar(0x1b);
|
||
putchar('E');
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* 'StartPage()' - Start a page of graphics.
|
||
*/
|
||
|
||
void
|
||
StartPage(ppd_file_t *ppd, /* I - PPD file */
|
||
cups_page_header2_t *header) /* I - Page header */
|
||
{
|
||
ppd_choice_t *choice; /* Marked choice */
|
||
unsigned length; /* Actual label length */
|
||
|
||
|
||
/*
|
||
* Show page device dictionary...
|
||
*/
|
||
|
||
fprintf(stderr, "DEBUG: StartPage...\n");
|
||
fprintf(stderr, "DEBUG: Duplex = %d\n", header->Duplex);
|
||
fprintf(stderr, "DEBUG: HWResolution = [ %d %d ]\n", header->HWResolution[0], header->HWResolution[1]);
|
||
fprintf(stderr, "DEBUG: ImagingBoundingBox = [ %d %d %d %d ]\n", header->ImagingBoundingBox[0], header->ImagingBoundingBox[1], header->ImagingBoundingBox[2], header->ImagingBoundingBox[3]);
|
||
fprintf(stderr, "DEBUG: Margins = [ %d %d ]\n", header->Margins[0], header->Margins[1]);
|
||
fprintf(stderr, "DEBUG: ManualFeed = %d\n", header->ManualFeed);
|
||
fprintf(stderr, "DEBUG: MediaPosition = %d\n", header->MediaPosition);
|
||
fprintf(stderr, "DEBUG: NumCopies = %d\n", header->NumCopies);
|
||
fprintf(stderr, "DEBUG: Orientation = %d\n", header->Orientation);
|
||
fprintf(stderr, "DEBUG: PageSize = [ %d %d ]\n", header->PageSize[0], header->PageSize[1]);
|
||
fprintf(stderr, "DEBUG: cupsWidth = %d\n", header->cupsWidth);
|
||
fprintf(stderr, "DEBUG: cupsHeight = %d\n", header->cupsHeight);
|
||
fprintf(stderr, "DEBUG: cupsMediaType = %d\n", header->cupsMediaType);
|
||
fprintf(stderr, "DEBUG: cupsBitsPerColor = %d\n", header->cupsBitsPerColor);
|
||
fprintf(stderr, "DEBUG: cupsBitsPerPixel = %d\n", header->cupsBitsPerPixel);
|
||
fprintf(stderr, "DEBUG: cupsBytesPerLine = %d\n", header->cupsBytesPerLine);
|
||
fprintf(stderr, "DEBUG: cupsColorOrder = %d\n", header->cupsColorOrder);
|
||
fprintf(stderr, "DEBUG: cupsColorSpace = %d\n", header->cupsColorSpace);
|
||
fprintf(stderr, "DEBUG: cupsCompression = %d\n", header->cupsCompression);
|
||
|
||
switch (ModelNumber)
|
||
{
|
||
case DYMO_3x0 :
|
||
/*
|
||
* Setup printer/job attributes...
|
||
*/
|
||
|
||
length = header->PageSize[1] * header->HWResolution[1] / 72;
|
||
|
||
printf("\033L%c%c", length >> 8, length);
|
||
printf("\033D%c", header->cupsBytesPerLine);
|
||
|
||
printf("\033%c", header->cupsCompression + 'c'); /* Darkness */
|
||
printf("\033q%d", header->MediaPosition + 1); /* Roll Select */
|
||
break;
|
||
|
||
case ZEBRA_EPL_LINE :
|
||
/*
|
||
* Set print rate...
|
||
*/
|
||
|
||
if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL &&
|
||
strcmp(choice->choice, "Default"))
|
||
printf("\033S%.0f", atof(choice->choice) * 2.0 - 2.0);
|
||
|
||
/*
|
||
* Set darkness...
|
||
*/
|
||
|
||
if (header->cupsCompression > 0 && header->cupsCompression <= 100)
|
||
printf("\033D%d", 7 * header->cupsCompression / 100);
|
||
|
||
/*
|
||
* Set left margin to 0...
|
||
*/
|
||
|
||
fputs("\033M01", stdout);
|
||
|
||
/*
|
||
* Start buffered output...
|
||
*/
|
||
|
||
fputs("\033B", stdout);
|
||
break;
|
||
|
||
case ZEBRA_EPL_PAGE :
|
||
/*
|
||
* Start a new label...
|
||
*/
|
||
|
||
puts("");
|
||
puts("N");
|
||
|
||
/*
|
||
* Set hardware options...
|
||
*/
|
||
|
||
if (!strcmp(header->MediaType, "Direct"))
|
||
puts("OD");
|
||
|
||
/*
|
||
* Set print rate...
|
||
*/
|
||
|
||
if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL &&
|
||
strcmp(choice->choice, "Default"))
|
||
{
|
||
double val = atof(choice->choice);
|
||
|
||
if (val >= 3.0)
|
||
printf("S%.0f\n", val);
|
||
else
|
||
printf("S%.0f\n", val * 2.0 - 2.0);
|
||
}
|
||
|
||
/*
|
||
* Set darkness...
|
||
*/
|
||
|
||
if (header->cupsCompression > 0 && header->cupsCompression <= 100)
|
||
printf("D%u\n", 15 * header->cupsCompression / 100);
|
||
|
||
/*
|
||
* Set label size...
|
||
*/
|
||
|
||
printf("q%u\n", (header->cupsWidth + 7) & ~7U);
|
||
break;
|
||
|
||
case ZEBRA_ZPL :
|
||
/*
|
||
* Set darkness...
|
||
*/
|
||
|
||
if (header->cupsCompression > 0 && header->cupsCompression <= 100)
|
||
printf("~SD%02u\n", 30 * header->cupsCompression / 100);
|
||
|
||
/*
|
||
* Start bitmap graphics...
|
||
*/
|
||
|
||
printf("~DGR:CUPS.GRF,%u,%u,\n",
|
||
header->cupsHeight * header->cupsBytesPerLine,
|
||
header->cupsBytesPerLine);
|
||
|
||
/*
|
||
* Allocate compression buffers...
|
||
*/
|
||
|
||
CompBuffer = malloc(2 * header->cupsBytesPerLine + 1);
|
||
LastBuffer = malloc(header->cupsBytesPerLine);
|
||
LastSet = 0;
|
||
break;
|
||
|
||
case ZEBRA_CPCL :
|
||
/*
|
||
* Start label...
|
||
*/
|
||
|
||
printf("! 0 %u %u %u %u\r\n", header->HWResolution[0],
|
||
header->HWResolution[1], header->cupsHeight,
|
||
header->NumCopies);
|
||
printf("PAGE-WIDTH %u\r\n", header->cupsWidth);
|
||
printf("PAGE-HEIGHT %u\r\n", header->cupsHeight);
|
||
break;
|
||
|
||
case INTELLITECH_PCL :
|
||
/*
|
||
* Set the media size...
|
||
*/
|
||
|
||
printf("\033&l6D\033&k12H"); /* Set 6 LPI, 10 CPI */
|
||
printf("\033&l0O"); /* Set portrait orientation */
|
||
|
||
switch (header->PageSize[1])
|
||
{
|
||
case 540 : /* Monarch Envelope */
|
||
printf("\033&l80A"); /* Set page size */
|
||
break;
|
||
|
||
case 624 : /* DL Envelope */
|
||
printf("\033&l90A"); /* Set page size */
|
||
break;
|
||
|
||
case 649 : /* C5 Envelope */
|
||
printf("\033&l91A"); /* Set page size */
|
||
break;
|
||
|
||
case 684 : /* COM-10 Envelope */
|
||
printf("\033&l81A"); /* Set page size */
|
||
break;
|
||
|
||
case 756 : /* Executive */
|
||
printf("\033&l1A"); /* Set page size */
|
||
break;
|
||
|
||
case 792 : /* Letter */
|
||
printf("\033&l2A"); /* Set page size */
|
||
break;
|
||
|
||
case 842 : /* A4 */
|
||
printf("\033&l26A"); /* Set page size */
|
||
break;
|
||
|
||
case 1008 : /* Legal */
|
||
printf("\033&l3A"); /* Set page size */
|
||
break;
|
||
|
||
default : /* Custom size */
|
||
printf("\033!f%uZ", header->PageSize[1] * 300 / 72);
|
||
break;
|
||
}
|
||
|
||
printf("\033&l%uP", /* Set page length */
|
||
header->PageSize[1] / 12);
|
||
printf("\033&l0E"); /* Set top margin to 0 */
|
||
if (header->NumCopies)
|
||
printf("\033&l%uX", header->NumCopies);
|
||
/* Set number copies */
|
||
printf("\033&l0L"); /* Turn off perforation skip */
|
||
|
||
/*
|
||
* Print settings...
|
||
*/
|
||
|
||
if (Page == 1)
|
||
{
|
||
if (header->cupsRowFeed) /* inPrintRate */
|
||
printf("\033!p%uS", header->cupsRowFeed);
|
||
|
||
if (header->cupsCompression != ~0U)
|
||
/* inPrintDensity */
|
||
printf("\033&d%dA", 30 * header->cupsCompression / 100 - 15);
|
||
|
||
if ((choice = ppdFindMarkedChoice(ppd, "inPrintMode")) != NULL)
|
||
{
|
||
if (!strcmp(choice->choice, "Standard"))
|
||
fputs("\033!p0M", stdout);
|
||
else if (!strcmp(choice->choice, "Tear"))
|
||
{
|
||
fputs("\033!p1M", stdout);
|
||
|
||
if (header->cupsRowCount) /* inTearInterval */
|
||
printf("\033!n%uT", header->cupsRowCount);
|
||
}
|
||
else
|
||
{
|
||
fputs("\033!p2M", stdout);
|
||
|
||
if (header->cupsRowStep) /* inCutInterval */
|
||
printf("\033!n%uC", header->cupsRowStep);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Setup graphics...
|
||
*/
|
||
|
||
printf("\033*t%uR", header->HWResolution[0]);
|
||
/* Set resolution */
|
||
|
||
printf("\033*r%uS", header->cupsWidth);
|
||
/* Set width */
|
||
printf("\033*r%uT", header->cupsHeight);
|
||
/* Set height */
|
||
|
||
printf("\033&a0H"); /* Set horizontal position */
|
||
printf("\033&a0V"); /* Set vertical position */
|
||
printf("\033*r1A"); /* Start graphics */
|
||
printf("\033*b3M"); /* Set compression */
|
||
|
||
/*
|
||
* Allocate compression buffers...
|
||
*/
|
||
|
||
CompBuffer = malloc(2 * header->cupsBytesPerLine + 1);
|
||
LastBuffer = malloc(header->cupsBytesPerLine);
|
||
LastSet = 0;
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* Allocate memory for a line of graphics...
|
||
*/
|
||
|
||
Buffer = malloc(header->cupsBytesPerLine);
|
||
Feed = 0;
|
||
}
|
||
|
||
|
||
/*
|
||
* 'EndPage()' - Finish a page of graphics.
|
||
*/
|
||
|
||
void
|
||
EndPage(ppd_file_t *ppd, /* I - PPD file */
|
||
cups_page_header2_t *header) /* I - Page header */
|
||
{
|
||
int val; /* Option value */
|
||
ppd_choice_t *choice; /* Marked choice */
|
||
|
||
|
||
switch (ModelNumber)
|
||
{
|
||
case DYMO_3x0 :
|
||
/*
|
||
* Eject the current page...
|
||
*/
|
||
|
||
fputs("\033E", stdout);
|
||
break;
|
||
|
||
case ZEBRA_EPL_LINE :
|
||
/*
|
||
* End buffered output, eject the label...
|
||
*/
|
||
|
||
fputs("\033E\014", stdout);
|
||
break;
|
||
|
||
case ZEBRA_EPL_PAGE :
|
||
/*
|
||
* Print the label...
|
||
*/
|
||
|
||
puts("P1");
|
||
|
||
/*
|
||
* Cut the label as needed...
|
||
*/
|
||
|
||
if (header->CutMedia)
|
||
puts("C");
|
||
break;
|
||
|
||
case ZEBRA_ZPL :
|
||
if (Canceled)
|
||
{
|
||
/*
|
||
* Cancel bitmap download...
|
||
*/
|
||
|
||
puts("~DN");
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* Start label...
|
||
*/
|
||
|
||
puts("^XA");
|
||
|
||
/*
|
||
* Rotate 180 degrees so that the top of the label/page is at the
|
||
* leading edge...
|
||
*/
|
||
|
||
puts("^POI");
|
||
|
||
/*
|
||
* Set print width...
|
||
*/
|
||
|
||
printf("^PW%u\n", header->cupsWidth);
|
||
|
||
/*
|
||
* Set print rate...
|
||
*/
|
||
|
||
if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL &&
|
||
strcmp(choice->choice, "Default"))
|
||
{
|
||
val = atoi(choice->choice);
|
||
printf("^PR%d,%d,%d\n", val, val, val);
|
||
}
|
||
|
||
/*
|
||
* Put label home in default position (0,0)...
|
||
*/
|
||
|
||
printf("^LH0,0\n");
|
||
|
||
/*
|
||
* Set media tracking...
|
||
*/
|
||
|
||
if (ppdIsMarked(ppd, "zeMediaTracking", "Continuous"))
|
||
{
|
||
/*
|
||
* Add label length command for continuous...
|
||
*/
|
||
|
||
printf("^LL%d\n", header->cupsHeight);
|
||
printf("^MNN\n");
|
||
}
|
||
else if (ppdIsMarked(ppd, "zeMediaTracking", "Web"))
|
||
printf("^MNY\n");
|
||
else if (ppdIsMarked(ppd, "zeMediaTracking", "Mark"))
|
||
printf("^MNM\n");
|
||
|
||
/*
|
||
* Set label top
|
||
*/
|
||
|
||
if (header->cupsRowStep != 200)
|
||
printf("^LT%d\n", header->cupsRowStep);
|
||
|
||
/*
|
||
* Set media type...
|
||
*/
|
||
|
||
if (!strcmp(header->MediaType, "Thermal"))
|
||
printf("^MTT\n");
|
||
else if (!strcmp(header->MediaType, "Direct"))
|
||
printf("^MTD\n");
|
||
|
||
/*
|
||
* Set print mode...
|
||
*/
|
||
|
||
if ((choice = ppdFindMarkedChoice(ppd, "zePrintMode")) != NULL &&
|
||
strcmp(choice->choice, "Saved"))
|
||
{
|
||
printf("^MM");
|
||
|
||
if (!strcmp(choice->choice, "Tear"))
|
||
printf("T,Y\n");
|
||
else if (!strcmp(choice->choice, "Peel"))
|
||
printf("P,Y\n");
|
||
else if (!strcmp(choice->choice, "Rewind"))
|
||
printf("R,Y\n");
|
||
else if (!strcmp(choice->choice, "Applicator"))
|
||
printf("A,Y\n");
|
||
else
|
||
printf("C,Y\n");
|
||
}
|
||
|
||
/*
|
||
* Set tear-off adjust position...
|
||
*/
|
||
|
||
if (header->AdvanceDistance != 1000)
|
||
{
|
||
if ((int)header->AdvanceDistance < 0)
|
||
printf("~TA%04d\n", (int)header->AdvanceDistance);
|
||
else
|
||
printf("~TA%03d\n", (int)header->AdvanceDistance);
|
||
}
|
||
|
||
/*
|
||
* Allow for reprinting after an error...
|
||
*/
|
||
|
||
if (ppdIsMarked(ppd, "zeErrorReprint", "Always"))
|
||
printf("^JZY\n");
|
||
else if (ppdIsMarked(ppd, "zeErrorReprint", "Never"))
|
||
printf("^JZN\n");
|
||
|
||
/*
|
||
* Print multiple copies
|
||
*/
|
||
|
||
if (header->NumCopies > 1)
|
||
printf("^PQ%d, 0, 0, N\n", header->NumCopies);
|
||
|
||
/*
|
||
* Display the label image...
|
||
*/
|
||
|
||
puts("^FO0,0^XGR:CUPS.GRF,1,1^FS");
|
||
|
||
/*
|
||
* End the label and eject...
|
||
*/
|
||
|
||
puts("^XZ");
|
||
|
||
/*
|
||
* Delete the label image...
|
||
*/
|
||
|
||
puts("^XA");
|
||
puts("^IDR:CUPS.GRF^FS");
|
||
puts("^XZ");
|
||
|
||
/*
|
||
* Cut the label as needed...
|
||
*/
|
||
|
||
if (header->CutMedia)
|
||
puts("^CN1");
|
||
break;
|
||
|
||
case ZEBRA_CPCL :
|
||
/*
|
||
* Set tear-off adjust position...
|
||
*/
|
||
|
||
if (header->AdvanceDistance != 1000)
|
||
printf("PRESENT-AT %d 1\r\n", (int)header->AdvanceDistance);
|
||
|
||
/*
|
||
* Allow for reprinting after an error...
|
||
*/
|
||
|
||
if (ppdIsMarked(ppd, "zeErrorReprint", "Always"))
|
||
puts("ON-OUT-OF-PAPER WAIT\r");
|
||
else if (ppdIsMarked(ppd, "zeErrorReprint", "Never"))
|
||
puts("ON-OUT-OF-PAPER PURGE\r");
|
||
|
||
/*
|
||
* Cut label?
|
||
*/
|
||
|
||
if (header->CutMedia)
|
||
puts("CUT\r");
|
||
|
||
/*
|
||
* Set darkness...
|
||
*/
|
||
|
||
if (header->cupsCompression > 0)
|
||
printf("TONE %u\r\n", 2 * header->cupsCompression);
|
||
|
||
/*
|
||
* Set print rate...
|
||
*/
|
||
|
||
if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL &&
|
||
strcmp(choice->choice, "Default"))
|
||
{
|
||
val = atoi(choice->choice);
|
||
printf("SPEED %d\r\n", val);
|
||
}
|
||
|
||
/*
|
||
* Print the label...
|
||
*/
|
||
|
||
if ((choice = ppdFindMarkedChoice(ppd, "zeMediaTracking")) == NULL ||
|
||
strcmp(choice->choice, "Continuous"))
|
||
puts("FORM\r");
|
||
|
||
puts("PRINT\r");
|
||
break;
|
||
|
||
case INTELLITECH_PCL :
|
||
printf("\033*rB"); /* End GFX */
|
||
printf("\014"); /* Eject current page */
|
||
break;
|
||
}
|
||
|
||
fflush(stdout);
|
||
|
||
/*
|
||
* Free memory...
|
||
*/
|
||
|
||
free(Buffer);
|
||
|
||
if (CompBuffer)
|
||
{
|
||
free(CompBuffer);
|
||
CompBuffer = NULL;
|
||
}
|
||
|
||
if (LastBuffer)
|
||
{
|
||
free(LastBuffer);
|
||
LastBuffer = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* 'CancelJob()' - Cancel the current job...
|
||
*/
|
||
|
||
void
|
||
CancelJob(int sig) /* I - Signal */
|
||
{
|
||
/*
|
||
* Tell the main loop to stop...
|
||
*/
|
||
|
||
(void)sig;
|
||
|
||
Canceled = 1;
|
||
}
|
||
|
||
|
||
/*
|
||
* 'OutputLine()' - Output a line of graphics...
|
||
*/
|
||
|
||
void
|
||
OutputLine(ppd_file_t *ppd, /* I - PPD file */
|
||
cups_page_header2_t *header, /* I - Page header */
|
||
unsigned y) /* I - Line number */
|
||
{
|
||
unsigned i; /* Looping var */
|
||
unsigned char *ptr; /* Pointer into buffer */
|
||
unsigned char *compptr; /* Pointer into compression buffer */
|
||
unsigned char repeat_char; /* Repeated character */
|
||
unsigned repeat_count; /* Number of repeated characters */
|
||
static const unsigned char *hex = (const unsigned char *)"0123456789ABCDEF";
|
||
/* Hex digits */
|
||
|
||
|
||
(void)ppd;
|
||
|
||
switch (ModelNumber)
|
||
{
|
||
case DYMO_3x0 :
|
||
/*
|
||
* See if the line is blank; if not, write it to the printer...
|
||
*/
|
||
|
||
if (Buffer[0] ||
|
||
memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine - 1))
|
||
{
|
||
if (Feed)
|
||
{
|
||
while (Feed > 255)
|
||
{
|
||
printf("\033f\001%c", 255);
|
||
Feed -= 255;
|
||
}
|
||
|
||
printf("\033f\001%c", Feed);
|
||
Feed = 0;
|
||
}
|
||
|
||
putchar(0x16);
|
||
fwrite(Buffer, header->cupsBytesPerLine, 1, stdout);
|
||
fflush(stdout);
|
||
}
|
||
else
|
||
Feed ++;
|
||
break;
|
||
|
||
case ZEBRA_EPL_LINE :
|
||
printf("\033g%03d", header->cupsBytesPerLine);
|
||
fwrite(Buffer, 1, header->cupsBytesPerLine, stdout);
|
||
fflush(stdout);
|
||
break;
|
||
|
||
case ZEBRA_EPL_PAGE :
|
||
if (Buffer[0] || memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine))
|
||
{
|
||
printf("GW0,%d,%d,1\n", y, header->cupsBytesPerLine);
|
||
for (i = header->cupsBytesPerLine, ptr = Buffer; i > 0; i --, ptr ++)
|
||
putchar(~*ptr);
|
||
putchar('\n');
|
||
fflush(stdout);
|
||
}
|
||
break;
|
||
|
||
case ZEBRA_ZPL :
|
||
/*
|
||
* Determine if this row is the same as the previous line.
|
||
* If so, output a ':' and return...
|
||
*/
|
||
|
||
if (LastSet)
|
||
{
|
||
if (!memcmp(Buffer, LastBuffer, header->cupsBytesPerLine))
|
||
{
|
||
putchar(':');
|
||
return;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Convert the line to hex digits...
|
||
*/
|
||
|
||
for (ptr = Buffer, compptr = CompBuffer, i = header->cupsBytesPerLine;
|
||
i > 0;
|
||
i --, ptr ++)
|
||
{
|
||
*compptr++ = hex[*ptr >> 4];
|
||
*compptr++ = hex[*ptr & 15];
|
||
}
|
||
|
||
*compptr = '\0';
|
||
|
||
/*
|
||
* Run-length compress the graphics...
|
||
*/
|
||
|
||
for (compptr = CompBuffer + 1, repeat_char = CompBuffer[0], repeat_count = 1;
|
||
*compptr;
|
||
compptr ++)
|
||
if (*compptr == repeat_char)
|
||
repeat_count ++;
|
||
else
|
||
{
|
||
ZPLCompress(repeat_char, repeat_count);
|
||
repeat_char = *compptr;
|
||
repeat_count = 1;
|
||
}
|
||
|
||
if (repeat_char == '0')
|
||
{
|
||
/*
|
||
* Handle 0's on the end of the line...
|
||
*/
|
||
|
||
if (repeat_count & 1)
|
||
{
|
||
repeat_count --;
|
||
putchar('0');
|
||
}
|
||
|
||
if (repeat_count > 0)
|
||
putchar(',');
|
||
}
|
||
else
|
||
ZPLCompress(repeat_char, repeat_count);
|
||
|
||
fflush(stdout);
|
||
|
||
/*
|
||
* Save this line for the next round...
|
||
*/
|
||
|
||
memcpy(LastBuffer, Buffer, header->cupsBytesPerLine);
|
||
LastSet = 1;
|
||
break;
|
||
|
||
case ZEBRA_CPCL :
|
||
if (Buffer[0] || memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine))
|
||
{
|
||
printf("CG %u 1 0 %d ", header->cupsBytesPerLine, y);
|
||
fwrite(Buffer, 1, header->cupsBytesPerLine, stdout);
|
||
puts("\r");
|
||
fflush(stdout);
|
||
}
|
||
break;
|
||
|
||
case INTELLITECH_PCL :
|
||
if (Buffer[0] ||
|
||
memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine - 1))
|
||
{
|
||
if (Feed)
|
||
{
|
||
printf("\033*b%dY", Feed);
|
||
Feed = 0;
|
||
LastSet = 0;
|
||
}
|
||
|
||
PCLCompress(Buffer, header->cupsBytesPerLine);
|
||
}
|
||
else
|
||
Feed ++;
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* 'PCLCompress()' - Output a PCL (mode 3) compressed line.
|
||
*/
|
||
|
||
void
|
||
PCLCompress(unsigned char *line, /* I - Line to compress */
|
||
unsigned length) /* I - Length of line */
|
||
{
|
||
unsigned char *line_ptr, /* Current byte pointer */
|
||
*line_end, /* End-of-line byte pointer */
|
||
*comp_ptr, /* Pointer into compression buffer */
|
||
*start, /* Start of compression sequence */
|
||
*seed; /* Seed buffer pointer */
|
||
unsigned count, /* Count of bytes for output */
|
||
offset; /* Offset of bytes for output */
|
||
|
||
|
||
/*
|
||
* Do delta-row compression...
|
||
*/
|
||
|
||
line_ptr = line;
|
||
line_end = line + length;
|
||
|
||
comp_ptr = CompBuffer;
|
||
seed = LastBuffer;
|
||
|
||
while (line_ptr < line_end)
|
||
{
|
||
/*
|
||
* Find the next non-matching sequence...
|
||
*/
|
||
|
||
start = line_ptr;
|
||
|
||
if (!LastSet)
|
||
{
|
||
/*
|
||
* The seed buffer is invalid, so do the next 8 bytes, max...
|
||
*/
|
||
|
||
offset = 0;
|
||
|
||
if ((count = (unsigned)(line_end - line_ptr)) > 8)
|
||
count = 8;
|
||
|
||
line_ptr += count;
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* The seed buffer is valid, so compare against it...
|
||
*/
|
||
|
||
while (*line_ptr == *seed &&
|
||
line_ptr < line_end)
|
||
{
|
||
line_ptr ++;
|
||
seed ++;
|
||
}
|
||
|
||
if (line_ptr == line_end)
|
||
break;
|
||
|
||
offset = (unsigned)(line_ptr - start);
|
||
|
||
/*
|
||
* Find up to 8 non-matching bytes...
|
||
*/
|
||
|
||
start = line_ptr;
|
||
count = 0;
|
||
while (*line_ptr != *seed &&
|
||
line_ptr < line_end &&
|
||
count < 8)
|
||
{
|
||
line_ptr ++;
|
||
seed ++;
|
||
count ++;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Place mode 3 compression data in the buffer; see HP manuals
|
||
* for details...
|
||
*/
|
||
|
||
if (offset >= 31)
|
||
{
|
||
/*
|
||
* Output multi-byte offset...
|
||
*/
|
||
|
||
*comp_ptr++ = (unsigned char)(((count - 1) << 5) | 31);
|
||
|
||
offset -= 31;
|
||
while (offset >= 255)
|
||
{
|
||
*comp_ptr++ = 255;
|
||
offset -= 255;
|
||
}
|
||
|
||
*comp_ptr++ = (unsigned char)offset;
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* Output single-byte offset...
|
||
*/
|
||
|
||
*comp_ptr++ = (unsigned char)(((count - 1) << 5) | offset);
|
||
}
|
||
|
||
memcpy(comp_ptr, start, count);
|
||
comp_ptr += count;
|
||
}
|
||
|
||
/*
|
||
* Set the length of the data and write it...
|
||
*/
|
||
|
||
printf("\033*b%dW", (int)(comp_ptr - CompBuffer));
|
||
fwrite(CompBuffer, (size_t)(comp_ptr - CompBuffer), 1, stdout);
|
||
|
||
/*
|
||
* Save this line as a "seed" buffer for the next...
|
||
*/
|
||
|
||
memcpy(LastBuffer, line, length);
|
||
LastSet = 1;
|
||
}
|
||
|
||
|
||
/*
|
||
* 'ZPLCompress()' - Output a run-length compression sequence.
|
||
*/
|
||
|
||
void
|
||
ZPLCompress(unsigned char repeat_char, /* I - Character to repeat */
|
||
unsigned repeat_count) /* I - Number of repeated characters */
|
||
{
|
||
if (repeat_count > 1)
|
||
{
|
||
/*
|
||
* Print as many z's as possible - they are the largest denomination
|
||
* representing 400 characters (zC stands for 400 adjacent C's)
|
||
*/
|
||
|
||
while (repeat_count >= 400)
|
||
{
|
||
putchar('z');
|
||
repeat_count -= 400;
|
||
}
|
||
|
||
/*
|
||
* Then print 'g' through 'y' as multiples of 20 characters...
|
||
*/
|
||
|
||
if (repeat_count >= 20)
|
||
{
|
||
putchar((int)('f' + repeat_count / 20));
|
||
repeat_count %= 20;
|
||
}
|
||
|
||
/*
|
||
* Finally, print 'G' through 'Y' as 1 through 19 characters...
|
||
*/
|
||
|
||
if (repeat_count > 0)
|
||
putchar((int)('F' + repeat_count));
|
||
}
|
||
|
||
/*
|
||
* Then the character to be repeated...
|
||
*/
|
||
|
||
putchar((int)repeat_char);
|
||
}
|
||
|
||
|
||
/*
|
||
* 'main()' - Main entry and processing of driver.
|
||
*/
|
||
|
||
int /* O - Exit status */
|
||
main(int argc, /* I - Number of command-line arguments */
|
||
char *argv[]) /* I - Command-line arguments */
|
||
{
|
||
int fd; /* File descriptor */
|
||
cups_raster_t *ras; /* Raster stream for printing */
|
||
cups_page_header2_t header; /* Page header from file */
|
||
unsigned y; /* Current line */
|
||
ppd_file_t *ppd; /* PPD file */
|
||
int num_options; /* Number of options */
|
||
cups_option_t *options; /* Options */
|
||
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
|
||
struct sigaction action; /* Actions for POSIX signals */
|
||
#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
|
||
|
||
|
||
/*
|
||
* Make sure status messages are not buffered...
|
||
*/
|
||
|
||
setbuf(stderr, NULL);
|
||
|
||
/*
|
||
* Check command-line...
|
||
*/
|
||
|
||
if (argc != 6 && argc != 7)
|
||
{
|
||
/*
|
||
* We don't have the correct number of arguments; write an error message
|
||
* and return.
|
||
*/
|
||
|
||
_cupsLangPrintFilter(stderr, "ERROR",
|
||
_("%s job-id user title copies options [file]"),
|
||
"rastertolabel");
|
||
return (1);
|
||
}
|
||
|
||
/*
|
||
* Open the page stream...
|
||
*/
|
||
|
||
if (argc == 7)
|
||
{
|
||
if ((fd = open(argv[6], O_RDONLY)) == -1)
|
||
{
|
||
_cupsLangPrintError("ERROR", _("Unable to open raster file"));
|
||
sleep(1);
|
||
return (1);
|
||
}
|
||
}
|
||
else
|
||
fd = 0;
|
||
|
||
ras = cupsRasterOpen(fd, CUPS_RASTER_READ);
|
||
|
||
/*
|
||
* Register a signal handler to eject the current page if the
|
||
* job is cancelled.
|
||
*/
|
||
|
||
Canceled = 0;
|
||
|
||
#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
|
||
sigset(SIGTERM, CancelJob);
|
||
#elif defined(HAVE_SIGACTION)
|
||
memset(&action, 0, sizeof(action));
|
||
|
||
sigemptyset(&action.sa_mask);
|
||
action.sa_handler = CancelJob;
|
||
sigaction(SIGTERM, &action, NULL);
|
||
#else
|
||
signal(SIGTERM, CancelJob);
|
||
#endif /* HAVE_SIGSET */
|
||
|
||
/*
|
||
* Open the PPD file and apply options...
|
||
*/
|
||
|
||
num_options = cupsParseOptions(argv[5], 0, &options);
|
||
|
||
ppd = ppdOpenFile(getenv("PPD"));
|
||
if (!ppd)
|
||
{
|
||
ppd_status_t status; /* PPD error */
|
||
int linenum; /* Line number */
|
||
|
||
_cupsLangPrintFilter(stderr, "ERROR",
|
||
_("The PPD file could not be opened."));
|
||
|
||
status = ppdLastError(&linenum);
|
||
|
||
fprintf(stderr, "DEBUG: %s on line %d.\n", ppdErrorString(status), linenum);
|
||
|
||
return (1);
|
||
}
|
||
|
||
ppdMarkDefaults(ppd);
|
||
cupsMarkOptions(ppd, num_options, options);
|
||
|
||
/*
|
||
* Initialize the print device...
|
||
*/
|
||
|
||
Setup(ppd);
|
||
|
||
/*
|
||
* Process pages as needed...
|
||
*/
|
||
|
||
Page = 0;
|
||
|
||
while (cupsRasterReadHeader2(ras, &header))
|
||
{
|
||
/*
|
||
* Write a status message with the page number and number of copies.
|
||
*/
|
||
|
||
if (Canceled)
|
||
break;
|
||
|
||
Page ++;
|
||
|
||
fprintf(stderr, "PAGE: %d 1\n", Page);
|
||
_cupsLangPrintFilter(stderr, "INFO", _("Starting page %d."), Page);
|
||
|
||
/*
|
||
* Start the page...
|
||
*/
|
||
|
||
StartPage(ppd, &header);
|
||
|
||
/*
|
||
* Loop for each line on the page...
|
||
*/
|
||
|
||
for (y = 0; y < header.cupsHeight; y ++)
|
||
{
|
||
/*
|
||
* Let the user know how far we have progressed...
|
||
*/
|
||
|
||
if (Canceled)
|
||
break;
|
||
|
||
if ((y & 15) == 0)
|
||
{
|
||
_cupsLangPrintFilter(stderr, "INFO",
|
||
_("Printing page %d, %u%% complete."),
|
||
Page, 100 * y / header.cupsHeight);
|
||
fprintf(stderr, "ATTR: job-media-progress=%u\n",
|
||
100 * y / header.cupsHeight);
|
||
}
|
||
|
||
/*
|
||
* Read a line of graphics...
|
||
*/
|
||
|
||
if (cupsRasterReadPixels(ras, Buffer, header.cupsBytesPerLine) < 1)
|
||
break;
|
||
|
||
/*
|
||
* Write it to the printer...
|
||
*/
|
||
|
||
OutputLine(ppd, &header, y);
|
||
}
|
||
|
||
/*
|
||
* Eject the page...
|
||
*/
|
||
|
||
_cupsLangPrintFilter(stderr, "INFO", _("Finished page %d."), Page);
|
||
|
||
EndPage(ppd, &header);
|
||
|
||
if (Canceled)
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* Close the raster stream...
|
||
*/
|
||
|
||
cupsRasterClose(ras);
|
||
if (fd != 0)
|
||
close(fd);
|
||
|
||
/*
|
||
* Close the PPD file and free the options...
|
||
*/
|
||
|
||
ppdClose(ppd);
|
||
cupsFreeOptions(num_options, options);
|
||
|
||
/*
|
||
* If no pages were printed, send an error message...
|
||
*/
|
||
|
||
if (Page == 0)
|
||
{
|
||
_cupsLangPrintFilter(stderr, "ERROR", _("No pages were found."));
|
||
return (1);
|
||
}
|
||
else
|
||
return (0);
|
||
}
|