blob: 202363e3f7e2335e3fba82ad3df3e5e99f9ce6fe [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
Florin Malita39fe8c82020-10-20 10:43:03 -040036template <typename T, typename TArray>
37bool SkSVGAttributeParser::parseEnumMap(const TArray& arr, T* result) {
38 for (size_t i = 0; i < SK_ARRAY_COUNT(arr); ++i) {
39 if (this->parseExpectedStringToken(std::get<0>(arr[i]))) {
40 *result = std::get<1>(arr[i]);
41 return true;
42 }
43 }
44 return false;
45}
46
fmalitabffc2562016-08-03 10:21:11 -070047SkSVGAttributeParser::SkSVGAttributeParser(const char attributeString[])
48 : fCurPos(attributeString) {}
49
50template <typename F>
51inline bool SkSVGAttributeParser::advanceWhile(F f) {
52 auto initial = fCurPos;
53 while (f(*fCurPos)) {
54 fCurPos++;
55 }
56 return fCurPos != initial;
57}
58
Tyler Denniston57154992020-11-04 16:08:30 -050059bool SkSVGAttributeParser::parseEOSToken() {
fmalitabffc2562016-08-03 10:21:11 -070060 return is_eos(*fCurPos);
61}
62
Tyler Denniston57154992020-11-04 16:08:30 -050063bool SkSVGAttributeParser::parseSepToken() {
fmalitabffc2562016-08-03 10:21:11 -070064 return this->advanceWhile(is_sep);
65}
66
Tyler Denniston57154992020-11-04 16:08:30 -050067bool SkSVGAttributeParser::parseWSToken() {
fmalitabffc2562016-08-03 10:21:11 -070068 return this->advanceWhile(is_ws);
69}
70
Tyler Denniston57154992020-11-04 16:08:30 -050071bool SkSVGAttributeParser::parseCommaWspToken() {
Tyler Denniston2d65b732020-04-15 16:08:26 -040072 // comma-wsp:
73 // (wsp+ comma? wsp*) | (comma wsp*)
74 return this->parseWSToken() || this->parseExpectedStringToken(",");
75}
76
Tyler Denniston57154992020-11-04 16:08:30 -050077bool SkSVGAttributeParser::parseExpectedStringToken(const char* expected) {
fmalitabffc2562016-08-03 10:21:11 -070078 const char* c = fCurPos;
79
80 while (*c && *expected && *c == *expected) {
81 c++;
82 expected++;
83 }
84
85 if (*expected) {
86 return false;
87 }
88
89 fCurPos = c;
90 return true;
91}
92
93bool SkSVGAttributeParser::parseScalarToken(SkScalar* res) {
94 if (const char* next = SkParse::FindScalar(fCurPos, res)) {
95 fCurPos = next;
96 return true;
97 }
98 return false;
99}
100
Tyler Dennistondada9602020-11-03 10:04:25 -0500101bool SkSVGAttributeParser::parseInt32Token(int32_t* res) {
102 if (const char* next = SkParse::FindS32(fCurPos, res)) {
103 fCurPos = next;
104 return true;
105 }
106 return false;
107}
108
fmalitabffc2562016-08-03 10:21:11 -0700109bool SkSVGAttributeParser::parseHexToken(uint32_t* res) {
110 if (const char* next = SkParse::FindHex(fCurPos, res)) {
111 fCurPos = next;
112 return true;
113 }
114 return false;
115}
116
117bool SkSVGAttributeParser::parseLengthUnitToken(SkSVGLength::Unit* unit) {
118 static const struct {
119 const char* fUnitName;
120 SkSVGLength::Unit fUnit;
121 } gUnitInfo[] = {
122 { "%" , SkSVGLength::Unit::kPercentage },
123 { "em", SkSVGLength::Unit::kEMS },
124 { "ex", SkSVGLength::Unit::kEXS },
125 { "px", SkSVGLength::Unit::kPX },
126 { "cm", SkSVGLength::Unit::kCM },
127 { "mm", SkSVGLength::Unit::kMM },
128 { "in", SkSVGLength::Unit::kIN },
129 { "pt", SkSVGLength::Unit::kPT },
130 { "pc", SkSVGLength::Unit::kPC },
131 };
132
133 for (size_t i = 0; i < SK_ARRAY_COUNT(gUnitInfo); ++i) {
134 if (this->parseExpectedStringToken(gUnitInfo[i].fUnitName)) {
135 *unit = gUnitInfo[i].fUnit;
136 return true;
137 }
138 }
139 return false;
140}
141
Florin Malitaf005c252020-04-08 10:10:53 -0400142// https://www.w3.org/TR/SVG11/types.html#DataTypeColor
fmalitabffc2562016-08-03 10:21:11 -0700143bool SkSVGAttributeParser::parseNamedColorToken(SkColor* c) {
144 if (const char* next = SkParse::FindNamedColor(fCurPos, strlen(fCurPos), c)) {
145 fCurPos = next;
146 return true;
147 }
148 return false;
149}
150
151bool SkSVGAttributeParser::parseHexColorToken(SkColor* c) {
152 uint32_t v;
153 const char* initial = fCurPos;
154
155 if (!this->parseExpectedStringToken("#") || !this->parseHexToken(&v)) {
156 return false;
157 }
158
159 switch (fCurPos - initial) {
160 case 7:
161 // matched #xxxxxxx
162 break;
163 case 4:
164 // matched '#xxx;
165 v = ((v << 12) & 0x00f00000) |
166 ((v << 8) & 0x000ff000) |
167 ((v << 4) & 0x00000ff0) |
168 ((v << 0) & 0x0000000f);
169 break;
170 default:
171 return false;
172 }
173
174 *c = v | 0xff000000;
175 return true;
176}
177
fmalita28d5b722016-09-12 17:06:47 -0700178bool SkSVGAttributeParser::parseColorComponentToken(int32_t* c) {
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400179 const auto parseIntegral = [this](int32_t* c) -> bool {
180 const char* p = SkParse::FindS32(fCurPos, c);
181 if (!p || *p == '.') {
182 // No value parsed, or fractional value.
183 return false;
184 }
185
186 if (*p == '%') {
187 *c = SkScalarRoundToInt(*c * 255.0f / 100);
188 p++;
189 }
190
191 fCurPos = p;
192 return true;
193 };
194
195 const auto parseFractional = [this](int32_t* c) -> bool {
196 SkScalar s;
197 const char* p = SkParse::FindScalar(fCurPos, &s);
198 if (!p || *p != '%') {
199 // Floating point must be a percentage (CSS2 rgb-percent syntax).
200 return false;
201 }
202 p++; // Skip '%'
203
204 *c = SkScalarRoundToInt(s * 255.0f / 100);
205 fCurPos = p;
206 return true;
207 };
208
209 if (!parseIntegral(c) && !parseFractional(c)) {
fmalita28d5b722016-09-12 17:06:47 -0700210 return false;
211 }
212
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400213 *c = SkTPin<int32_t>(*c, 0, 255);
fmalita28d5b722016-09-12 17:06:47 -0700214 return true;
215}
216
217bool SkSVGAttributeParser::parseRGBColorToken(SkColor* c) {
218 return this->parseParenthesized("rgb", [this](SkColor* c) -> bool {
219 int32_t r, g, b;
220 if (this->parseColorComponentToken(&r) &&
221 this->parseSepToken() &&
222 this->parseColorComponentToken(&g) &&
223 this->parseSepToken() &&
224 this->parseColorComponentToken(&b)) {
225
226 *c = SkColorSetRGB(static_cast<uint8_t>(r),
227 static_cast<uint8_t>(g),
228 static_cast<uint8_t>(b));
229 return true;
230 }
231 return false;
232 }, c);
233}
234
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400235// https://www.w3.org/TR/SVG11/types.html#DataTypeColor
236// And https://www.w3.org/TR/CSS2/syndata.html#color-units for the alternative
237// forms supported by SVG (e.g. RGB percentages).
fmalita397a5172016-08-08 11:38:55 -0700238bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) {
fmalitabffc2562016-08-03 10:21:11 -0700239 SkColor c;
240
fmalita61f36b32016-08-08 13:58:50 -0700241 // consume preceding whitespace
242 this->parseWSToken();
243
fmalita61f36b32016-08-08 13:58:50 -0700244 bool parsedValue = false;
fmalita28d5b722016-09-12 17:06:47 -0700245 if (this->parseHexColorToken(&c)
246 || this->parseNamedColorToken(&c)
247 || this->parseRGBColorToken(&c)) {
fmalita397a5172016-08-08 11:38:55 -0700248 *color = SkSVGColorType(c);
fmalita61f36b32016-08-08 13:58:50 -0700249 parsedValue = true;
250
251 // consume trailing whitespace
252 this->parseWSToken();
fmalitabffc2562016-08-03 10:21:11 -0700253 }
254
fmalita61f36b32016-08-08 13:58:50 -0700255 return parsedValue && this->parseEOSToken();
fmalitabffc2562016-08-03 10:21:11 -0700256}
257
Florin Malitaf005c252020-04-08 10:10:53 -0400258// https://www.w3.org/TR/SVG11/linking.html#IRIReference
fmalita28d5b722016-09-12 17:06:47 -0700259bool SkSVGAttributeParser::parseIRI(SkSVGStringType* iri) {
260 // consume preceding whitespace
261 this->parseWSToken();
262
263 // we only support local fragments
264 if (!this->parseExpectedStringToken("#")) {
265 return false;
266 }
267 const auto* start = fCurPos;
268 this->advanceWhile([](char c) -> bool { return !is_eos(c) && c != ')'; });
269 if (start == fCurPos) {
270 return false;
271 }
272 *iri = SkString(start, fCurPos - start);
273 return true;
274}
275
Florin Malitaf005c252020-04-08 10:10:53 -0400276// https://www.w3.org/TR/SVG11/types.html#DataTypeFuncIRI
fmalita28d5b722016-09-12 17:06:47 -0700277bool SkSVGAttributeParser::parseFuncIRI(SkSVGStringType* iri) {
278 return this->parseParenthesized("url", [this](SkSVGStringType* iri) -> bool {
279 return this->parseIRI(iri);
280 }, iri);
281}
282
Florin Malitaf005c252020-04-08 10:10:53 -0400283// https://www.w3.org/TR/SVG11/types.html#DataTypeNumber
fmalita397a5172016-08-08 11:38:55 -0700284bool SkSVGAttributeParser::parseNumber(SkSVGNumberType* number) {
fmalitabffc2562016-08-03 10:21:11 -0700285 // consume WS
286 this->parseWSToken();
287
288 SkScalar s;
289 if (this->parseScalarToken(&s)) {
fmalita397a5172016-08-08 11:38:55 -0700290 *number = SkSVGNumberType(s);
fmalitabffc2562016-08-03 10:21:11 -0700291 // consume trailing separators
292 this->parseSepToken();
293 return true;
294 }
295
296 return false;
297}
298
Tyler Dennistondada9602020-11-03 10:04:25 -0500299// https://www.w3.org/TR/SVG11/types.html#DataTypeInteger
300bool SkSVGAttributeParser::parseInteger(SkSVGIntegerType* number) {
301 // consume WS
302 this->parseWSToken();
303
304 // consume optional '+'
305 this->parseExpectedStringToken("+");
306
307 SkSVGIntegerType i;
308 if (this->parseInt32Token(&i)) {
309 *number = SkSVGNumberType(i);
310 // consume trailing separators
311 this->parseSepToken();
312 return true;
313 }
314
315 return false;
316}
317
Florin Malitaf005c252020-04-08 10:10:53 -0400318// https://www.w3.org/TR/SVG11/types.html#DataTypeLength
fmalitabffc2562016-08-03 10:21:11 -0700319bool SkSVGAttributeParser::parseLength(SkSVGLength* length) {
320 SkScalar s;
321 SkSVGLength::Unit u = SkSVGLength::Unit::kNumber;
322
323 if (this->parseScalarToken(&s) &&
324 (this->parseLengthUnitToken(&u) || this->parseSepToken() || this->parseEOSToken())) {
325 *length = SkSVGLength(s, u);
326 // consume trailing separators
327 this->parseSepToken();
328 return true;
329 }
330
331 return false;
332}
fmalita397a5172016-08-08 11:38:55 -0700333
Florin Malitaf005c252020-04-08 10:10:53 -0400334// https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute
fmalita397a5172016-08-08 11:38:55 -0700335bool SkSVGAttributeParser::parseViewBox(SkSVGViewBoxType* vb) {
336 SkScalar x, y, w, h;
337 this->parseWSToken();
338
339 bool parsedValue = false;
340 if (this->parseScalarToken(&x) && this->parseSepToken() &&
341 this->parseScalarToken(&y) && this->parseSepToken() &&
342 this->parseScalarToken(&w) && this->parseSepToken() &&
343 this->parseScalarToken(&h)) {
344
345 *vb = SkSVGViewBoxType(SkRect::MakeXYWH(x, y, w, h));
346 parsedValue = true;
347 // consume trailing whitespace
348 this->parseWSToken();
349 }
350 return parsedValue && this->parseEOSToken();
351}
fmalitac97796b2016-08-08 12:58:57 -0700352
353template <typename Func, typename T>
354bool SkSVGAttributeParser::parseParenthesized(const char* prefix, Func f, T* result) {
355 this->parseWSToken();
356 if (prefix && !this->parseExpectedStringToken(prefix)) {
357 return false;
358 }
359 this->parseWSToken();
360 if (!this->parseExpectedStringToken("(")) {
361 return false;
362 }
363 this->parseWSToken();
364
365 if (!f(result)) {
366 return false;
367 }
368 this->parseWSToken();
369
370 return this->parseExpectedStringToken(")");
371}
372
373bool SkSVGAttributeParser::parseMatrixToken(SkMatrix* matrix) {
374 return this->parseParenthesized("matrix", [this](SkMatrix* m) -> bool {
375 SkScalar scalars[6];
376 for (int i = 0; i < 6; ++i) {
377 if (!(this->parseScalarToken(scalars + i) &&
378 (i > 4 || this->parseSepToken()))) {
379 return false;
380 }
381 }
382
383 m->setAll(scalars[0], scalars[2], scalars[4], scalars[1], scalars[3], scalars[5], 0, 0, 1);
384 return true;
385 }, matrix);
386}
387
388bool SkSVGAttributeParser::parseTranslateToken(SkMatrix* matrix) {
389 return this->parseParenthesized("translate", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500390 SkScalar tx = 0.0, ty = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700391 this->parseWSToken();
392 if (!this->parseScalarToken(&tx)) {
393 return false;
394 }
395
Tyler Dennistona625f922020-04-15 15:52:01 -0400396 if (!this->parseSepToken() || !this->parseScalarToken(&ty)) {
397 ty = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700398 }
399
400 m->setTranslate(tx, ty);
401 return true;
402 }, matrix);
403}
404
405bool SkSVGAttributeParser::parseScaleToken(SkMatrix* matrix) {
406 return this->parseParenthesized("scale", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500407 SkScalar sx = 0.0, sy = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700408 if (!this->parseScalarToken(&sx)) {
409 return false;
410 }
411
412 if (!(this->parseSepToken() && this->parseScalarToken(&sy))) {
413 sy = sx;
414 }
415
416 m->setScale(sx, sy);
417 return true;
418 }, matrix);
419}
420
421bool SkSVGAttributeParser::parseRotateToken(SkMatrix* matrix) {
422 return this->parseParenthesized("rotate", [this](SkMatrix* m) -> bool {
423 SkScalar angle;
424 if (!this->parseScalarToken(&angle)) {
425 return false;
426 }
427
428 SkScalar cx = 0;
429 SkScalar cy = 0;
430 // optional [<cx> <cy>]
431 if (this->parseSepToken() && this->parseScalarToken(&cx)) {
432 if (!(this->parseSepToken() && this->parseScalarToken(&cy))) {
433 return false;
434 }
435 }
436
437 m->setRotate(angle, cx, cy);
438 return true;
439 }, matrix);
440}
441
442bool SkSVGAttributeParser::parseSkewXToken(SkMatrix* matrix) {
443 return this->parseParenthesized("skewX", [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->setSkewX(tanf(SkDegreesToRadians(angle)));
fmalitac97796b2016-08-08 12:58:57 -0700449 return true;
450 }, matrix);
451}
452
453bool SkSVGAttributeParser::parseSkewYToken(SkMatrix* matrix) {
454 return this->parseParenthesized("skewY", [this](SkMatrix* m) -> bool {
455 SkScalar angle;
456 if (!this->parseScalarToken(&angle)) {
457 return false;
458 }
Tyler Dennistond1e5b032020-04-08 13:02:41 -0400459 m->setSkewY(tanf(SkDegreesToRadians(angle)));
fmalitac97796b2016-08-08 12:58:57 -0700460 return true;
461 }, matrix);
462}
463
Florin Malitaf005c252020-04-08 10:10:53 -0400464// https://www.w3.org/TR/SVG11/coords.html#TransformAttribute
fmalitac97796b2016-08-08 12:58:57 -0700465bool SkSVGAttributeParser::parseTransform(SkSVGTransformType* t) {
466 SkMatrix matrix = SkMatrix::I();
467
468 bool parsed = false;
469 while (true) {
470 SkMatrix m;
471
472 if (!( this->parseMatrixToken(&m)
473 || this->parseTranslateToken(&m)
474 || this->parseScaleToken(&m)
475 || this->parseRotateToken(&m)
476 || this->parseSkewXToken(&m)
477 || this->parseSkewYToken(&m))) {
478 break;
479 }
480
481 matrix.preConcat(m);
482 parsed = true;
Tyler Denniston2d65b732020-04-15 16:08:26 -0400483
484 this->parseCommaWspToken();
fmalitac97796b2016-08-08 12:58:57 -0700485 }
486
487 this->parseWSToken();
488 if (!parsed || !this->parseEOSToken()) {
489 return false;
490 }
491
492 *t = SkSVGTransformType(matrix);
493 return true;
494}
fmalita2d961e02016-08-11 09:16:29 -0700495
Florin Malitaf005c252020-04-08 10:10:53 -0400496// https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint
fmalita2d961e02016-08-11 09:16:29 -0700497bool SkSVGAttributeParser::parsePaint(SkSVGPaint* paint) {
498 SkSVGColorType c;
fmalita28d5b722016-09-12 17:06:47 -0700499 SkSVGStringType iri;
fmalita2d961e02016-08-11 09:16:29 -0700500 bool parsedValue = false;
501 if (this->parseColor(&c)) {
502 *paint = SkSVGPaint(c);
503 parsedValue = true;
504 } else if (this->parseExpectedStringToken("none")) {
505 *paint = SkSVGPaint(SkSVGPaint::Type::kNone);
506 parsedValue = true;
507 } else if (this->parseExpectedStringToken("currentColor")) {
508 *paint = SkSVGPaint(SkSVGPaint::Type::kCurrentColor);
509 parsedValue = true;
510 } else if (this->parseExpectedStringToken("inherit")) {
511 *paint = SkSVGPaint(SkSVGPaint::Type::kInherit);
512 parsedValue = true;
fmalita28d5b722016-09-12 17:06:47 -0700513 } else if (this->parseFuncIRI(&iri)) {
Florin Malitaa3626692020-04-09 14:36:45 -0400514 *paint = SkSVGPaint(iri);
fmalita28d5b722016-09-12 17:06:47 -0700515 parsedValue = true;
fmalita2d961e02016-08-11 09:16:29 -0700516 }
517 return parsedValue && this->parseEOSToken();
518}
519
Florin Malitaf005c252020-04-08 10:10:53 -0400520// https://www.w3.org/TR/SVG11/masking.html#ClipPathProperty
Florin Malitace8840e2016-12-08 09:26:47 -0500521bool SkSVGAttributeParser::parseClipPath(SkSVGClip* clip) {
522 SkSVGStringType iri;
523 bool parsedValue = false;
524
525 if (this->parseExpectedStringToken("none")) {
526 *clip = SkSVGClip(SkSVGClip::Type::kNone);
527 parsedValue = true;
528 } else if (this->parseExpectedStringToken("inherit")) {
529 *clip = SkSVGClip(SkSVGClip::Type::kInherit);
530 parsedValue = true;
531 } else if (this->parseFuncIRI(&iri)) {
Florin Malitaa3626692020-04-09 14:36:45 -0400532 *clip = SkSVGClip(iri);
Florin Malitace8840e2016-12-08 09:26:47 -0500533 parsedValue = true;
534 }
535
536 return parsedValue && this->parseEOSToken();
537}
538
Florin Malitaf005c252020-04-08 10:10:53 -0400539// https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty
fmalita2d961e02016-08-11 09:16:29 -0700540bool SkSVGAttributeParser::parseLineCap(SkSVGLineCap* cap) {
541 static const struct {
542 SkSVGLineCap::Type fType;
543 const char* fName;
544 } gCapInfo[] = {
545 { SkSVGLineCap::Type::kButt , "butt" },
546 { SkSVGLineCap::Type::kRound , "round" },
547 { SkSVGLineCap::Type::kSquare , "square" },
548 { SkSVGLineCap::Type::kInherit, "inherit" },
549 };
550
551 bool parsedValue = false;
552 for (size_t i = 0; i < SK_ARRAY_COUNT(gCapInfo); ++i) {
553 if (this->parseExpectedStringToken(gCapInfo[i].fName)) {
554 *cap = SkSVGLineCap(gCapInfo[i].fType);
555 parsedValue = true;
556 break;
557 }
558 }
559
560 return parsedValue && this->parseEOSToken();
561}
562
Florin Malitaf005c252020-04-08 10:10:53 -0400563// https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty
fmalita2d961e02016-08-11 09:16:29 -0700564bool SkSVGAttributeParser::parseLineJoin(SkSVGLineJoin* join) {
565 static const struct {
566 SkSVGLineJoin::Type fType;
567 const char* fName;
568 } gJoinInfo[] = {
569 { SkSVGLineJoin::Type::kMiter , "miter" },
570 { SkSVGLineJoin::Type::kRound , "round" },
571 { SkSVGLineJoin::Type::kBevel , "bevel" },
572 { SkSVGLineJoin::Type::kInherit, "inherit" },
573 };
574
575 bool parsedValue = false;
576 for (size_t i = 0; i < SK_ARRAY_COUNT(gJoinInfo); ++i) {
577 if (this->parseExpectedStringToken(gJoinInfo[i].fName)) {
578 *join = SkSVGLineJoin(gJoinInfo[i].fType);
579 parsedValue = true;
580 break;
581 }
582 }
583
584 return parsedValue && this->parseEOSToken();
585}
fmalita5b31f322016-08-12 12:15:33 -0700586
Florin Malitaf005c252020-04-08 10:10:53 -0400587// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementSpreadMethodAttribute
fmalitacecd6172016-09-13 12:56:11 -0700588bool SkSVGAttributeParser::parseSpreadMethod(SkSVGSpreadMethod* spread) {
589 static const struct {
590 SkSVGSpreadMethod::Type fType;
591 const char* fName;
592 } gSpreadInfo[] = {
593 { SkSVGSpreadMethod::Type::kPad , "pad" },
594 { SkSVGSpreadMethod::Type::kReflect, "reflect" },
595 { SkSVGSpreadMethod::Type::kRepeat , "repeat" },
596 };
597
598 bool parsedValue = false;
599 for (size_t i = 0; i < SK_ARRAY_COUNT(gSpreadInfo); ++i) {
600 if (this->parseExpectedStringToken(gSpreadInfo[i].fName)) {
601 *spread = SkSVGSpreadMethod(gSpreadInfo[i].fType);
602 parsedValue = true;
603 break;
604 }
605 }
606
607 return parsedValue && this->parseEOSToken();
608}
609
Tyler Denniston308c0722020-04-14 10:53:41 -0400610// https://www.w3.org/TR/SVG11/pservers.html#StopElement
611bool SkSVGAttributeParser::parseStopColor(SkSVGStopColor* stopColor) {
612 SkSVGColorType c;
613 bool parsedValue = false;
614 if (this->parseColor(&c)) {
615 *stopColor = SkSVGStopColor(c);
616 parsedValue = true;
617 } else if (this->parseExpectedStringToken("currentColor")) {
618 *stopColor = SkSVGStopColor(SkSVGStopColor::Type::kCurrentColor);
619 parsedValue = true;
620 } else if (this->parseExpectedStringToken("inherit")) {
621 *stopColor = SkSVGStopColor(SkSVGStopColor::Type::kInherit);
622 parsedValue = true;
623 }
624 return parsedValue && this->parseEOSToken();
625}
626
Tyler Denniston30e327e2020-10-29 16:29:22 -0400627// https://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBoxUnits
628bool SkSVGAttributeParser::parseObjectBoundingBoxUnits(
629 SkSVGObjectBoundingBoxUnits* objectBoundingBoxUnits) {
Tyler Dennistonab76ab42020-10-21 15:08:45 -0400630 bool parsedValue = false;
631 if (this->parseExpectedStringToken("userSpaceOnUse")) {
Tyler Denniston30e327e2020-10-29 16:29:22 -0400632 *objectBoundingBoxUnits =
633 SkSVGObjectBoundingBoxUnits(SkSVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse);
Tyler Dennistonab76ab42020-10-21 15:08:45 -0400634 parsedValue = true;
635 } else if (this->parseExpectedStringToken("objectBoundingBox")) {
Tyler Denniston30e327e2020-10-29 16:29:22 -0400636 *objectBoundingBoxUnits =
637 SkSVGObjectBoundingBoxUnits(SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox);
Tyler Dennistonab76ab42020-10-21 15:08:45 -0400638 parsedValue = true;
639 }
640 return parsedValue && this->parseEOSToken();
641}
642
Florin Malitaf005c252020-04-08 10:10:53 -0400643// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
fmalita5b31f322016-08-12 12:15:33 -0700644bool SkSVGAttributeParser::parsePoints(SkSVGPointsType* points) {
645 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 Malitae932d4b2016-12-01 13:35:11 -0500692bool SkSVGAttributeParser::parseFillRule(SkSVGFillRule* fillRule) {
693 static const struct {
694 SkSVGFillRule::Type fType;
695 const char* fName;
696 } gFillRuleInfo[] = {
697 { SkSVGFillRule::Type::kNonZero, "nonzero" },
698 { SkSVGFillRule::Type::kEvenOdd, "evenodd" },
699 { SkSVGFillRule::Type::kInherit, "inherit" },
700 };
701
702 bool parsedValue = false;
703 for (size_t i = 0; i < SK_ARRAY_COUNT(gFillRuleInfo); ++i) {
704 if (this->parseExpectedStringToken(gFillRuleInfo[i].fName)) {
705 *fillRule = SkSVGFillRule(gFillRuleInfo[i].fType);
706 parsedValue = true;
707 break;
708 }
709 }
710
711 return parsedValue && this->parseEOSToken();
712}
Florin Malitaffe6ae42017-10-12 11:33:28 -0400713
Tyler Dennistonb3cafbc2020-10-30 15:00:48 -0400714// https://www.w3.org/TR/SVG11/filters.html#FilterProperty
715bool SkSVGAttributeParser::parseFilter(SkSVGFilterType* filter) {
716 SkSVGStringType iri;
717 bool parsedValue = false;
718
719 if (this->parseExpectedStringToken("none")) {
720 *filter = SkSVGFilterType(SkSVGFilterType::Type::kNone);
721 parsedValue = true;
722 } else if (this->parseExpectedStringToken("inherit")) {
723 *filter = SkSVGFilterType(SkSVGFilterType::Type::kInherit);
724 parsedValue = true;
725 } else if (this->parseFuncIRI(&iri)) {
726 *filter = SkSVGFilterType(iri);
727 parsedValue = true;
728 }
729
730 return parsedValue && this->parseEOSToken();
731}
732
Florin Malitaf005c252020-04-08 10:10:53 -0400733// https://www.w3.org/TR/SVG11/painting.html#VisibilityProperty
Florin Malitaffe6ae42017-10-12 11:33:28 -0400734bool SkSVGAttributeParser::parseVisibility(SkSVGVisibility* visibility) {
735 static const struct {
736 SkSVGVisibility::Type fType;
737 const char* fName;
738 } gVisibilityInfo[] = {
739 { SkSVGVisibility::Type::kVisible , "visible" },
740 { SkSVGVisibility::Type::kHidden , "hidden" },
741 { SkSVGVisibility::Type::kCollapse, "collapse" },
742 { SkSVGVisibility::Type::kInherit , "inherit" },
743 };
744
745 bool parsedValue = false;
746 for (const auto& parseInfo : gVisibilityInfo) {
747 if (this->parseExpectedStringToken(parseInfo.fName)) {
748 *visibility = SkSVGVisibility(parseInfo.fType);
749 parsedValue = true;
750 break;
751 }
752 }
753
754 return parsedValue && this->parseEOSToken();
755}
Florin Malitaf543a602017-10-13 14:07:44 -0400756
Florin Malitaf005c252020-04-08 10:10:53 -0400757// https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty
Florin Malitaf543a602017-10-13 14:07:44 -0400758bool SkSVGAttributeParser::parseDashArray(SkSVGDashArray* dashArray) {
759 bool parsedValue = false;
760 if (this->parseExpectedStringToken("none")) {
761 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kNone);
762 parsedValue = true;
763 } else if (this->parseExpectedStringToken("inherit")) {
764 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kInherit);
765 parsedValue = true;
766 } else {
767 SkTDArray<SkSVGLength> dashes;
768 for (;;) {
769 SkSVGLength dash;
770 // parseLength() also consumes trailing separators.
771 if (!this->parseLength(&dash)) {
772 break;
773 }
774
Mike Reed5edcd312018-08-08 11:23:41 -0400775 dashes.push_back(dash);
Florin Malitaf543a602017-10-13 14:07:44 -0400776 parsedValue = true;
777 }
778
779 if (parsedValue) {
780 *dashArray = SkSVGDashArray(std::move(dashes));
781 }
782 }
783
784 return parsedValue && this->parseEOSToken();
785}
Florin Malita39fe8c82020-10-20 10:43:03 -0400786
787// https://www.w3.org/TR/SVG11/text.html#FontFamilyProperty
788bool SkSVGAttributeParser::parseFontFamily(SkSVGFontFamily* family) {
789 bool parsedValue = false;
790 if (this->parseExpectedStringToken("inherit")) {
791 *family = SkSVGFontFamily();
792 parsedValue = true;
793 } else {
794 // The spec allows specifying a comma-separated list for explicit fallback order.
795 // For now, we only use the first entry and rely on the font manager to handle fallback.
796 const auto* comma = strchr(fCurPos, ',');
797 auto family_name = comma ? SkString(fCurPos, comma - fCurPos)
798 : SkString(fCurPos);
799 *family = SkSVGFontFamily(family_name.c_str());
800 fCurPos += strlen(fCurPos);
801 parsedValue = true;
802 }
803
804 return parsedValue && this->parseEOSToken();
805}
806
807// https://www.w3.org/TR/SVG11/text.html#FontSizeProperty
808bool SkSVGAttributeParser::parseFontSize(SkSVGFontSize* size) {
809 bool parsedValue = false;
810 if (this->parseExpectedStringToken("inherit")) {
811 *size = SkSVGFontSize();
812 parsedValue = true;
813 } else {
814 SkSVGLength length;
815 if (this->parseLength(&length)) {
816 *size = SkSVGFontSize(length);
817 parsedValue = true;
818 }
819 }
820
821 return parsedValue && this->parseEOSToken();
822}
823
824// https://www.w3.org/TR/SVG11/text.html#FontStyleProperty
825bool SkSVGAttributeParser::parseFontStyle(SkSVGFontStyle* style) {
826 static constexpr std::tuple<const char*, SkSVGFontStyle::Type> gStyleMap[] = {
827 { "normal" , SkSVGFontStyle::Type::kNormal },
828 { "italic" , SkSVGFontStyle::Type::kItalic },
829 { "oblique", SkSVGFontStyle::Type::kOblique },
830 { "inherit", SkSVGFontStyle::Type::kInherit },
831 };
832
833 bool parsedValue = false;
834 SkSVGFontStyle::Type type;
835
836 if (this->parseEnumMap(gStyleMap, &type)) {
837 *style = SkSVGFontStyle(type);
838 parsedValue = true;
839 }
840
841 return parsedValue && this->parseEOSToken();
842}
843
844// https://www.w3.org/TR/SVG11/text.html#FontWeightProperty
845bool SkSVGAttributeParser::parseFontWeight(SkSVGFontWeight* weight) {
846 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
875bool SkSVGAttributeParser::parseTextAnchor(SkSVGTextAnchor* anchor) {
876 static constexpr std::tuple<const char*, SkSVGTextAnchor::Type> gAnchorMap[] = {
877 { "start" , SkSVGTextAnchor::Type::kStart },
878 { "middle" , SkSVGTextAnchor::Type::kMiddle },
879 { "end" , SkSVGTextAnchor::Type::kEnd },
880 { "inherit", SkSVGTextAnchor::Type::kInherit},
881 };
882
883 bool parsedValue = false;
884 SkSVGTextAnchor::Type type;
885
886 if (this->parseEnumMap(gAnchorMap, &type)) {
887 *anchor = SkSVGTextAnchor(type);
888 parsedValue = true;
889 }
890
891 return parsedValue && this->parseEOSToken();
892}
893
Florin Malita385e7442020-10-21 16:55:46 -0400894// https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
895bool SkSVGAttributeParser::parsePreserveAspectRatio(SkSVGPreserveAspectRatio* par) {
896 static constexpr std::tuple<const char*, SkSVGPreserveAspectRatio::Align> gAlignMap[] = {
897 { "none" , SkSVGPreserveAspectRatio::kNone },
898 { "xMinYMin", SkSVGPreserveAspectRatio::kXMinYMin },
899 { "xMidYMin", SkSVGPreserveAspectRatio::kXMidYMin },
900 { "xMaxYMin", SkSVGPreserveAspectRatio::kXMaxYMin },
901 { "xMinYMid", SkSVGPreserveAspectRatio::kXMinYMid },
902 { "xMidYMid", SkSVGPreserveAspectRatio::kXMidYMid },
903 { "xMaxYMid", SkSVGPreserveAspectRatio::kXMaxYMid },
904 { "xMinYMax", SkSVGPreserveAspectRatio::kXMinYMax },
905 { "xMidYMax", SkSVGPreserveAspectRatio::kXMidYMax },
906 { "xMaxYMax", SkSVGPreserveAspectRatio::kXMaxYMax },
907 };
908
909 static constexpr std::tuple<const char*, SkSVGPreserveAspectRatio::Scale> gScaleMap[] = {
910 { "meet" , SkSVGPreserveAspectRatio::kMeet },
911 { "slice", SkSVGPreserveAspectRatio::kSlice },
912 };
913
914 bool parsedValue = false;
915
916 // ignoring optional 'defer'
917 this->parseExpectedStringToken("defer");
918 this->parseWSToken();
919
920 if (this->parseEnumMap(gAlignMap, &par->fAlign)) {
921 parsedValue = true;
922
923 // optional scaling selector
924 this->parseWSToken();
925 this->parseEnumMap(gScaleMap, &par->fScale);
926 }
927
928 return parsedValue && this->parseEOSToken();
929}