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
65unsigned char	*Buffer;		/* Output buffer */
66unsigned char	*CompBuffer;		/* Compression buffer */
67unsigned char	*LastBuffer;		/* Last buffer */
68unsigned	Feed;			/* Number of lines to skip */
69int		LastSet;		/* Number of repeat characters */
70int		ModelNumber,		/* cupsModelNumber attribute */
71		Page,			/* Current page */
72		Canceled;		/* Non-zero if job is canceled */
73
74
75/*
76 * Prototypes...
77 */
78
79void	Setup(ppd_file_t *ppd);
80void	StartPage(ppd_file_t *ppd, cups_page_header2_t *header);
81void	EndPage(ppd_file_t *ppd, cups_page_header2_t *header);
82void	CancelJob(int sig);
83void	OutputLine(ppd_file_t *ppd, cups_page_header2_t *header, unsigned y);
84void	PCLCompress(unsigned char *line, unsigned length);
85void	ZPLCompress(unsigned char repeat_char, unsigned repeat_count);
86
87
88/*
89 * 'Setup()' - Prepare the printer for printing.
90 */
91
92void
93Setup(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
154void
155StartPage(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
440void
441EndPage(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
717void
718CancelJob(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
734void
735OutputLine(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
904void
905PCLCompress(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
1037void
1038ZPLCompress(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
1084int					/* O - Exit status */
1085main(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