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