1 /*
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
12 namespace {
13
14 // TODO: these should be shared with SkParse.cpp
15
is_between(char c,char min,char max)16 inline bool is_between(char c, char min, char max) {
17 SkASSERT(min <= max);
18 return (unsigned)(c - min) <= (unsigned)(max - min);
19 }
20
is_eos(char c)21 inline bool is_eos(char c) {
22 return !c;
23 }
24
is_ws(char c)25 inline bool is_ws(char c) {
26 return is_between(c, 1, 32);
27 }
28
is_sep(char c)29 inline bool is_sep(char c) {
30 return is_ws(c) || c == ',' || c == ';';
31 }
32
33 } // anonymous ns
34
SkSVGAttributeParser(const char attributeString[])35 SkSVGAttributeParser::SkSVGAttributeParser(const char attributeString[])
36 : fCurPos(attributeString) {}
37
38 template <typename F>
advanceWhile(F f)39 inline bool SkSVGAttributeParser::advanceWhile(F f) {
40 auto initial = fCurPos;
41 while (f(*fCurPos)) {
42 fCurPos++;
43 }
44 return fCurPos != initial;
45 }
46
parseEOSToken()47 inline bool SkSVGAttributeParser::parseEOSToken() {
48 return is_eos(*fCurPos);
49 }
50
parseSepToken()51 inline bool SkSVGAttributeParser::parseSepToken() {
52 return this->advanceWhile(is_sep);
53 }
54
parseWSToken()55 inline bool SkSVGAttributeParser::parseWSToken() {
56 return this->advanceWhile(is_ws);
57 }
58
parseExpectedStringToken(const char * expected)59 inline 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
parseScalarToken(SkScalar * res)75 bool 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
parseHexToken(uint32_t * res)83 bool 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
parseLengthUnitToken(SkSVGLength::Unit * unit)91 bool 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
116 // https://www.w3.org/TR/SVG/types.html#DataTypeColor
parseNamedColorToken(SkColor * c)117 bool 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
parseHexColorToken(SkColor * c)125 bool 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
parseColorComponentToken(int32_t * c)152 bool 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
parseRGBColorToken(SkColor * c)166 bool 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
parseColor(SkSVGColorType * color)184 bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) {
185 SkColor c;
186
187 // consume preceding whitespace
188 this->parseWSToken();
189
190 // TODO: rgb(...)
191 bool parsedValue = false;
192 if (this->parseHexColorToken(&c)
193 || this->parseNamedColorToken(&c)
194 || this->parseRGBColorToken(&c)) {
195 *color = SkSVGColorType(c);
196 parsedValue = true;
197
198 // consume trailing whitespace
199 this->parseWSToken();
200 }
201
202 return parsedValue && this->parseEOSToken();
203 }
204
205 // https://www.w3.org/TR/SVG/linking.html#IRIReference
parseIRI(SkSVGStringType * iri)206 bool 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
parseFuncIRI(SkSVGStringType * iri)224 bool SkSVGAttributeParser::parseFuncIRI(SkSVGStringType* iri) {
225 return this->parseParenthesized("url", [this](SkSVGStringType* iri) -> bool {
226 return this->parseIRI(iri);
227 }, iri);
228 }
229
230 // https://www.w3.org/TR/SVG/types.html#DataTypeNumber
parseNumber(SkSVGNumberType * number)231 bool SkSVGAttributeParser::parseNumber(SkSVGNumberType* number) {
232 // consume WS
233 this->parseWSToken();
234
235 SkScalar s;
236 if (this->parseScalarToken(&s)) {
237 *number = SkSVGNumberType(s);
238 // 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
parseLength(SkSVGLength * length)247 bool 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 }
261
262 // https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
parseViewBox(SkSVGViewBoxType * vb)263 bool 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 }
280
281 template <typename Func, typename T>
parseParenthesized(const char * prefix,Func f,T * result)282 bool 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
parseMatrixToken(SkMatrix * matrix)301 bool 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
parseTranslateToken(SkMatrix * matrix)316 bool SkSVGAttributeParser::parseTranslateToken(SkMatrix* matrix) {
317 return this->parseParenthesized("translate", [this](SkMatrix* m) -> bool {
318 SkScalar tx = 0.0, ty = 0.0;
319 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
parseScaleToken(SkMatrix * matrix)333 bool SkSVGAttributeParser::parseScaleToken(SkMatrix* matrix) {
334 return this->parseParenthesized("scale", [this](SkMatrix* m) -> bool {
335 SkScalar sx = 0.0, sy = 0.0;
336 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
parseRotateToken(SkMatrix * matrix)349 bool 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
parseSkewXToken(SkMatrix * matrix)370 bool 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
parseSkewYToken(SkMatrix * matrix)381 bool 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
parseTransform(SkSVGTransformType * t)393 bool 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 }
421
422 // https://www.w3.org/TR/SVG/painting.html#SpecifyingPaint
parsePaint(SkSVGPaint * paint)423 bool SkSVGAttributeParser::parsePaint(SkSVGPaint* paint) {
424 SkSVGColorType c;
425 SkSVGStringType iri;
426 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;
439 } else if (this->parseFuncIRI(&iri)) {
440 *paint = SkSVGPaint(iri.value());
441 parsedValue = true;
442 }
443 return parsedValue && this->parseEOSToken();
444 }
445
446 // https://www.w3.org/TR/SVG/masking.html#ClipPathProperty
parseClipPath(SkSVGClip * clip)447 bool 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
465 // https://www.w3.org/TR/SVG/painting.html#StrokeLinecapProperty
parseLineCap(SkSVGLineCap * cap)466 bool 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
parseLineJoin(SkSVGLineJoin * join)490 bool 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 }
512
513 // https://www.w3.org/TR/SVG/pservers.html#LinearGradientElementSpreadMethodAttribute
parseSpreadMethod(SkSVGSpreadMethod * spread)514 bool 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
536 // https://www.w3.org/TR/SVG/shapes.html#PolygonElementPointsAttribute
parsePoints(SkSVGPointsType * points)537 bool 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_back(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 }
573
574 // https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
parseFillRule(SkSVGFillRule * fillRule)575 bool 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 }
596
597 // https://www.w3.org/TR/SVG/painting.html#VisibilityProperty
parseVisibility(SkSVGVisibility * visibility)598 bool 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 }
620
621 // https://www.w3.org/TR/SVG/painting.html#StrokeDasharrayProperty
parseDashArray(SkSVGDashArray * dashArray)622 bool 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_back(dash);
640 parsedValue = true;
641 }
642
643 if (parsedValue) {
644 *dashArray = SkSVGDashArray(std::move(dashes));
645 }
646 }
647
648 return parsedValue && this->parseEOSToken();
649 }
650