PTdecode/src/main.cpp

Wed, 05 Aug 2009 15:02:31 +0100

author
Philip Pemberton <philpem@philpem.me.uk>
date
Wed, 05 Aug 2009 15:02:31 +0100
changeset 13
a933b13e087f
parent 5
1204ebf9340d
child 23
f2c7acb4a258
permissions
-rw-r--r--

PTdecode: add support for uncompressed data (NOTE: *NOT* supported by the PT-2450DX)

     1 /****************************************************************************
     2  * ptdecode: P-touch PT-2450DX output decoder
     3  ****************************************************************************/
     5 #include <cstdio>
     6 #include <exception>
     7 #include "CImg.h"
     9 using namespace std;
    10 using namespace cimg_library;
    12 // maximum size of a Ptouch printer head in dots
    13 const unsigned int PT_HEAD_WIDTH = 1024;
    15 // If defined, makes "blank row" blocks visible
    16 //#define MAKE_BLANK_ROWS_VISIBLE
    18 // custom exception class for file read errors
    19 class EReadError : public exception {
    20 	public:
    21 		virtual const char* what() const throw()
    22 		{
    23 			return "Read error";
    24 		}
    25 };
    27 FILE *fp;
    29 // get next character from file
    30 unsigned char getNext() {
    31 	unsigned char ch;
    32 	int i;
    34 	i = fread(&ch, 1, 1, fp);
    35 	if (i != 1) {
    36 		throw EReadError();
    37 	} else {
    38 		return ch;
    39 	}
    40 }
    42 // Handler for graphics transfer mode 1
    43 void runGraphicsXferMode1()
    44 {
    45 	bool exit = false;
    46 	unsigned int cm = -1;
    47 	unsigned long xpos = 0;
    48 	unsigned long ypos = 0;
    49 	unsigned long ydim = 128;
    50 	CImg<unsigned char> img(0, 0, 0, 0, (unsigned char)0);
    52 	while (!exit) {
    53 		unsigned char ch = getNext();
    54 		unsigned int len = 0;
    55 		unsigned int rowpos = 0;
    56 		unsigned char row[PT_HEAD_WIDTH / 8];	// stores uncompressed row data
    58 		switch (ch) {
    59 			case 'M':			// Set compression mode
    60 				ch = getNext();
    61 				cm = ch;
    62 				printf("Set compression mode: 0x%02X", ch);
    63 				switch (cm) {
    64 					case 0x02:
    65 						printf(" (TIFF/Packbits)\n");
    66 						break;
    67 					default:
    68 						printf(" *** Unknown, assuming uncompressed ***\n");
    69 						cm = 1;
    70 						break;
    71 				}
    72 				break;
    74 			case 'Z':			// Blank raster line
    75 				// Increment x-position and resize the image
    76 				img.resize(xpos+1, ydim, 1, 1, 0, 0);
    78 				// Blank the new row
    79 				if (img.dimy() > 0) {
    80 //					printf("Clear row: x=%lu\n", xpos);
    81 					for (int i=0; i<img.dimy(); i++) {
    82 #ifdef MAKE_BLANK_ROWS_VISIBLE
    83 						img(xpos, i) = 128;
    84 #else
    85 						img(xpos, i) = 255;
    86 #endif
    87 					}
    88 				}
    90 				xpos++;
    91 				break;
    93 			case 'G':			// Graphics data row
    94 				// decode the length
    95 				ch = getNext();
    96 				len = (((int)getNext()) << 8) + ch;
    98 				// Is gfx payload compressed or uncompressed?
    99 				if (cm == 1) {
   100 					// Uncompressed. Read straight into the row buffer.
   101 					while (len > 0) {
   102 						row[rowpos++] = getNext(); len--;
   103 					}
   104 				} else {
   105 					// Decompress the gfx data
   106 					rowpos = 0;
   107 					while (len > 0) {
   108 						// get the prefix byte
   109 						ch = getNext(); len--;
   111 						// Is this a "run" (a single byte replicated) or a "copy"?
   112 						int runlen;
   113 						if (ch & 0x80) {
   114 							// MSB set, it's a run
   115 							runlen = 257 - ((int)ch);
   117 							// Get the byte to replicate, and replicate it into the o/p buffer
   118 							ch = getNext(); len--;
   119 							while (runlen-- > 0) {
   120 								row[rowpos++] = ch;
   121 							}
   122 						} else {
   123 							// MSB clear, it's a copy
   124 							runlen = ((int)ch) + 1;
   126 							// Copy N bytes from the input stream to the output
   127 							while (runlen-- > 0) {
   128 								row[rowpos++] = getNext();
   129 								len--;
   130 							}
   131 						}
   132 					}
   133 				}
   135 				// Row decode complete. row contains the image data, and rowpos
   136 				// contains its length in bytes. Now shuffle it into CImg...
   138 				// If image height is less than size of image row, then make the
   139 				// image taller.
   140 				if (((unsigned int)img.dimy()) < (rowpos * 8)) {
   141 					ydim = rowpos * 8;
   142 				} else {
   143 					ydim = img.dimy();
   144 				}
   146 				// Perform the Y resize if necessary, but also make Xdim=Xdim+1
   147 				img.resize(xpos+1, ydim, 1, 1, 0, 0);
   149 				img(xpos, ydim/2) = 128;
   151 				// Now copy the image data...
   152 				ypos = 0;
   153 				for (unsigned int byte=0; byte<rowpos; byte++) {
   154 					for (unsigned int bit=0; bit<8; bit++) {
   155 						if (row[byte] & (0x80>>bit)) {
   156 							img(xpos, ypos) = 0;
   157 						} else {
   158 							img(xpos, ypos) = 255;
   159 						}
   161 						// Increment y-position
   162 						ypos++;
   163 					}
   164 				}
   166 				// An entire row has been decoded. Increment x-position.
   167 				xpos++;
   168 				break;
   170 			case 0x0c:		// FF
   171 				printf("Formfeed: Print without label feed (job completed, more labels follow)\n");
   172 				exit = true;
   173 				break;
   175 			case 0x1a:		// Ctrl-Z
   176 				printf("Ctrl-Z:   Print with label feed    (job completed, no further labels)\n");
   177 				exit = true;
   178 				break;
   180 			default:			// Something else
   181 				printf("** Unrecognised command prefix in gfx mode: 0x%02x\n", ch);
   182 				break;
   183 		}
   184 	}
   186 	// Display the contents of the image
   187 	img.display();
   188 }
   190 // Parse an ESC i command
   191 void parse_esc_i()
   192 {
   193 	unsigned char ch = getNext();
   194 	unsigned int tmpI;
   196 	switch (ch) {
   197 		case 'B':				// ESC i B: Specify baud rate
   198 			tmpI = getNext();
   199 			ch = getNext();
   200 			tmpI += ((int)ch)*256;
   201 			printf("Set baud rate:\t%d00", tmpI);
   202 			if ((tmpI != 96) && (tmpI != 576) && (tmpI != 1152)) {
   203 				printf(" [ILLEGAL SETTING]\n");
   204 			} else {
   205 				printf("\n");
   206 			}
   207 			break;
   209 		case 'S':				// ESC i S: Status request
   210 			printf("Printer status request\n");
   211 			break;
   213 		case 'M':				// ESC i M: Set mode
   214 			ch = getNext();
   215 			printf("Set mode 0x%02X:\tAutoCut %s, Mirror %s\n", ch,
   216 					(ch & 0x40) ? "on" : "off",
   217 					(ch & 0x80) ? "on" : "off");
   218 			break;
   220 		case 'd':				// ESC i d: Set margin amount (feed amount)
   221 			tmpI = getNext();
   222 			ch = getNext();
   223 			tmpI += ((int)ch)*256;
   224 			printf("Set margin:\t%d dots", tmpI);
   225 			break;
   227 		case 'K':				// ESC i K: Set expanded mode
   228 			ch = getNext();
   229 			printf("Set expanded mode 0x%02X:\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n", ch,
   230 					(ch & 0x04) ? "Half-cut on" : "Half-cut off",
   231 					(ch & 0x08) ? "Chain-print off: last label will be fed and cut" : "Chain-print on: last label will NOT be fed or cut",
   232 					(ch & 0x20) ? "Label end cut: when printing multiple copies, end of last label is cut" : "Label end cut off",
   233 					(ch & 0x40) ? "High-resolution (360x720dpi)" : "Normal resolution (360x360dpi)",
   234 					(ch & 0x80) ? "Copy-printing on (expansion buffer not cleared on form-feed)" : "Copy-printing off"
   235 				  );
   236 			break;
   238 		case 'R':				// ESC i R: Set graphics transfer mode
   239 			ch = getNext();
   240 			printf("Set graphics transfer mode 0x%02X: ", ch);
   241 			if (ch == 1) {
   242 				printf("Raster graphics mode\n");
   243 				runGraphicsXferMode1();
   244 			} else {
   245 				printf("\n\tUnrecognised graphics transfer mode: remainder of data may be garbage.\n");
   246 			}
   247 			break;
   249 		default:
   250 			printf("Unrecognised cmnd: ESC i 0x%02X\n", ch);
   251 			break;
   252 	}
   253 }
   255 // Parse an ESC command
   256 void parse_esc()
   257 {
   258 	unsigned char ch = getNext();
   260 	switch(ch) {
   261 		case 'i':		// ESC i: Brother-specific extensions
   262 			parse_esc_i();
   263 			break;
   265 		case '@':		// ESC @: Initialize
   266 			printf("Initialize: clear buffer and reset print origin\n");
   267 			break;
   269 		default:
   270 			printf("Unrecognised cmnd: ESC 0x%02X\n", ch);
   271 			break;
   272 	}
   273 }
   275 int main(int argc, char **argv)
   276 {
   277 	// check params
   278 	if (argc != 2) {
   279 		// wrong!
   280 		printf("Usage: %s filename\n", argv[0]);
   281 		return -1;
   282 	}
   284 	// open binary dump file
   285 	fp = fopen(argv[1], "rb");
   286 	if (!fp) {
   287 		printf("Error opening source file\n");
   288 		return -1;
   289 	}
   291 	try {
   292 		while (true) {
   293 			unsigned char ch;
   295 			ch = getNext();
   297 			switch (ch) {
   298 				case 0x00:		// NULL
   299 					printf("Null\n");
   300 					break;
   301 				case 0x0c:		// FF
   302 					printf("Formfeed: Print without feed\n");
   303 					break;
   304 				case 0x1a:		// Ctrl-Z
   305 					printf("Ctrl-Z:   Print with label feed\n");
   306 					break;
   307 				case 0x1b:		// ESC
   308 					parse_esc();
   309 					break;
   310 				default:
   311 					printf("Unrecognised cmnd: 0x%02X\n", ch);
   312 					break;
   313 			}
   314 		}
   315 	} catch (EReadError &e) {
   316 		if (feof(fp)) {
   317 			printf("EOF reached.\n");
   318 		} else {
   319 			printf("Uncaught EReadException: %s\n", e.what());
   320 		}
   321 	} catch (exception &e) {
   322 		printf("Uncaught exception: %s\n", e.what());
   323 	} catch (...) {
   324 		printf("Uncaught and unrecognised exception. Something went *really* wrong here...\n");
   325 	}
   327 	// close the file
   328 	fclose(fp);
   330 	return 0;
   331 }