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