• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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