src/ptouch.c

Mon, 03 Aug 2009 23:39:53 +0100

author
Philip Pemberton <philpem@philpem.me.uk>
date
Mon, 03 Aug 2009 23:39:53 +0100
changeset 10
604c205d9163
parent 9
ebce4a7615e9
child 14
088286f9e1e4
permissions
-rw-r--r--

add basic test routine for Ptouch library

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