Mon, 03 Aug 2009 23:39:53 +0100
add basic test routine for Ptouch library
1 /****************************************************************************
2 * Project: P-touch printer driver library
3 * Developer: Philip Pemberton
4 * Purpose: Make Brother P-touch (PT-series) printers do something besides
5 * gather dust.
6 *
7 * Currently supports:
8 * PT-2450DX
9 ****************************************************************************/
11 // TODO: disable
12 #define DEBUG
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <stdbool.h>
17 #include <string.h>
18 #include <gd.h>
19 #include "hexdump.h"
20 #include "ptouch.h"
22 #define ESC 0x1b
24 pt_Device *pt_Initialise(char *path)
25 {
26 pt_Device *dev;
27 FILE *prn;
29 // Try and open the printer device
30 prn = fopen(path, "r+b");
31 if (prn == NULL) {
32 return NULL;
33 }
35 // Printer device open, send an init command and read the status
36 fprintf(prn, "%c%c", ESC, '@');
38 // Allocate memory for the device block
39 dev = malloc(sizeof(pt_Device));
40 if (dev == NULL) {
41 fclose(prn);
42 return NULL;
43 }
45 // Store the file pointer
46 dev->fp = prn;
48 // Memory allocation OK, now get the printer's status
49 if (pt_GetStatus(dev) == 0) {
50 return dev;
51 } else {
52 free(dev);
53 return NULL;
54 }
55 }
57 int pt_GetStatus(pt_Device *dev)
58 {
59 // REQUEST STATUS
60 fprintf(dev->fp, "%c%c%c", ESC, 'i', 'S');
62 // Read status buffer from printer
63 unsigned char buf[32];
64 int timeout = 128;
65 do {
66 fread(buf, 1, 32, dev->fp);
67 } while ((buf[0] != 0x80) && (timeout-- > 0));
69 // Check for timeout
70 if (timeout == 0) {
71 // Timeout
72 return PT_ERR_TIMEOUT;
73 }
75 #ifdef DEBUG
76 printf("DEBUG: Printer status buffer = \n");
77 hex_dump(buf, 32);
78 #endif
80 // Decode the status buffer, store the results in the device object
81 dev->errorInfo[0] = buf[8];
82 dev->errorInfo[1] = buf[9];
83 dev->mediaWidth = buf[10];
84 dev->mediaType = buf[11];
85 dev->mediaLength = buf[17];
86 dev->statusType = buf[18];
87 dev->phaseType = buf[19];
88 dev->phaseHi = buf[20];
89 dev->phaseLo = buf[21];
90 dev->notification = buf[22];
92 // Set pixel width (label width in pixels)
93 if (dev->mediaWidth >= 24) {
94 // Label tape is 24mm or wider. Print head is 128 dots at 180dpi,
95 // which is 18.06mm. Thus, we can only print on the centre 18mm
96 // of a tape that is wider than 18mm.
97 dev->pixelWidth = 128;
98 } else {
99 // Print head is 180dpi. Pixel size is mediaWidth * dpi. If we
100 // multiply by ten, then divide by 254, we can avoid using
101 // floating point to convert from inches to mm. The -2 is a
102 // safety margin -- one pixel on either side of the label.
103 // This is far closer than Brother suggest, but hey-ho.
104 dev->pixelWidth = ((dev->mediaWidth * 180 * 10) / 254) - 2;
105 }
107 // Operation succeeded
108 return PT_ERR_SUCCESS;
109 }
111 // TODO: print options struct parameter (e.g. fullcut, halfcut, print res, ...)
112 //
113 //
115 // labels: 1 or more labels
116 // count: number of labels, 0<count<MAXINT
117 int pt_Print(pt_Device *dev, gdImagePtr *labels, int count)
118 {
119 int err;
120 gdImagePtr *curLabel = labels;
122 // trap dev == NULL
123 if (dev == NULL) {
124 return PT_ERR_BAD_PARAMETER;
125 }
127 // trap labels == NULL
128 if (labels == NULL) {
129 return PT_ERR_BAD_PARAMETER;
130 }
132 // trap count == 0
133 if (count == 0) {
134 return PT_ERR_BAD_PARAMETER;
135 }
137 // Request current status from the printer
138 if ((err = pt_GetStatus(dev)) != PT_ERR_SUCCESS) {
139 return err;
140 }
142 // Make sure the printer has tape, and is ready
143 if ((dev->errorInfo[0] != 0x00) || (dev->errorInfo[1] != 0x00)) {
144 return PT_ERR_PRINTER_NOT_READY;
145 }
147 // Send the print initialisation commands
148 //
149 // ESC i M -- Set Mode
150 fprintf(dev->fp, "%ciM%c", ESC,
151 (dev->autocut ? 0x40 : 0x00) | (dev->mirror ? 0x80 : 0x00));
153 // ESC i K -- Set Expanded Mode
154 fprintf(dev->fp, "%ciK%c", ESC, 0x00);
156 // ESC i R {n1} -- Set Raster Graphics Transfer Mode
157 // {n1} = 0x01 ==> Raster Graphics mode
158 fprintf(dev->fp, "%ciR%c", ESC, 0x01);
160 // M {n1} -- Set Compression Mode
161 // {n1} = 0x00 ==> no compression
162 // {n1} = 0x01 ==> reserved
163 // {n1} = 0x02 ==> TIFF/Packbits
164 fprintf(dev->fp, "M%c", 0x00);
166 // Loop over the images that were passed in
167 for (int imnum=0; imnum < count; imnum++) {
168 // Make sure image is the right size for this label tape
169 if (gdImageSY(*curLabel) != dev->pixelWidth) {
170 return PT_ERR_LABEL_TOO_WIDE;
171 }
173 //
174 // TODO: trap image height / width == 0?
175 // will libgd allow an image with a zero dimension?
176 //
178 // Iterate left-to-right over the source image
179 for (int xpos = 0; xpos < gdImageSX(*curLabel); xpos++) {
180 char bitbuf[128/8]; // 128-dot printhead, 8 bits per byte
181 int rowclear = true; // TRUE if entire row is clear, else FALSE
183 // Fill bit buffer with zeroes
184 memset(bitbuf, 0x00, sizeof(bitbuf));
186 // Calculate left-side margin for this label size
187 // Again, 128-dot printhead.
188 int margin = (128 + dev->pixelWidth) / 2;
190 // Copy data from the image to the bit-buffer
191 for (int ypos = 0; ypos < gdImageSY(*curLabel); ypos++) {
192 // Get pixel from gd, is it white (palette entry 0)?
193 if (gdImageGetPixel(*curLabel, xpos, ypos) != 0) {
194 // No. Set the bit.
195 int bit = 1 << (7 - ((margin+ypos) % 8));
196 bitbuf[(margin+ypos) / 8] |= bit;
198 // Clear the "row is clear" flag
199 rowclear = false;
200 }
201 }
203 // We now have the image data in bitbuf, and a flag to say whether
204 // there are any black pixels in the buffer. We can pack full-rows
205 // by sending a "Z" character, i.e. "this row is entirely blank".
206 // If not, we have to send a (128/8)=16 byte chunk of image data.
207 if (rowclear) {
208 // Row is clear -- send a "clear row" command
209 fprintf(dev->fp, "Z");
210 } else {
211 // Row is not clear -- send the pixel data
212 //
213 // TODO: the printer supports Packbits compression. Implement!
214 fprintf(dev->fp, "G");
215 for (int i=0; i<sizeof(bitbuf); i++) {
216 fputc(bitbuf[i], dev->fp);
217 }
218 }
219 }
221 // Is this the last label?
222 if (imnum == (count-1)) {
223 // Yes, send an End Job command
224 fprintf(dev->fp, "%c", 0x1A);
225 } else {
226 // No, more labels to print. Send a formfeed.
227 fprintf(dev->fp, "%c", 0x0C);
228 }
230 // Move onto the next label
231 curLabel++;
232 }
234 // Operation successful.
235 return PT_ERR_SUCCESS;
236 }
238 void pt_Close(pt_Device *dev)
239 {
240 // Sanity check -- make sure dev is not null
241 if (dev == NULL) return;
243 // Close the printer stream
244 fclose(dev->fp);
246 // Release the memory allocated to the status buffer
247 free(dev);
248 }