Home | History | Annotate | Download | only in filter
      1 /*
      2  * Label printer filter for CUPS.
      3  *
      4  * Copyright 2007-2016 by Apple Inc.
      5  * Copyright 2001-2007 by Easy Software Products.
      6  *
      7  * These coded instructions, statements, and computer programs are the
      8  * property of Apple Inc. and are protected by Federal copyright
      9  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
     10  * which should have been included with this file.  If this file is
     11  * missing or damaged, see the license at "http://www.cups.org/".
     12  *
     13  * This file is subject to the Apple OS-Developed Software exception.
     14  */
     15 
     16 /*
     17  * Include necessary headers...
     18  */
     19 
     20 #include <cups/cups.h>
     21 #include <cups/ppd.h>
     22 #include <cups/string-private.h>
     23 #include <cups/language-private.h>
     24 #include <cups/raster.h>
     25 #include <unistd.h>
     26 #include <fcntl.h>
     27 #include <signal.h>
     28 
     29 
     30 /*
     31  * This driver filter currently supports Dymo, Intellitech, and Zebra
     32  * label printers.
     33  *
     34  * The Dymo portion of the driver has been tested with the 300, 330,
     35  * and 330 Turbo label printers; it may also work with other models.
     36  * The Dymo printers support printing at 136, 203, and 300 DPI.
     37  *
     38  * The Intellitech portion of the driver has been tested with the
     39  * Intellibar 408, 412, and 808 and supports their PCL variant.
     40  *
     41  * The Zebra portion of the driver has been tested with the LP-2844,
     42  * LP-2844Z, QL-320, and QL-420 label printers; it may also work with
     43  * other models.  The driver supports EPL line mode, EPL page mode,
     44  * ZPL, and CPCL as defined in Zebra's online developer documentation.
     45  */
     46 
     47 /*
     48  * Model number constants...
     49  */
     50 
     51 #define DYMO_3x0	0		/* Dymo Labelwriter 300/330/330 Turbo */
     52 
     53 #define ZEBRA_EPL_LINE	0x10		/* Zebra EPL line mode printers */
     54 #define ZEBRA_EPL_PAGE	0x11		/* Zebra EPL page mode printers */
     55 #define ZEBRA_ZPL	0x12		/* Zebra ZPL-based printers */
     56 #define ZEBRA_CPCL	0x13		/* Zebra CPCL-based printers */
     57 
     58 #define INTELLITECH_PCL	0x20		/* Intellitech PCL-based printers */
     59 
     60 
     61 /*
     62  * Globals...
     63  */
     64 
     65 unsigned char	*Buffer;		/* Output buffer */
     66 unsigned char	*CompBuffer;		/* Compression buffer */
     67 unsigned char	*LastBuffer;		/* Last buffer */
     68 unsigned	Feed;			/* Number of lines to skip */
     69 int		LastSet;		/* Number of repeat characters */
     70 int		ModelNumber,		/* cupsModelNumber attribute */
     71 		Page,			/* Current page */
     72 		Canceled;		/* Non-zero if job is canceled */
     73 
     74 
     75 /*
     76  * Prototypes...
     77  */
     78 
     79 void	Setup(ppd_file_t *ppd);
     80 void	StartPage(ppd_file_t *ppd, cups_page_header2_t *header);
     81 void	EndPage(ppd_file_t *ppd, cups_page_header2_t *header);
     82 void	CancelJob(int sig);
     83 void	OutputLine(ppd_file_t *ppd, cups_page_header2_t *header, unsigned y);
     84 void	PCLCompress(unsigned char *line, unsigned length);
     85 void	ZPLCompress(unsigned char repeat_char, unsigned repeat_count);
     86 
     87 
     88 /*
     89  * 'Setup()' - Prepare the printer for printing.
     90  */
     91 
     92 void
     93 Setup(ppd_file_t *ppd)			/* I - PPD file */
     94 {
     95   int		i;			/* Looping var */
     96 
     97 
     98  /*
     99   * Get the model number from the PPD file...
    100   */
    101 
    102   if (ppd)
    103     ModelNumber = ppd->model_number;
    104 
    105  /*
    106   * Initialize based on the model number...
    107   */
    108 
    109   switch (ModelNumber)
    110   {
    111     case DYMO_3x0 :
    112        /*
    113 	* Clear any remaining data...
    114 	*/
    115 
    116 	for (i = 0; i < 100; i ++)
    117 	  putchar(0x1b);
    118 
    119        /*
    120 	* Reset the printer...
    121 	*/
    122 
    123 	fputs("\033@", stdout);
    124 	break;
    125 
    126     case ZEBRA_EPL_LINE :
    127 	break;
    128 
    129     case ZEBRA_EPL_PAGE :
    130 	break;
    131 
    132     case ZEBRA_ZPL :
    133         break;
    134 
    135     case ZEBRA_CPCL :
    136         break;
    137 
    138     case INTELLITECH_PCL :
    139        /*
    140 	* Send a PCL reset sequence.
    141 	*/
    142 
    143 	putchar(0x1b);
    144 	putchar('E');
    145         break;
    146   }
    147 }
    148 
    149 
    150 /*
    151  * 'StartPage()' - Start a page of graphics.
    152  */
    153 
    154 void
    155 StartPage(ppd_file_t         *ppd,	/* I - PPD file */
    156           cups_page_header2_t *header)	/* I - Page header */
    157 {
    158   ppd_choice_t	*choice;		/* Marked choice */
    159   unsigned	length;			/* Actual label length */
    160 
    161 
    162  /*
    163   * Show page device dictionary...
    164   */
    165 
    166   fprintf(stderr, "DEBUG: StartPage...\n");
    167   fprintf(stderr, "DEBUG: Duplex = %d\n", header->Duplex);
    168   fprintf(stderr, "DEBUG: HWResolution = [ %d %d ]\n", header->HWResolution[0], header->HWResolution[1]);
    169   fprintf(stderr, "DEBUG: ImagingBoundingBox = [ %d %d %d %d ]\n", header->ImagingBoundingBox[0], header->ImagingBoundingBox[1], header->ImagingBoundingBox[2], header->ImagingBoundingBox[3]);
    170   fprintf(stderr, "DEBUG: Margins = [ %d %d ]\n", header->Margins[0], header->Margins[1]);
    171   fprintf(stderr, "DEBUG: ManualFeed = %d\n", header->ManualFeed);
    172   fprintf(stderr, "DEBUG: MediaPosition = %d\n", header->MediaPosition);
    173   fprintf(stderr, "DEBUG: NumCopies = %d\n", header->NumCopies);
    174   fprintf(stderr, "DEBUG: Orientation = %d\n", header->Orientation);
    175   fprintf(stderr, "DEBUG: PageSize = [ %d %d ]\n", header->PageSize[0], header->PageSize[1]);
    176   fprintf(stderr, "DEBUG: cupsWidth = %d\n", header->cupsWidth);
    177   fprintf(stderr, "DEBUG: cupsHeight = %d\n", header->cupsHeight);
    178   fprintf(stderr, "DEBUG: cupsMediaType = %d\n", header->cupsMediaType);
    179   fprintf(stderr, "DEBUG: cupsBitsPerColor = %d\n", header->cupsBitsPerColor);
    180   fprintf(stderr, "DEBUG: cupsBitsPerPixel = %d\n", header->cupsBitsPerPixel);
    181   fprintf(stderr, "DEBUG: cupsBytesPerLine = %d\n", header->cupsBytesPerLine);
    182   fprintf(stderr, "DEBUG: cupsColorOrder = %d\n", header->cupsColorOrder);
    183   fprintf(stderr, "DEBUG: cupsColorSpace = %d\n", header->cupsColorSpace);
    184   fprintf(stderr, "DEBUG: cupsCompression = %d\n", header->cupsCompression);
    185 
    186   switch (ModelNumber)
    187   {
    188     case DYMO_3x0 :
    189        /*
    190 	* Setup printer/job attributes...
    191 	*/
    192 
    193 	length = header->PageSize[1] * header->HWResolution[1] / 72;
    194 
    195 	printf("\033L%c%c", length >> 8, length);
    196 	printf("\033D%c", header->cupsBytesPerLine);
    197 
    198 	printf("\033%c", header->cupsCompression + 'c'); /* Darkness */
    199 	break;
    200 
    201     case ZEBRA_EPL_LINE :
    202        /*
    203         * Set print rate...
    204 	*/
    205 
    206 	if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL &&
    207 	    strcmp(choice->choice, "Default"))
    208 	  printf("\033S%.0f", atof(choice->choice) * 2.0 - 2.0);
    209 
    210        /*
    211         * Set darkness...
    212 	*/
    213 
    214         if (header->cupsCompression > 0 && header->cupsCompression <= 100)
    215 	  printf("\033D%d", 7 * header->cupsCompression / 100);
    216 
    217        /*
    218         * Set left margin to 0...
    219 	*/
    220 
    221 	fputs("\033M01", stdout);
    222 
    223        /*
    224         * Start buffered output...
    225 	*/
    226 
    227         fputs("\033B", stdout);
    228         break;
    229 
    230     case ZEBRA_EPL_PAGE :
    231        /*
    232         * Start a new label...
    233 	*/
    234 
    235         puts("");
    236 	puts("N");
    237 
    238        /*
    239         * Set hardware options...
    240 	*/
    241 
    242 	if (!strcmp(header->MediaType, "Direct"))
    243 	  puts("OD");
    244 
    245        /*
    246         * Set print rate...
    247 	*/
    248 
    249 	if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL &&
    250 	    strcmp(choice->choice, "Default"))
    251 	{
    252 	  double val = atof(choice->choice);
    253 
    254 	  if (val >= 3.0)
    255 	    printf("S%.0f\n", val);
    256 	  else
    257 	    printf("S%.0f\n", val * 2.0 - 2.0);
    258         }
    259 
    260        /*
    261         * Set darkness...
    262 	*/
    263 
    264         if (header->cupsCompression > 0 && header->cupsCompression <= 100)
    265 	  printf("D%u\n", 15 * header->cupsCompression / 100);
    266 
    267        /*
    268         * Set label size...
    269 	*/
    270 
    271         printf("q%u\n", (header->cupsWidth + 7) & ~7U);
    272         break;
    273 
    274     case ZEBRA_ZPL :
    275        /*
    276         * Set darkness...
    277 	*/
    278 
    279         if (header->cupsCompression > 0 && header->cupsCompression <= 100)
    280 	  printf("~SD%02u\n", 30 * header->cupsCompression / 100);
    281 
    282        /*
    283         * Start bitmap graphics...
    284 	*/
    285 
    286         printf("~DGR:CUPS.GRF,%u,%u,\n",
    287 	       header->cupsHeight * header->cupsBytesPerLine,
    288 	       header->cupsBytesPerLine);
    289 
    290        /*
    291         * Allocate compression buffers...
    292 	*/
    293 
    294 	CompBuffer = malloc(2 * header->cupsBytesPerLine + 1);
    295 	LastBuffer = malloc(header->cupsBytesPerLine);
    296 	LastSet    = 0;
    297         break;
    298 
    299     case ZEBRA_CPCL :
    300        /*
    301         * Start label...
    302 	*/
    303 
    304         printf("! 0 %u %u %u %u\r\n", header->HWResolution[0],
    305 	       header->HWResolution[1], header->cupsHeight,
    306 	       header->NumCopies);
    307 	printf("PAGE-WIDTH %u\r\n", header->cupsWidth);
    308 	printf("PAGE-HEIGHT %u\r\n", header->cupsWidth);
    309         break;
    310 
    311     case INTELLITECH_PCL :
    312        /*
    313         * Set the media size...
    314 	*/
    315 
    316 	printf("\033&l6D\033&k12H");	/* Set 6 LPI, 10 CPI */
    317 	printf("\033&l0O");		/* Set portrait orientation */
    318 
    319 	switch (header->PageSize[1])
    320 	{
    321 	  case 540 : /* Monarch Envelope */
    322               printf("\033&l80A");	/* Set page size */
    323 	      break;
    324 
    325 	  case 624 : /* DL Envelope */
    326               printf("\033&l90A");	/* Set page size */
    327 	      break;
    328 
    329 	  case 649 : /* C5 Envelope */
    330               printf("\033&l91A");	/* Set page size */
    331 	      break;
    332 
    333 	  case 684 : /* COM-10 Envelope */
    334               printf("\033&l81A");	/* Set page size */
    335 	      break;
    336 
    337 	  case 756 : /* Executive */
    338               printf("\033&l1A");	/* Set page size */
    339 	      break;
    340 
    341 	  case 792 : /* Letter */
    342               printf("\033&l2A");	/* Set page size */
    343 	      break;
    344 
    345 	  case 842 : /* A4 */
    346               printf("\033&l26A");	/* Set page size */
    347 	      break;
    348 
    349 	  case 1008 : /* Legal */
    350               printf("\033&l3A");	/* Set page size */
    351 	      break;
    352 
    353           default : /* Custom size */
    354 	      printf("\033!f%uZ", header->PageSize[1] * 300 / 72);
    355 	      break;
    356 	}
    357 
    358 	printf("\033&l%uP",		/* Set page length */
    359                header->PageSize[1] / 12);
    360 	printf("\033&l0E");		/* Set top margin to 0 */
    361         if (header->NumCopies)
    362 	  printf("\033&l%uX", header->NumCopies);
    363 					/* Set number copies */
    364         printf("\033&l0L");		/* Turn off perforation skip */
    365 
    366        /*
    367         * Print settings...
    368 	*/
    369 
    370 	if (Page == 1)
    371 	{
    372           if (header->cupsRowFeed)	/* inPrintRate */
    373 	    printf("\033!p%uS", header->cupsRowFeed);
    374 
    375           if (header->cupsCompression != ~0U)
    376 	  				/* inPrintDensity */
    377 	    printf("\033&d%uA", 30 * header->cupsCompression / 100 - 15);
    378 
    379 	  if ((choice = ppdFindMarkedChoice(ppd, "inPrintMode")) != NULL)
    380 	  {
    381 	    if (!strcmp(choice->choice, "Standard"))
    382 	      fputs("\033!p0M", stdout);
    383 	    else if (!strcmp(choice->choice, "Tear"))
    384 	    {
    385 	      fputs("\033!p1M", stdout);
    386 
    387               if (header->cupsRowCount)	/* inTearInterval */
    388 		printf("\033!n%uT", header->cupsRowCount);
    389             }
    390 	    else
    391 	    {
    392 	      fputs("\033!p2M", stdout);
    393 
    394               if (header->cupsRowStep)	/* inCutInterval */
    395 		printf("\033!n%uC", header->cupsRowStep);
    396             }
    397 	  }
    398         }
    399 
    400        /*
    401 	* Setup graphics...
    402 	*/
    403 
    404 	printf("\033*t%uR", header->HWResolution[0]);
    405 					/* Set resolution */
    406 
    407 	printf("\033*r%uS", header->cupsWidth);
    408 					/* Set width */
    409 	printf("\033*r%uT", header->cupsHeight);
    410 					/* Set height */
    411 
    412 	printf("\033&a0H");		/* Set horizontal position */
    413 	printf("\033&a0V");		/* Set vertical position */
    414         printf("\033*r1A");		/* Start graphics */
    415         printf("\033*b3M");		/* Set compression */
    416 
    417        /*
    418         * Allocate compression buffers...
    419 	*/
    420 
    421 	CompBuffer = malloc(2 * header->cupsBytesPerLine + 1);
    422 	LastBuffer = malloc(header->cupsBytesPerLine);
    423 	LastSet    = 0;
    424         break;
    425   }
    426 
    427  /*
    428   * Allocate memory for a line of graphics...
    429   */
    430 
    431   Buffer = malloc(header->cupsBytesPerLine);
    432   Feed   = 0;
    433 }
    434 
    435 
    436 /*
    437  * 'EndPage()' - Finish a page of graphics.
    438  */
    439 
    440 void
    441 EndPage(ppd_file_t          *ppd,	/* I - PPD file */
    442         cups_page_header2_t *header)	/* I - Page header */
    443 {
    444   int		val;			/* Option value */
    445   ppd_choice_t	*choice;		/* Marked choice */
    446 
    447 
    448   switch (ModelNumber)
    449   {
    450     case DYMO_3x0 :
    451        /*
    452 	* Eject the current page...
    453 	*/
    454 
    455 	fputs("\033E", stdout);
    456 	break;
    457 
    458     case ZEBRA_EPL_LINE :
    459        /*
    460         * End buffered output, eject the label...
    461 	*/
    462 
    463         fputs("\033E\014", stdout);
    464 	break;
    465 
    466     case ZEBRA_EPL_PAGE :
    467        /*
    468         * Print the label...
    469 	*/
    470 
    471         puts("P1");
    472 
    473        /*
    474         * Cut the label as needed...
    475         */
    476 
    477       	if (header->CutMedia)
    478 	  puts("C");
    479 	break;
    480 
    481     case ZEBRA_ZPL :
    482         if (Canceled)
    483 	{
    484 	 /*
    485 	  * Cancel bitmap download...
    486 	  */
    487 
    488 	  puts("~DN");
    489 	  break;
    490 	}
    491 
    492        /*
    493         * Start label...
    494 	*/
    495 
    496         puts("^XA");
    497 
    498        /*
    499         * Rotate 180 degrees so that the top of the label/page is at the
    500 	* leading edge...
    501 	*/
    502 
    503 	puts("^POI");
    504 
    505        /*
    506         * Set print width...
    507 	*/
    508 
    509         printf("^PW%u\n", header->cupsWidth);
    510 
    511        /*
    512         * Set print rate...
    513 	*/
    514 
    515 	if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL &&
    516 	    strcmp(choice->choice, "Default"))
    517 	{
    518 	  val = atoi(choice->choice);
    519 	  printf("^PR%d,%d,%d\n", val, val, val);
    520 	}
    521 
    522        /*
    523         * Put label home in default position (0,0)...
    524         */
    525 
    526 	printf("^LH0,0\n");
    527 
    528        /*
    529         * Set media tracking...
    530 	*/
    531 
    532 	if (ppdIsMarked(ppd, "zeMediaTracking", "Continuous"))
    533 	{
    534          /*
    535 	  * Add label length command for continuous...
    536 	  */
    537 
    538 	  printf("^LL%d\n", header->cupsHeight);
    539 	  printf("^MNN\n");
    540 	}
    541 	else if (ppdIsMarked(ppd, "zeMediaTracking", "Web"))
    542           printf("^MNY\n");
    543 	else if (ppdIsMarked(ppd, "zeMediaTracking", "Mark"))
    544 	  printf("^MNM\n");
    545 
    546        /*
    547         * Set label top
    548 	*/
    549 
    550 	if (header->cupsRowStep != 200)
    551 	  printf("^LT%d\n", header->cupsRowStep);
    552 
    553        /*
    554         * Set media type...
    555 	*/
    556 
    557 	if (!strcmp(header->MediaType, "Thermal"))
    558 	  printf("^MTT\n");
    559 	else if (!strcmp(header->MediaType, "Direct"))
    560 	  printf("^MTD\n");
    561 
    562        /*
    563         * Set print mode...
    564 	*/
    565 
    566 	if ((choice = ppdFindMarkedChoice(ppd, "zePrintMode")) != NULL &&
    567 	    strcmp(choice->choice, "Saved"))
    568 	{
    569 	  printf("^MM");
    570 
    571 	  if (!strcmp(choice->choice, "Tear"))
    572 	    printf("T,Y\n");
    573 	  else if (!strcmp(choice->choice, "Peel"))
    574 	    printf("P,Y\n");
    575 	  else if (!strcmp(choice->choice, "Rewind"))
    576 	    printf("R,Y\n");
    577 	  else if (!strcmp(choice->choice, "Applicator"))
    578 	    printf("A,Y\n");
    579 	  else
    580 	    printf("C,Y\n");
    581 	}
    582 
    583        /*
    584         * Set tear-off adjust position...
    585 	*/
    586 
    587 	if (header->AdvanceDistance != 1000)
    588 	{
    589 	  if ((int)header->AdvanceDistance < 0)
    590 	    printf("~TA%04d\n", (int)header->AdvanceDistance);
    591 	  else
    592 	    printf("~TA%03d\n", (int)header->AdvanceDistance);
    593 	}
    594 
    595        /*
    596         * Allow for reprinting after an error...
    597 	*/
    598 
    599 	if (ppdIsMarked(ppd, "zeErrorReprint", "Always"))
    600 	  printf("^JZY\n");
    601 	else if (ppdIsMarked(ppd, "zeErrorReprint", "Never"))
    602 	  printf("^JZN\n");
    603 
    604        /*
    605         * Print multiple copies
    606 	*/
    607 
    608 	if (header->NumCopies > 1)
    609 	  printf("^PQ%d, 0, 0, N\n", header->NumCopies);
    610 
    611        /*
    612         * Display the label image...
    613 	*/
    614 
    615 	puts("^FO0,0^XGR:CUPS.GRF,1,1^FS");
    616 
    617        /*
    618         * End the label and eject...
    619 	*/
    620 
    621 	puts("^XZ");
    622         puts("^IDR:CUPS.GRF^FS");
    623 
    624        /*
    625         * Cut the label as needed...
    626         */
    627 
    628       	if (header->CutMedia)
    629 	  puts("^CN1");
    630         break;
    631 
    632     case ZEBRA_CPCL :
    633        /*
    634         * Set tear-off adjust position...
    635 	*/
    636 
    637 	if (header->AdvanceDistance != 1000)
    638           printf("PRESENT-AT %d 1\r\n", (int)header->AdvanceDistance);
    639 
    640        /*
    641         * Allow for reprinting after an error...
    642 	*/
    643 
    644 	if (ppdIsMarked(ppd, "zeErrorReprint", "Always"))
    645 	  puts("ON-OUT-OF-PAPER WAIT\r");
    646 	else if (ppdIsMarked(ppd, "zeErrorReprint", "Never"))
    647 	  puts("ON-OUT-OF-PAPER PURGE\r");
    648 
    649        /*
    650         * Cut label?
    651 	*/
    652 
    653 	if (header->CutMedia)
    654 	  puts("CUT\r");
    655 
    656        /*
    657         * Set darkness...
    658 	*/
    659 
    660 	if (header->cupsCompression > 0)
    661 	  printf("TONE %u\r\n", 2 * header->cupsCompression);
    662 
    663        /*
    664         * Set print rate...
    665 	*/
    666 
    667 	if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL &&
    668 	    strcmp(choice->choice, "Default"))
    669 	{
    670 	  val = atoi(choice->choice);
    671 	  printf("SPEED %d\r\n", val);
    672 	}
    673 
    674        /*
    675         * Print the label...
    676 	*/
    677 
    678 	if ((choice = ppdFindMarkedChoice(ppd, "zeMediaTracking")) == NULL ||
    679 	    strcmp(choice->choice, "Continuous"))
    680           puts("FORM\r");
    681 
    682 	puts("PRINT\r");
    683 	break;
    684 
    685     case INTELLITECH_PCL :
    686         printf("\033*rB");		/* End GFX */
    687         printf("\014");			/* Eject current page */
    688         break;
    689   }
    690 
    691   fflush(stdout);
    692 
    693  /*
    694   * Free memory...
    695   */
    696 
    697   free(Buffer);
    698 
    699   if (CompBuffer)
    700   {
    701     free(CompBuffer);
    702     CompBuffer = NULL;
    703   }
    704 
    705   if (LastBuffer)
    706   {
    707     free(LastBuffer);
    708     LastBuffer = NULL;
    709   }
    710 }
    711 
    712 
    713 /*
    714  * 'CancelJob()' - Cancel the current job...
    715  */
    716 
    717 void
    718 CancelJob(int sig)			/* I - Signal */
    719 {
    720  /*
    721   * Tell the main loop to stop...
    722   */
    723 
    724   (void)sig;
    725 
    726   Canceled = 1;
    727 }
    728 
    729 
    730 /*
    731  * 'OutputLine()' - Output a line of graphics...
    732  */
    733 
    734 void
    735 OutputLine(ppd_file_t         *ppd,	/* I - PPD file */
    736            cups_page_header2_t *header,	/* I - Page header */
    737            unsigned           y)	/* I - Line number */
    738 {
    739   unsigned	i;			/* Looping var */
    740   unsigned char	*ptr;			/* Pointer into buffer */
    741   unsigned char	*compptr;		/* Pointer into compression buffer */
    742   unsigned char	repeat_char;		/* Repeated character */
    743   unsigned	repeat_count;		/* Number of repeated characters */
    744   static const unsigned char *hex = (const unsigned char *)"0123456789ABCDEF";
    745 					/* Hex digits */
    746 
    747 
    748   (void)ppd;
    749 
    750   switch (ModelNumber)
    751   {
    752     case DYMO_3x0 :
    753        /*
    754 	* See if the line is blank; if not, write it to the printer...
    755 	*/
    756 
    757 	if (Buffer[0] ||
    758             memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine - 1))
    759 	{
    760           if (Feed)
    761 	  {
    762 	    while (Feed > 255)
    763 	    {
    764 	      printf("\033f\001%c", 255);
    765 	      Feed -= 255;
    766 	    }
    767 
    768 	    printf("\033f\001%c", Feed);
    769 	    Feed = 0;
    770           }
    771 
    772           putchar(0x16);
    773 	  fwrite(Buffer, header->cupsBytesPerLine, 1, stdout);
    774 	  fflush(stdout);
    775 	}
    776 	else
    777           Feed ++;
    778 	break;
    779 
    780     case ZEBRA_EPL_LINE :
    781         printf("\033g%03d", header->cupsBytesPerLine);
    782 	fwrite(Buffer, 1, header->cupsBytesPerLine, stdout);
    783 	fflush(stdout);
    784         break;
    785 
    786     case ZEBRA_EPL_PAGE :
    787         if (Buffer[0] || memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine))
    788 	{
    789           printf("GW0,%d,%d,1\n", y, header->cupsBytesPerLine);
    790 	  for (i = header->cupsBytesPerLine, ptr = Buffer; i > 0; i --, ptr ++)
    791 	    putchar(~*ptr);
    792 	  putchar('\n');
    793 	  fflush(stdout);
    794 	}
    795         break;
    796 
    797     case ZEBRA_ZPL :
    798        /*
    799 	* Determine if this row is the same as the previous line.
    800         * If so, output a ':' and return...
    801         */
    802 
    803         if (LastSet)
    804 	{
    805 	  if (!memcmp(Buffer, LastBuffer, header->cupsBytesPerLine))
    806 	  {
    807 	    putchar(':');
    808 	    return;
    809 	  }
    810 	}
    811 
    812        /*
    813         * Convert the line to hex digits...
    814 	*/
    815 
    816 	for (ptr = Buffer, compptr = CompBuffer, i = header->cupsBytesPerLine;
    817 	     i > 0;
    818 	     i --, ptr ++)
    819         {
    820 	  *compptr++ = hex[*ptr >> 4];
    821 	  *compptr++ = hex[*ptr & 15];
    822 	}
    823 
    824         *compptr = '\0';
    825 
    826        /*
    827         * Run-length compress the graphics...
    828 	*/
    829 
    830 	for (compptr = CompBuffer + 1, repeat_char = CompBuffer[0], repeat_count = 1;
    831 	     *compptr;
    832 	     compptr ++)
    833 	  if (*compptr == repeat_char)
    834 	    repeat_count ++;
    835 	  else
    836 	  {
    837 	    ZPLCompress(repeat_char, repeat_count);
    838 	    repeat_char  = *compptr;
    839 	    repeat_count = 1;
    840 	  }
    841 
    842         if (repeat_char == '0')
    843 	{
    844 	 /*
    845 	  * Handle 0's on the end of the line...
    846 	  */
    847 
    848 	  if (repeat_count & 1)
    849 	  {
    850 	    repeat_count --;
    851 	    putchar('0');
    852 	  }
    853 
    854           if (repeat_count > 0)
    855 	    putchar(',');
    856 	}
    857 	else
    858 	  ZPLCompress(repeat_char, repeat_count);
    859 
    860 	fflush(stdout);
    861 
    862        /*
    863         * Save this line for the next round...
    864 	*/
    865 
    866 	memcpy(LastBuffer, Buffer, header->cupsBytesPerLine);
    867 	LastSet = 1;
    868         break;
    869 
    870     case ZEBRA_CPCL :
    871         if (Buffer[0] || memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine))
    872 	{
    873 	  printf("CG %u 1 0 %d ", header->cupsBytesPerLine, y);
    874           fwrite(Buffer, 1, header->cupsBytesPerLine, stdout);
    875 	  puts("\r");
    876 	  fflush(stdout);
    877 	}
    878 	break;
    879 
    880     case INTELLITECH_PCL :
    881 	if (Buffer[0] ||
    882             memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine - 1))
    883         {
    884 	  if (Feed)
    885 	  {
    886 	    printf("\033*b%dY", Feed);
    887 	    Feed    = 0;
    888 	    LastSet = 0;
    889 	  }
    890 
    891           PCLCompress(Buffer, header->cupsBytesPerLine);
    892 	}
    893 	else
    894 	  Feed ++;
    895         break;
    896   }
    897 }
    898 
    899 
    900 /*
    901  * 'PCLCompress()' - Output a PCL (mode 3) compressed line.
    902  */
    903 
    904 void
    905 PCLCompress(unsigned char *line,	/* I - Line to compress */
    906             unsigned      length)	/* I - Length of line */
    907 {
    908   unsigned char	*line_ptr,		/* Current byte pointer */
    909         	*line_end,		/* End-of-line byte pointer */
    910         	*comp_ptr,		/* Pointer into compression buffer */
    911         	*start,			/* Start of compression sequence */
    912 		*seed;			/* Seed buffer pointer */
    913   unsigned	count,			/* Count of bytes for output */
    914 		offset;			/* Offset of bytes for output */
    915 
    916 
    917  /*
    918   * Do delta-row compression...
    919   */
    920 
    921   line_ptr = line;
    922   line_end = line + length;
    923 
    924   comp_ptr = CompBuffer;
    925   seed     = LastBuffer;
    926 
    927   while (line_ptr < line_end)
    928   {
    929    /*
    930     * Find the next non-matching sequence...
    931     */
    932 
    933     start = line_ptr;
    934 
    935     if (!LastSet)
    936     {
    937      /*
    938       * The seed buffer is invalid, so do the next 8 bytes, max...
    939       */
    940 
    941       offset = 0;
    942 
    943       if ((count = (unsigned)(line_end - line_ptr)) > 8)
    944 	count = 8;
    945 
    946       line_ptr += count;
    947     }
    948     else
    949     {
    950      /*
    951       * The seed buffer is valid, so compare against it...
    952       */
    953 
    954       while (*line_ptr == *seed &&
    955              line_ptr < line_end)
    956       {
    957         line_ptr ++;
    958         seed ++;
    959       }
    960 
    961       if (line_ptr == line_end)
    962         break;
    963 
    964       offset = (unsigned)(line_ptr - start);
    965 
    966      /*
    967       * Find up to 8 non-matching bytes...
    968       */
    969 
    970       start = line_ptr;
    971       count = 0;
    972       while (*line_ptr != *seed &&
    973              line_ptr < line_end &&
    974              count < 8)
    975       {
    976         line_ptr ++;
    977         seed ++;
    978         count ++;
    979       }
    980     }
    981 
    982    /*
    983     * Place mode 3 compression data in the buffer; see HP manuals
    984     * for details...
    985     */
    986 
    987     if (offset >= 31)
    988     {
    989      /*
    990       * Output multi-byte offset...
    991       */
    992 
    993       *comp_ptr++ = (unsigned char)(((count - 1) << 5) | 31);
    994 
    995       offset -= 31;
    996       while (offset >= 255)
    997       {
    998         *comp_ptr++ = 255;
    999         offset    -= 255;
   1000       }
   1001 
   1002       *comp_ptr++ = (unsigned char)offset;
   1003     }
   1004     else
   1005     {
   1006      /*
   1007       * Output single-byte offset...
   1008       */
   1009 
   1010       *comp_ptr++ = (unsigned char)(((count - 1) << 5) | offset);
   1011     }
   1012 
   1013     memcpy(comp_ptr, start, count);
   1014     comp_ptr += count;
   1015   }
   1016 
   1017  /*
   1018   * Set the length of the data and write it...
   1019   */
   1020 
   1021   printf("\033*b%dW", (int)(comp_ptr - CompBuffer));
   1022   fwrite(CompBuffer, (size_t)(comp_ptr - CompBuffer), 1, stdout);
   1023 
   1024  /*
   1025   * Save this line as a "seed" buffer for the next...
   1026   */
   1027 
   1028   memcpy(LastBuffer, line, length);
   1029   LastSet = 1;
   1030 }
   1031 
   1032 
   1033 /*
   1034  * 'ZPLCompress()' - Output a run-length compression sequence.
   1035  */
   1036 
   1037 void
   1038 ZPLCompress(unsigned char repeat_char,	/* I - Character to repeat */
   1039 	    unsigned      repeat_count)	/* I - Number of repeated characters */
   1040 {
   1041   if (repeat_count > 1)
   1042   {
   1043    /*
   1044     * Print as many z's as possible - they are the largest denomination
   1045     * representing 400 characters (zC stands for 400 adjacent C's)
   1046     */
   1047 
   1048     while (repeat_count >= 400)
   1049     {
   1050       putchar('z');
   1051       repeat_count -= 400;
   1052     }
   1053 
   1054    /*
   1055     * Then print 'g' through 'y' as multiples of 20 characters...
   1056     */
   1057 
   1058     if (repeat_count >= 20)
   1059     {
   1060       putchar((int)('f' + repeat_count / 20));
   1061       repeat_count %= 20;
   1062     }
   1063 
   1064    /*
   1065     * Finally, print 'G' through 'Y' as 1 through 19 characters...
   1066     */
   1067 
   1068     if (repeat_count > 0)
   1069       putchar((int)('F' + repeat_count));
   1070   }
   1071 
   1072  /*
   1073   * Then the character to be repeated...
   1074   */
   1075 
   1076   putchar((int)repeat_char);
   1077 }
   1078 
   1079 
   1080 /*
   1081  * 'main()' - Main entry and processing of driver.
   1082  */
   1083 
   1084 int					/* O - Exit status */
   1085 main(int  argc,				/* I - Number of command-line arguments */
   1086      char *argv[])			/* I - Command-line arguments */
   1087 {
   1088   int			fd;		/* File descriptor */
   1089   cups_raster_t		*ras;		/* Raster stream for printing */
   1090   cups_page_header2_t	header;		/* Page header from file */
   1091   unsigned		y;		/* Current line */
   1092   ppd_file_t		*ppd;		/* PPD file */
   1093   int			num_options;	/* Number of options */
   1094   cups_option_t		*options;	/* Options */
   1095 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
   1096   struct sigaction action;		/* Actions for POSIX signals */
   1097 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
   1098 
   1099 
   1100  /*
   1101   * Make sure status messages are not buffered...
   1102   */
   1103 
   1104   setbuf(stderr, NULL);
   1105 
   1106  /*
   1107   * Check command-line...
   1108   */
   1109 
   1110   if (argc < 6 || argc > 7)
   1111   {
   1112    /*
   1113     * We don't have the correct number of arguments; write an error message
   1114     * and return.
   1115     */
   1116 
   1117     _cupsLangPrintFilter(stderr, "ERROR",
   1118                          _("%s job-id user title copies options [file]"),
   1119 			 "rastertolabel");
   1120     return (1);
   1121   }
   1122 
   1123  /*
   1124   * Open the page stream...
   1125   */
   1126 
   1127   if (argc == 7)
   1128   {
   1129     if ((fd = open(argv[6], O_RDONLY)) == -1)
   1130     {
   1131       _cupsLangPrintError("ERROR", _("Unable to open raster file"));
   1132       sleep(1);
   1133       return (1);
   1134     }
   1135   }
   1136   else
   1137     fd = 0;
   1138 
   1139   ras = cupsRasterOpen(fd, CUPS_RASTER_READ);
   1140 
   1141  /*
   1142   * Register a signal handler to eject the current page if the
   1143   * job is cancelled.
   1144   */
   1145 
   1146   Canceled = 0;
   1147 
   1148 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
   1149   sigset(SIGTERM, CancelJob);
   1150 #elif defined(HAVE_SIGACTION)
   1151   memset(&action, 0, sizeof(action));
   1152 
   1153   sigemptyset(&action.sa_mask);
   1154   action.sa_handler = CancelJob;
   1155   sigaction(SIGTERM, &action, NULL);
   1156 #else
   1157   signal(SIGTERM, CancelJob);
   1158 #endif /* HAVE_SIGSET */
   1159 
   1160  /*
   1161   * Open the PPD file and apply options...
   1162   */
   1163 
   1164   num_options = cupsParseOptions(argv[5], 0, &options);
   1165 
   1166   ppd = ppdOpenFile(getenv("PPD"));
   1167   if (!ppd)
   1168   {
   1169     ppd_status_t	status;		/* PPD error */
   1170     int			linenum;	/* Line number */
   1171 
   1172     _cupsLangPrintFilter(stderr, "ERROR",
   1173                          _("The PPD file could not be opened."));
   1174 
   1175     status = ppdLastError(&linenum);
   1176 
   1177     fprintf(stderr, "DEBUG: %s on line %d.\n", ppdErrorString(status), linenum);
   1178 
   1179     return (1);
   1180   }
   1181 
   1182   ppdMarkDefaults(ppd);
   1183   cupsMarkOptions(ppd, num_options, options);
   1184 
   1185  /*
   1186   * Initialize the print device...
   1187   */
   1188 
   1189   Setup(ppd);
   1190 
   1191  /*
   1192   * Process pages as needed...
   1193   */
   1194 
   1195   Page = 0;
   1196 
   1197   while (cupsRasterReadHeader2(ras, &header))
   1198   {
   1199    /*
   1200     * Write a status message with the page number and number of copies.
   1201     */
   1202 
   1203     if (Canceled)
   1204       break;
   1205 
   1206     Page ++;
   1207 
   1208     fprintf(stderr, "PAGE: %d 1\n", Page);
   1209     _cupsLangPrintFilter(stderr, "INFO", _("Starting page %d."), Page);
   1210 
   1211    /*
   1212     * Start the page...
   1213     */
   1214 
   1215     StartPage(ppd, &header);
   1216 
   1217    /*
   1218     * Loop for each line on the page...
   1219     */
   1220 
   1221     for (y = 0; y < header.cupsHeight && !Canceled; y ++)
   1222     {
   1223      /*
   1224       * Let the user know how far we have progressed...
   1225       */
   1226 
   1227       if (Canceled)
   1228 	break;
   1229 
   1230       if ((y & 15) == 0)
   1231       {
   1232         _cupsLangPrintFilter(stderr, "INFO",
   1233 	                     _("Printing page %d, %u%% complete."),
   1234 			     Page, 100 * y / header.cupsHeight);
   1235         fprintf(stderr, "ATTR: job-media-progress=%u\n",
   1236 		100 * y / header.cupsHeight);
   1237       }
   1238 
   1239      /*
   1240       * Read a line of graphics...
   1241       */
   1242 
   1243       if (cupsRasterReadPixels(ras, Buffer, header.cupsBytesPerLine) < 1)
   1244         break;
   1245 
   1246      /*
   1247       * Write it to the printer...
   1248       */
   1249 
   1250       OutputLine(ppd, &header, y);
   1251     }
   1252 
   1253    /*
   1254     * Eject the page...
   1255     */
   1256 
   1257     _cupsLangPrintFilter(stderr, "INFO", _("Finished page %d."), Page);
   1258 
   1259     EndPage(ppd, &header);
   1260 
   1261     if (Canceled)
   1262       break;
   1263   }
   1264 
   1265  /*
   1266   * Close the raster stream...
   1267   */
   1268 
   1269   cupsRasterClose(ras);
   1270   if (fd != 0)
   1271     close(fd);
   1272 
   1273  /*
   1274   * Close the PPD file and free the options...
   1275   */
   1276 
   1277   ppdClose(ppd);
   1278   cupsFreeOptions(num_options, options);
   1279 
   1280  /*
   1281   * If no pages were printed, send an error message...
   1282   */
   1283 
   1284   if (Page == 0)
   1285   {
   1286     _cupsLangPrintFilter(stderr, "ERROR", _("No pages were found."));
   1287     return (1);
   1288   }
   1289   else
   1290     return (0);
   1291 }
   1292