Contents
This article describes a few steps to get embroided glyph. You can choose any glyph from any true type font and embroid it. Firstly, we need to extract glyph geometry from a true type font file. The second step is to produce zigzags on the glyph lines. Produced glyph zigzags look like the following:
After converting glyph zigzags to an embroidery format, such as DST, it could be embroided by embroidery machine on clothes.
This article is based on an idea of the ability to embroid any glyph of any true type font without any commercial software.
First of all, the .NET library System.Windows.Media
should be referenced in the project, and the following line should be added in the namespace
declaration of our file:
using System.Windows.Media;
True type font family file usually contains a single font family. Font family has a few typefaces.
According to MSDN, typeface represents a combination of FontFamily, FontWeight, FontStyle, and FontStretch.
Arial true type font, which is usually installed on Windows platform has nine typefaces:
Figure 1 - True type font variation
To get font families from a font file, we use method GetFontFamilies
of a Fonts
class from System.Windows.Media
namespace as follows:
ICollection<FontFamily> families = Fonts.GetFontFamilies(fontPath);
If we are in need of iterating through typefaces of a font family, we use a GetTypefaces
class of FontFamily
class from the same namespace
:
ICollection<Typeface> typefaces = fontFamily.GetTypefaces();
Instance of Typeface
class allows to create GlyphTypeface
instance:
GlyphTypeface glyph;
GlyphTypeface glyphTypeface = typeface.TryGetGlyphTypeface(out glyph) ? glyph : null;
When we have got an instance of GlyphTypeface
class, we can get the character map of a typeface
from its property CharacterToGlyphMap
. This property has type of IDictionary<int, ushort>
with unicode as key and glyph index as value.
To work with glyph
geometry, the class GlyphTypeface
is used. Glyph
geometry is obtained by its index:
Geometry geom = glyphTypeface.GetGlyphOutline(glyphIndex, renderingEmSize, hintingEmSize);
Although more often, we would like to get glyph
by its unicode number. So we get glyph
index from character map by its unicode number and then obtain glyph
geometry. In the method GetGlyphOutline
, three parameters are passed. The third parameter hintingEmSize
is discussed in detail here.
In the method GetGlyphPoints()
, an enumeration of PointCollection
is built on PathGeometry
instance, which is passed in glyphGeom
parameter. PathGeometry
instance of a glyph is obtained by the method GetOutlinedPathGeometry()
of Geometry
instance. Each collection of points in the enumeration represents a sequence of points for each figure in PathGeometry
. Which will be zigzagified in the next step.
public IEnumerable<PointCollection> GetGlyphPoints(PathGeometry glyphGeom)
{
var result = new List<PointCollection>();
PointCollection curColl = new PointCollection();
foreach (PathFigure figure in glyphGeom.Figures)
{
PointCollection flattenedShape = PathFigureFlattening.GetPathFigureFlattenedPoints(figure);
result.Add(flattenedShape);
}
return result;
}
Now we have sequences of points for specified glyph, and it can be converted to SVG
picture or just JSON
object. Although let's apply zigzagifying effect on this contour of the glyph. To do that, we need to split each intercept into small ones to provide zigzagifying by them.
In the following code of the GetPointsOnLine()
method, an axis direction is chosen by the longest axis delta of an itercept. It means, if delta X
is greater than delta Y, then the pass through X
axis and calculating delta Y
for each step by fixed increment value of X
is chosen. In case when delta Y
of intercept is greater than delta X
, it passes through Y
axis with fixed pitch. It allows to avoid deviation. The length
variable is calculated by the Pythagorean theorem formula:
The square of hypothenuse is equals to sum of cathetus squares.
By the length
and the step
value, the quantity of points on intercept is calculated. For short intercepts with less than six points, it sets the quantity of points to seven. It allows to build zigzag and leaves enough points to create curved zigzags with neighbor intercepts. It also checks for horizontal and vertical lines, there is a block of code to calculate point coordinates in this case. The method returns points as three list of coordinates: first two points, mid points, and last two points. Start point and end point are not included in the result. Mid points will be used for simple zigzagifying by finding perpendicular points to mid points of small interceptors. First two and last two will be used for finding points on curve to neighbor intercepts by Casteljau algorithm of finding points on curve, and then resolved sequence of points will be used to build zigzagified curve.
private List<List<DoubleVertice>> GetPointsOnLine(DoubleVertice start, DoubleVertice end, double step)
{
var result = new List<List<DoubleVertice>>();
var firstTwo = new List<DoubleVertice>();
var lastTwo = new List<DoubleVertice>();
var mediPoints = new List<DoubleVertice>();
double cathetusX;
double cathetusY;
double dx;
double dy;
GetDxDy(start, end, out cathetusX, out cathetusY);
double y0 = start.Y;
double y1 = end.Y;
double x0 = (double)start.X;
double x1 = (double)end.X;
double k;
bool shortLine = false;
double deltaX = 0.0;
double deltaY = 0.0;
int length = (int)Math.Sqrt(cathetusX * cathetusX + cathetusY * cathetusY);
int n = (int) Math.Sqrt((length*length)/(step*step));
if (ShortLineIterceptsCount - 1 > n)
{
n = ShortLineIterceptsCount;
shortLine = true;
dx = dx/ShortLineIterceptsCount;
dy = dy/ShortLineIterceptsCount;
}
else
{
var deltaVector = Get_qrtnDeltaVector(x0, y0, x1, y1, step);
dx = Math.Abs(deltaVector.X);
dy = Math.Abs(deltaVector.Y);
}
if (dy == 0 && dx == 0)
{
result.Add(new List<DoubleVertice> {start, end});
return result;
}
if (dy == 0 || dx == 0)
{
if (dy == 0)
{
deltaY = 0;
if (shortLine) step = dx;
if (x1 > x0) deltaX = step;
else deltaX = -step;
}
else if (dx == 0)
{
deltaX = 0;
if (shortLine) step = dy;
if (y1 > y0) deltaY = step;
else deltaY = -step;
}
}
else
{
if ((x0 < x1) && (y0 > y1))
{
deltaX = dx;
deltaY = -dy;
}
else if ((x0 > x1) && (y0 > y1))
{
deltaX = -dx;
deltaY = -dy;
}
else if ((x0 > x1) && (y0 < y1))
{
deltaX = -dx;
deltaY = dy;
}
else
{
deltaX = dx;
deltaY = dy;
}
}
double dX = (deltaX);
double dY = (deltaY);
deltaX = 0;
deltaY = 0;
double curX = x0, curY = y0;
if ((dx >= dy) && (dx != 0))
{
k = dY/dX;
for (var i = 0; (i < 2) && (dX != 0); i++)
{
deltaX += dX;
deltaY = (deltaX*k);
curX = x0 + deltaX;
curY = y0 + deltaY;
firstTwo.Add(new DoubleVertice {X = curX, Y = curY});
}
for (var i = 4; (i < n) && (dX != 0); i++)
{
curX = x0 + deltaX;
curY = y0 + deltaY;
mediPoints.Add(new DoubleVertice {X = curX, Y = curY});
deltaX += dX;
deltaY = (deltaX*k);
}
for (var i = 0; (i < 2) && (dX != 0); i++)
{
curX = x0 + deltaX;
curY = y0 + deltaY;
lastTwo.Add(new DoubleVertice {X = curX, Y = curY});
deltaX += dX;
deltaY = (deltaX*k);
}
}
else if (dy != 0)
{
k = dX/dY;
for (int i = 0; (i < 2) && (dY != 0); i++)
{
deltaX = (deltaY*k);
deltaY += dY;
curY = y0 + deltaY;
curX = x0 + deltaX;
firstTwo.Add(new DoubleVertice {X = curX, Y = curY});
}
for (int i = 4; (i < n) && (dY != 0); i++)
{
curY = y0 + deltaY;
curX = x0 + deltaX;
mediPoints.Add(new DoubleVertice {X = curX, Y = curY});
deltaX = (deltaY*k);
deltaY += dY;
}
for (int i = 0; (i < 2) && (dY != 0); i++)
{
curY = y0 + deltaY;
curX = x0 + deltaX;
lastTwo.Add(new DoubleVertice {X = curX, Y = curY});
deltaX = (deltaY*k);
deltaY += dY;
}
}
result.Add(firstTwo);
result.Add(mediPoints);
result.Add(lastTwo);
if (mediPoints.Count == 0)
{
Trace.WriteLine(string.Format("{0} {1}", start, end));
}
return result;
}
In the following picture, we have:
- neighbour step points of intercept A B
- mid point of intercept M
- points of zigzag to be found C D
To find points of zigzags, the answer from stackoverflow was used. The solution is based on formula of equilaterial triangle height calculation. So coordinates of points C and D at figure 2 are calculated in the method GetMediPerpendicularPoint(
)
. Green dash line is our zigzag we are going to draw. Let's assume C is left point of zigzag. For point C, method is called with point A and M for point0
and point1
respectively. Parameter opposite
in this case is false
. So point D is right point of zigzag and opposite
is true. We pass points M and D as point0
and point1
for second zigzag point (D). To get all zigzags for whole intercept, we go through all mid points we get by GetPointsOnLine()
method.
Figure 2 - Perpendicular points detection
The normal of an equilateral triangle is the square root of three-quarters, so logic in the following method is based on this statement.
DoubleVertice GetMediPerpendicularPoint
(DoubleVertice point0, DoubleVertice point1, double distance, bool opposite)
{
double x, y, lx, ly, mx, my;
x = point1.X;
y = point1.Y;
lx = point0.X;
ly = point0.Y;
LineDirection direction;
double xPerpOffset = 0;
double yPerpOffset = 0;
var dx = x - lx;
var dy = y - ly;
mx = (lx + x) / 2;
my = (ly + y) / 2;
if (dx != 0.0 && dy != 0.0)
{
var scale = Math.Sqrt(0.75);
var dX = scale * (y - ly);
var dY = -scale * (x - lx);
var length = Math.Sqrt(dX * dX + dY * dY);
var xnorm = Math.Abs(dX / length);
var ynorm = Math.Abs(dY / length);
xPerpOffset = Math.Abs(xnorm * distance);
yPerpOffset = Math.Abs(ynorm * distance);
if ((x > lx) && (y > ly)) direction = LineDirection.UpRight;
else if ((x < lx) && (y < ly)) direction = LineDirection.DownLeft;
else if ((x > lx) && (y < ly)) direction = LineDirection.DownRight;
else direction = LineDirection.UpLeft;
switch (direction)
{
case LineDirection.UpRight:
if (opposite) yPerpOffset *= -1;
else xPerpOffset *= -1;
break;
case LineDirection.DownRight:
if (opposite)
{
yPerpOffset *= -1;
xPerpOffset *= -1;
}
break;
case LineDirection.DownLeft:
if (opposite) xPerpOffset *= -1;
else yPerpOffset *= -1;
break;
case LineDirection.UpLeft:
if (!opposite)
{
yPerpOffset *= -1;
xPerpOffset *= -1;
}
break;
}
}
else if (dx == 0.0)
{
yPerpOffset = 0.0;
if (ly < y)
{
xPerpOffset = distance * (opposite ? -1 : 1);
}
else
{
xPerpOffset = distance * (opposite ? 1 : -1);
}
}
else if (dy == 0.0)
{
xPerpOffset = 0.0;
if (lx < x)
{
yPerpOffset = distance * (opposite ? 1 : -1);
}
else
{
yPerpOffset = distance * (opposite ? -1 : 1);
}
}
return new DoubleVertice { X = mx + xPerpOffset, Y = my + yPerpOffset };
}
To get well rounded turns between intercepts, we add more mid points between second to last points of intercepts. Points are added by Casteljau algorithm. This is not the best way for this case, there are a few other ways, although for simple shapes, it works well. Interpretation of Casteljau algorithm adopted from a few CodeProject articles:
#region Casteljau DRAW METHOD
private List<DoubleVertice> casteljauPoints;
private List<DoubleVertice> drawCasteljau(List<DoubleVertice> list, double step)
{
casteljauPoints = list;
var result = new List<DoubleVertice>();
for (double t = 0; t <= 1; t += step)
{
DoubleVertice tmp = getCasteljauDoubleVertice(list.Count - 1, 0, t);
result.Add(tmp);
}
casteljauPoints.Clear();
return result;
}
private DoubleVertice getCasteljauDoubleVertice(int r, int i, double t)
{
if (r == 0) return casteljauPoints[i];
DoubleVertice p1 = getCasteljauDoubleVertice(r - 1, i, t);
DoubleVertice p2 = getCasteljauDoubleVertice(r - 1, i + 1, t);
return new DoubleVertice { X = ((1 - t) * p1.X + t * p2.X), Y = ((1 - t) * p1.Y + t * p2.Y) };
}
private IEnumerable<DoubleVertice> GetZigzagifiedTurn
(IEnumerable<DoubleVertice> vertices, double zigzagWidth, double step = 0.2)
{
var list = drawCasteljau(vertices.ToList(), step);
int pointCount = list.Count;
var result = new List<DoubleVertice>();
DoubleVertice prevVertex = list[0];
for (int i = 1; i < pointCount; i++)
{
var curVertex = list[i];
result.AddRange(GetZigzaggedBetweenTwoPoint(prevVertex, curVertex, zigzagWidth));
prevVertex = curVertex;
}
return result;
}
#endregion
How to convert sequence of points to Tajima DST format covered in an article.
Zigzagified glyph can be also converted to SVG with console application.
Now we have methods to get a sequence of points of any glyph from true type font file and can create zigzags on its lines. As was said, this way is not the best one. For more accurate drawing, there are another algorithms, which I did not find.
In the following figure is pictured zigzagified glyph in SVG format:
Figure 3 - One of webdings true type font glyphs.
Live example of glyph zigzagifying is here
You can download source code from here.
Points of Interest
I would like slow drawing.
History
- 12th January, 2016: Initial version