blob: 8cfc6c3c3008ba300d935b48df0b231c8f340db6 [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
59inline bool SkSVGAttributeParser::parseEOSToken() {
60 return is_eos(*fCurPos);
61}
62
63inline bool SkSVGAttributeParser::parseSepToken() {
64 return this->advanceWhile(is_sep);
65}
66
67inline bool SkSVGAttributeParser::parseWSToken() {
68 return this->advanceWhile(is_ws);
69}
70
Tyler Denniston2d65b732020-04-15 16:08:26 -040071inline bool SkSVGAttributeParser::parseCommaWspToken() {
72 // comma-wsp:
73 // (wsp+ comma? wsp*) | (comma wsp*)
74 return this->parseWSToken() || this->parseExpectedStringToken(",");
75}
76
fmalitabffc2562016-08-03 10:21:11 -070077inline bool SkSVGAttributeParser::parseExpectedStringToken(const char* expected) {
78 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
101bool SkSVGAttributeParser::parseHexToken(uint32_t* res) {
102 if (const char* next = SkParse::FindHex(fCurPos, res)) {
103 fCurPos = next;
104 return true;
105 }
106 return false;
107}
108
109bool SkSVGAttributeParser::parseLengthUnitToken(SkSVGLength::Unit* unit) {
110 static const struct {
111 const char* fUnitName;
112 SkSVGLength::Unit fUnit;
113 } gUnitInfo[] = {
114 { "%" , SkSVGLength::Unit::kPercentage },
115 { "em", SkSVGLength::Unit::kEMS },
116 { "ex", SkSVGLength::Unit::kEXS },
117 { "px", SkSVGLength::Unit::kPX },
118 { "cm", SkSVGLength::Unit::kCM },
119 { "mm", SkSVGLength::Unit::kMM },
120 { "in", SkSVGLength::Unit::kIN },
121 { "pt", SkSVGLength::Unit::kPT },
122 { "pc", SkSVGLength::Unit::kPC },
123 };
124
125 for (size_t i = 0; i < SK_ARRAY_COUNT(gUnitInfo); ++i) {
126 if (this->parseExpectedStringToken(gUnitInfo[i].fUnitName)) {
127 *unit = gUnitInfo[i].fUnit;
128 return true;
129 }
130 }
131 return false;
132}
133
Florin Malitaf005c252020-04-08 10:10:53 -0400134// https://www.w3.org/TR/SVG11/types.html#DataTypeColor
fmalitabffc2562016-08-03 10:21:11 -0700135bool SkSVGAttributeParser::parseNamedColorToken(SkColor* c) {
136 if (const char* next = SkParse::FindNamedColor(fCurPos, strlen(fCurPos), c)) {
137 fCurPos = next;
138 return true;
139 }
140 return false;
141}
142
143bool SkSVGAttributeParser::parseHexColorToken(SkColor* c) {
144 uint32_t v;
145 const char* initial = fCurPos;
146
147 if (!this->parseExpectedStringToken("#") || !this->parseHexToken(&v)) {
148 return false;
149 }
150
151 switch (fCurPos - initial) {
152 case 7:
153 // matched #xxxxxxx
154 break;
155 case 4:
156 // matched '#xxx;
157 v = ((v << 12) & 0x00f00000) |
158 ((v << 8) & 0x000ff000) |
159 ((v << 4) & 0x00000ff0) |
160 ((v << 0) & 0x0000000f);
161 break;
162 default:
163 return false;
164 }
165
166 *c = v | 0xff000000;
167 return true;
168}
169
fmalita28d5b722016-09-12 17:06:47 -0700170bool SkSVGAttributeParser::parseColorComponentToken(int32_t* c) {
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400171 const auto parseIntegral = [this](int32_t* c) -> bool {
172 const char* p = SkParse::FindS32(fCurPos, c);
173 if (!p || *p == '.') {
174 // No value parsed, or fractional value.
175 return false;
176 }
177
178 if (*p == '%') {
179 *c = SkScalarRoundToInt(*c * 255.0f / 100);
180 p++;
181 }
182
183 fCurPos = p;
184 return true;
185 };
186
187 const auto parseFractional = [this](int32_t* c) -> bool {
188 SkScalar s;
189 const char* p = SkParse::FindScalar(fCurPos, &s);
190 if (!p || *p != '%') {
191 // Floating point must be a percentage (CSS2 rgb-percent syntax).
192 return false;
193 }
194 p++; // Skip '%'
195
196 *c = SkScalarRoundToInt(s * 255.0f / 100);
197 fCurPos = p;
198 return true;
199 };
200
201 if (!parseIntegral(c) && !parseFractional(c)) {
fmalita28d5b722016-09-12 17:06:47 -0700202 return false;
203 }
204
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400205 *c = SkTPin<int32_t>(*c, 0, 255);
fmalita28d5b722016-09-12 17:06:47 -0700206 return true;
207}
208
209bool SkSVGAttributeParser::parseRGBColorToken(SkColor* c) {
210 return this->parseParenthesized("rgb", [this](SkColor* c) -> bool {
211 int32_t r, g, b;
212 if (this->parseColorComponentToken(&r) &&
213 this->parseSepToken() &&
214 this->parseColorComponentToken(&g) &&
215 this->parseSepToken() &&
216 this->parseColorComponentToken(&b)) {
217
218 *c = SkColorSetRGB(static_cast<uint8_t>(r),
219 static_cast<uint8_t>(g),
220 static_cast<uint8_t>(b));
221 return true;
222 }
223 return false;
224 }, c);
225}
226
Tyler Denniston8ac25c42020-04-15 11:05:27 -0400227// https://www.w3.org/TR/SVG11/types.html#DataTypeColor
228// And https://www.w3.org/TR/CSS2/syndata.html#color-units for the alternative
229// forms supported by SVG (e.g. RGB percentages).
fmalita397a5172016-08-08 11:38:55 -0700230bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) {
fmalitabffc2562016-08-03 10:21:11 -0700231 SkColor c;
232
fmalita61f36b32016-08-08 13:58:50 -0700233 // consume preceding whitespace
234 this->parseWSToken();
235
fmalita61f36b32016-08-08 13:58:50 -0700236 bool parsedValue = false;
fmalita28d5b722016-09-12 17:06:47 -0700237 if (this->parseHexColorToken(&c)
238 || this->parseNamedColorToken(&c)
239 || this->parseRGBColorToken(&c)) {
fmalita397a5172016-08-08 11:38:55 -0700240 *color = SkSVGColorType(c);
fmalita61f36b32016-08-08 13:58:50 -0700241 parsedValue = true;
242
243 // consume trailing whitespace
244 this->parseWSToken();
fmalitabffc2562016-08-03 10:21:11 -0700245 }
246
fmalita61f36b32016-08-08 13:58:50 -0700247 return parsedValue && this->parseEOSToken();
fmalitabffc2562016-08-03 10:21:11 -0700248}
249
Florin Malitaf005c252020-04-08 10:10:53 -0400250// https://www.w3.org/TR/SVG11/linking.html#IRIReference
fmalita28d5b722016-09-12 17:06:47 -0700251bool SkSVGAttributeParser::parseIRI(SkSVGStringType* iri) {
252 // consume preceding whitespace
253 this->parseWSToken();
254
255 // we only support local fragments
256 if (!this->parseExpectedStringToken("#")) {
257 return false;
258 }
259 const auto* start = fCurPos;
260 this->advanceWhile([](char c) -> bool { return !is_eos(c) && c != ')'; });
261 if (start == fCurPos) {
262 return false;
263 }
264 *iri = SkString(start, fCurPos - start);
265 return true;
266}
267
Florin Malitaf005c252020-04-08 10:10:53 -0400268// https://www.w3.org/TR/SVG11/types.html#DataTypeFuncIRI
fmalita28d5b722016-09-12 17:06:47 -0700269bool SkSVGAttributeParser::parseFuncIRI(SkSVGStringType* iri) {
270 return this->parseParenthesized("url", [this](SkSVGStringType* iri) -> bool {
271 return this->parseIRI(iri);
272 }, iri);
273}
274
Florin Malitaf005c252020-04-08 10:10:53 -0400275// https://www.w3.org/TR/SVG11/types.html#DataTypeNumber
fmalita397a5172016-08-08 11:38:55 -0700276bool SkSVGAttributeParser::parseNumber(SkSVGNumberType* number) {
fmalitabffc2562016-08-03 10:21:11 -0700277 // consume WS
278 this->parseWSToken();
279
280 SkScalar s;
281 if (this->parseScalarToken(&s)) {
fmalita397a5172016-08-08 11:38:55 -0700282 *number = SkSVGNumberType(s);
fmalitabffc2562016-08-03 10:21:11 -0700283 // consume trailing separators
284 this->parseSepToken();
285 return true;
286 }
287
288 return false;
289}
290
Florin Malitaf005c252020-04-08 10:10:53 -0400291// https://www.w3.org/TR/SVG11/types.html#DataTypeLength
fmalitabffc2562016-08-03 10:21:11 -0700292bool SkSVGAttributeParser::parseLength(SkSVGLength* length) {
293 SkScalar s;
294 SkSVGLength::Unit u = SkSVGLength::Unit::kNumber;
295
296 if (this->parseScalarToken(&s) &&
297 (this->parseLengthUnitToken(&u) || this->parseSepToken() || this->parseEOSToken())) {
298 *length = SkSVGLength(s, u);
299 // consume trailing separators
300 this->parseSepToken();
301 return true;
302 }
303
304 return false;
305}
fmalita397a5172016-08-08 11:38:55 -0700306
Florin Malitaf005c252020-04-08 10:10:53 -0400307// https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute
fmalita397a5172016-08-08 11:38:55 -0700308bool SkSVGAttributeParser::parseViewBox(SkSVGViewBoxType* vb) {
309 SkScalar x, y, w, h;
310 this->parseWSToken();
311
312 bool parsedValue = false;
313 if (this->parseScalarToken(&x) && this->parseSepToken() &&
314 this->parseScalarToken(&y) && this->parseSepToken() &&
315 this->parseScalarToken(&w) && this->parseSepToken() &&
316 this->parseScalarToken(&h)) {
317
318 *vb = SkSVGViewBoxType(SkRect::MakeXYWH(x, y, w, h));
319 parsedValue = true;
320 // consume trailing whitespace
321 this->parseWSToken();
322 }
323 return parsedValue && this->parseEOSToken();
324}
fmalitac97796b2016-08-08 12:58:57 -0700325
326template <typename Func, typename T>
327bool SkSVGAttributeParser::parseParenthesized(const char* prefix, Func f, T* result) {
328 this->parseWSToken();
329 if (prefix && !this->parseExpectedStringToken(prefix)) {
330 return false;
331 }
332 this->parseWSToken();
333 if (!this->parseExpectedStringToken("(")) {
334 return false;
335 }
336 this->parseWSToken();
337
338 if (!f(result)) {
339 return false;
340 }
341 this->parseWSToken();
342
343 return this->parseExpectedStringToken(")");
344}
345
346bool SkSVGAttributeParser::parseMatrixToken(SkMatrix* matrix) {
347 return this->parseParenthesized("matrix", [this](SkMatrix* m) -> bool {
348 SkScalar scalars[6];
349 for (int i = 0; i < 6; ++i) {
350 if (!(this->parseScalarToken(scalars + i) &&
351 (i > 4 || this->parseSepToken()))) {
352 return false;
353 }
354 }
355
356 m->setAll(scalars[0], scalars[2], scalars[4], scalars[1], scalars[3], scalars[5], 0, 0, 1);
357 return true;
358 }, matrix);
359}
360
361bool SkSVGAttributeParser::parseTranslateToken(SkMatrix* matrix) {
362 return this->parseParenthesized("translate", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500363 SkScalar tx = 0.0, ty = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700364 this->parseWSToken();
365 if (!this->parseScalarToken(&tx)) {
366 return false;
367 }
368
Tyler Dennistona625f922020-04-15 15:52:01 -0400369 if (!this->parseSepToken() || !this->parseScalarToken(&ty)) {
370 ty = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700371 }
372
373 m->setTranslate(tx, ty);
374 return true;
375 }, matrix);
376}
377
378bool SkSVGAttributeParser::parseScaleToken(SkMatrix* matrix) {
379 return this->parseParenthesized("scale", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500380 SkScalar sx = 0.0, sy = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700381 if (!this->parseScalarToken(&sx)) {
382 return false;
383 }
384
385 if (!(this->parseSepToken() && this->parseScalarToken(&sy))) {
386 sy = sx;
387 }
388
389 m->setScale(sx, sy);
390 return true;
391 }, matrix);
392}
393
394bool SkSVGAttributeParser::parseRotateToken(SkMatrix* matrix) {
395 return this->parseParenthesized("rotate", [this](SkMatrix* m) -> bool {
396 SkScalar angle;
397 if (!this->parseScalarToken(&angle)) {
398 return false;
399 }
400
401 SkScalar cx = 0;
402 SkScalar cy = 0;
403 // optional [<cx> <cy>]
404 if (this->parseSepToken() && this->parseScalarToken(&cx)) {
405 if (!(this->parseSepToken() && this->parseScalarToken(&cy))) {
406 return false;
407 }
408 }
409
410 m->setRotate(angle, cx, cy);
411 return true;
412 }, matrix);
413}
414
415bool SkSVGAttributeParser::parseSkewXToken(SkMatrix* matrix) {
416 return this->parseParenthesized("skewX", [this](SkMatrix* m) -> bool {
417 SkScalar angle;
418 if (!this->parseScalarToken(&angle)) {
419 return false;
420 }
Tyler Dennistond1e5b032020-04-08 13:02:41 -0400421 m->setSkewX(tanf(SkDegreesToRadians(angle)));
fmalitac97796b2016-08-08 12:58:57 -0700422 return true;
423 }, matrix);
424}
425
426bool SkSVGAttributeParser::parseSkewYToken(SkMatrix* matrix) {
427 return this->parseParenthesized("skewY", [this](SkMatrix* m) -> bool {
428 SkScalar angle;
429 if (!this->parseScalarToken(&angle)) {
430 return false;
431 }
Tyler Dennistond1e5b032020-04-08 13:02:41 -0400432 m->setSkewY(tanf(SkDegreesToRadians(angle)));
fmalitac97796b2016-08-08 12:58:57 -0700433 return true;
434 }, matrix);
435}
436
Florin Malitaf005c252020-04-08 10:10:53 -0400437// https://www.w3.org/TR/SVG11/coords.html#TransformAttribute
fmalitac97796b2016-08-08 12:58:57 -0700438bool SkSVGAttributeParser::parseTransform(SkSVGTransformType* t) {
439 SkMatrix matrix = SkMatrix::I();
440
441 bool parsed = false;
442 while (true) {
443 SkMatrix m;
444
445 if (!( this->parseMatrixToken(&m)
446 || this->parseTranslateToken(&m)
447 || this->parseScaleToken(&m)
448 || this->parseRotateToken(&m)
449 || this->parseSkewXToken(&m)
450 || this->parseSkewYToken(&m))) {
451 break;
452 }
453
454 matrix.preConcat(m);
455 parsed = true;
Tyler Denniston2d65b732020-04-15 16:08:26 -0400456
457 this->parseCommaWspToken();
fmalitac97796b2016-08-08 12:58:57 -0700458 }
459
460 this->parseWSToken();
461 if (!parsed || !this->parseEOSToken()) {
462 return false;
463 }
464
465 *t = SkSVGTransformType(matrix);
466 return true;
467}
fmalita2d961e02016-08-11 09:16:29 -0700468
Florin Malitaf005c252020-04-08 10:10:53 -0400469// https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint
fmalita2d961e02016-08-11 09:16:29 -0700470bool SkSVGAttributeParser::parsePaint(SkSVGPaint* paint) {
471 SkSVGColorType c;
fmalita28d5b722016-09-12 17:06:47 -0700472 SkSVGStringType iri;
fmalita2d961e02016-08-11 09:16:29 -0700473 bool parsedValue = false;
474 if (this->parseColor(&c)) {
475 *paint = SkSVGPaint(c);
476 parsedValue = true;
477 } else if (this->parseExpectedStringToken("none")) {
478 *paint = SkSVGPaint(SkSVGPaint::Type::kNone);
479 parsedValue = true;
480 } else if (this->parseExpectedStringToken("currentColor")) {
481 *paint = SkSVGPaint(SkSVGPaint::Type::kCurrentColor);
482 parsedValue = true;
483 } else if (this->parseExpectedStringToken("inherit")) {
484 *paint = SkSVGPaint(SkSVGPaint::Type::kInherit);
485 parsedValue = true;
fmalita28d5b722016-09-12 17:06:47 -0700486 } else if (this->parseFuncIRI(&iri)) {
Florin Malitaa3626692020-04-09 14:36:45 -0400487 *paint = SkSVGPaint(iri);
fmalita28d5b722016-09-12 17:06:47 -0700488 parsedValue = true;
fmalita2d961e02016-08-11 09:16:29 -0700489 }
490 return parsedValue && this->parseEOSToken();
491}
492
Florin Malitaf005c252020-04-08 10:10:53 -0400493// https://www.w3.org/TR/SVG11/masking.html#ClipPathProperty
Florin Malitace8840e2016-12-08 09:26:47 -0500494bool SkSVGAttributeParser::parseClipPath(SkSVGClip* clip) {
495 SkSVGStringType iri;
496 bool parsedValue = false;
497
498 if (this->parseExpectedStringToken("none")) {
499 *clip = SkSVGClip(SkSVGClip::Type::kNone);
500 parsedValue = true;
501 } else if (this->parseExpectedStringToken("inherit")) {
502 *clip = SkSVGClip(SkSVGClip::Type::kInherit);
503 parsedValue = true;
504 } else if (this->parseFuncIRI(&iri)) {
Florin Malitaa3626692020-04-09 14:36:45 -0400505 *clip = SkSVGClip(iri);
Florin Malitace8840e2016-12-08 09:26:47 -0500506 parsedValue = true;
507 }
508
509 return parsedValue && this->parseEOSToken();
510}
511
Florin Malitaf005c252020-04-08 10:10:53 -0400512// https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty
fmalita2d961e02016-08-11 09:16:29 -0700513bool SkSVGAttributeParser::parseLineCap(SkSVGLineCap* cap) {
514 static const struct {
515 SkSVGLineCap::Type fType;
516 const char* fName;
517 } gCapInfo[] = {
518 { SkSVGLineCap::Type::kButt , "butt" },
519 { SkSVGLineCap::Type::kRound , "round" },
520 { SkSVGLineCap::Type::kSquare , "square" },
521 { SkSVGLineCap::Type::kInherit, "inherit" },
522 };
523
524 bool parsedValue = false;
525 for (size_t i = 0; i < SK_ARRAY_COUNT(gCapInfo); ++i) {
526 if (this->parseExpectedStringToken(gCapInfo[i].fName)) {
527 *cap = SkSVGLineCap(gCapInfo[i].fType);
528 parsedValue = true;
529 break;
530 }
531 }
532
533 return parsedValue && this->parseEOSToken();
534}
535
Florin Malitaf005c252020-04-08 10:10:53 -0400536// https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty
fmalita2d961e02016-08-11 09:16:29 -0700537bool SkSVGAttributeParser::parseLineJoin(SkSVGLineJoin* join) {
538 static const struct {
539 SkSVGLineJoin::Type fType;
540 const char* fName;
541 } gJoinInfo[] = {
542 { SkSVGLineJoin::Type::kMiter , "miter" },
543 { SkSVGLineJoin::Type::kRound , "round" },
544 { SkSVGLineJoin::Type::kBevel , "bevel" },
545 { SkSVGLineJoin::Type::kInherit, "inherit" },
546 };
547
548 bool parsedValue = false;
549 for (size_t i = 0; i < SK_ARRAY_COUNT(gJoinInfo); ++i) {
550 if (this->parseExpectedStringToken(gJoinInfo[i].fName)) {
551 *join = SkSVGLineJoin(gJoinInfo[i].fType);
552 parsedValue = true;
553 break;
554 }
555 }
556
557 return parsedValue && this->parseEOSToken();
558}
fmalita5b31f322016-08-12 12:15:33 -0700559
Florin Malitaf005c252020-04-08 10:10:53 -0400560// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementSpreadMethodAttribute
fmalitacecd6172016-09-13 12:56:11 -0700561bool SkSVGAttributeParser::parseSpreadMethod(SkSVGSpreadMethod* spread) {
562 static const struct {
563 SkSVGSpreadMethod::Type fType;
564 const char* fName;
565 } gSpreadInfo[] = {
566 { SkSVGSpreadMethod::Type::kPad , "pad" },
567 { SkSVGSpreadMethod::Type::kReflect, "reflect" },
568 { SkSVGSpreadMethod::Type::kRepeat , "repeat" },
569 };
570
571 bool parsedValue = false;
572 for (size_t i = 0; i < SK_ARRAY_COUNT(gSpreadInfo); ++i) {
573 if (this->parseExpectedStringToken(gSpreadInfo[i].fName)) {
574 *spread = SkSVGSpreadMethod(gSpreadInfo[i].fType);
575 parsedValue = true;
576 break;
577 }
578 }
579
580 return parsedValue && this->parseEOSToken();
581}
582
Tyler Denniston308c0722020-04-14 10:53:41 -0400583// https://www.w3.org/TR/SVG11/pservers.html#StopElement
584bool SkSVGAttributeParser::parseStopColor(SkSVGStopColor* stopColor) {
585 SkSVGColorType c;
586 bool parsedValue = false;
587 if (this->parseColor(&c)) {
588 *stopColor = SkSVGStopColor(c);
589 parsedValue = true;
590 } else if (this->parseExpectedStringToken("currentColor")) {
591 *stopColor = SkSVGStopColor(SkSVGStopColor::Type::kCurrentColor);
592 parsedValue = true;
593 } else if (this->parseExpectedStringToken("inherit")) {
594 *stopColor = SkSVGStopColor(SkSVGStopColor::Type::kInherit);
595 parsedValue = true;
596 }
597 return parsedValue && this->parseEOSToken();
598}
599
Tyler Dennistonab76ab42020-10-21 15:08:45 -0400600// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementGradientUnitsAttribute
601bool SkSVGAttributeParser::parseGradientUnits(SkSVGGradientUnits* gradientUnits) {
602 bool parsedValue = false;
603 if (this->parseExpectedStringToken("userSpaceOnUse")) {
604 *gradientUnits = SkSVGGradientUnits(SkSVGGradientUnits::Type::kUserSpaceOnUse);
605 parsedValue = true;
606 } else if (this->parseExpectedStringToken("objectBoundingBox")) {
607 *gradientUnits = SkSVGGradientUnits(SkSVGGradientUnits::Type::kObjectBoundingBox);
608 parsedValue = true;
609 }
610 return parsedValue && this->parseEOSToken();
611}
612
Florin Malitaf005c252020-04-08 10:10:53 -0400613// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
fmalita5b31f322016-08-12 12:15:33 -0700614bool SkSVGAttributeParser::parsePoints(SkSVGPointsType* points) {
615 SkTDArray<SkPoint> pts;
616
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400617 // Skip initial wsp.
618 // list-of-points:
619 // wsp* coordinate-pairs? wsp*
620 this->advanceWhile(is_ws);
621
fmalita5b31f322016-08-12 12:15:33 -0700622 bool parsedValue = false;
623 for (;;) {
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400624 // Adjacent coordinate-pairs separated by comma-wsp.
625 // coordinate-pairs:
626 // coordinate-pair
627 // | coordinate-pair comma-wsp coordinate-pairs
Tyler Denniston2d65b732020-04-15 16:08:26 -0400628 if (parsedValue && !this->parseCommaWspToken()) {
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400629 break;
630 }
fmalita5b31f322016-08-12 12:15:33 -0700631
632 SkScalar x, y;
633 if (!this->parseScalarToken(&x)) {
634 break;
635 }
636
Tyler Dennistonb96bb972020-04-08 17:36:11 -0400637 // Coordinate values separated by comma-wsp or '-'.
638 // coordinate-pair:
639 // coordinate comma-wsp coordinate
640 // | coordinate negative-coordinate
Tyler Denniston2d65b732020-04-15 16:08:26 -0400641 if (!this->parseCommaWspToken() && !this->parseEOSToken() && *fCurPos != '-') {
fmalita5b31f322016-08-12 12:15:33 -0700642 break;
643 }
fmalita5b31f322016-08-12 12:15:33 -0700644
645 if (!this->parseScalarToken(&y)) {
646 break;
647 }
648
Mike Reed5edcd312018-08-08 11:23:41 -0400649 pts.push_back(SkPoint::Make(x, y));
fmalita5b31f322016-08-12 12:15:33 -0700650 parsedValue = true;
651 }
652
653 if (parsedValue && this->parseEOSToken()) {
654 *points = pts;
655 return true;
656 }
657
658 return false;
659}
Florin Malitae932d4b2016-12-01 13:35:11 -0500660
Florin Malitaf005c252020-04-08 10:10:53 -0400661// https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty
Florin Malitae932d4b2016-12-01 13:35:11 -0500662bool SkSVGAttributeParser::parseFillRule(SkSVGFillRule* fillRule) {
663 static const struct {
664 SkSVGFillRule::Type fType;
665 const char* fName;
666 } gFillRuleInfo[] = {
667 { SkSVGFillRule::Type::kNonZero, "nonzero" },
668 { SkSVGFillRule::Type::kEvenOdd, "evenodd" },
669 { SkSVGFillRule::Type::kInherit, "inherit" },
670 };
671
672 bool parsedValue = false;
673 for (size_t i = 0; i < SK_ARRAY_COUNT(gFillRuleInfo); ++i) {
674 if (this->parseExpectedStringToken(gFillRuleInfo[i].fName)) {
675 *fillRule = SkSVGFillRule(gFillRuleInfo[i].fType);
676 parsedValue = true;
677 break;
678 }
679 }
680
681 return parsedValue && this->parseEOSToken();
682}
Florin Malitaffe6ae42017-10-12 11:33:28 -0400683
Florin Malitaf005c252020-04-08 10:10:53 -0400684// https://www.w3.org/TR/SVG11/painting.html#VisibilityProperty
Florin Malitaffe6ae42017-10-12 11:33:28 -0400685bool SkSVGAttributeParser::parseVisibility(SkSVGVisibility* visibility) {
686 static const struct {
687 SkSVGVisibility::Type fType;
688 const char* fName;
689 } gVisibilityInfo[] = {
690 { SkSVGVisibility::Type::kVisible , "visible" },
691 { SkSVGVisibility::Type::kHidden , "hidden" },
692 { SkSVGVisibility::Type::kCollapse, "collapse" },
693 { SkSVGVisibility::Type::kInherit , "inherit" },
694 };
695
696 bool parsedValue = false;
697 for (const auto& parseInfo : gVisibilityInfo) {
698 if (this->parseExpectedStringToken(parseInfo.fName)) {
699 *visibility = SkSVGVisibility(parseInfo.fType);
700 parsedValue = true;
701 break;
702 }
703 }
704
705 return parsedValue && this->parseEOSToken();
706}
Florin Malitaf543a602017-10-13 14:07:44 -0400707
Florin Malitaf005c252020-04-08 10:10:53 -0400708// https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty
Florin Malitaf543a602017-10-13 14:07:44 -0400709bool SkSVGAttributeParser::parseDashArray(SkSVGDashArray* dashArray) {
710 bool parsedValue = false;
711 if (this->parseExpectedStringToken("none")) {
712 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kNone);
713 parsedValue = true;
714 } else if (this->parseExpectedStringToken("inherit")) {
715 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kInherit);
716 parsedValue = true;
717 } else {
718 SkTDArray<SkSVGLength> dashes;
719 for (;;) {
720 SkSVGLength dash;
721 // parseLength() also consumes trailing separators.
722 if (!this->parseLength(&dash)) {
723 break;
724 }
725
Mike Reed5edcd312018-08-08 11:23:41 -0400726 dashes.push_back(dash);
Florin Malitaf543a602017-10-13 14:07:44 -0400727 parsedValue = true;
728 }
729
730 if (parsedValue) {
731 *dashArray = SkSVGDashArray(std::move(dashes));
732 }
733 }
734
735 return parsedValue && this->parseEOSToken();
736}
Florin Malita39fe8c82020-10-20 10:43:03 -0400737
738// https://www.w3.org/TR/SVG11/text.html#FontFamilyProperty
739bool SkSVGAttributeParser::parseFontFamily(SkSVGFontFamily* family) {
740 bool parsedValue = false;
741 if (this->parseExpectedStringToken("inherit")) {
742 *family = SkSVGFontFamily();
743 parsedValue = true;
744 } else {
745 // The spec allows specifying a comma-separated list for explicit fallback order.
746 // For now, we only use the first entry and rely on the font manager to handle fallback.
747 const auto* comma = strchr(fCurPos, ',');
748 auto family_name = comma ? SkString(fCurPos, comma - fCurPos)
749 : SkString(fCurPos);
750 *family = SkSVGFontFamily(family_name.c_str());
751 fCurPos += strlen(fCurPos);
752 parsedValue = true;
753 }
754
755 return parsedValue && this->parseEOSToken();
756}
757
758// https://www.w3.org/TR/SVG11/text.html#FontSizeProperty
759bool SkSVGAttributeParser::parseFontSize(SkSVGFontSize* size) {
760 bool parsedValue = false;
761 if (this->parseExpectedStringToken("inherit")) {
762 *size = SkSVGFontSize();
763 parsedValue = true;
764 } else {
765 SkSVGLength length;
766 if (this->parseLength(&length)) {
767 *size = SkSVGFontSize(length);
768 parsedValue = true;
769 }
770 }
771
772 return parsedValue && this->parseEOSToken();
773}
774
775// https://www.w3.org/TR/SVG11/text.html#FontStyleProperty
776bool SkSVGAttributeParser::parseFontStyle(SkSVGFontStyle* style) {
777 static constexpr std::tuple<const char*, SkSVGFontStyle::Type> gStyleMap[] = {
778 { "normal" , SkSVGFontStyle::Type::kNormal },
779 { "italic" , SkSVGFontStyle::Type::kItalic },
780 { "oblique", SkSVGFontStyle::Type::kOblique },
781 { "inherit", SkSVGFontStyle::Type::kInherit },
782 };
783
784 bool parsedValue = false;
785 SkSVGFontStyle::Type type;
786
787 if (this->parseEnumMap(gStyleMap, &type)) {
788 *style = SkSVGFontStyle(type);
789 parsedValue = true;
790 }
791
792 return parsedValue && this->parseEOSToken();
793}
794
795// https://www.w3.org/TR/SVG11/text.html#FontWeightProperty
796bool SkSVGAttributeParser::parseFontWeight(SkSVGFontWeight* weight) {
797 static constexpr std::tuple<const char*, SkSVGFontWeight::Type> gWeightMap[] = {
798 { "normal" , SkSVGFontWeight::Type::kNormal },
799 { "bold" , SkSVGFontWeight::Type::kBold },
800 { "bolder" , SkSVGFontWeight::Type::kBolder },
801 { "lighter", SkSVGFontWeight::Type::kLighter },
802 { "100" , SkSVGFontWeight::Type::k100 },
803 { "200" , SkSVGFontWeight::Type::k200 },
804 { "300" , SkSVGFontWeight::Type::k300 },
805 { "400" , SkSVGFontWeight::Type::k400 },
806 { "500" , SkSVGFontWeight::Type::k500 },
807 { "600" , SkSVGFontWeight::Type::k600 },
808 { "700" , SkSVGFontWeight::Type::k700 },
809 { "800" , SkSVGFontWeight::Type::k800 },
810 { "900" , SkSVGFontWeight::Type::k900 },
811 { "inherit", SkSVGFontWeight::Type::kInherit },
812 };
813
814 bool parsedValue = false;
815 SkSVGFontWeight::Type type;
816
817 if (this->parseEnumMap(gWeightMap, &type)) {
818 *weight = SkSVGFontWeight(type);
819 parsedValue = true;
820 }
821
822 return parsedValue && this->parseEOSToken();
823}