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