blob: 7c27ad2c1efabe2746c381c4e7fb86a4b9f72396 [file] [log] [blame]
fmalitabffc2562016-08-03 10:21:11 -07001/*
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
Mike Klein8aa0edf2020-10-16 11:04:18 -05008#include "include/private/SkTPin.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -05009#include "include/utils/SkParse.h"
Florin Malitab3418102020-10-15 18:10:29 -040010#include "modules/svg/include/SkSVGAttributeParser.h"
11#include "modules/svg/include/SkSVGTypes.h"
fmalitabffc2562016-08-03 10:21:11 -070012
13namespace {
14
15// TODO: these should be shared with SkParse.cpp
16
17inline bool is_between(char c, char min, char max) {
18 SkASSERT(min <= max);
19 return (unsigned)(c - min) <= (unsigned)(max - min);
20}
21
22inline bool is_eos(char c) {
23 return !c;
24}
25
26inline bool is_ws(char c) {
27 return is_between(c, 1, 32);
28}
29
30inline bool is_sep(char c) {
31 return is_ws(c) || c == ',' || c == ';';
32}
33
John Stilesa6841be2020-08-06 14:11:56 -040034} // namespace
fmalitabffc2562016-08-03 10:21:11 -070035
36SkSVGAttributeParser::SkSVGAttributeParser(const char attributeString[])
37 : fCurPos(attributeString) {}
38
39template <typename F>
40inline bool SkSVGAttributeParser::advanceWhile(F f) {
41 auto initial = fCurPos;
42 while (f(*fCurPos)) {
43 fCurPos++;
44 }
45 return fCurPos != initial;
46}
47
Tyler Denniston209857c2021-01-27 14:22:19 -050048bool SkSVGAttributeParser::matchStringToken(const char* token, const char** newPos) const {
49 const char* c = fCurPos;
50
51 while (*c && *token && *c == *token) {
52 c++;
53 token++;
54 }
55
56 if (*token) {
57 return false;
58 }
59
60 if (newPos) {
61 *newPos = c;
62 }
63
64 return true;
65}
66
Tyler Denniston57154992020-11-04 16:08:30 -050067bool SkSVGAttributeParser::parseEOSToken() {
fmalitabffc2562016-08-03 10:21:11 -070068 return is_eos(*fCurPos);
69}
70
Tyler Denniston57154992020-11-04 16:08:30 -050071bool SkSVGAttributeParser::parseSepToken() {
fmalitabffc2562016-08-03 10:21:11 -070072 return this->advanceWhile(is_sep);
73}
74
Tyler Denniston57154992020-11-04 16:08:30 -050075bool SkSVGAttributeParser::parseWSToken() {
fmalitabffc2562016-08-03 10:21:11 -070076 return this->advanceWhile(is_ws);
77}
78
Tyler Denniston57154992020-11-04 16:08:30 -050079bool SkSVGAttributeParser::parseCommaWspToken() {
Tyler Denniston2d65b732020-04-15 16:08:26 -040080 // comma-wsp:
81 // (wsp+ comma? wsp*) | (comma wsp*)
82 return this->parseWSToken() || this->parseExpectedStringToken(",");
83}
84
Tyler Denniston57154992020-11-04 16:08:30 -050085bool SkSVGAttributeParser::parseExpectedStringToken(const char* expected) {
Tyler Denniston209857c2021-01-27 14:22:19 -050086 const char* newPos;
87 if (!matchStringToken(expected, &newPos)) {
fmalitabffc2562016-08-03 10:21:11 -070088 return false;
89 }
90
Tyler Denniston209857c2021-01-27 14:22:19 -050091 fCurPos = newPos;
fmalitabffc2562016-08-03 10:21:11 -070092 return true;
93}
94
95bool SkSVGAttributeParser::parseScalarToken(SkScalar* res) {
96 if (const char* next = SkParse::FindScalar(fCurPos, res)) {
97 fCurPos = next;
98 return true;
99 }
100 return false;
101}
102
Tyler Dennistondada9602020-11-03 10:04:25 -0500103bool SkSVGAttributeParser::parseInt32Token(int32_t* res) {
104 if (const char* next = SkParse::FindS32(fCurPos, res)) {
105 fCurPos = next;
106 return true;
107 }
108 return false;
109}
110
fmalitabffc2562016-08-03 10:21:11 -0700111bool SkSVGAttributeParser::parseHexToken(uint32_t* res) {
112 if (const char* next = SkParse::FindHex(fCurPos, res)) {
113 fCurPos = next;
114 return true;
115 }
116 return false;
117}
118
119bool SkSVGAttributeParser::parseLengthUnitToken(SkSVGLength::Unit* unit) {
120 static const struct {
121 const char* fUnitName;
122 SkSVGLength::Unit fUnit;
123 } gUnitInfo[] = {
124 { "%" , SkSVGLength::Unit::kPercentage },
125 { "em", SkSVGLength::Unit::kEMS },
126 { "ex", SkSVGLength::Unit::kEXS },
127 { "px", SkSVGLength::Unit::kPX },
128 { "cm", SkSVGLength::Unit::kCM },
129 { "mm", SkSVGLength::Unit::kMM },
130 { "in", SkSVGLength::Unit::kIN },
131 { "pt", SkSVGLength::Unit::kPT },
132 { "pc", SkSVGLength::Unit::kPC },
133 };
134
135 for (size_t i = 0; i < SK_ARRAY_COUNT(gUnitInfo); ++i) {
136 if (this->parseExpectedStringToken(gUnitInfo[i].fUnitName)) {
137 *unit = gUnitInfo[i].fUnit;
138 return true;
139 }
140 }
141 return false;
142}
143
Florin Malitaf005c252020-04-08 10:10:53 -0400144// https://www.w3.org/TR/SVG11/types.html#DataTypeColor
fmalitabffc2562016-08-03 10:21:11 -0700145bool SkSVGAttributeParser::parseNamedColorToken(SkColor* c) {
146 if (const char* next = SkParse::FindNamedColor(fCurPos, strlen(fCurPos), c)) {
147 fCurPos = next;
148 return true;
149 }
150 return false;
151}
152
153bool SkSVGAttributeParser::parseHexColorToken(SkColor* c) {
154 uint32_t v;
155 const char* initial = fCurPos;
156
157 if (!this->parseExpectedStringToken("#") || !this->parseHexToken(&v)) {
158 return false;
159 }
160
161 switch (fCurPos - initial) {
162 case 7:
163 // matched #xxxxxxx
164 break;
165 case 4:
166 // matched '#xxx;
167 v = ((v << 12) & 0x00f00000) |
168 ((v << 8) & 0x000ff000) |
169 ((v << 4) & 0x00000ff0) |
170 ((v << 0) & 0x0000000f);
171 break;
172 default:
173 return false;
174 }
175
176 *c = v | 0xff000000;
177 return true;
178}
179
fmalita28d5b722016-09-12 17:06:47 -0700180bool SkSVGAttributeParser::parseColorComponentToken(int32_t* c) {
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400181 const auto parseIntegral = [this](int32_t* c) -> bool {
182 const char* p = SkParse::FindS32(fCurPos, c);
183 if (!p || *p == '.') {
184 // No value parsed, or fractional value.
185 return false;
186 }
187
188 if (*p == '%') {
189 *c = SkScalarRoundToInt(*c * 255.0f / 100);
190 p++;
191 }
192
193 fCurPos = p;
194 return true;
195 };
196
197 const auto parseFractional = [this](int32_t* c) -> bool {
198 SkScalar s;
199 const char* p = SkParse::FindScalar(fCurPos, &s);
200 if (!p || *p != '%') {
201 // Floating point must be a percentage (CSS2 rgb-percent syntax).
202 return false;
203 }
204 p++; // Skip '%'
205
206 *c = SkScalarRoundToInt(s * 255.0f / 100);
207 fCurPos = p;
208 return true;
209 };
210
211 if (!parseIntegral(c) && !parseFractional(c)) {
fmalita28d5b722016-09-12 17:06:47 -0700212 return false;
213 }
214
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400215 *c = SkTPin<int32_t>(*c, 0, 255);
fmalita28d5b722016-09-12 17:06:47 -0700216 return true;
217}
218
219bool SkSVGAttributeParser::parseRGBColorToken(SkColor* c) {
220 return this->parseParenthesized("rgb", [this](SkColor* c) -> bool {
221 int32_t r, g, b;
222 if (this->parseColorComponentToken(&r) &&
223 this->parseSepToken() &&
224 this->parseColorComponentToken(&g) &&
225 this->parseSepToken() &&
226 this->parseColorComponentToken(&b)) {
227
228 *c = SkColorSetRGB(static_cast<uint8_t>(r),
229 static_cast<uint8_t>(g),
230 static_cast<uint8_t>(b));
231 return true;
232 }
233 return false;
234 }, c);
235}
236
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400237// https://www.w3.org/TR/SVG11/types.html#DataTypeColor
238// And https://www.w3.org/TR/CSS2/syndata.html#color-units for the alternative
239// forms supported by SVG (e.g. RGB percentages).
Tyler Denniston4c6f57a2020-11-30 15:31:32 -0500240template <>
241bool SkSVGAttributeParser::parse(SkSVGColorType* color) {
fmalitabffc2562016-08-03 10:21:11 -0700242 SkColor c;
243
fmalita61f36b32016-08-08 13:58:50 -0700244 // consume preceding whitespace
245 this->parseWSToken();
246
fmalita61f36b32016-08-08 13:58:50 -0700247 bool parsedValue = false;
fmalita28d5b722016-09-12 17:06:47 -0700248 if (this->parseHexColorToken(&c)
249 || this->parseNamedColorToken(&c)
250 || this->parseRGBColorToken(&c)) {
fmalita397a5172016-08-08 11:38:55 -0700251 *color = SkSVGColorType(c);
fmalita61f36b32016-08-08 13:58:50 -0700252 parsedValue = true;
253
254 // consume trailing whitespace
255 this->parseWSToken();
fmalitabffc2562016-08-03 10:21:11 -0700256 }
257
fmalita61f36b32016-08-08 13:58:50 -0700258 return parsedValue && this->parseEOSToken();
fmalitabffc2562016-08-03 10:21:11 -0700259}
260
Tyler Denniston74165712020-12-09 14:16:12 -0500261// https://www.w3.org/TR/SVG11/types.html#InterfaceSVGColor
262template <>
263bool SkSVGAttributeParser::parse(SkSVGColor* color) {
264 SkSVGColorType c;
265 bool parsedValue = false;
266
267 if (this->parse(&c)) {
268 *color = SkSVGColor(c);
269 parsedValue = true;
270 } else if (this->parseExpectedStringToken("currentColor")) {
271 *color = SkSVGColor(SkSVGColor::Type::kCurrentColor);
272 parsedValue = true;
273 }
274
275 return parsedValue && this->parseEOSToken();
276}
277
Florin Malitaf005c252020-04-08 10:10:53 -0400278// https://www.w3.org/TR/SVG11/linking.html#IRIReference
Tyler Dennistona0a51462020-11-10 13:13:28 -0500279template <>
280bool SkSVGAttributeParser::parse(SkSVGIRI* iri) {
fmalita28d5b722016-09-12 17:06:47 -0700281 // consume preceding whitespace
282 this->parseWSToken();
283
Tyler Denniston209857c2021-01-27 14:22:19 -0500284 SkSVGIRI::Type iriType;
285 if (this->parseExpectedStringToken("#")) {
286 iriType = SkSVGIRI::Type::kLocal;
287 } else if (this->matchStringToken("data:")) {
288 iriType = SkSVGIRI::Type::kDataURI;
289 } else {
290 iriType = SkSVGIRI::Type::kNonlocal;
fmalita28d5b722016-09-12 17:06:47 -0700291 }
Tyler Denniston209857c2021-01-27 14:22:19 -0500292
fmalita28d5b722016-09-12 17:06:47 -0700293 const auto* start = fCurPos;
294 this->advanceWhile([](char c) -> bool { return !is_eos(c) && c != ')'; });
295 if (start == fCurPos) {
296 return false;
297 }
Tyler Denniston209857c2021-01-27 14:22:19 -0500298 *iri = SkSVGIRI(iriType, SkString(start, fCurPos - start));
fmalita28d5b722016-09-12 17:06:47 -0700299 return true;
300}
301
Florin Malitaf005c252020-04-08 10:10:53 -0400302// https://www.w3.org/TR/SVG11/types.html#DataTypeFuncIRI
Tyler Dennistone71f5472021-01-27 13:30:59 -0500303bool SkSVGAttributeParser::parseFuncIRI(SkSVGFuncIRI* iri) {
304 return this->parseParenthesized("url", [this](SkSVGFuncIRI* iriResult) -> bool {
Tyler Dennistona0a51462020-11-10 13:13:28 -0500305 SkSVGIRI iri;
306 if (this->parse(&iri)) {
Tyler Dennistone71f5472021-01-27 13:30:59 -0500307 *iriResult = SkSVGFuncIRI(std::move(iri));
Tyler Dennistona0a51462020-11-10 13:13:28 -0500308 return true;
309 }
310 return false;
fmalita28d5b722016-09-12 17:06:47 -0700311 }, iri);
312}
313
Tyler Dennistonb25caae2020-11-09 12:46:02 -0500314template <>
315bool SkSVGAttributeParser::parse(SkSVGStringType* result) {
316 if (this->parseEOSToken()) {
317 return false;
318 }
319 *result = SkSVGStringType(fCurPos);
320 fCurPos += result->size();
321 return this->parseEOSToken();
322}
323
Florin Malitaf005c252020-04-08 10:10:53 -0400324// https://www.w3.org/TR/SVG11/types.html#DataTypeNumber
Tyler Denniston4c6f57a2020-11-30 15:31:32 -0500325template <>
326bool SkSVGAttributeParser::parse(SkSVGNumberType* number) {
fmalitabffc2562016-08-03 10:21:11 -0700327 // consume WS
328 this->parseWSToken();
329
330 SkScalar s;
331 if (this->parseScalarToken(&s)) {
fmalita397a5172016-08-08 11:38:55 -0700332 *number = SkSVGNumberType(s);
fmalitabffc2562016-08-03 10:21:11 -0700333 // consume trailing separators
334 this->parseSepToken();
335 return true;
336 }
337
338 return false;
339}
340
Tyler Dennistondada9602020-11-03 10:04:25 -0500341// https://www.w3.org/TR/SVG11/types.html#DataTypeInteger
342bool SkSVGAttributeParser::parseInteger(SkSVGIntegerType* number) {
343 // consume WS
344 this->parseWSToken();
345
346 // consume optional '+'
347 this->parseExpectedStringToken("+");
348
349 SkSVGIntegerType i;
350 if (this->parseInt32Token(&i)) {
351 *number = SkSVGNumberType(i);
352 // consume trailing separators
353 this->parseSepToken();
354 return true;
355 }
356
357 return false;
358}
359
Florin Malitaf005c252020-04-08 10:10:53 -0400360// https://www.w3.org/TR/SVG11/types.html#DataTypeLength
Tyler Dennistona0a51462020-11-10 13:13:28 -0500361template <>
362bool SkSVGAttributeParser::parse(SkSVGLength* length) {
fmalitabffc2562016-08-03 10:21:11 -0700363 SkScalar s;
364 SkSVGLength::Unit u = SkSVGLength::Unit::kNumber;
365
366 if (this->parseScalarToken(&s) &&
367 (this->parseLengthUnitToken(&u) || this->parseSepToken() || this->parseEOSToken())) {
368 *length = SkSVGLength(s, u);
369 // consume trailing separators
370 this->parseSepToken();
371 return true;
372 }
373
374 return false;
375}
fmalita397a5172016-08-08 11:38:55 -0700376
Florin Malitaf005c252020-04-08 10:10:53 -0400377// https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute
fmalita397a5172016-08-08 11:38:55 -0700378bool SkSVGAttributeParser::parseViewBox(SkSVGViewBoxType* vb) {
379 SkScalar x, y, w, h;
380 this->parseWSToken();
381
382 bool parsedValue = false;
383 if (this->parseScalarToken(&x) && this->parseSepToken() &&
384 this->parseScalarToken(&y) && this->parseSepToken() &&
385 this->parseScalarToken(&w) && this->parseSepToken() &&
386 this->parseScalarToken(&h)) {
387
388 *vb = SkSVGViewBoxType(SkRect::MakeXYWH(x, y, w, h));
389 parsedValue = true;
390 // consume trailing whitespace
391 this->parseWSToken();
392 }
393 return parsedValue && this->parseEOSToken();
394}
fmalitac97796b2016-08-08 12:58:57 -0700395
396template <typename Func, typename T>
397bool SkSVGAttributeParser::parseParenthesized(const char* prefix, Func f, T* result) {
398 this->parseWSToken();
399 if (prefix && !this->parseExpectedStringToken(prefix)) {
400 return false;
401 }
402 this->parseWSToken();
403 if (!this->parseExpectedStringToken("(")) {
404 return false;
405 }
406 this->parseWSToken();
407
408 if (!f(result)) {
409 return false;
410 }
411 this->parseWSToken();
412
413 return this->parseExpectedStringToken(")");
414}
415
416bool SkSVGAttributeParser::parseMatrixToken(SkMatrix* matrix) {
417 return this->parseParenthesized("matrix", [this](SkMatrix* m) -> bool {
418 SkScalar scalars[6];
419 for (int i = 0; i < 6; ++i) {
420 if (!(this->parseScalarToken(scalars + i) &&
421 (i > 4 || this->parseSepToken()))) {
422 return false;
423 }
424 }
425
426 m->setAll(scalars[0], scalars[2], scalars[4], scalars[1], scalars[3], scalars[5], 0, 0, 1);
427 return true;
428 }, matrix);
429}
430
431bool SkSVGAttributeParser::parseTranslateToken(SkMatrix* matrix) {
432 return this->parseParenthesized("translate", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500433 SkScalar tx = 0.0, ty = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700434 this->parseWSToken();
435 if (!this->parseScalarToken(&tx)) {
436 return false;
437 }
438
Tyler Dennistona625f922020-04-15 15:52:01 -0400439 if (!this->parseSepToken() || !this->parseScalarToken(&ty)) {
440 ty = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700441 }
442
443 m->setTranslate(tx, ty);
444 return true;
445 }, matrix);
446}
447
448bool SkSVGAttributeParser::parseScaleToken(SkMatrix* matrix) {
449 return this->parseParenthesized("scale", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500450 SkScalar sx = 0.0, sy = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700451 if (!this->parseScalarToken(&sx)) {
452 return false;
453 }
454
455 if (!(this->parseSepToken() && this->parseScalarToken(&sy))) {
456 sy = sx;
457 }
458
459 m->setScale(sx, sy);
460 return true;
461 }, matrix);
462}
463
464bool SkSVGAttributeParser::parseRotateToken(SkMatrix* matrix) {
465 return this->parseParenthesized("rotate", [this](SkMatrix* m) -> bool {
466 SkScalar angle;
467 if (!this->parseScalarToken(&angle)) {
468 return false;
469 }
470
471 SkScalar cx = 0;
472 SkScalar cy = 0;
473 // optional [<cx> <cy>]
474 if (this->parseSepToken() && this->parseScalarToken(&cx)) {
475 if (!(this->parseSepToken() && this->parseScalarToken(&cy))) {
476 return false;
477 }
478 }
479
480 m->setRotate(angle, cx, cy);
481 return true;
482 }, matrix);
483}
484
485bool SkSVGAttributeParser::parseSkewXToken(SkMatrix* matrix) {
486 return this->parseParenthesized("skewX", [this](SkMatrix* m) -> bool {
487 SkScalar angle;
488 if (!this->parseScalarToken(&angle)) {
489 return false;
490 }
Tyler Dennistond1e5b032020-04-08 13:02:41 -0400491 m->setSkewX(tanf(SkDegreesToRadians(angle)));
fmalitac97796b2016-08-08 12:58:57 -0700492 return true;
493 }, matrix);
494}
495
496bool SkSVGAttributeParser::parseSkewYToken(SkMatrix* matrix) {
497 return this->parseParenthesized("skewY", [this](SkMatrix* m) -> bool {
498 SkScalar angle;
499 if (!this->parseScalarToken(&angle)) {
500 return false;
501 }
Tyler Dennistond1e5b032020-04-08 13:02:41 -0400502 m->setSkewY(tanf(SkDegreesToRadians(angle)));
fmalitac97796b2016-08-08 12:58:57 -0700503 return true;
504 }, matrix);
505}
506
Florin Malitaf005c252020-04-08 10:10:53 -0400507// https://www.w3.org/TR/SVG11/coords.html#TransformAttribute
Tyler Dennistona0a51462020-11-10 13:13:28 -0500508template <>
509bool SkSVGAttributeParser::parse(SkSVGTransformType* t) {
fmalitac97796b2016-08-08 12:58:57 -0700510 SkMatrix matrix = SkMatrix::I();
511
512 bool parsed = false;
513 while (true) {
514 SkMatrix m;
515
516 if (!( this->parseMatrixToken(&m)
517 || this->parseTranslateToken(&m)
518 || this->parseScaleToken(&m)
519 || this->parseRotateToken(&m)
520 || this->parseSkewXToken(&m)
521 || this->parseSkewYToken(&m))) {
522 break;
523 }
524
525 matrix.preConcat(m);
526 parsed = true;
Tyler Denniston2d65b732020-04-15 16:08:26 -0400527
528 this->parseCommaWspToken();
fmalitac97796b2016-08-08 12:58:57 -0700529 }
530
531 this->parseWSToken();
532 if (!parsed || !this->parseEOSToken()) {
533 return false;
534 }
535
536 *t = SkSVGTransformType(matrix);
537 return true;
538}
fmalita2d961e02016-08-11 09:16:29 -0700539
Florin Malitaf005c252020-04-08 10:10:53 -0400540// https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint
Florin Malita8c425672020-11-06 13:49:37 -0500541template <>
542bool SkSVGAttributeParser::parse(SkSVGPaint* paint) {
Tyler Denniston74165712020-12-09 14:16:12 -0500543 SkSVGColor c;
Tyler Dennistone71f5472021-01-27 13:30:59 -0500544 SkSVGFuncIRI iri;
fmalita2d961e02016-08-11 09:16:29 -0700545 bool parsedValue = false;
Tyler Denniston4c6f57a2020-11-30 15:31:32 -0500546 if (this->parse(&c)) {
fmalita2d961e02016-08-11 09:16:29 -0700547 *paint = SkSVGPaint(c);
548 parsedValue = true;
549 } else if (this->parseExpectedStringToken("none")) {
550 *paint = SkSVGPaint(SkSVGPaint::Type::kNone);
551 parsedValue = true;
fmalita28d5b722016-09-12 17:06:47 -0700552 } else if (this->parseFuncIRI(&iri)) {
Tyler Dennistone71f5472021-01-27 13:30:59 -0500553 *paint = SkSVGPaint(iri.iri());
fmalita28d5b722016-09-12 17:06:47 -0700554 parsedValue = true;
fmalita2d961e02016-08-11 09:16:29 -0700555 }
556 return parsedValue && this->parseEOSToken();
557}
558
Florin Malitaf005c252020-04-08 10:10:53 -0400559// https://www.w3.org/TR/SVG11/masking.html#ClipPathProperty
Florin Malita836c2ca2021-01-13 11:48:02 -0500560// https://www.w3.org/TR/SVG11/masking.html#MaskProperty
561// https://www.w3.org/TR/SVG11/filters.html#FilterProperty
Florin Malita8c425672020-11-06 13:49:37 -0500562template <>
Florin Malita836c2ca2021-01-13 11:48:02 -0500563bool SkSVGAttributeParser::parse(SkSVGFuncIRI* firi) {
Florin Malitace8840e2016-12-08 09:26:47 -0500564 SkSVGStringType iri;
565 bool parsedValue = false;
566
567 if (this->parseExpectedStringToken("none")) {
Florin Malita836c2ca2021-01-13 11:48:02 -0500568 *firi = SkSVGFuncIRI();
Florin Malitace8840e2016-12-08 09:26:47 -0500569 parsedValue = true;
Tyler Dennistone71f5472021-01-27 13:30:59 -0500570 } else if (this->parseFuncIRI(firi)) {
Florin Malitace8840e2016-12-08 09:26:47 -0500571 parsedValue = true;
572 }
573
574 return parsedValue && this->parseEOSToken();
575}
576
Florin Malitaf005c252020-04-08 10:10:53 -0400577// https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty
Florin Malita8c425672020-11-06 13:49:37 -0500578template <>
579bool SkSVGAttributeParser::parse(SkSVGLineCap* cap) {
fmalita2d961e02016-08-11 09:16:29 -0700580 static const struct {
Tyler Denniston041f6652020-12-03 11:14:16 -0500581 SkSVGLineCap fType;
fmalita2d961e02016-08-11 09:16:29 -0700582 const char* fName;
583 } gCapInfo[] = {
Tyler Denniston041f6652020-12-03 11:14:16 -0500584 { SkSVGLineCap::kButt , "butt" },
585 { SkSVGLineCap::kRound , "round" },
586 { SkSVGLineCap::kSquare , "square" },
fmalita2d961e02016-08-11 09:16:29 -0700587 };
588
589 bool parsedValue = false;
590 for (size_t i = 0; i < SK_ARRAY_COUNT(gCapInfo); ++i) {
591 if (this->parseExpectedStringToken(gCapInfo[i].fName)) {
592 *cap = SkSVGLineCap(gCapInfo[i].fType);
593 parsedValue = true;
594 break;
595 }
596 }
597
598 return parsedValue && this->parseEOSToken();
599}
600
Florin Malitaf005c252020-04-08 10:10:53 -0400601// https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty
Florin Malita8c425672020-11-06 13:49:37 -0500602template <>
603bool SkSVGAttributeParser::parse(SkSVGLineJoin* join) {
fmalita2d961e02016-08-11 09:16:29 -0700604 static const struct {
605 SkSVGLineJoin::Type fType;
606 const char* fName;
607 } gJoinInfo[] = {
608 { SkSVGLineJoin::Type::kMiter , "miter" },
609 { SkSVGLineJoin::Type::kRound , "round" },
610 { SkSVGLineJoin::Type::kBevel , "bevel" },
611 { SkSVGLineJoin::Type::kInherit, "inherit" },
612 };
613
614 bool parsedValue = false;
615 for (size_t i = 0; i < SK_ARRAY_COUNT(gJoinInfo); ++i) {
616 if (this->parseExpectedStringToken(gJoinInfo[i].fName)) {
617 *join = SkSVGLineJoin(gJoinInfo[i].fType);
618 parsedValue = true;
619 break;
620 }
621 }
622
623 return parsedValue && this->parseEOSToken();
624}
fmalita5b31f322016-08-12 12:15:33 -0700625
Tyler Denniston30e327e2020-10-29 16:29:22 -0400626// https://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBoxUnits
Tyler Dennistona0a51462020-11-10 13:13:28 -0500627template <>
628bool SkSVGAttributeParser::parse(SkSVGObjectBoundingBoxUnits* objectBoundingBoxUnits) {
Tyler Dennistonab76ab42020-10-21 15:08:45 -0400629 bool parsedValue = false;
630 if (this->parseExpectedStringToken("userSpaceOnUse")) {
Tyler Denniston30e327e2020-10-29 16:29:22 -0400631 *objectBoundingBoxUnits =
632 SkSVGObjectBoundingBoxUnits(SkSVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse);
Tyler Dennistonab76ab42020-10-21 15:08:45 -0400633 parsedValue = true;
634 } else if (this->parseExpectedStringToken("objectBoundingBox")) {
Tyler Denniston30e327e2020-10-29 16:29:22 -0400635 *objectBoundingBoxUnits =
636 SkSVGObjectBoundingBoxUnits(SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox);
Tyler Dennistonab76ab42020-10-21 15:08:45 -0400637 parsedValue = true;
638 }
639 return parsedValue && this->parseEOSToken();
640}
641
Florin Malitaf005c252020-04-08 10:10:53 -0400642// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
Tyler Dennistonc6834822021-02-08 15:07:03 -0500643template <>
644bool SkSVGAttributeParser::parse(SkSVGPointsType* points) {
fmalita5b31f322016-08-12 12:15:33 -0700645 SkTDArray<SkPoint> pts;
646
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400647 // Skip initial wsp.
648 // list-of-points:
649 // wsp* coordinate-pairs? wsp*
650 this->advanceWhile(is_ws);
651
fmalita5b31f322016-08-12 12:15:33 -0700652 bool parsedValue = false;
653 for (;;) {
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400654 // Adjacent coordinate-pairs separated by comma-wsp.
655 // coordinate-pairs:
656 // coordinate-pair
657 // | coordinate-pair comma-wsp coordinate-pairs
Tyler Denniston2d65b732020-04-15 16:08:26 -0400658 if (parsedValue && !this->parseCommaWspToken()) {
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400659 break;
660 }
fmalita5b31f322016-08-12 12:15:33 -0700661
662 SkScalar x, y;
663 if (!this->parseScalarToken(&x)) {
664 break;
665 }
666
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400667 // Coordinate values separated by comma-wsp or '-'.
668 // coordinate-pair:
669 // coordinate comma-wsp coordinate
670 // | coordinate negative-coordinate
Tyler Denniston2d65b732020-04-15 16:08:26 -0400671 if (!this->parseCommaWspToken() && !this->parseEOSToken() && *fCurPos != '-') {
fmalita5b31f322016-08-12 12:15:33 -0700672 break;
673 }
fmalita5b31f322016-08-12 12:15:33 -0700674
675 if (!this->parseScalarToken(&y)) {
676 break;
677 }
678
Mike Reed5edcd312018-08-08 11:23:41 -0400679 pts.push_back(SkPoint::Make(x, y));
fmalita5b31f322016-08-12 12:15:33 -0700680 parsedValue = true;
681 }
682
683 if (parsedValue && this->parseEOSToken()) {
684 *points = pts;
685 return true;
686 }
687
688 return false;
689}
Florin Malitae932d4b2016-12-01 13:35:11 -0500690
Florin Malitaf005c252020-04-08 10:10:53 -0400691// https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty
Florin Malita8c425672020-11-06 13:49:37 -0500692template <>
693bool SkSVGAttributeParser::parse(SkSVGFillRule* fillRule) {
Florin Malitae932d4b2016-12-01 13:35:11 -0500694 static const struct {
695 SkSVGFillRule::Type fType;
696 const char* fName;
697 } gFillRuleInfo[] = {
698 { SkSVGFillRule::Type::kNonZero, "nonzero" },
699 { SkSVGFillRule::Type::kEvenOdd, "evenodd" },
700 { SkSVGFillRule::Type::kInherit, "inherit" },
701 };
702
703 bool parsedValue = false;
704 for (size_t i = 0; i < SK_ARRAY_COUNT(gFillRuleInfo); ++i) {
705 if (this->parseExpectedStringToken(gFillRuleInfo[i].fName)) {
706 *fillRule = SkSVGFillRule(gFillRuleInfo[i].fType);
707 parsedValue = true;
708 break;
709 }
710 }
711
712 return parsedValue && this->parseEOSToken();
713}
Florin Malitaffe6ae42017-10-12 11:33:28 -0400714
Florin Malitaf005c252020-04-08 10:10:53 -0400715// https://www.w3.org/TR/SVG11/painting.html#VisibilityProperty
Florin Malita8c425672020-11-06 13:49:37 -0500716template <>
717bool SkSVGAttributeParser::parse(SkSVGVisibility* visibility) {
Florin Malitaffe6ae42017-10-12 11:33:28 -0400718 static const struct {
719 SkSVGVisibility::Type fType;
720 const char* fName;
721 } gVisibilityInfo[] = {
722 { SkSVGVisibility::Type::kVisible , "visible" },
723 { SkSVGVisibility::Type::kHidden , "hidden" },
724 { SkSVGVisibility::Type::kCollapse, "collapse" },
725 { SkSVGVisibility::Type::kInherit , "inherit" },
726 };
727
728 bool parsedValue = false;
729 for (const auto& parseInfo : gVisibilityInfo) {
730 if (this->parseExpectedStringToken(parseInfo.fName)) {
731 *visibility = SkSVGVisibility(parseInfo.fType);
732 parsedValue = true;
733 break;
734 }
735 }
736
737 return parsedValue && this->parseEOSToken();
738}
Florin Malitaf543a602017-10-13 14:07:44 -0400739
Florin Malitaf005c252020-04-08 10:10:53 -0400740// https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty
Florin Malita8c425672020-11-06 13:49:37 -0500741template <>
742bool SkSVGAttributeParser::parse(SkSVGDashArray* dashArray) {
Florin Malitaf543a602017-10-13 14:07:44 -0400743 bool parsedValue = false;
744 if (this->parseExpectedStringToken("none")) {
745 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kNone);
746 parsedValue = true;
747 } else if (this->parseExpectedStringToken("inherit")) {
748 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kInherit);
749 parsedValue = true;
750 } else {
751 SkTDArray<SkSVGLength> dashes;
752 for (;;) {
753 SkSVGLength dash;
754 // parseLength() also consumes trailing separators.
Tyler Dennistona0a51462020-11-10 13:13:28 -0500755 if (!this->parse(&dash)) {
Florin Malitaf543a602017-10-13 14:07:44 -0400756 break;
757 }
758
Mike Reed5edcd312018-08-08 11:23:41 -0400759 dashes.push_back(dash);
Florin Malitaf543a602017-10-13 14:07:44 -0400760 parsedValue = true;
761 }
762
763 if (parsedValue) {
764 *dashArray = SkSVGDashArray(std::move(dashes));
765 }
766 }
767
768 return parsedValue && this->parseEOSToken();
769}
Florin Malita39fe8c82020-10-20 10:43:03 -0400770
771// https://www.w3.org/TR/SVG11/text.html#FontFamilyProperty
Florin Malita8c425672020-11-06 13:49:37 -0500772template <>
773bool SkSVGAttributeParser::parse(SkSVGFontFamily* family) {
Florin Malita39fe8c82020-10-20 10:43:03 -0400774 bool parsedValue = false;
775 if (this->parseExpectedStringToken("inherit")) {
776 *family = SkSVGFontFamily();
777 parsedValue = true;
778 } else {
779 // The spec allows specifying a comma-separated list for explicit fallback order.
780 // For now, we only use the first entry and rely on the font manager to handle fallback.
781 const auto* comma = strchr(fCurPos, ',');
782 auto family_name = comma ? SkString(fCurPos, comma - fCurPos)
783 : SkString(fCurPos);
784 *family = SkSVGFontFamily(family_name.c_str());
785 fCurPos += strlen(fCurPos);
786 parsedValue = true;
787 }
788
789 return parsedValue && this->parseEOSToken();
790}
791
792// https://www.w3.org/TR/SVG11/text.html#FontSizeProperty
Florin Malita8c425672020-11-06 13:49:37 -0500793template <>
794bool SkSVGAttributeParser::parse(SkSVGFontSize* size) {
Florin Malita39fe8c82020-10-20 10:43:03 -0400795 bool parsedValue = false;
796 if (this->parseExpectedStringToken("inherit")) {
797 *size = SkSVGFontSize();
798 parsedValue = true;
799 } else {
800 SkSVGLength length;
Tyler Dennistona0a51462020-11-10 13:13:28 -0500801 if (this->parse(&length)) {
Florin Malita39fe8c82020-10-20 10:43:03 -0400802 *size = SkSVGFontSize(length);
803 parsedValue = true;
804 }
805 }
806
807 return parsedValue && this->parseEOSToken();
808}
809
810// https://www.w3.org/TR/SVG11/text.html#FontStyleProperty
Florin Malita8c425672020-11-06 13:49:37 -0500811template <>
812bool SkSVGAttributeParser::parse(SkSVGFontStyle* style) {
Florin Malita39fe8c82020-10-20 10:43:03 -0400813 static constexpr std::tuple<const char*, SkSVGFontStyle::Type> gStyleMap[] = {
814 { "normal" , SkSVGFontStyle::Type::kNormal },
815 { "italic" , SkSVGFontStyle::Type::kItalic },
816 { "oblique", SkSVGFontStyle::Type::kOblique },
817 { "inherit", SkSVGFontStyle::Type::kInherit },
818 };
819
820 bool parsedValue = false;
821 SkSVGFontStyle::Type type;
822
823 if (this->parseEnumMap(gStyleMap, &type)) {
824 *style = SkSVGFontStyle(type);
825 parsedValue = true;
826 }
827
828 return parsedValue && this->parseEOSToken();
829}
830
831// https://www.w3.org/TR/SVG11/text.html#FontWeightProperty
Florin Malita8c425672020-11-06 13:49:37 -0500832template <>
833bool SkSVGAttributeParser::parse(SkSVGFontWeight* weight) {
Florin Malita39fe8c82020-10-20 10:43:03 -0400834 static constexpr std::tuple<const char*, SkSVGFontWeight::Type> gWeightMap[] = {
835 { "normal" , SkSVGFontWeight::Type::kNormal },
836 { "bold" , SkSVGFontWeight::Type::kBold },
837 { "bolder" , SkSVGFontWeight::Type::kBolder },
838 { "lighter", SkSVGFontWeight::Type::kLighter },
839 { "100" , SkSVGFontWeight::Type::k100 },
840 { "200" , SkSVGFontWeight::Type::k200 },
841 { "300" , SkSVGFontWeight::Type::k300 },
842 { "400" , SkSVGFontWeight::Type::k400 },
843 { "500" , SkSVGFontWeight::Type::k500 },
844 { "600" , SkSVGFontWeight::Type::k600 },
845 { "700" , SkSVGFontWeight::Type::k700 },
846 { "800" , SkSVGFontWeight::Type::k800 },
847 { "900" , SkSVGFontWeight::Type::k900 },
848 { "inherit", SkSVGFontWeight::Type::kInherit },
849 };
850
851 bool parsedValue = false;
852 SkSVGFontWeight::Type type;
853
854 if (this->parseEnumMap(gWeightMap, &type)) {
855 *weight = SkSVGFontWeight(type);
856 parsedValue = true;
857 }
858
859 return parsedValue && this->parseEOSToken();
860}
Florin Malita385e7442020-10-21 16:55:46 -0400861
Florin Malita056385b2020-10-27 22:57:56 -0400862// https://www.w3.org/TR/SVG11/text.html#TextAnchorProperty
Florin Malita8c425672020-11-06 13:49:37 -0500863template <>
864bool SkSVGAttributeParser::parse(SkSVGTextAnchor* anchor) {
Florin Malita056385b2020-10-27 22:57:56 -0400865 static constexpr std::tuple<const char*, SkSVGTextAnchor::Type> gAnchorMap[] = {
866 { "start" , SkSVGTextAnchor::Type::kStart },
867 { "middle" , SkSVGTextAnchor::Type::kMiddle },
868 { "end" , SkSVGTextAnchor::Type::kEnd },
869 { "inherit", SkSVGTextAnchor::Type::kInherit},
870 };
871
872 bool parsedValue = false;
873 SkSVGTextAnchor::Type type;
874
875 if (this->parseEnumMap(gAnchorMap, &type)) {
876 *anchor = SkSVGTextAnchor(type);
877 parsedValue = true;
878 }
879
880 return parsedValue && this->parseEOSToken();
881}
882
Florin Malita385e7442020-10-21 16:55:46 -0400883// https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
884bool SkSVGAttributeParser::parsePreserveAspectRatio(SkSVGPreserveAspectRatio* par) {
885 static constexpr std::tuple<const char*, SkSVGPreserveAspectRatio::Align> gAlignMap[] = {
886 { "none" , SkSVGPreserveAspectRatio::kNone },
887 { "xMinYMin", SkSVGPreserveAspectRatio::kXMinYMin },
888 { "xMidYMin", SkSVGPreserveAspectRatio::kXMidYMin },
889 { "xMaxYMin", SkSVGPreserveAspectRatio::kXMaxYMin },
890 { "xMinYMid", SkSVGPreserveAspectRatio::kXMinYMid },
891 { "xMidYMid", SkSVGPreserveAspectRatio::kXMidYMid },
892 { "xMaxYMid", SkSVGPreserveAspectRatio::kXMaxYMid },
893 { "xMinYMax", SkSVGPreserveAspectRatio::kXMinYMax },
894 { "xMidYMax", SkSVGPreserveAspectRatio::kXMidYMax },
895 { "xMaxYMax", SkSVGPreserveAspectRatio::kXMaxYMax },
896 };
897
898 static constexpr std::tuple<const char*, SkSVGPreserveAspectRatio::Scale> gScaleMap[] = {
899 { "meet" , SkSVGPreserveAspectRatio::kMeet },
900 { "slice", SkSVGPreserveAspectRatio::kSlice },
901 };
902
903 bool parsedValue = false;
904
905 // ignoring optional 'defer'
906 this->parseExpectedStringToken("defer");
907 this->parseWSToken();
908
909 if (this->parseEnumMap(gAlignMap, &par->fAlign)) {
910 parsedValue = true;
911
912 // optional scaling selector
913 this->parseWSToken();
914 this->parseEnumMap(gScaleMap, &par->fScale);
915 }
916
917 return parsedValue && this->parseEOSToken();
918}
Florin Malitadec78022020-12-17 16:36:54 -0500919
Tyler Denniston1f4cd072021-02-05 09:08:33 -0500920template <>
921bool SkSVGAttributeParser::parse(SkSVGPreserveAspectRatio* par) {
922 return this->parsePreserveAspectRatio(par);
923}
924
Florin Malitadec78022020-12-17 16:36:54 -0500925// https://www.w3.org/TR/SVG11/types.html#DataTypeCoordinates
Florin Malita2d059fc2021-01-05 11:53:15 -0500926template <typename T>
927bool SkSVGAttributeParser::parseList(std::vector<T>* vals) {
928 SkASSERT(vals->empty());
Florin Malitadec78022020-12-17 16:36:54 -0500929
Florin Malita2d059fc2021-01-05 11:53:15 -0500930 T v;
Florin Malitadec78022020-12-17 16:36:54 -0500931 for (;;) {
Florin Malita2d059fc2021-01-05 11:53:15 -0500932 if (!this->parse(&v)) {
Florin Malitadec78022020-12-17 16:36:54 -0500933 break;
934 }
935
Florin Malita2d059fc2021-01-05 11:53:15 -0500936 vals->push_back(v);
Florin Malitadec78022020-12-17 16:36:54 -0500937
938 this->parseCommaWspToken();
939 }
940
Florin Malita2d059fc2021-01-05 11:53:15 -0500941 return !vals->empty() && this->parseEOSToken();
942}
943
944template <>
945bool SkSVGAttributeParser::parse(std::vector<SkSVGLength>* lengths) {
946 return this->parseList(lengths);
947}
948
949template <>
950bool SkSVGAttributeParser::parse(std::vector<SkSVGNumberType>* numbers) {
951 return this->parseList(numbers);
Florin Malitadec78022020-12-17 16:36:54 -0500952}
Tyler Denniston7bb85db2021-01-13 12:08:04 -0500953
954template <>
955bool SkSVGAttributeParser::parse(SkSVGColorspace* colorspace) {
956 static constexpr std::tuple<const char*, SkSVGColorspace> gColorspaceMap[] = {
957 { "auto" , SkSVGColorspace::kAuto },
958 { "sRGB" , SkSVGColorspace::kSRGB },
959 { "linearRGB", SkSVGColorspace::kLinearRGB },
960 };
961
962 return this->parseEnumMap(gColorspaceMap, colorspace) && this->parseEOSToken();
963}