blob: dc328a1cf6e6ac760ec7a2217995eb33fa7d8fd1 [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
8#include "SkParse.h"
9#include "SkSVGAttributeParser.h"
10#include "SkSVGTypes.h"
11
12namespace {
13
14// TODO: these should be shared with SkParse.cpp
15
16inline bool is_between(char c, char min, char max) {
17 SkASSERT(min <= max);
18 return (unsigned)(c - min) <= (unsigned)(max - min);
19}
20
21inline bool is_eos(char c) {
22 return !c;
23}
24
25inline bool is_ws(char c) {
26 return is_between(c, 1, 32);
27}
28
29inline bool is_sep(char c) {
30 return is_ws(c) || c == ',' || c == ';';
31}
32
33} // anonymous ns
34
35SkSVGAttributeParser::SkSVGAttributeParser(const char attributeString[])
36 : fCurPos(attributeString) {}
37
38template <typename F>
39inline bool SkSVGAttributeParser::advanceWhile(F f) {
40 auto initial = fCurPos;
41 while (f(*fCurPos)) {
42 fCurPos++;
43 }
44 return fCurPos != initial;
45}
46
47inline bool SkSVGAttributeParser::parseEOSToken() {
48 return is_eos(*fCurPos);
49}
50
51inline bool SkSVGAttributeParser::parseSepToken() {
52 return this->advanceWhile(is_sep);
53}
54
55inline bool SkSVGAttributeParser::parseWSToken() {
56 return this->advanceWhile(is_ws);
57}
58
59inline bool SkSVGAttributeParser::parseExpectedStringToken(const char* expected) {
60 const char* c = fCurPos;
61
62 while (*c && *expected && *c == *expected) {
63 c++;
64 expected++;
65 }
66
67 if (*expected) {
68 return false;
69 }
70
71 fCurPos = c;
72 return true;
73}
74
75bool SkSVGAttributeParser::parseScalarToken(SkScalar* res) {
76 if (const char* next = SkParse::FindScalar(fCurPos, res)) {
77 fCurPos = next;
78 return true;
79 }
80 return false;
81}
82
83bool SkSVGAttributeParser::parseHexToken(uint32_t* res) {
84 if (const char* next = SkParse::FindHex(fCurPos, res)) {
85 fCurPos = next;
86 return true;
87 }
88 return false;
89}
90
91bool SkSVGAttributeParser::parseLengthUnitToken(SkSVGLength::Unit* unit) {
92 static const struct {
93 const char* fUnitName;
94 SkSVGLength::Unit fUnit;
95 } gUnitInfo[] = {
96 { "%" , SkSVGLength::Unit::kPercentage },
97 { "em", SkSVGLength::Unit::kEMS },
98 { "ex", SkSVGLength::Unit::kEXS },
99 { "px", SkSVGLength::Unit::kPX },
100 { "cm", SkSVGLength::Unit::kCM },
101 { "mm", SkSVGLength::Unit::kMM },
102 { "in", SkSVGLength::Unit::kIN },
103 { "pt", SkSVGLength::Unit::kPT },
104 { "pc", SkSVGLength::Unit::kPC },
105 };
106
107 for (size_t i = 0; i < SK_ARRAY_COUNT(gUnitInfo); ++i) {
108 if (this->parseExpectedStringToken(gUnitInfo[i].fUnitName)) {
109 *unit = gUnitInfo[i].fUnit;
110 return true;
111 }
112 }
113 return false;
114}
115
fmalita28d5b722016-09-12 17:06:47 -0700116// https://www.w3.org/TR/SVG/types.html#DataTypeColor
fmalitabffc2562016-08-03 10:21:11 -0700117bool SkSVGAttributeParser::parseNamedColorToken(SkColor* c) {
118 if (const char* next = SkParse::FindNamedColor(fCurPos, strlen(fCurPos), c)) {
119 fCurPos = next;
120 return true;
121 }
122 return false;
123}
124
125bool SkSVGAttributeParser::parseHexColorToken(SkColor* c) {
126 uint32_t v;
127 const char* initial = fCurPos;
128
129 if (!this->parseExpectedStringToken("#") || !this->parseHexToken(&v)) {
130 return false;
131 }
132
133 switch (fCurPos - initial) {
134 case 7:
135 // matched #xxxxxxx
136 break;
137 case 4:
138 // matched '#xxx;
139 v = ((v << 12) & 0x00f00000) |
140 ((v << 8) & 0x000ff000) |
141 ((v << 4) & 0x00000ff0) |
142 ((v << 0) & 0x0000000f);
143 break;
144 default:
145 return false;
146 }
147
148 *c = v | 0xff000000;
149 return true;
150}
151
fmalita28d5b722016-09-12 17:06:47 -0700152bool SkSVGAttributeParser::parseColorComponentToken(int32_t* c) {
153 fCurPos = SkParse::FindS32(fCurPos, c);
154 if (!fCurPos) {
155 return false;
156 }
157
158 if (*fCurPos == '%') {
159 *c = SkScalarRoundToInt(*c * 255.0f / 100);
160 fCurPos++;
161 }
162
163 return true;
164}
165
166bool SkSVGAttributeParser::parseRGBColorToken(SkColor* c) {
167 return this->parseParenthesized("rgb", [this](SkColor* c) -> bool {
168 int32_t r, g, b;
169 if (this->parseColorComponentToken(&r) &&
170 this->parseSepToken() &&
171 this->parseColorComponentToken(&g) &&
172 this->parseSepToken() &&
173 this->parseColorComponentToken(&b)) {
174
175 *c = SkColorSetRGB(static_cast<uint8_t>(r),
176 static_cast<uint8_t>(g),
177 static_cast<uint8_t>(b));
178 return true;
179 }
180 return false;
181 }, c);
182}
183
fmalita397a5172016-08-08 11:38:55 -0700184bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) {
fmalitabffc2562016-08-03 10:21:11 -0700185 SkColor c;
186
fmalita61f36b32016-08-08 13:58:50 -0700187 // consume preceding whitespace
188 this->parseWSToken();
189
fmalitabffc2562016-08-03 10:21:11 -0700190 // TODO: rgb(...)
fmalita61f36b32016-08-08 13:58:50 -0700191 bool parsedValue = false;
fmalita28d5b722016-09-12 17:06:47 -0700192 if (this->parseHexColorToken(&c)
193 || this->parseNamedColorToken(&c)
194 || this->parseRGBColorToken(&c)) {
fmalita397a5172016-08-08 11:38:55 -0700195 *color = SkSVGColorType(c);
fmalita61f36b32016-08-08 13:58:50 -0700196 parsedValue = true;
197
198 // consume trailing whitespace
199 this->parseWSToken();
fmalitabffc2562016-08-03 10:21:11 -0700200 }
201
fmalita61f36b32016-08-08 13:58:50 -0700202 return parsedValue && this->parseEOSToken();
fmalitabffc2562016-08-03 10:21:11 -0700203}
204
fmalita28d5b722016-09-12 17:06:47 -0700205// https://www.w3.org/TR/SVG/linking.html#IRIReference
206bool SkSVGAttributeParser::parseIRI(SkSVGStringType* iri) {
207 // consume preceding whitespace
208 this->parseWSToken();
209
210 // we only support local fragments
211 if (!this->parseExpectedStringToken("#")) {
212 return false;
213 }
214 const auto* start = fCurPos;
215 this->advanceWhile([](char c) -> bool { return !is_eos(c) && c != ')'; });
216 if (start == fCurPos) {
217 return false;
218 }
219 *iri = SkString(start, fCurPos - start);
220 return true;
221}
222
223// https://www.w3.org/TR/SVG/types.html#DataTypeFuncIRI
224bool SkSVGAttributeParser::parseFuncIRI(SkSVGStringType* iri) {
225 return this->parseParenthesized("url", [this](SkSVGStringType* iri) -> bool {
226 return this->parseIRI(iri);
227 }, iri);
228}
229
fmalitabffc2562016-08-03 10:21:11 -0700230// https://www.w3.org/TR/SVG/types.html#DataTypeNumber
fmalita397a5172016-08-08 11:38:55 -0700231bool SkSVGAttributeParser::parseNumber(SkSVGNumberType* number) {
fmalitabffc2562016-08-03 10:21:11 -0700232 // consume WS
233 this->parseWSToken();
234
235 SkScalar s;
236 if (this->parseScalarToken(&s)) {
fmalita397a5172016-08-08 11:38:55 -0700237 *number = SkSVGNumberType(s);
fmalitabffc2562016-08-03 10:21:11 -0700238 // consume trailing separators
239 this->parseSepToken();
240 return true;
241 }
242
243 return false;
244}
245
246// https://www.w3.org/TR/SVG/types.html#DataTypeLength
247bool SkSVGAttributeParser::parseLength(SkSVGLength* length) {
248 SkScalar s;
249 SkSVGLength::Unit u = SkSVGLength::Unit::kNumber;
250
251 if (this->parseScalarToken(&s) &&
252 (this->parseLengthUnitToken(&u) || this->parseSepToken() || this->parseEOSToken())) {
253 *length = SkSVGLength(s, u);
254 // consume trailing separators
255 this->parseSepToken();
256 return true;
257 }
258
259 return false;
260}
fmalita397a5172016-08-08 11:38:55 -0700261
262// https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
263bool SkSVGAttributeParser::parseViewBox(SkSVGViewBoxType* vb) {
264 SkScalar x, y, w, h;
265 this->parseWSToken();
266
267 bool parsedValue = false;
268 if (this->parseScalarToken(&x) && this->parseSepToken() &&
269 this->parseScalarToken(&y) && this->parseSepToken() &&
270 this->parseScalarToken(&w) && this->parseSepToken() &&
271 this->parseScalarToken(&h)) {
272
273 *vb = SkSVGViewBoxType(SkRect::MakeXYWH(x, y, w, h));
274 parsedValue = true;
275 // consume trailing whitespace
276 this->parseWSToken();
277 }
278 return parsedValue && this->parseEOSToken();
279}
fmalitac97796b2016-08-08 12:58:57 -0700280
281template <typename Func, typename T>
282bool SkSVGAttributeParser::parseParenthesized(const char* prefix, Func f, T* result) {
283 this->parseWSToken();
284 if (prefix && !this->parseExpectedStringToken(prefix)) {
285 return false;
286 }
287 this->parseWSToken();
288 if (!this->parseExpectedStringToken("(")) {
289 return false;
290 }
291 this->parseWSToken();
292
293 if (!f(result)) {
294 return false;
295 }
296 this->parseWSToken();
297
298 return this->parseExpectedStringToken(")");
299}
300
301bool SkSVGAttributeParser::parseMatrixToken(SkMatrix* matrix) {
302 return this->parseParenthesized("matrix", [this](SkMatrix* m) -> bool {
303 SkScalar scalars[6];
304 for (int i = 0; i < 6; ++i) {
305 if (!(this->parseScalarToken(scalars + i) &&
306 (i > 4 || this->parseSepToken()))) {
307 return false;
308 }
309 }
310
311 m->setAll(scalars[0], scalars[2], scalars[4], scalars[1], scalars[3], scalars[5], 0, 0, 1);
312 return true;
313 }, matrix);
314}
315
316bool SkSVGAttributeParser::parseTranslateToken(SkMatrix* matrix) {
317 return this->parseParenthesized("translate", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500318 SkScalar tx = 0.0, ty = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700319 this->parseWSToken();
320 if (!this->parseScalarToken(&tx)) {
321 return false;
322 }
323
324 if (!(this->parseSepToken() && this->parseScalarToken(&ty))) {
325 ty = tx;
326 }
327
328 m->setTranslate(tx, ty);
329 return true;
330 }, matrix);
331}
332
333bool SkSVGAttributeParser::parseScaleToken(SkMatrix* matrix) {
334 return this->parseParenthesized("scale", [this](SkMatrix* m) -> bool {
Kevin Lubick42846132018-01-05 10:11:11 -0500335 SkScalar sx = 0.0, sy = 0.0;
fmalitac97796b2016-08-08 12:58:57 -0700336 if (!this->parseScalarToken(&sx)) {
337 return false;
338 }
339
340 if (!(this->parseSepToken() && this->parseScalarToken(&sy))) {
341 sy = sx;
342 }
343
344 m->setScale(sx, sy);
345 return true;
346 }, matrix);
347}
348
349bool SkSVGAttributeParser::parseRotateToken(SkMatrix* matrix) {
350 return this->parseParenthesized("rotate", [this](SkMatrix* m) -> bool {
351 SkScalar angle;
352 if (!this->parseScalarToken(&angle)) {
353 return false;
354 }
355
356 SkScalar cx = 0;
357 SkScalar cy = 0;
358 // optional [<cx> <cy>]
359 if (this->parseSepToken() && this->parseScalarToken(&cx)) {
360 if (!(this->parseSepToken() && this->parseScalarToken(&cy))) {
361 return false;
362 }
363 }
364
365 m->setRotate(angle, cx, cy);
366 return true;
367 }, matrix);
368}
369
370bool SkSVGAttributeParser::parseSkewXToken(SkMatrix* matrix) {
371 return this->parseParenthesized("skewX", [this](SkMatrix* m) -> bool {
372 SkScalar angle;
373 if (!this->parseScalarToken(&angle)) {
374 return false;
375 }
376 m->setSkewX(angle);
377 return true;
378 }, matrix);
379}
380
381bool SkSVGAttributeParser::parseSkewYToken(SkMatrix* matrix) {
382 return this->parseParenthesized("skewY", [this](SkMatrix* m) -> bool {
383 SkScalar angle;
384 if (!this->parseScalarToken(&angle)) {
385 return false;
386 }
387 m->setSkewY(angle);
388 return true;
389 }, matrix);
390}
391
392// https://www.w3.org/TR/SVG/coords.html#TransformAttribute
393bool SkSVGAttributeParser::parseTransform(SkSVGTransformType* t) {
394 SkMatrix matrix = SkMatrix::I();
395
396 bool parsed = false;
397 while (true) {
398 SkMatrix m;
399
400 if (!( this->parseMatrixToken(&m)
401 || this->parseTranslateToken(&m)
402 || this->parseScaleToken(&m)
403 || this->parseRotateToken(&m)
404 || this->parseSkewXToken(&m)
405 || this->parseSkewYToken(&m))) {
406 break;
407 }
408
409 matrix.preConcat(m);
410 parsed = true;
411 }
412
413 this->parseWSToken();
414 if (!parsed || !this->parseEOSToken()) {
415 return false;
416 }
417
418 *t = SkSVGTransformType(matrix);
419 return true;
420}
fmalita2d961e02016-08-11 09:16:29 -0700421
422// https://www.w3.org/TR/SVG/painting.html#SpecifyingPaint
423bool SkSVGAttributeParser::parsePaint(SkSVGPaint* paint) {
424 SkSVGColorType c;
fmalita28d5b722016-09-12 17:06:47 -0700425 SkSVGStringType iri;
fmalita2d961e02016-08-11 09:16:29 -0700426 bool parsedValue = false;
427 if (this->parseColor(&c)) {
428 *paint = SkSVGPaint(c);
429 parsedValue = true;
430 } else if (this->parseExpectedStringToken("none")) {
431 *paint = SkSVGPaint(SkSVGPaint::Type::kNone);
432 parsedValue = true;
433 } else if (this->parseExpectedStringToken("currentColor")) {
434 *paint = SkSVGPaint(SkSVGPaint::Type::kCurrentColor);
435 parsedValue = true;
436 } else if (this->parseExpectedStringToken("inherit")) {
437 *paint = SkSVGPaint(SkSVGPaint::Type::kInherit);
438 parsedValue = true;
fmalita28d5b722016-09-12 17:06:47 -0700439 } else if (this->parseFuncIRI(&iri)) {
440 *paint = SkSVGPaint(iri.value());
441 parsedValue = true;
fmalita2d961e02016-08-11 09:16:29 -0700442 }
443 return parsedValue && this->parseEOSToken();
444}
445
Florin Malitace8840e2016-12-08 09:26:47 -0500446// https://www.w3.org/TR/SVG/masking.html#ClipPathProperty
447bool SkSVGAttributeParser::parseClipPath(SkSVGClip* clip) {
448 SkSVGStringType iri;
449 bool parsedValue = false;
450
451 if (this->parseExpectedStringToken("none")) {
452 *clip = SkSVGClip(SkSVGClip::Type::kNone);
453 parsedValue = true;
454 } else if (this->parseExpectedStringToken("inherit")) {
455 *clip = SkSVGClip(SkSVGClip::Type::kInherit);
456 parsedValue = true;
457 } else if (this->parseFuncIRI(&iri)) {
458 *clip = SkSVGClip(iri.value());
459 parsedValue = true;
460 }
461
462 return parsedValue && this->parseEOSToken();
463}
464
fmalita2d961e02016-08-11 09:16:29 -0700465// https://www.w3.org/TR/SVG/painting.html#StrokeLinecapProperty
466bool SkSVGAttributeParser::parseLineCap(SkSVGLineCap* cap) {
467 static const struct {
468 SkSVGLineCap::Type fType;
469 const char* fName;
470 } gCapInfo[] = {
471 { SkSVGLineCap::Type::kButt , "butt" },
472 { SkSVGLineCap::Type::kRound , "round" },
473 { SkSVGLineCap::Type::kSquare , "square" },
474 { SkSVGLineCap::Type::kInherit, "inherit" },
475 };
476
477 bool parsedValue = false;
478 for (size_t i = 0; i < SK_ARRAY_COUNT(gCapInfo); ++i) {
479 if (this->parseExpectedStringToken(gCapInfo[i].fName)) {
480 *cap = SkSVGLineCap(gCapInfo[i].fType);
481 parsedValue = true;
482 break;
483 }
484 }
485
486 return parsedValue && this->parseEOSToken();
487}
488
489// https://www.w3.org/TR/SVG/painting.html#StrokeLinejoinProperty
490bool SkSVGAttributeParser::parseLineJoin(SkSVGLineJoin* join) {
491 static const struct {
492 SkSVGLineJoin::Type fType;
493 const char* fName;
494 } gJoinInfo[] = {
495 { SkSVGLineJoin::Type::kMiter , "miter" },
496 { SkSVGLineJoin::Type::kRound , "round" },
497 { SkSVGLineJoin::Type::kBevel , "bevel" },
498 { SkSVGLineJoin::Type::kInherit, "inherit" },
499 };
500
501 bool parsedValue = false;
502 for (size_t i = 0; i < SK_ARRAY_COUNT(gJoinInfo); ++i) {
503 if (this->parseExpectedStringToken(gJoinInfo[i].fName)) {
504 *join = SkSVGLineJoin(gJoinInfo[i].fType);
505 parsedValue = true;
506 break;
507 }
508 }
509
510 return parsedValue && this->parseEOSToken();
511}
fmalita5b31f322016-08-12 12:15:33 -0700512
fmalitacecd6172016-09-13 12:56:11 -0700513// https://www.w3.org/TR/SVG/pservers.html#LinearGradientElementSpreadMethodAttribute
514bool SkSVGAttributeParser::parseSpreadMethod(SkSVGSpreadMethod* spread) {
515 static const struct {
516 SkSVGSpreadMethod::Type fType;
517 const char* fName;
518 } gSpreadInfo[] = {
519 { SkSVGSpreadMethod::Type::kPad , "pad" },
520 { SkSVGSpreadMethod::Type::kReflect, "reflect" },
521 { SkSVGSpreadMethod::Type::kRepeat , "repeat" },
522 };
523
524 bool parsedValue = false;
525 for (size_t i = 0; i < SK_ARRAY_COUNT(gSpreadInfo); ++i) {
526 if (this->parseExpectedStringToken(gSpreadInfo[i].fName)) {
527 *spread = SkSVGSpreadMethod(gSpreadInfo[i].fType);
528 parsedValue = true;
529 break;
530 }
531 }
532
533 return parsedValue && this->parseEOSToken();
534}
535
fmalita5b31f322016-08-12 12:15:33 -0700536// https://www.w3.org/TR/SVG/shapes.html#PolygonElementPointsAttribute
537bool SkSVGAttributeParser::parsePoints(SkSVGPointsType* points) {
538 SkTDArray<SkPoint> pts;
539
540 bool parsedValue = false;
541 for (;;) {
542 this->parseWSToken();
543
544 SkScalar x, y;
545 if (!this->parseScalarToken(&x)) {
546 break;
547 }
548
549 // comma-wsp:
550 // (wsp+ comma? wsp*) | (comma wsp*)
551 bool wsp = this->parseWSToken();
552 bool comma = this->parseExpectedStringToken(",");
553 if (!(wsp || comma)) {
554 break;
555 }
556 this->parseWSToken();
557
558 if (!this->parseScalarToken(&y)) {
559 break;
560 }
561
562 pts.push(SkPoint::Make(x, y));
563 parsedValue = true;
564 }
565
566 if (parsedValue && this->parseEOSToken()) {
567 *points = pts;
568 return true;
569 }
570
571 return false;
572}
Florin Malitae932d4b2016-12-01 13:35:11 -0500573
574// https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
575bool SkSVGAttributeParser::parseFillRule(SkSVGFillRule* fillRule) {
576 static const struct {
577 SkSVGFillRule::Type fType;
578 const char* fName;
579 } gFillRuleInfo[] = {
580 { SkSVGFillRule::Type::kNonZero, "nonzero" },
581 { SkSVGFillRule::Type::kEvenOdd, "evenodd" },
582 { SkSVGFillRule::Type::kInherit, "inherit" },
583 };
584
585 bool parsedValue = false;
586 for (size_t i = 0; i < SK_ARRAY_COUNT(gFillRuleInfo); ++i) {
587 if (this->parseExpectedStringToken(gFillRuleInfo[i].fName)) {
588 *fillRule = SkSVGFillRule(gFillRuleInfo[i].fType);
589 parsedValue = true;
590 break;
591 }
592 }
593
594 return parsedValue && this->parseEOSToken();
595}
Florin Malitaffe6ae42017-10-12 11:33:28 -0400596
597// https://www.w3.org/TR/SVG/painting.html#VisibilityProperty
598bool SkSVGAttributeParser::parseVisibility(SkSVGVisibility* visibility) {
599 static const struct {
600 SkSVGVisibility::Type fType;
601 const char* fName;
602 } gVisibilityInfo[] = {
603 { SkSVGVisibility::Type::kVisible , "visible" },
604 { SkSVGVisibility::Type::kHidden , "hidden" },
605 { SkSVGVisibility::Type::kCollapse, "collapse" },
606 { SkSVGVisibility::Type::kInherit , "inherit" },
607 };
608
609 bool parsedValue = false;
610 for (const auto& parseInfo : gVisibilityInfo) {
611 if (this->parseExpectedStringToken(parseInfo.fName)) {
612 *visibility = SkSVGVisibility(parseInfo.fType);
613 parsedValue = true;
614 break;
615 }
616 }
617
618 return parsedValue && this->parseEOSToken();
619}
Florin Malitaf543a602017-10-13 14:07:44 -0400620
621// https://www.w3.org/TR/SVG/painting.html#StrokeDasharrayProperty
622bool SkSVGAttributeParser::parseDashArray(SkSVGDashArray* dashArray) {
623 bool parsedValue = false;
624 if (this->parseExpectedStringToken("none")) {
625 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kNone);
626 parsedValue = true;
627 } else if (this->parseExpectedStringToken("inherit")) {
628 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kInherit);
629 parsedValue = true;
630 } else {
631 SkTDArray<SkSVGLength> dashes;
632 for (;;) {
633 SkSVGLength dash;
634 // parseLength() also consumes trailing separators.
635 if (!this->parseLength(&dash)) {
636 break;
637 }
638
639 dashes.push(dash);
640 parsedValue = true;
641 }
642
643 if (parsedValue) {
644 *dashArray = SkSVGDashArray(std::move(dashes));
645 }
646 }
647
648 return parsedValue && this->parseEOSToken();
649}