Commit 46e375e4 authored by Eric Domke's avatar Eric Domke
Browse files

Bug Fixes

- Fixing path parsing algorithm to deal with nuanced arc cases and
hopefully improve performance
- Attempts at better memory management
- Working toward getting symbols to render correctly
parent 7c70bd11
...@@ -45,14 +45,14 @@ namespace Svg ...@@ -45,14 +45,14 @@ namespace Svg
public IList<System.Drawing.RectangleF> MeasureCharacters(ISvgRenderer renderer, string text) public IList<System.Drawing.RectangleF> MeasureCharacters(ISvgRenderer renderer, string text)
{ {
var result = new List<RectangleF>(); var result = new List<RectangleF>();
GetPath(renderer, text, result, false); using (var path = GetPath(renderer, text, result, false)) { }
return result; return result;
} }
public System.Drawing.SizeF MeasureString(ISvgRenderer renderer, string text) public System.Drawing.SizeF MeasureString(ISvgRenderer renderer, string text)
{ {
var result = new List<RectangleF>(); var result = new List<RectangleF>();
GetPath(renderer, text, result, true); using (var path = GetPath(renderer, text, result, true)) { }
var nonEmpty = result.Where(r => r != RectangleF.Empty); var nonEmpty = result.Where(r => r != RectangleF.Empty);
if (!nonEmpty.Any()) return SizeF.Empty; if (!nonEmpty.Any()) return SizeF.Empty;
return new SizeF(nonEmpty.Last().Right - nonEmpty.First().Left, Ascent(renderer)); return new SizeF(nonEmpty.Last().Right - nonEmpty.First().Left, Ascent(renderer));
...@@ -63,10 +63,12 @@ namespace Svg ...@@ -63,10 +63,12 @@ namespace Svg
var textPath = GetPath(renderer, text, null, false); var textPath = GetPath(renderer, text, null, false);
if (textPath.PointCount > 0) if (textPath.PointCount > 0)
{ {
var translate = new Matrix(); using (var translate = new Matrix())
translate.Translate(location.X, location.Y); {
textPath.Transform(translate); translate.Translate(location.X, location.Y);
path.AddPath(textPath, false); textPath.Transform(translate);
path.AddPath(textPath, false);
}
} }
} }
...@@ -99,6 +101,7 @@ namespace Svg ...@@ -99,6 +101,7 @@ namespace Svg
scaleMatrix.Scale(_emScale, -1 * _emScale, MatrixOrder.Append); scaleMatrix.Scale(_emScale, -1 * _emScale, MatrixOrder.Append);
scaleMatrix.Translate(xPos, ascent, MatrixOrder.Append); scaleMatrix.Translate(xPos, ascent, MatrixOrder.Append);
path.Transform(scaleMatrix); path.Transform(scaleMatrix);
scaleMatrix.Dispose();
bounds = path.GetBounds(); bounds = path.GetBounds();
if (ranges != null) if (ranges != null)
...@@ -126,5 +129,11 @@ namespace Svg ...@@ -126,5 +129,11 @@ namespace Svg
if (_glyphs == null) _glyphs = _font.Descendants().OfType<SvgGlyph>().ToDictionary(g => g.Unicode ?? g.GlyphName ?? g.ID); if (_glyphs == null) _glyphs = _font.Descendants().OfType<SvgGlyph>().ToDictionary(g => g.Unicode ?? g.GlyphName ?? g.ID);
if (_kerning == null) _kerning = _font.Descendants().OfType<SvgKern>().ToDictionary(k => k.Glyph1 + "|" + k.Glyph2); if (_kerning == null) _kerning = _font.Descendants().OfType<SvgKern>().ToDictionary(k => k.Glyph1 + "|" + k.Glyph2);
} }
public void Dispose()
{
_glyphs = null;
_kerning = null;
}
} }
} }
...@@ -376,11 +376,13 @@ namespace Svg ...@@ -376,11 +376,13 @@ namespace Svg
} }
else else
{ {
var matrix = new Matrix(); using (var matrix = new Matrix())
matrix.Translate(-1 * bounds.X, 0, MatrixOrder.Append); {
matrix.Scale(specLength / actLength, 1, MatrixOrder.Append); matrix.Translate(-1 * bounds.X, 0, MatrixOrder.Append);
matrix.Translate(bounds.X, 0, MatrixOrder.Append); matrix.Scale(specLength / actLength, 1, MatrixOrder.Append);
path.Transform(matrix); matrix.Translate(bounds.X, 0, MatrixOrder.Append);
path.Transform(matrix);
}
} }
} }
} }
...@@ -575,186 +577,188 @@ namespace Svg ...@@ -575,186 +577,188 @@ namespace Svg
// Get any defined anchors // Get any defined anchors
var xAnchors = GetValues(value.Length, e => e._x, UnitRenderingType.HorizontalOffset); var xAnchors = GetValues(value.Length, e => e._x, UnitRenderingType.HorizontalOffset);
var yAnchors = GetValues(value.Length, e => e._y, UnitRenderingType.VerticalOffset); var yAnchors = GetValues(value.Length, e => e._y, UnitRenderingType.VerticalOffset);
var font = this.Element.GetFont(this.Renderer); using (var font = this.Element.GetFont(this.Renderer))
var fontBaselineHeight = this.Renderer.FontBaselineOffset(font);
PathStatistics pathStats = null;
var pathScale = 1.0;
if (BaselinePath != null)
{ {
pathStats = new PathStatistics(BaselinePath.PathData); var fontBaselineHeight = font.Ascent(this.Renderer);
if (_authorPathLength > 0) pathScale = _authorPathLength / pathStats.TotalLength; PathStatistics pathStats = null;
} var pathScale = 1.0;
if (BaselinePath != null)
// Get all of the offsets (explicit and defined by spacing)
IList<float> xOffsets;
IList<float> yOffsets;
IList<float> rotations;
float baselineShift = 0.0f;
try
{
this.Renderer.SetBoundable(new FontBoundable(font, (float)(pathStats == null ? 1 : pathStats.TotalLength)));
xOffsets = GetValues(value.Length, e => e._dx, UnitRenderingType.Horizontal);
yOffsets = GetValues(value.Length, e => e._dy, UnitRenderingType.Vertical);
if (StartOffsetAdjust != 0.0f)
{ {
if (xOffsets.Count < 1) pathStats = new PathStatistics(BaselinePath.PathData);
{ if (_authorPathLength > 0) pathScale = _authorPathLength / pathStats.TotalLength;
xOffsets.Add(StartOffsetAdjust);
}
else
{
xOffsets[0] += StartOffsetAdjust;
}
} }
if (this.Element.LetterSpacing.Value != 0.0f || this.Element.WordSpacing.Value != 0.0f || this.LetterSpacingAdjust != 0.0f) // Get all of the offsets (explicit and defined by spacing)
IList<float> xOffsets;
IList<float> yOffsets;
IList<float> rotations;
float baselineShift = 0.0f;
try
{ {
var spacing = this.Element.LetterSpacing.ToDeviceValue(this.Renderer, UnitRenderingType.Horizontal, this.Element) + this.LetterSpacingAdjust; this.Renderer.SetBoundable(new FontBoundable(font, (float)(pathStats == null ? 1 : pathStats.TotalLength)));
var wordSpacing = this.Element.WordSpacing.ToDeviceValue(this.Renderer, UnitRenderingType.Horizontal, this.Element); xOffsets = GetValues(value.Length, e => e._dx, UnitRenderingType.Horizontal);
if (this.Parent == null && this.NumChars == 0 && xOffsets.Count < 1) xOffsets.Add(0); yOffsets = GetValues(value.Length, e => e._dy, UnitRenderingType.Vertical);
for (int i = (this.Parent == null && this.NumChars == 0 ? 1 : 0); i < value.Length; i++) if (StartOffsetAdjust != 0.0f)
{ {
if (i >= xOffsets.Count) if (xOffsets.Count < 1)
{ {
xOffsets.Add(spacing + (char.IsWhiteSpace(value[i]) ? wordSpacing : 0)); xOffsets.Add(StartOffsetAdjust);
} }
else else
{ {
xOffsets[i] += spacing + (char.IsWhiteSpace(value[i]) ? wordSpacing : 0); xOffsets[0] += StartOffsetAdjust;
} }
} }
}
rotations = GetValues(value.Length, e => e._rotations); if (this.Element.LetterSpacing.Value != 0.0f || this.Element.WordSpacing.Value != 0.0f || this.LetterSpacingAdjust != 0.0f)
{
var spacing = this.Element.LetterSpacing.ToDeviceValue(this.Renderer, UnitRenderingType.Horizontal, this.Element) + this.LetterSpacingAdjust;
var wordSpacing = this.Element.WordSpacing.ToDeviceValue(this.Renderer, UnitRenderingType.Horizontal, this.Element);
if (this.Parent == null && this.NumChars == 0 && xOffsets.Count < 1) xOffsets.Add(0);
for (int i = (this.Parent == null && this.NumChars == 0 ? 1 : 0); i < value.Length; i++)
{
if (i >= xOffsets.Count)
{
xOffsets.Add(spacing + (char.IsWhiteSpace(value[i]) ? wordSpacing : 0));
}
else
{
xOffsets[i] += spacing + (char.IsWhiteSpace(value[i]) ? wordSpacing : 0);
}
}
}
// Calculate Y-offset due to baseline shift. Don't inherit the value so that it is not accumulated multiple times. rotations = GetValues(value.Length, e => e._rotations);
var baselineShiftText = this.Element.Attributes.GetAttribute<string>("baseline-shift");
switch (baselineShiftText) // Calculate Y-offset due to baseline shift. Don't inherit the value so that it is not accumulated multiple times.
{ var baselineShiftText = this.Element.Attributes.GetAttribute<string>("baseline-shift");
case null:
case "":
case "baseline":
case "inherit":
// do nothing
break;
case "sub":
baselineShift = new SvgUnit(SvgUnitType.Ex, 1).ToDeviceValue(this.Renderer, UnitRenderingType.Vertical, this.Element);
break;
case "super":
baselineShift = -1 * new SvgUnit(SvgUnitType.Ex, 1).ToDeviceValue(this.Renderer, UnitRenderingType.Vertical, this.Element);
break;
default:
var convert = new SvgUnitConverter();
var shiftUnit = (SvgUnit)convert.ConvertFromInvariantString(baselineShiftText);
baselineShift = -1 * shiftUnit.ToDeviceValue(this.Renderer, UnitRenderingType.Vertical, this.Element);
break;
}
if (baselineShift != 0.0f) switch (baselineShiftText)
{
if (yOffsets.Any())
{ {
yOffsets[0] += baselineShift; case null:
case "":
case "baseline":
case "inherit":
// do nothing
break;
case "sub":
baselineShift = new SvgUnit(SvgUnitType.Ex, 1).ToDeviceValue(this.Renderer, UnitRenderingType.Vertical, this.Element);
break;
case "super":
baselineShift = -1 * new SvgUnit(SvgUnitType.Ex, 1).ToDeviceValue(this.Renderer, UnitRenderingType.Vertical, this.Element);
break;
default:
var convert = new SvgUnitConverter();
var shiftUnit = (SvgUnit)convert.ConvertFromInvariantString(baselineShiftText);
baselineShift = -1 * shiftUnit.ToDeviceValue(this.Renderer, UnitRenderingType.Vertical, this.Element);
break;
} }
else
if (baselineShift != 0.0f)
{ {
yOffsets.Add(baselineShift); if (yOffsets.Any())
{
yOffsets[0] += baselineShift;
}
else
{
yOffsets.Add(baselineShift);
}
} }
} }
} finally
finally {
{ this.Renderer.PopBoundable();
this.Renderer.PopBoundable(); }
}
// NOTE: Assuming a horizontal left-to-right font // NOTE: Assuming a horizontal left-to-right font
// Render absolutely positioned items in the horizontal direction // Render absolutely positioned items in the horizontal direction
var yPos = Current.Y; var yPos = Current.Y;
for (int i = 0; i < xAnchors.Count - 1; i++) for (int i = 0; i < xAnchors.Count - 1; i++)
{ {
FlushPath(); FlushPath();
_xAnchor = xAnchors[i] + (xOffsets.Count > i ? xOffsets[i] : 0); _xAnchor = xAnchors[i] + (xOffsets.Count > i ? xOffsets[i] : 0);
EnsurePath(); EnsurePath();
yPos = (yAnchors.Count > i ? yAnchors[i] : yPos) + (yOffsets.Count > i ? yOffsets[i] : 0); yPos = (yAnchors.Count > i ? yAnchors[i] : yPos) + (yOffsets.Count > i ? yOffsets[i] : 0);
DrawStringOnCurrPath(value[i].ToString(), font, new PointF(_xAnchor, yPos), DrawStringOnCurrPath(value[i].ToString(), font, new PointF(_xAnchor, yPos),
fontBaselineHeight, (rotations.Count > i ? rotations[i] : rotations.LastOrDefault())); fontBaselineHeight, (rotations.Count > i ? rotations[i] : rotations.LastOrDefault()));
} }
// Render any remaining characters // Render any remaining characters
var renderChar = 0; var renderChar = 0;
var xPos = this.Current.X; var xPos = this.Current.X;
if (xAnchors.Any()) if (xAnchors.Any())
{ {
FlushPath(); FlushPath();
renderChar = xAnchors.Count - 1; renderChar = xAnchors.Count - 1;
xPos = xAnchors.Last(); xPos = xAnchors.Last();
_xAnchor = xPos; _xAnchor = xPos;
} }
EnsurePath(); EnsurePath();
// Render individual characters as necessary // Render individual characters as necessary
var lastIndividualChar = renderChar + Math.Max(Math.Max(Math.Max(Math.Max(xOffsets.Count, yOffsets.Count), yAnchors.Count), rotations.Count) - renderChar - 1, 0); var lastIndividualChar = renderChar + Math.Max(Math.Max(Math.Max(Math.Max(xOffsets.Count, yOffsets.Count), yAnchors.Count), rotations.Count) - renderChar - 1, 0);
if (rotations.LastOrDefault() != 0.0f || pathStats != null) lastIndividualChar = value.Length; if (rotations.LastOrDefault() != 0.0f || pathStats != null) lastIndividualChar = value.Length;
if (lastIndividualChar > renderChar) if (lastIndividualChar > renderChar)
{
var charBounds = this.Renderer.MeasureCharacters(value.Substring(renderChar, Math.Min(lastIndividualChar + 1, value.Length) - renderChar), font);
PointF pathPoint;
float rotation;
float halfWidth;
for (int i = renderChar; i < lastIndividualChar; i++)
{ {
xPos += (float)pathScale * (xOffsets.Count > i ? xOffsets[i] : 0) + (charBounds[i - renderChar].X - (i == renderChar ? 0 : charBounds[i - renderChar - 1].X)); var charBounds = font.MeasureCharacters(this.Renderer, value.Substring(renderChar, Math.Min(lastIndividualChar + 1, value.Length) - renderChar));
yPos = (yAnchors.Count > i ? yAnchors[i] : yPos) + (yOffsets.Count > i ? yOffsets[i] : 0); PointF pathPoint;
if (pathStats == null) float rotation;
float halfWidth;
for (int i = renderChar; i < lastIndividualChar; i++)
{ {
DrawStringOnCurrPath(value[i].ToString(), font, new PointF(xPos, yPos), xPos += (float)pathScale * (xOffsets.Count > i ? xOffsets[i] : 0) + (charBounds[i - renderChar].X - (i == renderChar ? 0 : charBounds[i - renderChar - 1].X));
fontBaselineHeight, (rotations.Count > i ? rotations[i] : rotations.LastOrDefault())); yPos = (yAnchors.Count > i ? yAnchors[i] : yPos) + (yOffsets.Count > i ? yOffsets[i] : 0);
if (pathStats == null)
{
DrawStringOnCurrPath(value[i].ToString(), font, new PointF(xPos, yPos),
fontBaselineHeight, (rotations.Count > i ? rotations[i] : rotations.LastOrDefault()));
}
else
{
xPos = Math.Max(xPos, 0);
halfWidth = charBounds[i - renderChar].Width / 2;
if (pathStats.OffsetOnPath(xPos + halfWidth))
{
pathStats.LocationAngleAtOffset(xPos + halfWidth, out pathPoint, out rotation);
pathPoint = new PointF((float)(pathPoint.X - halfWidth * Math.Cos(rotation * Math.PI / 180) - (float)pathScale * yPos * Math.Sin(rotation * Math.PI / 180)),
(float)(pathPoint.Y - halfWidth * Math.Sin(rotation * Math.PI / 180) + (float)pathScale * yPos * Math.Cos(rotation * Math.PI / 180)));
DrawStringOnCurrPath(value[i].ToString(), font, pathPoint, fontBaselineHeight, rotation);
}
}
}
// Add the kerning to the next character
if (lastIndividualChar < value.Length)
{
xPos += charBounds[charBounds.Count - 1].X - charBounds[charBounds.Count - 2].X;
} }
else else
{ {
xPos = Math.Max(xPos, 0); xPos += charBounds.Last().Width;
halfWidth = charBounds[i-renderChar].Width / 2;
if (pathStats.OffsetOnPath(xPos + halfWidth))
{
pathStats.LocationAngleAtOffset(xPos + halfWidth, out pathPoint, out rotation);
pathPoint = new PointF((float)(pathPoint.X - halfWidth * Math.Cos(rotation * Math.PI / 180) - (float)pathScale * yPos * Math.Sin(rotation * Math.PI / 180)),
(float)(pathPoint.Y - halfWidth * Math.Sin(rotation * Math.PI / 180) + (float)pathScale * yPos * Math.Cos(rotation * Math.PI / 180)));
DrawStringOnCurrPath(value[i].ToString(), font, pathPoint, fontBaselineHeight, rotation);
}
} }
} }
// Add the kerning to the next character // Render the string normally
if (lastIndividualChar < value.Length) if (lastIndividualChar < value.Length)
{
xPos += charBounds[charBounds.Count - 1].X - charBounds[charBounds.Count - 2].X;
}
else
{ {
xPos += charBounds.Last().Width; xPos += (xOffsets.Count > lastIndividualChar ? xOffsets[lastIndividualChar] : 0);
yPos = (yAnchors.Count > lastIndividualChar ? yAnchors[lastIndividualChar] : yPos) +
(yOffsets.Count > lastIndividualChar ? yOffsets[lastIndividualChar] : 0);
DrawStringOnCurrPath(value.Substring(lastIndividualChar), font, new PointF(xPos, yPos),
fontBaselineHeight, rotations.LastOrDefault());
var bounds = font.MeasureString(this.Renderer, value.Substring(lastIndividualChar));
xPos += bounds.Width;
} }
}
// Render the string normally
if (lastIndividualChar < value.Length)
{
xPos += (xOffsets.Count > lastIndividualChar ? xOffsets[lastIndividualChar] : 0);
yPos = (yAnchors.Count > lastIndividualChar ? yAnchors[lastIndividualChar] : yPos) +
(yOffsets.Count > lastIndividualChar ? yOffsets[lastIndividualChar] : 0);
DrawStringOnCurrPath(value.Substring(lastIndividualChar), font, new PointF(xPos, yPos),
fontBaselineHeight, rotations.LastOrDefault());
var bounds = this.Renderer.MeasureString(value.Substring(lastIndividualChar), font);
xPos += bounds.Width;
}
NumChars += value.Length; NumChars += value.Length;
// Undo any baseline shift. This is not persisted, unlike normal vertical offsets. // Undo any baseline shift. This is not persisted, unlike normal vertical offsets.
this.Current = new PointF(xPos, yPos - baselineShift); this.Current = new PointF(xPos, yPos - baselineShift);
}
} }
private void DrawStringOnCurrPath(string value, IFontDefn font, PointF location, float fontBaselineHeight, float rotation) private void DrawStringOnCurrPath(string value, IFontDefn font, PointF location, float fontBaselineHeight, float rotation)
...@@ -764,12 +768,14 @@ namespace Svg ...@@ -764,12 +768,14 @@ namespace Svg
font.AddStringToPath(this.Renderer, drawPath, value, new PointF(location.X, location.Y - fontBaselineHeight)); font.AddStringToPath(this.Renderer, drawPath, value, new PointF(location.X, location.Y - fontBaselineHeight));
if (rotation != 0.0f && drawPath.PointCount > 0) if (rotation != 0.0f && drawPath.PointCount > 0)
{ {
var matrix = new Matrix(); using (var matrix = new Matrix())
matrix.Translate(-1 * location.X, -1 * location.Y, MatrixOrder.Append); {
matrix.Rotate(rotation, MatrixOrder.Append); matrix.Translate(-1 * location.X, -1 * location.Y, MatrixOrder.Append);
matrix.Translate(location.X, location.Y, MatrixOrder.Append); matrix.Rotate(rotation, MatrixOrder.Append);
drawPath.Transform(matrix); matrix.Translate(location.X, location.Y, MatrixOrder.Append);
_currPath.AddPath(drawPath, false); drawPath.Transform(matrix);
_currPath.AddPath(drawPath, false);
}
} }
} }
...@@ -830,11 +836,13 @@ namespace Svg ...@@ -830,11 +836,13 @@ namespace Svg
if (xOffset != 0) if (xOffset != 0)
{ {
var matrix = new Matrix(); using (var matrix = new Matrix())
matrix.Translate(xOffset, 0);
foreach (var path in _anchoredPaths)
{ {
path.Transform(matrix); matrix.Translate(xOffset, 0);
foreach (var path in _anchoredPaths)
{
path.Transform(matrix);
}
} }
} }
......
...@@ -27,7 +27,7 @@ namespace Svg.Transforms ...@@ -27,7 +27,7 @@ namespace Svg.Transforms
{ {
get get
{ {
System.Drawing.Drawing2D.Matrix matrix = new System.Drawing.Drawing2D.Matrix(); var matrix = new System.Drawing.Drawing2D.Matrix();
matrix.Scale(this.X, this.Y); matrix.Scale(this.X, this.Y);
return matrix; return matrix;
} }
......
...@@ -32,6 +32,7 @@ namespace SvgW3CTestRunner ...@@ -32,6 +32,7 @@ namespace SvgW3CTestRunner
var fileName = lstFiles.SelectedItem.ToString(); var fileName = lstFiles.SelectedItem.ToString();
try try
{ {
Debug.Print(fileName);
var doc = SvgDocument.Open(_svgBasePath + fileName); var doc = SvgDocument.Open(_svgBasePath + fileName);
if (fileName.StartsWith("__")) if (fileName.StartsWith("__"))
{ {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment