| /* |
| 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) |