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