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