PTdecode/src/main.cpp

Tue, 18 Mar 2014 01:27:15 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Tue, 18 Mar 2014 01:27:15 +0000
changeset 23
f2c7acb4a258
parent 13
a933b13e087f
permissions
-rw-r--r--

Update PTdecode to handle output from other Ptouch drivers

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