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 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 
     25 #if ENABLE(SVG)
     26 #include "SVGParserUtilities.h"
     27 
     28 #include "Document.h"
     29 #include "FloatPoint.h"
     30 #include "SVGPointList.h"
     31 
     32 #include <limits>
     33 #include <wtf/ASCIICType.h>
     34 
     35 namespace WebCore {
     36 
     37 template <typename FloatType> 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 FloatType> static bool genericParseNumber(const UChar*& ptr, const UChar* end, FloatType& number, bool skip)
     47 {
     48     FloatType integer, decimal, frac, exponent;
     49     int sign, expsign;
     50     const UChar* start = ptr;
     51 
     52     exponent = 0;
     53     integer = 0;
     54     frac = 1;
     55     decimal = 0;
     56     sign = 1;
     57     expsign = 1;
     58 
     59     // read the sign
     60     if (ptr < end && *ptr == '+')
     61         ptr++;
     62     else if (ptr < end && *ptr == '-') {
     63         ptr++;
     64         sign = -1;
     65     }
     66 
     67     if (ptr == end || ((*ptr < '0' || *ptr > '9') && *ptr != '.'))
     68         // The first character of a number must be one of [0-9+-.]
     69         return false;
     70 
     71     // read the integer part, build right-to-left
     72     const UChar* ptrStartIntPart = ptr;
     73     while (ptr < end && *ptr >= '0' && *ptr <= '9')
     74         ++ptr; // Advance to first non-digit.
     75 
     76     if (ptr != ptrStartIntPart) {
     77         const UChar* ptrScanIntPart = ptr - 1;
     78         FloatType multiplier = 1;
     79         while (ptrScanIntPart >= ptrStartIntPart) {
     80             integer += multiplier * static_cast<FloatType>(*(ptrScanIntPart--) - '0');
     81             multiplier *= 10;
     82         }
     83         // Bail out early if this overflows.
     84         if (!isValidRange(integer))
     85             return false;
     86     }
     87 
     88     if (ptr < end && *ptr == '.') { // read the decimals
     89         ptr++;
     90 
     91         // There must be a least one digit following the .
     92         if (ptr >= end || *ptr < '0' || *ptr > '9')
     93             return false;
     94 
     95         while (ptr < end && *ptr >= '0' && *ptr <= '9')
     96             decimal += (*(ptr++) - '0') * (frac *= static_cast<FloatType>(0.1));
     97     }
     98 
     99     // read the exponent part
    100     if (ptr != start && ptr + 1 < end && (*ptr == 'e' || *ptr == 'E')
    101         && (ptr[1] != 'x' && ptr[1] != 'm')) {
    102         ptr++;
    103 
    104         // read the sign of the exponent
    105         if (*ptr == '+')
    106             ptr++;
    107         else if (*ptr == '-') {
    108             ptr++;
    109             expsign = -1;
    110         }
    111 
    112         // There must be an exponent
    113         if (ptr >= end || *ptr < '0' || *ptr > '9')
    114             return false;
    115 
    116         while (ptr < end && *ptr >= '0' && *ptr <= '9') {
    117             exponent *= static_cast<FloatType>(10);
    118             exponent += *ptr - '0';
    119             ptr++;
    120         }
    121         // Make sure exponent is valid.
    122         if (!isValidRange(exponent) || exponent > std::numeric_limits<FloatType>::max_exponent)
    123             return false;
    124     }
    125 
    126     number = integer + decimal;
    127     number *= sign;
    128 
    129     if (exponent)
    130         number *= static_cast<FloatType>(pow(10.0, expsign * static_cast<int>(exponent)));
    131 
    132     // Don't return Infinity() or NaN().
    133     if (!isValidRange(number))
    134         return false;
    135 
    136     if (start == ptr)
    137         return false;
    138 
    139     if (skip)
    140         skipOptionalSpacesOrDelimiter(ptr, end);
    141 
    142     return true;
    143 }
    144 
    145 bool parseNumber(const UChar*& ptr, const UChar* end, float& number, bool skip)
    146 {
    147     return genericParseNumber(ptr, end, number, skip);
    148 }
    149 
    150 // only used to parse largeArcFlag and sweepFlag which must be a "0" or "1"
    151 // and might not have any whitespace/comma after it
    152 bool parseArcFlag(const UChar*& ptr, const UChar* end, bool& flag)
    153 {
    154     const UChar flagChar = *ptr++;
    155     if (flagChar == '0')
    156         flag = false;
    157     else if (flagChar == '1')
    158         flag = true;
    159     else
    160         return false;
    161 
    162     skipOptionalSpacesOrDelimiter(ptr, end);
    163 
    164     return true;
    165 }
    166 
    167 bool parseNumberOptionalNumber(const String& s, float& x, float& y)
    168 {
    169     if (s.isEmpty())
    170         return false;
    171     const UChar* cur = s.characters();
    172     const UChar* end = cur + s.length();
    173 
    174     if (!parseNumber(cur, end, x))
    175         return false;
    176 
    177     if (cur == end)
    178         y = x;
    179     else if (!parseNumber(cur, end, y, false))
    180         return false;
    181 
    182     return cur == end;
    183 }
    184 
    185 bool pointsListFromSVGData(SVGPointList& pointsList, const String& points)
    186 {
    187     if (points.isEmpty())
    188         return true;
    189     const UChar* cur = points.characters();
    190     const UChar* end = cur + points.length();
    191 
    192     skipOptionalSpaces(cur, end);
    193 
    194     bool delimParsed = false;
    195     while (cur < end) {
    196         delimParsed = false;
    197         float xPos = 0.0f;
    198         if (!parseNumber(cur, end, xPos))
    199            return false;
    200 
    201         float yPos = 0.0f;
    202         if (!parseNumber(cur, end, yPos, false))
    203             return false;
    204 
    205         skipOptionalSpaces(cur, end);
    206 
    207         if (cur < end && *cur == ',') {
    208             delimParsed = true;
    209             cur++;
    210         }
    211         skipOptionalSpaces(cur, end);
    212 
    213         pointsList.append(FloatPoint(xPos, yPos));
    214     }
    215     return cur == end && !delimParsed;
    216 }
    217 
    218 bool parseGlyphName(const String& input, HashSet<String>& values)
    219 {
    220     // FIXME: Parsing error detection is missing.
    221     values.clear();
    222 
    223     const UChar* ptr = input.characters();
    224     const UChar* end = ptr + input.length();
    225     skipOptionalSpaces(ptr, end);
    226 
    227     while (ptr < end) {
    228         // Leading and trailing white space, and white space before and after separators, will be ignored.
    229         const UChar* inputStart = ptr;
    230         while (ptr < end && *ptr != ',')
    231             ++ptr;
    232 
    233         if (ptr == inputStart)
    234             break;
    235 
    236         // walk backwards from the ; to ignore any whitespace
    237         const UChar* inputEnd = ptr - 1;
    238         while (inputStart < inputEnd && isWhitespace(*inputEnd))
    239             --inputEnd;
    240 
    241         values.add(String(inputStart, inputEnd - inputStart + 1));
    242         skipOptionalSpacesOrDelimiter(ptr, end, ',');
    243     }
    244 
    245     return true;
    246 }
    247 
    248 static bool parseUnicodeRange(const UChar* characters, unsigned length, UnicodeRange& range)
    249 {
    250     if (length < 2 || characters[0] != 'U' || characters[1] != '+')
    251         return false;
    252 
    253     // Parse the starting hex number (or its prefix).
    254     unsigned startRange = 0;
    255     unsigned startLength = 0;
    256 
    257     const UChar* ptr = characters + 2;
    258     const UChar* end = characters + length;
    259     while (ptr < end) {
    260         if (!isASCIIHexDigit(*ptr))
    261             break;
    262         ++startLength;
    263         if (startLength > 6)
    264             return false;
    265         startRange = (startRange << 4) | toASCIIHexValue(*ptr);
    266         ++ptr;
    267     }
    268 
    269     // Handle the case of ranges separated by "-" sign.
    270     if (2 + startLength < length && *ptr == '-') {
    271         if (!startLength)
    272             return false;
    273 
    274         // Parse the ending hex number (or its prefix).
    275         unsigned endRange = 0;
    276         unsigned endLength = 0;
    277         ++ptr;
    278         while (ptr < end) {
    279             if (!isASCIIHexDigit(*ptr))
    280                 break;
    281             ++endLength;
    282             if (endLength > 6)
    283                 return false;
    284             endRange = (endRange << 4) | toASCIIHexValue(*ptr);
    285             ++ptr;
    286         }
    287 
    288         if (!endLength)
    289             return false;
    290 
    291         range.first = startRange;
    292         range.second = endRange;
    293         return true;
    294     }
    295 
    296     // Handle the case of a number with some optional trailing question marks.
    297     unsigned endRange = startRange;
    298     while (ptr < end) {
    299         if (*ptr != '?')
    300             break;
    301         ++startLength;
    302         if (startLength > 6)
    303             return false;
    304         startRange <<= 4;
    305         endRange = (endRange << 4) | 0xF;
    306         ++ptr;
    307     }
    308 
    309     if (!startLength)
    310         return false;
    311 
    312     range.first = startRange;
    313     range.second = endRange;
    314     return true;
    315 }
    316 
    317 bool parseKerningUnicodeString(const String& input, UnicodeRanges& rangeList, HashSet<String>& stringList)
    318 {
    319     // FIXME: Parsing error detection is missing.
    320     const UChar* ptr = input.characters();
    321     const UChar* end = ptr + input.length();
    322 
    323     while (ptr < end) {
    324         const UChar* inputStart = ptr;
    325         while (ptr < end && *ptr != ',')
    326             ++ptr;
    327 
    328         if (ptr == inputStart)
    329             break;
    330 
    331         // Try to parse unicode range first
    332         UnicodeRange range;
    333         if (parseUnicodeRange(inputStart, ptr - inputStart, range))
    334             rangeList.append(range);
    335         else
    336             stringList.add(String(inputStart, ptr - inputStart));
    337         ++ptr;
    338     }
    339 
    340     return true;
    341 }
    342 
    343 Vector<String> parseDelimitedString(const String& input, const char seperator)
    344 {
    345     Vector<String> values;
    346 
    347     const UChar* ptr = input.characters();
    348     const UChar* end = ptr + input.length();
    349     skipOptionalSpaces(ptr, end);
    350 
    351     while (ptr < end) {
    352         // Leading and trailing white space, and white space before and after semicolon separators, will be ignored.
    353         const UChar* inputStart = ptr;
    354         while (ptr < end && *ptr != seperator) // careful not to ignore whitespace inside inputs
    355             ptr++;
    356 
    357         if (ptr == inputStart)
    358             break;
    359 
    360         // walk backwards from the ; to ignore any whitespace
    361         const UChar* inputEnd = ptr - 1;
    362         while (inputStart < inputEnd && isWhitespace(*inputEnd))
    363             inputEnd--;
    364 
    365         values.append(String(inputStart, inputEnd - inputStart + 1));
    366         skipOptionalSpacesOrDelimiter(ptr, end, seperator);
    367     }
    368 
    369     return values;
    370 }
    371 
    372 }
    373 
    374 #endif // ENABLE(SVG)
    375