/* * Brother QL-570 thermal printing program * It prints a mono PNG image directly to the printers block device * * Copyright 2011 Asbjørn Sloth Tønnesen * * PNG reading based on: * A simple libpng example program * http://zarb.org/~gc/html/libpng.html * Copyright 2002-2011 Guillaume Cottenceau and contributors. * * This software may be freely redistributed under the terms * of the libpng license. * http://libpng.org/pub/png/src/libpng-LICENSE.txt * * Example usage: * ./ql570 /dev/usb/lp0 image.png */ #include #include #include #include #include #define PNG_DEBUG 3 #include #define ESC 0x1b #define DEBUG FILE * fp; struct { int16_t w; int16_t h; uint8_t * data; } typedef pngdata_t; FILE * ql570_open(const char * path) { fp = fopen(path, "r+b"); if (fp == NULL) { perror("fopen"); exit(EXIT_FAILURE); } return fp; } void check_img(pngdata_t * img) { int i, j; int lb = (img->w / 8) + (img->w % 8 > 0); printf("lb: %d\n", lb); for (i=0;ih;i++) { for (j=img->w-1;j!=0;j--) { if (img->data[i*lb+j/8] & (1 << (7-(j % 8)))) { printf("#"); } else { printf(" "); } } printf("\n"); } } void ql570_print(pngdata_t * img) { /* Init */ fprintf(fp, "%c%c", ESC, '@'); /* Set media type */ fprintf(fp, "%c%c%c%c%c%c%c%c%c%c%c%c%c", ESC, 'i', 'z', 0xa6, 0x0a, 29, 0, img->h & 0xff, img->h >> 8, 0, 0, 0, 0); /* Set cut type */ fprintf(fp, "%c%c%c", ESC, 'i', 'K', 8); /* Enable cutter */ fprintf(fp, "%c%c%c", ESC, 'i', 'A', 1); /* Set margin = 0 */ fprintf(fp, "%c%c%c%c%c", ESC, 'i', 'd', 0, 0); int i, j; int lb = (img->w / 8) + (img->w % 8 > 0); for (i=0;ih;i++) { fprintf(fp, "%c%c%c", 'g', 0x00, 90); for (j=0;jdata[i*lb+j]); } for (;j<90;j++) { fprintf(fp, "%c", 0x00); } } /* Print */ fprintf(fp, "%c", 0x1a); } void abort_(const char * s, ...) { va_list args; va_start(args, s); vfprintf(stderr, s, args); fprintf(stderr, "\n"); va_end(args); abort(); } pngdata_t * loadpng(const char * path) { png_structp png_ptr; png_infop info_ptr; png_bytep * row_pointers; int width, height, rowbytes; int x, y; unsigned char header[8]; // 8 is the maximum size that can be checked png_byte* ptr; FILE *fp; int type = 0; int lb; uint8_t * bitmap; /* open file and test for it being a png */ fp = fopen(path, "rb"); if (!fp) abort_("[read_png_file] File %s could not be opened for reading", path); fread(header, 1, 8, fp); if (png_sig_cmp(header, 0, 8)) abort_("[read_png_file] File %s is not recognized as a PNG file", path); /* initialize stuff */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) abort_("[read_png_file] png_create_read_struct failed"); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) abort_("[read_png_file] png_create_info_struct failed"); if (setjmp(png_jmpbuf(png_ptr))) abort_("[read_png_file] Error during init_io"); png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); if (png_get_channels(png_ptr, info_ptr) == 1 && png_get_bit_depth(png_ptr, info_ptr) == 1) { type = 1; } else if (png_get_channels(png_ptr, info_ptr) == 4 && png_get_bit_depth(png_ptr, info_ptr) == 8) { type = 2; } if (type == 0) { fprintf(stderr, "Invalid PNG! Only mono or mono 4x8-bit RGBA PNG files are allowed\n"); exit(1); } width = png_get_image_width(png_ptr, info_ptr); height = png_get_image_height(png_ptr, info_ptr); png_read_update_info(png_ptr, info_ptr); /* read file */ if (setjmp(png_jmpbuf(png_ptr))) abort_("[read_png_file] Error during read_image"); row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height); rowbytes = type == 1 ? (width / 8) + (width % 8 > 0) : width << 2; for (y=0; y 0); bitmap = malloc(width*lb); memset(bitmap, 0, width*lb); #define heat_on(x, y) bitmap[(x*lb)+(y/8)] |= 1 << (7-(y%8)) for (y=0; yw = height; ret->h = width; ret->data = bitmap; return ret; } int main(int argc, const char ** argv) { if (argc <= 2) { fprintf(stderr, "Usage: %s printer pngfile\n", argv[0]); exit(EXIT_FAILURE); } ql570_open(argv[1]); pngdata_t * data = loadpng(argv[2]); //check_img(data); printf("w: %d\th: %d\n", data->w, data->h); //check_img(data); ql570_print(data); return EXIT_SUCCESS; }