WebCore/svg/SVGParserUtilities.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/svg/SVGParserUtilities.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,348 @@
+/*
+   Copyright (C) 2002, 2003 The Karbon Developers
+                 2006       Alexander Kellett <lypanov@kde.org>
+                 2006, 2007 Rob Buis <buis@kde.org>
+   Copyrigth (C) 2007, 2009 Apple, Inc.  All rights reserved.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.
+*/
+
+#include "config.h"
+
+#if ENABLE(SVG)
+#include "SVGParserUtilities.h"
+
+#include "Document.h"
+#include "ExceptionCode.h"
+#include "FloatPoint.h"
+#include "PlatformString.h"
+#include "SVGPointList.h"
+#include <wtf/ASCIICType.h>
+
+namespace WebCore {
+
+/* We use this generic _parseNumber function to allow the Path parsing code to work 
+ * at a higher precision internally, without any unnecessary runtime cost or code
+ * complexity
+ */    
+template <typename FloatType> static bool _parseNumber(const UChar*& ptr, const UChar* end, FloatType& number, bool skip)
+{
+    int integer, exponent;
+    FloatType decimal, frac;
+    int sign, expsign;
+    const UChar* start = ptr;
+
+    exponent = 0;
+    integer = 0;
+    frac = 1;
+    decimal = 0;
+    sign = 1;
+    expsign = 1;
+
+    // read the sign
+    if (ptr < end && *ptr == '+')
+        ptr++;
+    else if (ptr < end && *ptr == '-') {
+        ptr++;
+        sign = -1;
+    } 
+    
+    if (ptr == end || ((*ptr < '0' || *ptr > '9') && *ptr != '.'))
+        // The first character of a number must be one of [0-9+-.]
+        return false;
+
+    // read the integer part
+    while (ptr < end && *ptr >= '0' && *ptr <= '9')
+        integer = (integer * 10) + *(ptr++) - '0';
+
+    if (ptr < end && *ptr == '.') { // read the decimals
+        ptr++;
+        
+        // There must be a least one digit following the .
+        if (ptr >= end || *ptr < '0' || *ptr > '9')
+            return false;
+        
+        while (ptr < end && *ptr >= '0' && *ptr <= '9')
+            decimal += (*(ptr++) - '0') * (frac *= static_cast<FloatType>(0.1));
+    }
+
+    // read the exponent part
+    if (ptr != start && ptr + 1 < end && (*ptr == 'e' || *ptr == 'E') 
+        && (ptr[1] != 'x' && ptr[1] != 'm')) { 
+        ptr++;
+
+        // read the sign of the exponent
+        if (*ptr == '+')
+            ptr++;
+        else if (*ptr == '-') {
+            ptr++;
+            expsign = -1;
+        }
+        
+        // There must be an exponent
+        if (ptr >= end || *ptr < '0' || *ptr > '9')
+            return false;
+
+        while (ptr < end && *ptr >= '0' && *ptr <= '9') {
+            exponent *= 10;
+            exponent += *ptr - '0';
+            ptr++;
+        }
+    }
+
+    number = integer + decimal;
+    number *= sign * static_cast<FloatType>(pow(10.0, expsign * exponent));
+
+    if (start == ptr)
+        return false;
+
+    if (skip)
+        skipOptionalSpacesOrDelimiter(ptr, end);
+
+    return true;
+}
+
+bool parseNumber(const UChar*& ptr, const UChar* end, float& number, bool skip) 
+{
+    return _parseNumber(ptr, end, number, skip);
+}
+
+// only used to parse largeArcFlag and sweepFlag which must be a "0" or "1"
+// and might not have any whitespace/comma after it
+bool parseArcFlag(const UChar*& ptr, const UChar* end, bool& flag)
+{
+    const UChar flagChar = *ptr++;
+    if (flagChar == '0')
+        flag = false;
+    else if (flagChar == '1')
+        flag = true;
+    else
+        return false;
+    
+    skipOptionalSpacesOrDelimiter(ptr, end);
+    
+    return true;
+}
+
+bool parseNumberOptionalNumber(const String& s, float& x, float& y)
+{
+    if (s.isEmpty())
+        return false;
+    const UChar* cur = s.characters();
+    const UChar* end = cur + s.length();
+
+    if (!parseNumber(cur, end, x))
+        return false;
+
+    if (cur == end)
+        y = x;
+    else if (!parseNumber(cur, end, y, false))
+        return false;
+
+    return cur == end;
+}
+
+bool pointsListFromSVGData(SVGPointList* pointsList, const String& points)
+{
+    if (points.isEmpty())
+        return true;
+    const UChar* cur = points.characters();
+    const UChar* end = cur + points.length();
+
+    skipOptionalSpaces(cur, end);
+
+    bool delimParsed = false;
+    while (cur < end) {
+        delimParsed = false;
+        float xPos = 0.0f;
+        if (!parseNumber(cur, end, xPos))
+           return false;
+
+        float yPos = 0.0f;
+        if (!parseNumber(cur, end, yPos, false))
+            return false;
+
+        skipOptionalSpaces(cur, end);
+
+        if (cur < end && *cur == ',') {
+            delimParsed = true;
+            cur++;
+        }
+        skipOptionalSpaces(cur, end);
+
+        ExceptionCode ec = 0;
+        pointsList->appendItem(FloatPoint(xPos, yPos), ec);
+    }
+    return cur == end && !delimParsed;
+}
+
+bool parseGlyphName(const String& input, HashSet<String>& values)
+{
+    // FIXME: Parsing error detection is missing.
+    values.clear();
+
+    const UChar* ptr = input.characters();
+    const UChar* end = ptr + input.length();
+    skipOptionalSpaces(ptr, end);
+
+    while (ptr < end) {
+        // Leading and trailing white space, and white space before and after separators, will be ignored.
+        const UChar* inputStart = ptr;
+        while (ptr < end && *ptr != ',')
+            ++ptr;
+
+        if (ptr == inputStart)
+            break;
+
+        // walk backwards from the ; to ignore any whitespace
+        const UChar* inputEnd = ptr - 1;
+        while (inputStart < inputEnd && isWhitespace(*inputEnd))
+            --inputEnd;
+
+        values.add(String(inputStart, inputEnd - inputStart + 1));
+        skipOptionalSpacesOrDelimiter(ptr, end, ',');
+    }
+
+    return true;
+}
+
+static bool parseUnicodeRange(const UChar* characters, unsigned length, UnicodeRange& range)
+{
+    if (length < 2 || characters[0] != 'U' || characters[1] != '+')
+        return false;
+    
+    // Parse the starting hex number (or its prefix).
+    unsigned startRange = 0;
+    unsigned startLength = 0;
+
+    const UChar* ptr = characters + 2;
+    const UChar* end = characters + length;
+    while (ptr < end) {
+        if (!isASCIIHexDigit(*ptr))
+            break;
+        ++startLength;
+        if (startLength > 6)
+            return false;
+        startRange = (startRange << 4) | toASCIIHexValue(*ptr);
+        ++ptr;
+    }
+    
+    // Handle the case of ranges separated by "-" sign.
+    if (2 + startLength < length && *ptr == '-') {
+        if (!startLength)
+            return false;
+        
+        // Parse the ending hex number (or its prefix).
+        unsigned endRange = 0;
+        unsigned endLength = 0;
+        ++ptr;
+        while (ptr < end) {
+            if (!isASCIIHexDigit(*ptr))
+                break;
+            ++endLength;
+            if (endLength > 6)
+                return false;
+            endRange = (endRange << 4) | toASCIIHexValue(*ptr);
+            ++ptr;
+        }
+        
+        if (!endLength)
+            return false;
+        
+        range.first = startRange;
+        range.second = endRange;
+        return true;
+    }
+    
+    // Handle the case of a number with some optional trailing question marks.
+    unsigned endRange = startRange;
+    while (ptr < end) {
+        if (*ptr != '?')
+            break;
+        ++startLength;
+        if (startLength > 6)
+            return false;
+        startRange <<= 4;
+        endRange = (endRange << 4) | 0xF;
+        ++ptr;
+    }
+    
+    if (!startLength)
+        return false;
+    
+    range.first = startRange;
+    range.second = endRange;
+    return true;
+}
+
+bool parseKerningUnicodeString(const String& input, UnicodeRanges& rangeList, HashSet<String>& stringList)
+{
+    // FIXME: Parsing error detection is missing.
+    const UChar* ptr = input.characters();
+    const UChar* end = ptr + input.length();
+
+    while (ptr < end) {
+        const UChar* inputStart = ptr;
+        while (ptr < end && *ptr != ',')
+            ++ptr;
+
+        if (ptr == inputStart)
+            break;
+
+        // Try to parse unicode range first
+        UnicodeRange range;
+        if (parseUnicodeRange(inputStart, ptr - inputStart, range))
+            rangeList.append(range);
+        else
+            stringList.add(String(inputStart, ptr - inputStart));
+        ++ptr;
+    }
+
+    return true;
+}
+
+Vector<String> parseDelimitedString(const String& input, const char seperator)
+{
+    Vector<String> values;
+
+    const UChar* ptr = input.characters();
+    const UChar* end = ptr + input.length();
+    skipOptionalSpaces(ptr, end);
+
+    while (ptr < end) {
+        // Leading and trailing white space, and white space before and after semicolon separators, will be ignored.
+        const UChar* inputStart = ptr;
+        while (ptr < end && *ptr != seperator) // careful not to ignore whitespace inside inputs
+            ptr++;
+
+        if (ptr == inputStart)
+            break;
+
+        // walk backwards from the ; to ignore any whitespace
+        const UChar* inputEnd = ptr - 1;
+        while (inputStart < inputEnd && isWhitespace(*inputEnd))
+            inputEnd--;
+
+        values.append(String(inputStart, inputEnd - inputStart + 1));
+        skipOptionalSpacesOrDelimiter(ptr, end, seperator);
+    }
+
+    return values;
+}
+
+}
+
+#endif // ENABLE(SVG)