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 <fmalita@google.com>
Commit-Queue: Dan Field <dnfield@google.com>
This commit is contained in:
Dan Field 2022-03-14 11:08:18 -07:00 committed by SkCQ
parent bd11ec8eab
commit 23cb294413
2 changed files with 32 additions and 5 deletions

View File

@ -72,6 +72,22 @@ static const char* find_scalar(const char str[], SkScalar* value,
return str; 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) { bool SkParsePath::FromSVGString(const char data[], SkPath* result) {
SkPath path; SkPath path;
SkPoint first = {0, 0}; SkPoint first = {0, 0};
@ -164,18 +180,19 @@ bool SkParsePath::FromSVGString(const char data[], SkPath* result) {
break; break;
case 'A': { case 'A': {
SkPoint radii; SkPoint radii;
SkScalar angle, largeArc, sweep; SkScalar angle;
bool largeArc, sweep;
if ((data = find_points(data, &radii, 1, false, nullptr)) if ((data = find_points(data, &radii, 1, false, nullptr))
&& (data = skip_sep(data)) && (data = skip_sep(data))
&& (data = find_scalar(data, &angle, false, 0)) && (data = find_scalar(data, &angle, false, 0))
&& (data = skip_sep(data)) && (data = skip_sep(data))
&& (data = find_scalar(data, &largeArc, false, 0)) && (data = find_flag(data, &largeArc))
&& (data = skip_sep(data)) && (data = skip_sep(data))
&& (data = find_scalar(data, &sweep, false, 0)) && (data = find_flag(data, &sweep))
&& (data = skip_sep(data)) && (data = skip_sep(data))
&& (data = find_points(data, &points[0], 1, relative, &c))) { && (data = find_points(data, &points[0], 1, relative, &c))) {
path.arcTo(radii, angle, (SkPath::ArcSize) SkToBool(largeArc), path.arcTo(radii, angle, (SkPath::ArcSize) largeArc,
(SkPathDirection) !SkToBool(sweep), points[0]); (SkPathDirection) !sweep, points[0]);
path.getLastPt(&c); path.getLastPt(&c);
} }
} break; } break;

View File

@ -128,3 +128,13 @@ DEF_TEST(ParsePathOptionalCommand, r) {
REPORTER_ASSERT(r, path.countPoints() == gTests[i].fPoints); 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);
}