// To get required headers, run // sudo apt-get install libcups2-dev libcupsimage2-dev #include #include #include #include #include #include #ifndef DEBUGFILE #define DEBUGFILE "/tmp/debugraster.txt" #endif struct settings_ { int cashDrawer1; int cashDrawer2; int blankSpace; int feedDist; }; struct settings_ settings; struct command { int length; char *command; }; /* * zj uses kind of epson ESC/P dialect code. Here is subset of commands * * initialize - esc @ * cash drawer 1 - esc p 0 @ P * cash drawer 2 - esc p 1 @ P * start raster - esc v 0 * skip lines - esc j * cut - esc i */ // define printer initialize command static const struct command printerInitializeCommand = {2, (char[2]){0x1b, 0x40}}; // esc @ // define cashDrawerEjector command static const struct command cashDrawerEject[2] = { {5, (char[5]){0x1b, 0x70, 0, 0x40, 0x50}}, // esc p N @ P {5, (char[5]){0x1b, 0x70, 1, 0x40, 0x50}}}; // define raster mode start command static const struct command rasterModeStartCommand = { 4, (char[4]){0x1d, 0x76, 0x30, 0}}; // esc v 0 #ifndef DEBUGP static inline void mputchar(char c) { putchar(c); } #else FILE *lfd = 0; // putchar with debug wrapper static inline void mputchar(char c) { unsigned char m = c; if (lfd) fprintf(lfd, "%02x ", m); putchar(m); } #endif // procedure to output an array static inline void outputarray(const char *array, int length) { int i = 0; for (; i < length; ++i) mputchar(array[i]); } // output a command static inline void outputCommand(struct command output) { outputarray(output.command, output.length); } static inline int lo(int val) { return val & 0xFF; } static inline int hi(int val) { return lo(val >> 8); } // enter raster mode and set up x and y dimensions static inline void rasterheader(int xsize, int ysize) { outputCommand(rasterModeStartCommand); mputchar(lo(xsize)); mputchar(hi(xsize)); mputchar(lo(ysize)); mputchar(hi(ysize)); } // print all unprinted (i.e. flush the buffer) // then feed given number of lines static inline void skiplines(int size) { mputchar(0x1b); mputchar(0x4a); // esc j mputchar(size); } // get an option static inline int getOptionChoiceIndex(const char *choiceName, ppd_file_t *ppd) { ppd_choice_t *choice; ppd_option_t *option; choice = ppdFindMarkedChoice(ppd, choiceName); if (choice == NULL) { if ((option = ppdFindOption(ppd, choiceName)) == NULL) return -1; if ((choice = ppdFindChoice(option, option->defchoice)) == NULL) return -1; } return atoi(choice->choice); } static void initializeSettings(char *commandLineOptionSettings) { ppd_file_t *ppd = NULL; cups_option_t *options = NULL; int numOptions = 0; ppd = ppdOpenFile(getenv("PPD")); ppdMarkDefaults(ppd); numOptions = cupsParseOptions(commandLineOptionSettings, 0, &options); if ((numOptions != 0) && (options != NULL)) { cupsMarkOptions(ppd, numOptions, options); cupsFreeOptions(numOptions, options); } memset(&settings, 0x00, sizeof(struct settings_)); settings.cashDrawer1 = getOptionChoiceIndex("CashDrawer1Setting", ppd); settings.cashDrawer2 = getOptionChoiceIndex("CashDrawer2Setting", ppd); settings.blankSpace = getOptionChoiceIndex("BlankSpace", ppd); settings.feedDist = getOptionChoiceIndex("FeedDist", ppd); ppdClose(ppd); } // sent on the beginning of print job void jobSetup() { if (settings.cashDrawer1 == 1) outputCommand(cashDrawerEject[0]); if (settings.cashDrawer2 == 1) outputCommand(cashDrawerEject[1]); outputCommand(printerInitializeCommand); } // sent at the very end of print job void ShutDown() { if (settings.cashDrawer1 == 2) outputCommand(cashDrawerEject[0]); if (settings.cashDrawer2 == 2) outputCommand(cashDrawerEject[1]); outputCommand(printerInitializeCommand); } // sent at the end of every page #ifndef __sighandler_t typedef void (*__sighandler_t)(int); #endif __sighandler_t old_signal; void EndPage() { int i; for (i = 0; i < settings.feedDist; ++i) skiplines(0x18); signal(15, old_signal); } // sent on job canceling void cancelJob() { int i = 0; for (; i < 0x258; ++i) mputchar(0); EndPage(); ShutDown(); } // invoked before starting to print a page void pageSetup() { old_signal = signal(15, cancelJob); } ////////////////////////////////////////////// ////////////////////////////////////////////// int main(int argc, char *argv[]) { int fd = 0; // File descriptor providing CUPS raster data cups_raster_t *ras = NULL; // Raster stream for printing cups_page_header2_t header; // CUPS Page header int page = 0; // Current page int y = 0; // Vertical position in page 0 <= y <= header.cupsHeight unsigned char *rasterData = NULL; // Pointer to raster data from CUPS unsigned char *originalRasterDataPtr = NULL; // Copy of original pointer for freeing buffer if (argc < 6 || argc > 7) { fputs("ERROR: rastertozj job-id user title copies options [file]\n", stderr); return EXIT_FAILURE; } if (argc == 7) { if ((fd = open(argv[6], O_RDONLY)) == -1) { perror("ERROR: Unable to open raster file - "); sleep(1); return EXIT_FAILURE; } } else fd = 0; #ifdef DEBUGP lfd = fopen(DEBUGFILE, "w"); #endif initializeSettings(argv[5]); jobSetup(); ras = cupsRasterOpen(fd, CUPS_RASTER_READ); page = 0; while (cupsRasterReadHeader2(ras, &header)) { if ((header.cupsHeight == 0) || (header.cupsBytesPerLine == 0)) break; #ifdef DEBUGP if (lfd) fprintf(lfd, "\nheader.cupsHeight=%d, header.cupsBytesPerLine=%d\n", header.cupsHeight, header.cupsBytesPerLine); #endif if (rasterData == NULL) { rasterData = malloc(header.cupsBytesPerLine * 24); if (rasterData == NULL) { if (originalRasterDataPtr != NULL) free(originalRasterDataPtr); cupsRasterClose(ras); if (fd != 0) close(fd); return EXIT_FAILURE; } originalRasterDataPtr = rasterData; // used to later free the memory } fprintf(stderr, "PAGE: %d %d\n", ++page, header.NumCopies); pageSetup(); int foo = (header.cupsWidth > 0x180) ? 0x180 : header.cupsWidth; foo = (foo + 7) & 0xFFFFFFF8; int width_size = foo >> 3; #ifdef DEBUGP if (lfd) fprintf(lfd, "\nheader.cupsWidth=%d, foo=%d, width_size=%d\n", header.cupsWidth, foo, width_size); #endif y = 0; int zeroy = 0; while (y < header.cupsHeight) { if ((y & 127) != 0) fprintf(stderr, "INFO: Printing page %d, %d%% complete...\n", page, (100 * y / header.cupsHeight)); int rest = header.cupsHeight - y; if (rest > 24) rest = 24; #ifdef DEBUGP if (lfd) fprintf(lfd, "\nProcessing block of %d, starting from %d lines\n", rest, y); #endif y += rest; if (y) { unsigned char *buf = rasterData; int j; for (j = 0; j < rest; ++j) { if (!cupsRasterReadPixels(ras, buf, header.cupsBytesPerLine)) break; buf += width_size; } #ifdef DEBUGP if (lfd) fprintf(lfd, "\nRead %d lines\n", j); #endif if (j < rest) continue; } int rest_bytes = rest * width_size; int j; for (j = 0; j < rest_bytes; ++j) if (rasterData[j] != 0) break; #ifdef DEBUGP if (lfd) fprintf(lfd, "\nChecked %d bytes of %d for zero\n", j, rest_bytes); #endif if (j >= rest_bytes) { ++zeroy; continue; } for (; zeroy > 0; --zeroy) skiplines(24); rasterheader(width_size, rest); outputarray((char *)rasterData, width_size * 24); skiplines(0); } if (!settings.blankSpace) for (; zeroy > 0; --zeroy) skiplines(24); EndPage(); } ShutDown(); if (originalRasterDataPtr) free(originalRasterDataPtr); cupsRasterClose(ras); if (fd) close(fd); fputs(page ? "INFO: Ready to print.\n" : "ERROR: No pages found!\n", stderr); #ifdef DEBUGP if (lfd) fclose(lfd); #endif return page ? EXIT_SUCCESS : EXIT_FAILURE; } // end of rastertozj.c