blob: 9914456b6acfa2713725def966916082d276565e [file] [log] [blame]
fmalitabffc2562016-08-03 10:21:11 -07001/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Klein8aa0edf2020-10-16 11:04:18 -05008#include "include/private/SkTPin.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -05009#include "include/utils/SkParse.h"
Florin Malitab3418102020-10-15 18:10:29 -040010#include "modules/svg/include/SkSVGAttributeParser.h"
11#include "modules/svg/include/SkSVGTypes.h"
fmalitabffc2562016-08-03 10:21:11 -070012
13namespace {
14
15// TODO: these should be shared with SkParse.cpp
16
17inline bool is_between(char c, char min, char max) {
18 SkASSERT(min <= max);
19 return (unsigned)(c - min) <= (unsigned)(max - min);
20}
21
22inline bool is_eos(char c) {
23 return !c;
24}
25
26inline bool is_ws(char c) {
27 return is_between(c, 1, 32);
28}
29
30inline bool is_sep(char c) {
31 return is_ws(c) || c == ',' || c == ';';
32}
33
John Stilesa6841be2020-08-06 14:11:56 -040034} // namespace
fmalitabffc2562016-08-03 10:21:11 -070035
36SkSVGAttributeParser::SkSVGAttributeParser(const char attributeString[])
37 : fCurPos(attributeString) {}
38
39template <typename F>
40inline bool SkSVGAttributeParser::advanceWhile(F f) {
41 auto initial = fCurPos;
42 while (f(*fCurPos)) {
43 fCurPos++;
44 }
45 return fCurPos != initial;
46}
47
48inline bool SkSVGAttributeParser::parseEOSToken() {
49 return is_eos(*fCurPos);
50}
51
52inline bool SkSVGAttributeParser::parseSepToken() {
53 return this->advanceWhile(is_sep);
54}
55
56inline bool SkSVGAttributeParser::parseWSToken() {
57 return this->advanceWhile(is_ws);
58}
59
Tyler Denniston2d65b732020-04-15 16:08:26 -040060inline bool SkSVGAttributeParser::parseCommaWspToken() {
61 // comma-wsp:
62 // (wsp+ comma? wsp*) | (comma wsp*)
63 return this->parseWSToken() || this->parseExpectedStringToken(",");
64}
65
fmalitabffc2562016-08-03 10:21:11 -070066inline bool SkSVGAttributeParser::parseExpectedStringToken(const char* expected) {
67 const char* c = fCurPos;
68
69 while (*c && *expected && *c == *expected) {
70 c++;
71 expected++;
72 }
73
74 if (*expected) {
75 return false;
76 }
77
78 fCurPos = c;
79 return true;
80}
81
82bool SkSVGAttributeParser::parseScalarToken(SkScalar* res) {
83 if (const char* next = SkParse::FindScalar(fCurPos, res)) {
84 fCurPos = next;
85 return true;
86 }
87 return false;
88}
89
90bool SkSVGAttributeParser::parseHexToken(uint32_t* res) {
91 if (const char* next = SkParse::FindHex(fCurPos, res)) {
92 fCurPos = next;
93 return true;
94 }
95 return false;
96}
97
98bool SkSVGAttributeParser::parseLengthUnitToken(SkSVGLength::Unit* unit) {
99 static const struct {
100 const char* fUnitName;
101 SkSVGLength::Unit fUnit;
102 } gUnitInfo[] = {
103 { "%" , SkSVGLength::Unit::kPercentage },
104 { "em", SkSVGLength::Unit::kEMS },
105 { "ex", SkSVGLength::Unit::kEXS },
106 { "px", SkSVGLength::Unit::kPX },
107 { "cm", SkSVGLength::Unit::kCM },
108 { "mm", SkSVGLength::Unit::kMM },
109 { "in", SkSVGLength::Unit::kIN },
110 { "pt", SkSVGLength::Unit::kPT },
111 { "pc", SkSVGLength::Unit::kPC },
112 };
113
114 for (size_t i = 0; i < SK_ARRAY_COUNT(gUnitInfo); ++i) {
115 if (this->parseExpectedStringToken(gUnitInfo[i].fUnitName)) {
116 *unit = gUnitInfo[i].fUnit;
117 return true;
118 }
119 }
120 return false;
121}
122
Florin Malitaf005c252020-04-08 10:10:53 -0400123// https://www.w3.org/TR/SVG11/types.html#DataTypeColor
fmalitabffc2562016-08-03 10:21:11 -0700124bool SkSVGAttributeParser::parseNamedColorToken(SkColor* c) {
125 if (const char* next = SkParse::FindNamedColor(fCurPos, strlen(fCurPos), c)) {
126 fCurPos = next;
127 return true;
128 }
129 return false;
130}
131
132bool SkSVGAttributeParser::parseHexColorToken(SkColor* c) {
133 uint32_t v;
134 const char* initial = fCurPos;
135
136 if (!this->parseExpectedStringToken("#") || !this->parseHexToken(&v)) {
137 return false;
138 }
139
140 switch (fCurPos - initial) {
141 case 7:
142 // matched #xxxxxxx
143 break;
144 case 4:
145 // matched '#xxx;
146 v = ((v << 12) & 0x00f00000) |
147 ((v << 8) & 0x000ff000) |
148 ((v << 4) & 0x00000ff0) |
149 ((v << 0) & 0x0000000f);
150 break;
151 default:
152 return false;
153 }
154
155 *c = v | 0xff000000;
156 return true;
157}
158
fmalita28d5b722016-09-12 17:06:47 -0700159bool SkSVGAttributeParser::parseColorComponentToken(int32_t* c) {
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400160 const auto parseIntegral = [this](int32_t* c) -> bool {
161 const char* p = SkParse::FindS32(fCurPos, c);
162 if (!p || *p == '.') {
163 // No value parsed, or fractional value.
164 return false;
165 }
166
167 if (*p == '%') {
168 *c = SkScalarRoundToInt(*c * 255.0f / 100);
169 p++;
170 }
171
172 fCurPos = p;
173 return true;
174 };
175
176 const auto parseFractional = [this](int32_t* c) -> bool {
177 SkScalar s;
178 const char* p = SkParse::FindScalar(fCurPos, &s);
179 if (!p || *p != '%') {
180 // Floating point must be a percentage (CSS2 rgb-percent syntax).
181 return false;
182 }
183 p++; // Skip '%'
184
185 *c = SkScalarRoundToInt(s * 255.0f / 100);
186 fCurPos = p;
187 return true;
188 };
189
190 if (!parseIntegral(c) && !parseFractional(c)) {
fmalita28d5b722016-09-12 17:06:47 -0700191 return false;
192 }
193
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400194 *c = SkTPin<int32_t>(*c, 0, 255);
fmalita28d5b722016-09-12 17:06:47 -0700195 return true;
196}
197
198bool SkSVGAttributeParser::parseRGBColorToken(SkColor* c) {
199 return this->parseParenthesized("rgb", [this](SkColor* c) -> bool {
200 int32_t r, g, b;
201 if (this->parseColorComponentToken(&r) &&
202 this->parseSepToken() &&
203 this->parseColorComponentToken(&g) &&
204 this->parseSepToken() &&
205 this->parseColorComponentToken(&b)) {
206
207 *c = SkColorSetRGB(static_cast<uint8_t>(r),
208 static_cast<uint8_t>(g),
209 static_cast<uint8_t>(b));
210 return true;
211 }
212 return false;
213 }, c);
214}
215
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400216// https://www.w3.org/TR/SVG11/types.html#DataTypeColor
217// And https://www.w3.org/TR/CSS2/syndata.html#color-units for the alternative
218// forms supported by SVG (e.g. RGB percentages).
fmalita397a5172016-08-08 11:38:55 -0700219bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) {
fmalitabffc2562016-08-03 10:21:11 -0700220 SkColor c;
221
fmalita61f36b32016-08-08 13:58:50 -0700222 // consume preceding whitespace
223 this->parseWSToken();
224
fmalita61f36b32016-08-08 13:58:50 -0700225 bool parsedValue = false;
fmalita28d5b722016-09-12 17:06:47 -0700226 if (this->parseHexColorToken(&c)
227 || this->parseNamedColorToken(&c)
228 || this->parseRGBColorToken(&c)) {
fmalita397a5172016-08-08 11:38:55 -0700229 *color = SkSVGColorType(c);
fmalita61f36b32016-08-08 13:58:50 -0700230 parsedValue = true;
231
232 // consume trailing whitespace
233 this->parseWSToken();
fmalitabffc2562016-08-03 10:21:11 -0700234 }
235
fmalita61f36b32016-08-08 13:58:50 -0700236 return parsedValue && this->parseEOSToken();
fmalitabffc2562016-08-03 10:21:11 -0700237}
238
Florin Malitaf005c252020-04-08 10:10:53 -0400239// https://www.w3.org/TR/SVG11/linking.html#IRIReference
fmalita28d5b722016-09-12 17:06:47 -0700240bool SkSVGAttributeParser::parseIRI(SkSVGStringType* iri) {
241 // consume preceding whitespace
242 this->parseWSToken();
243
244 // we only support local fragments
245 if (!this->parseExpectedStringToken("#")) {
246 return false;
247 }
248 const auto* start = fCurPos;
249 this->advanceWhile([](char c) -> bool { return !is_eos(c) && c != ')'; });
250 if (start == fCurPos) {
251 return false;
252 }
253 *iri = SkString(start, fCurPos - start);
254 return true;
255}
256
Florin Malitaf005c252020-04-08 10:10:53 -0400257// https://www.w3.org/TR/SVG11/types.html#DataTypeFuncIRI
fmalita28d5b722016-09-12 17:06:47 -0700258bool SkSVGAttributeParser::parseFuncIRI(SkSVGStringType* iri) {
259 return this->parseParenthesized("url", [this](SkSVGStringType* iri) -> bool {
260 return this->parseIRI(iri);
261 }, iri);
262}
263
Florin Malitaf005c252020-04-08 10:10:53 -0400264// https://www.w3.org/TR/SVG11/types.html#DataTypeNumber
fmalita397a5172016-08-08 11:38:55 -0700265bool SkSVGAttributeParser::parseNumber(SkSVGNumberType* number) {
fmalitabffc2562016-08-03 10:21:11 -0700266 // consume WS
267 this->parseWSToken();
268
269 SkScalar s;
270 if (this->parseScalarToken(&s)) {
fmalita397a5172016-08-08 11:38:55 -0700271 *number = SkSVGNumberType(s);
fmalitabffc2562016-08-03 10:21:11 -0700272 // consume trailing separators
273 this->parseSepToken();
274 return true;
275 }
276
277 return false;
278}
279
Florin Malitaf005c252020-04-08 10:10:53 -0400280// https://www.w3.org/TR/SVG11/types.html#DataTypeLength
fmalitabffc2562016-08-03 10:21:11 -0700281bool SkSVGAttributeParser::parseLength(SkSVGLength* length) {
282 SkScalar s;
283 SkSVGLength::Unit u = SkSVGLength::Unit::kNumber;
284
285 if (this->parseScalarToken(&s) &&
286 (this->parseLengthUnitToken(&u) || this->parseSepToken() || this->parseEOSToken())) {
287 *length = SkSVGLength(s, u);
288 // consume trailing separators
289 this->parseSepToken();
290 return true;
291 }
292
293 return false;
294}
fmalita397a5172016-08-08 11:38:55 -0700295
Florin Malitaf005c252020-04-08 10:10:53 -0400296// https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute
fmalita397a5172016-08-08 11:38:55 -0700297bool SkSVGAttributeParser::parseViewBox(SkSVGViewBoxType* vb) {
298 SkScalar x, y, w, h;
299 this->parseWSToken();
300
301 bool parsedValue = false;
302 if (this->parseScalarToken(&x) && this->parseSepToken() &&
303 this->parseScalarToken(&y) && this->parseSepToken() &&
304 this->parseScalarToken(&w) && this->parseSepToken() &&
305 this->parseScalarToken(&h)) {
306
307 *vb = SkSVGViewBoxType(SkRect::MakeXYWH(x, y, w, h));
308 parsedValue = true;
309 // consume trailing whitespace
310 this->parseWSToken();
311 }
312 return parsedValue && this->parseEOSToken();
313}
fmalitac97796b2016-08-08 12:58:57 -0700314
315template <typename Func, typename T>
316bool SkSVGAttributeParser::parseParenthesized(const char* prefix, Func f, T* result) {
317 this->parseWSToken();
318 if (prefix && !this->parseExpectedStringToken(prefix)) {
319 return false;
320 }
321 this->parseWSToken();
322 if (!this->parseExpectedStringToken("(")) {
323 return false;
324 }
325 this->parseWSToken();
326
327 if (!f(result)) {
328 return false;
329 }
330 this->parseWSToken();
331
332 return this->parseExpectedStringToken(")");
333}
334
335bool SkSVGAttributeParser::parseMatrixToken(SkMatrix* matrix) {
336 return this->parseParenthesized("matrix", [this](SkMatrix* m) -> bool {
337 SkScalar scalars[6];
338 for (int i = 0; i < 6; ++i) {
339 if (!(this->parseScalarToken(scalars + i) &&
340 (i > 4 || this->parseSepToken()))) {
341 return false;
342 }
343 }
344
345 m->setAll(scalars[0], scalars[2], scalars[4], scalars[1], scalars[3], scalars[5], 0, 0, 1);
346 return true;
347 }, matrix);
348}
349
350bool SkSVGAttributeParser::parseTranslateToken(SkMatrix* matrix) {
351 return this->parseParenthesized("translate", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500352 SkScalar tx = 0.0, ty = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700353 this->parseWSToken();
354 if (!this->parseScalarToken(&tx)) {
355 return false;
356 }
357
Tyler Dennistona625f922020-04-15 15:52:01 -0400358 if (!this->parseSepToken() || !this->parseScalarToken(&ty)) {
359 ty = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700360 }
361
362 m->setTranslate(tx, ty);
363 return true;
364 }, matrix);
365}
366
367bool SkSVGAttributeParser::parseScaleToken(SkMatrix* matrix) {
368 return this->parseParenthesized("scale", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500369 SkScalar sx = 0.0, sy = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700370 if (!this->parseScalarToken(&sx)) {
371 return false;
372 }
373
374 if (!(this->parseSepToken() && this->parseScalarToken(&sy))) {
375 sy = sx;
376 }
377
378 m->setScale(sx, sy);
379 return true;
380 }, matrix);
381}
382
383bool SkSVGAttributeParser::parseRotateToken(SkMatrix* matrix) {
384 return this->parseParenthesized("rotate", [this](SkMatrix* m) -> bool {
385 SkScalar angle;
386 if (!this->parseScalarToken(&angle)) {
387 return false;
388 }
389
390 SkScalar cx = 0;
391 SkScalar cy = 0;
392 // optional [<cx> <cy>]
393 if (this->parseSepToken() && this->parseScalarToken(&cx)) {
394 if (!(this->parseSepToken() && this->parseScalarToken(&cy))) {
395 return false;
396 }
397 }
398
399 m->setRotate(angle, cx, cy);
400 return true;
401 }, matrix);
402}
403
404bool SkSVGAttributeParser::parseSkewXToken(SkMatrix* matrix) {
405 return this->parseParenthesized("skewX", [this](SkMatrix* m) -> bool {
406 SkScalar angle;
407 if (!this->parseScalarToken(&angle)) {
408 return false;
409 }
Tyler Dennistond1e5b032020-04-08 13:02:41 -0400410 m->setSkewX(tanf(SkDegreesToRadians(angle)));
fmalitac97796b2016-08-08 12:58:57 -0700411 return true;
412 }, matrix);
413}
414
415bool SkSVGAttributeParser::parseSkewYToken(SkMatrix* matrix) {
416 return this->parseParenthesized("skewY", [this](SkMatrix* m) -> bool {
417 SkScalar angle;
418 if (!this->parseScalarToken(&angle)) {
419 return false;
420 }
Tyler Dennistond1e5b032020-04-08 13:02:41 -0400421 m->setSkewY(tanf(SkDegreesToRadians(angle)));
fmalitac97796b2016-08-08 12:58:57 -0700422 return true;
423 }, matrix);
424}
425
Florin Malitaf005c252020-04-08 10:10:53 -0400426// https://www.w3.org/TR/SVG11/coords.html#TransformAttribute
fmalitac97796b2016-08-08 12:58:57 -0700427bool SkSVGAttributeParser::parseTransform(SkSVGTransformType* t) {
428 SkMatrix matrix = SkMatrix::I();
429
430 bool parsed = false;
431 while (true) {
432 SkMatrix m;
433
434 if (!( this->parseMatrixToken(&m)
435 || this->parseTranslateToken(&m)
436 || this->parseScaleToken(&m)
437 || this->parseRotateToken(&m)
438 || this->parseSkewXToken(&m)
439 || this->parseSkewYToken(&m))) {
440 break;
441 }
442
443 matrix.preConcat(m);
444 parsed = true;
Tyler Denniston2d65b732020-04-15 16:08:26 -0400445
446 this->parseCommaWspToken();
fmalitac97796b2016-08-08 12:58:57 -0700447 }
448
449 this->parseWSToken();
450 if (!parsed || !this->parseEOSToken()) {
451 return false;
452 }
453
454 *t = SkSVGTransformType(matrix);
455 return true;
456}
fmalita2d961e02016-08-11 09:16:29 -0700457
Florin Malitaf005c252020-04-08 10:10:53 -0400458// https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint
fmalita2d961e02016-08-11 09:16:29 -0700459bool SkSVGAttributeParser::parsePaint(SkSVGPaint* paint) {
460 SkSVGColorType c;
fmalita28d5b722016-09-12 17:06:47 -0700461 SkSVGStringType iri;
fmalita2d961e02016-08-11 09:16:29 -0700462 bool parsedValue = false;
463 if (this->parseColor(&c)) {
464 *paint = SkSVGPaint(c);
465 parsedValue = true;
466 } else if (this->parseExpectedStringToken("none")) {
467 *paint = SkSVGPaint(SkSVGPaint::Type::kNone);
468 parsedValue = true;
469 } else if (this->parseExpectedStringToken("currentColor")) {
470 *paint = SkSVGPaint(SkSVGPaint::Type::kCurrentColor);
471 parsedValue = true;
472 } else if (this->parseExpectedStringToken("inherit")) {
473 *paint = SkSVGPaint(SkSVGPaint::Type::kInherit);
474 parsedValue = true;
fmalita28d5b722016-09-12 17:06:47 -0700475 } else if (this->parseFuncIRI(&iri)) {
Florin Malitaa3626692020-04-09 14:36:45 -0400476 *paint = SkSVGPaint(iri);
fmalita28d5b722016-09-12 17:06:47 -0700477 parsedValue = true;
fmalita2d961e02016-08-11 09:16:29 -0700478 }
479 return parsedValue && this->parseEOSToken();
480}
481
Florin Malitaf005c252020-04-08 10:10:53 -0400482// https://www.w3.org/TR/SVG11/masking.html#ClipPathProperty
Florin Malitace8840e2016-12-08 09:26:47 -0500483bool SkSVGAttributeParser::parseClipPath(SkSVGClip* clip) {
484 SkSVGStringType iri;
485 bool parsedValue = false;
486
487 if (this->parseExpectedStringToken("none")) {
488 *clip = SkSVGClip(SkSVGClip::Type::kNone);
489 parsedValue = true;
490 } else if (this->parseExpectedStringToken("inherit")) {
491 *clip = SkSVGClip(SkSVGClip::Type::kInherit);
492 parsedValue = true;
493 } else if (this->parseFuncIRI(&iri)) {
Florin Malitaa3626692020-04-09 14:36:45 -0400494 *clip = SkSVGClip(iri);
Florin Malitace8840e2016-12-08 09:26:47 -0500495 parsedValue = true;
496 }
497
498 return parsedValue && this->parseEOSToken();
499}
500
Florin Malitaf005c252020-04-08 10:10:53 -0400501// https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty
fmalita2d961e02016-08-11 09:16:29 -0700502bool SkSVGAttributeParser::parseLineCap(SkSVGLineCap* cap) {
503 static const struct {
504 SkSVGLineCap::Type fType;
505 const char* fName;
506 } gCapInfo[] = {
507 { SkSVGLineCap::Type::kButt , "butt" },
508 { SkSVGLineCap::Type::kRound , "round" },
509 { SkSVGLineCap::Type::kSquare , "square" },
510 { SkSVGLineCap::Type::kInherit, "inherit" },
511 };
512
513 bool parsedValue = false;
514 for (size_t i = 0; i < SK_ARRAY_COUNT(gCapInfo); ++i) {
515 if (this->parseExpectedStringToken(gCapInfo[i].fName)) {
516 *cap = SkSVGLineCap(gCapInfo[i].fType);
517 parsedValue = true;
518 break;
519 }
520 }
521
522 return parsedValue && this->parseEOSToken();
523}
524
Florin Malitaf005c252020-04-08 10:10:53 -0400525// https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty
fmalita2d961e02016-08-11 09:16:29 -0700526bool SkSVGAttributeParser::parseLineJoin(SkSVGLineJoin* join) {
527 static const struct {
528 SkSVGLineJoin::Type fType;
529 const char* fName;
530 } gJoinInfo[] = {
531 { SkSVGLineJoin::Type::kMiter , "miter" },
532 { SkSVGLineJoin::Type::kRound , "round" },
533 { SkSVGLineJoin::Type::kBevel , "bevel" },
534 { SkSVGLineJoin::Type::kInherit, "inherit" },
535 };
536
537 bool parsedValue = false;
538 for (size_t i = 0; i < SK_ARRAY_COUNT(gJoinInfo); ++i) {
539 if (this->parseExpectedStringToken(gJoinInfo[i].fName)) {
540 *join = SkSVGLineJoin(gJoinInfo[i].fType);
541 parsedValue = true;
542 break;
543 }
544 }
545
546 return parsedValue && this->parseEOSToken();
547}
fmalita5b31f322016-08-12 12:15:33 -0700548
Florin Malitaf005c252020-04-08 10:10:53 -0400549// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementSpreadMethodAttribute
fmalitacecd6172016-09-13 12:56:11 -0700550bool SkSVGAttributeParser::parseSpreadMethod(SkSVGSpreadMethod* spread) {
551 static const struct {
552 SkSVGSpreadMethod::Type fType;
553 const char* fName;
554 } gSpreadInfo[] = {
555 { SkSVGSpreadMethod::Type::kPad , "pad" },
556 { SkSVGSpreadMethod::Type::kReflect, "reflect" },
557 { SkSVGSpreadMethod::Type::kRepeat , "repeat" },
558 };
559
560 bool parsedValue = false;
561 for (size_t i = 0; i < SK_ARRAY_COUNT(gSpreadInfo); ++i) {
562 if (this->parseExpectedStringToken(gSpreadInfo[i].fName)) {
563 *spread = SkSVGSpreadMethod(gSpreadInfo[i].fType);
564 parsedValue = true;
565 break;
566 }
567 }
568
569 return parsedValue && this->parseEOSToken();
570}
571
Tyler Denniston308c0722020-04-14 10:53:41 -0400572// https://www.w3.org/TR/SVG11/pservers.html#StopElement
573bool SkSVGAttributeParser::parseStopColor(SkSVGStopColor* stopColor) {
574 SkSVGColorType c;
575 bool parsedValue = false;
576 if (this->parseColor(&c)) {
577 *stopColor = SkSVGStopColor(c);
578 parsedValue = true;
579 } else if (this->parseExpectedStringToken("currentColor")) {
580 *stopColor = SkSVGStopColor(SkSVGStopColor::Type::kCurrentColor);
581 parsedValue = true;
582 } else if (this->parseExpectedStringToken("inherit")) {
583 *stopColor = SkSVGStopColor(SkSVGStopColor::Type::kInherit);
584 parsedValue = true;
585 }
586 return parsedValue && this->parseEOSToken();
587}
588
Florin Malitaf005c252020-04-08 10:10:53 -0400589// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
fmalita5b31f322016-08-12 12:15:33 -0700590bool SkSVGAttributeParser::parsePoints(SkSVGPointsType* points) {
591 SkTDArray<SkPoint> pts;
592
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400593 // Skip initial wsp.
594 // list-of-points:
595 // wsp* coordinate-pairs? wsp*
596 this->advanceWhile(is_ws);
597
fmalita5b31f322016-08-12 12:15:33 -0700598 bool parsedValue = false;
599 for (;;) {
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400600 // Adjacent coordinate-pairs separated by comma-wsp.
601 // coordinate-pairs:
602 // coordinate-pair
603 // | coordinate-pair comma-wsp coordinate-pairs
Tyler Denniston2d65b732020-04-15 16:08:26 -0400604 if (parsedValue && !this->parseCommaWspToken()) {
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400605 break;
606 }
fmalita5b31f322016-08-12 12:15:33 -0700607
608 SkScalar x, y;
609 if (!this->parseScalarToken(&x)) {
610 break;
611 }
612
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400613 // Coordinate values separated by comma-wsp or '-'.
614 // coordinate-pair:
615 // coordinate comma-wsp coordinate
616 // | coordinate negative-coordinate
Tyler Denniston2d65b732020-04-15 16:08:26 -0400617 if (!this->parseCommaWspToken() && !this->parseEOSToken() && *fCurPos != '-') {
fmalita5b31f322016-08-12 12:15:33 -0700618 break;
619 }
fmalita5b31f322016-08-12 12:15:33 -0700620
621 if (!this->parseScalarToken(&y)) {
622 break;
623 }
624
Mike Reed5edcd312018-08-08 11:23:41 -0400625 pts.push_back(SkPoint::Make(x, y));
fmalita5b31f322016-08-12 12:15:33 -0700626 parsedValue = true;
627 }
628
629 if (parsedValue && this->parseEOSToken()) {
630 *points = pts;
631 return true;
632 }
633
634 return false;
635}
Florin Malitae932d4b2016-12-01 13:35:11 -0500636
Florin Malitaf005c252020-04-08 10:10:53 -0400637// https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty
Florin Malitae932d4b2016-12-01 13:35:11 -0500638bool SkSVGAttributeParser::parseFillRule(SkSVGFillRule* fillRule) {
639 static const struct {
640 SkSVGFillRule::Type fType;
641 const char* fName;
642 } gFillRuleInfo[] = {
643 { SkSVGFillRule::Type::kNonZero, "nonzero" },
644 { SkSVGFillRule::Type::kEvenOdd, "evenodd" },
645 { SkSVGFillRule::Type::kInherit, "inherit" },
646 };
647
648 bool parsedValue = false;
649 for (size_t i = 0; i < SK_ARRAY_COUNT(gFillRuleInfo); ++i) {
650 if (this->parseExpectedStringToken(gFillRuleInfo[i].fName)) {
651 *fillRule = SkSVGFillRule(gFillRuleInfo[i].fType);
652 parsedValue = true;
653 break;
654 }
655 }
656
657 return parsedValue && this->parseEOSToken();
658}
Florin Malitaffe6ae42017-10-12 11:33:28 -0400659
Florin Malitaf005c252020-04-08 10:10:53 -0400660// https://www.w3.org/TR/SVG11/painting.html#VisibilityProperty
Florin Malitaffe6ae42017-10-12 11:33:28 -0400661bool SkSVGAttributeParser::parseVisibility(SkSVGVisibility* visibility) {
662 static const struct {
663 SkSVGVisibility::Type fType;
664 const char* fName;
665 } gVisibilityInfo[] = {
666 { SkSVGVisibility::Type::kVisible , "visible" },
667 { SkSVGVisibility::Type::kHidden , "hidden" },
668 { SkSVGVisibility::Type::kCollapse, "collapse" },
669 { SkSVGVisibility::Type::kInherit , "inherit" },
670 };
671
672 bool parsedValue = false;
673 for (const auto& parseInfo : gVisibilityInfo) {
674 if (this->parseExpectedStringToken(parseInfo.fName)) {
675 *visibility = SkSVGVisibility(parseInfo.fType);
676 parsedValue = true;
677 break;
678 }
679 }
680
681 return parsedValue && this->parseEOSToken();
682}
Florin Malitaf543a602017-10-13 14:07:44 -0400683
Florin Malitaf005c252020-04-08 10:10:53 -0400684// https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty
Florin Malitaf543a602017-10-13 14:07:44 -0400685bool SkSVGAttributeParser::parseDashArray(SkSVGDashArray* dashArray) {
686 bool parsedValue = false;
687 if (this->parseExpectedStringToken("none")) {
688 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kNone);
689 parsedValue = true;
690 } else if (this->parseExpectedStringToken("inherit")) {
691 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kInherit);
692 parsedValue = true;
693 } else {
694 SkTDArray<SkSVGLength> dashes;
695 for (;;) {
696 SkSVGLength dash;
697 // parseLength() also consumes trailing separators.
698 if (!this->parseLength(&dash)) {
699 break;
700 }
701
Mike Reed5edcd312018-08-08 11:23:41 -0400702 dashes.push_back(dash);
Florin Malitaf543a602017-10-13 14:07:44 -0400703 parsedValue = true;
704 }
705
706 if (parsedValue) {
707 *dashArray = SkSVGDashArray(std::move(dashes));
708 }
709 }
710
711 return parsedValue && this->parseEOSToken();
712}