Tue, 18 Mar 2014 01:27:15 +0000
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 | } |