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
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
116// https://www.w3.org/TR/SVG/types.html#DataTypeColor
117bool 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
152bool 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
184bool 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
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
230// https://www.w3.org/TR/SVG/types.html#DataTypeNumber
231bool 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
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}
261
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}
280
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 {
318        SkScalar tx, ty;
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
333bool SkSVGAttributeParser::parseScaleToken(SkMatrix* matrix) {
334    return this->parseParenthesized("scale", [this](SkMatrix* m) -> bool {
335        SkScalar sx, sy;
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
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}
421
422// https://www.w3.org/TR/SVG/painting.html#SpecifyingPaint
423bool 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
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
465// 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}
512
513// 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
536// 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}
573
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}
596