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