Home | History | Annotate | Download | only in model
      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 
     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 
     21 inline bool is_eos(char c) {
     22     return !c;
     23 }
     24 
     25 inline bool is_ws(char c) {
     26     return is_between(c, 1, 32);
     27 }
     28 
     29 inline bool is_sep(char c) {
     30     return is_ws(c) || c == ',' || c == ';';
     31 }
     32 
     33 } // anonymous ns
     34 
     35 SkSVGAttributeParser::SkSVGAttributeParser(const char attributeString[])
     36     : fCurPos(attributeString) {}
     37 
     38 template <typename 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 
     47 inline bool SkSVGAttributeParser::parseEOSToken() {
     48     return is_eos(*fCurPos);
     49 }
     50 
     51 inline bool SkSVGAttributeParser::parseSepToken() {
     52     return this->advanceWhile(is_sep);
     53 }
     54 
     55 inline bool SkSVGAttributeParser::parseWSToken() {
     56     return this->advanceWhile(is_ws);
     57 }
     58 
     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 
     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 
     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 
     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
    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 
    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 
    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 
    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 
    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
    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
    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
    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
    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
    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>
    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 
    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 
    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 
    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 
    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 
    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 
    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
    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
    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
    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
    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
    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
    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
    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(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
    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
    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
    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(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