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

     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 }