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
Setup(ppd_file_t * ppd)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
StartPage(ppd_file_t * ppd,cups_page_header2_t * header)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
EndPage(ppd_file_t * ppd,cups_page_header2_t * header)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
CancelJob(int sig)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
OutputLine(ppd_file_t * ppd,cups_page_header2_t * header,unsigned y)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
PCLCompress(unsigned char * line,unsigned length)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
ZPLCompress(unsigned char repeat_char,unsigned repeat_count)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 */
main(int argc,char * argv[])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