Initial commit
[ql570.git] / ql570.c
1 /*
2  * Brother QL-570 thermal printing program
3  * It prints a mono PNG image directly to the printers block device
4  *
5  * Copyright 2011 Asbjørn Sloth Tønnesen <code@asbjorn.it>
6  *
7  * PNG reading based on:
8  *   A simple libpng example program
9  *   http://zarb.org/~gc/html/libpng.html
10  *   Copyright 2002-2011 Guillaume Cottenceau and contributors.
11  *
12  * This software may be freely redistributed under the terms
13  * of the libpng license.
14  * http://libpng.org/pub/png/src/libpng-LICENSE.txt
15  *
16  * Example usage:
17  *   ./ql570 /dev/usb/lp0 image.png
18  */
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <stdint.h>
23 #include <stdarg.h>
24 #include <errno.h>
25
26 #define PNG_DEBUG 3
27 #include <png.h>
28
29 #define ESC 0x1b
30
31 #define DEBUG
32
33 FILE * fp;
34
35 struct {
36         int16_t w;
37         int16_t h;
38         uint8_t * data;
39 } typedef pngdata_t;
40
41 FILE * ql570_open(const char * path)
42 {
43         fp = fopen(path, "r+b");
44         if (fp == NULL) {
45                 perror("fopen");
46                 exit(EXIT_FAILURE);
47         }
48         return fp;
49 }
50
51 void check_img(pngdata_t * img)
52 {
53         int i, j;
54         int lb = (img->w / 8) + (img->w % 8 > 0);
55         printf("lb: %d\n", lb);
56         for (i=0;i<img->h;i++) {
57                 for (j=img->w-1;j!=0;j--) {
58                         if (img->data[i*lb+j/8] & (1 << (7-(j % 8)))) {
59                                 printf("#");
60                         } else {
61                                 printf(" ");
62                         }
63                 }
64                 printf("\n");
65         }
66
67 }
68
69 void ql570_print(pngdata_t * img)
70 {
71         /* Init */
72         fprintf(fp, "%c%c", ESC, '@');
73
74         /* Set media type */
75         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);
76
77         /* Set cut type */
78         fprintf(fp, "%c%c%c", ESC, 'i', 'K', 8);
79
80         /* Enable cutter */
81         fprintf(fp, "%c%c%c", ESC, 'i', 'A', 1);
82
83         /* Set margin = 0 */
84         fprintf(fp, "%c%c%c%c%c", ESC, 'i', 'd', 0, 0);
85
86         int i, j;
87         int lb = (img->w / 8) + (img->w % 8 > 0);
88         for (i=0;i<img->h;i++) {
89                 fprintf(fp, "%c%c%c", 'g', 0x00, 90);
90                 for (j=0;j<lb;j++) {
91                         fprintf(fp, "%c", img->data[i*lb+j]);
92                 }
93                 for (;j<90;j++) {
94                         fprintf(fp, "%c", 0x00);
95                 }
96         }
97
98         /* Print */
99         fprintf(fp, "%c", 0x1a);
100 }
101
102 void abort_(const char * s, ...)
103 {
104         va_list args;
105         va_start(args, s);
106         vfprintf(stderr, s, args);
107         fprintf(stderr, "\n");
108         va_end(args);
109         abort();
110 }
111
112 pngdata_t * loadpng(const char * path)
113 {
114         png_structp png_ptr;
115         png_infop info_ptr;
116         png_bytep * row_pointers;
117         int width, height, rowbytes;
118         int x, y;
119         unsigned char header[8];    // 8 is the maximum size that can be checked
120         png_byte* ptr;
121         FILE *fp;
122         int type = 0;
123         int lb;
124         uint8_t * bitmap;
125
126         /* open file and test for it being a png */
127         fp = fopen(path, "rb");
128         if (!fp)
129                 abort_("[read_png_file] File %s could not be opened for reading", path);
130         fread(header, 1, 8, fp);
131         if (png_sig_cmp(header, 0, 8))
132                 abort_("[read_png_file] File %s is not recognized as a PNG file", path);
133
134         /* initialize stuff */
135         png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
136
137         if (!png_ptr)
138                 abort_("[read_png_file] png_create_read_struct failed");
139
140         info_ptr = png_create_info_struct(png_ptr);
141         if (!info_ptr)
142                 abort_("[read_png_file] png_create_info_struct failed");
143
144         if (setjmp(png_jmpbuf(png_ptr)))
145                 abort_("[read_png_file] Error during init_io");
146
147         png_init_io(png_ptr, fp);
148         png_set_sig_bytes(png_ptr, 8);
149
150         png_read_info(png_ptr, info_ptr);
151
152         if (png_get_channels(png_ptr, info_ptr) == 1
153             && png_get_bit_depth(png_ptr, info_ptr) == 1) {
154                 type = 1;
155         } else if (png_get_channels(png_ptr, info_ptr) == 4
156             && png_get_bit_depth(png_ptr, info_ptr) == 8) {
157                 type = 2;
158         }
159
160         if (type == 0) {
161                 fprintf(stderr, "Invalid PNG! Only mono or 4x8-bit RGBA PNG files are allowed\n");
162                 exit(1);
163         }
164
165         width = png_get_image_width(png_ptr, info_ptr);
166         height = png_get_image_height(png_ptr, info_ptr);
167
168         png_read_update_info(png_ptr, info_ptr);
169
170         /* read file */
171         if (setjmp(png_jmpbuf(png_ptr)))
172                 abort_("[read_png_file] Error during read_image");
173
174         row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
175
176         rowbytes = type == 1 ? (width / 8) + (width % 8 > 0) : width << 2;
177
178         for (y=0; y<height; y++)
179                 row_pointers[y] = (png_byte*) malloc(rowbytes);
180
181         png_read_image(png_ptr, row_pointers);
182
183         fclose(fp);
184
185         lb = (height / 8) + (height % 8 > 0);
186         bitmap = malloc(width*lb);
187         memset(bitmap, 0, width*lb);
188
189         #define heat_on(x, y) bitmap[(x*lb)+(y/8)] |= 1 << (7-(y%8))
190         for (y=0; y<height; y++) {
191                 png_byte* row = row_pointers[y];
192                 for (x=0; x<width; x++) {
193                         if (type == 1) {
194                                 ptr = &(row[x/8]);
195                         } else {
196                                 ptr = &(row[x<<2]);
197                         }
198                         if ((ptr[0] & (1 << (7-(x%8)))) == 0) {
199                                 heat_on(x, y);
200                         }
201                 }
202         }
203
204
205         pngdata_t * ret = malloc(sizeof(pngdata_t));
206         ret->w = height;
207         ret->h = width;
208         ret->data = bitmap;
209         return ret;
210 }
211
212 int main(int argc, const char ** argv)
213 {
214         if (argc <= 2) {
215                 fprintf(stderr, "Usage: %s printer pngfile\n", argv[0]);
216                 exit(EXIT_FAILURE);
217         }
218
219         ql570_open(argv[1]);
220         pngdata_t * data = loadpng(argv[2]);
221         //check_img(data);
222         printf("w: %d\th: %d\n", data->w, data->h);
223         //check_img(data);
224         ql570_print(data);
225         return EXIT_SUCCESS;
226 }