NGraphics is a cross platform library for rendering vector graphics on .NET. It provides a unified API for both immediate and retained mode graphics using high quality native renderers.
MIT License
705
stars
133
forks
source link
Bug in SvgReader for paths with 'm' command after 'z' command #75
The problem lies in the fact that an 'm' command (relative moveto) follows a 'z' command (close). The information about the previous point in the path is lost after the 'z' command and cannot be applied to the 'm' command.
Posting here a corrected version of SvgReader.ReadPath(). However, I am not an SVG expert so please check the proposed solution for overall correctness.
void ReadPath (Path p, string pathDescriptor)
{
Match m = pathRegex.Match(pathDescriptor);
Point previousPoint = new Point();
while(m.Success)
{
var match = m.Value.TrimStart ();
var op = match[0];
if (op == 'z' || op == 'Z') {
p.Close ();
} else {
// make sure negative numbers are split properly
match = negativeNumberRe.Replace(match.Substring(1), " -");
var args = match.Split(WSC, StringSplitOptions.RemoveEmptyEntries);
int index = 0;
while(index < args.Length)
{
if ((op == 'M' || op == 'm') && args.Length >= index+2) {
var point = new Point (ReadNumber (args [index]), ReadNumber (args [index+1]));
if (op == 'm')
point += previousPoint;
p.MoveTo (point);
index += 2;
} else if ((op == 'L' || op == 'l') && args.Length >= index+2) {
var point = new Point (ReadNumber (args [index]), ReadNumber (args [index+1]));
if (op == 'l')
point += previousPoint;
p.LineTo (point);
index += 2;
} else if ((op == 'C' || op == 'c') && args.Length >= index+6) {
var c1 = new Point (ReadNumber (args [index]), ReadNumber (args [index+1]));
var c2 = new Point (ReadNumber (args [index+2]), ReadNumber (args [index+3]));
var pt = new Point (ReadNumber (args [index+4]), ReadNumber (args [index+5]));
if (op == 'c')
{
c1 += previousPoint;
c2 += previousPoint;
pt += previousPoint;
}
p.CurveTo (c1, c2, pt);
index += 6;
} else if ((op == 'S' || op == 's') && args.Length >= index+4) {
var c = new Point (ReadNumber (args [index]), ReadNumber (args [index+1]));
var pt = new Point (ReadNumber (args [index+2]), ReadNumber (args [index+3]));
if (op == 's')
{
c += previousPoint;
pt += previousPoint;
}
p.ContinueCurveTo (c, pt);
index += 4;
} else if ((op == 'A' || op == 'a') && args.Length >= index+7) {
var r = new Size (ReadNumber (args [index]), ReadNumber (args [index+1]));
var laf = ReadNumber (args [index+3]) != 0;
var swf = ReadNumber (args [index+4]) != 0;
var pt = new Point (ReadNumber (args [index+5]), ReadNumber (args [index+6]));
if (op == 'a')
pt += previousPoint;
p.ArcTo (r, laf, swf, pt);
index += 7;
} else if ((op == 'V' || op == 'v') && args.Length >= index+1 && p.Operations.Count > 0) {
var previousX = previousPoint.X;
var y = ReadNumber(args[index]);
if (op == 'v')
y += previousPoint.Y;
var point = new Point(previousX, y);
p.LineTo(point);
index += 1;
} else if ((op == 'H' || op == 'h') && args.Length >= index+1 && p.Operations.Count > 0) {
var previousY = previousPoint.Y;
var x = ReadNumber(args[index]);
if (op == 'h')
x += previousPoint.X;
var point = new Point(x, previousY);
p.LineTo(point);
index += 1;
} else {
throw new NotSupportedException ("Path Operation " + op);
}
previousPoint = p.Operations.Last().EndPoint;
}
}
m = m.NextMatch();
}
}
The following material design icon is not processed correctly by SvgReader:
The problem lies in the fact that an 'm' command (relative moveto) follows a 'z' command (close). The information about the previous point in the path is lost after the 'z' command and cannot be applied to the 'm' command.
Posting here a corrected version of SvgReader.ReadPath(). However, I am not an SVG expert so please check the proposed solution for overall correctness.