Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2002, 2003 The Karbon Developers
      3  * Copyright (C) 2006 Alexander Kellett <lypanov (at) kde.org>
      4  * Copyright (C) 2006, 2007 Rob Buis <buis (at) kde.org>
      5  * Copyright (C) 2007, 2009, 2013 Apple Inc. All rights reserved.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  */
     22 
     23 #include "config.h"
     24 #include "core/svg/SVGParserUtilities.h"
     25 
     26 #include "core/svg/SVGPointList.h"
     27 #include "platform/geometry/FloatRect.h"
     28 #include "platform/transforms/AffineTransform.h"
     29 #include "wtf/ASCIICType.h"
     30 #include "wtf/text/StringHash.h"
     31 #include <limits>
     32 
     33 namespace blink {
     34 
     35 template <typename FloatType>
     36 static inline bool isValidRange(const FloatType& x)
     37 {
     38     static const FloatType max = std::numeric_limits<FloatType>::max();
     39     return x >= -max && x <= max;
     40 }
     41 
     42 // We use this generic parseNumber function to allow the Path parsing code to work
     43 // at a higher precision internally, without any unnecessary runtime cost or code
     44 // complexity.
     45 template <typename CharType, typename FloatType>
     46 static bool genericParseNumber(const CharType*& ptr, const CharType* end, FloatType& number, WhitespaceMode mode)
     47 {
     48     FloatType integer, decimal, frac, exponent;
     49     int sign, expsign;
     50     const CharType* start = ptr;
     51 
     52     exponent = 0;
     53     integer = 0;
     54     frac = 1;
     55     decimal = 0;
     56     sign = 1;
     57     expsign = 1;
     58 
     59     if (mode & AllowLeadingWhitespace)
     60         skipOptionalSVGSpaces(ptr, end);
     61 
     62     // read the sign
     63     if (ptr < end && *ptr == '+')
     64         ptr++;
     65     else if (ptr < end && *ptr == '-') {
     66         ptr++;
     67         sign = -1;
     68     }
     69 
     70     if (ptr == end || ((*ptr < '0' || *ptr > '9') && *ptr != '.'))
     71         // The first character of a number must be one of [0-9+-.]
     72         return false;
     73 
     74     // read the integer part, build right-to-left
     75     const CharType* ptrStartIntPart = ptr;
     76     while (ptr < end && *ptr >= '0' && *ptr <= '9')
     77         ++ptr; // Advance to first non-digit.
     78 
     79     if (ptr != ptrStartIntPart) {
     80         const CharType* ptrScanIntPart = ptr - 1;
     81         FloatType multiplier = 1;
     82         while (ptrScanIntPart >= ptrStartIntPart) {
     83             integer += multiplier * static_cast<FloatType>(*(ptrScanIntPart--) - '0');
     84             multiplier *= 10;
     85         }
     86         // Bail out early if this overflows.
     87         if (!isValidRange(integer))
     88             return false;
     89     }
     90 
     91     if (ptr < end && *ptr == '.') { // read the decimals
     92         ptr++;
     93 
     94         // There must be a least one digit following the .
     95         if (ptr >= end || *ptr < '0' || *ptr > '9')
     96             return false;
     97 
     98         while (ptr < end && *ptr >= '0' && *ptr <= '9')
     99             decimal += (*(ptr++) - '0') * (frac *= static_cast<FloatType>(0.1));
    100     }
    101 
    102     // read the exponent part
    103     if (ptr != start && ptr + 1 < end && (*ptr == 'e' || *ptr == 'E')
    104         && (ptr[1] != 'x' && ptr[1] != 'm')) {
    105         ptr++;
    106 
    107         // read the sign of the exponent
    108         if (*ptr == '+')
    109             ptr++;
    110         else if (*ptr == '-') {
    111             ptr++;
    112             expsign = -1;
    113         }
    114 
    115         // There must be an exponent
    116         if (ptr >= end || *ptr < '0' || *ptr > '9')
    117             return false;
    118 
    119         while (ptr < end && *ptr >= '0' && *ptr <= '9') {
    120             exponent *= static_cast<FloatType>(10);
    121             exponent += *ptr - '0';
    122             ptr++;
    123         }
    124         // Make sure exponent is valid.
    125         if (!isValidRange(exponent) || exponent > std::numeric_limits<FloatType>::max_exponent)
    126             return false;
    127     }
    128 
    129     number = integer + decimal;
    130     number *= sign;
    131 
    132     if (exponent)
    133         number *= static_cast<FloatType>(pow(10.0, expsign * static_cast<int>(exponent)));
    134 
    135     // Don't return Infinity() or NaN().
    136     if (!isValidRange(number))
    137         return false;
    138 
    139     if (start == ptr)
    140         return false;
    141 
    142     if (mode & AllowTrailingWhitespace)
    143         skipOptionalSVGSpacesOrDelimiter(ptr, end);
    144 
    145     return true;
    146 }
    147 
    148 bool parseNumber(const LChar*& ptr, const LChar* end, float& number, WhitespaceMode mode)
    149 {
    150     return genericParseNumber(ptr, end, number, mode);
    151 }
    152 
    153 bool parseNumber(const UChar*& ptr, const UChar* end, float& number, WhitespaceMode mode)
    154 {
    155     return genericParseNumber(ptr, end, number, mode);
    156 }
    157 
    158 // only used to parse largeArcFlag and sweepFlag which must be a "0" or "1"
    159 // and might not have any whitespace/comma after it
    160 template <typename CharType>
    161 bool genericParseArcFlag(const CharType*& ptr, const CharType* end, bool& flag)
    162 {
    163     if (ptr >= end)
    164         return false;
    165     const CharType flagChar = *ptr++;
    166     if (flagChar == '0')
    167         flag = false;
    168     else if (flagChar == '1')
    169         flag = true;
    170     else
    171         return false;
    172 
    173     skipOptionalSVGSpacesOrDelimiter(ptr, end);
    174 
    175     return true;
    176 }
    177 
    178 bool parseArcFlag(const LChar*& ptr, const LChar* end, bool& flag)
    179 {
    180     return genericParseArcFlag(ptr, end, flag);
    181 }
    182 
    183 bool parseArcFlag(const UChar*& ptr, const UChar* end, bool& flag)
    184 {
    185     return genericParseArcFlag(ptr, end, flag);
    186 }
    187 
    188 template<typename CharType>
    189 static bool genericParseNumberOptionalNumber(const CharType*& ptr, const CharType* end, float& x, float& y)
    190 {
    191     if (!parseNumber(ptr, end, x))
    192         return false;
    193 
    194     if (ptr == end)
    195         y = x;
    196     else if (!parseNumber(ptr, end, y, AllowLeadingAndTrailingWhitespace))
    197         return false;
    198 
    199     return ptr == end;
    200 }
    201 
    202 bool parseNumberOptionalNumber(const String& string, float& x, float& y)
    203 {
    204     if (string.isEmpty())
    205         return false;
    206 
    207     if (string.is8Bit()) {
    208         const LChar* ptr = string.characters8();
    209         const LChar* end = ptr + string.length();
    210         return genericParseNumberOptionalNumber(ptr, end, x, y);
    211     }
    212     const UChar* ptr = string.characters16();
    213     const UChar* end = ptr + string.length();
    214     return genericParseNumberOptionalNumber(ptr, end, x, y);
    215 }
    216 
    217 template<typename CharType>
    218 bool genericParseNumberOrPercentage(const CharType*& ptr, const CharType* end, float& number)
    219 {
    220     if (genericParseNumber(ptr, end, number, AllowLeadingWhitespace)) {
    221         if (ptr == end)
    222             return true;
    223 
    224         bool isPercentage = (*ptr == '%');
    225         if (isPercentage)
    226             ptr++;
    227 
    228         skipOptionalSVGSpaces(ptr, end);
    229 
    230         if (isPercentage)
    231             number /= 100.f;
    232 
    233         return ptr == end;
    234     }
    235 
    236     return false;
    237 }
    238 
    239 bool parseNumberOrPercentage(const String& string, float& number)
    240 {
    241     if (string.isEmpty())
    242         return false;
    243 
    244     if (string.is8Bit()) {
    245         const LChar* ptr = string.characters8();
    246         const LChar* end = ptr + string.length();
    247         return genericParseNumberOrPercentage(ptr, end, number);
    248     }
    249     const UChar* ptr = string.characters16();
    250     const UChar* end = ptr + string.length();
    251     return genericParseNumberOrPercentage(ptr, end, number);
    252 }
    253 
    254 #if ENABLE(SVG_FONTS)
    255 template<typename CharType>
    256 static bool parseGlyphName(const CharType*& ptr, const CharType* end, HashSet<String>& values)
    257 {
    258     skipOptionalSVGSpaces(ptr, end);
    259 
    260     while (ptr < end) {
    261         // Leading and trailing white space, and white space before and after separators, will be ignored.
    262         const CharType* inputStart = ptr;
    263         while (ptr < end && *ptr != ',')
    264             ++ptr;
    265 
    266         if (ptr == inputStart)
    267             break;
    268 
    269         // walk backwards from the ; to ignore any whitespace
    270         const CharType* inputEnd = ptr - 1;
    271         while (inputStart < inputEnd && isHTMLSpace<CharType>(*inputEnd))
    272             --inputEnd;
    273 
    274         values.add(String(inputStart, inputEnd - inputStart + 1));
    275         skipOptionalSVGSpacesOrDelimiter(ptr, end, ',');
    276     }
    277 
    278     return true;
    279 }
    280 
    281 bool parseGlyphName(const String& input, HashSet<String>& values)
    282 {
    283     // FIXME: Parsing error detection is missing.
    284     values.clear();
    285     if (input.isEmpty())
    286         return true;
    287     if (input.is8Bit()) {
    288         const LChar* ptr = input.characters8();
    289         const LChar* end = ptr + input.length();
    290         return parseGlyphName(ptr, end, values);
    291     }
    292     const UChar* ptr = input.characters16();
    293     const UChar* end = ptr + input.length();
    294     return parseGlyphName(ptr, end, values);
    295 }
    296 
    297 template<typename CharType>
    298 static bool parseUnicodeRange(const CharType* characters, unsigned length, UnicodeRange& range)
    299 {
    300     if (length < 2 || characters[0] != 'U' || characters[1] != '+')
    301         return false;
    302 
    303     // Parse the starting hex number (or its prefix).
    304     unsigned startRange = 0;
    305     unsigned startLength = 0;
    306 
    307     const CharType* ptr = characters + 2;
    308     const CharType* end = characters + length;
    309     while (ptr < end) {
    310         if (!isASCIIHexDigit(*ptr))
    311             break;
    312         ++startLength;
    313         if (startLength > 6)
    314             return false;
    315         startRange = (startRange << 4) | toASCIIHexValue(*ptr);
    316         ++ptr;
    317     }
    318 
    319     // Handle the case of ranges separated by "-" sign.
    320     if (2 + startLength < length && *ptr == '-') {
    321         if (!startLength)
    322             return false;
    323 
    324         // Parse the ending hex number (or its prefix).
    325         unsigned endRange = 0;
    326         unsigned endLength = 0;
    327         ++ptr;
    328         while (ptr < end) {
    329             if (!isASCIIHexDigit(*ptr))
    330                 break;
    331             ++endLength;
    332             if (endLength > 6)
    333                 return false;
    334             endRange = (endRange << 4) | toASCIIHexValue(*ptr);
    335             ++ptr;
    336         }
    337 
    338         if (!endLength)
    339             return false;
    340 
    341         range.first = startRange;
    342         range.second = endRange;
    343         return true;
    344     }
    345 
    346     // Handle the case of a number with some optional trailing question marks.
    347     unsigned endRange = startRange;
    348     while (ptr < end) {
    349         if (*ptr != '?')
    350             break;
    351         ++startLength;
    352         if (startLength > 6)
    353             return false;
    354         startRange <<= 4;
    355         endRange = (endRange << 4) | 0xF;
    356         ++ptr;
    357     }
    358 
    359     if (!startLength)
    360         return false;
    361 
    362     range.first = startRange;
    363     range.second = endRange;
    364     return true;
    365 }
    366 
    367 template<typename CharType>
    368 static bool genericParseKerningUnicodeString(const CharType*& ptr, const CharType* end, UnicodeRanges& rangeList, HashSet<String>& stringList)
    369 {
    370     while (ptr < end) {
    371         const CharType* inputStart = ptr;
    372         while (ptr < end && *ptr != ',')
    373             ++ptr;
    374 
    375         if (ptr == inputStart)
    376             break;
    377 
    378         // Try to parse unicode range first
    379         UnicodeRange range;
    380         if (parseUnicodeRange(inputStart, ptr - inputStart, range))
    381             rangeList.append(range);
    382         else
    383             stringList.add(String(inputStart, ptr - inputStart));
    384         ++ptr;
    385     }
    386 
    387     return true;
    388 }
    389 
    390 bool parseKerningUnicodeString(const String& input, UnicodeRanges& rangeList, HashSet<String>& stringList)
    391 {
    392     // FIXME: Parsing error detection is missing.
    393     if (input.isEmpty())
    394         return true;
    395     if (input.is8Bit()) {
    396         const LChar* ptr = input.characters8();
    397         const LChar* end = ptr + input.length();
    398         return genericParseKerningUnicodeString(ptr, end, rangeList, stringList);
    399     }
    400     const UChar* ptr = input.characters16();
    401     const UChar* end = ptr + input.length();
    402     return genericParseKerningUnicodeString(ptr, end, rangeList, stringList);
    403 }
    404 
    405 template<typename CharType>
    406 static Vector<String> genericParseDelimitedString(const CharType*& ptr, const CharType* end, const char seperator)
    407 {
    408     Vector<String> values;
    409 
    410     skipOptionalSVGSpaces(ptr, end);
    411 
    412     while (ptr < end) {
    413         // Leading and trailing white space, and white space before and after semicolon separators, will be ignored.
    414         const CharType* inputStart = ptr;
    415         while (ptr < end && *ptr != seperator) // careful not to ignore whitespace inside inputs
    416             ptr++;
    417 
    418         if (ptr == inputStart)
    419             break;
    420 
    421         // walk backwards from the ; to ignore any whitespace
    422         const CharType* inputEnd = ptr - 1;
    423         while (inputStart < inputEnd && isHTMLSpace<CharType>(*inputEnd))
    424             inputEnd--;
    425 
    426         values.append(String(inputStart, inputEnd - inputStart + 1));
    427         skipOptionalSVGSpacesOrDelimiter(ptr, end, seperator);
    428     }
    429 
    430     return values;
    431 }
    432 
    433 Vector<String> parseDelimitedString(const String& input, const char seperator)
    434 {
    435     if (input.isEmpty())
    436         return Vector<String>();
    437     if (input.is8Bit()) {
    438         const LChar* ptr = input.characters8();
    439         const LChar* end = ptr + input.length();
    440         return genericParseDelimitedString(ptr, end, seperator);
    441     }
    442     const UChar* ptr = input.characters16();
    443     const UChar* end = ptr + input.length();
    444     return genericParseDelimitedString(ptr, end, seperator);
    445 }
    446 #endif
    447 
    448 template <typename CharType>
    449 bool parseFloatPoint(const CharType*& current, const CharType* end, FloatPoint& point)
    450 {
    451     float x;
    452     float y;
    453     if (!parseNumber(current, end, x)
    454         || !parseNumber(current, end, y))
    455         return false;
    456     point = FloatPoint(x, y);
    457     return true;
    458 }
    459 
    460 template bool parseFloatPoint(const LChar*& current, const LChar* end, FloatPoint& point1);
    461 template bool parseFloatPoint(const UChar*& current, const UChar* end, FloatPoint& point1);
    462 
    463 template <typename CharType>
    464 inline bool parseFloatPoint2(const CharType*& current, const CharType* end, FloatPoint& point1, FloatPoint& point2)
    465 {
    466     float x1;
    467     float y1;
    468     float x2;
    469     float y2;
    470     if (!parseNumber(current, end, x1)
    471         || !parseNumber(current, end, y1)
    472         || !parseNumber(current, end, x2)
    473         || !parseNumber(current, end, y2))
    474         return false;
    475     point1 = FloatPoint(x1, y1);
    476     point2 = FloatPoint(x2, y2);
    477     return true;
    478 }
    479 
    480 template bool parseFloatPoint2(const LChar*& current, const LChar* end, FloatPoint& point1, FloatPoint& point2);
    481 template bool parseFloatPoint2(const UChar*& current, const UChar* end, FloatPoint& point1, FloatPoint& point2);
    482 
    483 template <typename CharType>
    484 bool parseFloatPoint3(const CharType*& current, const CharType* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3)
    485 {
    486     float x1;
    487     float y1;
    488     float x2;
    489     float y2;
    490     float x3;
    491     float y3;
    492     if (!parseNumber(current, end, x1)
    493         || !parseNumber(current, end, y1)
    494         || !parseNumber(current, end, x2)
    495         || !parseNumber(current, end, y2)
    496         || !parseNumber(current, end, x3)
    497         || !parseNumber(current, end, y3))
    498         return false;
    499     point1 = FloatPoint(x1, y1);
    500     point2 = FloatPoint(x2, y2);
    501     point3 = FloatPoint(x3, y3);
    502     return true;
    503 }
    504 
    505 template bool parseFloatPoint3(const LChar*& current, const LChar* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3);
    506 template bool parseFloatPoint3(const UChar*& current, const UChar* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3);
    507 
    508 static const LChar skewXDesc[] =  {'s', 'k', 'e', 'w', 'X'};
    509 static const LChar skewYDesc[] =  {'s', 'k', 'e', 'w', 'Y'};
    510 static const LChar scaleDesc[] =  {'s', 'c', 'a', 'l', 'e'};
    511 static const LChar translateDesc[] =  {'t', 'r', 'a', 'n', 's', 'l', 'a', 't', 'e'};
    512 static const LChar rotateDesc[] =  {'r', 'o', 't', 'a', 't', 'e'};
    513 static const LChar matrixDesc[] =  {'m', 'a', 't', 'r', 'i', 'x'};
    514 
    515 template<typename CharType>
    516 bool parseAndSkipTransformType(const CharType*& ptr, const CharType* end, SVGTransformType& type)
    517 {
    518     if (ptr >= end)
    519         return false;
    520 
    521     if (*ptr == 's') {
    522         if (skipString(ptr, end, skewXDesc, WTF_ARRAY_LENGTH(skewXDesc)))
    523             type = SVG_TRANSFORM_SKEWX;
    524         else if (skipString(ptr, end, skewYDesc, WTF_ARRAY_LENGTH(skewYDesc)))
    525             type = SVG_TRANSFORM_SKEWY;
    526         else if (skipString(ptr, end, scaleDesc, WTF_ARRAY_LENGTH(scaleDesc)))
    527             type = SVG_TRANSFORM_SCALE;
    528         else
    529             return false;
    530     } else if (skipString(ptr, end, translateDesc, WTF_ARRAY_LENGTH(translateDesc)))
    531         type = SVG_TRANSFORM_TRANSLATE;
    532     else if (skipString(ptr, end, rotateDesc, WTF_ARRAY_LENGTH(rotateDesc)))
    533         type = SVG_TRANSFORM_ROTATE;
    534     else if (skipString(ptr, end, matrixDesc, WTF_ARRAY_LENGTH(matrixDesc)))
    535         type = SVG_TRANSFORM_MATRIX;
    536     else
    537         return false;
    538 
    539     return true;
    540 }
    541 
    542 template bool parseAndSkipTransformType(const UChar*& current, const UChar* end, SVGTransformType&);
    543 template bool parseAndSkipTransformType(const LChar*& current, const LChar* end, SVGTransformType&);
    544 
    545 SVGTransformType parseTransformType(const String& string)
    546 {
    547     if (string.isEmpty())
    548         return SVG_TRANSFORM_UNKNOWN;
    549     SVGTransformType type = SVG_TRANSFORM_UNKNOWN;
    550     if (string.is8Bit()) {
    551         const LChar* ptr = string.characters8();
    552         const LChar* end = ptr + string.length();
    553         parseAndSkipTransformType(ptr, end, type);
    554     } else {
    555         const UChar* ptr = string.characters16();
    556         const UChar* end = ptr + string.length();
    557         parseAndSkipTransformType(ptr, end, type);
    558     }
    559     return type;
    560 }
    561 
    562 }
    563