From 23cb29441338073fe82da37624f8bee26645919c Mon Sep 17 00:00:00 2001 From: Dan Field Date: Mon, 14 Mar 2022 11:08:18 -0700 Subject: [PATCH] Parse arc flags correctly when there is no optional ws Today, if the arc command flags are not separated by whitespace, the parser fails to parse the string. I noticed this when trying to parse a path similar to the one in the test case when playing around with PathKit.FromSVGString. Change-Id: I40967c07dfa03d76d26ac2e060b3ef7ac488d0fc Reviewed-on: https://skia-review.googlesource.com/c/skia/+/520256 Reviewed-by: Florin Malita Commit-Queue: Dan Field --- src/utils/SkParsePath.cpp | 27 ++++++++++++++++++++++----- tests/ParsePathTest.cpp | 10 ++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/utils/SkParsePath.cpp b/src/utils/SkParsePath.cpp index d0fa97ac6a..40100a4daf 100644 --- a/src/utils/SkParsePath.cpp +++ b/src/utils/SkParsePath.cpp @@ -72,6 +72,22 @@ static const char* find_scalar(const char str[], SkScalar* value, return str; } +// https://www.w3.org/TR/SVG11/paths.html#PathDataBNF +// +// flag: +// "0" | "1" +static const char* find_flag(const char str[], bool* value) { + if (!str) { + return nullptr; + } + if (str[0] != '1' && str[0] != '0') { + return nullptr; + } + *value = str[0] != '0'; + str = skip_sep(str + 1); + return str; +} + bool SkParsePath::FromSVGString(const char data[], SkPath* result) { SkPath path; SkPoint first = {0, 0}; @@ -164,18 +180,19 @@ bool SkParsePath::FromSVGString(const char data[], SkPath* result) { break; case 'A': { SkPoint radii; - SkScalar angle, largeArc, sweep; + SkScalar angle; + bool largeArc, sweep; if ((data = find_points(data, &radii, 1, false, nullptr)) && (data = skip_sep(data)) && (data = find_scalar(data, &angle, false, 0)) && (data = skip_sep(data)) - && (data = find_scalar(data, &largeArc, false, 0)) + && (data = find_flag(data, &largeArc)) && (data = skip_sep(data)) - && (data = find_scalar(data, &sweep, false, 0)) + && (data = find_flag(data, &sweep)) && (data = skip_sep(data)) && (data = find_points(data, &points[0], 1, relative, &c))) { - path.arcTo(radii, angle, (SkPath::ArcSize) SkToBool(largeArc), - (SkPathDirection) !SkToBool(sweep), points[0]); + path.arcTo(radii, angle, (SkPath::ArcSize) largeArc, + (SkPathDirection) !sweep, points[0]); path.getLastPt(&c); } } break; diff --git a/tests/ParsePathTest.cpp b/tests/ParsePathTest.cpp index 00f5c86ad8..01923859fe 100644 --- a/tests/ParsePathTest.cpp +++ b/tests/ParsePathTest.cpp @@ -128,3 +128,13 @@ DEF_TEST(ParsePathOptionalCommand, r) { REPORTER_ASSERT(r, path.countPoints() == gTests[i].fPoints); } } + +DEF_TEST(ParsePathArcFlags, r) { + const char* arcs = "M10 10a2.143 2.143 0 100-4.285 2.143 2.143 0 000 4.286"; + SkPath path; + REPORTER_ASSERT(r, SkParsePath::FromSVGString(arcs, &path)); + // Arcs decompose to two conics. + REPORTER_ASSERT(r, path.countVerbs() == 5); + // One for move, 2x per conic. + REPORTER_ASSERT(r, path.countPoints() == 9); +}