PTdecode/src/main.cpp

Wed, 05 Aug 2009 17:32:05 +0100

author
Philip Pemberton <philpem@philpem.me.uk>
date
Wed, 05 Aug 2009 17:32:05 +0100
changeset 18
fd1c6f6066da
parent 13
a933b13e087f
child 23
f2c7acb4a258
permissions
-rw-r--r--

updated README

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