Closed franzdevel closed 5 years ago
Is the "c0-1.11.9-2 2-2" token expected to be decoded correctly?
Is it C 0 1.1 1.9 -2 2 -2
or C 0 1.11 .9 -2 2 -2
?
I think it is the second version c 0 1.11 .9 -2 2 -2
(with the small 'c' for relative curveto). In the standard at https://www.w3.org/TR/SVG/paths.html#PathData, the end of the section reads:
The processing of the BNF must consume as much of a given BNF production as possible, stopping at the point when a character is encountered which no longer satisfies the production. Thus, in the string "M 100-200", the first coordinate for the "moveto" consumes the characters "100" and stops upon encountering the minus sign because the minus sign cannot follow a digit in the production of a "coordinate". The result is that the first coordinate will be "100" and the second coordinate will be "-200".
Similarly, for the string "M 0.6.5", the first coordinate of the "moveto" consumes the characters "0.6" and stops upon encountering the second decimal point because the production of a "coordinate" only allows one decimal point. The result is that the first coordinate will be "0.6" and the second coordinate will be ".5".
In our example, decoding the first coordinate in -1.11.9
cannot stop at -1.1
because -1.11
is still a valid number. So we take -1.11
as the first coordinate and .9
as the second one.
I'm working on a corresponding fix for SvgReader.ReadPath().
Here comes the fix for SvgReader.ReadPath().
(this version also includes the fix for #75)
Please check solution for overall correctness.
static Regex pathRegex = new Regex(@"[MLHVCSQTAZmlhvcsqtaz][^MLHVCSQTAZmlhvcsqtaz]*", RegexOptions.Singleline);
static Regex negativeNumberRe = new Regex("(?<=[0-9])-");
static Regex floatingPointRe = new Regex("(?<=\\.[0-9]+)\\.");
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 {
// ensure all coordinates are split by WSC
match = negativeNumberRe.Replace(match.Substring(1), " -");
match = floatingPointRe.Replace(match, " .");
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 ("Cannot decode path operation " + op + match);
}
previousPoint = p.Operations.Last().EndPoint;
}
}
m = m.NextMatch();
}
}
Is this fix in a current build?
No, I don't think I have the rights to actually submit code to the project.
Hello, thank you for fixing this. I have merged it in.
The following SVG image is not processed correctly by the SvgReader:
The issue is with one of the 'c' (curveto) commands in the path:
c0-1.11.9-2 2-2
. More concretely, the reader cannot correctly decode the block-1.11.9
which includes multiple coordinates not separated by wsp.