Mon, 03 Aug 2009 17:23:54 +0100
1st version with printing support. Now uses Libgd for image generation functionality.
Makefile | file | annotate | diff | revisions | |
src/ptouch.c | file | annotate | diff | revisions | |
src/ptouch.h | file | annotate | diff | revisions |
1.1 --- a/Makefile Mon Aug 03 16:14:35 2009 +0100 1.2 +++ b/Makefile Mon Aug 03 17:23:54 2009 +0100 1.3 @@ -113,7 +113,7 @@ 1.4 # libraries to link in -- these will be specified as "-l" parameters, the -l 1.5 # is prepended automatically 1.6 #LIB = jpeg tiff png z 1.7 -LIB = 1.8 +LIB = gd 1.9 # library paths -- where to search for the above libraries 1.10 LIBPATH = 1.11 # include paths -- where to search for #include files (in addition to the
2.1 --- a/src/ptouch.c Mon Aug 03 16:14:35 2009 +0100 2.2 +++ b/src/ptouch.c Mon Aug 03 17:23:54 2009 +0100 2.3 @@ -13,9 +13,10 @@ 2.4 2.5 #include <stdio.h> 2.6 #include <stdlib.h> 2.7 +#include <stdbool.h> 2.8 #include <string.h> 2.9 +#include <gd.h> 2.10 #include "hexdump.h" 2.11 -#include "pt_image.h" 2.12 #include "ptouch.h" 2.13 2.14 #define ESC 0x1b 2.15 @@ -68,7 +69,7 @@ 2.16 // Check for timeout 2.17 if (timeout == 0) { 2.18 // Timeout 2.19 - return -1; 2.20 + return PT_ERR_TIMEOUT; 2.21 } 2.22 2.23 #ifdef DEBUG 2.24 @@ -77,8 +78,6 @@ 2.25 #endif 2.26 2.27 // Decode the status buffer, store the results in the device object 2.28 - dev->headMark = buf[0]; 2.29 - dev->size = buf[1]; 2.30 dev->errorInfo[0] = buf[8]; 2.31 dev->errorInfo[1] = buf[9]; 2.32 dev->mediaWidth = buf[10]; 2.33 @@ -90,29 +89,150 @@ 2.34 dev->phaseLo = buf[21]; 2.35 dev->notification = buf[22]; 2.36 2.37 + // Set pixel width (label width in pixels) 2.38 + if (dev->mediaWidth >= 24) { 2.39 + // Label tape is 24mm or wider. Print head is 128 dots at 180dpi, 2.40 + // which is 18.06mm. Thus, we can only print on the centre 18mm 2.41 + // of a tape that is wider than 18mm. 2.42 + dev->pixelWidth = 128; 2.43 + } else { 2.44 + // Print head is 180dpi. Pixel size is mediaWidth * dpi. If we 2.45 + // multiply by ten, then divide by 254, we can avoid using 2.46 + // floating point to convert from inches to mm. The -2 is a 2.47 + // safety margin -- one pixel on either side of the label. 2.48 + // This is far closer than Brother suggest, but hey-ho. 2.49 + dev->pixelWidth = ((dev->mediaWidth * 180 * 10) / 254) - 2; 2.50 + } 2.51 + 2.52 // Operation succeeded 2.53 - return 0; 2.54 + return PT_ERR_SUCCESS; 2.55 } 2.56 2.57 -// TODO: print options struct parameter (e.g. fullcut, halfcut, print res, 2.58 +// TODO: print options struct parameter (e.g. fullcut, halfcut, print res, ...) 2.59 +// 2.60 // 2.61 -int pt_Print(pt_Device *dev, pt_Image *image) 2.62 + 2.63 +// labels: 1 or more labels 2.64 +// count: number of labels, 0<count<MAXINT 2.65 +int pt_Print(pt_Device *dev, gdImagePtr *labels, int count) 2.66 { 2.67 - // TODO: trap dev == NULL 2.68 - // TODO: trap image == NULL 2.69 - // TODO: trap image.height > printhead.height 2.70 - // TODO: trap image.height <= 0 2.71 - // TODO: trap image.width <= 0 2.72 + int err; 2.73 + gdImagePtr *curLabel = labels; 2.74 + 2.75 + // trap dev == NULL 2.76 + if (dev == NULL) { 2.77 + return PT_ERR_BAD_PARAMETER; 2.78 + } 2.79 + 2.80 + // trap labels == NULL 2.81 + if (labels == NULL) { 2.82 + return PT_ERR_BAD_PARAMETER; 2.83 + } 2.84 + 2.85 + // trap count == 0 2.86 + if (count == 0) { 2.87 + return PT_ERR_BAD_PARAMETER; 2.88 + } 2.89 + 2.90 + // Request current status from the printer 2.91 + if ((err = pt_GetStatus(dev)) != PT_ERR_SUCCESS) { 2.92 + return err; 2.93 + } 2.94 + 2.95 + // Make sure the printer has tape, and is ready 2.96 + if ((dev->errorInfo[0] != 0x00) || (dev->errorInfo[1] != 0x00)) { 2.97 + return PT_ERR_PRINTER_NOT_READY; 2.98 + } 2.99 + 2.100 + // Send the print initialisation commands 2.101 // 2.102 - // allocate print buffer 2.103 - // 2.104 - // pack pixels -- 8 pixels => 1 byte 2.105 - // 2.106 - // compress print data (packbits) 2.107 - // 2.108 - // send print buffer to printer 2.109 - // 2.110 - // free print buffer 2.111 + // ESC i M -- Set Mode 2.112 + fprintf(dev->fp, "%ciM%c", ESC, 2.113 + (dev->autocut ? 0x40 : 0x00) | (dev->mirror ? 0x80 : 0x00)); 2.114 + 2.115 + // ESC i K -- Set Expanded Mode 2.116 + fprintf(dev->fp, "%ciK%c", ESC, 0x00); 2.117 + 2.118 + // ESC i R {n1} -- Set Raster Graphics Transfer Mode 2.119 + // {n1} = 0x01 ==> Raster Graphics mode 2.120 + fprintf(dev->fp, "%ciR%c", ESC, 0x01); 2.121 + 2.122 + // M {n1} -- Set Compression Mode 2.123 + // {n1} = 0x00 ==> no compression 2.124 + // {n1} = 0x01 ==> reserved 2.125 + // {n1} = 0x02 ==> TIFF/Packbits 2.126 + fprintf(dev->fp, "M%c", 0x00); 2.127 + 2.128 + // Loop over the images that were passed in 2.129 + for (int imnum=0; imnum < count; imnum++) { 2.130 + // Make sure image is the right size for this label tape 2.131 + if (gdImageSY(*curLabel) != dev->pixelWidth) { 2.132 + return PT_ERR_LABEL_TOO_WIDE; 2.133 + } 2.134 + 2.135 + // 2.136 + // TODO: trap image height / width == 0? 2.137 + // will libgd allow an image with a zero dimension? 2.138 + // 2.139 + 2.140 + // Iterate left-to-right over the source image 2.141 + for (int xpos = 0; xpos < gdImageSX(*curLabel); xpos++) { 2.142 + char bitbuf[128/8]; // 128-dot printhead, 8 bits per byte 2.143 + int rowclear = true; // TRUE if entire row is clear, else FALSE 2.144 + 2.145 + // Fill bit buffer with zeroes 2.146 + memset(bitbuf, 0x00, sizeof(bitbuf)); 2.147 + 2.148 + // Calculate left-side margin for this label size 2.149 + // Again, 128-dot printhead. 2.150 + int margin = (128 + dev->pixelWidth) / 2; 2.151 + 2.152 + // Copy data from the image to the bit-buffer 2.153 + for (int ypos = 0; ypos < gdImageSY(*curLabel); ypos++) { 2.154 + // Get pixel from gd, is it white (palette entry 0)? 2.155 + if (gdImageGetPixel(*curLabel, xpos, ypos) != 0) { 2.156 + // No. Set the bit. 2.157 + int bit = 1 << (7 - ((margin+ypos) % 8)); 2.158 + bitbuf[(margin+ypos) / 8] |= bit; 2.159 + 2.160 + // Clear the "row is clear" flag 2.161 + rowclear = false; 2.162 + } 2.163 + } 2.164 + 2.165 + // We now have the image data in bitbuf, and a flag to say whether 2.166 + // there are any black pixels in the buffer. We can pack full-rows 2.167 + // by sending a "Z" character, i.e. "this row is entirely blank". 2.168 + // If not, we have to send a (128/8)=16 byte chunk of image data. 2.169 + if (rowclear) { 2.170 + // Row is clear -- send a "clear row" command 2.171 + fprintf(dev->fp, "Z"); 2.172 + } else { 2.173 + // Row is not clear -- send the pixel data 2.174 + // 2.175 + // TODO: the printer supports Packbits compression. Implement! 2.176 + fprintf(dev->fp, "G"); 2.177 + for (int i=0; i<sizeof(bitbuf); i++) { 2.178 + fputc(bitbuf[i], dev->fp); 2.179 + } 2.180 + } 2.181 + } 2.182 + 2.183 + // Is this the last label? 2.184 + if (imnum == (count-1)) { 2.185 + // Yes, send an End Job command 2.186 + fprintf(dev->fp, "%c", 0x1A); 2.187 + } else { 2.188 + // No, more labels to print. Send a formfeed. 2.189 + fprintf(dev->fp, "%c", 0x0C); 2.190 + } 2.191 + 2.192 + // Move onto the next label 2.193 + curLabel++; 2.194 + } 2.195 + 2.196 + // Operation successful. 2.197 + return PT_ERR_SUCCESS; 2.198 } 2.199 2.200 void pt_Close(pt_Device *dev)
3.1 --- a/src/ptouch.h Mon Aug 03 16:14:35 2009 +0100 3.2 +++ b/src/ptouch.h Mon Aug 03 17:23:54 2009 +0100 3.3 @@ -11,17 +11,102 @@ 3.4 #ifndef PTOUCH_H 3.5 #define PTOUCH_H 3.6 3.7 +#include <gd.h> 3.8 + 3.9 +/** 3.10 + * @brief Device information structure 3.11 + * 3.12 + * This is used to store the state of the printer as of the last call to 3.13 + * pt_GetStatus(), the current job settings, and other details required 3.14 + * by the printer driver. 3.15 + */ 3.16 typedef struct { 3.17 + /// Reference to the printer device 3.18 FILE *fp; 3.19 - int headMark, size, errorInfo[2]; 3.20 + /// Error information 3.21 + int errorInfo[2]; 3.22 + /// Label width, type and length 3.23 int mediaWidth, mediaType, mediaLength; 3.24 + /// Label width in pixels 3.25 + int pixelWidth; 3.26 + /// Status type, phase type, and phase number 3.27 int statusType, phaseType, phaseHi, phaseLo; 3.28 + /// Notification number 3.29 int notification; 3.30 + 3.31 + /// Print parameter: autocutter enable 3.32 + int autocut; 3.33 + /// Print parameter: mirror printing enable 3.34 + int mirror; 3.35 + /// Print parameter: half-cut enable 3.36 + /// Print parameter: chainprint enable 3.37 + /// Print parameter: label end cut 3.38 } pt_Device; 3.39 3.40 -// printer functions 3.41 +/* 3.42 + * Function return codes 3.43 + */ 3.44 +enum { 3.45 +/// Operation completed successfully 3.46 + PT_ERR_SUCCESS = 0, 3.47 + 3.48 +/// Data transfer timed out 3.49 + PT_ERR_TIMEOUT = -1, 3.50 + 3.51 +/// Invalid parameter 3.52 + PT_ERR_BAD_PARAMETER = -2, 3.53 + 3.54 +/// Label image is too large for this label tape 3.55 + PT_ERR_LABEL_TOO_WIDE = -3, 3.56 + 3.57 +/// Printer is not ready 3.58 + PT_ERR_PRINTER_NOT_READY = -4 3.59 +}; 3.60 + 3.61 + 3.62 +/** 3.63 + * @brief Initialise the printer 3.64 + * 3.65 + * Initialises the printer and returns a pointer to a pt_Device struct 3.66 + * describing it. 3.67 + * 3.68 + * @param path Path to the printer device file (e.g. /dev/usb/lp0) 3.69 + * @return On success, a pt_Device struct referring to the printer. 3.70 + * On failure, NULL. 3.71 + */ 3.72 pt_Device *pt_Initialise(char *path); 3.73 -int pt_GetStatus(pt_Device *dev); 3.74 + 3.75 +/** 3.76 + * @brief Close a printer device 3.77 + * 3.78 + * Closes the connection to the printer, and destroys the pt_Device struct. 3.79 + * 3.80 + * @param dev A pt_Device struct created by pt_Initialise. 3.81 + */ 3.82 void pt_Close(pt_Device *dev); 3.83 3.84 +/** 3.85 + * @brief Get the current status of the printer 3.86 + * 3.87 + * Queries the printer for its current status, then returns the result. 3.88 + * 3.89 + * @param dev A pt_Device struct created by pt_Initialise. 3.90 + * @return Any valid PT_ERR_* constant. The pt_Device struct passed in 3.91 + * is also updated with the current status of the printer. 3.92 + */ 3.93 +int pt_GetStatus(pt_Device *dev); 3.94 + 3.95 +/** 3.96 + * @brief Print one or more labels 3.97 + * 3.98 + * Takes a pointer to an array of Libgd images, and prints each of them. 3.99 + * 3.100 + * @param dev A pt_Device struct created by pt_Initialise. 3.101 + * @param labels A pointer to an array of gdImagePtr objects containing 3.102 + * the labels to be printed. 3.103 + * @param count The number of labels to be printed. 3.104 + * @return Any valid PT_ERR_* constant. 3.105 + */ 3.106 +int pt_Print(pt_Device *dev, gdImagePtr *labels, int count); 3.107 + 3.108 #endif // PTOUCH_H