SVGParserUtilities.cpp   [plain text]


/*
 * Copyright (C) 2002, 2003 The Karbon Developers
 * Copyright (C) 2006 Alexander Kellett <lypanov@kde.org>
 * Copyright (C) 2006, 2007 Rob Buis <buis@kde.org>
 * Copyright (C) 2007-2018 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"
#include "SVGParserUtilities.h"

#include "Document.h"
#include "FloatRect.h"
#include <limits>
#include <wtf/ASCIICType.h>
#include <wtf/text/StringParsingBuffer.h>
#include <wtf/text/StringView.h>

namespace WebCore {

template <typename FloatType> static inline bool isValidRange(const FloatType& x)
{
    static const FloatType max = std::numeric_limits<FloatType>::max();
    return x >= -max && x <= max;
}

// 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.
// FIXME: Can this be shared/replaced with number parsing in WTF?
template <typename CharacterType, typename FloatType = float> static Optional<FloatType> genericParseNumber(StringParsingBuffer<CharacterType>& buffer, SuffixSkippingPolicy skip = SuffixSkippingPolicy::Skip)
{
    FloatType number = 0;
    FloatType integer = 0;
    FloatType decimal = 0;
    FloatType frac = 1;
    FloatType exponent = 0;
    int sign = 1;
    int expsign = 1;
    auto start = buffer.position();

    // read the sign
    if (buffer.hasCharactersRemaining() && *buffer == '+')
        ++buffer;
    else if (buffer.hasCharactersRemaining() && *buffer == '-') {
        ++buffer;
        sign = -1;
    } 
    
    if (buffer.atEnd() || (!isASCIIDigit(*buffer) && *buffer != '.'))
        return WTF::nullopt;

    // read the integer part, build right-to-left
    auto ptrStartIntPart = buffer.position();
    
    // Advance to first non-digit.
    skipWhile<isASCIIDigit>(buffer);

    if (buffer.position() != ptrStartIntPart) {
        auto ptrScanIntPart = buffer.position() - 1;
        FloatType multiplier = 1;
        while (ptrScanIntPart >= ptrStartIntPart) {
            integer += multiplier * static_cast<FloatType>(*(ptrScanIntPart--) - '0');
            multiplier *= 10;
        }
        // Bail out early if this overflows.
        if (!isValidRange(integer))
            return WTF::nullopt;
    }

    // read the decimals
    if (buffer.hasCharactersRemaining() && *buffer == '.') {
        ++buffer;
        
        // There must be a least one digit following the .
        if (buffer.atEnd() || !isASCIIDigit(*buffer))
            return WTF::nullopt;
        
        while (buffer.hasCharactersRemaining() && isASCIIDigit(*buffer))
            decimal += (*(buffer++) - '0') * (frac *= static_cast<FloatType>(0.1));
    }

    // read the exponent part
    if (buffer.position() != start && buffer.position() + 1 < buffer.end() && (*buffer == 'e' || *buffer == 'E')
        && (buffer[1] != 'x' && buffer[1] != 'm')) {
        ++buffer;

        // read the sign of the exponent
        if (*buffer == '+')
            ++buffer;
        else if (*buffer == '-') {
            ++buffer;
            expsign = -1;
        }
        
        // There must be an exponent
        if (buffer.atEnd() || !isASCIIDigit(*buffer))
            return WTF::nullopt;

        while (buffer.hasCharactersRemaining() && isASCIIDigit(*buffer)) {
            exponent *= static_cast<FloatType>(10);
            exponent += *buffer++ - '0';
        }
        // Make sure exponent is valid.
        if (!isValidRange(exponent) || exponent > std::numeric_limits<FloatType>::max_exponent)
            return WTF::nullopt;
    }

    number = integer + decimal;
    number *= sign;

    if (exponent)
        number *= static_cast<FloatType>(pow(10.0, expsign * static_cast<int>(exponent)));

    // Don't return Infinity() or NaN().
    if (!isValidRange(number))
        return WTF::nullopt;

    if (start == buffer.position())
        return WTF::nullopt;

    if (skip == SuffixSkippingPolicy::Skip)
        skipOptionalSVGSpacesOrDelimiter(buffer);

    return number;
}

Optional<float> parseNumber(StringParsingBuffer<LChar>& buffer, SuffixSkippingPolicy skip)
{
    return genericParseNumber(buffer, skip);
}

Optional<float> parseNumber(StringParsingBuffer<UChar>& buffer, SuffixSkippingPolicy skip)
{
    return genericParseNumber(buffer, skip);
}

Optional<float> parseNumber(const StringView& string, SuffixSkippingPolicy skip)
{
    return readCharactersForParsing(string, [skip](auto buffer) -> Optional<float> {
        auto result = genericParseNumber(buffer, skip);
        if (!buffer.atEnd())
            return WTF::nullopt;
        return result;
    });
}

// only used to parse largeArcFlag and sweepFlag which must be a "0" or "1"
// and might not have any whitespace/comma after it
template <typename CharacterType> Optional<bool> genericParseArcFlag(StringParsingBuffer<CharacterType>& buffer)
{
    if (buffer.atEnd())
        return WTF::nullopt;

    const CharacterType flagChar = *buffer;
    ++buffer;

    bool flag;
    if (flagChar == '0')
        flag = false;
    else if (flagChar == '1')
        flag = true;
    else
        return WTF::nullopt;

    skipOptionalSVGSpacesOrDelimiter(buffer);
    
    return flag;
}

Optional<bool> parseArcFlag(StringParsingBuffer<LChar>& buffer)
{
    return genericParseArcFlag(buffer);
}

Optional<bool> parseArcFlag(StringParsingBuffer<UChar>& buffer)
{
    return genericParseArcFlag(buffer);
}

Optional<std::pair<float, float>> parseNumberOptionalNumber(const StringView& string)
{
    if (string.isEmpty())
        return WTF::nullopt;

    return readCharactersForParsing(string, [](auto buffer) -> Optional<std::pair<float, float>> {
        auto x = parseNumber(buffer);
        if (!x)
            return WTF::nullopt;

        if (buffer.atEnd())
            return std::make_pair(*x, *x);

        auto y = parseNumber(buffer, SuffixSkippingPolicy::DontSkip);
        if (!y)
            return WTF::nullopt;

        if (!buffer.atEnd())
            return WTF::nullopt;

        return std::make_pair(*x, *y);
    });
}

Optional<FloatPoint> parsePoint(const StringView& string)
{
    if (string.isEmpty())
        return WTF::nullopt;

    return readCharactersForParsing(string, [](auto buffer) -> Optional<FloatPoint> {
        if (!skipOptionalSVGSpaces(buffer))
            return WTF::nullopt;

        auto point = parseFloatPoint(buffer);
        if (!point)
            return WTF::nullopt;

        // Disallow anything except spaces at the end.
        skipOptionalSVGSpaces(buffer);
        
        return point;
    });
}

Optional<FloatRect> parseRect(const StringView& string)
{
    return readCharactersForParsing(string, [](auto buffer) -> Optional<FloatRect> {
        skipOptionalSVGSpaces(buffer);
        
        auto x = parseNumber(buffer);
        if (!x)
            return WTF::nullopt;
        auto y = parseNumber(buffer);
        if (!y)
            return WTF::nullopt;
        auto width = parseNumber(buffer);
        if (!width)
            return WTF::nullopt;
        auto height = parseNumber(buffer, SuffixSkippingPolicy::DontSkip);
        if (!height)
            return WTF::nullopt;

        return FloatRect { *x, *y, *width, *height };
    });
}

Optional<HashSet<String>> parseGlyphName(const StringView& string)
{
    // FIXME: Parsing error detection is missing.

    return readCharactersForParsing(string, [](auto buffer) -> HashSet<String> {
        skipOptionalSVGSpaces(buffer);

        HashSet<String> values;

        while (buffer.hasCharactersRemaining()) {
            // Leading and trailing white space, and white space before and after separators, will be ignored.
            auto inputStart = buffer.position();

            skipUntil(buffer, ',');

            if (buffer.position() == inputStart)
                break;

            // walk backwards from the ; to ignore any whitespace
            auto inputEnd = buffer.position() - 1;
            while (inputStart < inputEnd && isSVGSpace(*inputEnd))
                --inputEnd;

            values.add(String(inputStart, inputEnd - inputStart + 1));
            skipOptionalSVGSpacesOrDelimiter(buffer, ',');
        }
        return values;
    });

}

template<typename CharacterType> static Optional<UnicodeRange> parseUnicodeRange(StringParsingBuffer<CharacterType> buffer)
{
    unsigned length = buffer.lengthRemaining();
    if (length < 2 || buffer[0] != 'U' || buffer[1] != '+')
        return WTF::nullopt;

    buffer += 2;

    // Parse the starting hex number (or its prefix).
    unsigned startRange = 0;
    unsigned startLength = 0;

    while (buffer.hasCharactersRemaining()) {
        if (!isASCIIHexDigit(*buffer))
            break;
        ++startLength;
        if (startLength > 6)
            return WTF::nullopt;
        startRange = (startRange << 4) | toASCIIHexValue(*buffer);
        ++buffer;
    }

    // Handle the case of ranges separated by "-" sign.
    if (2 + startLength < length && *buffer == '-') {
        if (!startLength)
            return WTF::nullopt;
        
        // Parse the ending hex number (or its prefix).
        unsigned endRange = 0;
        unsigned endLength = 0;
        ++buffer;
        while (buffer.hasCharactersRemaining()) {
            if (!isASCIIHexDigit(*buffer))
                break;
            ++endLength;
            if (endLength > 6)
                return WTF::nullopt;
            endRange = (endRange << 4) | toASCIIHexValue(*buffer);
            ++buffer;
        }
        
        if (!endLength)
            return WTF::nullopt;
        
        UnicodeRange range;
        range.first = startRange;
        range.second = endRange;
        return range;
    }
    
    // Handle the case of a number with some optional trailing question marks.
    unsigned endRange = startRange;
    while (buffer.hasCharactersRemaining()) {
        if (*buffer != '?')
            break;
        ++startLength;
        if (startLength > 6)
            return WTF::nullopt;
        startRange <<= 4;
        endRange = (endRange << 4) | 0xF;
        ++buffer;
    }
    
    if (!startLength)
        return WTF::nullopt;
    
    UnicodeRange range;
    range.first = startRange;
    range.second = endRange;
    return range;
}

Optional<std::pair<UnicodeRanges, HashSet<String>>> parseKerningUnicodeString(const StringView& string)
{
    // FIXME: Parsing error detection is missing.

    return readCharactersForParsing(string, [](auto buffer) -> std::pair<UnicodeRanges, HashSet<String>> {
        UnicodeRanges rangeList;
        HashSet<String> stringList;

        while (1) {
            auto inputStart = buffer.position();

            skipUntil(buffer, ',');

            if (buffer.position() == inputStart)
                break;

            // Try to parse unicode range first
            if (auto range = parseUnicodeRange(StringParsingBuffer { inputStart, buffer.position() }))
                rangeList.append(WTFMove(*range));
            else
                stringList.add(String(inputStart, buffer.position() - inputStart));

            if (buffer.atEnd())
                break;

            ++buffer;
        }

        return std::make_pair(WTFMove(rangeList), WTFMove(stringList));
    });
}

template <typename CharacterType> static Optional<FloatPoint> genericParseFloatPoint(StringParsingBuffer<CharacterType>& buffer)
{
    auto x = parseNumber(buffer);
    if (!x)
        return WTF::nullopt;

    auto y = parseNumber(buffer);
    if (!y)
        return WTF::nullopt;

    return FloatPoint { *x, *y };
}

Optional<FloatPoint> parseFloatPoint(StringParsingBuffer<LChar>& buffer)
{
    return genericParseFloatPoint(buffer);
}

Optional<FloatPoint> parseFloatPoint(StringParsingBuffer<UChar>& buffer)
{
    return genericParseFloatPoint(buffer);
}

}