aboutsummaryrefslogblamecommitdiffstats
path: root/ql570.c
blob: 4ed80a492d79b57fda6f0cfb5ecfa3ee50231814 (plain) (tree)































































































































































                                                                                                                            
                                                                                                       
































































                                                                          
/*
 * 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 <code@asbjorn.it>
 *
 * 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 <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <errno.h>

#define PNG_DEBUG 3
#include <png.h>

#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;i<img->h;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;i<img->h;i++) {
		fprintf(fp, "%c%c%c", 'g', 0x00, 90);
		for (j=0;j<lb;j++) {
			fprintf(fp, "%c", img->data[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<height; y++)
                row_pointers[y] = (png_byte*) malloc(rowbytes);

        png_read_image(png_ptr, row_pointers);

        fclose(fp);

	lb = (height / 8) + (height % 8 > 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; y<height; y++) {
                png_byte* row = row_pointers[y];
       	        for (x=0; x<width; x++) {
			if (type == 1) {
				ptr = &(row[x/8]);
			} else {
	                	ptr = &(row[x<<2]);
			}
			if ((ptr[0] & (1 << (7-(x%8)))) == 0) {
				heat_on(x, y);
			}
       	        }
       	}


	pngdata_t * ret = malloc(sizeof(pngdata_t));
	ret->w = 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;
}