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 "core/svg/SVGTransformList.h"
     29 #include "platform/geometry/FloatRect.h"
     30 #include "platform/transforms/AffineTransform.h"
     31 #include "wtf/ASCIICType.h"
     32 #include <limits>
     33 
     34 namespace WebCore {
     35 
     36 template <typename FloatType>
     37 static inline bool isValidRange(const FloatType& x)
     38 {
     39     static const FloatType max = std::numeric_limits<FloatType>::max();
     40     return x >= -max && x <= max;
     41 }
     42 
     43 // We use this generic parseNumber function to allow the Path parsing code to work
     44 // at a higher precision internally, without any unnecessary runtime cost or code
     45 // complexity.
     46 template <typename CharType, typename FloatType>
     47 static bool genericParseNumber(const CharType*& ptr, const CharType* end, FloatType& number, bool skip)
     48 {
     49     FloatType integer, decimal, frac, exponent;
     50     int sign, expsign;
     51     const CharType* start = ptr;
     52 
     53     exponent = 0;
     54     integer = 0;
     55     frac = 1;
     56     decimal = 0;
     57     sign = 1;
     58     expsign = 1;
     59 
     60     // read the sign
     61     if (ptr < end && *ptr == '+')
     62         ptr++;
     63     else if (ptr < end && *ptr == '-') {
     64         ptr++;
     65         sign = -1;
     66     }
     67 
     68     if (ptr == end || ((*ptr < '0' || *ptr > '9') && *ptr != '.'))
     69         // The first character of a number must be one of [0-9+-.]
     70         return false;
     71 
     72     // read the integer part, build right-to-left
     73     const CharType* ptrStartIntPart = ptr;
     74     while (ptr < end && *ptr >= '0' && *ptr <= '9')
     75         ++ptr; // Advance to first non-digit.
     76 
     77     if (ptr != ptrStartIntPart) {
     78         const CharType* ptrScanIntPart = ptr - 1;
     79         FloatType multiplier = 1;
     80         while (ptrScanIntPart >= ptrStartIntPart) {
     81             integer += multiplier * static_cast<FloatType>(*(ptrScanIntPart--) - '0');
     82             multiplier *= 10;
     83         }
     84         // Bail out early if this overflows.
     85         if (!isValidRange(integer))
     86             return false;
     87     }
     88 
     89     if (ptr < end && *ptr == '.') { // read the decimals
     90         ptr++;
     91 
     92         // There must be a least one digit following the .
     93         if (ptr >= end || *ptr < '0' || *ptr > '9')
     94             return false;
     95 
     96         while (ptr < end && *ptr >= '0' && *ptr <= '9')
     97             decimal += (*(ptr++) - '0') * (frac *= static_cast<FloatType>(0.1));
     98     }
     99 
    100     // read the exponent part
    101     if (ptr != start && ptr + 1 < end && (*ptr == 'e' || *ptr == 'E')
    102         && (ptr[1] != 'x' && ptr[1] != 'm')) {
    103         ptr++;
    104 
    105         // read the sign of the exponent
    106         if (*ptr == '+')
    107             ptr++;
    108         else if (*ptr == '-') {
    109             ptr++;
    110             expsign = -1;
    111         }
    112 
    113         // There must be an exponent
    114         if (ptr >= end || *ptr < '0' || *ptr > '9')
    115             return false;
    116 
    117         while (ptr < end && *ptr >= '0' && *ptr <= '9') {
    118             exponent *= static_cast<FloatType>(10);
    119             exponent += *ptr - '0';
    120             ptr++;
    121         }
    122         // Make sure exponent is valid.
    123         if (!isValidRange(exponent) || exponent > std::numeric_limits<FloatType>::max_exponent)
    124             return false;
    125     }
    126 
    127     number = integer + decimal;
    128     number *= sign;
    129 
    130     if (exponent)
    131         number *= static_cast<FloatType>(pow(10.0, expsign * static_cast<int>(exponent)));
    132 
    133     // Don't return Infinity() or NaN().
    134     if (!isValidRange(number))
    135         return false;
    136 
    137     if (start == ptr)
    138         return false;
    139 
    140     if (skip)
    141         skipOptionalSVGSpacesOrDelimiter(ptr, end);
    142 
    143     return true;
    144 }
    145 
    146 template <typename CharType>
    147 bool parseSVGNumber(CharType* begin, size_t length, double& number)
    148 {
    149     const CharType* ptr = begin;
    150     const CharType* end = ptr + length;
    151     return genericParseNumber(ptr, end, number, false);
    152 }
    153 
    154 // Explicitly instantiate the two flavors of parseSVGNumber() to satisfy external callers
    155 template bool parseSVGNumber(LChar* begin, size_t length, double&);
    156 template bool parseSVGNumber(UChar* begin, size_t length, double&);
    157 
    158 bool parseNumber(const LChar*& ptr, const LChar* end, float& number, bool skip)
    159 {
    160     return genericParseNumber(ptr, end, number, skip);
    161 }
    162 
    163 bool parseNumber(const UChar*& ptr, const UChar* end, float& number, bool skip)
    164 {
    165     return genericParseNumber(ptr, end, number, skip);
    166 }
    167 
    168 bool parseNumberFromString(const String& string, float& number, bool skip)
    169 {
    170     if (string.isEmpty())
    171         return false;
    172     if (string.is8Bit()) {
    173         const LChar* ptr = string.characters8();
    174         const LChar* end = ptr + string.length();
    175         return genericParseNumber(ptr, end, number, skip) && ptr == end;
    176     }
    177     const UChar* ptr = string.characters16();
    178     const UChar* end = ptr + string.length();
    179     return genericParseNumber(ptr, end, number, skip) && ptr == end;
    180 }
    181 
    182 // only used to parse largeArcFlag and sweepFlag which must be a "0" or "1"
    183 // and might not have any whitespace/comma after it
    184 template <typename CharType>
    185 bool genericParseArcFlag(const CharType*& ptr, const CharType* end, bool& flag)
    186 {
    187     if (ptr >= end)
    188         return false;
    189     const CharType flagChar = *ptr++;
    190     if (flagChar == '0')
    191         flag = false;
    192     else if (flagChar == '1')
    193         flag = true;
    194     else
    195         return false;
    196 
    197     skipOptionalSVGSpacesOrDelimiter(ptr, end);
    198 
    199     return true;
    200 }
    201 
    202 bool parseArcFlag(const LChar*& ptr, const LChar* end, bool& flag)
    203 {
    204     return genericParseArcFlag(ptr, end, flag);
    205 }
    206 
    207 bool parseArcFlag(const UChar*& ptr, const UChar* end, bool& flag)
    208 {
    209     return genericParseArcFlag(ptr, end, flag);
    210 }
    211 
    212 template<typename CharType>
    213 static bool genericParseNumberOptionalNumber(const CharType*& ptr, const CharType* end, float& x, float& y)
    214 {
    215     if (!parseNumber(ptr, end, x))
    216         return false;
    217 
    218     if (ptr == end)
    219         y = x;
    220     else if (!parseNumber(ptr, end, y, false))
    221         return false;
    222 
    223     return ptr == end;
    224 }
    225 
    226 bool parseNumberOptionalNumber(const String& string, float& x, float& y)
    227 {
    228     if (string.isEmpty())
    229         return false;
    230     if (string.is8Bit()) {
    231         const LChar* ptr = string.characters8();
    232         const LChar* end = ptr + string.length();
    233         return genericParseNumberOptionalNumber(ptr, end, x, y);
    234     }
    235     const UChar* ptr = string.characters16();
    236     const UChar* end = ptr + string.length();
    237     return genericParseNumberOptionalNumber(ptr, end, x, y);
    238 }
    239 
    240 template<typename CharType>
    241 static bool genericParseRect(const CharType*& ptr, const CharType* end, FloatRect& rect)
    242 {
    243     skipOptionalSVGSpaces(ptr, end);
    244 
    245     float x = 0;
    246     float y = 0;
    247     float width = 0;
    248     float height = 0;
    249     bool valid = parseNumber(ptr, end, x) && parseNumber(ptr, end, y) && parseNumber(ptr, end, width) && parseNumber(ptr, end, height, false);
    250     rect = FloatRect(x, y, width, height);
    251     return valid;
    252 }
    253 
    254 bool parseRect(const String& string, FloatRect& rect)
    255 {
    256     if (string.isEmpty())
    257         return false;
    258     if (string.is8Bit()) {
    259         const LChar* ptr = string.characters8();
    260         const LChar* end = ptr + string.length();
    261         return genericParseRect(ptr, end, rect);
    262     }
    263     const UChar* ptr = string.characters16();
    264     const UChar* end = ptr + string.length();
    265     return genericParseRect(ptr, end, rect);
    266 }
    267 
    268 template<typename CharType>
    269 static bool genericParsePointsList(SVGPointList& pointsList, const CharType*& ptr, const CharType* end)
    270 {
    271     skipOptionalSVGSpaces(ptr, end);
    272 
    273     bool delimParsed = false;
    274     while (ptr < end) {
    275         delimParsed = false;
    276         float xPos = 0.0f;
    277         if (!parseNumber(ptr, end, xPos))
    278            return false;
    279 
    280         float yPos = 0.0f;
    281         if (!parseNumber(ptr, end, yPos, false))
    282             return false;
    283 
    284         skipOptionalSVGSpaces(ptr, end);
    285 
    286         if (ptr < end && *ptr == ',') {
    287             delimParsed = true;
    288             ptr++;
    289         }
    290         skipOptionalSVGSpaces(ptr, end);
    291 
    292         pointsList.append(FloatPoint(xPos, yPos));
    293     }
    294     return ptr == end && !delimParsed;
    295 }
    296 
    297 // FIXME: Why is the out parameter first?
    298 bool pointsListFromSVGData(SVGPointList& pointsList, const String& points)
    299 {
    300     if (points.isEmpty())
    301         return true;
    302     if (points.is8Bit()) {
    303         const LChar* ptr = points.characters8();
    304         const LChar* end = ptr + points.length();
    305         return genericParsePointsList(pointsList, ptr, end);
    306     }
    307     const UChar* ptr = points.characters16();
    308     const UChar* end = ptr + points.length();
    309     return genericParsePointsList(pointsList, ptr, end);
    310 }
    311 
    312 template<typename CharType>
    313 static bool parseGlyphName(const CharType*& ptr, const CharType* end, HashSet<String>& values)
    314 {
    315     skipOptionalSVGSpaces(ptr, end);
    316 
    317     while (ptr < end) {
    318         // Leading and trailing white space, and white space before and after separators, will be ignored.
    319         const CharType* inputStart = ptr;
    320         while (ptr < end && *ptr != ',')
    321             ++ptr;
    322 
    323         if (ptr == inputStart)
    324             break;
    325 
    326         // walk backwards from the ; to ignore any whitespace
    327         const CharType* inputEnd = ptr - 1;
    328         while (inputStart < inputEnd && isSVGSpace(*inputEnd))
    329             --inputEnd;
    330 
    331         values.add(String(inputStart, inputEnd - inputStart + 1));
    332         skipOptionalSVGSpacesOrDelimiter(ptr, end, ',');
    333     }
    334 
    335     return true;
    336 }
    337 
    338 bool parseGlyphName(const String& input, HashSet<String>& values)
    339 {
    340     // FIXME: Parsing error detection is missing.
    341     values.clear();
    342     if (input.isEmpty())
    343         return true;
    344     if (input.is8Bit()) {
    345         const LChar* ptr = input.characters8();
    346         const LChar* end = ptr + input.length();
    347         return parseGlyphName(ptr, end, values);
    348     }
    349     const UChar* ptr = input.characters16();
    350     const UChar* end = ptr + input.length();
    351     return parseGlyphName(ptr, end, values);
    352 }
    353 
    354 template<typename CharType>
    355 static bool parseUnicodeRange(const CharType* characters, unsigned length, UnicodeRange& range)
    356 {
    357     if (length < 2 || characters[0] != 'U' || characters[1] != '+')
    358         return false;
    359 
    360     // Parse the starting hex number (or its prefix).
    361     unsigned startRange = 0;
    362     unsigned startLength = 0;
    363 
    364     const CharType* ptr = characters + 2;
    365     const CharType* end = characters + length;
    366     while (ptr < end) {
    367         if (!isASCIIHexDigit(*ptr))
    368             break;
    369         ++startLength;
    370         if (startLength > 6)
    371             return false;
    372         startRange = (startRange << 4) | toASCIIHexValue(*ptr);
    373         ++ptr;
    374     }
    375 
    376     // Handle the case of ranges separated by "-" sign.
    377     if (2 + startLength < length && *ptr == '-') {
    378         if (!startLength)
    379             return false;
    380 
    381         // Parse the ending hex number (or its prefix).
    382         unsigned endRange = 0;
    383         unsigned endLength = 0;
    384         ++ptr;
    385         while (ptr < end) {
    386             if (!isASCIIHexDigit(*ptr))
    387                 break;
    388             ++endLength;
    389             if (endLength > 6)
    390                 return false;
    391             endRange = (endRange << 4) | toASCIIHexValue(*ptr);
    392             ++ptr;
    393         }
    394 
    395         if (!endLength)
    396             return false;
    397 
    398         range.first = startRange;
    399         range.second = endRange;
    400         return true;
    401     }
    402 
    403     // Handle the case of a number with some optional trailing question marks.
    404     unsigned endRange = startRange;
    405     while (ptr < end) {
    406         if (*ptr != '?')
    407             break;
    408         ++startLength;
    409         if (startLength > 6)
    410             return false;
    411         startRange <<= 4;
    412         endRange = (endRange << 4) | 0xF;
    413         ++ptr;
    414     }
    415 
    416     if (!startLength)
    417         return false;
    418 
    419     range.first = startRange;
    420     range.second = endRange;
    421     return true;
    422 }
    423 
    424 template<typename CharType>
    425 static bool genericParseKerningUnicodeString(const CharType*& ptr, const CharType* end, UnicodeRanges& rangeList, HashSet<String>& stringList)
    426 {
    427     while (ptr < end) {
    428         const CharType* inputStart = ptr;
    429         while (ptr < end && *ptr != ',')
    430             ++ptr;
    431 
    432         if (ptr == inputStart)
    433             break;
    434 
    435         // Try to parse unicode range first
    436         UnicodeRange range;
    437         if (parseUnicodeRange(inputStart, ptr - inputStart, range))
    438             rangeList.append(range);
    439         else
    440             stringList.add(String(inputStart, ptr - inputStart));
    441         ++ptr;
    442     }
    443 
    444     return true;
    445 }
    446 
    447 bool parseKerningUnicodeString(const String& input, UnicodeRanges& rangeList, HashSet<String>& stringList)
    448 {
    449     // FIXME: Parsing error detection is missing.
    450     if (input.isEmpty())
    451         return true;
    452     if (input.is8Bit()) {
    453         const LChar* ptr = input.characters8();
    454         const LChar* end = ptr + input.length();
    455         return genericParseKerningUnicodeString(ptr, end, rangeList, stringList);
    456     }
    457     const UChar* ptr = input.characters16();
    458     const UChar* end = ptr + input.length();
    459     return genericParseKerningUnicodeString(ptr, end, rangeList, stringList);
    460 }
    461 
    462 template<typename CharType>
    463 static Vector<String> genericParseDelimitedString(const CharType*& ptr, const CharType* end, const char seperator)
    464 {
    465     Vector<String> values;
    466 
    467     skipOptionalSVGSpaces(ptr, end);
    468 
    469     while (ptr < end) {
    470         // Leading and trailing white space, and white space before and after semicolon separators, will be ignored.
    471         const CharType* inputStart = ptr;
    472         while (ptr < end && *ptr != seperator) // careful not to ignore whitespace inside inputs
    473             ptr++;
    474 
    475         if (ptr == inputStart)
    476             break;
    477 
    478         // walk backwards from the ; to ignore any whitespace
    479         const CharType* inputEnd = ptr - 1;
    480         while (inputStart < inputEnd && isSVGSpace(*inputEnd))
    481             inputEnd--;
    482 
    483         values.append(String(inputStart, inputEnd - inputStart + 1));
    484         skipOptionalSVGSpacesOrDelimiter(ptr, end, seperator);
    485     }
    486 
    487     return values;
    488 }
    489 
    490 Vector<String> parseDelimitedString(const String& input, const char seperator)
    491 {
    492     if (input.isEmpty())
    493         return Vector<String>();
    494     if (input.is8Bit()) {
    495         const LChar* ptr = input.characters8();
    496         const LChar* end = ptr + input.length();
    497         return genericParseDelimitedString(ptr, end, seperator);
    498     }
    499     const UChar* ptr = input.characters16();
    500     const UChar* end = ptr + input.length();
    501     return genericParseDelimitedString(ptr, end, seperator);
    502 }
    503 
    504 template <typename CharType>
    505 bool parseFloatPoint(const CharType*& current, const CharType* end, FloatPoint& point)
    506 {
    507     float x;
    508     float y;
    509     if (!parseNumber(current, end, x)
    510         || !parseNumber(current, end, y))
    511         return false;
    512     point = FloatPoint(x, y);
    513     return true;
    514 }
    515 
    516 template bool parseFloatPoint(const LChar*& current, const LChar* end, FloatPoint& point1);
    517 template bool parseFloatPoint(const UChar*& current, const UChar* end, FloatPoint& point1);
    518 
    519 template <typename CharType>
    520 inline bool parseFloatPoint2(const CharType*& current, const CharType* end, FloatPoint& point1, FloatPoint& point2)
    521 {
    522     float x1;
    523     float y1;
    524     float x2;
    525     float y2;
    526     if (!parseNumber(current, end, x1)
    527         || !parseNumber(current, end, y1)
    528         || !parseNumber(current, end, x2)
    529         || !parseNumber(current, end, y2))
    530         return false;
    531     point1 = FloatPoint(x1, y1);
    532     point2 = FloatPoint(x2, y2);
    533     return true;
    534 }
    535 
    536 template bool parseFloatPoint2(const LChar*& current, const LChar* end, FloatPoint& point1, FloatPoint& point2);
    537 template bool parseFloatPoint2(const UChar*& current, const UChar* end, FloatPoint& point1, FloatPoint& point2);
    538 
    539 template <typename CharType>
    540 bool parseFloatPoint3(const CharType*& current, const CharType* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3)
    541 {
    542     float x1;
    543     float y1;
    544     float x2;
    545     float y2;
    546     float x3;
    547     float y3;
    548     if (!parseNumber(current, end, x1)
    549         || !parseNumber(current, end, y1)
    550         || !parseNumber(current, end, x2)
    551         || !parseNumber(current, end, y2)
    552         || !parseNumber(current, end, x3)
    553         || !parseNumber(current, end, y3))
    554         return false;
    555     point1 = FloatPoint(x1, y1);
    556     point2 = FloatPoint(x2, y2);
    557     point3 = FloatPoint(x3, y3);
    558     return true;
    559 }
    560 
    561 template bool parseFloatPoint3(const LChar*& current, const LChar* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3);
    562 template bool parseFloatPoint3(const UChar*& current, const UChar* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3);
    563 
    564 template<typename CharType>
    565 static int parseTransformParamList(const CharType*& ptr, const CharType* end, float* values, int required, int optional)
    566 {
    567     int optionalParams = 0, requiredParams = 0;
    568 
    569     if (!skipOptionalSVGSpaces(ptr, end) || *ptr != '(')
    570         return -1;
    571 
    572     ptr++;
    573 
    574     skipOptionalSVGSpaces(ptr, end);
    575 
    576     while (requiredParams < required) {
    577         if (ptr >= end || !parseNumber(ptr, end, values[requiredParams], false))
    578             return -1;
    579         requiredParams++;
    580         if (requiredParams < required)
    581             skipOptionalSVGSpacesOrDelimiter(ptr, end);
    582     }
    583     if (!skipOptionalSVGSpaces(ptr, end))
    584         return -1;
    585 
    586     bool delimParsed = skipOptionalSVGSpacesOrDelimiter(ptr, end);
    587 
    588     if (ptr >= end)
    589         return -1;
    590 
    591     if (*ptr == ')') { // skip optionals
    592         ptr++;
    593         if (delimParsed)
    594             return -1;
    595     } else {
    596         while (optionalParams < optional) {
    597             if (ptr >= end || !parseNumber(ptr, end, values[requiredParams + optionalParams], false))
    598                 return -1;
    599             optionalParams++;
    600             if (optionalParams < optional)
    601                 skipOptionalSVGSpacesOrDelimiter(ptr, end);
    602         }
    603 
    604         if (!skipOptionalSVGSpaces(ptr, end))
    605             return -1;
    606 
    607         delimParsed = skipOptionalSVGSpacesOrDelimiter(ptr, end);
    608 
    609         if (ptr >= end || *ptr != ')' || delimParsed)
    610             return -1;
    611         ptr++;
    612     }
    613 
    614     return requiredParams + optionalParams;
    615 }
    616 
    617 // These should be kept in sync with enum SVGTransformType
    618 static const int requiredValuesForType[] =  {0, 6, 1, 1, 1, 1, 1};
    619 static const int optionalValuesForType[] =  {0, 0, 1, 1, 2, 0, 0};
    620 
    621 template<typename CharType>
    622 static bool parseTransformValueInternal(unsigned type, const CharType*& ptr, const CharType* end, SVGTransform& transform)
    623 {
    624     if (type == SVGTransform::SVG_TRANSFORM_UNKNOWN)
    625         return false;
    626 
    627     int valueCount = 0;
    628     float values[] = {0, 0, 0, 0, 0, 0};
    629     if ((valueCount = parseTransformParamList(ptr, end, values, requiredValuesForType[type], optionalValuesForType[type])) < 0)
    630         return false;
    631 
    632     switch (type) {
    633     case SVGTransform::SVG_TRANSFORM_SKEWX:
    634         transform.setSkewX(values[0]);
    635         break;
    636     case SVGTransform::SVG_TRANSFORM_SKEWY:
    637         transform.setSkewY(values[0]);
    638         break;
    639     case SVGTransform::SVG_TRANSFORM_SCALE:
    640         if (valueCount == 1) // Spec: if only one param given, assume uniform scaling
    641             transform.setScale(values[0], values[0]);
    642         else
    643             transform.setScale(values[0], values[1]);
    644         break;
    645     case SVGTransform::SVG_TRANSFORM_TRANSLATE:
    646         if (valueCount == 1) // Spec: if only one param given, assume 2nd param to be 0
    647             transform.setTranslate(values[0], 0);
    648         else
    649             transform.setTranslate(values[0], values[1]);
    650         break;
    651     case SVGTransform::SVG_TRANSFORM_ROTATE:
    652         if (valueCount == 1)
    653             transform.setRotate(values[0], 0, 0);
    654         else
    655             transform.setRotate(values[0], values[1], values[2]);
    656         break;
    657     case SVGTransform::SVG_TRANSFORM_MATRIX:
    658         transform.setMatrix(AffineTransform(values[0], values[1], values[2], values[3], values[4], values[5]));
    659         break;
    660     }
    661 
    662     return true;
    663 }
    664 
    665 bool parseTransformValue(unsigned type, const LChar*& ptr, const LChar* end, SVGTransform& transform)
    666 {
    667     return parseTransformValueInternal(type, ptr, end, transform);
    668 }
    669 
    670 bool parseTransformValue(unsigned type, const UChar*& ptr, const UChar* end, SVGTransform& transform)
    671 {
    672     return parseTransformValueInternal(type, ptr, end, transform);
    673 }
    674 
    675 static const LChar skewXDesc[] =  {'s', 'k', 'e', 'w', 'X'};
    676 static const LChar skewYDesc[] =  {'s', 'k', 'e', 'w', 'Y'};
    677 static const LChar scaleDesc[] =  {'s', 'c', 'a', 'l', 'e'};
    678 static const LChar translateDesc[] =  {'t', 'r', 'a', 'n', 's', 'l', 'a', 't', 'e'};
    679 static const LChar rotateDesc[] =  {'r', 'o', 't', 'a', 't', 'e'};
    680 static const LChar matrixDesc[] =  {'m', 'a', 't', 'r', 'i', 'x'};
    681 
    682 template<typename CharType>
    683 static inline bool parseAndSkipType(const CharType*& ptr, const CharType* end, unsigned short& type)
    684 {
    685     if (ptr >= end)
    686         return false;
    687 
    688     if (*ptr == 's') {
    689         if (skipString(ptr, end, skewXDesc, WTF_ARRAY_LENGTH(skewXDesc)))
    690             type = SVGTransform::SVG_TRANSFORM_SKEWX;
    691         else if (skipString(ptr, end, skewYDesc, WTF_ARRAY_LENGTH(skewYDesc)))
    692             type = SVGTransform::SVG_TRANSFORM_SKEWY;
    693         else if (skipString(ptr, end, scaleDesc, WTF_ARRAY_LENGTH(scaleDesc)))
    694             type = SVGTransform::SVG_TRANSFORM_SCALE;
    695         else
    696             return false;
    697     } else if (skipString(ptr, end, translateDesc, WTF_ARRAY_LENGTH(translateDesc)))
    698         type = SVGTransform::SVG_TRANSFORM_TRANSLATE;
    699     else if (skipString(ptr, end, rotateDesc, WTF_ARRAY_LENGTH(rotateDesc)))
    700         type = SVGTransform::SVG_TRANSFORM_ROTATE;
    701     else if (skipString(ptr, end, matrixDesc, WTF_ARRAY_LENGTH(matrixDesc)))
    702         type = SVGTransform::SVG_TRANSFORM_MATRIX;
    703     else
    704         return false;
    705 
    706     return true;
    707 }
    708 
    709 SVGTransform::SVGTransformType parseTransformType(const String& string)
    710 {
    711     if (string.isEmpty())
    712         return SVGTransform::SVG_TRANSFORM_UNKNOWN;
    713     unsigned short type = SVGTransform::SVG_TRANSFORM_UNKNOWN;
    714     if (string.is8Bit()) {
    715         const LChar* ptr = string.characters8();
    716         const LChar* end = ptr + string.length();
    717         parseAndSkipType(ptr, end, type);
    718     } else {
    719         const UChar* ptr = string.characters16();
    720         const UChar* end = ptr + string.length();
    721         parseAndSkipType(ptr, end, type);
    722     }
    723     return static_cast<SVGTransform::SVGTransformType>(type);
    724 }
    725 
    726 template<typename CharType>
    727 bool parseTransformAttributeInternal(SVGTransformList& list, const CharType*& ptr, const CharType* end, TransformParsingMode mode)
    728 {
    729     if (mode == ClearList)
    730         list.clear();
    731 
    732     bool delimParsed = false;
    733     while (ptr < end) {
    734         delimParsed = false;
    735         unsigned short type = SVGTransform::SVG_TRANSFORM_UNKNOWN;
    736         skipOptionalSVGSpaces(ptr, end);
    737 
    738         if (!parseAndSkipType(ptr, end, type))
    739             return false;
    740 
    741         SVGTransform transform;
    742         if (!parseTransformValue(type, ptr, end, transform))
    743             return false;
    744 
    745         list.append(transform);
    746         skipOptionalSVGSpaces(ptr, end);
    747         if (ptr < end && *ptr == ',') {
    748             delimParsed = true;
    749             ++ptr;
    750         }
    751         skipOptionalSVGSpaces(ptr, end);
    752     }
    753 
    754     return !delimParsed;
    755 }
    756 
    757 bool parseTransformAttribute(SVGTransformList& list, const LChar*& ptr, const LChar* end, TransformParsingMode mode)
    758 {
    759     return parseTransformAttributeInternal(list, ptr, end, mode);
    760 }
    761 
    762 bool parseTransformAttribute(SVGTransformList& list, const UChar*& ptr, const UChar* end, TransformParsingMode mode)
    763 {
    764     return parseTransformAttributeInternal(list, ptr, end, mode);
    765 }
    766 
    767 }
    768