Click here to Skip to main content
15,889,096 members
Articles / Programming Languages / C#

QR Code Embroidery DST File Creator

Rate me:
Please Sign up or sign in to vote.
2.78/5 (9 votes)
8 May 2015CPOL6 min read 31.2K   609   14   3
Creating a .DST embroidery design file from QR-code matrix.

Introduction

Nowadays everybody knows about QR-codes. The QR-code structure was invented in Japan by Masahiro Hara. This article describes the process of converting QR-code matrix into embroidery design format (Taijama DST).

Problem

There is no default function to create Qr-code in embroidery software so far. It is only possible to generate  a raster picture in any on-line service, which provides a Qr-code image. And then generate stitches on that picture in an embroidery program. It takes not much time for 3 or 5 QR-codes, although for larger quantity of QR-codes it takes a lot of time.

Solution

To automate QR-code generation is used external library. And then for six areas of QR-code column by column stitches are generated and finaly are saved in a file. Deviding in six areas is shown on folowing picture:

Image 1

Console application which demonstates QR-code generation can be downloaded by the following link:

In the root folder of the application are an executable file, dll and  a text file test.asc. To start the program from command prompt just type the following:

qrcodegen.exe test.asc

The application generates the new file with .DST extension. File can be opened with any embroidery design program. Input string can contains visit card format (VCARD) ,geolocation data (GEO), URL link or just simple text.

Example of VCARD format:

BEGIN:VCARD
VERSION:3.0
FN:John Doo
N:Jonh
ORG:Company name
URL:http://en.wikipedia.org/wiki/QR_code
EMAIL;TYPE=INTERNET:vasya.kvakin@example.com
END:VCARD

Input data in case of geographic coordinates, should contain longitude and latitude devided by comma separator and go after word GEO with colon:

GEO:30.31616, 59.95015

If you have access to an embroidery machine you be able to stitch this QR-code on a T-shirt or any clothes you choose:

Image 2

Background

 The code is written in C#. It uses MessagingToolkit.QRCode library to create QR-code matrix from  a string.  Library is available for installation from nuget: 

 

PM> Install-Package MessagingToolkit.QRCode

 Input string could be a representation of VCARD, GEO coordinates or just  an URL as noticed earlier.

QR-code matrix is created as two dimensional jugged boolean array.

At this point we have a boolean matrix of QR-Code and the next step is generating a list of lines. Lines are vertical series of cells without gaps.

These lines are used to create blocks of stitches, so stitches are composed in blocks. Blocks are vertical or horizontal bars, they are set of stitches.

The three distinctive squares are located at the corners of the QR code. Therefore QR-code is divided into six areas. The first area is  the left top square, so it is generated first. Then the second area is generated - vertical bars between the previous area and the the square at the bottom. The third area is the left bottom square. Then column by column, the fourth area is generated until the top right square is reached. The top right distinctive square is generated as the first and second square. Finally the sixth area is generated column by column.

Code description

First thing to do is to generate QR-code matrix. 

Class QRCodeCreator

This class uses MessagingToolkit.QRCode.Codec namespace to generate a matrix of QR-code:

using System.Text;
using MessagingToolkit.QRCode.Codec;
namespace EmbroideryFile.QRCode
{
    internal class QRCodeCreator
    {
        public bool[][] GetQRCodeMatrix(string DataToEncode)
        {
            if (string.IsNullOrEmpty(DataToEncode))
                return null;
            QRCodeEncoder qrCodeEncoder = new QRCodeEncoder();
            qrCodeEncoder.CharacterSet = "UTF8";
            qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE;
            qrCodeEncoder.QRCodeScale = 1;
            qrCodeEncoder.QRCodeVersion = -1;
            qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.L;
            return qrCodeEncoder.CalQrcode(Encoding.UTF8.GetBytes(DataToEncode));
        }
    }
}

CharacterSet is set to UTF8 to have ability to encode non latin characters. Encoding redundancy is set to low level  (QRCodeEncoder.ERROR_CORRECTION.L) into property QRCodeErrorCorrect. It supposes QR-code will not be damaged, so hi level of redundacy is excesive. This class is aggregated in class QRCodeStitcher.

Class QRCodeStitcher

All stitch blocks creation is implemented in this class. It  proceeds in two steps:

  1. Prepare a list of vertical lines  without gaps from cells for each of six areas of QR-code matrix; 
  2. Create stitch blocks for each continuous line. 

To achieve first step  it goes throught columns of matrix cells and when next empty cell is detectect then the current line is finished and is added to the result list. So from the boolean matrix a list of continuous lines is created. Each line contains information about the start point, end point, length of line and a flag of touching the bottom of QR-code.

For second step it goes through each line cell by cell and add zigzagified stitches to each stitch block result.

In a line bar step in Y and X axis and cell size are hard coded: dX = 25; dY = 2; cellSize = 25; The units here is 0.1 mm.

Class  QRCodeStitcher has class QRCodeCreater as a member _qrCode. When property Info of type QRCodeStitchInfo is set then in method SetInfo property Matrix  of _info memeber is calculated by GetQrCodeMatrix method.

  Line has the following structure:

C#
 public struct Line
{
    public Coords Dot1 { get; set; }
    public Coords Dot2 { get; set; }
    public int Length { get; set; }
    public bool Lowest { get; set; }
}

This method generates all stitch blocks for all six areas:

C#
private List<List<Coords>> GenerateQRCodeStitchesBoxed()
{
  var blocks = new List<List<Coords>>();
  int rectSize = GetRectSize();
  // the left top rectangle
  blocks.AddRange(GetRectangleSatin(0, 0, rectSize - 1, rectSize - 1));
  // the left top inner box
  blocks.Add(GenerateBoxStitchBlock(2, 2, rectSize - 4));
  // area between top and bottom left rectangle
  blocks.AddRange(GetSatinStitches(GetLaneList(0, rectSize + 1, rectSize, _dimension - rectSize - 1)));
  // the left bottom rectangle
  blocks.AddRange(GetRectangleSatin(0, _dimension - rectSize, rectSize - 1, _dimension - 1));
  // the left bottom inner box
  blocks.Add(GenerateBoxStitchBlock(2, _dimension - rectSize + 2, rectSize - 4));
  // middle area 
  blocks.AddRange(GetSatinStitches(GetLaneList(rectSize + 1, 0, _dimension - rectSize - 1, _dimension - 1)));
  // right left top rectangle
  blocks.AddRange(GetRectangleSatin(_dimension - rectSize, 0, _dimension - 1, rectSize - 1));
  // the right top inner box
  blocks.Add(GenerateBoxStitchBlock(_dimension - rectSize + 2, 2, rectSize - 4));
  // area under the right top rectangle
  blocks.AddRange(GetSatinStitches(GetLaneList(_dimension - rectSize, rectSize + 1, _dimension - 1, _dimension - 1)));
  return blocks;
}

Method GenerateBoxStitchBlock is used to generate inner box of distinctive squares:

C#
/// <summary>
/// Creates stitches for full filled box
/// </summary>
/// <param name="cellHorizonPos">Horisontal position of top left cell of box</param>
/// <param name="cellVerticalPos">Vertical postion of top left cell of box</param>
/// <param name="boxSize">Size of the box</param>
/// <returns>List of Coords</returns>
private List<Coords> GenerateBoxStitchBlock(int cellHorizonPos, int cellVerticalPos, int boxSize)
{
   var block = new List<Coords>();
   int y = 0; int x = 0;
   int startX = cellHorizonPos * _cellSize;
   int startY = cellVerticalPos * _cellSize;
   block.Add(new Coords { X = startX, Y = startY });
   while (y < _cellSize * boxSize)
   {
        while (x < _cellSize * boxSize - _dX)
        {
           x = x + _dX;
           block.Add(new Coords{ X = startX + x, Y = startY + y });
        }
        x = boxSize * _cellSize;
        block.Add(new Coords { X = startX + x, Y = startY + y });
        y = y + _dY;
        while (x > _dX)
        {
            x = x - _dX;
            block.Add(new Coords { X = startX + x, Y = startY + y });
        }
        x = 0;
        block.Add(new Coords { X = startX + x, Y = startY + y });
        y = y + _dY;
   }
   return block;
}

 Method GetRectangleSatin creates blocks of stitches for distinctive rectangle.

C#
IEnumerable<List<Coords>> GetRectangleSatin(int x1, int y1, int x2, int y2)
{
     int LeftX = (x1 > x2) ? x2 : x1;
     int TopY = (y1 > y2) ? y2 : y1;
     int RightX = (x1 < x2) ? x2 : x1;
     var BottomY = (y1 < y2) ? y2 : y1;
     int length = RightX - LeftX;
     var rect = new List<List<Coords>>();
     rect.Add(GenerateVerticalColumnStitchBlock(LeftX, TopY, length));
     rect.Add(GenerateHorizonColumnStitchBlock(LeftX, BottomY, length));
     rect.Add(ReverseCoords(GenerateVerticalColumnStitchBlock(RightX, TopY + 1, length)));
     rect.Add(ReverseCoords(GenerateHorizonColumnStitchBlock(LeftX + 1, TopY, length)));
     return rect;
}

Zigzaggified columns are formed by following method:

C#
/// <summary>
/// Generates sequence of stitch blocks (columns) by list of lanes
/// </summary>
private List<List<Coords>> GetSatinStitches(List<Line> lanes)
{
  List<List<Coords>> blockList = new List<List<Coords>>();
  foreach (var lane in lanes)
  {           
      List<Coords> satin = null;
      if (((lane.Length == 1) && ((lane.Dot1.X % 2) == 0)) ||
           ((lane.Length > 1) && (lane.Dot2.Y > lane.Dot1.Y)))
            satin = GenerateVerticalColumnStitchBlock(lane.Dot1.X, lane.Dot1.Y, lane.Length); 
      else                       
        satin = ReverseCoords(GenerateVerticalColumnStitchBlock(lane.Dot2.X, lane.Dot2.Y, lane.Length));
       
       blockList.Add(satin);
  }
  return blockList;
}

 Even columns are stitched from top to bottom, odd columns are stitched from bottom to top. 

Method GenerateVerticalColoumnStitchBlock creates list of stitches for line downwards.

C#
/// <summary>
/// Generates stitches for vertical line according to position of Dots
/// </summary>
/// <param name="cellHorizonPos"></param>
/// <param name="cellVerticalPos"></param>
/// <param name="length"></param>
List<Coords> GenerateVerticalColumnStitchBlock(int cellHorizonPos, int cellVerticalPos, int length)
{
     var block = new List<Coords>();
     int curX, curY;
     int columnLength = _cellSize * length;
     int startX = cellHorizonPos * _cellSize;
     int startY = cellVerticalPos * _cellSize;
     block.Add(new Coords { X = startX + _cellSize, Y = startY });
     for (curY = 0; curY < columnLength; curY = curY + _dY)
     {
      for (curX = (curY == 0) ? 0 : _dX; (curX < _cellSize) && (curY < columnLength); curX = curX + _dX)
        {
         block.Add(new Coords { X = startX + curX, Y = startY + curY });
         curY = curY + _dY;
        }
      int edgedX = _cellSize - (curX - _dX);
      int edgedY = edgedX * _dY / _dX;
      curX = _cellSize;
      curY = curY + edgedY - _dY;
      block.Add(new Coords { X = startX + curX, Y = startY + curY });
      curY = curY + _dY;
      for (curX = _cellSize - _dX; (curX > 0) && (curY < columnLength); curX = curX - _dX)
      {
        block.Add(new Coords { X = startX + curX, Y = startY + curY });
        curY = curY + _dY;
      }
      edgedX = curX + _dX;
      edgedY = edgedX * _dY / _dX;
      curY = curY + edgedY - _dY;
      block.Add(new Coords { X = startX, Y = startY + curY });
    }
    curX = _cellSize;
    curY = columnLength;
    block.Add(new Coords { X = startX + curX, Y = startY + curY });
    return block;
}

Method GetLaneList returns a list of continuous lines for specified by coordinates of corners area.

/// <summary>
/// Gets list of vertical string for specified rectangular area
/// </summary>
/// <param name="x1">start X position of a lane</param>
/// <param name="y1">start Y position of a lane</param>
/// <param name="x2">end X position of a lane</param>
/// <param name="y2">end Y position of a lane</param>
/// <returns></returns>
private List<Line> GetLaneList(int x1, int y1, int x2, int y2)
{
            try
            {
                if (_lines != null) _lines.Clear();
                if (y1 > y2)
                {
                    _topY = y2;
                    _bottomY = y1;
                }
                else
                {
                    _topY = y1;
                    _bottomY = y2;
                }
                if (x1 > x2)
                {
                    _leftX = x2;
                    _rightX = x1;
                }
                else
                {
                    _leftX = x1;
                    _rightX = x2;
                }
                for (int j = _leftX; j <= _rightX; j = j + 2) //X
                {
                    _state = false;
                    for (int i = _topY; i <= _bottomY; i++) // Y               
                    {
                        ConsumeRelativeCellDown(j, i);
                    }
                    if (j >= _rightX) break;
                    _state = false;
                    for (int i = _bottomY; i >= _topY; i--) // Y               
                    {
                        ConsumeRelativeCellUp(j + 1, i);
                    }
                }
                return _lines;
            }
            catch (Exception ex)
            {
                Trace.WriteLine(string.Format("GetLineList(): {0}",ex));
                throw;
           }
}

Method  ConsumeRelativeCellDown is used for even lines on the way from the top cell to  the bottom cell:

C#
 /// <summary>
 /// Check cell to stop current line or start  new in the down direction
 /// </summary>
 /// <param name="j"></param>
 /// <param name="i"></param>
 void ConsumeRelativeCellDown(int j, int i)
{
            if (_cells[j][i] == true)
            {
                // begin lane at the top of part
                if ((i == _topY))
                {
                    _dot1 = new Coords() { X = j, Y = i };
                    
                    _curLane.Dot1 = _dot1;
                    _laneLen = 1;
                    _state = true;
                }
                else if ((_state == false))
                {
                    // single dot at the bottom of 
                    if (i == _bottomY)
                    {
                        _dot1 = new Coords() { X = j, Y = i };
                        _curLane.Dot1 = _dot1;
                        _dot2 = new Coords() { X = j, Y = i };
                        _curLane.Dot2 = _dot2;
                        _curLane.Length = 1;
                        _curLane.Lowest = true;
                        _endLaneFlag = true;
                    }
                    // begin lane
                    else
                    {
                        _dot1 = new Coords() { X = j, Y = i };                        
                        _curLane.Dot1 = _dot1;
                        _laneLen = 1;
                        _state = true;
                    }
                }
                else if ((i == _bottomY))
                {
                    //   end of lane at the bottom
                    _dot2 = new Coords() { X = j, Y = i };
                    _curLane.Dot2 = _dot2;
                    _curLane.Length = ++_laneLen;
                    _curLane.Lowest = true;
                    _endLaneFlag = true;
                }  // in lane
                else
                {
                    _laneLen++;
                }
            }
            // end lane not an edge
            else if (_state == true)
            {
                _dot2 = new Coords() { X = j, Y = i - 1 };
                _curLane.Dot2 = _dot2;
                _curLane.Length = _laneLen;
                _state = false;
                _endLaneFlag = true;
            }
            if (_endLaneFlag == true)
            {
                _lines.Add(_curLane);
                _endLaneFlag = false;
            }

}

Method  ConsumeRelativeCellUp is used  for odd lines on the way from the bottom cell to  the top cell:

C#
void ConsumeRelativeCellUp(int j, int i)
{
            if (_cells[j][i] == true)
            {
                // begin lane at the bottom of part
                if ((i == _bottomY))
                {

                    _dot1 = new Coords { X = j, Y = i };
                    _curLane.Dot1 = _dot1;
                    _laneLen = 1;
                    _state = true;
                }
                else if ((_state == false))
                {
                    // single dot at the top of part
                    if (i == _topY)
                    {
                        _dot1 = new Coords { X = j, Y = i };
                        _curLane.Dot1 = _dot1;
                        _dot2 = new Coords { X = j, Y = i };
                        _curLane.Dot2 = _dot2;
                        _curLane.Length = 1;
                        _curLane.Lowest = true;
                        _endLaneFlag = true;
                    }
                    // begin lane
                    else
                    {
                        _dot1 = new Coords { X = j, Y = i };
                        _curLane.Dot1 = _dot1;
                        _laneLen = 1;
                        _state = true;
                    }
                }
                else if ((i == _topY))
                {
                    //   end of lane at the top
                    _dot2 = new Coords { X = j, Y = i };
                    _curLane.Dot2 = _dot2;
                    _curLane.Length = ++_laneLen;
                    _curLane.Lowest = true;
                    _endLaneFlag = true;
                }  // in lane
                else
                {
                    _laneLen++;
                }
            }
            // end lane not an edge
            else if (_state)
            {
                _dot2 = new Coords { X = j, Y = i + 1 };
                _curLane.Dot2 = _dot2;
                _curLane.Length = _laneLen;
                _state = false;
                _endLaneFlag = true;
            }
            if (_endLaneFlag)
            {
                _lines.Add(_curLane);
                _endLaneFlag = false;
            }

}

 In the following video the whole process of stitching is shown: QR-code stitching video

Class QrcodeDst

Class aggregates DstFile and QrCodeStitcher instances. DstFile instance is used to save stitches in DST file. Blocks of stitches are generated in QrCodeStitcher instance.

C#
public QrcodeDst()
{
     _dst = new DstFile();
     _stitchGen = new QrCodeStitcher();
}

Class has the following property setter:

C#
public QRCodeStitchInfo QrStitchInfo
{
   set { _stitchGen.Info = value; }
}

In class QrcodeDst is implemented method FillStreamWithDst(Stream stream) to save generated design of QR-code in  Tajima DST format.

Following code demonstrate how to use QrcodeDst instance to read text from a file and save it in DST format:

C#
var  qrcodeGen = new QrcodeDst(); 
using (var inputStreamReader = new StreamReader(fileName))
{
    var text = inputStreamReader.ReadToEnd();
    using (Stream outStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write))
    {
      if (qrcodeGen != null)
      {
          qrcodeGen.QrStitchInfo = new QRCodeStitchInfo {QrCodeText = text};
          qrcodeGen.FillStream(outStream);
      }
    }
}

To save stitches into an embroidery Tajima DST format there is a desription of Tajima DST file on a stage. Next paragraph describes it  shortly.

Tajima DST file format

Conversion from a list of stitches to DST file bytes is based on format description from here. In a file we have a sequence of stitches as comands of needle movements. Stiches are stored as offsets from previous stitch positon. For each stitch the type of comand is also stored. Types of stitch are:

  • Normal
  • Jump
  • Stop

DST file has a header. Stitch data starts at the 512 byte position with numeration from zero.

Stitch is coded with three bytes:

Bit no 7 6 5 4 3 2 1 0  
Byte 1 y+1 y-1 y+9 y-9 x-9 x+9 x-1 x+1  
Byte 2 y+3 y-3 y+27 y-27 x-27 x+27 x-3 x+3  
  Byte 3 jump stop y+81 y-81 x-81 x+81 always on always on 

 

DST file ends with three bytes: x00 00 F3

Following method encode stitch to bytes with type information: 

C#
byte[] encode_record(int x, int y, DstStitchType stitchType)
      {
          byte b0, b1, b2;
          b0 = b1 = b2 = 0;
          byte[] b = new byte[3];
          // following values imposible to convert >+121 or < -121.

          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)
          {
             throw;

          };
          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)
          {
             throw;

      };

          switch (stitchType)
          {
              case DstStitchType.NORMAL:
                  b2 += (byte)3;
                  break;
              case DstStitchType.END:
                  b2 = (byte)243;
                  b0 = b1 = (byte)0;
                  break;
              case DstStitchType.JUMP:
                  b2 += (byte)131;
                  break;
              case DstStitchType.STOP:
                  b2 += (byte)195;
                  break;
              default:
                  b2 += 3;
                  break;
          };
          b[0] = b0; b[1] = b1; b[2] = b2;
          return b;
      }

By this link  it is possible to generate QR-Code embroidery online:

Image 3

To see source code of generating QR-code  embroidery in .DST format follow the link here.

Related links

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Russian Federation Russian Federation
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionCreate simple stitches Pin
FikriK2-Aug-23 1:07
FikriK2-Aug-23 1:07 
GeneralMy vote of 1 Pin
RedDk22-Apr-15 7:31
RedDk22-Apr-15 7:31 
QuestionThought Pin
Nelek14-Apr-15 11:59
protectorNelek14-Apr-15 11:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.