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