/* .DST (Tajima) embroidery file read/write routines * Format comments are thanks to tspilman@dalcoathletic.com who's * notes appeared at http://www.wotsit.org under Tajima Format. */ #include "format-dst.h" #include "emb-file.h" #include "emb-logging.h" #include "helpers-binary.h" #include "helpers-misc.h" #include #include #include static int decode_record_flags(unsigned char b2) { int returnCode = 0; if(b2 == 0xF3) { return END; } if(b2 & 0x80) { returnCode |= JUMP; } if(b2 & 0x40) { returnCode |= STOP; } return returnCode; } static unsigned char setbit(int pos) { return (unsigned char)(1 << pos); } /* TODO: review this then remove since emb-pattern.c has a similar function */ /* void combineJumpStitches(EmbPattern* p, int jumpsPerTrim) { if(!p) { embLog_error("format-dst.c combineJumpStitches(), p argument is null\n"); return; } EmbStitchList* pointer = p->stitchList; int jumpCount = 0; EmbStitchList* jumpListStart = 0; char needleDown = 0; while(pointer) { if((pointer->stitch.flags & JUMP) && !(pointer->stitch.flags & STOP)) { if(jumpCount == 0) { jumpListStart = pointer; } jumpCount++; if(needleDown && jumpCount >= jumpsPerTrim) { EmbStitchList* removePointer = jumpListStart->next; jumpListStart->stitch.xx = pointer->stitch.xx; jumpListStart->stitch.yy = pointer->stitch.yy; jumpListStart->stitch.flags |= TRIM; jumpListStart->next = pointer; jumpCount-=2; for(; jumpCount > 0; jumpCount--) { EmbStitchList* tempPointer = removePointer->next; jumpListStart->stitch.flags |= removePointer->stitch.flags; free(removePointer); removePointer = 0; removePointer = tempPointer; } jumpCount = 0; needleDown = 0; } } else { if(pointer->stitch.flags == NORMAL) { needleDown = 1; jumpCount = 0; } } pointer = pointer->next; } } */ static void encode_record(EmbFile* file, int x, int y, int flags) { char b0, b1, b2; b0 = b1 = b2 = 0; /* cannot encode values > +121 or < -121. */ if(x > 121 || x < -121) embLog_error("format-dst.c encode_record(), x is not in valid range [-121,121] , x = %d\n", x); if(y > 121 || y < -121) embLog_error("format-dst.c encode_record(), y is not in valid range [-121,121] , y = %d\n", y); if(x >= +41) { b2 += setbit(2); x -= 81; } if(x <= -41) { b2 += setbit(3); x += 81; } if(x >= +14) { b1 += setbit(2); x -= 27; } if(x <= -14) { b1 += setbit(3); x += 27; } if(x >= +5) { b0 += setbit(2); x -= 9; } if(x <= -5) { b0 += setbit(3); x += 9; } if(x >= +2) { b1 += setbit(0); x -= 3; } if(x <= -2) { b1 += setbit(1); x += 3; } if(x >= +1) { b0 += setbit(0); x -= 1; } if(x <= -1) { b0 += setbit(1); x += 1; } if(x != 0) { embLog_error("format-dst.c encode_record(), x should be zero yet x = %d\n", x); } if(y >= +41) { b2 += setbit(5); y -= 81; } if(y <= -41) { b2 += setbit(4); y += 81; } if(y >= +14) { b1 += setbit(5); y -= 27; } if(y <= -14) { b1 += setbit(4); y += 27; } if(y >= +5) { b0 += setbit(5); y -= 9; } if(y <= -5) { b0 += setbit(4); y += 9; } if(y >= +2) { b1 += setbit(7); y -= 3; } if(y <= -2) { b1 += setbit(6); y += 3; } if(y >= +1) { b0 += setbit(7); y -= 1; } if(y <= -1) { b0 += setbit(6); y += 1; } if(y != 0) { embLog_error("format-dst.c encode_record(), y should be zero yet y = %d\n", y); } b2 |= (char) 3; if(flags & END) { b2 = (char) -13; b0 = b1 = (char) 0; } /* if(flags & TRIM) { int v = 5; int dx = (int)(x/v), dy = (int)(y/v); for(i = 1; i < v; i++) { encode_record(file, dx, dy, JUMP); } encode_record(file, x - (dx * (v - 1)), y - (dy * (v - 1)), JUMP); return; } */ if(flags & (JUMP | TRIM)) { b2 = (char) (b2 | 0x83); } if(flags & STOP) { b2 = (char) (b2 | 0xC3); } binaryWriteByte(file, (unsigned char)b0); binaryWriteByte(file, (unsigned char)b1); binaryWriteByte(file, (unsigned char)b2); } /*convert 2 characters into 1 int for case statement */ /*#define cci(s) (s[0]*256+s[1]) */ #define cci(c1,c2) (c1*256+c2) static void set_dst_variable(EmbPattern* pattern, char* var, char* val) { unsigned int i; EmbThread t; for(i = 0; i <= (unsigned int)strlen(var); i++) { /* uppercase the var */ if(var[i] >= 'a' && var[i] <= 'z') { var[i] += 'A' - 'a'; } } /* macro converts 2 characters to 1 int, allows case statement... */ switch(cci(var[0],var[1])) { case cci('L','A'): /* Design Name (LA) */ /*pattern->set_variable("Design_Name",val); TODO: review this line. */ break; case cci('S','T'): /* Stitch count, 7 digits padded by leading 0's */ case cci('C','O'): /* Color change count, 3 digits padded by leading 0's */ case cci('+','X'): /* Design extents (+/-X,+/-Y), 5 digits padded by leading 0's */ case cci('-','X'): case cci('+','Y'): case cci('-','Y'): /* don't store these variables, they are recalculated at save */ break; case cci('A','X'): /* Relative coordinates of last point, 6 digits, padded with leading spaces, first char may be +/- */ case cci('A','Y'): case cci('M','X'): /* Coordinates of last point in previous file of multi-volume design, 6 digits, padded with leading spaces, first char may be +/- */ case cci('M','Y'): /* store these variables as-is, they will be converted to numbers and back at save; */ /*pattern->set_variable(var,val); TODO: review this line. */ break; case cci('P','D'): /* store this string as-is, it will be saved as-is, 6 characters */ if(strlen(val) != 6) { /*pattern->messages.add("Warning: in DST file read, PD is not 6 characters, but ",(int)strlen(val)); */ } /*pattern->set_variable(var,val);*/ break; /* Begin extended fields section */ case cci('A','U'): /* Author string, arbitrary length */ case cci('C','P'): /* Copyright string, arbitrary length */ /*pattern->set_variable(var,val); TODO: review this line. */ break; case cci('T','C'): /*Thread Color: #RRGGBB,Description,Catalog Number (1st field RGB hex values, 2nd&3rd fields optional arbitrary length) */ /* TODO: review these lines below. description=split_cell_str(val,2); catalog_number=split_cell_str(val,3); */ t.color = embColor_fromHexStr(val); t.description = ""; t.catalogNumber = ""; embPattern_addThread(pattern, t); break; default: /* unknown field, just save it. */ /*pattern->set_variable(var,val); TODO: review this line. */ break; } } /*! Reads a file with the given \a fileName and loads the data into \a pattern. * Returns \c true if successful, otherwise returns \c false. */ int readDst(EmbPattern* pattern, const char* fileName) { char var[3]; /* temporary storage variable name */ char val[512]; /* temporary storage variable value */ int valpos; unsigned char b[3]; char header[512 + 1]; EmbFile* file = 0; int i = 0; int flags; /* for converting stitches from file encoding */ /* * The header seems to contain information about the design. * Seems to be ASCII text delimited by 0x0D (carriage returns). * This must be in the file for most new software or hardware * to consider it a good file! This is much more important * than I originally believed. The header is 125 bytes in * length and padded out by 0x20 to 512 bytes total. * All entries in the header seem to be 2 ASCII characters * followed by a colon, then it's value trailed by a carriage return. * * char LA[16+1]; First is the 'LA' entry, which is the design name with no * path or extension information. The blank is 16 characters * in total, but the name must not be longer that 8 characters * and padded out with 0x20. * * char ST[7+1]; Next is the stitch count ST, this is a 7 digit number * padded by leading zeros. This is the total stitch count * including color changes, jumps, nups, and special records. * * char CO[3+1]; Next, is CO or colors, a 3 digit number padded by leading * zeros. This is the number of color change records in the file. * * char POSX[5+1]; Next is +X or the positive X extent in centimeters, a 5 * digit non-decimal number padded by leading zeros. * * char NEGX[5+1]; Following is the -X or the negative X extent in millimeters, * a 5 digit non-decimal number padded by leading zeros. * * char POSY[5+1]; Again, the +Y extents. * * char NEGY[5+1]; Again, the -Y extents. * * char AX[6+1]; AX and AY should express the relative coordinates of the * char AY[6+1]; last point from the start point in 0.1 mm. If the start * and last points are the same, the coordinates are (0,0). * * char MX[6+1]; MX and MY should express coordinates of the last point of * char MY[6+1]; the previous file for a multi-volume design. A multi- * volume design means a design consisted of two or more files. * This was used for huge designs that can not be stored in a * single paper tape roll. It is not used so much (almost * never) nowadays. * * char PD[9+1]; PD is also storing some information for multi-volume design. */ /* TODO: review commented code below pattern->clear(); pattern->set_variable("file_name",filename); */ if(!pattern) { embLog_error("format-dst.c readDst(), pattern argument is null\n"); return 0; } if(!fileName) { embLog_error("format-dst.c readDst(), fileName argument is null\n"); return 0; } file = embFile_open(fileName, "rb"); if(!file) { embLog_error("format-dst.c readDst(), cannot open %s for reading\n", fileName); return 0; } embPattern_loadExternalColorFile(pattern, fileName); /* READ 512 BYTE HEADER INTO header[] */ for(i = 0; i < 512; i++) { header[i] = (char)embFile_getc(file); } /*TODO:It would probably be a good idea to validate file before accepting it. */ /* fill variables from header fields */ for(i = 0; i < 512; i++) { if(header[i] == ':' && i > 1) { var[0] = header[i - 2]; var[1] = header[i - 1]; var[2] = '\0'; valpos = i + 1; for(i++; i < 512; i++) { /* don't accept : without CR because there's a bug below: i-valpos must be > 0 which is not the case if the : is before the third character. */ if(header[i] == 13/*||header[i]==':'*/) /* 0x0d = carriage return */ { if(header[i] == ':') /* : indicates another variable, CR was missing! */ { i -= 2; } strncpy(val, &header[valpos], (size_t)(i - valpos)); val[i - valpos] = '\0'; set_dst_variable(pattern, var, val); break; } } } } while(embFile_read(b, 1, 3, file) == 3) { int x = 0; int y = 0; if(b[0] & 0x01) x += 1; if(b[0] & 0x02) x -= 1; if(b[0] & 0x04) x += 9; if(b[0] & 0x08) x -= 9; if(b[0] & 0x80) y += 1; if(b[0] & 0x40) y -= 1; if(b[0] & 0x20) y += 9; if(b[0] & 0x10) y -= 9; if(b[1] & 0x01) x += 3; if(b[1] & 0x02) x -= 3; if(b[1] & 0x04) x += 27; if(b[1] & 0x08) x -= 27; if(b[1] & 0x80) y += 3; if(b[1] & 0x40) y -= 3; if(b[1] & 0x20) y += 27; if(b[1] & 0x10) y -= 27; if(b[2] & 0x04) x += 81; if(b[2] & 0x08) x -= 81; if(b[2] & 0x20) y += 81; if(b[2] & 0x10) y -= 81; flags = decode_record_flags(b[2]); if(flags == END) { break; } embPattern_addStitchRel(pattern, x / 10.0, y / 10.0, flags, 1); } embFile_close(file); /* Check for an END stitch and add one if it is not present */ if(pattern->lastStitch->stitch.flags != END) embPattern_addStitchRel(pattern, 0, 0, END, 1); /* combineJumpStitches(pattern, 5); */ return 1; } /*! Writes the data from \a pattern to a file with the given \a fileName. * Returns \c true if successful, otherwise returns \c false. */ int writeDst(EmbPattern* pattern, const char* fileName) { EmbRect boundingRect; EmbFile* file = 0; int xx, yy, dx, dy, flags; int i; int co = 1, st = 0; int ax, ay, mx, my; char* pd = 0; EmbStitchList* pointer = 0; if(!pattern) { embLog_error("format-dst.c writeDst(), pattern argument is null\n"); return 0; } if(!fileName) { embLog_error("format-dst.c writeDst(), fileName argument is null\n"); return 0; } if(!embStitchList_count(pattern->stitchList)) { embLog_error("format-dst.c writeDst(), pattern contains no stitches\n"); return 0; } /* Check for an END stitch and add one if it is not present */ if(pattern->lastStitch->stitch.flags != END) embPattern_addStitchRel(pattern, 0, 0, END, 1); file = embFile_open(fileName, "wb"); if(!file) { embLog_error("format-dst.c writeDst(), cannot open %s for writing\n", fileName); return 0; } embPattern_correctForMaxStitchLength(pattern, 12.1, 12.1); xx = yy = 0; co = 1; co = embThreadList_count(pattern->threadList); st = 0; st = embStitchList_count(pattern->stitchList); flags = NORMAL; boundingRect = embPattern_calcBoundingBox(pattern); /* TODO: review the code below if(pattern->get_variable("design_name") != NULL) { char *la = stralloccopy(pattern->get_variable("design_name")); if(strlen(la)>16) la[16]='\0'; embFile_printf(file,"LA:%-16s\x0d",la); free(la); } else { */ embFile_printf(file, "LA:%-16s\x0d", "Untitled"); /*} */ embFile_printf(file, "ST:%7d\x0d", st); embFile_printf(file, "CO:%3d\x0d", co - 1); /* number of color changes, not number of colors! */ embFile_printf(file, "+X:%5d\x0d", (int)(boundingRect.right * 10.0)); embFile_printf(file, "-X:%5d\x0d", (int)(fabs(boundingRect.left) * 10.0)); embFile_printf(file, "+Y:%5d\x0d", (int)(boundingRect.bottom * 10.0)); embFile_printf(file, "-Y:%5d\x0d", (int)(fabs(boundingRect.top) * 10.0)); ax = ay = mx = my = 0; /* TODO: review the code below */ /*ax=pattern->get_variable_int("ax"); */ /* will return 0 if not defined */ /*ay=pattern->get_variable_int("ay"); */ /*mx=pattern->get_variable_int("mx"); */ /*my=pattern->get_variable_int("my"); */ /*pd=pattern->get_variable("pd");*/ /* will return null pointer if not defined */ pd = 0; if(pd == 0 || strlen(pd) != 6) { /* pd is not valid, so fill in a default consisting of "******" */ pd = "******"; } embFile_printf(file, "AX:+%5d\x0d", ax); embFile_printf(file, "AY:+%5d\x0d", ay); embFile_printf(file, "MX:+%5d\x0d", mx); embFile_printf(file, "MY:+%5d\x0d", my); embFile_printf(file, "PD:%6s\x0d", pd); binaryWriteByte(file, 0x1a); /* 0x1a is the code for end of section. */ /* pad out header to proper length */ for(i = 125; i < 512; i++) { embFile_printf(file, " "); } /* write stitches */ xx = yy = 0; pointer = pattern->stitchList; while(pointer) { /* convert from mm to 0.1mm for file format */ dx = roundDouble(pointer->stitch.xx * 10.0) - xx; dy = roundDouble(pointer->stitch.yy * 10.0) - yy; xx = roundDouble(pointer->stitch.xx * 10.0); yy = roundDouble(pointer->stitch.yy * 10.0); flags = pointer->stitch.flags; encode_record(file, dx, dy, flags); pointer = pointer->next; } binaryWriteByte(file, 0xA1); /* finish file with a terminator character */ binaryWriteShort(file, 0); embFile_close(file); return 1; } /* kate: bom off; indent-mode cstyle; indent-width 4; replace-trailing-space-save on; */