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/dom/Document.h"
     27 #include "core/svg/SVGPointList.h"
     28 #include "platform/geometry/FloatRect.h"
     29 #include "platform/transforms/AffineTransform.h"
     30 #include "wtf/ASCIICType.h"
     31 #include <limits>
     32 
     33 namespace WebCore {
     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 template <typename CharType>
    149 bool parseSVGNumber(CharType* begin, size_t length, double& number)
    150 {
    151     const CharType* ptr = begin;
    152     const CharType* end = ptr + length;
    153     return genericParseNumber(ptr, end, number, AllowLeadingAndTrailingWhitespace);
    154 }
    155 
    156 // Explicitly instantiate the two flavors of parseSVGNumber() to satisfy external callers
    157 template bool parseSVGNumber(LChar* begin, size_t length, double&);
    158 template bool parseSVGNumber(UChar* begin, size_t length, double&);
    159 
    160 bool parseNumber(const LChar*& ptr, const LChar* end, float& number, WhitespaceMode mode)
    161 {
    162     return genericParseNumber(ptr, end, number, mode);
    163 }
    164 
    165 bool parseNumber(const UChar*& ptr, const UChar* end, float& number, WhitespaceMode mode)
    166 {
    167     return genericParseNumber(ptr, end, number, mode);
    168 }
    169 
    170 // only used to parse largeArcFlag and sweepFlag which must be a "0" or "1"
    171 // and might not have any whitespace/comma after it
    172 template <typename CharType>
    173 bool genericParseArcFlag(const CharType*& ptr, const CharType* end, bool& flag)
    174 {
    175     if (ptr >= end)
    176         return false;
    177     const CharType flagChar = *ptr++;
    178     if (flagChar == '0')
    179         flag = false;
    180     else if (flagChar == '1')
    181         flag = true;
    182     else
    183         return false;
    184 
    185     skipOptionalSVGSpacesOrDelimiter(ptr, end);
    186 
    187     return true;
    188 }
    189 
    190 bool parseArcFlag(const LChar*& ptr, const LChar* end, bool& flag)
    191 {
    192     return genericParseArcFlag(ptr, end, flag);
    193 }
    194 
    195 bool parseArcFlag(const UChar*& ptr, const UChar* end, bool& flag)
    196 {
    197     return genericParseArcFlag(ptr, end, flag);
    198 }
    199 
    200 template<typename CharType>
    201 static bool genericParseNumberOptionalNumber(const CharType*& ptr, const CharType* end, float& x, float& y)
    202 {
    203     if (!parseNumber(ptr, end, x))
    204         return false;
    205 
    206     if (ptr == end)
    207         y = x;
    208     else if (!parseNumber(ptr, end, y, AllowLeadingAndTrailingWhitespace))
    209         return false;
    210 
    211     return ptr == end;
    212 }
    213 
    214 bool parseNumberOptionalNumber(const String& string, float& x, float& y)
    215 {
    216     if (string.isEmpty())
    217         return false;
    218 
    219     if (string.is8Bit()) {
    220         const LChar* ptr = string.characters8();
    221         const LChar* end = ptr + string.length();
    222         return genericParseNumberOptionalNumber(ptr, end, x, y);
    223     }
    224     const UChar* ptr = string.characters16();
    225     const UChar* end = ptr + string.length();
    226     return genericParseNumberOptionalNumber(ptr, end, x, y);
    227 }
    228 
    229 template<typename CharType>
    230 bool genericParseNumberOrPercentage(const CharType*& ptr, const CharType* end, float& number)
    231 {
    232     if (genericParseNumber(ptr, end, number, AllowLeadingWhitespace)) {
    233         if (ptr == end)
    234             return true;
    235 
    236         bool isPercentage = (*ptr == '%');
    237         if (isPercentage)
    238             ptr++;
    239 
    240         skipOptionalSVGSpaces(ptr, end);
    241 
    242         if (isPercentage)
    243             number /= 100.f;
    244 
    245         return ptr == end;
    246     }
    247 
    248     return false;
    249 }
    250 
    251 bool parseNumberOrPercentage(const String& string, float& number)
    252 {
    253     if (string.isEmpty())
    254         return false;
    255 
    256     if (string.is8Bit()) {
    257         const LChar* ptr = string.characters8();
    258         const LChar* end = ptr + string.length();
    259         return genericParseNumberOrPercentage(ptr, end, number);
    260     }
    261     const UChar* ptr = string.characters16();
    262     const UChar* end = ptr + string.length();
    263     return genericParseNumberOrPercentage(ptr, end, number);
    264 }
    265 
    266 template<typename CharType>
    267 static bool parseGlyphName(const CharType*& ptr, const CharType* end, HashSet<String>& values)
    268 {
    269     skipOptionalSVGSpaces(ptr, end);
    270 
    271     while (ptr < end) {
    272         // Leading and trailing white space, and white space before and after separators, will be ignored.
    273         const CharType* inputStart = ptr;
    274         while (ptr < end && *ptr != ',')
    275             ++ptr;
    276 
    277         if (ptr == inputStart)
    278             break;
    279 
    280         // walk backwards from the ; to ignore any whitespace
    281         const CharType* inputEnd = ptr - 1;
    282         while (inputStart < inputEnd && isHTMLSpace<CharType>(*inputEnd))
    283             --inputEnd;
    284 
    285         values.add(String(inputStart, inputEnd - inputStart + 1));
    286         skipOptionalSVGSpacesOrDelimiter(ptr, end, ',');
    287     }
    288 
    289     return true;
    290 }
    291 
    292 bool parseGlyphName(const String& input, HashSet<String>& values)
    293 {
    294     // FIXME: Parsing error detection is missing.
    295     values.clear();
    296     if (input.isEmpty())
    297         return true;
    298     if (input.is8Bit()) {
    299         const LChar* ptr = input.characters8();
    300         const LChar* end = ptr + input.length();
    301         return parseGlyphName(ptr, end, values);
    302     }
    303     const UChar* ptr = input.characters16();
    304     const UChar* end = ptr + input.length();
    305     return parseGlyphName(ptr, end, values);
    306 }
    307 
    308 template<typename CharType>
    309 static bool parseUnicodeRange(const CharType* characters, unsigned length, UnicodeRange& range)
    310 {
    311     if (length < 2 || characters[0] != 'U' || characters[1] != '+')
    312         return false;
    313 
    314     // Parse the starting hex number (or its prefix).
    315     unsigned startRange = 0;
    316     unsigned startLength = 0;
    317 
    318     const CharType* ptr = characters + 2;
    319     const CharType* end = characters + length;
    320     while (ptr < end) {
    321         if (!isASCIIHexDigit(*ptr))
    322             break;
    323         ++startLength;
    324         if (startLength > 6)
    325             return false;
    326         startRange = (startRange << 4) | toASCIIHexValue(*ptr);
    327         ++ptr;
    328     }
    329 
    330     // Handle the case of ranges separated by "-" sign.
    331     if (2 + startLength < length && *ptr == '-') {
    332         if (!startLength)
    333             return false;
    334 
    335         // Parse the ending hex number (or its prefix).
    336         unsigned endRange = 0;
    337         unsigned endLength = 0;
    338         ++ptr;
    339         while (ptr < end) {
    340             if (!isASCIIHexDigit(*ptr))
    341                 break;
    342             ++endLength;
    343             if (endLength > 6)
    344                 return false;
    345             endRange = (endRange << 4) | toASCIIHexValue(*ptr);
    346             ++ptr;
    347         }
    348 
    349         if (!endLength)
    350             return false;
    351 
    352         range.first = startRange;
    353         range.second = endRange;
    354         return true;
    355     }
    356 
    357     // Handle the case of a number with some optional trailing question marks.
    358     unsigned endRange = startRange;
    359     while (ptr < end) {
    360         if (*ptr != '?')
    361             break;
    362         ++startLength;
    363         if (startLength > 6)
    364             return false;
    365         startRange <<= 4;
    366         endRange = (endRange << 4) | 0xF;
    367         ++ptr;
    368     }
    369 
    370     if (!startLength)
    371         return false;
    372 
    373     range.first = startRange;
    374     range.second = endRange;
    375     return true;
    376 }
    377 
    378 template<typename CharType>
    379 static bool genericParseKerningUnicodeString(const CharType*& ptr, const CharType* end, UnicodeRanges& rangeList, HashSet<String>& stringList)
    380 {
    381     while (ptr < end) {
    382         const CharType* inputStart = ptr;
    383         while (ptr < end && *ptr != ',')
    384             ++ptr;
    385 
    386         if (ptr == inputStart)
    387             break;
    388 
    389         // Try to parse unicode range first
    390         UnicodeRange range;
    391         if (parseUnicodeRange(inputStart, ptr - inputStart, range))
    392             rangeList.append(range);
    393         else
    394             stringList.add(String(inputStart, ptr - inputStart));
    395         ++ptr;
    396     }
    397 
    398     return true;
    399 }
    400 
    401 bool parseKerningUnicodeString(const String& input, UnicodeRanges& rangeList, HashSet<String>& stringList)
    402 {
    403     // FIXME: Parsing error detection is missing.
    404     if (input.isEmpty())
    405         return true;
    406     if (input.is8Bit()) {
    407         const LChar* ptr = input.characters8();
    408         const LChar* end = ptr + input.length();
    409         return genericParseKerningUnicodeString(ptr, end, rangeList, stringList);
    410     }
    411     const UChar* ptr = input.characters16();
    412     const UChar* end = ptr + input.length();
    413     return genericParseKerningUnicodeString(ptr, end, rangeList, stringList);
    414 }
    415 
    416 template<typename CharType>
    417 static Vector<String> genericParseDelimitedString(const CharType*& ptr, const CharType* end, const char seperator)
    418 {
    419     Vector<String> values;
    420 
    421     skipOptionalSVGSpaces(ptr, end);
    422 
    423     while (ptr < end) {
    424         // Leading and trailing white space, and white space before and after semicolon separators, will be ignored.
    425         const CharType* inputStart = ptr;
    426         while (ptr < end && *ptr != seperator) // careful not to ignore whitespace inside inputs
    427             ptr++;
    428 
    429         if (ptr == inputStart)
    430             break;
    431 
    432         // walk backwards from the ; to ignore any whitespace
    433         const CharType* inputEnd = ptr - 1;
    434         while (inputStart < inputEnd && isHTMLSpace<CharType>(*inputEnd))
    435             inputEnd--;
    436 
    437         values.append(String(inputStart, inputEnd - inputStart + 1));
    438         skipOptionalSVGSpacesOrDelimiter(ptr, end, seperator);
    439     }
    440 
    441     return values;
    442 }
    443 
    444 Vector<String> parseDelimitedString(const String& input, const char seperator)
    445 {
    446     if (input.isEmpty())
    447         return Vector<String>();
    448     if (input.is8Bit()) {
    449         const LChar* ptr = input.characters8();
    450         const LChar* end = ptr + input.length();
    451         return genericParseDelimitedString(ptr, end, seperator);
    452     }
    453     const UChar* ptr = input.characters16();
    454     const UChar* end = ptr + input.length();
    455     return genericParseDelimitedString(ptr, end, seperator);
    456 }
    457 
    458 template <typename CharType>
    459 bool parseFloatPoint(const CharType*& current, const CharType* end, FloatPoint& point)
    460 {
    461     float x;
    462     float y;
    463     if (!parseNumber(current, end, x)
    464         || !parseNumber(current, end, y))
    465         return false;
    466     point = FloatPoint(x, y);
    467     return true;
    468 }
    469 
    470 template bool parseFloatPoint(const LChar*& current, const LChar* end, FloatPoint& point1);
    471 template bool parseFloatPoint(const UChar*& current, const UChar* end, FloatPoint& point1);
    472 
    473 template <typename CharType>
    474 inline bool parseFloatPoint2(const CharType*& current, const CharType* end, FloatPoint& point1, FloatPoint& point2)
    475 {
    476     float x1;
    477     float y1;
    478     float x2;
    479     float y2;
    480     if (!parseNumber(current, end, x1)
    481         || !parseNumber(current, end, y1)
    482         || !parseNumber(current, end, x2)
    483         || !parseNumber(current, end, y2))
    484         return false;
    485     point1 = FloatPoint(x1, y1);
    486     point2 = FloatPoint(x2, y2);
    487     return true;
    488 }
    489 
    490 template bool parseFloatPoint2(const LChar*& current, const LChar* end, FloatPoint& point1, FloatPoint& point2);
    491 template bool parseFloatPoint2(const UChar*& current, const UChar* end, FloatPoint& point1, FloatPoint& point2);
    492 
    493 template <typename CharType>
    494 bool parseFloatPoint3(const CharType*& current, const CharType* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3)
    495 {
    496     float x1;
    497     float y1;
    498     float x2;
    499     float y2;
    500     float x3;
    501     float y3;
    502     if (!parseNumber(current, end, x1)
    503         || !parseNumber(current, end, y1)
    504         || !parseNumber(current, end, x2)
    505         || !parseNumber(current, end, y2)
    506         || !parseNumber(current, end, x3)
    507         || !parseNumber(current, end, y3))
    508         return false;
    509     point1 = FloatPoint(x1, y1);
    510     point2 = FloatPoint(x2, y2);
    511     point3 = FloatPoint(x3, y3);
    512     return true;
    513 }
    514 
    515 template bool parseFloatPoint3(const LChar*& current, const LChar* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3);
    516 template bool parseFloatPoint3(const UChar*& current, const UChar* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3);
    517 
    518 static const LChar skewXDesc[] =  {'s', 'k', 'e', 'w', 'X'};
    519 static const LChar skewYDesc[] =  {'s', 'k', 'e', 'w', 'Y'};
    520 static const LChar scaleDesc[] =  {'s', 'c', 'a', 'l', 'e'};
    521 static const LChar translateDesc[] =  {'t', 'r', 'a', 'n', 's', 'l', 'a', 't', 'e'};
    522 static const LChar rotateDesc[] =  {'r', 'o', 't', 'a', 't', 'e'};
    523 static const LChar matrixDesc[] =  {'m', 'a', 't', 'r', 'i', 'x'};
    524 
    525 template<typename CharType>
    526 bool parseAndSkipTransformType(const CharType*& ptr, const CharType* end, SVGTransformType& type)
    527 {
    528     if (ptr >= end)
    529         return false;
    530 
    531     if (*ptr == 's') {
    532         if (skipString(ptr, end, skewXDesc, WTF_ARRAY_LENGTH(skewXDesc)))
    533             type = SVG_TRANSFORM_SKEWX;
    534         else if (skipString(ptr, end, skewYDesc, WTF_ARRAY_LENGTH(skewYDesc)))
    535             type = SVG_TRANSFORM_SKEWY;
    536         else if (skipString(ptr, end, scaleDesc, WTF_ARRAY_LENGTH(scaleDesc)))
    537             type = SVG_TRANSFORM_SCALE;
    538         else
    539             return false;
    540     } else if (skipString(ptr, end, translateDesc, WTF_ARRAY_LENGTH(translateDesc)))
    541         type = SVG_TRANSFORM_TRANSLATE;
    542     else if (skipString(ptr, end, rotateDesc, WTF_ARRAY_LENGTH(rotateDesc)))
    543         type = SVG_TRANSFORM_ROTATE;
    544     else if (skipString(ptr, end, matrixDesc, WTF_ARRAY_LENGTH(matrixDesc)))
    545         type = SVG_TRANSFORM_MATRIX;
    546     else
    547         return false;
    548 
    549     return true;
    550 }
    551 
    552 template bool parseAndSkipTransformType(const UChar*& current, const UChar* end, SVGTransformType&);
    553 template bool parseAndSkipTransformType(const LChar*& current, const LChar* end, SVGTransformType&);
    554 
    555 SVGTransformType parseTransformType(const String& string)
    556 {
    557     if (string.isEmpty())
    558         return SVG_TRANSFORM_UNKNOWN;
    559     SVGTransformType type = SVG_TRANSFORM_UNKNOWN;
    560     if (string.is8Bit()) {
    561         const LChar* ptr = string.characters8();
    562         const LChar* end = ptr + string.length();
    563         parseAndSkipTransformType(ptr, end, type);
    564     } else {
    565         const UChar* ptr = string.characters16();
    566         const UChar* end = ptr + string.length();
    567         parseAndSkipTransformType(ptr, end, type);
    568     }
    569     return type;
    570 }
    571 
    572 }
    573