blob: 4c844b33eb1a3ccea669aea769b64308f807afa6 [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
59inline bool SkSVGAttributeParser::parseExpectedStringToken(const char* expected) {
60 const char* c = fCurPos;
61
62 while (*c && *expected && *c == *expected) {
63 c++;
64 expected++;
65 }
66
67 if (*expected) {
68 return false;
69 }
70
71 fCurPos = c;
72 return true;
73}
74
75bool SkSVGAttributeParser::parseScalarToken(SkScalar* res) {
76 if (const char* next = SkParse::FindScalar(fCurPos, res)) {
77 fCurPos = next;
78 return true;
79 }
80 return false;
81}
82
83bool SkSVGAttributeParser::parseHexToken(uint32_t* res) {
84 if (const char* next = SkParse::FindHex(fCurPos, res)) {
85 fCurPos = next;
86 return true;
87 }
88 return false;
89}
90
91bool SkSVGAttributeParser::parseLengthUnitToken(SkSVGLength::Unit* unit) {
92 static const struct {
93 const char* fUnitName;
94 SkSVGLength::Unit fUnit;
95 } gUnitInfo[] = {
96 { "%" , SkSVGLength::Unit::kPercentage },
97 { "em", SkSVGLength::Unit::kEMS },
98 { "ex", SkSVGLength::Unit::kEXS },
99 { "px", SkSVGLength::Unit::kPX },
100 { "cm", SkSVGLength::Unit::kCM },
101 { "mm", SkSVGLength::Unit::kMM },
102 { "in", SkSVGLength::Unit::kIN },
103 { "pt", SkSVGLength::Unit::kPT },
104 { "pc", SkSVGLength::Unit::kPC },
105 };
106
107 for (size_t i = 0; i < SK_ARRAY_COUNT(gUnitInfo); ++i) {
108 if (this->parseExpectedStringToken(gUnitInfo[i].fUnitName)) {
109 *unit = gUnitInfo[i].fUnit;
110 return true;
111 }
112 }
113 return false;
114}
115
Florin Malitaf005c252020-04-08 10:10:53 -0400116// https://www.w3.org/TR/SVG11/types.html#DataTypeColor
fmalitabffc2562016-08-03 10:21:11 -0700117bool SkSVGAttributeParser::parseNamedColorToken(SkColor* c) {
118 if (const char* next = SkParse::FindNamedColor(fCurPos, strlen(fCurPos), c)) {
119 fCurPos = next;
120 return true;
121 }
122 return false;
123}
124
125bool SkSVGAttributeParser::parseHexColorToken(SkColor* c) {
126 uint32_t v;
127 const char* initial = fCurPos;
128
129 if (!this->parseExpectedStringToken("#") || !this->parseHexToken(&v)) {
130 return false;
131 }
132
133 switch (fCurPos - initial) {
134 case 7:
135 // matched #xxxxxxx
136 break;
137 case 4:
138 // matched '#xxx;
139 v = ((v << 12) & 0x00f00000) |
140 ((v << 8) & 0x000ff000) |
141 ((v << 4) & 0x00000ff0) |
142 ((v << 0) & 0x0000000f);
143 break;
144 default:
145 return false;
146 }
147
148 *c = v | 0xff000000;
149 return true;
150}
151
fmalita28d5b722016-09-12 17:06:47 -0700152bool SkSVGAttributeParser::parseColorComponentToken(int32_t* c) {
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400153 const auto parseIntegral = [this](int32_t* c) -> bool {
154 const char* p = SkParse::FindS32(fCurPos, c);
155 if (!p || *p == '.') {
156 // No value parsed, or fractional value.
157 return false;
158 }
159
160 if (*p == '%') {
161 *c = SkScalarRoundToInt(*c * 255.0f / 100);
162 p++;
163 }
164
165 fCurPos = p;
166 return true;
167 };
168
169 const auto parseFractional = [this](int32_t* c) -> bool {
170 SkScalar s;
171 const char* p = SkParse::FindScalar(fCurPos, &s);
172 if (!p || *p != '%') {
173 // Floating point must be a percentage (CSS2 rgb-percent syntax).
174 return false;
175 }
176 p++; // Skip '%'
177
178 *c = SkScalarRoundToInt(s * 255.0f / 100);
179 fCurPos = p;
180 return true;
181 };
182
183 if (!parseIntegral(c) && !parseFractional(c)) {
fmalita28d5b722016-09-12 17:06:47 -0700184 return false;
185 }
186
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400187 *c = SkTPin<int32_t>(*c, 0, 255);
fmalita28d5b722016-09-12 17:06:47 -0700188 return true;
189}
190
191bool SkSVGAttributeParser::parseRGBColorToken(SkColor* c) {
192 return this->parseParenthesized("rgb", [this](SkColor* c) -> bool {
193 int32_t r, g, b;
194 if (this->parseColorComponentToken(&r) &&
195 this->parseSepToken() &&
196 this->parseColorComponentToken(&g) &&
197 this->parseSepToken() &&
198 this->parseColorComponentToken(&b)) {
199
200 *c = SkColorSetRGB(static_cast<uint8_t>(r),
201 static_cast<uint8_t>(g),
202 static_cast<uint8_t>(b));
203 return true;
204 }
205 return false;
206 }, c);
207}
208
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400209// https://www.w3.org/TR/SVG11/types.html#DataTypeColor
210// And https://www.w3.org/TR/CSS2/syndata.html#color-units for the alternative
211// forms supported by SVG (e.g. RGB percentages).
fmalita397a5172016-08-08 11:38:55 -0700212bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) {
fmalitabffc2562016-08-03 10:21:11 -0700213 SkColor c;
214
fmalita61f36b32016-08-08 13:58:50 -0700215 // consume preceding whitespace
216 this->parseWSToken();
217
fmalita61f36b32016-08-08 13:58:50 -0700218 bool parsedValue = false;
fmalita28d5b722016-09-12 17:06:47 -0700219 if (this->parseHexColorToken(&c)
220 || this->parseNamedColorToken(&c)
221 || this->parseRGBColorToken(&c)) {
fmalita397a5172016-08-08 11:38:55 -0700222 *color = SkSVGColorType(c);
fmalita61f36b32016-08-08 13:58:50 -0700223 parsedValue = true;
224
225 // consume trailing whitespace
226 this->parseWSToken();
fmalitabffc2562016-08-03 10:21:11 -0700227 }
228
fmalita61f36b32016-08-08 13:58:50 -0700229 return parsedValue && this->parseEOSToken();
fmalitabffc2562016-08-03 10:21:11 -0700230}
231
Florin Malitaf005c252020-04-08 10:10:53 -0400232// https://www.w3.org/TR/SVG11/linking.html#IRIReference
fmalita28d5b722016-09-12 17:06:47 -0700233bool SkSVGAttributeParser::parseIRI(SkSVGStringType* iri) {
234 // consume preceding whitespace
235 this->parseWSToken();
236
237 // we only support local fragments
238 if (!this->parseExpectedStringToken("#")) {
239 return false;
240 }
241 const auto* start = fCurPos;
242 this->advanceWhile([](char c) -> bool { return !is_eos(c) && c != ')'; });
243 if (start == fCurPos) {
244 return false;
245 }
246 *iri = SkString(start, fCurPos - start);
247 return true;
248}
249
Florin Malitaf005c252020-04-08 10:10:53 -0400250// https://www.w3.org/TR/SVG11/types.html#DataTypeFuncIRI
fmalita28d5b722016-09-12 17:06:47 -0700251bool SkSVGAttributeParser::parseFuncIRI(SkSVGStringType* iri) {
252 return this->parseParenthesized("url", [this](SkSVGStringType* iri) -> bool {
253 return this->parseIRI(iri);
254 }, iri);
255}
256
Florin Malitaf005c252020-04-08 10:10:53 -0400257// https://www.w3.org/TR/SVG11/types.html#DataTypeNumber
fmalita397a5172016-08-08 11:38:55 -0700258bool SkSVGAttributeParser::parseNumber(SkSVGNumberType* number) {
fmalitabffc2562016-08-03 10:21:11 -0700259 // consume WS
260 this->parseWSToken();
261
262 SkScalar s;
263 if (this->parseScalarToken(&s)) {
fmalita397a5172016-08-08 11:38:55 -0700264 *number = SkSVGNumberType(s);
fmalitabffc2562016-08-03 10:21:11 -0700265 // consume trailing separators
266 this->parseSepToken();
267 return true;
268 }
269
270 return false;
271}
272
Florin Malitaf005c252020-04-08 10:10:53 -0400273// https://www.w3.org/TR/SVG11/types.html#DataTypeLength
fmalitabffc2562016-08-03 10:21:11 -0700274bool SkSVGAttributeParser::parseLength(SkSVGLength* length) {
275 SkScalar s;
276 SkSVGLength::Unit u = SkSVGLength::Unit::kNumber;
277
278 if (this->parseScalarToken(&s) &&
279 (this->parseLengthUnitToken(&u) || this->parseSepToken() || this->parseEOSToken())) {
280 *length = SkSVGLength(s, u);
281 // consume trailing separators
282 this->parseSepToken();
283 return true;
284 }
285
286 return false;
287}
fmalita397a5172016-08-08 11:38:55 -0700288
Florin Malitaf005c252020-04-08 10:10:53 -0400289// https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute
fmalita397a5172016-08-08 11:38:55 -0700290bool SkSVGAttributeParser::parseViewBox(SkSVGViewBoxType* vb) {
291 SkScalar x, y, w, h;
292 this->parseWSToken();
293
294 bool parsedValue = false;
295 if (this->parseScalarToken(&x) && this->parseSepToken() &&
296 this->parseScalarToken(&y) && this->parseSepToken() &&
297 this->parseScalarToken(&w) && this->parseSepToken() &&
298 this->parseScalarToken(&h)) {
299
300 *vb = SkSVGViewBoxType(SkRect::MakeXYWH(x, y, w, h));
301 parsedValue = true;
302 // consume trailing whitespace
303 this->parseWSToken();
304 }
305 return parsedValue && this->parseEOSToken();
306}
fmalitac97796b2016-08-08 12:58:57 -0700307
308template <typename Func, typename T>
309bool SkSVGAttributeParser::parseParenthesized(const char* prefix, Func f, T* result) {
310 this->parseWSToken();
311 if (prefix && !this->parseExpectedStringToken(prefix)) {
312 return false;
313 }
314 this->parseWSToken();
315 if (!this->parseExpectedStringToken("(")) {
316 return false;
317 }
318 this->parseWSToken();
319
320 if (!f(result)) {
321 return false;
322 }
323 this->parseWSToken();
324
325 return this->parseExpectedStringToken(")");
326}
327
328bool SkSVGAttributeParser::parseMatrixToken(SkMatrix* matrix) {
329 return this->parseParenthesized("matrix", [this](SkMatrix* m) -> bool {
330 SkScalar scalars[6];
331 for (int i = 0; i < 6; ++i) {
332 if (!(this->parseScalarToken(scalars + i) &&
333 (i > 4 || this->parseSepToken()))) {
334 return false;
335 }
336 }
337
338 m->setAll(scalars[0], scalars[2], scalars[4], scalars[1], scalars[3], scalars[5], 0, 0, 1);
339 return true;
340 }, matrix);
341}
342
343bool SkSVGAttributeParser::parseTranslateToken(SkMatrix* matrix) {
344 return this->parseParenthesized("translate", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500345 SkScalar tx = 0.0, ty = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700346 this->parseWSToken();
347 if (!this->parseScalarToken(&tx)) {
348 return false;
349 }
350
351 if (!(this->parseSepToken() && this->parseScalarToken(&ty))) {
352 ty = tx;
353 }
354
355 m->setTranslate(tx, ty);
356 return true;
357 }, matrix);
358}
359
360bool SkSVGAttributeParser::parseScaleToken(SkMatrix* matrix) {
361 return this->parseParenthesized("scale", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500362 SkScalar sx = 0.0, sy = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700363 if (!this->parseScalarToken(&sx)) {
364 return false;
365 }
366
367 if (!(this->parseSepToken() && this->parseScalarToken(&sy))) {
368 sy = sx;
369 }
370
371 m->setScale(sx, sy);
372 return true;
373 }, matrix);
374}
375
376bool SkSVGAttributeParser::parseRotateToken(SkMatrix* matrix) {
377 return this->parseParenthesized("rotate", [this](SkMatrix* m) -> bool {
378 SkScalar angle;
379 if (!this->parseScalarToken(&angle)) {
380 return false;
381 }
382
383 SkScalar cx = 0;
384 SkScalar cy = 0;
385 // optional [<cx> <cy>]
386 if (this->parseSepToken() && this->parseScalarToken(&cx)) {
387 if (!(this->parseSepToken() && this->parseScalarToken(&cy))) {
388 return false;
389 }
390 }
391
392 m->setRotate(angle, cx, cy);
393 return true;
394 }, matrix);
395}
396
397bool SkSVGAttributeParser::parseSkewXToken(SkMatrix* matrix) {
398 return this->parseParenthesized("skewX", [this](SkMatrix* m) -> bool {
399 SkScalar angle;
400 if (!this->parseScalarToken(&angle)) {
401 return false;
402 }
Tyler Dennistond1e5b032020-04-08 13:02:41 -0400403 m->setSkewX(tanf(SkDegreesToRadians(angle)));
fmalitac97796b2016-08-08 12:58:57 -0700404 return true;
405 }, matrix);
406}
407
408bool SkSVGAttributeParser::parseSkewYToken(SkMatrix* matrix) {
409 return this->parseParenthesized("skewY", [this](SkMatrix* m) -> bool {
410 SkScalar angle;
411 if (!this->parseScalarToken(&angle)) {
412 return false;
413 }
Tyler Dennistond1e5b032020-04-08 13:02:41 -0400414 m->setSkewY(tanf(SkDegreesToRadians(angle)));
fmalitac97796b2016-08-08 12:58:57 -0700415 return true;
416 }, matrix);
417}
418
Florin Malitaf005c252020-04-08 10:10:53 -0400419// https://www.w3.org/TR/SVG11/coords.html#TransformAttribute
fmalitac97796b2016-08-08 12:58:57 -0700420bool SkSVGAttributeParser::parseTransform(SkSVGTransformType* t) {
421 SkMatrix matrix = SkMatrix::I();
422
423 bool parsed = false;
424 while (true) {
425 SkMatrix m;
426
427 if (!( this->parseMatrixToken(&m)
428 || this->parseTranslateToken(&m)
429 || this->parseScaleToken(&m)
430 || this->parseRotateToken(&m)
431 || this->parseSkewXToken(&m)
432 || this->parseSkewYToken(&m))) {
433 break;
434 }
435
436 matrix.preConcat(m);
437 parsed = true;
438 }
439
440 this->parseWSToken();
441 if (!parsed || !this->parseEOSToken()) {
442 return false;
443 }
444
445 *t = SkSVGTransformType(matrix);
446 return true;
447}
fmalita2d961e02016-08-11 09:16:29 -0700448
Florin Malitaf005c252020-04-08 10:10:53 -0400449// https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint
fmalita2d961e02016-08-11 09:16:29 -0700450bool SkSVGAttributeParser::parsePaint(SkSVGPaint* paint) {
451 SkSVGColorType c;
fmalita28d5b722016-09-12 17:06:47 -0700452 SkSVGStringType iri;
fmalita2d961e02016-08-11 09:16:29 -0700453 bool parsedValue = false;
454 if (this->parseColor(&c)) {
455 *paint = SkSVGPaint(c);
456 parsedValue = true;
457 } else if (this->parseExpectedStringToken("none")) {
458 *paint = SkSVGPaint(SkSVGPaint::Type::kNone);
459 parsedValue = true;
460 } else if (this->parseExpectedStringToken("currentColor")) {
461 *paint = SkSVGPaint(SkSVGPaint::Type::kCurrentColor);
462 parsedValue = true;
463 } else if (this->parseExpectedStringToken("inherit")) {
464 *paint = SkSVGPaint(SkSVGPaint::Type::kInherit);
465 parsedValue = true;
fmalita28d5b722016-09-12 17:06:47 -0700466 } else if (this->parseFuncIRI(&iri)) {
Florin Malitaa3626692020-04-09 14:36:45 -0400467 *paint = SkSVGPaint(iri);
fmalita28d5b722016-09-12 17:06:47 -0700468 parsedValue = true;
fmalita2d961e02016-08-11 09:16:29 -0700469 }
470 return parsedValue && this->parseEOSToken();
471}
472
Florin Malitaf005c252020-04-08 10:10:53 -0400473// https://www.w3.org/TR/SVG11/masking.html#ClipPathProperty
Florin Malitace8840e2016-12-08 09:26:47 -0500474bool SkSVGAttributeParser::parseClipPath(SkSVGClip* clip) {
475 SkSVGStringType iri;
476 bool parsedValue = false;
477
478 if (this->parseExpectedStringToken("none")) {
479 *clip = SkSVGClip(SkSVGClip::Type::kNone);
480 parsedValue = true;
481 } else if (this->parseExpectedStringToken("inherit")) {
482 *clip = SkSVGClip(SkSVGClip::Type::kInherit);
483 parsedValue = true;
484 } else if (this->parseFuncIRI(&iri)) {
Florin Malitaa3626692020-04-09 14:36:45 -0400485 *clip = SkSVGClip(iri);
Florin Malitace8840e2016-12-08 09:26:47 -0500486 parsedValue = true;
487 }
488
489 return parsedValue && this->parseEOSToken();
490}
491
Florin Malitaf005c252020-04-08 10:10:53 -0400492// https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty
fmalita2d961e02016-08-11 09:16:29 -0700493bool SkSVGAttributeParser::parseLineCap(SkSVGLineCap* cap) {
494 static const struct {
495 SkSVGLineCap::Type fType;
496 const char* fName;
497 } gCapInfo[] = {
498 { SkSVGLineCap::Type::kButt , "butt" },
499 { SkSVGLineCap::Type::kRound , "round" },
500 { SkSVGLineCap::Type::kSquare , "square" },
501 { SkSVGLineCap::Type::kInherit, "inherit" },
502 };
503
504 bool parsedValue = false;
505 for (size_t i = 0; i < SK_ARRAY_COUNT(gCapInfo); ++i) {
506 if (this->parseExpectedStringToken(gCapInfo[i].fName)) {
507 *cap = SkSVGLineCap(gCapInfo[i].fType);
508 parsedValue = true;
509 break;
510 }
511 }
512
513 return parsedValue && this->parseEOSToken();
514}
515
Florin Malitaf005c252020-04-08 10:10:53 -0400516// https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty
fmalita2d961e02016-08-11 09:16:29 -0700517bool SkSVGAttributeParser::parseLineJoin(SkSVGLineJoin* join) {
518 static const struct {
519 SkSVGLineJoin::Type fType;
520 const char* fName;
521 } gJoinInfo[] = {
522 { SkSVGLineJoin::Type::kMiter , "miter" },
523 { SkSVGLineJoin::Type::kRound , "round" },
524 { SkSVGLineJoin::Type::kBevel , "bevel" },
525 { SkSVGLineJoin::Type::kInherit, "inherit" },
526 };
527
528 bool parsedValue = false;
529 for (size_t i = 0; i < SK_ARRAY_COUNT(gJoinInfo); ++i) {
530 if (this->parseExpectedStringToken(gJoinInfo[i].fName)) {
531 *join = SkSVGLineJoin(gJoinInfo[i].fType);
532 parsedValue = true;
533 break;
534 }
535 }
536
537 return parsedValue && this->parseEOSToken();
538}
fmalita5b31f322016-08-12 12:15:33 -0700539
Florin Malitaf005c252020-04-08 10:10:53 -0400540// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementSpreadMethodAttribute
fmalitacecd6172016-09-13 12:56:11 -0700541bool SkSVGAttributeParser::parseSpreadMethod(SkSVGSpreadMethod* spread) {
542 static const struct {
543 SkSVGSpreadMethod::Type fType;
544 const char* fName;
545 } gSpreadInfo[] = {
546 { SkSVGSpreadMethod::Type::kPad , "pad" },
547 { SkSVGSpreadMethod::Type::kReflect, "reflect" },
548 { SkSVGSpreadMethod::Type::kRepeat , "repeat" },
549 };
550
551 bool parsedValue = false;
552 for (size_t i = 0; i < SK_ARRAY_COUNT(gSpreadInfo); ++i) {
553 if (this->parseExpectedStringToken(gSpreadInfo[i].fName)) {
554 *spread = SkSVGSpreadMethod(gSpreadInfo[i].fType);
555 parsedValue = true;
556 break;
557 }
558 }
559
560 return parsedValue && this->parseEOSToken();
561}
562
Tyler Denniston308c0722020-04-14 10:53:41 -0400563// https://www.w3.org/TR/SVG11/pservers.html#StopElement
564bool SkSVGAttributeParser::parseStopColor(SkSVGStopColor* stopColor) {
565 SkSVGColorType c;
566 bool parsedValue = false;
567 if (this->parseColor(&c)) {
568 *stopColor = SkSVGStopColor(c);
569 parsedValue = true;
570 } else if (this->parseExpectedStringToken("currentColor")) {
571 *stopColor = SkSVGStopColor(SkSVGStopColor::Type::kCurrentColor);
572 parsedValue = true;
573 } else if (this->parseExpectedStringToken("inherit")) {
574 *stopColor = SkSVGStopColor(SkSVGStopColor::Type::kInherit);
575 parsedValue = true;
576 }
577 return parsedValue && this->parseEOSToken();
578}
579
Florin Malitaf005c252020-04-08 10:10:53 -0400580// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
fmalita5b31f322016-08-12 12:15:33 -0700581bool SkSVGAttributeParser::parsePoints(SkSVGPointsType* points) {
582 SkTDArray<SkPoint> pts;
583
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400584 // comma-wsp:
585 // (wsp+ comma? wsp*) | (comma wsp*)
586 const auto parseCommaWsp = [this]() -> bool {
587 const bool wsp = this->parseWSToken();
588 const bool comma = this->parseExpectedStringToken(",");
589 return wsp || comma;
590 };
591
592 // 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
603 if (parsedValue && !parseCommaWsp()) {
604 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
616 if (!parseCommaWsp() && !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}