1 /*
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
8 #include "include/private/base/SkTPin.h"
9 #include "include/utils/SkParse.h"
10 #include "modules/svg/include/SkSVGAttributeParser.h"
11 #include "modules/svg/include/SkSVGTypes.h"
12 #include "src/base/SkUTF.h"
13
14 namespace {
15
16 // TODO: these should be shared with SkParse.cpp
17
is_between(char c,char min,char max)18 inline bool is_between(char c, char min, char max) {
19 SkASSERT(min <= max);
20 return (unsigned)(c - min) <= (unsigned)(max - min);
21 }
22
is_ws(char c)23 inline bool is_ws(char c) {
24 return is_between(c, 1, 32);
25 }
26
is_sep(char c)27 inline bool is_sep(char c) {
28 return is_ws(c) || c == ',' || c == ';';
29 }
30
is_nl(char c)31 inline bool is_nl(char c) {
32 return c == '\n' || c == '\r' || c == '\f';
33 }
34
is_hex(char c)35 inline bool is_hex(char c) {
36 return is_between(c, 'a', 'f') ||
37 is_between(c, 'A', 'F') ||
38 is_between(c, '0', '9');
39 }
40
41 } // namespace
42
SkSVGAttributeParser(const char attributeString[])43 SkSVGAttributeParser::SkSVGAttributeParser(const char attributeString[])
44 // TODO: need actual UTF-8 with length.
45 : fCurPos(attributeString), fEndPos(fCurPos + strlen(attributeString)) {}
46
47 template <typename F>
advanceWhile(F f)48 inline bool SkSVGAttributeParser::advanceWhile(F f) {
49 auto initial = fCurPos;
50 while (fCurPos < fEndPos && f(*fCurPos)) {
51 fCurPos++;
52 }
53 return fCurPos != initial;
54 }
55
matchStringToken(const char * token,const char ** newPos) const56 bool SkSVGAttributeParser::matchStringToken(const char* token, const char** newPos) const {
57 const char* c = fCurPos;
58
59 while (c < fEndPos && *token && *c == *token) {
60 c++;
61 token++;
62 }
63
64 if (*token) {
65 return false;
66 }
67
68 if (newPos) {
69 *newPos = c;
70 }
71
72 return true;
73 }
74
parseEOSToken()75 bool SkSVGAttributeParser::parseEOSToken() {
76 return fCurPos == fEndPos;
77 }
78
parseSepToken()79 bool SkSVGAttributeParser::parseSepToken() {
80 return this->advanceWhile(is_sep);
81 }
82
parseWSToken()83 bool SkSVGAttributeParser::parseWSToken() {
84 return this->advanceWhile(is_ws);
85 }
86
parseCommaWspToken()87 bool SkSVGAttributeParser::parseCommaWspToken() {
88 // comma-wsp:
89 // (wsp+ comma? wsp*) | (comma wsp*)
90 return this->parseWSToken() || this->parseExpectedStringToken(",");
91 }
92
parseExpectedStringToken(const char * expected)93 bool SkSVGAttributeParser::parseExpectedStringToken(const char* expected) {
94 const char* newPos;
95 if (!matchStringToken(expected, &newPos)) {
96 return false;
97 }
98
99 fCurPos = newPos;
100 return true;
101 }
102
parseScalarToken(SkScalar * res)103 bool SkSVGAttributeParser::parseScalarToken(SkScalar* res) {
104 if (const char* next = SkParse::FindScalar(fCurPos, res)) {
105 fCurPos = next;
106 return true;
107 }
108 return false;
109 }
110
parseInt32Token(int32_t * res)111 bool SkSVGAttributeParser::parseInt32Token(int32_t* res) {
112 if (const char* next = SkParse::FindS32(fCurPos, res)) {
113 fCurPos = next;
114 return true;
115 }
116 return false;
117 }
118
matchHexToken(const char ** newPos) const119 bool SkSVGAttributeParser::matchHexToken(const char** newPos) const {
120 *newPos = fCurPos;
121 while (*newPos < fEndPos && is_hex(**newPos)) { ++*newPos; }
122 return *newPos != fCurPos;
123 }
124
parseEscape(SkUnichar * c)125 bool SkSVGAttributeParser::parseEscape(SkUnichar* c) {
126 // \(hexDigit{1,6}whitespace?|[^newline|hexDigit])
127 RestoreCurPos restoreCurPos(this);
128
129 if (!this->parseExpectedStringToken("\\")) {
130 return false;
131 }
132 const char* hexEnd;
133 if (this->matchHexToken(&hexEnd)) {
134 if (hexEnd - fCurPos > 6) {
135 hexEnd = fCurPos + 6;
136 }
137 char hexString[7];
138 size_t hexSize = hexEnd - fCurPos;
139 memcpy(hexString, fCurPos, hexSize);
140 hexString[hexSize] = '\0';
141 uint32_t cp;
142 const char* hexFound = SkParse::FindHex(hexString, &cp);
143 if (!hexFound || cp < 1 || (0xD800 <= cp && cp <= 0xDFFF) || 0x10FFFF < cp) {
144 cp = 0xFFFD;
145 }
146 *c = cp;
147 fCurPos = hexEnd;
148 this->parseWSToken();
149 } else if (this->parseEOSToken() || is_nl(*fCurPos)) {
150 *c = 0xFFFD;
151 return false;
152 } else {
153 if ((*c = SkUTF::NextUTF8(&fCurPos, fEndPos)) < 0) {
154 return false;
155 }
156 }
157
158 restoreCurPos.clear();
159 return true;
160 }
161
parseIdentToken(SkString * ident)162 bool SkSVGAttributeParser::parseIdentToken(SkString* ident) {
163 // <ident-token>
164 // (--|-?([a-z|A-Z|_|non-ASCII]|escape))([a-z|A-Z|0-9|_|-|non-ASCII]|escape)?
165 RestoreCurPos restoreCurPos(this);
166
167 SkUnichar c;
168 if (this->parseExpectedStringToken("--")) {
169 ident->append("--");
170 } else {
171 if (this->parseExpectedStringToken("-")) {
172 ident->append("-");
173 }
174 if (this->parseEscape(&c)) {
175 ident->appendUnichar(c);
176 } else {
177 if ((c = SkUTF::NextUTF8(&fCurPos, fEndPos)) < 0) {
178 return false;
179 }
180 if ((c < 'a' || 'z' < c) &&
181 (c < 'A' || 'Z' < c) &&
182 (c != '_') &&
183 (c < 0x80 || 0x10FFFF < c))
184 {
185 return false;
186 }
187 ident->appendUnichar(c);
188 }
189 }
190 while (fCurPos < fEndPos) {
191 if (this->parseEscape(&c)) {
192 ident->appendUnichar(c);
193 continue;
194 }
195 const char* next = fCurPos;
196 if ((c = SkUTF::NextUTF8(&next, fEndPos)) < 0) {
197 break;
198 }
199 if ((c < 'a' || 'z' < c) &&
200 (c < 'A' || 'Z' < c) &&
201 (c < '0' || '9' < c) &&
202 (c != '_') &&
203 (c != '-') &&
204 (c < 0x80 || 0x10FFFF < c))
205 {
206 break;
207 }
208 ident->appendUnichar(c);
209 fCurPos = next;
210 }
211
212 restoreCurPos.clear();
213 return true;
214 }
215
parseLengthUnitToken(SkSVGLength::Unit * unit)216 bool SkSVGAttributeParser::parseLengthUnitToken(SkSVGLength::Unit* unit) {
217 static const struct {
218 const char* fUnitName;
219 SkSVGLength::Unit fUnit;
220 } gUnitInfo[] = {
221 { "%" , SkSVGLength::Unit::kPercentage },
222 { "em", SkSVGLength::Unit::kEMS },
223 { "ex", SkSVGLength::Unit::kEXS },
224 { "px", SkSVGLength::Unit::kPX },
225 { "cm", SkSVGLength::Unit::kCM },
226 { "mm", SkSVGLength::Unit::kMM },
227 { "in", SkSVGLength::Unit::kIN },
228 { "pt", SkSVGLength::Unit::kPT },
229 { "pc", SkSVGLength::Unit::kPC },
230 };
231
232 for (size_t i = 0; i < std::size(gUnitInfo); ++i) {
233 if (this->parseExpectedStringToken(gUnitInfo[i].fUnitName)) {
234 *unit = gUnitInfo[i].fUnit;
235 return true;
236 }
237 }
238 return false;
239 }
240
241 // https://www.w3.org/TR/SVG11/types.html#DataTypeColor
parseNamedColorToken(SkColor * c)242 bool SkSVGAttributeParser::parseNamedColorToken(SkColor* c) {
243 RestoreCurPos restoreCurPos(this);
244
245 SkString ident;
246 if (!this->parseIdentToken(&ident)) {
247 return false;
248 }
249 if (!SkParse::FindNamedColor(ident.c_str(), ident.size(), c)) {
250 return false;
251 }
252
253 restoreCurPos.clear();
254 return true;
255 }
256
parseHexColorToken(SkColor * c)257 bool SkSVGAttributeParser::parseHexColorToken(SkColor* c) {
258 RestoreCurPos restoreCurPos(this);
259
260 const char* hexEnd;
261 if (!this->parseExpectedStringToken("#") || !this->matchHexToken(&hexEnd)) {
262 return false;
263 }
264
265 uint32_t v;
266 SkString hexString(fCurPos, hexEnd - fCurPos);
267 SkParse::FindHex(hexString.c_str(), &v);
268
269 switch (hexString.size()) {
270 case 6:
271 // matched #xxxxxxx
272 break;
273 case 3:
274 // matched '#xxx;
275 v = ((v << 12) & 0x00f00000) |
276 ((v << 8) & 0x000ff000) |
277 ((v << 4) & 0x00000ff0) |
278 ((v << 0) & 0x0000000f);
279 break;
280 default:
281 return false;
282 }
283
284 *c = v | 0xff000000;
285 fCurPos = hexEnd;
286
287 restoreCurPos.clear();
288 return true;
289 }
290
parseColorComponentToken(int32_t * c)291 bool SkSVGAttributeParser::parseColorComponentToken(int32_t* c) {
292 const auto parseIntegral = [this](int32_t* c) -> bool {
293 const char* p = SkParse::FindS32(fCurPos, c);
294 if (!p || *p == '.') {
295 // No value parsed, or fractional value.
296 return false;
297 }
298
299 if (*p == '%') {
300 *c = SkScalarRoundToInt(*c * 255.0f / 100);
301 p++;
302 }
303
304 fCurPos = p;
305 return true;
306 };
307
308 const auto parseFractional = [this](int32_t* c) -> bool {
309 SkScalar s;
310 const char* p = SkParse::FindScalar(fCurPos, &s);
311 if (!p || *p != '%') {
312 // Floating point must be a percentage (CSS2 rgb-percent syntax).
313 return false;
314 }
315 p++; // Skip '%'
316
317 *c = SkScalarRoundToInt(s * 255.0f / 100);
318 fCurPos = p;
319 return true;
320 };
321
322 if (!parseIntegral(c) && !parseFractional(c)) {
323 return false;
324 }
325
326 *c = SkTPin<int32_t>(*c, 0, 255);
327 return true;
328 }
329
parseRGBColorToken(SkColor * c)330 bool SkSVGAttributeParser::parseRGBColorToken(SkColor* c) {
331 return this->parseParenthesized("rgb", [this](SkColor* c) -> bool {
332 int32_t r, g, b;
333 if (this->parseColorComponentToken(&r) &&
334 this->parseSepToken() &&
335 this->parseColorComponentToken(&g) &&
336 this->parseSepToken() &&
337 this->parseColorComponentToken(&b)) {
338
339 *c = SkColorSetRGB(static_cast<uint8_t>(r),
340 static_cast<uint8_t>(g),
341 static_cast<uint8_t>(b));
342 return true;
343 }
344 return false;
345 }, c);
346 }
347
parseColorToken(SkColor * c)348 bool SkSVGAttributeParser::parseColorToken(SkColor* c) {
349 return this->parseHexColorToken(c) ||
350 this->parseNamedColorToken(c) ||
351 this->parseRGBColorToken(c);
352 }
353
parseSVGColorType(SkSVGColorType * color)354 bool SkSVGAttributeParser::parseSVGColorType(SkSVGColorType* color) {
355 SkColor c;
356 if (!this->parseColorToken(&c)) {
357 return false;
358 }
359 *color = SkSVGColorType(c);
360 return true;
361 }
362
363 // https://www.w3.org/TR/SVG11/types.html#DataTypeColor
364 // And https://www.w3.org/TR/CSS2/syndata.html#color-units for the alternative
365 // forms supported by SVG (e.g. RGB percentages).
366 template <>
parse(SkSVGColorType * color)367 bool SkSVGAttributeParser::parse(SkSVGColorType* color) {
368 this->parseWSToken();
369 if (!this->parseSVGColorType(color)) {
370 return false;
371 }
372 this->parseWSToken();
373 return this->parseEOSToken();
374 }
375
parseSVGColor(SkSVGColor * color,SkSVGColor::Vars && vars)376 bool SkSVGAttributeParser::parseSVGColor(SkSVGColor* color, SkSVGColor::Vars&& vars) {
377 static const constexpr int kVarsLimit = 32;
378
379 if (SkSVGColorType c; this->parseSVGColorType(&c)) {
380 *color = SkSVGColor(c, std::move(vars));
381 return true;
382 }
383 if (this->parseExpectedStringToken("currentColor")) {
384 *color = SkSVGColor(SkSVGColor::Type::kCurrentColor, std::move(vars));
385 return true;
386 }
387 // https://drafts.csswg.org/css-variables/#using-variables
388 if (this->parseParenthesized("var", [this, &vars](SkSVGColor* colorResult) -> bool {
389 SkString ident;
390 if (!this->parseIdentToken(&ident) || ident.size() < 2 || !ident.startsWith("--")) {
391 return false;
392 }
393 ident.remove(0, 2);
394 vars.push_back(std::move(ident));
395 this->parseWSToken();
396 if (!this->parseExpectedStringToken(",")) {
397 *colorResult = SkSVGColor(SK_ColorBLACK, std::move(vars));
398 return true;
399 }
400 this->parseWSToken();
401 if (this->matchStringToken(")")) {
402 *colorResult = SkSVGColor(SK_ColorBLACK, std::move(vars));
403 return true;
404 }
405 return vars.size() < kVarsLimit && this->parseSVGColor(colorResult, std::move(vars));
406 }, color))
407 {
408 return true;
409 }
410 return false;
411 }
412
413 // https://www.w3.org/TR/SVG11/types.html#InterfaceSVGColor
414 template <>
parse(SkSVGColor * color)415 bool SkSVGAttributeParser::parse(SkSVGColor* color) {
416 this->parseWSToken();
417 if (!this->parseSVGColor(color, SkSVGColor::Vars())) {
418 return false;
419 }
420 this->parseWSToken();
421 return this->parseEOSToken();
422 }
423
424 // https://www.w3.org/TR/SVG11/linking.html#IRIReference
425 template <>
parse(SkSVGIRI * iri)426 bool SkSVGAttributeParser::parse(SkSVGIRI* iri) {
427 // consume preceding whitespace
428 this->parseWSToken();
429
430 SkSVGIRI::Type iriType;
431 if (this->parseExpectedStringToken("#")) {
432 iriType = SkSVGIRI::Type::kLocal;
433 } else if (this->matchStringToken("data:")) {
434 iriType = SkSVGIRI::Type::kDataURI;
435 } else {
436 iriType = SkSVGIRI::Type::kNonlocal;
437 }
438
439 const auto* start = fCurPos;
440 if (!this->advanceWhile([](char c) -> bool { return c != ')'; })) {
441 return false;
442 }
443 *iri = SkSVGIRI(iriType, SkString(start, fCurPos - start));
444 return true;
445 }
446
447 // https://www.w3.org/TR/SVG11/types.html#DataTypeFuncIRI
parseFuncIRI(SkSVGFuncIRI * iri)448 bool SkSVGAttributeParser::parseFuncIRI(SkSVGFuncIRI* iri) {
449 return this->parseParenthesized("url", [this](SkSVGFuncIRI* iriResult) -> bool {
450 SkSVGIRI iri;
451 if (this->parse(&iri)) {
452 *iriResult = SkSVGFuncIRI(std::move(iri));
453 return true;
454 }
455 return false;
456 }, iri);
457 }
458
459 template <>
parse(SkSVGStringType * result)460 bool SkSVGAttributeParser::parse(SkSVGStringType* result) {
461 if (this->parseEOSToken()) {
462 return false;
463 }
464 *result = SkSVGStringType(fCurPos);
465 fCurPos += result->size();
466 return this->parseEOSToken();
467 }
468
469 // https://www.w3.org/TR/SVG11/types.html#DataTypeNumber
470 template <>
parse(SkSVGNumberType * number)471 bool SkSVGAttributeParser::parse(SkSVGNumberType* number) {
472 // consume WS
473 this->parseWSToken();
474
475 SkScalar s;
476 if (this->parseScalarToken(&s)) {
477 *number = SkSVGNumberType(s);
478 // consume trailing separators
479 this->parseSepToken();
480 return true;
481 }
482
483 return false;
484 }
485
486 // https://www.w3.org/TR/SVG11/types.html#DataTypeInteger
parseInteger(SkSVGIntegerType * number)487 bool SkSVGAttributeParser::parseInteger(SkSVGIntegerType* number) {
488 // consume WS
489 this->parseWSToken();
490
491 // consume optional '+'
492 this->parseExpectedStringToken("+");
493
494 SkSVGIntegerType i;
495 if (this->parseInt32Token(&i)) {
496 *number = SkSVGNumberType(i);
497 // consume trailing separators
498 this->parseSepToken();
499 return true;
500 }
501
502 return false;
503 }
504
505 // https://www.w3.org/TR/SVG11/types.html#DataTypeLength
506 template <>
parse(SkSVGLength * length)507 bool SkSVGAttributeParser::parse(SkSVGLength* length) {
508 SkScalar s;
509 SkSVGLength::Unit u = SkSVGLength::Unit::kNumber;
510
511 if (this->parseScalarToken(&s) &&
512 (this->parseLengthUnitToken(&u) || this->parseSepToken() || this->parseEOSToken())) {
513 *length = SkSVGLength(s, u);
514 // consume trailing separators
515 this->parseSepToken();
516 return true;
517 }
518
519 return false;
520 }
521
522 // https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute
parseViewBox(SkSVGViewBoxType * vb)523 bool SkSVGAttributeParser::parseViewBox(SkSVGViewBoxType* vb) {
524 SkScalar x, y, w, h;
525 this->parseWSToken();
526
527 bool parsedValue = false;
528 if (this->parseScalarToken(&x) && this->parseSepToken() &&
529 this->parseScalarToken(&y) && this->parseSepToken() &&
530 this->parseScalarToken(&w) && this->parseSepToken() &&
531 this->parseScalarToken(&h)) {
532
533 *vb = SkSVGViewBoxType(SkRect::MakeXYWH(x, y, w, h));
534 parsedValue = true;
535 // consume trailing whitespace
536 this->parseWSToken();
537 }
538 return parsedValue && this->parseEOSToken();
539 }
540
541 template <typename Func, typename T>
parseParenthesized(const char * prefix,Func f,T * result)542 bool SkSVGAttributeParser::parseParenthesized(const char* prefix, Func f, T* result) {
543 RestoreCurPos restoreCurPos(this);
544
545 this->parseWSToken();
546 if (prefix && !this->parseExpectedStringToken(prefix)) {
547 return false;
548 }
549 this->parseWSToken();
550 if (!this->parseExpectedStringToken("(")) {
551 return false;
552 }
553 this->parseWSToken();
554
555 if (!f(result)) {
556 return false;
557 }
558
559 this->parseWSToken();
560 if (!this->parseExpectedStringToken(")")) {
561 return false;
562 }
563
564 restoreCurPos.clear();
565 return true;
566 }
567
parseMatrixToken(SkMatrix * matrix)568 bool SkSVGAttributeParser::parseMatrixToken(SkMatrix* matrix) {
569 return this->parseParenthesized("matrix", [this](SkMatrix* m) -> bool {
570 SkScalar scalars[6];
571 for (int i = 0; i < 6; ++i) {
572 if (!(this->parseScalarToken(scalars + i) &&
573 (i > 4 || this->parseSepToken()))) {
574 return false;
575 }
576 }
577
578 m->setAll(scalars[0], scalars[2], scalars[4], scalars[1], scalars[3], scalars[5], 0, 0, 1);
579 return true;
580 }, matrix);
581 }
582
parseTranslateToken(SkMatrix * matrix)583 bool SkSVGAttributeParser::parseTranslateToken(SkMatrix* matrix) {
584 return this->parseParenthesized("translate", [this](SkMatrix* m) -> bool {
585 SkScalar tx = 0.0, ty = 0.0;
586 this->parseWSToken();
587 if (!this->parseScalarToken(&tx)) {
588 return false;
589 }
590
591 if (!this->parseSepToken() || !this->parseScalarToken(&ty)) {
592 ty = 0.0;
593 }
594
595 m->setTranslate(tx, ty);
596 return true;
597 }, matrix);
598 }
599
parseScaleToken(SkMatrix * matrix)600 bool SkSVGAttributeParser::parseScaleToken(SkMatrix* matrix) {
601 return this->parseParenthesized("scale", [this](SkMatrix* m) -> bool {
602 SkScalar sx = 0.0, sy = 0.0;
603 if (!this->parseScalarToken(&sx)) {
604 return false;
605 }
606
607 if (!(this->parseSepToken() && this->parseScalarToken(&sy))) {
608 sy = sx;
609 }
610
611 m->setScale(sx, sy);
612 return true;
613 }, matrix);
614 }
615
parseRotateToken(SkMatrix * matrix)616 bool SkSVGAttributeParser::parseRotateToken(SkMatrix* matrix) {
617 return this->parseParenthesized("rotate", [this](SkMatrix* m) -> bool {
618 SkScalar angle;
619 if (!this->parseScalarToken(&angle)) {
620 return false;
621 }
622
623 SkScalar cx = 0;
624 SkScalar cy = 0;
625 // optional [<cx> <cy>]
626 if (this->parseSepToken() && this->parseScalarToken(&cx)) {
627 if (!(this->parseSepToken() && this->parseScalarToken(&cy))) {
628 return false;
629 }
630 }
631
632 m->setRotate(angle, cx, cy);
633 return true;
634 }, matrix);
635 }
636
parseSkewXToken(SkMatrix * matrix)637 bool SkSVGAttributeParser::parseSkewXToken(SkMatrix* matrix) {
638 return this->parseParenthesized("skewX", [this](SkMatrix* m) -> bool {
639 SkScalar angle;
640 if (!this->parseScalarToken(&angle)) {
641 return false;
642 }
643 m->setSkewX(tanf(SkDegreesToRadians(angle)));
644 return true;
645 }, matrix);
646 }
647
parseSkewYToken(SkMatrix * matrix)648 bool SkSVGAttributeParser::parseSkewYToken(SkMatrix* matrix) {
649 return this->parseParenthesized("skewY", [this](SkMatrix* m) -> bool {
650 SkScalar angle;
651 if (!this->parseScalarToken(&angle)) {
652 return false;
653 }
654 m->setSkewY(tanf(SkDegreesToRadians(angle)));
655 return true;
656 }, matrix);
657 }
658
659 // https://www.w3.org/TR/SVG11/coords.html#TransformAttribute
660 template <>
parse(SkSVGTransformType * t)661 bool SkSVGAttributeParser::parse(SkSVGTransformType* t) {
662 SkMatrix matrix = SkMatrix::I();
663
664 bool parsed = false;
665 while (true) {
666 SkMatrix m;
667
668 if (!( this->parseMatrixToken(&m)
669 || this->parseTranslateToken(&m)
670 || this->parseScaleToken(&m)
671 || this->parseRotateToken(&m)
672 || this->parseSkewXToken(&m)
673 || this->parseSkewYToken(&m))) {
674 break;
675 }
676
677 matrix.preConcat(m);
678 parsed = true;
679
680 this->parseCommaWspToken();
681 }
682
683 this->parseWSToken();
684 if (!parsed || !this->parseEOSToken()) {
685 return false;
686 }
687
688 *t = SkSVGTransformType(matrix);
689 return true;
690 }
691
692 // https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint
693 template <>
parse(SkSVGPaint * paint)694 bool SkSVGAttributeParser::parse(SkSVGPaint* paint) {
695 SkSVGColor c;
696 SkSVGFuncIRI iri;
697 bool parsedValue = false;
698
699 this->parseWSToken();
700 if (this->parseSVGColor(&c, SkSVGColor::Vars())) {
701 *paint = SkSVGPaint(std::move(c));
702 parsedValue = true;
703 } else if (this->parseExpectedStringToken("none")) {
704 *paint = SkSVGPaint(SkSVGPaint::Type::kNone);
705 parsedValue = true;
706 } else if (this->parseFuncIRI(&iri)) {
707 // optional fallback color
708 this->parseWSToken();
709 this->parseSVGColor(&c, SkSVGColor::Vars());
710 *paint = SkSVGPaint(iri.iri(), std::move(c));
711 parsedValue = true;
712 }
713 this->parseWSToken();
714 return parsedValue && this->parseEOSToken();
715 }
716
717 // https://www.w3.org/TR/SVG11/masking.html#ClipPathProperty
718 // https://www.w3.org/TR/SVG11/masking.html#MaskProperty
719 // https://www.w3.org/TR/SVG11/filters.html#FilterProperty
720 template <>
parse(SkSVGFuncIRI * firi)721 bool SkSVGAttributeParser::parse(SkSVGFuncIRI* firi) {
722 SkSVGStringType iri;
723 bool parsedValue = false;
724
725 if (this->parseExpectedStringToken("none")) {
726 *firi = SkSVGFuncIRI();
727 parsedValue = true;
728 } else if (this->parseFuncIRI(firi)) {
729 parsedValue = true;
730 }
731
732 return parsedValue && this->parseEOSToken();
733 }
734
735 // https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty
736 template <>
parse(SkSVGLineCap * cap)737 bool SkSVGAttributeParser::parse(SkSVGLineCap* cap) {
738 static const struct {
739 SkSVGLineCap fType;
740 const char* fName;
741 } gCapInfo[] = {
742 { SkSVGLineCap::kButt , "butt" },
743 { SkSVGLineCap::kRound , "round" },
744 { SkSVGLineCap::kSquare , "square" },
745 };
746
747 bool parsedValue = false;
748 for (size_t i = 0; i < std::size(gCapInfo); ++i) {
749 if (this->parseExpectedStringToken(gCapInfo[i].fName)) {
750 *cap = SkSVGLineCap(gCapInfo[i].fType);
751 parsedValue = true;
752 break;
753 }
754 }
755
756 return parsedValue && this->parseEOSToken();
757 }
758
759 // https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty
760 template <>
parse(SkSVGLineJoin * join)761 bool SkSVGAttributeParser::parse(SkSVGLineJoin* join) {
762 static const struct {
763 SkSVGLineJoin::Type fType;
764 const char* fName;
765 } gJoinInfo[] = {
766 { SkSVGLineJoin::Type::kMiter , "miter" },
767 { SkSVGLineJoin::Type::kRound , "round" },
768 { SkSVGLineJoin::Type::kBevel , "bevel" },
769 { SkSVGLineJoin::Type::kInherit, "inherit" },
770 };
771
772 bool parsedValue = false;
773 for (size_t i = 0; i < std::size(gJoinInfo); ++i) {
774 if (this->parseExpectedStringToken(gJoinInfo[i].fName)) {
775 *join = SkSVGLineJoin(gJoinInfo[i].fType);
776 parsedValue = true;
777 break;
778 }
779 }
780
781 return parsedValue && this->parseEOSToken();
782 }
783
784 // https://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBoxUnits
785 template <>
parse(SkSVGObjectBoundingBoxUnits * objectBoundingBoxUnits)786 bool SkSVGAttributeParser::parse(SkSVGObjectBoundingBoxUnits* objectBoundingBoxUnits) {
787 bool parsedValue = false;
788 if (this->parseExpectedStringToken("userSpaceOnUse")) {
789 *objectBoundingBoxUnits =
790 SkSVGObjectBoundingBoxUnits(SkSVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse);
791 parsedValue = true;
792 } else if (this->parseExpectedStringToken("objectBoundingBox")) {
793 *objectBoundingBoxUnits =
794 SkSVGObjectBoundingBoxUnits(SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox);
795 parsedValue = true;
796 }
797 return parsedValue && this->parseEOSToken();
798 }
799
800 // https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
801 template <>
parse(SkSVGPointsType * points)802 bool SkSVGAttributeParser::parse(SkSVGPointsType* points) {
803 SkTDArray<SkPoint> pts;
804
805 // Skip initial wsp.
806 // list-of-points:
807 // wsp* coordinate-pairs? wsp*
808 this->advanceWhile(is_ws);
809
810 bool parsedValue = false;
811 for (;;) {
812 // Adjacent coordinate-pairs separated by comma-wsp.
813 // coordinate-pairs:
814 // coordinate-pair
815 // | coordinate-pair comma-wsp coordinate-pairs
816 if (parsedValue && !this->parseCommaWspToken()) {
817 break;
818 }
819
820 SkScalar x, y;
821 if (!this->parseScalarToken(&x)) {
822 break;
823 }
824
825 // Coordinate values separated by comma-wsp or '-'.
826 // coordinate-pair:
827 // coordinate comma-wsp coordinate
828 // | coordinate negative-coordinate
829 if (!this->parseCommaWspToken() && !this->parseEOSToken() && *fCurPos != '-') {
830 break;
831 }
832
833 if (!this->parseScalarToken(&y)) {
834 break;
835 }
836
837 pts.push_back(SkPoint::Make(x, y));
838 parsedValue = true;
839 }
840
841 if (parsedValue && this->parseEOSToken()) {
842 *points = pts;
843 return true;
844 }
845
846 return false;
847 }
848
849 // https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty
850 template <>
parse(SkSVGFillRule * fillRule)851 bool SkSVGAttributeParser::parse(SkSVGFillRule* fillRule) {
852 static const struct {
853 SkSVGFillRule::Type fType;
854 const char* fName;
855 } gFillRuleInfo[] = {
856 { SkSVGFillRule::Type::kNonZero, "nonzero" },
857 { SkSVGFillRule::Type::kEvenOdd, "evenodd" },
858 { SkSVGFillRule::Type::kInherit, "inherit" },
859 };
860
861 bool parsedValue = false;
862 for (size_t i = 0; i < std::size(gFillRuleInfo); ++i) {
863 if (this->parseExpectedStringToken(gFillRuleInfo[i].fName)) {
864 *fillRule = SkSVGFillRule(gFillRuleInfo[i].fType);
865 parsedValue = true;
866 break;
867 }
868 }
869
870 return parsedValue && this->parseEOSToken();
871 }
872
873 // https://www.w3.org/TR/SVG11/painting.html#VisibilityProperty
874 template <>
parse(SkSVGVisibility * visibility)875 bool SkSVGAttributeParser::parse(SkSVGVisibility* visibility) {
876 static const struct {
877 SkSVGVisibility::Type fType;
878 const char* fName;
879 } gVisibilityInfo[] = {
880 { SkSVGVisibility::Type::kVisible , "visible" },
881 { SkSVGVisibility::Type::kHidden , "hidden" },
882 { SkSVGVisibility::Type::kCollapse, "collapse" },
883 { SkSVGVisibility::Type::kInherit , "inherit" },
884 };
885
886 bool parsedValue = false;
887 for (const auto& parseInfo : gVisibilityInfo) {
888 if (this->parseExpectedStringToken(parseInfo.fName)) {
889 *visibility = SkSVGVisibility(parseInfo.fType);
890 parsedValue = true;
891 break;
892 }
893 }
894
895 return parsedValue && this->parseEOSToken();
896 }
897
898 // https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty
899 template <>
parse(SkSVGDashArray * dashArray)900 bool SkSVGAttributeParser::parse(SkSVGDashArray* dashArray) {
901 bool parsedValue = false;
902 if (this->parseExpectedStringToken("none")) {
903 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kNone);
904 parsedValue = true;
905 } else if (this->parseExpectedStringToken("inherit")) {
906 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kInherit);
907 parsedValue = true;
908 } else {
909 SkTDArray<SkSVGLength> dashes;
910 for (;;) {
911 SkSVGLength dash;
912 // parseLength() also consumes trailing separators.
913 if (!this->parse(&dash)) {
914 break;
915 }
916
917 dashes.push_back(dash);
918 parsedValue = true;
919 }
920
921 if (parsedValue) {
922 *dashArray = SkSVGDashArray(std::move(dashes));
923 }
924 }
925
926 return parsedValue && this->parseEOSToken();
927 }
928
929 // https://www.w3.org/TR/SVG11/text.html#FontFamilyProperty
930 template <>
parse(SkSVGFontFamily * family)931 bool SkSVGAttributeParser::parse(SkSVGFontFamily* family) {
932 bool parsedValue = false;
933 if (this->parseExpectedStringToken("inherit")) {
934 *family = SkSVGFontFamily();
935 parsedValue = true;
936 } else {
937 // The spec allows specifying a comma-separated list for explicit fallback order.
938 // For now, we only use the first entry and rely on the font manager to handle fallback.
939 const auto* comma = strchr(fCurPos, ',');
940 auto family_name = comma ? SkString(fCurPos, comma - fCurPos)
941 : SkString(fCurPos);
942 *family = SkSVGFontFamily(family_name.c_str());
943 fCurPos += strlen(fCurPos);
944 parsedValue = true;
945 }
946
947 return parsedValue && this->parseEOSToken();
948 }
949
950 // https://www.w3.org/TR/SVG11/text.html#FontSizeProperty
951 template <>
parse(SkSVGFontSize * size)952 bool SkSVGAttributeParser::parse(SkSVGFontSize* size) {
953 bool parsedValue = false;
954 if (this->parseExpectedStringToken("inherit")) {
955 *size = SkSVGFontSize();
956 parsedValue = true;
957 } else {
958 SkSVGLength length;
959 if (this->parse(&length)) {
960 *size = SkSVGFontSize(length);
961 parsedValue = true;
962 }
963 }
964
965 return parsedValue && this->parseEOSToken();
966 }
967
968 // https://www.w3.org/TR/SVG11/text.html#FontStyleProperty
969 template <>
parse(SkSVGFontStyle * style)970 bool SkSVGAttributeParser::parse(SkSVGFontStyle* style) {
971 static constexpr std::tuple<const char*, SkSVGFontStyle::Type> gStyleMap[] = {
972 { "normal" , SkSVGFontStyle::Type::kNormal },
973 { "italic" , SkSVGFontStyle::Type::kItalic },
974 { "oblique", SkSVGFontStyle::Type::kOblique },
975 { "inherit", SkSVGFontStyle::Type::kInherit },
976 };
977
978 bool parsedValue = false;
979 SkSVGFontStyle::Type type;
980
981 if (this->parseEnumMap(gStyleMap, &type)) {
982 *style = SkSVGFontStyle(type);
983 parsedValue = true;
984 }
985
986 return parsedValue && this->parseEOSToken();
987 }
988
989 // https://www.w3.org/TR/SVG11/text.html#FontWeightProperty
990 template <>
parse(SkSVGFontWeight * weight)991 bool SkSVGAttributeParser::parse(SkSVGFontWeight* weight) {
992 static constexpr std::tuple<const char*, SkSVGFontWeight::Type> gWeightMap[] = {
993 { "normal" , SkSVGFontWeight::Type::kNormal },
994 { "bold" , SkSVGFontWeight::Type::kBold },
995 { "bolder" , SkSVGFontWeight::Type::kBolder },
996 { "lighter", SkSVGFontWeight::Type::kLighter },
997 { "100" , SkSVGFontWeight::Type::k100 },
998 { "200" , SkSVGFontWeight::Type::k200 },
999 { "300" , SkSVGFontWeight::Type::k300 },
1000 { "400" , SkSVGFontWeight::Type::k400 },
1001 { "500" , SkSVGFontWeight::Type::k500 },
1002 { "600" , SkSVGFontWeight::Type::k600 },
1003 { "700" , SkSVGFontWeight::Type::k700 },
1004 { "800" , SkSVGFontWeight::Type::k800 },
1005 { "900" , SkSVGFontWeight::Type::k900 },
1006 { "inherit", SkSVGFontWeight::Type::kInherit },
1007 };
1008
1009 bool parsedValue = false;
1010 SkSVGFontWeight::Type type;
1011
1012 if (this->parseEnumMap(gWeightMap, &type)) {
1013 *weight = SkSVGFontWeight(type);
1014 parsedValue = true;
1015 }
1016
1017 return parsedValue && this->parseEOSToken();
1018 }
1019
1020 // https://www.w3.org/TR/SVG11/text.html#TextAnchorProperty
1021 template <>
parse(SkSVGTextAnchor * anchor)1022 bool SkSVGAttributeParser::parse(SkSVGTextAnchor* anchor) {
1023 static constexpr std::tuple<const char*, SkSVGTextAnchor::Type> gAnchorMap[] = {
1024 { "start" , SkSVGTextAnchor::Type::kStart },
1025 { "middle" , SkSVGTextAnchor::Type::kMiddle },
1026 { "end" , SkSVGTextAnchor::Type::kEnd },
1027 { "inherit", SkSVGTextAnchor::Type::kInherit},
1028 };
1029
1030 bool parsedValue = false;
1031 SkSVGTextAnchor::Type type;
1032
1033 if (this->parseEnumMap(gAnchorMap, &type)) {
1034 *anchor = SkSVGTextAnchor(type);
1035 parsedValue = true;
1036 }
1037
1038 return parsedValue && this->parseEOSToken();
1039 }
1040
1041 // https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
parsePreserveAspectRatio(SkSVGPreserveAspectRatio * par)1042 bool SkSVGAttributeParser::parsePreserveAspectRatio(SkSVGPreserveAspectRatio* par) {
1043 static constexpr std::tuple<const char*, SkSVGPreserveAspectRatio::Align> gAlignMap[] = {
1044 { "none" , SkSVGPreserveAspectRatio::kNone },
1045 { "xMinYMin", SkSVGPreserveAspectRatio::kXMinYMin },
1046 { "xMidYMin", SkSVGPreserveAspectRatio::kXMidYMin },
1047 { "xMaxYMin", SkSVGPreserveAspectRatio::kXMaxYMin },
1048 { "xMinYMid", SkSVGPreserveAspectRatio::kXMinYMid },
1049 { "xMidYMid", SkSVGPreserveAspectRatio::kXMidYMid },
1050 { "xMaxYMid", SkSVGPreserveAspectRatio::kXMaxYMid },
1051 { "xMinYMax", SkSVGPreserveAspectRatio::kXMinYMax },
1052 { "xMidYMax", SkSVGPreserveAspectRatio::kXMidYMax },
1053 { "xMaxYMax", SkSVGPreserveAspectRatio::kXMaxYMax },
1054 };
1055
1056 static constexpr std::tuple<const char*, SkSVGPreserveAspectRatio::Scale> gScaleMap[] = {
1057 { "meet" , SkSVGPreserveAspectRatio::kMeet },
1058 { "slice", SkSVGPreserveAspectRatio::kSlice },
1059 };
1060
1061 bool parsedValue = false;
1062
1063 // ignoring optional 'defer'
1064 this->parseExpectedStringToken("defer");
1065 this->parseWSToken();
1066
1067 if (this->parseEnumMap(gAlignMap, &par->fAlign)) {
1068 parsedValue = true;
1069
1070 // optional scaling selector
1071 this->parseWSToken();
1072 this->parseEnumMap(gScaleMap, &par->fScale);
1073 }
1074
1075 return parsedValue && this->parseEOSToken();
1076 }
1077
1078 template <>
parse(SkSVGPreserveAspectRatio * par)1079 bool SkSVGAttributeParser::parse(SkSVGPreserveAspectRatio* par) {
1080 return this->parsePreserveAspectRatio(par);
1081 }
1082
1083 // https://www.w3.org/TR/SVG11/types.html#DataTypeCoordinates
1084 template <typename T>
parseList(std::vector<T> * vals)1085 bool SkSVGAttributeParser::parseList(std::vector<T>* vals) {
1086 SkASSERT(vals->empty());
1087
1088 T v;
1089 for (;;) {
1090 if (!this->parse(&v)) {
1091 break;
1092 }
1093
1094 vals->push_back(v);
1095
1096 this->parseCommaWspToken();
1097 }
1098
1099 return !vals->empty() && this->parseEOSToken();
1100 }
1101
1102 template <>
parse(std::vector<SkSVGLength> * lengths)1103 bool SkSVGAttributeParser::parse(std::vector<SkSVGLength>* lengths) {
1104 return this->parseList(lengths);
1105 }
1106
1107 template <>
parse(std::vector<SkSVGNumberType> * numbers)1108 bool SkSVGAttributeParser::parse(std::vector<SkSVGNumberType>* numbers) {
1109 return this->parseList(numbers);
1110 }
1111
1112 template <>
parse(SkSVGColorspace * colorspace)1113 bool SkSVGAttributeParser::parse(SkSVGColorspace* colorspace) {
1114 static constexpr std::tuple<const char*, SkSVGColorspace> gColorspaceMap[] = {
1115 { "auto" , SkSVGColorspace::kAuto },
1116 { "sRGB" , SkSVGColorspace::kSRGB },
1117 { "linearRGB", SkSVGColorspace::kLinearRGB },
1118 };
1119
1120 return this->parseEnumMap(gColorspaceMap, colorspace) && this->parseEOSToken();
1121 }
1122
1123 // https://www.w3.org/TR/SVG11/painting.html#DisplayProperty
1124 template <>
parse(SkSVGDisplay * display)1125 bool SkSVGAttributeParser::parse(SkSVGDisplay* display) {
1126 static const struct {
1127 SkSVGDisplay fType;
1128 const char* fName;
1129 } gDisplayInfo[] = {
1130 { SkSVGDisplay::kInline, "inline" },
1131 { SkSVGDisplay::kNone , "none" },
1132 };
1133
1134 bool parsedValue = false;
1135 for (const auto& parseInfo : gDisplayInfo) {
1136 if (this->parseExpectedStringToken(parseInfo.fName)) {
1137 *display = SkSVGDisplay(parseInfo.fType);
1138 parsedValue = true;
1139 break;
1140 }
1141 }
1142
1143 return parsedValue && this->parseEOSToken();
1144 }
1145