]> git.ipfire.org Git - thirdparty/cups.git/blob - filter/rastertolabel.c
Load cups into easysw/current.
[thirdparty/cups.git] / filter / rastertolabel.c
1 /*
2 * "$Id: rastertolabel.c 4920 2006-01-12 15:12:12Z mike $"
3 *
4 * Label printer filter for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2001-2006 by Easy Software Products.
7 *
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
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
19 *
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * This file is subject to the Apple OS-Developed Software exception.
25 *
26 * Contents:
27 *
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.
35 */
36
37 /*
38 * Include necessary headers...
39 */
40
41 #include <cups/cups.h>
42 #include <cups/string.h>
43 #include "raster.h"
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <fcntl.h>
47 #include <signal.h>
48
49
50 /*
51 * This driver filter currently supports Dymo and Zebra label printers.
52 *
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.
56 *
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.
61 */
62
63 /*
64 * Model number constants...
65 */
66
67 #define DYMO_3x0 0 /* Dymo Labelwriter 300/330/330 Turbo */
68
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 */
73
74
75 /*
76 * Globals...
77 */
78
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 */
87
88
89 /*
90 * Prototypes...
91 */
92
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);
99
100
101 /*
102 * 'Setup()' - Prepare the printer for printing.
103 */
104
105 void
106 Setup(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 }
152
153
154 /*
155 * 'StartPage()' - Start a page of graphics.
156 */
157
158 void
159 StartPage(ppd_file_t *ppd, /* I - PPD file */
160 cups_page_header_t *header) /* I - Page header */
161 {
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 */
167
168
169 /*
170 * Register a signal handler to eject the current page if the
171 * job is canceled.
172 */
173
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));
178
179 sigemptyset(&action.sa_mask);
180 action.sa_handler = CancelJob;
181 sigaction(SIGTERM, &action, NULL);
182 #else
183 signal(SIGTERM, CancelJob);
184 #endif /* HAVE_SIGSET */
185
186 switch (ModelNumber)
187 {
188 case DYMO_3x0 :
189 /*
190 * Setup printer/job attributes...
191 */
192
193 length = header->PageSize[1] * header->HWResolution[1] / 72;
194
195 printf("\033L%c%c", length >> 8, length);
196 printf("\033D%c", header->cupsBytesPerLine);
197
198 printf("\033%c", header->cupsCompression + 'c'); /* Darkness */
199 break;
200
201 case ZEBRA_EPL_LINE :
202 /*
203 * Set print rate...
204 */
205
206 if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL &&
207 strcmp(choice->choice, "Default"))
208 printf("\033S%.0f", atof(choice->choice) * 2.0 - 2.0);
209
210 /*
211 * Set darkness...
212 */
213
214 if (header->cupsCompression > 0 && header->cupsCompression <= 100)
215 printf("\033D%d", 7 * header->cupsCompression / 100);
216
217 /*
218 * Set left margin to 0...
219 */
220
221 fputs("\033M01", stdout);
222
223 /*
224 * Start buffered output...
225 */
226
227 fputs("\033B", stdout);
228 break;
229
230 case ZEBRA_EPL_PAGE :
231 /*
232 * Start a new label...
233 */
234
235 puts("");
236 puts("N");
237
238 /*
239 * Set hardware options...
240 */
241
242 if (!strcmp(header->MediaType, "Direct"))
243 puts("OD");
244
245 /*
246 * Set print rate...
247 */
248
249 if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL &&
250 strcmp(choice->choice, "Default"))
251 {
252 float val = atof(choice->choice);
253
254 if (val >= 3.0)
255 printf("S%.0f\n", val);
256 else
257 printf("S%.0f\n", val * 2.0 - 2.0);
258 }
259
260 /*
261 * Set darkness...
262 */
263
264 if (header->cupsCompression > 0 && header->cupsCompression <= 100)
265 printf("D%d\n", 15 * header->cupsCompression / 100);
266
267 /*
268 * Set label size...
269 */
270
271 printf("q%d\n", header->cupsWidth);
272 break;
273
274 case ZEBRA_ZPL :
275 /*
276 * Set darkness...
277 */
278
279 if (header->cupsCompression > 0 && header->cupsCompression <= 100)
280 printf("~SD%02d\n", 30 * header->cupsCompression / 100);
281
282 /*
283 * Start bitmap graphics...
284 */
285
286 printf("~DGR:CUPS.GRF,%d,%d,\n",
287 header->cupsHeight * header->cupsBytesPerLine,
288 header->cupsBytesPerLine);
289
290 /*
291 * Allocate compression buffers...
292 */
293
294 CompBuffer = malloc(2 * header->cupsBytesPerLine + 1);
295 LastBuffer = malloc(header->cupsBytesPerLine);
296 LastSet = 0;
297 break;
298
299 case ZEBRA_CPCL :
300 /*
301 * Start label...
302 */
303
304 printf("! 0 %u %u %u %u\r\n", header->HWResolution[0],
305 header->HWResolution[1], header->cupsHeight,
306 header->NumCopies);
307 break;
308 }
309
310 /*
311 * Allocate memory for a line of graphics...
312 */
313
314 Buffer = malloc(header->cupsBytesPerLine);
315 Feed = 0;
316 }
317
318
319 /*
320 * 'EndPage()' - Finish a page of graphics.
321 */
322
323 void
324 EndPage(ppd_file_t *ppd, /* I - PPD file */
325 cups_page_header_t *header) /* I - Page header */
326 {
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 */
332
333
334 switch (ModelNumber)
335 {
336 case DYMO_3x0 :
337 /*
338 * Eject the current page...
339 */
340
341 fputs("\033E", stdout);
342 break;
343
344 case ZEBRA_EPL_LINE :
345 /*
346 * End buffered output, eject the label...
347 */
348
349 fputs("\033E\014", stdout);
350 break;
351
352 case ZEBRA_EPL_PAGE :
353 /*
354 * Print the label...
355 */
356
357 puts("P1");
358 break;
359
360 case ZEBRA_ZPL :
361 if (Canceled)
362 {
363 /*
364 * Cancel bitmap download...
365 */
366
367 puts("~DN");
368 break;
369 }
370
371 /*
372 * Start label...
373 */
374
375 puts("^XA");
376
377 /*
378 * Set print rate...
379 */
380
381 if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL &&
382 strcmp(choice->choice, "Default"))
383 {
384 val = atoi(choice->choice);
385 printf("^PR%d,%d,%d\n", val, val, val);
386 }
387
388 /*
389 * Put label home in default position (0,0)...
390 */
391
392 printf("^LH0,0\n");
393
394 /*
395 * Set media tracking...
396 */
397
398 if (ppdIsMarked(ppd, "zeMediaTracking", "Continuous"))
399 {
400 /*
401 * Add label length command for continuous...
402 */
403
404 printf("^LL%d\n", header->cupsHeight);
405 printf("^MNN\n");
406 }
407 else if (ppdIsMarked(ppd, "zeMediaTracking", "Web"))
408 printf("^MNY\n");
409 else if (ppdIsMarked(ppd, "zeMediaTracking", "Mark"))
410 printf("^MNM\n");
411
412 /*
413 * Set label top
414 */
415
416 if (header->cupsRowStep != 200)
417 printf("^LT%u\n", header->cupsRowStep);
418
419 /*
420 * Set media type...
421 */
422
423 if (!strcmp(header->MediaType, "Thermal"))
424 printf("^MTT\n");
425 else if (!strcmp(header->MediaType, "Direct"))
426 printf("^MTD\n");
427
428 /*
429 * Set print mode...
430 */
431
432 if ((choice = ppdFindMarkedChoice(ppd, "zePrintMode")) != NULL &&
433 strcmp(choice->choice, "Saved"))
434 {
435 printf("^MM");
436
437 if (!strcmp(choice->choice, "Tear"))
438 printf("T,Y\n");
439 else if (!strcmp(choice->choice, "Peel"))
440 printf("P,Y\n");
441 else if (!strcmp(choice->choice, "Rewind"))
442 printf("R,Y\n");
443 else if (!strcmp(choice->choice, "Applicator"))
444 printf("A,Y\n");
445 else
446 printf("C,Y\n");
447 }
448
449 /*
450 * Set tear-off adjust position...
451 */
452
453 if (header->AdvanceDistance != 1000)
454 {
455 if ((int)header->AdvanceDistance < 0)
456 printf("~TA%04d\n", (int)header->AdvanceDistance);
457 else
458 printf("~TA%03d\n", (int)header->AdvanceDistance);
459 }
460
461 /*
462 * Allow for reprinting after an error...
463 */
464
465 if (ppdIsMarked(ppd, "zeErrorReprint", "Always"))
466 printf("^JZY\n");
467 else if (ppdIsMarked(ppd, "zeErrorReprint", "Never"))
468 printf("^JZN\n");
469
470 /*
471 * Print multiple copies
472 */
473
474 if (header->NumCopies > 1)
475 printf("^PQ%d, 0, 0, N\n", header->NumCopies);
476
477 /*
478 * Display the label image...
479 */
480
481 puts("^FO0,0^XGR:CUPS.GRF,1,1^FS");
482
483 /*
484 * End the label and eject...
485 */
486
487 puts("^XZ");
488
489 /*
490 * Free compression buffers...
491 */
492
493 free(CompBuffer);
494 free(LastBuffer);
495 break;
496
497 case ZEBRA_CPCL :
498 /*
499 * Set tear-off adjust position...
500 */
501
502 if (header->AdvanceDistance != 1000)
503 printf("PRESENT-AT %d 1\r\n", (int)header->AdvanceDistance);
504
505 /*
506 * Allow for reprinting after an error...
507 */
508
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");
513
514 /*
515 * Cut label?
516 */
517
518 if (header->CutMedia)
519 puts("CUT\r");
520
521 /*
522 * Set darkness...
523 */
524
525 if (header->cupsCompression > 0)
526 printf("TONE %u\r\n", 2 * header->cupsCompression);
527
528 /*
529 * Set print rate...
530 */
531
532 if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL &&
533 strcmp(choice->choice, "Default"))
534 {
535 val = atoi(choice->choice);
536 printf("SPEED %d\r\n", val);
537 }
538
539 /*
540 * Print the label...
541 */
542
543 puts("FORM\r");
544 puts("PRINT\r");
545 break;
546 }
547
548 fflush(stdout);
549
550 /*
551 * Unregister the signal handler...
552 */
553
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));
558
559 sigemptyset(&action.sa_mask);
560 action.sa_handler = SIG_IGN;
561 sigaction(SIGTERM, &action, NULL);
562 #else
563 signal(SIGTERM, SIG_IGN);
564 #endif /* HAVE_SIGSET */
565
566 /*
567 * Free memory...
568 */
569
570 free(Buffer);
571 }
572
573
574 /*
575 * 'CancelJob()' - Cancel the current job...
576 */
577
578 void
579 CancelJob(int sig) /* I - Signal */
580 {
581 /*
582 * Tell the main loop to stop...
583 */
584
585 (void)sig;
586
587 Canceled = 1;
588 }
589
590
591 /*
592 * 'OutputLine()' - Output a line of graphics...
593 */
594
595 void
596 OutputLine(ppd_file_t *ppd, /* I - PPD file */
597 cups_page_header_t *header, /* I - Page header */
598 int y) /* I - Line number */
599 {
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";
606 /* Hex digits */
607
608
609 switch (ModelNumber)
610 {
611 case DYMO_3x0 :
612 /*
613 * See if the line is blank; if not, write it to the printer...
614 */
615
616 if (Buffer[0] ||
617 memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine - 1))
618 {
619 if (Feed)
620 {
621 while (Feed > 255)
622 {
623 printf("\033f\001%c", 255);
624 Feed -= 255;
625 }
626
627 printf("\033f\001%c", Feed);
628 Feed = 0;
629 }
630
631 putchar(0x16);
632 fwrite(Buffer, header->cupsBytesPerLine, 1, stdout);
633 fflush(stdout);
634
635 #ifdef __sgi
636 /*
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
640 * out properly.
641 */
642
643 sginap(1);
644 #endif /* __sgi */
645 }
646 else
647 Feed ++;
648 break;
649
650 case ZEBRA_EPL_LINE :
651 printf("\033g%03d", header->cupsBytesPerLine);
652 fwrite(Buffer, 1, header->cupsBytesPerLine, stdout);
653 fflush(stdout);
654 break;
655
656 case ZEBRA_EPL_PAGE :
657 if (Buffer[0] || memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine))
658 {
659 printf("GW0,%d,%d,1\n", y, header->cupsBytesPerLine);
660 for (i = header->cupsBytesPerLine, ptr = Buffer; i > 0; i --, ptr ++)
661 putchar(~*ptr);
662 putchar('\n');
663 fflush(stdout);
664 }
665 break;
666
667 case ZEBRA_ZPL :
668 /*
669 * Determine if this row is the same as the previous line.
670 * If so, output a ':' and return...
671 */
672
673 if (LastSet)
674 {
675 if (!memcmp(Buffer, LastBuffer, header->cupsBytesPerLine))
676 {
677 putchar(':');
678 return;
679 }
680 }
681
682 /*
683 * Convert the line to hex digits...
684 */
685
686 for (ptr = Buffer, compptr = CompBuffer, i = header->cupsBytesPerLine;
687 i > 0;
688 i --, ptr ++)
689 {
690 *compptr++ = hex[*ptr >> 4];
691 *compptr++ = hex[*ptr & 15];
692 }
693
694 *compptr = '\0';
695
696 /*
697 * Run-length compress the graphics...
698 */
699
700 for (compptr = CompBuffer, repeat_char = CompBuffer[0], repeat_count = 1;
701 *compptr;
702 compptr ++)
703 if (*compptr == repeat_char)
704 repeat_count ++;
705 else
706 {
707 ZPLCompress(repeat_char, repeat_count);
708 repeat_char = *compptr;
709 repeat_count = 1;
710 }
711
712 if (repeat_char == '0')
713 {
714 /*
715 * Handle 0's on the end of the line...
716 */
717
718 if (repeat_count & 1)
719 putchar('0');
720
721 putchar(',');
722 }
723 else
724 ZPLCompress(repeat_char, repeat_count);
725
726 /*
727 * Save this line for the next round...
728 */
729
730 memcpy(LastBuffer, Buffer, header->cupsBytesPerLine);
731 LastSet = 1;
732 break;
733
734 case ZEBRA_CPCL :
735 if (Buffer[0] || memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine))
736 {
737 printf("CG %u 1 0 %d ", header->cupsBytesPerLine, y);
738 fwrite(Buffer, 1, header->cupsBytesPerLine, stdout);
739 puts("\r");
740 fflush(stdout);
741 }
742 break;
743 }
744 }
745
746
747 /*
748 * 'ZPLCompress()' - Output a run-length compression sequence.
749 */
750
751 void
752 ZPLCompress(char repeat_char, /* I - Character to repeat */
753 int repeat_count) /* I - Number of repeated characters */
754 {
755 if (repeat_count > 1)
756 {
757 /*
758 * Print as many z's as possible - they are the largest denomination
759 * representing 400 characters (zC stands for 400 adjacent C's)
760 */
761
762 while (repeat_count >= 400)
763 {
764 putchar('z');
765 repeat_count -= 400;
766 }
767
768 /*
769 * Then print 'g' through 'y' as multiples of 20 characters...
770 */
771
772 if (repeat_count >= 20)
773 {
774 putchar('f' + repeat_count / 20);
775 repeat_count %= 20;
776 }
777
778 /*
779 * Finally, print 'G' through 'Y' as 1 through 19 characters...
780 */
781
782 if (repeat_count > 0)
783 putchar('F' + repeat_count);
784 }
785
786 /*
787 * Then the character to be repeated...
788 */
789
790 putchar(repeat_char);
791 }
792
793
794 /*
795 * 'main()' - Main entry and processing of driver.
796 */
797
798 int /* O - Exit status */
799 main(int argc, /* I - Number of command-line arguments */
800 char *argv[]) /* I - Command-line arguments */
801 {
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 */
809
810
811 /*
812 * Make sure status messages are not buffered...
813 */
814
815 setbuf(stderr, NULL);
816
817 /*
818 * Check command-line...
819 */
820
821 if (argc < 6 || argc > 7)
822 {
823 /*
824 * We don't have the correct number of arguments; write an error message
825 * and return.
826 */
827
828 fputs("ERROR: rastertodymo job-id user title copies options [file]\n", stderr);
829 return (1);
830 }
831
832 /*
833 * Open the page stream...
834 */
835
836 if (argc == 7)
837 {
838 if ((fd = open(argv[6], O_RDONLY)) == -1)
839 {
840 perror("ERROR: Unable to open raster file - ");
841 sleep(1);
842 return (1);
843 }
844 }
845 else
846 fd = 0;
847
848 ras = cupsRasterOpen(fd, CUPS_RASTER_READ);
849
850 /*
851 * Open the PPD file and apply options...
852 */
853
854 num_options = cupsParseOptions(argv[5], 0, &options);
855
856 if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL)
857 {
858 ppdMarkDefaults(ppd);
859 cupsMarkOptions(ppd, num_options, options);
860 }
861
862 /*
863 * Initialize the print device...
864 */
865
866 Setup(ppd);
867
868 /*
869 * Process pages as needed...
870 */
871
872 Page = 0;
873 Canceled = 0;
874
875 while (cupsRasterReadHeader(ras, &header))
876 {
877 /*
878 * Write a status message with the page number and number of copies.
879 */
880
881 Page ++;
882
883 fprintf(stderr, "PAGE: %d 1\n", Page);
884
885 /*
886 * Start the page...
887 */
888
889 StartPage(ppd, &header);
890
891 /*
892 * Loop for each line on the page...
893 */
894
895 for (y = 0; y < header.cupsHeight && !Canceled; y ++)
896 {
897 /*
898 * Let the user know how far we have progressed...
899 */
900
901 if ((y & 15) == 0)
902 fprintf(stderr, "INFO: Printing page %d, %d%% complete...\n", Page,
903 100 * y / header.cupsHeight);
904
905 /*
906 * Read a line of graphics...
907 */
908
909 if (cupsRasterReadPixels(ras, Buffer, header.cupsBytesPerLine) < 1)
910 break;
911
912 /*
913 * Write it to the printer...
914 */
915
916 OutputLine(ppd, &header, y);
917 }
918
919 /*
920 * Eject the page...
921 */
922
923 EndPage(ppd, &header);
924
925 if (Canceled)
926 break;
927 }
928
929 /*
930 * Close the raster stream...
931 */
932
933 cupsRasterClose(ras);
934 if (fd != 0)
935 close(fd);
936
937 /*
938 * Close the PPD file and free the options...
939 */
940
941 ppdClose(ppd);
942 cupsFreeOptions(num_options, options);
943
944 /*
945 * If no pages were printed, send an error message...
946 */
947
948 if (Page == 0)
949 fputs("ERROR: No pages found!\n", stderr);
950 else
951 fputs("INFO: Ready to print.\n", stderr);
952
953 return (Page == 0);
954 }
955
956
957 /*
958 * End of "$Id: rastertolabel.c 4920 2006-01-12 15:12:12Z mike $".
959 */