• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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