2 * "$Id: rastertolabel.c 4920 2006-01-12 15:12:12Z mike $"
4 * Label printer filter for the Common UNIX Printing System (CUPS).
6 * Copyright 2001-2006 by Easy Software Products.
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
24 * This file is subject to the Apple OS-Developed Software exception.
28 * Setup() - Prepare the printer for printing.
29 * StartPage() - Start a page of graphics.
30 * EndPage() - Finish a page of graphics.
31 * CancelJob() - Cancel the current job...
32 * OutputLine() - Output a line of graphics.
33 * ZPLCompress() - Output a run-length compression sequence.
34 * main() - Main entry and processing of driver.
38 * Include necessary headers...
41 #include <cups/cups.h>
42 #include <cups/string.h>
51 * This driver filter currently supports Dymo and Zebra label printers.
53 * The Dymo portion of the driver has been tested with the 300, 330,
54 * and 330 Turbo label printers; it may also work with older models.
55 * The Dymo printers support printing at 136, 203, and 300 DPI.
57 * The Zebra portion of the driver has been tested with the LP-2844Z label
58 * printer; it may also work with other models. The driver supports EPL
59 * line mode, EPL page mode, ZPL, and CPCL as defined in Zebra's on-line
60 * developer documentation.
64 * Model number constants...
67 #define DYMO_3x0 0 /* Dymo Labelwriter 300/330/330 Turbo */
69 #define ZEBRA_EPL_LINE 0x10 /* Zebra EPL line mode printers */
70 #define ZEBRA_EPL_PAGE 0x11 /* Zebra EPL page mode printers */
71 #define ZEBRA_ZPL 0x12 /* Zebra ZPL-based printers */
72 #define ZEBRA_CPCL 0x13 /* Zebra CPCL-based printers */
79 unsigned char *Buffer
; /* Output buffer */
80 char *CompBuffer
; /* Compression buffer */
81 unsigned char *LastBuffer
; /* Last buffer */
82 int LastSet
; /* Number of repeat characters */
83 int ModelNumber
, /* cupsModelNumber attribute */
84 Page
, /* Current page */
85 Feed
, /* Number of lines to skip */
86 Canceled
; /* Non-zero if job is canceled */
93 void Setup(ppd_file_t
*ppd
);
94 void StartPage(ppd_file_t
*ppd
, cups_page_header_t
*header
);
95 void EndPage(ppd_file_t
*ppd
, cups_page_header_t
*header
);
96 void CancelJob(int sig
);
97 void OutputLine(ppd_file_t
*ppd
, cups_page_header_t
*header
, int y
);
98 void ZPLCompress(char repeat_char
, int repeat_count
);
102 * 'Setup()' - Prepare the printer for printing.
106 Setup(ppd_file_t
*ppd
) /* I - PPD file */
108 int i
; /* Looping var */
112 * Get the model number from the PPD file...
116 ModelNumber
= ppd
->model_number
;
119 * Initialize based on the model number...
126 * Clear any remaining data...
129 for (i
= 0; i
< 100; i
++)
133 * Reset the printer...
136 fputs("\033@", stdout
);
139 case ZEBRA_EPL_LINE
:
142 case ZEBRA_EPL_PAGE
:
155 * 'StartPage()' - Start a page of graphics.
159 StartPage(ppd_file_t
*ppd
, /* I - PPD file */
160 cups_page_header_t
*header
) /* I - Page header */
162 ppd_choice_t
*choice
; /* Marked choice */
163 int length
; /* Actual label length */
164 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
165 struct sigaction action
; /* Actions for POSIX signals */
166 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
170 * Register a signal handler to eject the current page if the
174 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
175 sigset(SIGTERM
, CancelJob
);
176 #elif defined(HAVE_SIGACTION)
177 memset(&action
, 0, sizeof(action
));
179 sigemptyset(&action
.sa_mask
);
180 action
.sa_handler
= CancelJob
;
181 sigaction(SIGTERM
, &action
, NULL
);
183 signal(SIGTERM
, CancelJob
);
184 #endif /* HAVE_SIGSET */
190 * Setup printer/job attributes...
193 length
= header
->PageSize
[1] * header
->HWResolution
[1] / 72;
195 printf("\033L%c%c", length
>> 8, length
);
196 printf("\033D%c", header
->cupsBytesPerLine
);
198 printf("\033%c", header
->cupsCompression
+ 'c'); /* Darkness */
201 case ZEBRA_EPL_LINE
:
206 if ((choice
= ppdFindMarkedChoice(ppd
, "zePrintRate")) != NULL
&&
207 strcmp(choice
->choice
, "Default"))
208 printf("\033S%.0f", atof(choice
->choice
) * 2.0 - 2.0);
214 if (header
->cupsCompression
> 0 && header
->cupsCompression
<= 100)
215 printf("\033D%d", 7 * header
->cupsCompression
/ 100);
218 * Set left margin to 0...
221 fputs("\033M01", stdout
);
224 * Start buffered output...
227 fputs("\033B", stdout
);
230 case ZEBRA_EPL_PAGE
:
232 * Start a new label...
239 * Set hardware options...
242 if (!strcmp(header
->MediaType
, "Direct"))
249 if ((choice
= ppdFindMarkedChoice(ppd
, "zePrintRate")) != NULL
&&
250 strcmp(choice
->choice
, "Default"))
252 float val
= atof(choice
->choice
);
255 printf("S%.0f\n", val
);
257 printf("S%.0f\n", val
* 2.0 - 2.0);
264 if (header
->cupsCompression
> 0 && header
->cupsCompression
<= 100)
265 printf("D%d\n", 15 * header
->cupsCompression
/ 100);
271 printf("q%d\n", header
->cupsWidth
);
279 if (header
->cupsCompression
> 0 && header
->cupsCompression
<= 100)
280 printf("~SD%02d\n", 30 * header
->cupsCompression
/ 100);
283 * Start bitmap graphics...
286 printf("~DGR:CUPS.GRF,%d,%d,\n",
287 header
->cupsHeight
* header
->cupsBytesPerLine
,
288 header
->cupsBytesPerLine
);
291 * Allocate compression buffers...
294 CompBuffer
= malloc(2 * header
->cupsBytesPerLine
+ 1);
295 LastBuffer
= malloc(header
->cupsBytesPerLine
);
304 printf("! 0 %u %u %u %u\r\n", header
->HWResolution
[0],
305 header
->HWResolution
[1], header
->cupsHeight
,
311 * Allocate memory for a line of graphics...
314 Buffer
= malloc(header
->cupsBytesPerLine
);
320 * 'EndPage()' - Finish a page of graphics.
324 EndPage(ppd_file_t
*ppd
, /* I - PPD file */
325 cups_page_header_t
*header
) /* I - Page header */
327 int val
; /* Option value */
328 ppd_choice_t
*choice
; /* Marked choice */
329 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
330 struct sigaction action
; /* Actions for POSIX signals */
331 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
338 * Eject the current page...
341 fputs("\033E", stdout
);
344 case ZEBRA_EPL_LINE
:
346 * End buffered output, eject the label...
349 fputs("\033E\014", stdout
);
352 case ZEBRA_EPL_PAGE
:
364 * Cancel bitmap download...
381 if ((choice
= ppdFindMarkedChoice(ppd
, "zePrintRate")) != NULL
&&
382 strcmp(choice
->choice
, "Default"))
384 val
= atoi(choice
->choice
);
385 printf("^PR%d,%d,%d\n", val
, val
, val
);
389 * Put label home in default position (0,0)...
395 * Set media tracking...
398 if (ppdIsMarked(ppd
, "zeMediaTracking", "Continuous"))
401 * Add label length command for continuous...
404 printf("^LL%d\n", header
->cupsHeight
);
407 else if (ppdIsMarked(ppd
, "zeMediaTracking", "Web"))
409 else if (ppdIsMarked(ppd
, "zeMediaTracking", "Mark"))
416 if (header
->cupsRowStep
!= 200)
417 printf("^LT%u\n", header
->cupsRowStep
);
423 if (!strcmp(header
->MediaType
, "Thermal"))
425 else if (!strcmp(header
->MediaType
, "Direct"))
432 if ((choice
= ppdFindMarkedChoice(ppd
, "zePrintMode")) != NULL
&&
433 strcmp(choice
->choice
, "Saved"))
437 if (!strcmp(choice
->choice
, "Tear"))
439 else if (!strcmp(choice
->choice
, "Peel"))
441 else if (!strcmp(choice
->choice
, "Rewind"))
443 else if (!strcmp(choice
->choice
, "Applicator"))
450 * Set tear-off adjust position...
453 if (header
->AdvanceDistance
!= 1000)
455 if ((int)header
->AdvanceDistance
< 0)
456 printf("~TA%04d\n", (int)header
->AdvanceDistance
);
458 printf("~TA%03d\n", (int)header
->AdvanceDistance
);
462 * Allow for reprinting after an error...
465 if (ppdIsMarked(ppd
, "zeErrorReprint", "Always"))
467 else if (ppdIsMarked(ppd
, "zeErrorReprint", "Never"))
471 * Print multiple copies
474 if (header
->NumCopies
> 1)
475 printf("^PQ%d, 0, 0, N\n", header
->NumCopies
);
478 * Display the label image...
481 puts("^FO0,0^XGR:CUPS.GRF,1,1^FS");
484 * End the label and eject...
490 * Free compression buffers...
499 * Set tear-off adjust position...
502 if (header
->AdvanceDistance
!= 1000)
503 printf("PRESENT-AT %d 1\r\n", (int)header
->AdvanceDistance
);
506 * Allow for reprinting after an error...
509 if (ppdIsMarked(ppd
, "zeErrorReprint", "Always"))
510 puts("ON-OUT-OF-PAPER WAIT\r");
511 else if (ppdIsMarked(ppd
, "zeErrorReprint", "Never"))
512 puts("ON-OUT-OF-PAPER PURGE\r");
518 if (header
->CutMedia
)
525 if (header
->cupsCompression
> 0)
526 printf("TONE %u\r\n", 2 * header
->cupsCompression
);
532 if ((choice
= ppdFindMarkedChoice(ppd
, "zePrintRate")) != NULL
&&
533 strcmp(choice
->choice
, "Default"))
535 val
= atoi(choice
->choice
);
536 printf("SPEED %d\r\n", val
);
551 * Unregister the signal handler...
554 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
555 sigset(SIGTERM
, SIG_IGN
);
556 #elif defined(HAVE_SIGACTION)
557 memset(&action
, 0, sizeof(action
));
559 sigemptyset(&action
.sa_mask
);
560 action
.sa_handler
= SIG_IGN
;
561 sigaction(SIGTERM
, &action
, NULL
);
563 signal(SIGTERM
, SIG_IGN
);
564 #endif /* HAVE_SIGSET */
575 * 'CancelJob()' - Cancel the current job...
579 CancelJob(int sig
) /* I - Signal */
582 * Tell the main loop to stop...
592 * 'OutputLine()' - Output a line of graphics...
596 OutputLine(ppd_file_t
*ppd
, /* I - PPD file */
597 cups_page_header_t
*header
, /* I - Page header */
598 int y
) /* I - Line number */
600 int i
; /* Looping var */
601 unsigned char *ptr
; /* Pointer into buffer */
602 char *compptr
; /* Pointer into compression buffer */
603 char repeat_char
; /* Repeated character */
604 int repeat_count
; /* Number of repeated characters */
605 static const char *hex
= "0123456789ABCDEF";
613 * See if the line is blank; if not, write it to the printer...
617 memcmp(Buffer
, Buffer
+ 1, header
->cupsBytesPerLine
- 1))
623 printf("\033f\001%c", 255);
627 printf("\033f\001%c", Feed
);
632 fwrite(Buffer
, header
->cupsBytesPerLine
, 1, stdout
);
637 * This hack works around a bug in the IRIX serial port driver when
638 * run at high baud rates (e.g. 115200 baud)... This results in
639 * slightly slower label printing, but at least the labels come
650 case ZEBRA_EPL_LINE
:
651 printf("\033g%03d", header
->cupsBytesPerLine
);
652 fwrite(Buffer
, 1, header
->cupsBytesPerLine
, stdout
);
656 case ZEBRA_EPL_PAGE
:
657 if (Buffer
[0] || memcmp(Buffer
, Buffer
+ 1, header
->cupsBytesPerLine
))
659 printf("GW0,%d,%d,1\n", y
, header
->cupsBytesPerLine
);
660 for (i
= header
->cupsBytesPerLine
, ptr
= Buffer
; i
> 0; i
--, ptr
++)
669 * Determine if this row is the same as the previous line.
670 * If so, output a ':' and return...
675 if (!memcmp(Buffer
, LastBuffer
, header
->cupsBytesPerLine
))
683 * Convert the line to hex digits...
686 for (ptr
= Buffer
, compptr
= CompBuffer
, i
= header
->cupsBytesPerLine
;
690 *compptr
++ = hex
[*ptr
>> 4];
691 *compptr
++ = hex
[*ptr
& 15];
697 * Run-length compress the graphics...
700 for (compptr
= CompBuffer
, repeat_char
= CompBuffer
[0], repeat_count
= 1;
703 if (*compptr
== repeat_char
)
707 ZPLCompress(repeat_char
, repeat_count
);
708 repeat_char
= *compptr
;
712 if (repeat_char
== '0')
715 * Handle 0's on the end of the line...
718 if (repeat_count
& 1)
724 ZPLCompress(repeat_char
, repeat_count
);
727 * Save this line for the next round...
730 memcpy(LastBuffer
, Buffer
, header
->cupsBytesPerLine
);
735 if (Buffer
[0] || memcmp(Buffer
, Buffer
+ 1, header
->cupsBytesPerLine
))
737 printf("CG %u 1 0 %d ", header
->cupsBytesPerLine
, y
);
738 fwrite(Buffer
, 1, header
->cupsBytesPerLine
, stdout
);
748 * 'ZPLCompress()' - Output a run-length compression sequence.
752 ZPLCompress(char repeat_char
, /* I - Character to repeat */
753 int repeat_count
) /* I - Number of repeated characters */
755 if (repeat_count
> 1)
758 * Print as many z's as possible - they are the largest denomination
759 * representing 400 characters (zC stands for 400 adjacent C's)
762 while (repeat_count
>= 400)
769 * Then print 'g' through 'y' as multiples of 20 characters...
772 if (repeat_count
>= 20)
774 putchar('f' + repeat_count
/ 20);
779 * Finally, print 'G' through 'Y' as 1 through 19 characters...
782 if (repeat_count
> 0)
783 putchar('F' + repeat_count
);
787 * Then the character to be repeated...
790 putchar(repeat_char
);
795 * 'main()' - Main entry and processing of driver.
798 int /* O - Exit status */
799 main(int argc
, /* I - Number of command-line arguments */
800 char *argv
[]) /* I - Command-line arguments */
802 int fd
; /* File descriptor */
803 cups_raster_t
*ras
; /* Raster stream for printing */
804 cups_page_header_t header
; /* Page header from file */
805 int y
; /* Current line */
806 ppd_file_t
*ppd
; /* PPD file */
807 int num_options
; /* Number of options */
808 cups_option_t
*options
; /* Options */
812 * Make sure status messages are not buffered...
815 setbuf(stderr
, NULL
);
818 * Check command-line...
821 if (argc
< 6 || argc
> 7)
824 * We don't have the correct number of arguments; write an error message
828 fputs("ERROR: rastertodymo job-id user title copies options [file]\n", stderr
);
833 * Open the page stream...
838 if ((fd
= open(argv
[6], O_RDONLY
)) == -1)
840 perror("ERROR: Unable to open raster file - ");
848 ras
= cupsRasterOpen(fd
, CUPS_RASTER_READ
);
851 * Open the PPD file and apply options...
854 num_options
= cupsParseOptions(argv
[5], 0, &options
);
856 if ((ppd
= ppdOpenFile(getenv("PPD"))) != NULL
)
858 ppdMarkDefaults(ppd
);
859 cupsMarkOptions(ppd
, num_options
, options
);
863 * Initialize the print device...
869 * Process pages as needed...
875 while (cupsRasterReadHeader(ras
, &header
))
878 * Write a status message with the page number and number of copies.
883 fprintf(stderr
, "PAGE: %d 1\n", Page
);
889 StartPage(ppd
, &header
);
892 * Loop for each line on the page...
895 for (y
= 0; y
< header
.cupsHeight
&& !Canceled
; y
++)
898 * Let the user know how far we have progressed...
902 fprintf(stderr
, "INFO: Printing page %d, %d%% complete...\n", Page
,
903 100 * y
/ header
.cupsHeight
);
906 * Read a line of graphics...
909 if (cupsRasterReadPixels(ras
, Buffer
, header
.cupsBytesPerLine
) < 1)
913 * Write it to the printer...
916 OutputLine(ppd
, &header
, y
);
923 EndPage(ppd
, &header
);
930 * Close the raster stream...
933 cupsRasterClose(ras
);
938 * Close the PPD file and free the options...
942 cupsFreeOptions(num_options
, options
);
945 * If no pages were printed, send an error message...
949 fputs("ERROR: No pages found!\n", stderr
);
951 fputs("INFO: Ready to print.\n", stderr
);
958 * End of "$Id: rastertolabel.c 4920 2006-01-12 15:12:12Z mike $".