libembroidery-sys 0.1.2

Rust FFI bindings for libembroidery
Documentation
/* .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 <math.h>
#include <string.h>
#include <stdlib.h>

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; */