SVGParserUtilities.cpp   [plain text]


/*
   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 "ExceptionCode.h"
#include "FloatConversion.h"
#include "FloatPoint.h"
#include "Path.h"
#include "PlatformString.h"
#include "SVGPathSegList.h"
#include "SVGPathSegArc.h"
#include "SVGPathSegClosePath.h"
#include "SVGPathSegCurvetoCubic.h"
#include "SVGPathSegCurvetoCubicSmooth.h"
#include "SVGPathSegCurvetoQuadratic.h"
#include "SVGPathSegCurvetoQuadraticSmooth.h"
#include "SVGPathSegLineto.h"
#include "SVGPathSegLinetoHorizontal.h"
#include "SVGPathSegLinetoVertical.h"
#include "SVGPathSegList.h"
#include "SVGPathSegMoveto.h"
#include "SVGPointList.h"
#include "SVGPathElement.h"
#include <math.h>
#include <wtf/MathExtras.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 for parsing Paths
static bool parseNumber(const UChar*& ptr, const UChar* end, double& number, bool skip = true) 
{
    return _parseNumber(ptr, end, number, skip);
}

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;
}

    /**
     * Parser for svg path data, contained in the d attribute.
     *
     * The parser delivers encountered commands and parameters by calling
     * methods that correspond to those commands. Clients have to derive
     * from this class and implement the abstract command methods.
     *
     * There are two operating modes. By default the parser just delivers unaltered
     * svg path data commands and parameters. In the second mode, it will convert all
     * relative coordinates to absolute ones, and convert all curves to cubic beziers.
     */
    class SVGPathParser {
    public:
        virtual ~SVGPathParser() { }
        bool parseSVG(const String& d, bool process = false);

    protected:
        virtual void svgMoveTo(double x1, double y1, bool closed, bool abs = true) = 0;
        virtual void svgLineTo(double x1, double y1, bool abs = true) = 0;
        virtual void svgLineToHorizontal(double, bool /*abs*/ = true) { }
        virtual void svgLineToVertical(double /*y*/, bool /*abs*/ = true) { }
        virtual void svgCurveToCubic(double x1, double y1, double x2, double y2, double x, double y, bool abs = true) = 0;
        virtual void svgCurveToCubicSmooth(double /*x*/, double /*y*/, double /*x2*/, double /*y2*/, bool /*abs*/ = true) { }
        virtual void svgCurveToQuadratic(double /*x*/, double /*y*/, double /*x1*/, double /*y1*/, bool /*abs*/ = true) { }
        virtual void svgCurveToQuadraticSmooth(double /*x*/, double /*y*/, bool /*abs*/ = true) { }
        virtual void svgArcTo(double /*x*/, double /*y*/, double /*r1*/, double /*r2*/, double /*angle*/, bool /*largeArcFlag*/, bool /*sweepFlag*/, bool /*abs*/ = true) { }
        virtual void svgClosePath() = 0;

    private:
        void calculateArc(bool relative, double& curx, double& cury, double angle, double x, double y, double r1, double r2, bool largeArcFlag, bool sweepFlag);
    };
    
bool SVGPathParser::parseSVG(const String& s, bool process)
{
    const UChar* ptr = s.characters();
    const UChar* end = ptr + s.length();

    double contrlx, contrly, curx, cury, subpathx, subpathy, tox, toy, x1, y1, x2, y2, xc, yc;
    double px1, py1, px2, py2, px3, py3;
    bool closed = true;
    
    if (!skipOptionalSpaces(ptr, end)) // skip any leading spaces
        return false;
    
    char command = *(ptr++), lastCommand = ' ';
    if (command != 'm' && command != 'M') // path must start with moveto
        return false;

    subpathx = subpathy = curx = cury = contrlx = contrly = 0.0;
    while (1) {
        skipOptionalSpaces(ptr, end); // skip spaces between command and first coord

        bool relative = false;

        switch(command)
        {
            case 'm':
                relative = true;
            case 'M':
            {
                if (!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
                    return false;

                if (process) {
                    subpathx = curx = relative ? curx + tox : tox;
                    subpathy = cury = relative ? cury + toy : toy;

                    svgMoveTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury), closed);
                } else
                    svgMoveTo(narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), closed, !relative);
                closed = false;
                break;
            }
            case 'l':
                relative = true;
            case 'L':
            {
                if (!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
                    return false;

                if (process) {
                    curx = relative ? curx + tox : tox;
                    cury = relative ? cury + toy : toy;

                    svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
                }
                else
                    svgLineTo(narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
                break;
            }
            case 'h':
            {
                if (!parseNumber(ptr, end, tox))
                    return false;
                if (process) {
                    curx = curx + tox;
                    svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
                }
                else
                    svgLineToHorizontal(narrowPrecisionToFloat(tox), false);
                break;
            }
            case 'H':
            {
                if (!parseNumber(ptr, end, tox))
                    return false;
                if (process) {
                    curx = tox;
                    svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
                }
                else
                    svgLineToHorizontal(narrowPrecisionToFloat(tox));
                break;
            }
            case 'v':
            {
                if (!parseNumber(ptr, end, toy))
                    return false;
                if (process) {
                    cury = cury + toy;
                    svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
                }
                else
                    svgLineToVertical(narrowPrecisionToFloat(toy), false);
                break;
            }
            case 'V':
            {
                if (!parseNumber(ptr, end, toy))
                    return false;
                if (process) {
                    cury = toy;
                    svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
                }
                else
                    svgLineToVertical(narrowPrecisionToFloat(toy));
                break;
            }
            case 'z':
            case 'Z':
            {
                // reset curx, cury for next path
                if (process) {
                    curx = subpathx;
                    cury = subpathy;
                }
                closed = true;
                svgClosePath();
                break;
            }
            case 'c':
                relative = true;
            case 'C':
            {
                if (!parseNumber(ptr, end, x1)  || !parseNumber(ptr, end, y1) ||
                    !parseNumber(ptr, end, x2)  || !parseNumber(ptr, end, y2) ||
                    !parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
                    return false;

                if (process) {
                    px1 = relative ? curx + x1 : x1;
                    py1 = relative ? cury + y1 : y1;
                    px2 = relative ? curx + x2 : x2;
                    py2 = relative ? cury + y2 : y2;
                    px3 = relative ? curx + tox : tox;
                    py3 = relative ? cury + toy : toy;

                    svgCurveToCubic(narrowPrecisionToFloat(px1), narrowPrecisionToFloat(py1), narrowPrecisionToFloat(px2), 
                                    narrowPrecisionToFloat(py2), narrowPrecisionToFloat(px3), narrowPrecisionToFloat(py3));

                    contrlx = relative ? curx + x2 : x2;
                    contrly = relative ? cury + y2 : y2;
                    curx = relative ? curx + tox : tox;
                    cury = relative ? cury + toy : toy;
                }
                else
                    svgCurveToCubic(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1), narrowPrecisionToFloat(x2),
                                    narrowPrecisionToFloat(y2), narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);

                break;
            }
            case 's':
                relative = true;
            case 'S':
            {
                if (!parseNumber(ptr, end, x2)  || !parseNumber(ptr, end, y2) ||
                    !parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
                    return false;

                if (!(lastCommand == 'c' || lastCommand == 'C' ||
                     lastCommand == 's' || lastCommand == 'S')) {
                    contrlx = curx;
                    contrly = cury;
                }

                if (process) {
                    px1 = 2 * curx - contrlx;
                    py1 = 2 * cury - contrly;
                    px2 = relative ? curx + x2 : x2;
                    py2 = relative ? cury + y2 : y2;
                    px3 = relative ? curx + tox : tox;
                    py3 = relative ? cury + toy : toy;

                    svgCurveToCubic(narrowPrecisionToFloat(px1), narrowPrecisionToFloat(py1), narrowPrecisionToFloat(px2),
                                    narrowPrecisionToFloat(py2), narrowPrecisionToFloat(px3), narrowPrecisionToFloat(py3));

                    contrlx = relative ? curx + x2 : x2;
                    contrly = relative ? cury + y2 : y2;
                    curx = relative ? curx + tox : tox;
                    cury = relative ? cury + toy : toy;
                }
                else
                    svgCurveToCubicSmooth(narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2), 
                                          narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
                break;
            }
            case 'q':
                relative = true;
            case 'Q':
            {
                if (!parseNumber(ptr, end, x1)  || !parseNumber(ptr, end, y1) ||
                    !parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
                    return false;

                if (process) {
                    px1 = relative ? (curx + 2 * (x1 + curx)) * (1.0 / 3.0) : (curx + 2 * x1) * (1.0 / 3.0);
                    py1 = relative ? (cury + 2 * (y1 + cury)) * (1.0 / 3.0) : (cury + 2 * y1) * (1.0 / 3.0);
                    px2 = relative ? ((curx + tox) + 2 * (x1 + curx)) * (1.0 / 3.0) : (tox + 2 * x1) * (1.0 / 3.0);
                    py2 = relative ? ((cury + toy) + 2 * (y1 + cury)) * (1.0 / 3.0) : (toy + 2 * y1) * (1.0 / 3.0);
                    px3 = relative ? curx + tox : tox;
                    py3 = relative ? cury + toy : toy;

                    svgCurveToCubic(narrowPrecisionToFloat(px1), narrowPrecisionToFloat(py1), narrowPrecisionToFloat(px2),
                                    narrowPrecisionToFloat(py2), narrowPrecisionToFloat(px3), narrowPrecisionToFloat(py3));

                    contrlx = relative ? curx + x1 : x1;
                    contrly = relative ? cury + y1 : y1;
                    curx = relative ? curx + tox : tox;
                    cury = relative ? cury + toy : toy;
                }
                else
                    svgCurveToQuadratic(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1),
                                        narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
                break;
            }
            case 't':
                relative = true;
            case 'T':
            {
                if (!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
                    return false;
                if (!(lastCommand == 'q' || lastCommand == 'Q' ||
                     lastCommand == 't' || lastCommand == 'T')) {
                    contrlx = curx;
                    contrly = cury;
                }

                if (process) {
                    xc = 2 * curx - contrlx;
                    yc = 2 * cury - contrly;

                    px1 = relative ? (curx + 2 * xc) * (1.0 / 3.0) : (curx + 2 * xc) * (1.0 / 3.0);
                    py1 = relative ? (cury + 2 * yc) * (1.0 / 3.0) : (cury + 2 * yc) * (1.0 / 3.0);
                    px2 = relative ? ((curx + tox) + 2 * xc) * (1.0 / 3.0) : (tox + 2 * xc) * (1.0 / 3.0);
                    py2 = relative ? ((cury + toy) + 2 * yc) * (1.0 / 3.0) : (toy + 2 * yc) * (1.0 / 3.0);
                    px3 = relative ? curx + tox : tox;
                    py3 = relative ? cury + toy : toy;

                    svgCurveToCubic(narrowPrecisionToFloat(px1), narrowPrecisionToFloat(py1), narrowPrecisionToFloat(px2),
                                    narrowPrecisionToFloat(py2), narrowPrecisionToFloat(px3), narrowPrecisionToFloat(py3));

                    contrlx = xc;
                    contrly = yc;
                    curx = relative ? curx + tox : tox;
                    cury = relative ? cury + toy : toy;
                }
                else
                    svgCurveToQuadraticSmooth(narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
                break;
            }
            case 'a':
                relative = true;
            case 'A':
            {
                bool largeArc, sweep;
                double angle, rx, ry;
                if (!parseNumber(ptr, end, rx)    || !parseNumber(ptr, end, ry) ||
                    !parseNumber(ptr, end, angle) || !parseNumber(ptr, end, tox))
                    return false;
                largeArc = tox == 1;
                if (!parseNumber(ptr, end, tox))
                    return false;
                sweep = tox == 1;
                if (!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
                    return false;

                // Spec: radii are nonnegative numbers
                rx = fabs(rx);
                ry = fabs(ry);

                if (process)
                    calculateArc(relative, curx, cury, angle, tox, toy, rx, ry, largeArc, sweep);
                else
                    svgArcTo(narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), narrowPrecisionToFloat(rx), narrowPrecisionToFloat(ry),
                             narrowPrecisionToFloat(angle), largeArc, sweep, !relative);
                break;
            }
            default:
                // FIXME: An error should go to the JavaScript console, or the like.
                return false;
        }
        lastCommand = command;

        if (ptr >= end)
            return true;

        // Check for remaining coordinates in the current command.
        if ((*ptr == '+' || *ptr == '-' || (*ptr >= '0' && *ptr <= '9')) &&
            (command != 'z' && command != 'Z')) {
            if (command == 'M')
                command = 'L';
            else if (command == 'm')
                command = 'l';
        } else
            command = *(ptr++);

        if (lastCommand != 'C' && lastCommand != 'c' &&
            lastCommand != 'S' && lastCommand != 's' &&
            lastCommand != 'Q' && lastCommand != 'q' &&
            lastCommand != 'T' && lastCommand != 't') {
            contrlx = curx;
            contrly = cury;
        }
    }

    return false;
}

// This works by converting the SVG arc to "simple" beziers.
// For each bezier found a svgToCurve call is done.
// Adapted from Niko's code in kdelibs/kdecore/svgicons.
// Maybe this can serve in some shared lib? (Rob)
void SVGPathParser::calculateArc(bool relative, double& curx, double& cury, double angle, double x, double y, double r1, double r2, bool largeArcFlag, bool sweepFlag)
{
    double sin_th, cos_th;
    double a00, a01, a10, a11;
    double x0, y0, x1, y1, xc, yc;
    double d, sfactor, sfactor_sq;
    double th0, th1, th_arc;
    int i, n_segs;

    sin_th = sin(angle * (piDouble / 180.0));
    cos_th = cos(angle * (piDouble / 180.0));

    double dx;

    if (!relative)
        dx = (curx - x) / 2.0;
    else
        dx = -x / 2.0;

    double dy;
        
    if (!relative)
        dy = (cury - y) / 2.0;
    else
        dy = -y / 2.0;
        
    double _x1 =  cos_th * dx + sin_th * dy;
    double _y1 = -sin_th * dx + cos_th * dy;
    double Pr1 = r1 * r1;
    double Pr2 = r2 * r2;
    double Px = _x1 * _x1;
    double Py = _y1 * _y1;

    // Spec : check if radii are large enough
    double check = Px / Pr1 + Py / Pr2;
    if (check > 1) {
        r1 = r1 * sqrt(check);
        r2 = r2 * sqrt(check);
    }

    a00 = cos_th / r1;
    a01 = sin_th / r1;
    a10 = -sin_th / r2;
    a11 = cos_th / r2;

    x0 = a00 * curx + a01 * cury;
    y0 = a10 * curx + a11 * cury;

    if (!relative)
        x1 = a00 * x + a01 * y;
    else
        x1 = a00 * (curx + x) + a01 * (cury + y);
        
    if (!relative)
        y1 = a10 * x + a11 * y;
    else
        y1 = a10 * (curx + x) + a11 * (cury + y);

    /* (x0, y0) is current point in transformed coordinate space.
       (x1, y1) is new point in transformed coordinate space.

       The arc fits a unit-radius circle in this space.
    */

    d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);

    sfactor_sq = 1.0 / d - 0.25;

    if (sfactor_sq < 0)
        sfactor_sq = 0;

    sfactor = sqrt(sfactor_sq);

    if (sweepFlag == largeArcFlag)
        sfactor = -sfactor;

    xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
    yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);

    /* (xc, yc) is center of the circle. */
    th0 = atan2(y0 - yc, x0 - xc);
    th1 = atan2(y1 - yc, x1 - xc);

    th_arc = th1 - th0;
    if (th_arc < 0 && sweepFlag)
        th_arc += 2 * piDouble;
    else if (th_arc > 0 && !sweepFlag)
        th_arc -= 2 * piDouble;

    n_segs = (int) (int) ceil(fabs(th_arc / (piDouble * 0.5 + 0.001)));

    for(i = 0; i < n_segs; i++) {
        double sin_th, cos_th;
        double a00, a01, a10, a11;
        double x1, y1, x2, y2, x3, y3;
        double t;
        double th_half;

        double _th0 = th0 + i * th_arc / n_segs;
        double _th1 = th0 + (i + 1) * th_arc / n_segs;

        sin_th = sin(angle * (piDouble / 180.0));
        cos_th = cos(angle * (piDouble / 180.0));

        /* inverse transform compared with rsvg_path_arc */
        a00 = cos_th * r1;
        a01 = -sin_th * r2;
        a10 = sin_th * r1;
        a11 = cos_th * r2;

        th_half = 0.5 * (_th1 - _th0);
        t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half);
        x1 = xc + cos(_th0) - t * sin(_th0);
        y1 = yc + sin(_th0) + t * cos(_th0);
        x3 = xc + cos(_th1);
        y3 = yc + sin(_th1);
        x2 = x3 + t * sin(_th1);
        y2 = y3 - t * cos(_th1);

        svgCurveToCubic(narrowPrecisionToFloat(a00 * x1 + a01 * y1), narrowPrecisionToFloat(a10 * x1 + a11 * y1),
                        narrowPrecisionToFloat(a00 * x2 + a01 * y2), narrowPrecisionToFloat(a10 * x2 + a11 * y2),
                        narrowPrecisionToFloat(a00 * x3 + a01 * y3), narrowPrecisionToFloat(a10 * x3 + a11 * y3));
    }

    if (!relative)
        curx = x;
    else
        curx += x;

    if (!relative)
        cury = y;
    else
        cury += y;    
}

class PathBuilder : private SVGPathParser {
public:
    bool build(Path* path, const String& d)
    {
        Path temporaryPath;
        m_path = &temporaryPath;
        if (!parseSVG(d, true))
            return false;
        temporaryPath.swap(*path);
        return true;
    }

private:
    virtual void svgMoveTo(double x1, double y1, bool closed, bool abs = true)
    {
        current.setX(narrowPrecisionToFloat(abs ? x1 : current.x() + x1));
        current.setY(narrowPrecisionToFloat(abs ? y1 : current.y() + y1));
        if (closed)
            m_path->closeSubpath();
        m_path->moveTo(current);
    }
    virtual void svgLineTo(double x1, double y1, bool abs = true)
    {
        current.setX(narrowPrecisionToFloat(abs ? x1 : current.x() + x1));
        current.setY(narrowPrecisionToFloat(abs ? y1 : current.y() + y1));
        m_path->addLineTo(current);
    }
    virtual void svgCurveToCubic(double x1, double y1, double x2, double y2, double x, double y, bool abs = true)
    {
        if (!abs) {
            x1 += current.x();
            y1 += current.y();
            x2 += current.x();
            y2 += current.y();
        }
        current.setX(narrowPrecisionToFloat(abs ? x : current.x() + x));
        current.setY(narrowPrecisionToFloat(abs ? y : current.y() + y));
        m_path->addBezierCurveTo(FloatPoint::narrowPrecision(x1, y1), FloatPoint::narrowPrecision(x2, y2), current);
    }
    virtual void svgClosePath()
    {
        m_path->closeSubpath();
    }

    Path* m_path;
    FloatPoint current;
};

bool pathFromSVGData(Path& path, const String& d)
{
    PathBuilder builder;
    return builder.build(&path, d);
}

class SVGPathSegListBuilder : private SVGPathParser {
public:
    bool build(SVGPathSegList* segList, const String& d, bool process)
    {
        if (!parseSVG(d, process))
            return false;
        size_t size = m_vector.size();
        for (size_t i = 0; i < size; ++i) {
            ExceptionCode ec;
            segList->appendItem(m_vector[i].release(), ec);
        }
        m_vector.clear();
        return true;
    }

private:
    virtual void svgMoveTo(double x1, double y1, bool, bool abs = true)
    {
        if (abs)
            m_vector.append(SVGPathElement::createSVGPathSegMovetoAbs(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1)));
        else
            m_vector.append(SVGPathElement::createSVGPathSegMovetoRel(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1)));
    }
    virtual void svgLineTo(double x1, double y1, bool abs = true)
    {
        if (abs)
            m_vector.append(SVGPathElement::createSVGPathSegLinetoAbs(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1)));
        else
            m_vector.append(SVGPathElement::createSVGPathSegLinetoRel(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1)));
    }
    virtual void svgLineToHorizontal(double x, bool abs)
    {
        if (abs)
            m_vector.append(SVGPathElement::createSVGPathSegLinetoHorizontalAbs(narrowPrecisionToFloat(x)));
        else
            m_vector.append(SVGPathElement::createSVGPathSegLinetoHorizontalRel(narrowPrecisionToFloat(x)));
    }
    virtual void svgLineToVertical(double y, bool abs)
    {
        if (abs)
            m_vector.append(SVGPathElement::createSVGPathSegLinetoVerticalAbs(narrowPrecisionToFloat(y)));
        else
            m_vector.append(SVGPathElement::createSVGPathSegLinetoVerticalRel(narrowPrecisionToFloat(y)));
    }
    virtual void svgCurveToCubic(double x1, double y1, double x2, double y2, double x, double y, bool abs = true)
    {
        if (abs)
            m_vector.append(SVGPathElement::createSVGPathSegCurvetoCubicAbs(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y),
                                                                                      narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1),
                                                                                      narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2)));
        else
            m_vector.append(SVGPathElement::createSVGPathSegCurvetoCubicRel(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y),
                                                                                      narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1),
                                                                                      narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2)));
    }
    virtual void svgCurveToCubicSmooth(double x, double y, double x2, double y2, bool abs)
    {
        if (abs)
            m_vector.append(SVGPathElement::createSVGPathSegCurvetoCubicSmoothAbs(narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2),
                                                                                            narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)));
        else
            m_vector.append(SVGPathElement::createSVGPathSegCurvetoCubicSmoothRel(narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2),
                                                                                            narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)));
    }
    virtual void svgCurveToQuadratic(double x, double y, double x1, double y1, bool abs)
    {
        if (abs)
            m_vector.append(SVGPathElement::createSVGPathSegCurvetoQuadraticAbs(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1), 
                                                                                          narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)));
        else
            m_vector.append(SVGPathElement::createSVGPathSegCurvetoQuadraticRel(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1), 
                                                                                          narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)));
    }
    virtual void svgCurveToQuadraticSmooth(double x, double y, bool abs)
    {
        if (abs)
            m_vector.append(SVGPathElement::createSVGPathSegCurvetoQuadraticSmoothAbs(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)));
        else
            m_vector.append(SVGPathElement::createSVGPathSegCurvetoQuadraticSmoothRel(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)));
    }
    virtual void svgArcTo(double x, double y, double r1, double r2, double angle, bool largeArcFlag, bool sweepFlag, bool abs)
    {
        if (abs)
            m_vector.append(SVGPathElement::createSVGPathSegArcAbs(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y),
                                                                             narrowPrecisionToFloat(r1), narrowPrecisionToFloat(r2), 
                                                                             narrowPrecisionToFloat(angle), largeArcFlag, sweepFlag));
        else
            m_vector.append(SVGPathElement::createSVGPathSegArcRel(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y),
                                                                             narrowPrecisionToFloat(r1), narrowPrecisionToFloat(r2),
                                                                             narrowPrecisionToFloat(angle), largeArcFlag, sweepFlag));
    }
    virtual void svgClosePath()
    {
        m_vector.append(SVGPathElement::createSVGPathSegClosePath());
    }

    Vector<RefPtr<SVGPathSeg> > m_vector;
};

bool pathSegListFromSVGData(SVGPathSegList* path, const String& d, bool process)
{
    SVGPathSegListBuilder builder;
    return builder.build(path, d, process);
}

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)