blob: c23f9f1331aa6e76bc8a64a0b5ef81543f5766b4 [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 Kleinc0bd9f92019-04-23 12:05:21 -05008#include "experimental/svg/model/SkSVGAttributeParser.h"
9#include "experimental/svg/model/SkSVGTypes.h"
10#include "include/utils/SkParse.h"
fmalitabffc2562016-08-03 10:21:11 -070011
12namespace {
13
14// TODO: these should be shared with SkParse.cpp
15
16inline bool is_between(char c, char min, char max) {
17 SkASSERT(min <= max);
18 return (unsigned)(c - min) <= (unsigned)(max - min);
19}
20
21inline bool is_eos(char c) {
22 return !c;
23}
24
25inline bool is_ws(char c) {
26 return is_between(c, 1, 32);
27}
28
29inline bool is_sep(char c) {
30 return is_ws(c) || c == ',' || c == ';';
31}
32
33} // anonymous ns
34
35SkSVGAttributeParser::SkSVGAttributeParser(const char attributeString[])
36 : fCurPos(attributeString) {}
37
38template <typename F>
39inline bool SkSVGAttributeParser::advanceWhile(F f) {
40 auto initial = fCurPos;
41 while (f(*fCurPos)) {
42 fCurPos++;
43 }
44 return fCurPos != initial;
45}
46
47inline bool SkSVGAttributeParser::parseEOSToken() {
48 return is_eos(*fCurPos);
49}
50
51inline bool SkSVGAttributeParser::parseSepToken() {
52 return this->advanceWhile(is_sep);
53}
54
55inline bool SkSVGAttributeParser::parseWSToken() {
56 return this->advanceWhile(is_ws);
57}
58
Tyler Denniston2d65b732020-04-15 16:08:26 -040059inline bool SkSVGAttributeParser::parseCommaWspToken() {
60 // comma-wsp:
61 // (wsp+ comma? wsp*) | (comma wsp*)
62 return this->parseWSToken() || this->parseExpectedStringToken(",");
63}
64
fmalitabffc2562016-08-03 10:21:11 -070065inline bool SkSVGAttributeParser::parseExpectedStringToken(const char* expected) {
66 const char* c = fCurPos;
67
68 while (*c && *expected && *c == *expected) {
69 c++;
70 expected++;
71 }
72
73 if (*expected) {
74 return false;
75 }
76
77 fCurPos = c;
78 return true;
79}
80
81bool SkSVGAttributeParser::parseScalarToken(SkScalar* res) {
82 if (const char* next = SkParse::FindScalar(fCurPos, res)) {
83 fCurPos = next;
84 return true;
85 }
86 return false;
87}
88
89bool SkSVGAttributeParser::parseHexToken(uint32_t* res) {
90 if (const char* next = SkParse::FindHex(fCurPos, res)) {
91 fCurPos = next;
92 return true;
93 }
94 return false;
95}
96
97bool SkSVGAttributeParser::parseLengthUnitToken(SkSVGLength::Unit* unit) {
98 static const struct {
99 const char* fUnitName;
100 SkSVGLength::Unit fUnit;
101 } gUnitInfo[] = {
102 { "%" , SkSVGLength::Unit::kPercentage },
103 { "em", SkSVGLength::Unit::kEMS },
104 { "ex", SkSVGLength::Unit::kEXS },
105 { "px", SkSVGLength::Unit::kPX },
106 { "cm", SkSVGLength::Unit::kCM },
107 { "mm", SkSVGLength::Unit::kMM },
108 { "in", SkSVGLength::Unit::kIN },
109 { "pt", SkSVGLength::Unit::kPT },
110 { "pc", SkSVGLength::Unit::kPC },
111 };
112
113 for (size_t i = 0; i < SK_ARRAY_COUNT(gUnitInfo); ++i) {
114 if (this->parseExpectedStringToken(gUnitInfo[i].fUnitName)) {
115 *unit = gUnitInfo[i].fUnit;
116 return true;
117 }
118 }
119 return false;
120}
121
Florin Malitaf005c252020-04-08 10:10:53 -0400122// https://www.w3.org/TR/SVG11/types.html#DataTypeColor
fmalitabffc2562016-08-03 10:21:11 -0700123bool SkSVGAttributeParser::parseNamedColorToken(SkColor* c) {
124 if (const char* next = SkParse::FindNamedColor(fCurPos, strlen(fCurPos), c)) {
125 fCurPos = next;
126 return true;
127 }
128 return false;
129}
130
131bool SkSVGAttributeParser::parseHexColorToken(SkColor* c) {
132 uint32_t v;
133 const char* initial = fCurPos;
134
135 if (!this->parseExpectedStringToken("#") || !this->parseHexToken(&v)) {
136 return false;
137 }
138
139 switch (fCurPos - initial) {
140 case 7:
141 // matched #xxxxxxx
142 break;
143 case 4:
144 // matched '#xxx;
145 v = ((v << 12) & 0x00f00000) |
146 ((v << 8) & 0x000ff000) |
147 ((v << 4) & 0x00000ff0) |
148 ((v << 0) & 0x0000000f);
149 break;
150 default:
151 return false;
152 }
153
154 *c = v | 0xff000000;
155 return true;
156}
157
fmalita28d5b722016-09-12 17:06:47 -0700158bool SkSVGAttributeParser::parseColorComponentToken(int32_t* c) {
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400159 const auto parseIntegral = [this](int32_t* c) -> bool {
160 const char* p = SkParse::FindS32(fCurPos, c);
161 if (!p || *p == '.') {
162 // No value parsed, or fractional value.
163 return false;
164 }
165
166 if (*p == '%') {
167 *c = SkScalarRoundToInt(*c * 255.0f / 100);
168 p++;
169 }
170
171 fCurPos = p;
172 return true;
173 };
174
175 const auto parseFractional = [this](int32_t* c) -> bool {
176 SkScalar s;
177 const char* p = SkParse::FindScalar(fCurPos, &s);
178 if (!p || *p != '%') {
179 // Floating point must be a percentage (CSS2 rgb-percent syntax).
180 return false;
181 }
182 p++; // Skip '%'
183
184 *c = SkScalarRoundToInt(s * 255.0f / 100);
185 fCurPos = p;
186 return true;
187 };
188
189 if (!parseIntegral(c) && !parseFractional(c)) {
fmalita28d5b722016-09-12 17:06:47 -0700190 return false;
191 }
192
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400193 *c = SkTPin<int32_t>(*c, 0, 255);
fmalita28d5b722016-09-12 17:06:47 -0700194 return true;
195}
196
197bool SkSVGAttributeParser::parseRGBColorToken(SkColor* c) {
198 return this->parseParenthesized("rgb", [this](SkColor* c) -> bool {
199 int32_t r, g, b;
200 if (this->parseColorComponentToken(&r) &&
201 this->parseSepToken() &&
202 this->parseColorComponentToken(&g) &&
203 this->parseSepToken() &&
204 this->parseColorComponentToken(&b)) {
205
206 *c = SkColorSetRGB(static_cast<uint8_t>(r),
207 static_cast<uint8_t>(g),
208 static_cast<uint8_t>(b));
209 return true;
210 }
211 return false;
212 }, c);
213}
214
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400215// https://www.w3.org/TR/SVG11/types.html#DataTypeColor
216// And https://www.w3.org/TR/CSS2/syndata.html#color-units for the alternative
217// forms supported by SVG (e.g. RGB percentages).
fmalita397a5172016-08-08 11:38:55 -0700218bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) {
fmalitabffc2562016-08-03 10:21:11 -0700219 SkColor c;
220
fmalita61f36b32016-08-08 13:58:50 -0700221 // consume preceding whitespace
222 this->parseWSToken();
223
fmalita61f36b32016-08-08 13:58:50 -0700224 bool parsedValue = false;
fmalita28d5b722016-09-12 17:06:47 -0700225 if (this->parseHexColorToken(&c)
226 || this->parseNamedColorToken(&c)
227 || this->parseRGBColorToken(&c)) {
fmalita397a5172016-08-08 11:38:55 -0700228 *color = SkSVGColorType(c);
fmalita61f36b32016-08-08 13:58:50 -0700229 parsedValue = true;
230
231 // consume trailing whitespace
232 this->parseWSToken();
fmalitabffc2562016-08-03 10:21:11 -0700233 }
234
fmalita61f36b32016-08-08 13:58:50 -0700235 return parsedValue && this->parseEOSToken();
fmalitabffc2562016-08-03 10:21:11 -0700236}
237
Florin Malitaf005c252020-04-08 10:10:53 -0400238// https://www.w3.org/TR/SVG11/linking.html#IRIReference
fmalita28d5b722016-09-12 17:06:47 -0700239bool SkSVGAttributeParser::parseIRI(SkSVGStringType* iri) {
240 // consume preceding whitespace
241 this->parseWSToken();
242
243 // we only support local fragments
244 if (!this->parseExpectedStringToken("#")) {
245 return false;
246 }
247 const auto* start = fCurPos;
248 this->advanceWhile([](char c) -> bool { return !is_eos(c) && c != ')'; });
249 if (start == fCurPos) {
250 return false;
251 }
252 *iri = SkString(start, fCurPos - start);
253 return true;
254}
255
Florin Malitaf005c252020-04-08 10:10:53 -0400256// https://www.w3.org/TR/SVG11/types.html#DataTypeFuncIRI
fmalita28d5b722016-09-12 17:06:47 -0700257bool SkSVGAttributeParser::parseFuncIRI(SkSVGStringType* iri) {
258 return this->parseParenthesized("url", [this](SkSVGStringType* iri) -> bool {
259 return this->parseIRI(iri);
260 }, iri);
261}
262
Florin Malitaf005c252020-04-08 10:10:53 -0400263// https://www.w3.org/TR/SVG11/types.html#DataTypeNumber
fmalita397a5172016-08-08 11:38:55 -0700264bool SkSVGAttributeParser::parseNumber(SkSVGNumberType* number) {
fmalitabffc2562016-08-03 10:21:11 -0700265 // consume WS
266 this->parseWSToken();
267
268 SkScalar s;
269 if (this->parseScalarToken(&s)) {
fmalita397a5172016-08-08 11:38:55 -0700270 *number = SkSVGNumberType(s);
fmalitabffc2562016-08-03 10:21:11 -0700271 // consume trailing separators
272 this->parseSepToken();
273 return true;
274 }
275
276 return false;
277}
278
Florin Malitaf005c252020-04-08 10:10:53 -0400279// https://www.w3.org/TR/SVG11/types.html#DataTypeLength
fmalitabffc2562016-08-03 10:21:11 -0700280bool SkSVGAttributeParser::parseLength(SkSVGLength* length) {
281 SkScalar s;
282 SkSVGLength::Unit u = SkSVGLength::Unit::kNumber;
283
284 if (this->parseScalarToken(&s) &&
285 (this->parseLengthUnitToken(&u) || this->parseSepToken() || this->parseEOSToken())) {
286 *length = SkSVGLength(s, u);
287 // consume trailing separators
288 this->parseSepToken();
289 return true;
290 }
291
292 return false;
293}
fmalita397a5172016-08-08 11:38:55 -0700294
Florin Malitaf005c252020-04-08 10:10:53 -0400295// https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute
fmalita397a5172016-08-08 11:38:55 -0700296bool SkSVGAttributeParser::parseViewBox(SkSVGViewBoxType* vb) {
297 SkScalar x, y, w, h;
298 this->parseWSToken();
299
300 bool parsedValue = false;
301 if (this->parseScalarToken(&x) && this->parseSepToken() &&
302 this->parseScalarToken(&y) && this->parseSepToken() &&
303 this->parseScalarToken(&w) && this->parseSepToken() &&
304 this->parseScalarToken(&h)) {
305
306 *vb = SkSVGViewBoxType(SkRect::MakeXYWH(x, y, w, h));
307 parsedValue = true;
308 // consume trailing whitespace
309 this->parseWSToken();
310 }
311 return parsedValue && this->parseEOSToken();
312}
fmalitac97796b2016-08-08 12:58:57 -0700313
314template <typename Func, typename T>
315bool SkSVGAttributeParser::parseParenthesized(const char* prefix, Func f, T* result) {
316 this->parseWSToken();
317 if (prefix && !this->parseExpectedStringToken(prefix)) {
318 return false;
319 }
320 this->parseWSToken();
321 if (!this->parseExpectedStringToken("(")) {
322 return false;
323 }
324 this->parseWSToken();
325
326 if (!f(result)) {
327 return false;
328 }
329 this->parseWSToken();
330
331 return this->parseExpectedStringToken(")");
332}
333
334bool SkSVGAttributeParser::parseMatrixToken(SkMatrix* matrix) {
335 return this->parseParenthesized("matrix", [this](SkMatrix* m) -> bool {
336 SkScalar scalars[6];
337 for (int i = 0; i < 6; ++i) {
338 if (!(this->parseScalarToken(scalars + i) &&
339 (i > 4 || this->parseSepToken()))) {
340 return false;
341 }
342 }
343
344 m->setAll(scalars[0], scalars[2], scalars[4], scalars[1], scalars[3], scalars[5], 0, 0, 1);
345 return true;
346 }, matrix);
347}
348
349bool SkSVGAttributeParser::parseTranslateToken(SkMatrix* matrix) {
350 return this->parseParenthesized("translate", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500351 SkScalar tx = 0.0, ty = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700352 this->parseWSToken();
353 if (!this->parseScalarToken(&tx)) {
354 return false;
355 }
356
Tyler Dennistona625f922020-04-15 15:52:01 -0400357 if (!this->parseSepToken() || !this->parseScalarToken(&ty)) {
358 ty = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700359 }
360
361 m->setTranslate(tx, ty);
362 return true;
363 }, matrix);
364}
365
366bool SkSVGAttributeParser::parseScaleToken(SkMatrix* matrix) {
367 return this->parseParenthesized("scale", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500368 SkScalar sx = 0.0, sy = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700369 if (!this->parseScalarToken(&sx)) {
370 return false;
371 }
372
373 if (!(this->parseSepToken() && this->parseScalarToken(&sy))) {
374 sy = sx;
375 }
376
377 m->setScale(sx, sy);
378 return true;
379 }, matrix);
380}
381
382bool SkSVGAttributeParser::parseRotateToken(SkMatrix* matrix) {
383 return this->parseParenthesized("rotate", [this](SkMatrix* m) -> bool {
384 SkScalar angle;
385 if (!this->parseScalarToken(&angle)) {
386 return false;
387 }
388
389 SkScalar cx = 0;
390 SkScalar cy = 0;
391 // optional [<cx> <cy>]
392 if (this->parseSepToken() && this->parseScalarToken(&cx)) {
393 if (!(this->parseSepToken() && this->parseScalarToken(&cy))) {
394 return false;
395 }
396 }
397
398 m->setRotate(angle, cx, cy);
399 return true;
400 }, matrix);
401}
402
403bool SkSVGAttributeParser::parseSkewXToken(SkMatrix* matrix) {
404 return this->parseParenthesized("skewX", [this](SkMatrix* m) -> bool {
405 SkScalar angle;
406 if (!this->parseScalarToken(&angle)) {
407 return false;
408 }
Tyler Dennistond1e5b032020-04-08 13:02:41 -0400409 m->setSkewX(tanf(SkDegreesToRadians(angle)));
fmalitac97796b2016-08-08 12:58:57 -0700410 return true;
411 }, matrix);
412}
413
414bool SkSVGAttributeParser::parseSkewYToken(SkMatrix* matrix) {
415 return this->parseParenthesized("skewY", [this](SkMatrix* m) -> bool {
416 SkScalar angle;
417 if (!this->parseScalarToken(&angle)) {
418 return false;
419 }
Tyler Dennistond1e5b032020-04-08 13:02:41 -0400420 m->setSkewY(tanf(SkDegreesToRadians(angle)));
fmalitac97796b2016-08-08 12:58:57 -0700421 return true;
422 }, matrix);
423}
424
Florin Malitaf005c252020-04-08 10:10:53 -0400425// https://www.w3.org/TR/SVG11/coords.html#TransformAttribute
fmalitac97796b2016-08-08 12:58:57 -0700426bool SkSVGAttributeParser::parseTransform(SkSVGTransformType* t) {
427 SkMatrix matrix = SkMatrix::I();
428
429 bool parsed = false;
430 while (true) {
431 SkMatrix m;
432
433 if (!( this->parseMatrixToken(&m)
434 || this->parseTranslateToken(&m)
435 || this->parseScaleToken(&m)
436 || this->parseRotateToken(&m)
437 || this->parseSkewXToken(&m)
438 || this->parseSkewYToken(&m))) {
439 break;
440 }
441
442 matrix.preConcat(m);
443 parsed = true;
Tyler Denniston2d65b732020-04-15 16:08:26 -0400444
445 this->parseCommaWspToken();
fmalitac97796b2016-08-08 12:58:57 -0700446 }
447
448 this->parseWSToken();
449 if (!parsed || !this->parseEOSToken()) {
450 return false;
451 }
452
453 *t = SkSVGTransformType(matrix);
454 return true;
455}
fmalita2d961e02016-08-11 09:16:29 -0700456
Florin Malitaf005c252020-04-08 10:10:53 -0400457// https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint
fmalita2d961e02016-08-11 09:16:29 -0700458bool SkSVGAttributeParser::parsePaint(SkSVGPaint* paint) {
459 SkSVGColorType c;
fmalita28d5b722016-09-12 17:06:47 -0700460 SkSVGStringType iri;
fmalita2d961e02016-08-11 09:16:29 -0700461 bool parsedValue = false;
462 if (this->parseColor(&c)) {
463 *paint = SkSVGPaint(c);
464 parsedValue = true;
465 } else if (this->parseExpectedStringToken("none")) {
466 *paint = SkSVGPaint(SkSVGPaint::Type::kNone);
467 parsedValue = true;
468 } else if (this->parseExpectedStringToken("currentColor")) {
469 *paint = SkSVGPaint(SkSVGPaint::Type::kCurrentColor);
470 parsedValue = true;
471 } else if (this->parseExpectedStringToken("inherit")) {
472 *paint = SkSVGPaint(SkSVGPaint::Type::kInherit);
473 parsedValue = true;
fmalita28d5b722016-09-12 17:06:47 -0700474 } else if (this->parseFuncIRI(&iri)) {
Florin Malitaa3626692020-04-09 14:36:45 -0400475 *paint = SkSVGPaint(iri);
fmalita28d5b722016-09-12 17:06:47 -0700476 parsedValue = true;
fmalita2d961e02016-08-11 09:16:29 -0700477 }
478 return parsedValue && this->parseEOSToken();
479}
480
Florin Malitaf005c252020-04-08 10:10:53 -0400481// https://www.w3.org/TR/SVG11/masking.html#ClipPathProperty
Florin Malitace8840e2016-12-08 09:26:47 -0500482bool SkSVGAttributeParser::parseClipPath(SkSVGClip* clip) {
483 SkSVGStringType iri;
484 bool parsedValue = false;
485
486 if (this->parseExpectedStringToken("none")) {
487 *clip = SkSVGClip(SkSVGClip::Type::kNone);
488 parsedValue = true;
489 } else if (this->parseExpectedStringToken("inherit")) {
490 *clip = SkSVGClip(SkSVGClip::Type::kInherit);
491 parsedValue = true;
492 } else if (this->parseFuncIRI(&iri)) {
Florin Malitaa3626692020-04-09 14:36:45 -0400493 *clip = SkSVGClip(iri);
Florin Malitace8840e2016-12-08 09:26:47 -0500494 parsedValue = true;
495 }
496
497 return parsedValue && this->parseEOSToken();
498}
499
Florin Malitaf005c252020-04-08 10:10:53 -0400500// https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty
fmalita2d961e02016-08-11 09:16:29 -0700501bool SkSVGAttributeParser::parseLineCap(SkSVGLineCap* cap) {
502 static const struct {
503 SkSVGLineCap::Type fType;
504 const char* fName;
505 } gCapInfo[] = {
506 { SkSVGLineCap::Type::kButt , "butt" },
507 { SkSVGLineCap::Type::kRound , "round" },
508 { SkSVGLineCap::Type::kSquare , "square" },
509 { SkSVGLineCap::Type::kInherit, "inherit" },
510 };
511
512 bool parsedValue = false;
513 for (size_t i = 0; i < SK_ARRAY_COUNT(gCapInfo); ++i) {
514 if (this->parseExpectedStringToken(gCapInfo[i].fName)) {
515 *cap = SkSVGLineCap(gCapInfo[i].fType);
516 parsedValue = true;
517 break;
518 }
519 }
520
521 return parsedValue && this->parseEOSToken();
522}
523
Florin Malitaf005c252020-04-08 10:10:53 -0400524// https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty
fmalita2d961e02016-08-11 09:16:29 -0700525bool SkSVGAttributeParser::parseLineJoin(SkSVGLineJoin* join) {
526 static const struct {
527 SkSVGLineJoin::Type fType;
528 const char* fName;
529 } gJoinInfo[] = {
530 { SkSVGLineJoin::Type::kMiter , "miter" },
531 { SkSVGLineJoin::Type::kRound , "round" },
532 { SkSVGLineJoin::Type::kBevel , "bevel" },
533 { SkSVGLineJoin::Type::kInherit, "inherit" },
534 };
535
536 bool parsedValue = false;
537 for (size_t i = 0; i < SK_ARRAY_COUNT(gJoinInfo); ++i) {
538 if (this->parseExpectedStringToken(gJoinInfo[i].fName)) {
539 *join = SkSVGLineJoin(gJoinInfo[i].fType);
540 parsedValue = true;
541 break;
542 }
543 }
544
545 return parsedValue && this->parseEOSToken();
546}
fmalita5b31f322016-08-12 12:15:33 -0700547
Florin Malitaf005c252020-04-08 10:10:53 -0400548// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementSpreadMethodAttribute
fmalitacecd6172016-09-13 12:56:11 -0700549bool SkSVGAttributeParser::parseSpreadMethod(SkSVGSpreadMethod* spread) {
550 static const struct {
551 SkSVGSpreadMethod::Type fType;
552 const char* fName;
553 } gSpreadInfo[] = {
554 { SkSVGSpreadMethod::Type::kPad , "pad" },
555 { SkSVGSpreadMethod::Type::kReflect, "reflect" },
556 { SkSVGSpreadMethod::Type::kRepeat , "repeat" },
557 };
558
559 bool parsedValue = false;
560 for (size_t i = 0; i < SK_ARRAY_COUNT(gSpreadInfo); ++i) {
561 if (this->parseExpectedStringToken(gSpreadInfo[i].fName)) {
562 *spread = SkSVGSpreadMethod(gSpreadInfo[i].fType);
563 parsedValue = true;
564 break;
565 }
566 }
567
568 return parsedValue && this->parseEOSToken();
569}
570
Tyler Denniston308c0722020-04-14 10:53:41 -0400571// https://www.w3.org/TR/SVG11/pservers.html#StopElement
572bool SkSVGAttributeParser::parseStopColor(SkSVGStopColor* stopColor) {
573 SkSVGColorType c;
574 bool parsedValue = false;
575 if (this->parseColor(&c)) {
576 *stopColor = SkSVGStopColor(c);
577 parsedValue = true;
578 } else if (this->parseExpectedStringToken("currentColor")) {
579 *stopColor = SkSVGStopColor(SkSVGStopColor::Type::kCurrentColor);
580 parsedValue = true;
581 } else if (this->parseExpectedStringToken("inherit")) {
582 *stopColor = SkSVGStopColor(SkSVGStopColor::Type::kInherit);
583 parsedValue = true;
584 }
585 return parsedValue && this->parseEOSToken();
586}
587
Florin Malitaf005c252020-04-08 10:10:53 -0400588// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
fmalita5b31f322016-08-12 12:15:33 -0700589bool SkSVGAttributeParser::parsePoints(SkSVGPointsType* points) {
590 SkTDArray<SkPoint> pts;
591
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400592 // Skip initial wsp.
593 // list-of-points:
594 // wsp* coordinate-pairs? wsp*
595 this->advanceWhile(is_ws);
596
fmalita5b31f322016-08-12 12:15:33 -0700597 bool parsedValue = false;
598 for (;;) {
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400599 // Adjacent coordinate-pairs separated by comma-wsp.
600 // coordinate-pairs:
601 // coordinate-pair
602 // | coordinate-pair comma-wsp coordinate-pairs
Tyler Denniston2d65b732020-04-15 16:08:26 -0400603 if (parsedValue && !this->parseCommaWspToken()) {
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400604 break;
605 }
fmalita5b31f322016-08-12 12:15:33 -0700606
607 SkScalar x, y;
608 if (!this->parseScalarToken(&x)) {
609 break;
610 }
611
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400612 // Coordinate values separated by comma-wsp or '-'.
613 // coordinate-pair:
614 // coordinate comma-wsp coordinate
615 // | coordinate negative-coordinate
Tyler Denniston2d65b732020-04-15 16:08:26 -0400616 if (!this->parseCommaWspToken() && !this->parseEOSToken() && *fCurPos != '-') {
fmalita5b31f322016-08-12 12:15:33 -0700617 break;
618 }
fmalita5b31f322016-08-12 12:15:33 -0700619
620 if (!this->parseScalarToken(&y)) {
621 break;
622 }
623
Mike Reed5edcd312018-08-08 11:23:41 -0400624 pts.push_back(SkPoint::Make(x, y));
fmalita5b31f322016-08-12 12:15:33 -0700625 parsedValue = true;
626 }
627
628 if (parsedValue && this->parseEOSToken()) {
629 *points = pts;
630 return true;
631 }
632
633 return false;
634}
Florin Malitae932d4b2016-12-01 13:35:11 -0500635
Florin Malitaf005c252020-04-08 10:10:53 -0400636// https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty
Florin Malitae932d4b2016-12-01 13:35:11 -0500637bool SkSVGAttributeParser::parseFillRule(SkSVGFillRule* fillRule) {
638 static const struct {
639 SkSVGFillRule::Type fType;
640 const char* fName;
641 } gFillRuleInfo[] = {
642 { SkSVGFillRule::Type::kNonZero, "nonzero" },
643 { SkSVGFillRule::Type::kEvenOdd, "evenodd" },
644 { SkSVGFillRule::Type::kInherit, "inherit" },
645 };
646
647 bool parsedValue = false;
648 for (size_t i = 0; i < SK_ARRAY_COUNT(gFillRuleInfo); ++i) {
649 if (this->parseExpectedStringToken(gFillRuleInfo[i].fName)) {
650 *fillRule = SkSVGFillRule(gFillRuleInfo[i].fType);
651 parsedValue = true;
652 break;
653 }
654 }
655
656 return parsedValue && this->parseEOSToken();
657}
Florin Malitaffe6ae42017-10-12 11:33:28 -0400658
Florin Malitaf005c252020-04-08 10:10:53 -0400659// https://www.w3.org/TR/SVG11/painting.html#VisibilityProperty
Florin Malitaffe6ae42017-10-12 11:33:28 -0400660bool SkSVGAttributeParser::parseVisibility(SkSVGVisibility* visibility) {
661 static const struct {
662 SkSVGVisibility::Type fType;
663 const char* fName;
664 } gVisibilityInfo[] = {
665 { SkSVGVisibility::Type::kVisible , "visible" },
666 { SkSVGVisibility::Type::kHidden , "hidden" },
667 { SkSVGVisibility::Type::kCollapse, "collapse" },
668 { SkSVGVisibility::Type::kInherit , "inherit" },
669 };
670
671 bool parsedValue = false;
672 for (const auto& parseInfo : gVisibilityInfo) {
673 if (this->parseExpectedStringToken(parseInfo.fName)) {
674 *visibility = SkSVGVisibility(parseInfo.fType);
675 parsedValue = true;
676 break;
677 }
678 }
679
680 return parsedValue && this->parseEOSToken();
681}
Florin Malitaf543a602017-10-13 14:07:44 -0400682
Florin Malitaf005c252020-04-08 10:10:53 -0400683// https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty
Florin Malitaf543a602017-10-13 14:07:44 -0400684bool SkSVGAttributeParser::parseDashArray(SkSVGDashArray* dashArray) {
685 bool parsedValue = false;
686 if (this->parseExpectedStringToken("none")) {
687 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kNone);
688 parsedValue = true;
689 } else if (this->parseExpectedStringToken("inherit")) {
690 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kInherit);
691 parsedValue = true;
692 } else {
693 SkTDArray<SkSVGLength> dashes;
694 for (;;) {
695 SkSVGLength dash;
696 // parseLength() also consumes trailing separators.
697 if (!this->parseLength(&dash)) {
698 break;
699 }
700
Mike Reed5edcd312018-08-08 11:23:41 -0400701 dashes.push_back(dash);
Florin Malitaf543a602017-10-13 14:07:44 -0400702 parsedValue = true;
703 }
704
705 if (parsedValue) {
706 *dashArray = SkSVGDashArray(std::move(dashes));
707 }
708 }
709
710 return parsedValue && this->parseEOSToken();
711}