blob: 0f74906491d911281b526e966efb5d45ca7f0167 [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).
fmalita397a5172016-08-08 11:38:55 -0700227bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) {
fmalitabffc2562016-08-03 10:21:11 -0700228 SkColor c;
229
fmalita61f36b32016-08-08 13:58:50 -0700230 // consume preceding whitespace
231 this->parseWSToken();
232
fmalita61f36b32016-08-08 13:58:50 -0700233 bool parsedValue = false;
fmalita28d5b722016-09-12 17:06:47 -0700234 if (this->parseHexColorToken(&c)
235 || this->parseNamedColorToken(&c)
236 || this->parseRGBColorToken(&c)) {
fmalita397a5172016-08-08 11:38:55 -0700237 *color = SkSVGColorType(c);
fmalita61f36b32016-08-08 13:58:50 -0700238 parsedValue = true;
239
240 // consume trailing whitespace
241 this->parseWSToken();
fmalitabffc2562016-08-03 10:21:11 -0700242 }
243
fmalita61f36b32016-08-08 13:58:50 -0700244 return parsedValue && this->parseEOSToken();
fmalitabffc2562016-08-03 10:21:11 -0700245}
246
Florin Malitaf005c252020-04-08 10:10:53 -0400247// https://www.w3.org/TR/SVG11/linking.html#IRIReference
fmalita28d5b722016-09-12 17:06:47 -0700248bool SkSVGAttributeParser::parseIRI(SkSVGStringType* iri) {
249 // consume preceding whitespace
250 this->parseWSToken();
251
252 // we only support local fragments
253 if (!this->parseExpectedStringToken("#")) {
254 return false;
255 }
256 const auto* start = fCurPos;
257 this->advanceWhile([](char c) -> bool { return !is_eos(c) && c != ')'; });
258 if (start == fCurPos) {
259 return false;
260 }
261 *iri = SkString(start, fCurPos - start);
262 return true;
263}
264
Florin Malitaf005c252020-04-08 10:10:53 -0400265// https://www.w3.org/TR/SVG11/types.html#DataTypeFuncIRI
fmalita28d5b722016-09-12 17:06:47 -0700266bool SkSVGAttributeParser::parseFuncIRI(SkSVGStringType* iri) {
267 return this->parseParenthesized("url", [this](SkSVGStringType* iri) -> bool {
268 return this->parseIRI(iri);
269 }, iri);
270}
271
Florin Malitaf005c252020-04-08 10:10:53 -0400272// https://www.w3.org/TR/SVG11/types.html#DataTypeNumber
fmalita397a5172016-08-08 11:38:55 -0700273bool SkSVGAttributeParser::parseNumber(SkSVGNumberType* number) {
fmalitabffc2562016-08-03 10:21:11 -0700274 // consume WS
275 this->parseWSToken();
276
277 SkScalar s;
278 if (this->parseScalarToken(&s)) {
fmalita397a5172016-08-08 11:38:55 -0700279 *number = SkSVGNumberType(s);
fmalitabffc2562016-08-03 10:21:11 -0700280 // consume trailing separators
281 this->parseSepToken();
282 return true;
283 }
284
285 return false;
286}
287
Tyler Dennistondada9602020-11-03 10:04:25 -0500288// https://www.w3.org/TR/SVG11/types.html#DataTypeInteger
289bool SkSVGAttributeParser::parseInteger(SkSVGIntegerType* number) {
290 // consume WS
291 this->parseWSToken();
292
293 // consume optional '+'
294 this->parseExpectedStringToken("+");
295
296 SkSVGIntegerType i;
297 if (this->parseInt32Token(&i)) {
298 *number = SkSVGNumberType(i);
299 // consume trailing separators
300 this->parseSepToken();
301 return true;
302 }
303
304 return false;
305}
306
Florin Malitaf005c252020-04-08 10:10:53 -0400307// https://www.w3.org/TR/SVG11/types.html#DataTypeLength
fmalitabffc2562016-08-03 10:21:11 -0700308bool SkSVGAttributeParser::parseLength(SkSVGLength* length) {
309 SkScalar s;
310 SkSVGLength::Unit u = SkSVGLength::Unit::kNumber;
311
312 if (this->parseScalarToken(&s) &&
313 (this->parseLengthUnitToken(&u) || this->parseSepToken() || this->parseEOSToken())) {
314 *length = SkSVGLength(s, u);
315 // consume trailing separators
316 this->parseSepToken();
317 return true;
318 }
319
320 return false;
321}
fmalita397a5172016-08-08 11:38:55 -0700322
Florin Malitaf005c252020-04-08 10:10:53 -0400323// https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute
fmalita397a5172016-08-08 11:38:55 -0700324bool SkSVGAttributeParser::parseViewBox(SkSVGViewBoxType* vb) {
325 SkScalar x, y, w, h;
326 this->parseWSToken();
327
328 bool parsedValue = false;
329 if (this->parseScalarToken(&x) && this->parseSepToken() &&
330 this->parseScalarToken(&y) && this->parseSepToken() &&
331 this->parseScalarToken(&w) && this->parseSepToken() &&
332 this->parseScalarToken(&h)) {
333
334 *vb = SkSVGViewBoxType(SkRect::MakeXYWH(x, y, w, h));
335 parsedValue = true;
336 // consume trailing whitespace
337 this->parseWSToken();
338 }
339 return parsedValue && this->parseEOSToken();
340}
fmalitac97796b2016-08-08 12:58:57 -0700341
342template <typename Func, typename T>
343bool SkSVGAttributeParser::parseParenthesized(const char* prefix, Func f, T* result) {
344 this->parseWSToken();
345 if (prefix && !this->parseExpectedStringToken(prefix)) {
346 return false;
347 }
348 this->parseWSToken();
349 if (!this->parseExpectedStringToken("(")) {
350 return false;
351 }
352 this->parseWSToken();
353
354 if (!f(result)) {
355 return false;
356 }
357 this->parseWSToken();
358
359 return this->parseExpectedStringToken(")");
360}
361
362bool SkSVGAttributeParser::parseMatrixToken(SkMatrix* matrix) {
363 return this->parseParenthesized("matrix", [this](SkMatrix* m) -> bool {
364 SkScalar scalars[6];
365 for (int i = 0; i < 6; ++i) {
366 if (!(this->parseScalarToken(scalars + i) &&
367 (i > 4 || this->parseSepToken()))) {
368 return false;
369 }
370 }
371
372 m->setAll(scalars[0], scalars[2], scalars[4], scalars[1], scalars[3], scalars[5], 0, 0, 1);
373 return true;
374 }, matrix);
375}
376
377bool SkSVGAttributeParser::parseTranslateToken(SkMatrix* matrix) {
378 return this->parseParenthesized("translate", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500379 SkScalar tx = 0.0, ty = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700380 this->parseWSToken();
381 if (!this->parseScalarToken(&tx)) {
382 return false;
383 }
384
Tyler Dennistona625f922020-04-15 15:52:01 -0400385 if (!this->parseSepToken() || !this->parseScalarToken(&ty)) {
386 ty = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700387 }
388
389 m->setTranslate(tx, ty);
390 return true;
391 }, matrix);
392}
393
394bool SkSVGAttributeParser::parseScaleToken(SkMatrix* matrix) {
395 return this->parseParenthesized("scale", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500396 SkScalar sx = 0.0, sy = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700397 if (!this->parseScalarToken(&sx)) {
398 return false;
399 }
400
401 if (!(this->parseSepToken() && this->parseScalarToken(&sy))) {
402 sy = sx;
403 }
404
405 m->setScale(sx, sy);
406 return true;
407 }, matrix);
408}
409
410bool SkSVGAttributeParser::parseRotateToken(SkMatrix* matrix) {
411 return this->parseParenthesized("rotate", [this](SkMatrix* m) -> bool {
412 SkScalar angle;
413 if (!this->parseScalarToken(&angle)) {
414 return false;
415 }
416
417 SkScalar cx = 0;
418 SkScalar cy = 0;
419 // optional [<cx> <cy>]
420 if (this->parseSepToken() && this->parseScalarToken(&cx)) {
421 if (!(this->parseSepToken() && this->parseScalarToken(&cy))) {
422 return false;
423 }
424 }
425
426 m->setRotate(angle, cx, cy);
427 return true;
428 }, matrix);
429}
430
431bool SkSVGAttributeParser::parseSkewXToken(SkMatrix* matrix) {
432 return this->parseParenthesized("skewX", [this](SkMatrix* m) -> bool {
433 SkScalar angle;
434 if (!this->parseScalarToken(&angle)) {
435 return false;
436 }
Tyler Dennistond1e5b032020-04-08 13:02:41 -0400437 m->setSkewX(tanf(SkDegreesToRadians(angle)));
fmalitac97796b2016-08-08 12:58:57 -0700438 return true;
439 }, matrix);
440}
441
442bool SkSVGAttributeParser::parseSkewYToken(SkMatrix* matrix) {
443 return this->parseParenthesized("skewY", [this](SkMatrix* m) -> bool {
444 SkScalar angle;
445 if (!this->parseScalarToken(&angle)) {
446 return false;
447 }
Tyler Dennistond1e5b032020-04-08 13:02:41 -0400448 m->setSkewY(tanf(SkDegreesToRadians(angle)));
fmalitac97796b2016-08-08 12:58:57 -0700449 return true;
450 }, matrix);
451}
452
Florin Malitaf005c252020-04-08 10:10:53 -0400453// https://www.w3.org/TR/SVG11/coords.html#TransformAttribute
fmalitac97796b2016-08-08 12:58:57 -0700454bool SkSVGAttributeParser::parseTransform(SkSVGTransformType* t) {
455 SkMatrix matrix = SkMatrix::I();
456
457 bool parsed = false;
458 while (true) {
459 SkMatrix m;
460
461 if (!( this->parseMatrixToken(&m)
462 || this->parseTranslateToken(&m)
463 || this->parseScaleToken(&m)
464 || this->parseRotateToken(&m)
465 || this->parseSkewXToken(&m)
466 || this->parseSkewYToken(&m))) {
467 break;
468 }
469
470 matrix.preConcat(m);
471 parsed = true;
Tyler Denniston2d65b732020-04-15 16:08:26 -0400472
473 this->parseCommaWspToken();
fmalitac97796b2016-08-08 12:58:57 -0700474 }
475
476 this->parseWSToken();
477 if (!parsed || !this->parseEOSToken()) {
478 return false;
479 }
480
481 *t = SkSVGTransformType(matrix);
482 return true;
483}
fmalita2d961e02016-08-11 09:16:29 -0700484
Florin Malitaf005c252020-04-08 10:10:53 -0400485// https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint
Florin Malita8c425672020-11-06 13:49:37 -0500486template <>
487bool SkSVGAttributeParser::parse(SkSVGPaint* paint) {
fmalita2d961e02016-08-11 09:16:29 -0700488 SkSVGColorType c;
fmalita28d5b722016-09-12 17:06:47 -0700489 SkSVGStringType iri;
fmalita2d961e02016-08-11 09:16:29 -0700490 bool parsedValue = false;
491 if (this->parseColor(&c)) {
492 *paint = SkSVGPaint(c);
493 parsedValue = true;
494 } else if (this->parseExpectedStringToken("none")) {
495 *paint = SkSVGPaint(SkSVGPaint::Type::kNone);
496 parsedValue = true;
497 } else if (this->parseExpectedStringToken("currentColor")) {
498 *paint = SkSVGPaint(SkSVGPaint::Type::kCurrentColor);
499 parsedValue = true;
500 } else if (this->parseExpectedStringToken("inherit")) {
501 *paint = SkSVGPaint(SkSVGPaint::Type::kInherit);
502 parsedValue = true;
fmalita28d5b722016-09-12 17:06:47 -0700503 } else if (this->parseFuncIRI(&iri)) {
Florin Malitaa3626692020-04-09 14:36:45 -0400504 *paint = SkSVGPaint(iri);
fmalita28d5b722016-09-12 17:06:47 -0700505 parsedValue = true;
fmalita2d961e02016-08-11 09:16:29 -0700506 }
507 return parsedValue && this->parseEOSToken();
508}
509
Florin Malitaf005c252020-04-08 10:10:53 -0400510// https://www.w3.org/TR/SVG11/masking.html#ClipPathProperty
Florin Malita8c425672020-11-06 13:49:37 -0500511template <>
512bool SkSVGAttributeParser::parse(SkSVGClip* clip) {
Florin Malitace8840e2016-12-08 09:26:47 -0500513 SkSVGStringType iri;
514 bool parsedValue = false;
515
516 if (this->parseExpectedStringToken("none")) {
517 *clip = SkSVGClip(SkSVGClip::Type::kNone);
518 parsedValue = true;
519 } else if (this->parseExpectedStringToken("inherit")) {
520 *clip = SkSVGClip(SkSVGClip::Type::kInherit);
521 parsedValue = true;
522 } else if (this->parseFuncIRI(&iri)) {
Florin Malitaa3626692020-04-09 14:36:45 -0400523 *clip = SkSVGClip(iri);
Florin Malitace8840e2016-12-08 09:26:47 -0500524 parsedValue = true;
525 }
526
527 return parsedValue && this->parseEOSToken();
528}
529
Florin Malitaf005c252020-04-08 10:10:53 -0400530// https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty
Florin Malita8c425672020-11-06 13:49:37 -0500531template <>
532bool SkSVGAttributeParser::parse(SkSVGLineCap* cap) {
fmalita2d961e02016-08-11 09:16:29 -0700533 static const struct {
534 SkSVGLineCap::Type fType;
535 const char* fName;
536 } gCapInfo[] = {
537 { SkSVGLineCap::Type::kButt , "butt" },
538 { SkSVGLineCap::Type::kRound , "round" },
539 { SkSVGLineCap::Type::kSquare , "square" },
540 { SkSVGLineCap::Type::kInherit, "inherit" },
541 };
542
543 bool parsedValue = false;
544 for (size_t i = 0; i < SK_ARRAY_COUNT(gCapInfo); ++i) {
545 if (this->parseExpectedStringToken(gCapInfo[i].fName)) {
546 *cap = SkSVGLineCap(gCapInfo[i].fType);
547 parsedValue = true;
548 break;
549 }
550 }
551
552 return parsedValue && this->parseEOSToken();
553}
554
Florin Malitaf005c252020-04-08 10:10:53 -0400555// https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty
Florin Malita8c425672020-11-06 13:49:37 -0500556template <>
557bool SkSVGAttributeParser::parse(SkSVGLineJoin* join) {
fmalita2d961e02016-08-11 09:16:29 -0700558 static const struct {
559 SkSVGLineJoin::Type fType;
560 const char* fName;
561 } gJoinInfo[] = {
562 { SkSVGLineJoin::Type::kMiter , "miter" },
563 { SkSVGLineJoin::Type::kRound , "round" },
564 { SkSVGLineJoin::Type::kBevel , "bevel" },
565 { SkSVGLineJoin::Type::kInherit, "inherit" },
566 };
567
568 bool parsedValue = false;
569 for (size_t i = 0; i < SK_ARRAY_COUNT(gJoinInfo); ++i) {
570 if (this->parseExpectedStringToken(gJoinInfo[i].fName)) {
571 *join = SkSVGLineJoin(gJoinInfo[i].fType);
572 parsedValue = true;
573 break;
574 }
575 }
576
577 return parsedValue && this->parseEOSToken();
578}
fmalita5b31f322016-08-12 12:15:33 -0700579
Florin Malitaf005c252020-04-08 10:10:53 -0400580// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementSpreadMethodAttribute
fmalitacecd6172016-09-13 12:56:11 -0700581bool SkSVGAttributeParser::parseSpreadMethod(SkSVGSpreadMethod* spread) {
582 static const struct {
583 SkSVGSpreadMethod::Type fType;
584 const char* fName;
585 } gSpreadInfo[] = {
586 { SkSVGSpreadMethod::Type::kPad , "pad" },
587 { SkSVGSpreadMethod::Type::kReflect, "reflect" },
588 { SkSVGSpreadMethod::Type::kRepeat , "repeat" },
589 };
590
591 bool parsedValue = false;
592 for (size_t i = 0; i < SK_ARRAY_COUNT(gSpreadInfo); ++i) {
593 if (this->parseExpectedStringToken(gSpreadInfo[i].fName)) {
594 *spread = SkSVGSpreadMethod(gSpreadInfo[i].fType);
595 parsedValue = true;
596 break;
597 }
598 }
599
600 return parsedValue && this->parseEOSToken();
601}
602
Tyler Denniston308c0722020-04-14 10:53:41 -0400603// https://www.w3.org/TR/SVG11/pservers.html#StopElement
604bool SkSVGAttributeParser::parseStopColor(SkSVGStopColor* stopColor) {
605 SkSVGColorType c;
606 bool parsedValue = false;
607 if (this->parseColor(&c)) {
608 *stopColor = SkSVGStopColor(c);
609 parsedValue = true;
610 } else if (this->parseExpectedStringToken("currentColor")) {
611 *stopColor = SkSVGStopColor(SkSVGStopColor::Type::kCurrentColor);
612 parsedValue = true;
613 } else if (this->parseExpectedStringToken("inherit")) {
614 *stopColor = SkSVGStopColor(SkSVGStopColor::Type::kInherit);
615 parsedValue = true;
616 }
617 return parsedValue && this->parseEOSToken();
618}
619
Tyler Denniston30e327e2020-10-29 16:29:22 -0400620// https://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBoxUnits
621bool SkSVGAttributeParser::parseObjectBoundingBoxUnits(
622 SkSVGObjectBoundingBoxUnits* objectBoundingBoxUnits) {
Tyler Dennistonab76ab42020-10-21 15:08:45 -0400623 bool parsedValue = false;
624 if (this->parseExpectedStringToken("userSpaceOnUse")) {
Tyler Denniston30e327e2020-10-29 16:29:22 -0400625 *objectBoundingBoxUnits =
626 SkSVGObjectBoundingBoxUnits(SkSVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse);
Tyler Dennistonab76ab42020-10-21 15:08:45 -0400627 parsedValue = true;
628 } else if (this->parseExpectedStringToken("objectBoundingBox")) {
Tyler Denniston30e327e2020-10-29 16:29:22 -0400629 *objectBoundingBoxUnits =
630 SkSVGObjectBoundingBoxUnits(SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox);
Tyler Dennistonab76ab42020-10-21 15:08:45 -0400631 parsedValue = true;
632 }
633 return parsedValue && this->parseEOSToken();
634}
635
Florin Malitaf005c252020-04-08 10:10:53 -0400636// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
fmalita5b31f322016-08-12 12:15:33 -0700637bool SkSVGAttributeParser::parsePoints(SkSVGPointsType* points) {
638 SkTDArray<SkPoint> pts;
639
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400640 // Skip initial wsp.
641 // list-of-points:
642 // wsp* coordinate-pairs? wsp*
643 this->advanceWhile(is_ws);
644
fmalita5b31f322016-08-12 12:15:33 -0700645 bool parsedValue = false;
646 for (;;) {
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400647 // Adjacent coordinate-pairs separated by comma-wsp.
648 // coordinate-pairs:
649 // coordinate-pair
650 // | coordinate-pair comma-wsp coordinate-pairs
Tyler Denniston2d65b732020-04-15 16:08:26 -0400651 if (parsedValue && !this->parseCommaWspToken()) {
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400652 break;
653 }
fmalita5b31f322016-08-12 12:15:33 -0700654
655 SkScalar x, y;
656 if (!this->parseScalarToken(&x)) {
657 break;
658 }
659
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400660 // Coordinate values separated by comma-wsp or '-'.
661 // coordinate-pair:
662 // coordinate comma-wsp coordinate
663 // | coordinate negative-coordinate
Tyler Denniston2d65b732020-04-15 16:08:26 -0400664 if (!this->parseCommaWspToken() && !this->parseEOSToken() && *fCurPos != '-') {
fmalita5b31f322016-08-12 12:15:33 -0700665 break;
666 }
fmalita5b31f322016-08-12 12:15:33 -0700667
668 if (!this->parseScalarToken(&y)) {
669 break;
670 }
671
Mike Reed5edcd312018-08-08 11:23:41 -0400672 pts.push_back(SkPoint::Make(x, y));
fmalita5b31f322016-08-12 12:15:33 -0700673 parsedValue = true;
674 }
675
676 if (parsedValue && this->parseEOSToken()) {
677 *points = pts;
678 return true;
679 }
680
681 return false;
682}
Florin Malitae932d4b2016-12-01 13:35:11 -0500683
Florin Malitaf005c252020-04-08 10:10:53 -0400684// https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty
Florin Malita8c425672020-11-06 13:49:37 -0500685template <>
686bool SkSVGAttributeParser::parse(SkSVGFillRule* fillRule) {
Florin Malitae932d4b2016-12-01 13:35:11 -0500687 static const struct {
688 SkSVGFillRule::Type fType;
689 const char* fName;
690 } gFillRuleInfo[] = {
691 { SkSVGFillRule::Type::kNonZero, "nonzero" },
692 { SkSVGFillRule::Type::kEvenOdd, "evenodd" },
693 { SkSVGFillRule::Type::kInherit, "inherit" },
694 };
695
696 bool parsedValue = false;
697 for (size_t i = 0; i < SK_ARRAY_COUNT(gFillRuleInfo); ++i) {
698 if (this->parseExpectedStringToken(gFillRuleInfo[i].fName)) {
699 *fillRule = SkSVGFillRule(gFillRuleInfo[i].fType);
700 parsedValue = true;
701 break;
702 }
703 }
704
705 return parsedValue && this->parseEOSToken();
706}
Florin Malitaffe6ae42017-10-12 11:33:28 -0400707
Tyler Dennistonb3cafbc2020-10-30 15:00:48 -0400708// https://www.w3.org/TR/SVG11/filters.html#FilterProperty
709bool SkSVGAttributeParser::parseFilter(SkSVGFilterType* filter) {
710 SkSVGStringType iri;
711 bool parsedValue = false;
712
713 if (this->parseExpectedStringToken("none")) {
714 *filter = SkSVGFilterType(SkSVGFilterType::Type::kNone);
715 parsedValue = true;
716 } else if (this->parseExpectedStringToken("inherit")) {
717 *filter = SkSVGFilterType(SkSVGFilterType::Type::kInherit);
718 parsedValue = true;
719 } else if (this->parseFuncIRI(&iri)) {
720 *filter = SkSVGFilterType(iri);
721 parsedValue = true;
722 }
723
724 return parsedValue && this->parseEOSToken();
725}
726
Florin Malitaf005c252020-04-08 10:10:53 -0400727// https://www.w3.org/TR/SVG11/painting.html#VisibilityProperty
Florin Malita8c425672020-11-06 13:49:37 -0500728template <>
729bool SkSVGAttributeParser::parse(SkSVGVisibility* visibility) {
Florin Malitaffe6ae42017-10-12 11:33:28 -0400730 static const struct {
731 SkSVGVisibility::Type fType;
732 const char* fName;
733 } gVisibilityInfo[] = {
734 { SkSVGVisibility::Type::kVisible , "visible" },
735 { SkSVGVisibility::Type::kHidden , "hidden" },
736 { SkSVGVisibility::Type::kCollapse, "collapse" },
737 { SkSVGVisibility::Type::kInherit , "inherit" },
738 };
739
740 bool parsedValue = false;
741 for (const auto& parseInfo : gVisibilityInfo) {
742 if (this->parseExpectedStringToken(parseInfo.fName)) {
743 *visibility = SkSVGVisibility(parseInfo.fType);
744 parsedValue = true;
745 break;
746 }
747 }
748
749 return parsedValue && this->parseEOSToken();
750}
Florin Malitaf543a602017-10-13 14:07:44 -0400751
Florin Malitaf005c252020-04-08 10:10:53 -0400752// https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty
Florin Malita8c425672020-11-06 13:49:37 -0500753template <>
754bool SkSVGAttributeParser::parse(SkSVGDashArray* dashArray) {
Florin Malitaf543a602017-10-13 14:07:44 -0400755 bool parsedValue = false;
756 if (this->parseExpectedStringToken("none")) {
757 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kNone);
758 parsedValue = true;
759 } else if (this->parseExpectedStringToken("inherit")) {
760 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kInherit);
761 parsedValue = true;
762 } else {
763 SkTDArray<SkSVGLength> dashes;
764 for (;;) {
765 SkSVGLength dash;
766 // parseLength() also consumes trailing separators.
767 if (!this->parseLength(&dash)) {
768 break;
769 }
770
Mike Reed5edcd312018-08-08 11:23:41 -0400771 dashes.push_back(dash);
Florin Malitaf543a602017-10-13 14:07:44 -0400772 parsedValue = true;
773 }
774
775 if (parsedValue) {
776 *dashArray = SkSVGDashArray(std::move(dashes));
777 }
778 }
779
780 return parsedValue && this->parseEOSToken();
781}
Florin Malita39fe8c82020-10-20 10:43:03 -0400782
783// https://www.w3.org/TR/SVG11/text.html#FontFamilyProperty
Florin Malita8c425672020-11-06 13:49:37 -0500784template <>
785bool SkSVGAttributeParser::parse(SkSVGFontFamily* family) {
Florin Malita39fe8c82020-10-20 10:43:03 -0400786 bool parsedValue = false;
787 if (this->parseExpectedStringToken("inherit")) {
788 *family = SkSVGFontFamily();
789 parsedValue = true;
790 } else {
791 // The spec allows specifying a comma-separated list for explicit fallback order.
792 // For now, we only use the first entry and rely on the font manager to handle fallback.
793 const auto* comma = strchr(fCurPos, ',');
794 auto family_name = comma ? SkString(fCurPos, comma - fCurPos)
795 : SkString(fCurPos);
796 *family = SkSVGFontFamily(family_name.c_str());
797 fCurPos += strlen(fCurPos);
798 parsedValue = true;
799 }
800
801 return parsedValue && this->parseEOSToken();
802}
803
804// https://www.w3.org/TR/SVG11/text.html#FontSizeProperty
Florin Malita8c425672020-11-06 13:49:37 -0500805template <>
806bool SkSVGAttributeParser::parse(SkSVGFontSize* size) {
Florin Malita39fe8c82020-10-20 10:43:03 -0400807 bool parsedValue = false;
808 if (this->parseExpectedStringToken("inherit")) {
809 *size = SkSVGFontSize();
810 parsedValue = true;
811 } else {
812 SkSVGLength length;
813 if (this->parseLength(&length)) {
814 *size = SkSVGFontSize(length);
815 parsedValue = true;
816 }
817 }
818
819 return parsedValue && this->parseEOSToken();
820}
821
822// https://www.w3.org/TR/SVG11/text.html#FontStyleProperty
Florin Malita8c425672020-11-06 13:49:37 -0500823template <>
824bool SkSVGAttributeParser::parse(SkSVGFontStyle* style) {
Florin Malita39fe8c82020-10-20 10:43:03 -0400825 static constexpr std::tuple<const char*, SkSVGFontStyle::Type> gStyleMap[] = {
826 { "normal" , SkSVGFontStyle::Type::kNormal },
827 { "italic" , SkSVGFontStyle::Type::kItalic },
828 { "oblique", SkSVGFontStyle::Type::kOblique },
829 { "inherit", SkSVGFontStyle::Type::kInherit },
830 };
831
832 bool parsedValue = false;
833 SkSVGFontStyle::Type type;
834
835 if (this->parseEnumMap(gStyleMap, &type)) {
836 *style = SkSVGFontStyle(type);
837 parsedValue = true;
838 }
839
840 return parsedValue && this->parseEOSToken();
841}
842
843// https://www.w3.org/TR/SVG11/text.html#FontWeightProperty
Florin Malita8c425672020-11-06 13:49:37 -0500844template <>
845bool SkSVGAttributeParser::parse(SkSVGFontWeight* weight) {
Florin Malita39fe8c82020-10-20 10:43:03 -0400846 static constexpr std::tuple<const char*, SkSVGFontWeight::Type> gWeightMap[] = {
847 { "normal" , SkSVGFontWeight::Type::kNormal },
848 { "bold" , SkSVGFontWeight::Type::kBold },
849 { "bolder" , SkSVGFontWeight::Type::kBolder },
850 { "lighter", SkSVGFontWeight::Type::kLighter },
851 { "100" , SkSVGFontWeight::Type::k100 },
852 { "200" , SkSVGFontWeight::Type::k200 },
853 { "300" , SkSVGFontWeight::Type::k300 },
854 { "400" , SkSVGFontWeight::Type::k400 },
855 { "500" , SkSVGFontWeight::Type::k500 },
856 { "600" , SkSVGFontWeight::Type::k600 },
857 { "700" , SkSVGFontWeight::Type::k700 },
858 { "800" , SkSVGFontWeight::Type::k800 },
859 { "900" , SkSVGFontWeight::Type::k900 },
860 { "inherit", SkSVGFontWeight::Type::kInherit },
861 };
862
863 bool parsedValue = false;
864 SkSVGFontWeight::Type type;
865
866 if (this->parseEnumMap(gWeightMap, &type)) {
867 *weight = SkSVGFontWeight(type);
868 parsedValue = true;
869 }
870
871 return parsedValue && this->parseEOSToken();
872}
Florin Malita385e7442020-10-21 16:55:46 -0400873
Florin Malita056385b2020-10-27 22:57:56 -0400874// https://www.w3.org/TR/SVG11/text.html#TextAnchorProperty
Florin Malita8c425672020-11-06 13:49:37 -0500875template <>
876bool SkSVGAttributeParser::parse(SkSVGTextAnchor* anchor) {
Florin Malita056385b2020-10-27 22:57:56 -0400877 static constexpr std::tuple<const char*, SkSVGTextAnchor::Type> gAnchorMap[] = {
878 { "start" , SkSVGTextAnchor::Type::kStart },
879 { "middle" , SkSVGTextAnchor::Type::kMiddle },
880 { "end" , SkSVGTextAnchor::Type::kEnd },
881 { "inherit", SkSVGTextAnchor::Type::kInherit},
882 };
883
884 bool parsedValue = false;
885 SkSVGTextAnchor::Type type;
886
887 if (this->parseEnumMap(gAnchorMap, &type)) {
888 *anchor = SkSVGTextAnchor(type);
889 parsedValue = true;
890 }
891
892 return parsedValue && this->parseEOSToken();
893}
894
Florin Malita385e7442020-10-21 16:55:46 -0400895// https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
896bool SkSVGAttributeParser::parsePreserveAspectRatio(SkSVGPreserveAspectRatio* par) {
897 static constexpr std::tuple<const char*, SkSVGPreserveAspectRatio::Align> gAlignMap[] = {
898 { "none" , SkSVGPreserveAspectRatio::kNone },
899 { "xMinYMin", SkSVGPreserveAspectRatio::kXMinYMin },
900 { "xMidYMin", SkSVGPreserveAspectRatio::kXMidYMin },
901 { "xMaxYMin", SkSVGPreserveAspectRatio::kXMaxYMin },
902 { "xMinYMid", SkSVGPreserveAspectRatio::kXMinYMid },
903 { "xMidYMid", SkSVGPreserveAspectRatio::kXMidYMid },
904 { "xMaxYMid", SkSVGPreserveAspectRatio::kXMaxYMid },
905 { "xMinYMax", SkSVGPreserveAspectRatio::kXMinYMax },
906 { "xMidYMax", SkSVGPreserveAspectRatio::kXMidYMax },
907 { "xMaxYMax", SkSVGPreserveAspectRatio::kXMaxYMax },
908 };
909
910 static constexpr std::tuple<const char*, SkSVGPreserveAspectRatio::Scale> gScaleMap[] = {
911 { "meet" , SkSVGPreserveAspectRatio::kMeet },
912 { "slice", SkSVGPreserveAspectRatio::kSlice },
913 };
914
915 bool parsedValue = false;
916
917 // ignoring optional 'defer'
918 this->parseExpectedStringToken("defer");
919 this->parseWSToken();
920
921 if (this->parseEnumMap(gAlignMap, &par->fAlign)) {
922 parsedValue = true;
923
924 // optional scaling selector
925 this->parseWSToken();
926 this->parseEnumMap(gScaleMap, &par->fScale);
927 }
928
929 return parsedValue && this->parseEOSToken();
930}