/* * Label printer filter for CUPS. * * Copyright © 2007-2019 by Apple Inc. * Copyright © 2001-2007 by Easy Software Products. * * Licensed under Apache License v2.0. See the file "LICENSE" for more * information. */ /* * Include necessary headers... */ #include #include #include #include #include #include #include #include /* * This driver filter currently supports DYMO, Intellitech, and Zebra * label printers. * * The DYMO portion of the driver has been tested with the 300, 330, * 330 Turbo, and 450 Twin Turbo label printers; it may also work with other * models. The DYMO printers support printing at 136, 203, and 300 DPI. * * The Intellitech portion of the driver has been tested with the * Intellibar 408, 412, and 808 and supports their PCL variant. * * The Zebra portion of the driver has been tested with the LP-2844, * LP-2844Z, QL-320, and QL-420 label printers; it may also work with * other models. The driver supports EPL line mode, EPL page mode, * ZPL, and CPCL as defined in Zebra's online developer documentation. */ /* * Model number constants... */ #define DYMO_3x0 0 /* DYMO Labelwriter 300/330/330 Turbo */ #define ZEBRA_EPL_LINE 0x10 /* Zebra EPL line mode printers */ #define ZEBRA_EPL_PAGE 0x11 /* Zebra EPL page mode printers */ #define ZEBRA_ZPL 0x12 /* Zebra ZPL-based printers */ #define ZEBRA_CPCL 0x13 /* Zebra CPCL-based printers */ #define INTELLITECH_PCL 0x20 /* Intellitech PCL-based printers */ /* * Globals... */ unsigned char *Buffer; /* Output buffer */ unsigned char *CompBuffer; /* Compression buffer */ unsigned char *LastBuffer; /* Last buffer */ unsigned Feed; /* Number of lines to skip */ int LastSet; /* Number of repeat characters */ int ModelNumber, /* cupsModelNumber attribute */ Page, /* Current page */ Canceled; /* Non-zero if job is canceled */ /* * Prototypes... */ void Setup(ppd_file_t *ppd); void StartPage(ppd_file_t *ppd, cups_page_header2_t *header); void EndPage(ppd_file_t *ppd, cups_page_header2_t *header); void CancelJob(int sig); void OutputLine(ppd_file_t *ppd, cups_page_header2_t *header, unsigned y); void PCLCompress(unsigned char *line, unsigned length); void ZPLCompress(unsigned char repeat_char, unsigned repeat_count); /* * 'Setup()' - Prepare the printer for printing. */ void Setup(ppd_file_t *ppd) /* I - PPD file */ { int i; /* Looping var */ /* * Get the model number from the PPD file... */ if (ppd) ModelNumber = ppd->model_number; /* * Initialize based on the model number... */ switch (ModelNumber) { case DYMO_3x0 : /* * Clear any remaining data... */ for (i = 0; i < 100; i ++) putchar(0x1b); /* * Reset the printer... */ fputs("\033@", stdout); break; case ZEBRA_EPL_LINE : break; case ZEBRA_EPL_PAGE : break; case ZEBRA_ZPL : break; case ZEBRA_CPCL : break; case INTELLITECH_PCL : /* * Send a PCL reset sequence. */ putchar(0x1b); putchar('E'); break; } } /* * 'StartPage()' - Start a page of graphics. */ void StartPage(ppd_file_t *ppd, /* I - PPD file */ cups_page_header2_t *header) /* I - Page header */ { ppd_choice_t *choice; /* Marked choice */ unsigned length; /* Actual label length */ /* * Show page device dictionary... */ fprintf(stderr, "DEBUG: StartPage...\n"); fprintf(stderr, "DEBUG: Duplex = %d\n", header->Duplex); fprintf(stderr, "DEBUG: HWResolution = [ %d %d ]\n", header->HWResolution[0], header->HWResolution[1]); fprintf(stderr, "DEBUG: ImagingBoundingBox = [ %d %d %d %d ]\n", header->ImagingBoundingBox[0], header->ImagingBoundingBox[1], header->ImagingBoundingBox[2], header->ImagingBoundingBox[3]); fprintf(stderr, "DEBUG: Margins = [ %d %d ]\n", header->Margins[0], header->Margins[1]); fprintf(stderr, "DEBUG: ManualFeed = %d\n", header->ManualFeed); fprintf(stderr, "DEBUG: MediaPosition = %d\n", header->MediaPosition); fprintf(stderr, "DEBUG: NumCopies = %d\n", header->NumCopies); fprintf(stderr, "DEBUG: Orientation = %d\n", header->Orientation); fprintf(stderr, "DEBUG: PageSize = [ %d %d ]\n", header->PageSize[0], header->PageSize[1]); fprintf(stderr, "DEBUG: cupsWidth = %d\n", header->cupsWidth); fprintf(stderr, "DEBUG: cupsHeight = %d\n", header->cupsHeight); fprintf(stderr, "DEBUG: cupsMediaType = %d\n", header->cupsMediaType); fprintf(stderr, "DEBUG: cupsBitsPerColor = %d\n", header->cupsBitsPerColor); fprintf(stderr, "DEBUG: cupsBitsPerPixel = %d\n", header->cupsBitsPerPixel); fprintf(stderr, "DEBUG: cupsBytesPerLine = %d\n", header->cupsBytesPerLine); fprintf(stderr, "DEBUG: cupsColorOrder = %d\n", header->cupsColorOrder); fprintf(stderr, "DEBUG: cupsColorSpace = %d\n", header->cupsColorSpace); fprintf(stderr, "DEBUG: cupsCompression = %d\n", header->cupsCompression); switch (ModelNumber) { case DYMO_3x0 : /* * Setup printer/job attributes... */ length = header->PageSize[1] * header->HWResolution[1] / 72; printf("\033L%c%c", length >> 8, length); printf("\033D%c", header->cupsBytesPerLine); printf("\033%c", header->cupsCompression + 'c'); /* Darkness */ printf("\033q%d", header->MediaPosition + 1); /* Roll Select */ break; case ZEBRA_EPL_LINE : /* * Set print rate... */ if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL && strcmp(choice->choice, "Default")) printf("\033S%.0f", atof(choice->choice) * 2.0 - 2.0); /* * Set darkness... */ if (header->cupsCompression > 0 && header->cupsCompression <= 100) printf("\033D%d", 7 * header->cupsCompression / 100); /* * Set left margin to 0... */ fputs("\033M01", stdout); /* * Start buffered output... */ fputs("\033B", stdout); break; case ZEBRA_EPL_PAGE : /* * Start a new label... */ puts(""); puts("N"); /* * Set hardware options... */ if (!strcmp(header->MediaType, "Direct")) puts("OD"); /* * Set print rate... */ if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL && strcmp(choice->choice, "Default")) { double val = atof(choice->choice); if (val >= 3.0) printf("S%.0f\n", val); else printf("S%.0f\n", val * 2.0 - 2.0); } /* * Set darkness... */ if (header->cupsCompression > 0 && header->cupsCompression <= 100) printf("D%u\n", 15 * header->cupsCompression / 100); /* * Set label size... */ printf("q%u\n", (header->cupsWidth + 7) & ~7U); break; case ZEBRA_ZPL : /* * Set darkness... */ if (header->cupsCompression > 0 && header->cupsCompression <= 100) printf("~SD%02u\n", 30 * header->cupsCompression / 100); /* * Start bitmap graphics... */ printf("~DGR:CUPS.GRF,%u,%u,\n", header->cupsHeight * header->cupsBytesPerLine, header->cupsBytesPerLine); /* * Allocate compression buffers... */ CompBuffer = malloc(2 * header->cupsBytesPerLine + 1); LastBuffer = malloc(header->cupsBytesPerLine); LastSet = 0; break; case ZEBRA_CPCL : /* * Start label... */ printf("! 0 %u %u %u %u\r\n", header->HWResolution[0], header->HWResolution[1], header->cupsHeight, header->NumCopies); printf("PAGE-WIDTH %u\r\n", header->cupsWidth); printf("PAGE-HEIGHT %u\r\n", header->cupsHeight); break; case INTELLITECH_PCL : /* * Set the media size... */ printf("\033&l6D\033&k12H"); /* Set 6 LPI, 10 CPI */ printf("\033&l0O"); /* Set portrait orientation */ switch (header->PageSize[1]) { case 540 : /* Monarch Envelope */ printf("\033&l80A"); /* Set page size */ break; case 624 : /* DL Envelope */ printf("\033&l90A"); /* Set page size */ break; case 649 : /* C5 Envelope */ printf("\033&l91A"); /* Set page size */ break; case 684 : /* COM-10 Envelope */ printf("\033&l81A"); /* Set page size */ break; case 756 : /* Executive */ printf("\033&l1A"); /* Set page size */ break; case 792 : /* Letter */ printf("\033&l2A"); /* Set page size */ break; case 842 : /* A4 */ printf("\033&l26A"); /* Set page size */ break; case 1008 : /* Legal */ printf("\033&l3A"); /* Set page size */ break; default : /* Custom size */ printf("\033!f%uZ", header->PageSize[1] * 300 / 72); break; } printf("\033&l%uP", /* Set page length */ header->PageSize[1] / 12); printf("\033&l0E"); /* Set top margin to 0 */ if (header->NumCopies) printf("\033&l%uX", header->NumCopies); /* Set number copies */ printf("\033&l0L"); /* Turn off perforation skip */ /* * Print settings... */ if (Page == 1) { if (header->cupsRowFeed) /* inPrintRate */ printf("\033!p%uS", header->cupsRowFeed); if (header->cupsCompression != ~0U) /* inPrintDensity */ printf("\033&d%dA", 30 * header->cupsCompression / 100 - 15); if ((choice = ppdFindMarkedChoice(ppd, "inPrintMode")) != NULL) { if (!strcmp(choice->choice, "Standard")) fputs("\033!p0M", stdout); else if (!strcmp(choice->choice, "Tear")) { fputs("\033!p1M", stdout); if (header->cupsRowCount) /* inTearInterval */ printf("\033!n%uT", header->cupsRowCount); } else { fputs("\033!p2M", stdout); if (header->cupsRowStep) /* inCutInterval */ printf("\033!n%uC", header->cupsRowStep); } } } /* * Setup graphics... */ printf("\033*t%uR", header->HWResolution[0]); /* Set resolution */ printf("\033*r%uS", header->cupsWidth); /* Set width */ printf("\033*r%uT", header->cupsHeight); /* Set height */ printf("\033&a0H"); /* Set horizontal position */ printf("\033&a0V"); /* Set vertical position */ printf("\033*r1A"); /* Start graphics */ printf("\033*b3M"); /* Set compression */ /* * Allocate compression buffers... */ CompBuffer = malloc(2 * header->cupsBytesPerLine + 1); LastBuffer = malloc(header->cupsBytesPerLine); LastSet = 0; break; } /* * Allocate memory for a line of graphics... */ Buffer = malloc(header->cupsBytesPerLine); Feed = 0; } /* * 'EndPage()' - Finish a page of graphics. */ void EndPage(ppd_file_t *ppd, /* I - PPD file */ cups_page_header2_t *header) /* I - Page header */ { int val; /* Option value */ ppd_choice_t *choice; /* Marked choice */ switch (ModelNumber) { case DYMO_3x0 : /* * Eject the current page... */ fputs("\033E", stdout); break; case ZEBRA_EPL_LINE : /* * End buffered output, eject the label... */ fputs("\033E\014", stdout); break; case ZEBRA_EPL_PAGE : /* * Print the label... */ puts("P1"); /* * Cut the label as needed... */ if (header->CutMedia) puts("C"); break; case ZEBRA_ZPL : if (Canceled) { /* * Cancel bitmap download... */ puts("~DN"); break; } /* * Start label... */ puts("^XA"); /* * Rotate 180 degrees so that the top of the label/page is at the * leading edge... */ puts("^POI"); /* * Set print width... */ printf("^PW%u\n", header->cupsWidth); /* * Set print rate... */ if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL && strcmp(choice->choice, "Default")) { val = atoi(choice->choice); printf("^PR%d,%d,%d\n", val, val, val); } /* * Put label home in default position (0,0)... */ printf("^LH0,0\n"); /* * Set media tracking... */ if (ppdIsMarked(ppd, "zeMediaTracking", "Continuous")) { /* * Add label length command for continuous... */ printf("^LL%d\n", header->cupsHeight); printf("^MNN\n"); } else if (ppdIsMarked(ppd, "zeMediaTracking", "Web")) printf("^MNY\n"); else if (ppdIsMarked(ppd, "zeMediaTracking", "Mark")) printf("^MNM\n"); /* * Set label top */ if (header->cupsRowStep != 200) printf("^LT%d\n", header->cupsRowStep); /* * Set media type... */ if (!strcmp(header->MediaType, "Thermal")) printf("^MTT\n"); else if (!strcmp(header->MediaType, "Direct")) printf("^MTD\n"); /* * Set print mode... */ if ((choice = ppdFindMarkedChoice(ppd, "zePrintMode")) != NULL && strcmp(choice->choice, "Saved")) { printf("^MM"); if (!strcmp(choice->choice, "Tear")) printf("T,Y\n"); else if (!strcmp(choice->choice, "Peel")) printf("P,Y\n"); else if (!strcmp(choice->choice, "Rewind")) printf("R,Y\n"); else if (!strcmp(choice->choice, "Applicator")) printf("A,Y\n"); else printf("C,Y\n"); } /* * Set tear-off adjust position... */ if (header->AdvanceDistance != 1000) { if ((int)header->AdvanceDistance < 0) printf("~TA%04d\n", (int)header->AdvanceDistance); else printf("~TA%03d\n", (int)header->AdvanceDistance); } /* * Allow for reprinting after an error... */ if (ppdIsMarked(ppd, "zeErrorReprint", "Always")) printf("^JZY\n"); else if (ppdIsMarked(ppd, "zeErrorReprint", "Never")) printf("^JZN\n"); /* * Print multiple copies */ if (header->NumCopies > 1) printf("^PQ%d, 0, 0, N\n", header->NumCopies); /* * Display the label image... */ puts("^FO0,0^XGR:CUPS.GRF,1,1^FS"); /* * End the label and eject... */ puts("^XZ"); /* * Delete the label image... */ puts("^XA"); puts("^IDR:CUPS.GRF^FS"); puts("^XZ"); /* * Cut the label as needed... */ if (header->CutMedia) puts("^CN1"); break; case ZEBRA_CPCL : /* * Set tear-off adjust position... */ if (header->AdvanceDistance != 1000) printf("PRESENT-AT %d 1\r\n", (int)header->AdvanceDistance); /* * Allow for reprinting after an error... */ if (ppdIsMarked(ppd, "zeErrorReprint", "Always")) puts("ON-OUT-OF-PAPER WAIT\r"); else if (ppdIsMarked(ppd, "zeErrorReprint", "Never")) puts("ON-OUT-OF-PAPER PURGE\r"); /* * Cut label? */ if (header->CutMedia) puts("CUT\r"); /* * Set darkness... */ if (header->cupsCompression > 0) printf("TONE %u\r\n", 2 * header->cupsCompression); /* * Set print rate... */ if ((choice = ppdFindMarkedChoice(ppd, "zePrintRate")) != NULL && strcmp(choice->choice, "Default")) { val = atoi(choice->choice); printf("SPEED %d\r\n", val); } /* * Print the label... */ if ((choice = ppdFindMarkedChoice(ppd, "zeMediaTracking")) == NULL || strcmp(choice->choice, "Continuous")) puts("FORM\r"); puts("PRINT\r"); break; case INTELLITECH_PCL : printf("\033*rB"); /* End GFX */ printf("\014"); /* Eject current page */ break; } fflush(stdout); /* * Free memory... */ free(Buffer); if (CompBuffer) { free(CompBuffer); CompBuffer = NULL; } if (LastBuffer) { free(LastBuffer); LastBuffer = NULL; } } /* * 'CancelJob()' - Cancel the current job... */ void CancelJob(int sig) /* I - Signal */ { /* * Tell the main loop to stop... */ (void)sig; Canceled = 1; } /* * 'OutputLine()' - Output a line of graphics... */ void OutputLine(ppd_file_t *ppd, /* I - PPD file */ cups_page_header2_t *header, /* I - Page header */ unsigned y) /* I - Line number */ { unsigned i; /* Looping var */ unsigned char *ptr; /* Pointer into buffer */ unsigned char *compptr; /* Pointer into compression buffer */ unsigned char repeat_char; /* Repeated character */ unsigned repeat_count; /* Number of repeated characters */ static const unsigned char *hex = (const unsigned char *)"0123456789ABCDEF"; /* Hex digits */ (void)ppd; switch (ModelNumber) { case DYMO_3x0 : /* * See if the line is blank; if not, write it to the printer... */ if (Buffer[0] || memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine - 1)) { if (Feed) { while (Feed > 255) { printf("\033f\001%c", 255); Feed -= 255; } printf("\033f\001%c", Feed); Feed = 0; } putchar(0x16); fwrite(Buffer, header->cupsBytesPerLine, 1, stdout); fflush(stdout); } else Feed ++; break; case ZEBRA_EPL_LINE : printf("\033g%03d", header->cupsBytesPerLine); fwrite(Buffer, 1, header->cupsBytesPerLine, stdout); fflush(stdout); break; case ZEBRA_EPL_PAGE : if (Buffer[0] || memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine)) { printf("GW0,%d,%d,1\n", y, header->cupsBytesPerLine); for (i = header->cupsBytesPerLine, ptr = Buffer; i > 0; i --, ptr ++) putchar(~*ptr); putchar('\n'); fflush(stdout); } break; case ZEBRA_ZPL : /* * Determine if this row is the same as the previous line. * If so, output a ':' and return... */ if (LastSet) { if (!memcmp(Buffer, LastBuffer, header->cupsBytesPerLine)) { putchar(':'); return; } } /* * Convert the line to hex digits... */ for (ptr = Buffer, compptr = CompBuffer, i = header->cupsBytesPerLine; i > 0; i --, ptr ++) { *compptr++ = hex[*ptr >> 4]; *compptr++ = hex[*ptr & 15]; } *compptr = '\0'; /* * Run-length compress the graphics... */ for (compptr = CompBuffer + 1, repeat_char = CompBuffer[0], repeat_count = 1; *compptr; compptr ++) if (*compptr == repeat_char) repeat_count ++; else { ZPLCompress(repeat_char, repeat_count); repeat_char = *compptr; repeat_count = 1; } if (repeat_char == '0') { /* * Handle 0's on the end of the line... */ if (repeat_count & 1) { repeat_count --; putchar('0'); } if (repeat_count > 0) putchar(','); } else ZPLCompress(repeat_char, repeat_count); fflush(stdout); /* * Save this line for the next round... */ memcpy(LastBuffer, Buffer, header->cupsBytesPerLine); LastSet = 1; break; case ZEBRA_CPCL : if (Buffer[0] || memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine)) { printf("CG %u 1 0 %d ", header->cupsBytesPerLine, y); fwrite(Buffer, 1, header->cupsBytesPerLine, stdout); puts("\r"); fflush(stdout); } break; case INTELLITECH_PCL : if (Buffer[0] || memcmp(Buffer, Buffer + 1, header->cupsBytesPerLine - 1)) { if (Feed) { printf("\033*b%dY", Feed); Feed = 0; LastSet = 0; } PCLCompress(Buffer, header->cupsBytesPerLine); } else Feed ++; break; } } /* * 'PCLCompress()' - Output a PCL (mode 3) compressed line. */ void PCLCompress(unsigned char *line, /* I - Line to compress */ unsigned length) /* I - Length of line */ { unsigned char *line_ptr, /* Current byte pointer */ *line_end, /* End-of-line byte pointer */ *comp_ptr, /* Pointer into compression buffer */ *start, /* Start of compression sequence */ *seed; /* Seed buffer pointer */ unsigned count, /* Count of bytes for output */ offset; /* Offset of bytes for output */ /* * Do delta-row compression... */ line_ptr = line; line_end = line + length; comp_ptr = CompBuffer; seed = LastBuffer; while (line_ptr < line_end) { /* * Find the next non-matching sequence... */ start = line_ptr; if (!LastSet) { /* * The seed buffer is invalid, so do the next 8 bytes, max... */ offset = 0; if ((count = (unsigned)(line_end - line_ptr)) > 8) count = 8; line_ptr += count; } else { /* * The seed buffer is valid, so compare against it... */ while (*line_ptr == *seed && line_ptr < line_end) { line_ptr ++; seed ++; } if (line_ptr == line_end) break; offset = (unsigned)(line_ptr - start); /* * Find up to 8 non-matching bytes... */ start = line_ptr; count = 0; while (*line_ptr != *seed && line_ptr < line_end && count < 8) { line_ptr ++; seed ++; count ++; } } /* * Place mode 3 compression data in the buffer; see HP manuals * for details... */ if (offset >= 31) { /* * Output multi-byte offset... */ *comp_ptr++ = (unsigned char)(((count - 1) << 5) | 31); offset -= 31; while (offset >= 255) { *comp_ptr++ = 255; offset -= 255; } *comp_ptr++ = (unsigned char)offset; } else { /* * Output single-byte offset... */ *comp_ptr++ = (unsigned char)(((count - 1) << 5) | offset); } memcpy(comp_ptr, start, count); comp_ptr += count; } /* * Set the length of the data and write it... */ printf("\033*b%dW", (int)(comp_ptr - CompBuffer)); fwrite(CompBuffer, (size_t)(comp_ptr - CompBuffer), 1, stdout); /* * Save this line as a "seed" buffer for the next... */ memcpy(LastBuffer, line, length); LastSet = 1; } /* * 'ZPLCompress()' - Output a run-length compression sequence. */ void ZPLCompress(unsigned char repeat_char, /* I - Character to repeat */ unsigned repeat_count) /* I - Number of repeated characters */ { if (repeat_count > 1) { /* * Print as many z's as possible - they are the largest denomination * representing 400 characters (zC stands for 400 adjacent C's) */ while (repeat_count >= 400) { putchar('z'); repeat_count -= 400; } /* * Then print 'g' through 'y' as multiples of 20 characters... */ if (repeat_count >= 20) { putchar((int)('f' + repeat_count / 20)); repeat_count %= 20; } /* * Finally, print 'G' through 'Y' as 1 through 19 characters... */ if (repeat_count > 0) putchar((int)('F' + repeat_count)); } /* * Then the character to be repeated... */ putchar((int)repeat_char); } /* * 'main()' - Main entry and processing of driver. */ int /* O - Exit status */ main(int argc, /* I - Number of command-line arguments */ char *argv[]) /* I - Command-line arguments */ { int fd; /* File descriptor */ cups_raster_t *ras; /* Raster stream for printing */ cups_page_header2_t header; /* Page header from file */ unsigned y; /* Current line */ ppd_file_t *ppd; /* PPD file */ int num_options; /* Number of options */ cups_option_t *options; /* Options */ #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) struct sigaction action; /* Actions for POSIX signals */ #endif /* HAVE_SIGACTION && !HAVE_SIGSET */ /* * Make sure status messages are not buffered... */ setbuf(stderr, NULL); /* * Check command-line... */ if (argc != 6 && argc != 7) { /* * We don't have the correct number of arguments; write an error message * and return. */ _cupsLangPrintFilter(stderr, "ERROR", _("%s job-id user title copies options [file]"), "rastertolabel"); return (1); } /* * Open the page stream... */ if (argc == 7) { if ((fd = open(argv[6], O_RDONLY)) == -1) { _cupsLangPrintError("ERROR", _("Unable to open raster file")); sleep(1); return (1); } } else fd = 0; ras = cupsRasterOpen(fd, CUPS_RASTER_READ); /* * Register a signal handler to eject the current page if the * job is cancelled. */ Canceled = 0; #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */ sigset(SIGTERM, CancelJob); #elif defined(HAVE_SIGACTION) memset(&action, 0, sizeof(action)); sigemptyset(&action.sa_mask); action.sa_handler = CancelJob; sigaction(SIGTERM, &action, NULL); #else signal(SIGTERM, CancelJob); #endif /* HAVE_SIGSET */ /* * Open the PPD file and apply options... */ num_options = cupsParseOptions(argv[5], 0, &options); ppd = ppdOpenFile(getenv("PPD")); if (!ppd) { ppd_status_t status; /* PPD error */ int linenum; /* Line number */ _cupsLangPrintFilter(stderr, "ERROR", _("The PPD file could not be opened.")); status = ppdLastError(&linenum); fprintf(stderr, "DEBUG: %s on line %d.\n", ppdErrorString(status), linenum); return (1); } ppdMarkDefaults(ppd); cupsMarkOptions(ppd, num_options, options); /* * Initialize the print device... */ Setup(ppd); /* * Process pages as needed... */ Page = 0; while (cupsRasterReadHeader2(ras, &header)) { /* * Write a status message with the page number and number of copies. */ if (Canceled) break; Page ++; fprintf(stderr, "PAGE: %d 1\n", Page); _cupsLangPrintFilter(stderr, "INFO", _("Starting page %d."), Page); /* * Start the page... */ StartPage(ppd, &header); /* * Loop for each line on the page... */ for (y = 0; y < header.cupsHeight; y ++) { /* * Let the user know how far we have progressed... */ if (Canceled) break; if ((y & 15) == 0) { _cupsLangPrintFilter(stderr, "INFO", _("Printing page %d, %u%% complete."), Page, 100 * y / header.cupsHeight); fprintf(stderr, "ATTR: job-media-progress=%u\n", 100 * y / header.cupsHeight); } /* * Read a line of graphics... */ if (cupsRasterReadPixels(ras, Buffer, header.cupsBytesPerLine) < 1) break; /* * Write it to the printer... */ OutputLine(ppd, &header, y); } /* * Eject the page... */ _cupsLangPrintFilter(stderr, "INFO", _("Finished page %d."), Page); EndPage(ppd, &header); if (Canceled) break; } /* * Close the raster stream... */ cupsRasterClose(ras); if (fd != 0) close(fd); /* * Close the PPD file and free the options... */ ppdClose(ppd); cupsFreeOptions(num_options, options); /* * If no pages were printed, send an error message... */ if (Page == 0) { _cupsLangPrintFilter(stderr, "ERROR", _("No pages were found.")); return (1); } else return (0); }