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