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