Skip to content

Commit 9f54ed0

Browse files
Fix ArcTo parsing sanitation
1 parent 2fadcbc commit 9f54ed0

1 file changed

Lines changed: 110 additions & 74 deletions

File tree

  • src/ImageSharp.Drawing/Shapes

src/ImageSharp.Drawing/Shapes/Path.cs

Lines changed: 110 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -191,14 +191,14 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> svgPath, out IPath value)
191191
switch (op)
192192
{
193193
case 'M':
194-
svgPath = FindPoint(svgPath, out point1, relative, c);
194+
svgPath = FindPoint(svgPath, relative, c, out point1);
195195
builder.MoveTo(point1);
196196
previousOp = '\0';
197197
op = 'L';
198198
c = point1;
199199
break;
200200
case 'L':
201-
svgPath = FindPoint(svgPath, out point1, relative, c);
201+
svgPath = FindPoint(svgPath, relative, c, out point1);
202202
builder.LineTo(point1);
203203
c = point1;
204204
break;
@@ -223,16 +223,16 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> svgPath, out IPath value)
223223
c.Y = y;
224224
break;
225225
case 'C':
226-
svgPath = FindPoint(svgPath, out point1, relative, c);
227-
svgPath = FindPoint(svgPath, out point2, relative, c);
228-
svgPath = FindPoint(svgPath, out point3, relative, c);
226+
svgPath = FindPoint(svgPath, relative, c, out point1);
227+
svgPath = FindPoint(svgPath, relative, c, out point2);
228+
svgPath = FindPoint(svgPath, relative, c, out point3);
229229
builder.CubicBezierTo(point1, point2, point3);
230230
lastc = point2;
231231
c = point3;
232232
break;
233233
case 'S':
234-
svgPath = FindPoint(svgPath, out point2, relative, c);
235-
svgPath = FindPoint(svgPath, out point3, relative, c);
234+
svgPath = FindPoint(svgPath, relative, c, out point2);
235+
svgPath = FindPoint(svgPath, relative, c, out point3);
236236
point1 = c;
237237
if (previousOp is 'C' or 'S')
238238
{
@@ -245,14 +245,14 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> svgPath, out IPath value)
245245
c = point3;
246246
break;
247247
case 'Q': // Quadratic Bezier Curve
248-
svgPath = FindPoint(svgPath, out point1, relative, c);
249-
svgPath = FindPoint(svgPath, out point2, relative, c);
248+
svgPath = FindPoint(svgPath, relative, c, out point1);
249+
svgPath = FindPoint(svgPath, relative, c, out point2);
250250
builder.QuadraticBezierTo(point1, point2);
251251
lastc = point1;
252252
c = point2;
253253
break;
254254
case 'T':
255-
svgPath = FindPoint(svgPath, out point2, relative, c);
255+
svgPath = FindPoint(svgPath, relative, c, out point2);
256256
point1 = c;
257257
if (previousOp is 'Q' or 'T')
258258
{
@@ -265,20 +265,16 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> svgPath, out IPath value)
265265
c = point2;
266266
break;
267267
case 'A':
268-
svgPath = FindScaler(svgPath, out float radiiX);
269-
svgPath = TrimSeparator(svgPath);
270-
svgPath = FindScaler(svgPath, out float radiiY);
271-
svgPath = TrimSeparator(svgPath);
272-
svgPath = FindScaler(svgPath, out float angle);
273-
svgPath = TrimSeparator(svgPath);
274-
svgPath = FindScaler(svgPath, out float largeArc);
275-
svgPath = TrimSeparator(svgPath);
276-
svgPath = FindScaler(svgPath, out float sweep);
277-
svgPath = FindPoint(svgPath, out PointF point, relative, c);
278-
279-
// TODO: Skia compares the input SVG with the chars not the length.
280-
// Maybe we can do something with SpanAction<T>?
281-
// if (svgPath.Length > 0)
268+
if (TryFindScaler(ref svgPath, out float radiiX)
269+
&& TryTrimSeparator(ref svgPath)
270+
&& TryFindScaler(ref svgPath, out float radiiY)
271+
&& TryTrimSeparator(ref svgPath)
272+
&& TryFindScaler(ref svgPath, out float angle)
273+
&& TryTrimSeparator(ref svgPath)
274+
&& TryFindScaler(ref svgPath, out float largeArc)
275+
&& TryTrimSeparator(ref svgPath)
276+
&& TryFindScaler(ref svgPath, out float sweep)
277+
&& TryFindPoint(ref svgPath, relative, c, out PointF point))
282278
{
283279
builder.ArcTo(radiiX, radiiY, angle, largeArc == 1, sweep == 1, point);
284280
c = point;
@@ -290,8 +286,8 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> svgPath, out IPath value)
290286
c = first;
291287
break;
292288
case '~':
293-
svgPath = FindPoint(svgPath, out point1, relative, c);
294-
svgPath = FindPoint(svgPath, out point2, relative, c);
289+
svgPath = FindPoint(svgPath, relative, c, out point1);
290+
svgPath = FindPoint(svgPath, relative, c, out point2);
295291
builder.MoveTo(point1);
296292
builder.LineTo(point2);
297293
break;
@@ -309,77 +305,117 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> svgPath, out IPath value)
309305

310306
value = builder.Build();
311307
return true;
308+
}
312309

313-
static bool IsSeparator(char ch)
314-
=> char.IsWhiteSpace(ch) || ch == ',';
310+
private static bool TryTrimSeparator(ref ReadOnlySpan<char> str)
311+
{
312+
ReadOnlySpan<char> result = TrimSeparator(str);
313+
if (str.Slice(str.Length - result.Length).StartsWith(result))
314+
{
315+
str = result;
316+
return true;
317+
}
315318

316-
static ReadOnlySpan<char> TrimSeparator(ReadOnlySpan<char> data)
319+
return false;
320+
}
321+
322+
private static bool TryFindScaler(ref ReadOnlySpan<char> str, out float value)
323+
{
324+
ReadOnlySpan<char> result = FindScaler(str, out float valueInner);
325+
if (str.Slice(str.Length - result.Length).StartsWith(result))
317326
{
318-
if (data.Length == 0)
319-
{
320-
return data;
321-
}
327+
value = valueInner;
328+
str = result;
329+
return true;
330+
}
322331

323-
int idx = 0;
324-
for (; idx < data.Length; idx++)
325-
{
326-
if (!IsSeparator(data[idx]))
327-
{
328-
break;
329-
}
330-
}
332+
value = default;
333+
return false;
334+
}
335+
336+
private static bool TryFindPoint(ref ReadOnlySpan<char> str, bool relative, PointF current, out PointF value)
337+
{
338+
ReadOnlySpan<char> result = FindPoint(str, relative, current, out PointF valueInner);
339+
if (str.Slice(str.Length - result.Length).StartsWith(result))
340+
{
341+
value = valueInner;
342+
str = result;
343+
return true;
344+
}
345+
346+
value = default;
347+
return false;
348+
}
331349

332-
return data.Slice(idx);
350+
private static ReadOnlySpan<char> FindPoint(ReadOnlySpan<char> str, bool isRelative, PointF relative, out PointF value)
351+
{
352+
str = FindScaler(str, out float x);
353+
str = FindScaler(str, out float y);
354+
if (isRelative)
355+
{
356+
x += relative.X;
357+
y += relative.Y;
333358
}
334359

335-
static ReadOnlySpan<char> FindPoint(ReadOnlySpan<char> str, out PointF value, bool isRelative, PointF relative)
360+
value = new PointF(x, y);
361+
return str;
362+
}
363+
364+
private static ReadOnlySpan<char> FindScaler(ReadOnlySpan<char> str, out float scaler)
365+
{
366+
str = TrimSeparator(str);
367+
scaler = 0;
368+
369+
for (int i = 0; i < str.Length; i++)
336370
{
337-
str = FindScaler(str, out float x);
338-
str = FindScaler(str, out float y);
339-
if (isRelative)
371+
if (IsSeparator(str[i]) || i == str.Length)
340372
{
341-
x += relative.X;
342-
y += relative.Y;
373+
scaler = ParseFloat(str.Slice(0, i));
374+
return str.Slice(i);
343375
}
344-
345-
value = new PointF(x, y);
346-
return str;
347376
}
348377

349-
static ReadOnlySpan<char> FindScaler(ReadOnlySpan<char> str, out float scaler)
378+
if (str.Length > 0)
350379
{
351-
str = TrimSeparator(str);
352-
scaler = 0;
380+
scaler = ParseFloat(str);
381+
}
353382

354-
for (int i = 0; i < str.Length; i++)
355-
{
356-
if (IsSeparator(str[i]) || i == str.Length)
357-
{
358-
scaler = ParseFloat(str.Slice(0, i));
359-
return str.Slice(i);
360-
}
361-
}
383+
return ReadOnlySpan<char>.Empty;
384+
}
362385

363-
if (str.Length > 0)
364-
{
365-
scaler = ParseFloat(str);
366-
}
386+
private static bool IsSeparator(char ch)
387+
=> char.IsWhiteSpace(ch) || ch == ',';
367388

368-
return ReadOnlySpan<char>.Empty;
389+
private static ReadOnlySpan<char> TrimSeparator(ReadOnlySpan<char> data)
390+
{
391+
if (data.Length == 0)
392+
{
393+
return data;
369394
}
370395

371-
#if !NETCOREAPP2_1_OR_GREATER
372-
static unsafe float ParseFloat(ReadOnlySpan<char> str)
396+
int idx = 0;
397+
for (; idx < data.Length; idx++)
373398
{
374-
fixed (char* p = str)
399+
if (!IsSeparator(data[idx]))
375400
{
376-
return float.Parse(new string(p, 0, str.Length));
401+
break;
377402
}
378403
}
404+
405+
return data.Slice(idx);
406+
}
407+
408+
#if !NETCOREAPP2_1_OR_GREATER
409+
private static unsafe float ParseFloat(ReadOnlySpan<char> str)
410+
{
411+
fixed (char* p = str)
412+
{
413+
return float.Parse(new string(p, 0, str.Length));
414+
}
415+
}
379416
#else
380-
static float ParseFloat(ReadOnlySpan<char> str)
381-
=> float.Parse(str);
417+
private static float ParseFloat(ReadOnlySpan<char> str)
418+
=> float.Parse(str);
382419
#endif
383-
}
384420
}
385421
}

0 commit comments

Comments
 (0)