Flutter Engine
The Flutter Engine
SkSVGAttributeParser.cpp
Go to the documentation of this file.
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
12#include "src/base/SkUTF.h"
13
14namespace {
15
16// TODO: these should be shared with SkParse.cpp
17
18inline bool is_between(char c, char min, char max) {
19 SkASSERT(min <= max);
20 return (unsigned)(c - min) <= (unsigned)(max - min);
21}
22
23inline bool is_ws(char c) {
24 return is_between(c, 1, 32);
25}
26
27inline bool is_sep(char c) {
28 return is_ws(c) || c == ',' || c == ';';
29}
30
31inline bool is_nl(char c) {
32 return c == '\n' || c == '\r' || c == '\f';
33}
34
35inline 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
43SkSVGAttributeParser::SkSVGAttributeParser(const char attributeString[])
44 // TODO: need actual UTF-8 with length.
45 : fCurPos(attributeString), fEndPos(fCurPos + strlen(attributeString)) {}
46
47template <typename F>
48inline bool SkSVGAttributeParser::advanceWhile(F f) {
49 auto initial = fCurPos;
50 while (fCurPos < fEndPos && f(*fCurPos)) {
51 fCurPos++;
52 }
53 return fCurPos != initial;
54}
55
56bool 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
75bool SkSVGAttributeParser::parseEOSToken() {
76 return fCurPos == fEndPos;
77}
78
79bool SkSVGAttributeParser::parseSepToken() {
80 return this->advanceWhile(is_sep);
81}
82
83bool SkSVGAttributeParser::parseWSToken() {
84 return this->advanceWhile(is_ws);
85}
86
87bool SkSVGAttributeParser::parseCommaWspToken() {
88 // comma-wsp:
89 // (wsp+ comma? wsp*) | (comma wsp*)
90 return this->parseWSToken() || this->parseExpectedStringToken(",");
91}
92
93bool 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
103bool 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
111bool 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
119bool SkSVGAttributeParser::matchHexToken(const char** newPos) const {
120 *newPos = fCurPos;
121 while (*newPos < fEndPos && is_hex(**newPos)) { ++*newPos; }
122 return *newPos != fCurPos;
123}
124
125bool 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
162bool 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
216bool SkSVGAttributeParser::parseLengthUnitToken(SkSVGLength::Unit* unit) {
217 static const struct {
218 const char* fUnitName;
219 SkSVGLength::Unit fUnit;
220 } gUnitInfo[] = {
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
242bool 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
257bool 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
291bool 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
308bool 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
323bool 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
334bool SkSVGAttributeParser::parseColorComponentToken(int32_t* c) {
335 return parseColorComponentIntegralToken(c) ||
336 parseColorComponentFractionalToken(c);
337}
338
339bool 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
357bool 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
378bool SkSVGAttributeParser::parseColorToken(SkColor* c) {
379 return this->parseHexColorToken(c) ||
380 this->parseNamedColorToken(c) ||
381 this->parseRGBAColorToken(c) ||
382 this->parseRGBColorToken(c);
383}
384
385bool 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).
397template <>
399 this->parseWSToken();
400 if (!this->parseSVGColorType(color)) {
401 return false;
402 }
403 this->parseWSToken();
404 return this->parseEOSToken();
405}
406
407bool 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")) {
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
445template <>
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
456template <>
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 {
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
479bool 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
490template <>
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
501template <>
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
519 // consume WS
520 this->parseWSToken();
521
522 // consume optional '+'
523 this->parseExpectedStringToken("+");
524
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
537template <>
539 SkScalar s;
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
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
565 parsedValue = true;
566 // consume trailing whitespace
567 this->parseWSToken();
568 }
569 return parsedValue && this->parseEOSToken();
570}
571
572template <typename Func, typename T>
573bool 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
599bool 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
614bool 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
631bool 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
647bool 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
668bool 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
679bool 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
691template <>
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
720 return true;
721}
722
723// https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint
724template <>
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")) {
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
751template <>
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
767template <>
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
791template <>
793 static const struct {
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
816template <>
818 bool parsedValue = false;
819 if (this->parseExpectedStringToken("userSpaceOnUse")) {
820 *objectBoundingBoxUnits =
822 parsedValue = true;
823 } else if (this->parseExpectedStringToken("objectBoundingBox")) {
824 *objectBoundingBoxUnits =
826 parsedValue = true;
827 }
828 return parsedValue && this->parseEOSToken();
829}
830
831// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
832template <>
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
881template <>
883 static const struct {
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
905template <>
907 static const struct {
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
930template <>
932 bool parsedValue = false;
933 if (this->parseExpectedStringToken("none")) {
935 parsedValue = true;
936 } else if (this->parseExpectedStringToken("inherit")) {
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
961template <>
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
982template <>
984 bool parsedValue = false;
985 if (this->parseExpectedStringToken("inherit")) {
986 *size = SkSVGFontSize();
987 parsedValue = true;
988 } else {
990 if (this->parse(&length)) {
992 parsedValue = true;
993 }
994 }
995
996 return parsedValue && this->parseEOSToken();
997}
998
999// https://www.w3.org/TR/SVG11/text.html#FontStyleProperty
1000template <>
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;
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
1021template <>
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;
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
1052template <>
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;
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
1074 static constexpr std::tuple<const char*, SkSVGPreserveAspectRatio::Align> gAlignMap[] = {
1085 };
1086
1087 static constexpr std::tuple<const char*, SkSVGPreserveAspectRatio::Scale> gScaleMap[] = {
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
1109template <>
1111 return this->parsePreserveAspectRatio(par);
1112}
1113
1114// https://www.w3.org/TR/SVG11/types.html#DataTypeCoordinates
1115template <typename T>
1116bool 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
1133template <>
1134bool SkSVGAttributeParser::parse(std::vector<SkSVGLength>* lengths) {
1135 return this->parseList(lengths);
1136}
1137
1138template <>
1139bool SkSVGAttributeParser::parse(std::vector<SkSVGNumberType>* numbers) {
1140 return this->parseList(numbers);
1141}
1142
1143template <>
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
1155template <>
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}
const char * fName
static const int points[]
static float next(float f)
#define SkASSERT(cond)
Definition: SkAssert.h:116
uint32_t SkColor
Definition: SkColor.h:37
#define SkColorSetRGB(r, g, b)
Definition: SkColor.h:57
static constexpr SkColor SkColorSetARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
Definition: SkColor.h:49
constexpr SkColor SK_ColorBLACK
Definition: SkColor.h:103
static bool is_ws(int c)
Definition: SkParse.cpp:24
static bool is_sep(int c)
Definition: SkParse.cpp:34
static bool is_hex(int c)
Definition: SkParse.cpp:51
static bool is_between(int c, int min, int max)
Definition: SkParse.cpp:19
SkSVGDisplay
Definition: SkSVGTypes.h:726
SkSVGLineCap
Definition: SkSVGTypes.h:288
SkRect SkSVGViewBoxType
Definition: SkSVGTypes.h:29
SkColor SkSVGColorType
Definition: SkSVGTypes.h:25
SkSVGColorspace
Definition: SkSVGTypes.h:719
std::vector< SkPoint > SkSVGPointsType
Definition: SkSVGTypes.h:31
SkString SkSVGStringType
Definition: SkSVGTypes.h:28
SkMatrix SkSVGTransformType
Definition: SkSVGTypes.h:30
SkScalar SkSVGNumberType
Definition: SkSVGTypes.h:27
int SkSVGIntegerType
Definition: SkSVGTypes.h:26
#define SkDegreesToRadians(degrees)
Definition: SkScalar.h:77
#define SkScalarRoundToInt(x)
Definition: SkScalar.h:37
int32_t SkUnichar
Definition: SkTypes.h:175
GLenum type
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
static const char * FindScalar(const char str[], SkScalar *value)
Definition: SkParse.cpp:216
static const char * FindHex(const char str[], uint32_t *value)
Definition: SkParse.cpp:114
static const char * FindS32(const char str[], int32_t *value)
Definition: SkParse.cpp:143
static const char * FindNamedColor(const char str[], size_t len, SkColor *color)
bool parsePreserveAspectRatio(SkSVGPreserveAspectRatio *)
bool parseInteger(SkSVGIntegerType *)
bool parseViewBox(SkSVGViewBoxType *)
SkSVGAttributeParser(const char[])
bool parse(SkSVGIntegerType *v)
std::vector< SkString > Vars
Definition: SkSVGTypes.h:184
const SkSVGIRI & iri() const
Definition: SkSVGTypes.h:281
void appendUnichar(SkUnichar uni)
Definition: SkString.h:207
bool startsWith(const char prefixStr[]) const
Definition: SkString.h:140
void void void void void void void remove(size_t offset, size_t length)
Definition: SkString.cpp:592
size_t size() const
Definition: SkString.h:131
void append(const char text[])
Definition: SkString.h:203
const char * c_str() const
Definition: SkString.h:133
const Paint & paint
Definition: color_source.cc:38
DlColor color
float SkScalar
Definition: extension.cpp:12
static bool b
struct MyStruct s
struct MyStruct a[10]
if(end==-1)
GAsyncResult * result
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
size_t length
double y
double x
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
SK_SPI SkUnichar NextUTF8(const char **ptr, const char *end)
Definition: SkUTF.cpp:118
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
Definition: __init__.py:1
SkScalar w
SkScalar h
#define T
Definition: precompiler.cc:65
static SkString join(const CommandLineFlags::StringArray &)
Definition: skpbench.cpp:741
Definition: SkMD5.cpp:120
static constexpr SkPoint Make(float x, float y)
Definition: SkPoint_impl.h:173
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659