blob: 6b2a593e942c9d94c37fc31bdb22e692e55987ad [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 Denniston57154992020-11-04 16:08:30 -050048bool SkSVGAttributeParser::parseEOSToken() {
fmalitabffc2562016-08-03 10:21:11 -070049 return is_eos(*fCurPos);
50}
51
Tyler Denniston57154992020-11-04 16:08:30 -050052bool SkSVGAttributeParser::parseSepToken() {
fmalitabffc2562016-08-03 10:21:11 -070053 return this->advanceWhile(is_sep);
54}
55
Tyler Denniston57154992020-11-04 16:08:30 -050056bool SkSVGAttributeParser::parseWSToken() {
fmalitabffc2562016-08-03 10:21:11 -070057 return this->advanceWhile(is_ws);
58}
59
Tyler Denniston57154992020-11-04 16:08:30 -050060bool SkSVGAttributeParser::parseCommaWspToken() {
Tyler Denniston2d65b732020-04-15 16:08:26 -040061 // comma-wsp:
62 // (wsp+ comma? wsp*) | (comma wsp*)
63 return this->parseWSToken() || this->parseExpectedStringToken(",");
64}
65
Tyler Denniston57154992020-11-04 16:08:30 -050066bool SkSVGAttributeParser::parseExpectedStringToken(const char* expected) {
fmalitabffc2562016-08-03 10:21:11 -070067 const char* c = fCurPos;
68
69 while (*c && *expected && *c == *expected) {
70 c++;
71 expected++;
72 }
73
74 if (*expected) {
75 return false;
76 }
77
78 fCurPos = c;
79 return true;
80}
81
82bool SkSVGAttributeParser::parseScalarToken(SkScalar* res) {
83 if (const char* next = SkParse::FindScalar(fCurPos, res)) {
84 fCurPos = next;
85 return true;
86 }
87 return false;
88}
89
Tyler Dennistondada9602020-11-03 10:04:25 -050090bool SkSVGAttributeParser::parseInt32Token(int32_t* res) {
91 if (const char* next = SkParse::FindS32(fCurPos, res)) {
92 fCurPos = next;
93 return true;
94 }
95 return false;
96}
97
fmalitabffc2562016-08-03 10:21:11 -070098bool SkSVGAttributeParser::parseHexToken(uint32_t* res) {
99 if (const char* next = SkParse::FindHex(fCurPos, res)) {
100 fCurPos = next;
101 return true;
102 }
103 return false;
104}
105
106bool SkSVGAttributeParser::parseLengthUnitToken(SkSVGLength::Unit* unit) {
107 static const struct {
108 const char* fUnitName;
109 SkSVGLength::Unit fUnit;
110 } gUnitInfo[] = {
111 { "%" , SkSVGLength::Unit::kPercentage },
112 { "em", SkSVGLength::Unit::kEMS },
113 { "ex", SkSVGLength::Unit::kEXS },
114 { "px", SkSVGLength::Unit::kPX },
115 { "cm", SkSVGLength::Unit::kCM },
116 { "mm", SkSVGLength::Unit::kMM },
117 { "in", SkSVGLength::Unit::kIN },
118 { "pt", SkSVGLength::Unit::kPT },
119 { "pc", SkSVGLength::Unit::kPC },
120 };
121
122 for (size_t i = 0; i < SK_ARRAY_COUNT(gUnitInfo); ++i) {
123 if (this->parseExpectedStringToken(gUnitInfo[i].fUnitName)) {
124 *unit = gUnitInfo[i].fUnit;
125 return true;
126 }
127 }
128 return false;
129}
130
Florin Malitaf005c252020-04-08 10:10:53 -0400131// https://www.w3.org/TR/SVG11/types.html#DataTypeColor
fmalitabffc2562016-08-03 10:21:11 -0700132bool SkSVGAttributeParser::parseNamedColorToken(SkColor* c) {
133 if (const char* next = SkParse::FindNamedColor(fCurPos, strlen(fCurPos), c)) {
134 fCurPos = next;
135 return true;
136 }
137 return false;
138}
139
140bool SkSVGAttributeParser::parseHexColorToken(SkColor* c) {
141 uint32_t v;
142 const char* initial = fCurPos;
143
144 if (!this->parseExpectedStringToken("#") || !this->parseHexToken(&v)) {
145 return false;
146 }
147
148 switch (fCurPos - initial) {
149 case 7:
150 // matched #xxxxxxx
151 break;
152 case 4:
153 // matched '#xxx;
154 v = ((v << 12) & 0x00f00000) |
155 ((v << 8) & 0x000ff000) |
156 ((v << 4) & 0x00000ff0) |
157 ((v << 0) & 0x0000000f);
158 break;
159 default:
160 return false;
161 }
162
163 *c = v | 0xff000000;
164 return true;
165}
166
fmalita28d5b722016-09-12 17:06:47 -0700167bool SkSVGAttributeParser::parseColorComponentToken(int32_t* c) {
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400168 const auto parseIntegral = [this](int32_t* c) -> bool {
169 const char* p = SkParse::FindS32(fCurPos, c);
170 if (!p || *p == '.') {
171 // No value parsed, or fractional value.
172 return false;
173 }
174
175 if (*p == '%') {
176 *c = SkScalarRoundToInt(*c * 255.0f / 100);
177 p++;
178 }
179
180 fCurPos = p;
181 return true;
182 };
183
184 const auto parseFractional = [this](int32_t* c) -> bool {
185 SkScalar s;
186 const char* p = SkParse::FindScalar(fCurPos, &s);
187 if (!p || *p != '%') {
188 // Floating point must be a percentage (CSS2 rgb-percent syntax).
189 return false;
190 }
191 p++; // Skip '%'
192
193 *c = SkScalarRoundToInt(s * 255.0f / 100);
194 fCurPos = p;
195 return true;
196 };
197
198 if (!parseIntegral(c) && !parseFractional(c)) {
fmalita28d5b722016-09-12 17:06:47 -0700199 return false;
200 }
201
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400202 *c = SkTPin<int32_t>(*c, 0, 255);
fmalita28d5b722016-09-12 17:06:47 -0700203 return true;
204}
205
206bool SkSVGAttributeParser::parseRGBColorToken(SkColor* c) {
207 return this->parseParenthesized("rgb", [this](SkColor* c) -> bool {
208 int32_t r, g, b;
209 if (this->parseColorComponentToken(&r) &&
210 this->parseSepToken() &&
211 this->parseColorComponentToken(&g) &&
212 this->parseSepToken() &&
213 this->parseColorComponentToken(&b)) {
214
215 *c = SkColorSetRGB(static_cast<uint8_t>(r),
216 static_cast<uint8_t>(g),
217 static_cast<uint8_t>(b));
218 return true;
219 }
220 return false;
221 }, c);
222}
223
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400224// https://www.w3.org/TR/SVG11/types.html#DataTypeColor
225// And https://www.w3.org/TR/CSS2/syndata.html#color-units for the alternative
226// forms supported by SVG (e.g. RGB percentages).
Tyler Denniston4c6f57a2020-11-30 15:31:32 -0500227template <>
228bool SkSVGAttributeParser::parse(SkSVGColorType* color) {
fmalitabffc2562016-08-03 10:21:11 -0700229 SkColor c;
230
fmalita61f36b32016-08-08 13:58:50 -0700231 // consume preceding whitespace
232 this->parseWSToken();
233
fmalita61f36b32016-08-08 13:58:50 -0700234 bool parsedValue = false;
fmalita28d5b722016-09-12 17:06:47 -0700235 if (this->parseHexColorToken(&c)
236 || this->parseNamedColorToken(&c)
237 || this->parseRGBColorToken(&c)) {
fmalita397a5172016-08-08 11:38:55 -0700238 *color = SkSVGColorType(c);
fmalita61f36b32016-08-08 13:58:50 -0700239 parsedValue = true;
240
241 // consume trailing whitespace
242 this->parseWSToken();
fmalitabffc2562016-08-03 10:21:11 -0700243 }
244
fmalita61f36b32016-08-08 13:58:50 -0700245 return parsedValue && this->parseEOSToken();
fmalitabffc2562016-08-03 10:21:11 -0700246}
247
Tyler Denniston74165712020-12-09 14:16:12 -0500248// https://www.w3.org/TR/SVG11/types.html#InterfaceSVGColor
249template <>
250bool SkSVGAttributeParser::parse(SkSVGColor* color) {
251 SkSVGColorType c;
252 bool parsedValue = false;
253
254 if (this->parse(&c)) {
255 *color = SkSVGColor(c);
256 parsedValue = true;
257 } else if (this->parseExpectedStringToken("currentColor")) {
258 *color = SkSVGColor(SkSVGColor::Type::kCurrentColor);
259 parsedValue = true;
260 }
261
262 return parsedValue && this->parseEOSToken();
263}
264
Florin Malitaf005c252020-04-08 10:10:53 -0400265// https://www.w3.org/TR/SVG11/linking.html#IRIReference
Tyler Dennistona0a51462020-11-10 13:13:28 -0500266template <>
267bool SkSVGAttributeParser::parse(SkSVGIRI* iri) {
fmalita28d5b722016-09-12 17:06:47 -0700268 // consume preceding whitespace
269 this->parseWSToken();
270
271 // we only support local fragments
272 if (!this->parseExpectedStringToken("#")) {
273 return false;
274 }
275 const auto* start = fCurPos;
276 this->advanceWhile([](char c) -> bool { return !is_eos(c) && c != ')'; });
277 if (start == fCurPos) {
278 return false;
279 }
Tyler Dennistone71f5472021-01-27 13:30:59 -0500280 *iri = SkSVGIRI(SkSVGIRI::Type::kLocal, SkString(start, fCurPos - start));
fmalita28d5b722016-09-12 17:06:47 -0700281 return true;
282}
283
Florin Malitaf005c252020-04-08 10:10:53 -0400284// https://www.w3.org/TR/SVG11/types.html#DataTypeFuncIRI
Tyler Dennistone71f5472021-01-27 13:30:59 -0500285bool SkSVGAttributeParser::parseFuncIRI(SkSVGFuncIRI* iri) {
286 return this->parseParenthesized("url", [this](SkSVGFuncIRI* iriResult) -> bool {
Tyler Dennistona0a51462020-11-10 13:13:28 -0500287 SkSVGIRI iri;
288 if (this->parse(&iri)) {
Tyler Dennistone71f5472021-01-27 13:30:59 -0500289 *iriResult = SkSVGFuncIRI(std::move(iri));
Tyler Dennistona0a51462020-11-10 13:13:28 -0500290 return true;
291 }
292 return false;
fmalita28d5b722016-09-12 17:06:47 -0700293 }, iri);
294}
295
Tyler Dennistonb25caae2020-11-09 12:46:02 -0500296template <>
297bool SkSVGAttributeParser::parse(SkSVGStringType* result) {
298 if (this->parseEOSToken()) {
299 return false;
300 }
301 *result = SkSVGStringType(fCurPos);
302 fCurPos += result->size();
303 return this->parseEOSToken();
304}
305
Florin Malitaf005c252020-04-08 10:10:53 -0400306// https://www.w3.org/TR/SVG11/types.html#DataTypeNumber
Tyler Denniston4c6f57a2020-11-30 15:31:32 -0500307template <>
308bool SkSVGAttributeParser::parse(SkSVGNumberType* number) {
fmalitabffc2562016-08-03 10:21:11 -0700309 // consume WS
310 this->parseWSToken();
311
312 SkScalar s;
313 if (this->parseScalarToken(&s)) {
fmalita397a5172016-08-08 11:38:55 -0700314 *number = SkSVGNumberType(s);
fmalitabffc2562016-08-03 10:21:11 -0700315 // consume trailing separators
316 this->parseSepToken();
317 return true;
318 }
319
320 return false;
321}
322
Tyler Dennistondada9602020-11-03 10:04:25 -0500323// https://www.w3.org/TR/SVG11/types.html#DataTypeInteger
324bool SkSVGAttributeParser::parseInteger(SkSVGIntegerType* number) {
325 // consume WS
326 this->parseWSToken();
327
328 // consume optional '+'
329 this->parseExpectedStringToken("+");
330
331 SkSVGIntegerType i;
332 if (this->parseInt32Token(&i)) {
333 *number = SkSVGNumberType(i);
334 // consume trailing separators
335 this->parseSepToken();
336 return true;
337 }
338
339 return false;
340}
341
Florin Malitaf005c252020-04-08 10:10:53 -0400342// https://www.w3.org/TR/SVG11/types.html#DataTypeLength
Tyler Dennistona0a51462020-11-10 13:13:28 -0500343template <>
344bool SkSVGAttributeParser::parse(SkSVGLength* length) {
fmalitabffc2562016-08-03 10:21:11 -0700345 SkScalar s;
346 SkSVGLength::Unit u = SkSVGLength::Unit::kNumber;
347
348 if (this->parseScalarToken(&s) &&
349 (this->parseLengthUnitToken(&u) || this->parseSepToken() || this->parseEOSToken())) {
350 *length = SkSVGLength(s, u);
351 // consume trailing separators
352 this->parseSepToken();
353 return true;
354 }
355
356 return false;
357}
fmalita397a5172016-08-08 11:38:55 -0700358
Florin Malitaf005c252020-04-08 10:10:53 -0400359// https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute
fmalita397a5172016-08-08 11:38:55 -0700360bool SkSVGAttributeParser::parseViewBox(SkSVGViewBoxType* vb) {
361 SkScalar x, y, w, h;
362 this->parseWSToken();
363
364 bool parsedValue = false;
365 if (this->parseScalarToken(&x) && this->parseSepToken() &&
366 this->parseScalarToken(&y) && this->parseSepToken() &&
367 this->parseScalarToken(&w) && this->parseSepToken() &&
368 this->parseScalarToken(&h)) {
369
370 *vb = SkSVGViewBoxType(SkRect::MakeXYWH(x, y, w, h));
371 parsedValue = true;
372 // consume trailing whitespace
373 this->parseWSToken();
374 }
375 return parsedValue && this->parseEOSToken();
376}
fmalitac97796b2016-08-08 12:58:57 -0700377
378template <typename Func, typename T>
379bool SkSVGAttributeParser::parseParenthesized(const char* prefix, Func f, T* result) {
380 this->parseWSToken();
381 if (prefix && !this->parseExpectedStringToken(prefix)) {
382 return false;
383 }
384 this->parseWSToken();
385 if (!this->parseExpectedStringToken("(")) {
386 return false;
387 }
388 this->parseWSToken();
389
390 if (!f(result)) {
391 return false;
392 }
393 this->parseWSToken();
394
395 return this->parseExpectedStringToken(")");
396}
397
398bool SkSVGAttributeParser::parseMatrixToken(SkMatrix* matrix) {
399 return this->parseParenthesized("matrix", [this](SkMatrix* m) -> bool {
400 SkScalar scalars[6];
401 for (int i = 0; i < 6; ++i) {
402 if (!(this->parseScalarToken(scalars + i) &&
403 (i > 4 || this->parseSepToken()))) {
404 return false;
405 }
406 }
407
408 m->setAll(scalars[0], scalars[2], scalars[4], scalars[1], scalars[3], scalars[5], 0, 0, 1);
409 return true;
410 }, matrix);
411}
412
413bool SkSVGAttributeParser::parseTranslateToken(SkMatrix* matrix) {
414 return this->parseParenthesized("translate", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500415 SkScalar tx = 0.0, ty = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700416 this->parseWSToken();
417 if (!this->parseScalarToken(&tx)) {
418 return false;
419 }
420
Tyler Dennistona625f922020-04-15 15:52:01 -0400421 if (!this->parseSepToken() || !this->parseScalarToken(&ty)) {
422 ty = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700423 }
424
425 m->setTranslate(tx, ty);
426 return true;
427 }, matrix);
428}
429
430bool SkSVGAttributeParser::parseScaleToken(SkMatrix* matrix) {
431 return this->parseParenthesized("scale", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500432 SkScalar sx = 0.0, sy = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700433 if (!this->parseScalarToken(&sx)) {
434 return false;
435 }
436
437 if (!(this->parseSepToken() && this->parseScalarToken(&sy))) {
438 sy = sx;
439 }
440
441 m->setScale(sx, sy);
442 return true;
443 }, matrix);
444}
445
446bool SkSVGAttributeParser::parseRotateToken(SkMatrix* matrix) {
447 return this->parseParenthesized("rotate", [this](SkMatrix* m) -> bool {
448 SkScalar angle;
449 if (!this->parseScalarToken(&angle)) {
450 return false;
451 }
452
453 SkScalar cx = 0;
454 SkScalar cy = 0;
455 // optional [<cx> <cy>]
456 if (this->parseSepToken() && this->parseScalarToken(&cx)) {
457 if (!(this->parseSepToken() && this->parseScalarToken(&cy))) {
458 return false;
459 }
460 }
461
462 m->setRotate(angle, cx, cy);
463 return true;
464 }, matrix);
465}
466
467bool SkSVGAttributeParser::parseSkewXToken(SkMatrix* matrix) {
468 return this->parseParenthesized("skewX", [this](SkMatrix* m) -> bool {
469 SkScalar angle;
470 if (!this->parseScalarToken(&angle)) {
471 return false;
472 }
Tyler Dennistond1e5b032020-04-08 13:02:41 -0400473 m->setSkewX(tanf(SkDegreesToRadians(angle)));
fmalitac97796b2016-08-08 12:58:57 -0700474 return true;
475 }, matrix);
476}
477
478bool SkSVGAttributeParser::parseSkewYToken(SkMatrix* matrix) {
479 return this->parseParenthesized("skewY", [this](SkMatrix* m) -> bool {
480 SkScalar angle;
481 if (!this->parseScalarToken(&angle)) {
482 return false;
483 }
Tyler Dennistond1e5b032020-04-08 13:02:41 -0400484 m->setSkewY(tanf(SkDegreesToRadians(angle)));
fmalitac97796b2016-08-08 12:58:57 -0700485 return true;
486 }, matrix);
487}
488
Florin Malitaf005c252020-04-08 10:10:53 -0400489// https://www.w3.org/TR/SVG11/coords.html#TransformAttribute
Tyler Dennistona0a51462020-11-10 13:13:28 -0500490template <>
491bool SkSVGAttributeParser::parse(SkSVGTransformType* t) {
fmalitac97796b2016-08-08 12:58:57 -0700492 SkMatrix matrix = SkMatrix::I();
493
494 bool parsed = false;
495 while (true) {
496 SkMatrix m;
497
498 if (!( this->parseMatrixToken(&m)
499 || this->parseTranslateToken(&m)
500 || this->parseScaleToken(&m)
501 || this->parseRotateToken(&m)
502 || this->parseSkewXToken(&m)
503 || this->parseSkewYToken(&m))) {
504 break;
505 }
506
507 matrix.preConcat(m);
508 parsed = true;
Tyler Denniston2d65b732020-04-15 16:08:26 -0400509
510 this->parseCommaWspToken();
fmalitac97796b2016-08-08 12:58:57 -0700511 }
512
513 this->parseWSToken();
514 if (!parsed || !this->parseEOSToken()) {
515 return false;
516 }
517
518 *t = SkSVGTransformType(matrix);
519 return true;
520}
fmalita2d961e02016-08-11 09:16:29 -0700521
Florin Malitaf005c252020-04-08 10:10:53 -0400522// https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint
Florin Malita8c425672020-11-06 13:49:37 -0500523template <>
524bool SkSVGAttributeParser::parse(SkSVGPaint* paint) {
Tyler Denniston74165712020-12-09 14:16:12 -0500525 SkSVGColor c;
Tyler Dennistone71f5472021-01-27 13:30:59 -0500526 SkSVGFuncIRI iri;
fmalita2d961e02016-08-11 09:16:29 -0700527 bool parsedValue = false;
Tyler Denniston4c6f57a2020-11-30 15:31:32 -0500528 if (this->parse(&c)) {
fmalita2d961e02016-08-11 09:16:29 -0700529 *paint = SkSVGPaint(c);
530 parsedValue = true;
531 } else if (this->parseExpectedStringToken("none")) {
532 *paint = SkSVGPaint(SkSVGPaint::Type::kNone);
533 parsedValue = true;
fmalita28d5b722016-09-12 17:06:47 -0700534 } else if (this->parseFuncIRI(&iri)) {
Tyler Dennistone71f5472021-01-27 13:30:59 -0500535 *paint = SkSVGPaint(iri.iri());
fmalita28d5b722016-09-12 17:06:47 -0700536 parsedValue = true;
fmalita2d961e02016-08-11 09:16:29 -0700537 }
538 return parsedValue && this->parseEOSToken();
539}
540
Florin Malitaf005c252020-04-08 10:10:53 -0400541// https://www.w3.org/TR/SVG11/masking.html#ClipPathProperty
Florin Malita836c2ca2021-01-13 11:48:02 -0500542// https://www.w3.org/TR/SVG11/masking.html#MaskProperty
543// https://www.w3.org/TR/SVG11/filters.html#FilterProperty
Florin Malita8c425672020-11-06 13:49:37 -0500544template <>
Florin Malita836c2ca2021-01-13 11:48:02 -0500545bool SkSVGAttributeParser::parse(SkSVGFuncIRI* firi) {
Florin Malitace8840e2016-12-08 09:26:47 -0500546 SkSVGStringType iri;
547 bool parsedValue = false;
548
549 if (this->parseExpectedStringToken("none")) {
Florin Malita836c2ca2021-01-13 11:48:02 -0500550 *firi = SkSVGFuncIRI();
Florin Malitace8840e2016-12-08 09:26:47 -0500551 parsedValue = true;
Tyler Dennistone71f5472021-01-27 13:30:59 -0500552 } else if (this->parseFuncIRI(firi)) {
Florin Malitace8840e2016-12-08 09:26:47 -0500553 parsedValue = true;
554 }
555
556 return parsedValue && this->parseEOSToken();
557}
558
Florin Malitaf005c252020-04-08 10:10:53 -0400559// https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty
Florin Malita8c425672020-11-06 13:49:37 -0500560template <>
561bool SkSVGAttributeParser::parse(SkSVGLineCap* cap) {
fmalita2d961e02016-08-11 09:16:29 -0700562 static const struct {
Tyler Denniston041f6652020-12-03 11:14:16 -0500563 SkSVGLineCap fType;
fmalita2d961e02016-08-11 09:16:29 -0700564 const char* fName;
565 } gCapInfo[] = {
Tyler Denniston041f6652020-12-03 11:14:16 -0500566 { SkSVGLineCap::kButt , "butt" },
567 { SkSVGLineCap::kRound , "round" },
568 { SkSVGLineCap::kSquare , "square" },
fmalita2d961e02016-08-11 09:16:29 -0700569 };
570
571 bool parsedValue = false;
572 for (size_t i = 0; i < SK_ARRAY_COUNT(gCapInfo); ++i) {
573 if (this->parseExpectedStringToken(gCapInfo[i].fName)) {
574 *cap = SkSVGLineCap(gCapInfo[i].fType);
575 parsedValue = true;
576 break;
577 }
578 }
579
580 return parsedValue && this->parseEOSToken();
581}
582
Florin Malitaf005c252020-04-08 10:10:53 -0400583// https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty
Florin Malita8c425672020-11-06 13:49:37 -0500584template <>
585bool SkSVGAttributeParser::parse(SkSVGLineJoin* join) {
fmalita2d961e02016-08-11 09:16:29 -0700586 static const struct {
587 SkSVGLineJoin::Type fType;
588 const char* fName;
589 } gJoinInfo[] = {
590 { SkSVGLineJoin::Type::kMiter , "miter" },
591 { SkSVGLineJoin::Type::kRound , "round" },
592 { SkSVGLineJoin::Type::kBevel , "bevel" },
593 { SkSVGLineJoin::Type::kInherit, "inherit" },
594 };
595
596 bool parsedValue = false;
597 for (size_t i = 0; i < SK_ARRAY_COUNT(gJoinInfo); ++i) {
598 if (this->parseExpectedStringToken(gJoinInfo[i].fName)) {
599 *join = SkSVGLineJoin(gJoinInfo[i].fType);
600 parsedValue = true;
601 break;
602 }
603 }
604
605 return parsedValue && this->parseEOSToken();
606}
fmalita5b31f322016-08-12 12:15:33 -0700607
Tyler Denniston30e327e2020-10-29 16:29:22 -0400608// https://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBoxUnits
Tyler Dennistona0a51462020-11-10 13:13:28 -0500609template <>
610bool SkSVGAttributeParser::parse(SkSVGObjectBoundingBoxUnits* objectBoundingBoxUnits) {
Tyler Dennistonab76ab42020-10-21 15:08:45 -0400611 bool parsedValue = false;
612 if (this->parseExpectedStringToken("userSpaceOnUse")) {
Tyler Denniston30e327e2020-10-29 16:29:22 -0400613 *objectBoundingBoxUnits =
614 SkSVGObjectBoundingBoxUnits(SkSVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse);
Tyler Dennistonab76ab42020-10-21 15:08:45 -0400615 parsedValue = true;
616 } else if (this->parseExpectedStringToken("objectBoundingBox")) {
Tyler Denniston30e327e2020-10-29 16:29:22 -0400617 *objectBoundingBoxUnits =
618 SkSVGObjectBoundingBoxUnits(SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox);
Tyler Dennistonab76ab42020-10-21 15:08:45 -0400619 parsedValue = true;
620 }
621 return parsedValue && this->parseEOSToken();
622}
623
Florin Malitaf005c252020-04-08 10:10:53 -0400624// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
fmalita5b31f322016-08-12 12:15:33 -0700625bool SkSVGAttributeParser::parsePoints(SkSVGPointsType* points) {
626 SkTDArray<SkPoint> pts;
627
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400628 // Skip initial wsp.
629 // list-of-points:
630 // wsp* coordinate-pairs? wsp*
631 this->advanceWhile(is_ws);
632
fmalita5b31f322016-08-12 12:15:33 -0700633 bool parsedValue = false;
634 for (;;) {
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400635 // Adjacent coordinate-pairs separated by comma-wsp.
636 // coordinate-pairs:
637 // coordinate-pair
638 // | coordinate-pair comma-wsp coordinate-pairs
Tyler Denniston2d65b732020-04-15 16:08:26 -0400639 if (parsedValue && !this->parseCommaWspToken()) {
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400640 break;
641 }
fmalita5b31f322016-08-12 12:15:33 -0700642
643 SkScalar x, y;
644 if (!this->parseScalarToken(&x)) {
645 break;
646 }
647
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400648 // Coordinate values separated by comma-wsp or '-'.
649 // coordinate-pair:
650 // coordinate comma-wsp coordinate
651 // | coordinate negative-coordinate
Tyler Denniston2d65b732020-04-15 16:08:26 -0400652 if (!this->parseCommaWspToken() && !this->parseEOSToken() && *fCurPos != '-') {
fmalita5b31f322016-08-12 12:15:33 -0700653 break;
654 }
fmalita5b31f322016-08-12 12:15:33 -0700655
656 if (!this->parseScalarToken(&y)) {
657 break;
658 }
659
Mike Reed5edcd312018-08-08 11:23:41 -0400660 pts.push_back(SkPoint::Make(x, y));
fmalita5b31f322016-08-12 12:15:33 -0700661 parsedValue = true;
662 }
663
664 if (parsedValue && this->parseEOSToken()) {
665 *points = pts;
666 return true;
667 }
668
669 return false;
670}
Florin Malitae932d4b2016-12-01 13:35:11 -0500671
Florin Malitaf005c252020-04-08 10:10:53 -0400672// https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty
Florin Malita8c425672020-11-06 13:49:37 -0500673template <>
674bool SkSVGAttributeParser::parse(SkSVGFillRule* fillRule) {
Florin Malitae932d4b2016-12-01 13:35:11 -0500675 static const struct {
676 SkSVGFillRule::Type fType;
677 const char* fName;
678 } gFillRuleInfo[] = {
679 { SkSVGFillRule::Type::kNonZero, "nonzero" },
680 { SkSVGFillRule::Type::kEvenOdd, "evenodd" },
681 { SkSVGFillRule::Type::kInherit, "inherit" },
682 };
683
684 bool parsedValue = false;
685 for (size_t i = 0; i < SK_ARRAY_COUNT(gFillRuleInfo); ++i) {
686 if (this->parseExpectedStringToken(gFillRuleInfo[i].fName)) {
687 *fillRule = SkSVGFillRule(gFillRuleInfo[i].fType);
688 parsedValue = true;
689 break;
690 }
691 }
692
693 return parsedValue && this->parseEOSToken();
694}
Florin Malitaffe6ae42017-10-12 11:33:28 -0400695
Florin Malitaf005c252020-04-08 10:10:53 -0400696// https://www.w3.org/TR/SVG11/painting.html#VisibilityProperty
Florin Malita8c425672020-11-06 13:49:37 -0500697template <>
698bool SkSVGAttributeParser::parse(SkSVGVisibility* visibility) {
Florin Malitaffe6ae42017-10-12 11:33:28 -0400699 static const struct {
700 SkSVGVisibility::Type fType;
701 const char* fName;
702 } gVisibilityInfo[] = {
703 { SkSVGVisibility::Type::kVisible , "visible" },
704 { SkSVGVisibility::Type::kHidden , "hidden" },
705 { SkSVGVisibility::Type::kCollapse, "collapse" },
706 { SkSVGVisibility::Type::kInherit , "inherit" },
707 };
708
709 bool parsedValue = false;
710 for (const auto& parseInfo : gVisibilityInfo) {
711 if (this->parseExpectedStringToken(parseInfo.fName)) {
712 *visibility = SkSVGVisibility(parseInfo.fType);
713 parsedValue = true;
714 break;
715 }
716 }
717
718 return parsedValue && this->parseEOSToken();
719}
Florin Malitaf543a602017-10-13 14:07:44 -0400720
Florin Malitaf005c252020-04-08 10:10:53 -0400721// https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty
Florin Malita8c425672020-11-06 13:49:37 -0500722template <>
723bool SkSVGAttributeParser::parse(SkSVGDashArray* dashArray) {
Florin Malitaf543a602017-10-13 14:07:44 -0400724 bool parsedValue = false;
725 if (this->parseExpectedStringToken("none")) {
726 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kNone);
727 parsedValue = true;
728 } else if (this->parseExpectedStringToken("inherit")) {
729 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kInherit);
730 parsedValue = true;
731 } else {
732 SkTDArray<SkSVGLength> dashes;
733 for (;;) {
734 SkSVGLength dash;
735 // parseLength() also consumes trailing separators.
Tyler Dennistona0a51462020-11-10 13:13:28 -0500736 if (!this->parse(&dash)) {
Florin Malitaf543a602017-10-13 14:07:44 -0400737 break;
738 }
739
Mike Reed5edcd312018-08-08 11:23:41 -0400740 dashes.push_back(dash);
Florin Malitaf543a602017-10-13 14:07:44 -0400741 parsedValue = true;
742 }
743
744 if (parsedValue) {
745 *dashArray = SkSVGDashArray(std::move(dashes));
746 }
747 }
748
749 return parsedValue && this->parseEOSToken();
750}
Florin Malita39fe8c82020-10-20 10:43:03 -0400751
752// https://www.w3.org/TR/SVG11/text.html#FontFamilyProperty
Florin Malita8c425672020-11-06 13:49:37 -0500753template <>
754bool SkSVGAttributeParser::parse(SkSVGFontFamily* family) {
Florin Malita39fe8c82020-10-20 10:43:03 -0400755 bool parsedValue = false;
756 if (this->parseExpectedStringToken("inherit")) {
757 *family = SkSVGFontFamily();
758 parsedValue = true;
759 } else {
760 // The spec allows specifying a comma-separated list for explicit fallback order.
761 // For now, we only use the first entry and rely on the font manager to handle fallback.
762 const auto* comma = strchr(fCurPos, ',');
763 auto family_name = comma ? SkString(fCurPos, comma - fCurPos)
764 : SkString(fCurPos);
765 *family = SkSVGFontFamily(family_name.c_str());
766 fCurPos += strlen(fCurPos);
767 parsedValue = true;
768 }
769
770 return parsedValue && this->parseEOSToken();
771}
772
773// https://www.w3.org/TR/SVG11/text.html#FontSizeProperty
Florin Malita8c425672020-11-06 13:49:37 -0500774template <>
775bool SkSVGAttributeParser::parse(SkSVGFontSize* size) {
Florin Malita39fe8c82020-10-20 10:43:03 -0400776 bool parsedValue = false;
777 if (this->parseExpectedStringToken("inherit")) {
778 *size = SkSVGFontSize();
779 parsedValue = true;
780 } else {
781 SkSVGLength length;
Tyler Dennistona0a51462020-11-10 13:13:28 -0500782 if (this->parse(&length)) {
Florin Malita39fe8c82020-10-20 10:43:03 -0400783 *size = SkSVGFontSize(length);
784 parsedValue = true;
785 }
786 }
787
788 return parsedValue && this->parseEOSToken();
789}
790
791// https://www.w3.org/TR/SVG11/text.html#FontStyleProperty
Florin Malita8c425672020-11-06 13:49:37 -0500792template <>
793bool SkSVGAttributeParser::parse(SkSVGFontStyle* style) {
Florin Malita39fe8c82020-10-20 10:43:03 -0400794 static constexpr std::tuple<const char*, SkSVGFontStyle::Type> gStyleMap[] = {
795 { "normal" , SkSVGFontStyle::Type::kNormal },
796 { "italic" , SkSVGFontStyle::Type::kItalic },
797 { "oblique", SkSVGFontStyle::Type::kOblique },
798 { "inherit", SkSVGFontStyle::Type::kInherit },
799 };
800
801 bool parsedValue = false;
802 SkSVGFontStyle::Type type;
803
804 if (this->parseEnumMap(gStyleMap, &type)) {
805 *style = SkSVGFontStyle(type);
806 parsedValue = true;
807 }
808
809 return parsedValue && this->parseEOSToken();
810}
811
812// https://www.w3.org/TR/SVG11/text.html#FontWeightProperty
Florin Malita8c425672020-11-06 13:49:37 -0500813template <>
814bool SkSVGAttributeParser::parse(SkSVGFontWeight* weight) {
Florin Malita39fe8c82020-10-20 10:43:03 -0400815 static constexpr std::tuple<const char*, SkSVGFontWeight::Type> gWeightMap[] = {
816 { "normal" , SkSVGFontWeight::Type::kNormal },
817 { "bold" , SkSVGFontWeight::Type::kBold },
818 { "bolder" , SkSVGFontWeight::Type::kBolder },
819 { "lighter", SkSVGFontWeight::Type::kLighter },
820 { "100" , SkSVGFontWeight::Type::k100 },
821 { "200" , SkSVGFontWeight::Type::k200 },
822 { "300" , SkSVGFontWeight::Type::k300 },
823 { "400" , SkSVGFontWeight::Type::k400 },
824 { "500" , SkSVGFontWeight::Type::k500 },
825 { "600" , SkSVGFontWeight::Type::k600 },
826 { "700" , SkSVGFontWeight::Type::k700 },
827 { "800" , SkSVGFontWeight::Type::k800 },
828 { "900" , SkSVGFontWeight::Type::k900 },
829 { "inherit", SkSVGFontWeight::Type::kInherit },
830 };
831
832 bool parsedValue = false;
833 SkSVGFontWeight::Type type;
834
835 if (this->parseEnumMap(gWeightMap, &type)) {
836 *weight = SkSVGFontWeight(type);
837 parsedValue = true;
838 }
839
840 return parsedValue && this->parseEOSToken();
841}
Florin Malita385e7442020-10-21 16:55:46 -0400842
Florin Malita056385b2020-10-27 22:57:56 -0400843// https://www.w3.org/TR/SVG11/text.html#TextAnchorProperty
Florin Malita8c425672020-11-06 13:49:37 -0500844template <>
845bool SkSVGAttributeParser::parse(SkSVGTextAnchor* anchor) {
Florin Malita056385b2020-10-27 22:57:56 -0400846 static constexpr std::tuple<const char*, SkSVGTextAnchor::Type> gAnchorMap[] = {
847 { "start" , SkSVGTextAnchor::Type::kStart },
848 { "middle" , SkSVGTextAnchor::Type::kMiddle },
849 { "end" , SkSVGTextAnchor::Type::kEnd },
850 { "inherit", SkSVGTextAnchor::Type::kInherit},
851 };
852
853 bool parsedValue = false;
854 SkSVGTextAnchor::Type type;
855
856 if (this->parseEnumMap(gAnchorMap, &type)) {
857 *anchor = SkSVGTextAnchor(type);
858 parsedValue = true;
859 }
860
861 return parsedValue && this->parseEOSToken();
862}
863
Florin Malita385e7442020-10-21 16:55:46 -0400864// https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
865bool SkSVGAttributeParser::parsePreserveAspectRatio(SkSVGPreserveAspectRatio* par) {
866 static constexpr std::tuple<const char*, SkSVGPreserveAspectRatio::Align> gAlignMap[] = {
867 { "none" , SkSVGPreserveAspectRatio::kNone },
868 { "xMinYMin", SkSVGPreserveAspectRatio::kXMinYMin },
869 { "xMidYMin", SkSVGPreserveAspectRatio::kXMidYMin },
870 { "xMaxYMin", SkSVGPreserveAspectRatio::kXMaxYMin },
871 { "xMinYMid", SkSVGPreserveAspectRatio::kXMinYMid },
872 { "xMidYMid", SkSVGPreserveAspectRatio::kXMidYMid },
873 { "xMaxYMid", SkSVGPreserveAspectRatio::kXMaxYMid },
874 { "xMinYMax", SkSVGPreserveAspectRatio::kXMinYMax },
875 { "xMidYMax", SkSVGPreserveAspectRatio::kXMidYMax },
876 { "xMaxYMax", SkSVGPreserveAspectRatio::kXMaxYMax },
877 };
878
879 static constexpr std::tuple<const char*, SkSVGPreserveAspectRatio::Scale> gScaleMap[] = {
880 { "meet" , SkSVGPreserveAspectRatio::kMeet },
881 { "slice", SkSVGPreserveAspectRatio::kSlice },
882 };
883
884 bool parsedValue = false;
885
886 // ignoring optional 'defer'
887 this->parseExpectedStringToken("defer");
888 this->parseWSToken();
889
890 if (this->parseEnumMap(gAlignMap, &par->fAlign)) {
891 parsedValue = true;
892
893 // optional scaling selector
894 this->parseWSToken();
895 this->parseEnumMap(gScaleMap, &par->fScale);
896 }
897
898 return parsedValue && this->parseEOSToken();
899}
Florin Malitadec78022020-12-17 16:36:54 -0500900
901// https://www.w3.org/TR/SVG11/types.html#DataTypeCoordinates
Florin Malita2d059fc2021-01-05 11:53:15 -0500902template <typename T>
903bool SkSVGAttributeParser::parseList(std::vector<T>* vals) {
904 SkASSERT(vals->empty());
Florin Malitadec78022020-12-17 16:36:54 -0500905
Florin Malita2d059fc2021-01-05 11:53:15 -0500906 T v;
Florin Malitadec78022020-12-17 16:36:54 -0500907 for (;;) {
Florin Malita2d059fc2021-01-05 11:53:15 -0500908 if (!this->parse(&v)) {
Florin Malitadec78022020-12-17 16:36:54 -0500909 break;
910 }
911
Florin Malita2d059fc2021-01-05 11:53:15 -0500912 vals->push_back(v);
Florin Malitadec78022020-12-17 16:36:54 -0500913
914 this->parseCommaWspToken();
915 }
916
Florin Malita2d059fc2021-01-05 11:53:15 -0500917 return !vals->empty() && this->parseEOSToken();
918}
919
920template <>
921bool SkSVGAttributeParser::parse(std::vector<SkSVGLength>* lengths) {
922 return this->parseList(lengths);
923}
924
925template <>
926bool SkSVGAttributeParser::parse(std::vector<SkSVGNumberType>* numbers) {
927 return this->parseList(numbers);
Florin Malitadec78022020-12-17 16:36:54 -0500928}
Tyler Denniston7bb85db2021-01-13 12:08:04 -0500929
930template <>
931bool SkSVGAttributeParser::parse(SkSVGColorspace* colorspace) {
932 static constexpr std::tuple<const char*, SkSVGColorspace> gColorspaceMap[] = {
933 { "auto" , SkSVGColorspace::kAuto },
934 { "sRGB" , SkSVGColorspace::kSRGB },
935 { "linearRGB", SkSVGColorspace::kLinearRGB },
936 };
937
938 return this->parseEnumMap(gColorspaceMap, colorspace) && this->parseEOSToken();
939}