File indexing completed on 2024-04-28 11:39:18

0001 /* This file is part of the KDE project
0002    Copyright (C) 2002, 2003 The Karbon Developers
0003                  2006       Alexander Kellett <lypanov@kde.org>
0004                  2006, 2007 Rob Buis <buis@kde.org>
0005                  2007       Apple, Inc.  All rights reserved.
0006 
0007    This library is free software; you can redistribute it and/or
0008    modify it under the terms of the GNU Library General Public
0009    License as published by the Free Software Foundation; either
0010    version 2 of the License, or (at your option) any later version.
0011 
0012    This library is distributed in the hope that it will be useful,
0013    but WITHOUT ANY WARRANTY; without even the implied warranty of
0014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015    Library General Public License for more details.
0016 
0017    You should have received a copy of the GNU Library General Public License
0018    along with this library; see the file COPYING.LIB.  If not, write to
0019    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020    Boston, MA 02110-1301, USA.
0021 */
0022 
0023 #include "wtf/Platform.h"
0024 #if ENABLE(SVG)
0025 #include "xml/Document.h"
0026 #include "SVGParserUtilities.h"
0027 
0028 #include "ExceptionCode.h"
0029 #include "FloatConversion.h"
0030 #include "FloatPoint.h"
0031 #include "Path.h"
0032 #include "PlatformString.h"
0033 #include "SVGPathSegList.h"
0034 #include "SVGPathSegArc.h"
0035 #include "SVGPathSegClosePath.h"
0036 #include "SVGPathSegCurvetoCubic.h"
0037 #include "SVGPathSegCurvetoCubicSmooth.h"
0038 #include "SVGPathSegCurvetoQuadratic.h"
0039 #include "SVGPathSegCurvetoQuadraticSmooth.h"
0040 #include "SVGPathSegLineto.h"
0041 #include "SVGPathSegLinetoHorizontal.h"
0042 #include "SVGPathSegLinetoVertical.h"
0043 #include "SVGPathSegList.h"
0044 #include "SVGPathSegMoveto.h"
0045 #include "SVGPointList.h"
0046 #include "SVGPathElement.h"
0047 #include <math.h>
0048 #include <wtf/MathExtras.h>
0049 
0050 namespace WebCore
0051 {
0052 
0053 /* We use this generic _parseNumber function to allow the Path parsing code to work
0054  * at a higher precision internally, without any unnecessary runtime cost or code
0055  * complexity
0056  */
0057 template <typename FloatType> static bool _parseNumber(const UChar *&ptr, const UChar *end, FloatType &number, bool skip)
0058 {
0059     int integer, exponent;
0060     FloatType decimal, frac;
0061     int sign, expsign;
0062     const UChar *start = ptr;
0063 
0064     exponent = 0;
0065     integer = 0;
0066     frac = 1;
0067     decimal = 0;
0068     sign = 1;
0069     expsign = 1;
0070 
0071     // read the sign
0072     if (ptr < end && *ptr == '+') {
0073         ptr++;
0074     } else if (ptr < end && *ptr == '-') {
0075         ptr++;
0076         sign = -1;
0077     }
0078 
0079     if (ptr == end || ((*ptr < '0' || *ptr > '9') && *ptr != '.'))
0080         // The first character of a number must be one of [0-9+-.]
0081     {
0082         return false;
0083     }
0084 
0085     // read the integer part
0086     while (ptr < end && *ptr >= '0' && *ptr <= '9') {
0087         integer = (integer * 10) + (ptr++)->unicode() - '0';
0088     }
0089 
0090     if (ptr < end && *ptr == '.') { // read the decimals
0091         ptr++;
0092 
0093         // There must be a least one digit following the .
0094         if (ptr >= end || *ptr < '0' || *ptr > '9') {
0095             return false;
0096         }
0097 
0098         while (ptr < end && *ptr >= '0' && *ptr <= '9') {
0099             decimal += ((ptr++)->unicode() - '0') * (frac *= static_cast<FloatType>(0.1));
0100         }
0101     }
0102 
0103     // read the exponent part
0104     if (ptr != start && ptr + 1 < end && (*ptr == 'e' || *ptr == 'E')
0105             && (ptr[1] != 'x' && ptr[1] != 'm')) {
0106         ptr++;
0107 
0108         // read the sign of the exponent
0109         if (*ptr == '+') {
0110             ptr++;
0111         } else if (*ptr == '-') {
0112             ptr++;
0113             expsign = -1;
0114         }
0115 
0116         // There must be an exponent
0117         if (ptr >= end || *ptr < '0' || *ptr > '9') {
0118             return false;
0119         }
0120 
0121         while (ptr < end && *ptr >= '0' && *ptr <= '9') {
0122             exponent *= 10;
0123             exponent += ptr->unicode() - '0';
0124             ptr++;
0125         }
0126     }
0127 
0128     number = integer + decimal;
0129     number *= sign * static_cast<FloatType>(pow(10.0, expsign * exponent));
0130 
0131     if (start == ptr) {
0132         return false;
0133     }
0134 
0135     if (skip) {
0136         skipOptionalSpacesOrDelimiter(ptr, end);
0137     }
0138 
0139     return true;
0140 }
0141 
0142 bool parseNumber(const UChar *&ptr, const UChar *end, float &number, bool skip)
0143 {
0144     return _parseNumber(ptr, end, number, skip);
0145 }
0146 
0147 // Only used for parsing Paths
0148 static bool parseNumber(const UChar *&ptr, const UChar *end, double &number, bool skip = true)
0149 {
0150     return _parseNumber(ptr, end, number, skip);
0151 }
0152 
0153 bool parseNumberOptionalNumber(const String &s, float &x, float &y)
0154 {
0155     if (s.isEmpty()) {
0156         return false;
0157     }
0158     const UChar *cur = s.characters();
0159     const UChar *end = cur + s.length();
0160 
0161     if (!parseNumber(cur, end, x)) {
0162         return false;
0163     }
0164 
0165     if (cur == end) {
0166         y = x;
0167     } else if (!parseNumber(cur, end, y, false)) {
0168         return false;
0169     }
0170 
0171     return cur == end;
0172 }
0173 
0174 bool pointsListFromSVGData(SVGPointList *pointsList, const String &points)
0175 {
0176     if (points.isEmpty()) {
0177         return true;
0178     }
0179     const UChar *cur = points.characters();
0180     const UChar *end = cur + points.length();
0181 
0182     skipOptionalSpaces(cur, end);
0183 
0184     bool delimParsed = false;
0185     while (cur < end) {
0186         delimParsed = false;
0187         float xPos = 0.0f;
0188         if (!parseNumber(cur, end, xPos)) {
0189             return false;
0190         }
0191 
0192         float yPos = 0.0f;
0193         if (!parseNumber(cur, end, yPos, false)) {
0194             return false;
0195         }
0196 
0197         skipOptionalSpaces(cur, end);
0198 
0199         if (cur < end && *cur == ',') {
0200             delimParsed = true;
0201             cur++;
0202         }
0203         skipOptionalSpaces(cur, end);
0204 
0205         ExceptionCode ec = 0;
0206         pointsList->appendItem(FloatPoint(xPos, yPos), ec);
0207     }
0208     return cur == end && !delimParsed;
0209 }
0210 
0211 /**
0212  * Parser for svg path data, contained in the d attribute.
0213  *
0214  * The parser delivers encountered commands and parameters by calling
0215  * methods that correspond to those commands. Clients have to derive
0216  * from this class and implement the abstract command methods.
0217  *
0218  * There are two operating modes. By default the parser just delivers unaltered
0219  * svg path data commands and parameters. In the second mode, it will convert all
0220  * relative coordinates to absolute ones, and convert all curves to cubic beziers.
0221  */
0222 class SVGPathParser
0223 {
0224 public:
0225     virtual ~SVGPathParser() { }
0226     bool parseSVG(const String &d, bool process = false);
0227 
0228 protected:
0229     virtual void svgMoveTo(double x1, double y1, bool closed, bool abs = true) = 0;
0230     virtual void svgLineTo(double x1, double y1, bool abs = true) = 0;
0231     virtual void svgLineToHorizontal(double x, bool abs = true)
0232     {
0233         Q_UNUSED(x);
0234         Q_UNUSED(abs);
0235     }
0236     virtual void svgLineToVertical(double y, bool abs = true)
0237     {
0238         Q_UNUSED(y);
0239         Q_UNUSED(abs);
0240     }
0241     virtual void svgCurveToCubic(double x1, double y1, double x2, double y2, double x, double y, bool abs = true) = 0;
0242     virtual void svgCurveToCubicSmooth(double x, double y, double x2, double y2, bool abs = true)
0243     {
0244         Q_UNUSED(x);
0245         Q_UNUSED(y);
0246         Q_UNUSED(x2);
0247         Q_UNUSED(y2);
0248         Q_UNUSED(abs);
0249     }
0250     virtual void svgCurveToQuadratic(double x, double y, double x1, double y1, bool abs = true)
0251     {
0252         Q_UNUSED(x);
0253         Q_UNUSED(y);
0254         Q_UNUSED(x1);
0255         Q_UNUSED(y1);
0256         Q_UNUSED(abs);
0257     }
0258     virtual void svgCurveToQuadraticSmooth(double x, double y, bool abs = true)
0259     {
0260         Q_UNUSED(x);
0261         Q_UNUSED(y);
0262         Q_UNUSED(abs);
0263     }
0264     virtual void svgArcTo(double x, double y, double r1, double r2, double angle, bool largeArcFlag, bool sweepFlag, bool abs = true)
0265     {
0266         Q_UNUSED(x);
0267         Q_UNUSED(y);
0268         Q_UNUSED(r1);
0269         Q_UNUSED(r2);
0270         Q_UNUSED(angle);
0271         Q_UNUSED(largeArcFlag);
0272         Q_UNUSED(sweepFlag);
0273         Q_UNUSED(abs);
0274     }
0275     virtual void svgClosePath() = 0;
0276 private:
0277     void calculateArc(bool relative, double &curx, double &cury, double angle, double x, double y, double r1, double r2, bool largeArcFlag, bool sweepFlag);
0278 };
0279 
0280 bool SVGPathParser::parseSVG(const String &s, bool process)
0281 {
0282     if (s.isEmpty()) {
0283         return false;
0284     }
0285 
0286     const UChar *ptr = s.characters();
0287     const UChar *end = ptr + s.length();
0288 
0289     double contrlx, contrly, curx, cury, subpathx, subpathy, tox, toy, x1, y1, x2, y2, xc, yc;
0290     double px1, py1, px2, py2, px3, py3;
0291     bool closed = true;
0292 
0293     if (!skipOptionalSpaces(ptr, end)) { // skip any leading spaces
0294         return false;
0295     }
0296 
0297     char command = (ptr++)->unicode(), lastCommand = ' ';// or toLatin1() instead of unicode()???
0298     if (command != 'm' && command != 'M') { // path must start with moveto
0299         return false;
0300     }
0301 
0302     subpathx = subpathy = curx = cury = contrlx = contrly = 0.0;
0303     while (1) {
0304         skipOptionalSpaces(ptr, end); // skip spaces between command and first coord
0305 
0306         bool relative = false;
0307 
0308         switch (command) {
0309         case 'm':
0310             relative = true;
0311         case 'M': {
0312             if (!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy)) {
0313                 return false;
0314             }
0315 
0316             if (process) {
0317                 subpathx = curx = relative ? curx + tox : tox;
0318                 subpathy = cury = relative ? cury + toy : toy;
0319 
0320                 svgMoveTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury), closed);
0321             } else {
0322                 svgMoveTo(narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), closed, !relative);
0323             }
0324             closed = false;
0325             break;
0326         }
0327         case 'l':
0328             relative = true;
0329         case 'L': {
0330             if (!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy)) {
0331                 return false;
0332             }
0333 
0334             if (process) {
0335                 curx = relative ? curx + tox : tox;
0336                 cury = relative ? cury + toy : toy;
0337 
0338                 svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
0339             } else {
0340                 svgLineTo(narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
0341             }
0342             break;
0343         }
0344         case 'h': {
0345             if (!parseNumber(ptr, end, tox)) {
0346                 return false;
0347             }
0348             if (process) {
0349                 curx = curx + tox;
0350                 svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
0351             } else {
0352                 svgLineToHorizontal(narrowPrecisionToFloat(tox), false);
0353             }
0354             break;
0355         }
0356         case 'H': {
0357             if (!parseNumber(ptr, end, tox)) {
0358                 return false;
0359             }
0360             if (process) {
0361                 curx = tox;
0362                 svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
0363             } else {
0364                 svgLineToHorizontal(narrowPrecisionToFloat(tox));
0365             }
0366             break;
0367         }
0368         case 'v': {
0369             if (!parseNumber(ptr, end, toy)) {
0370                 return false;
0371             }
0372             if (process) {
0373                 cury = cury + toy;
0374                 svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
0375             } else {
0376                 svgLineToVertical(narrowPrecisionToFloat(toy), false);
0377             }
0378             break;
0379         }
0380         case 'V': {
0381             if (!parseNumber(ptr, end, toy)) {
0382                 return false;
0383             }
0384             if (process) {
0385                 cury = toy;
0386                 svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
0387             } else {
0388                 svgLineToVertical(narrowPrecisionToFloat(toy));
0389             }
0390             break;
0391         }
0392         case 'z':
0393         case 'Z': {
0394             // reset curx, cury for next path
0395             if (process) {
0396                 curx = subpathx;
0397                 cury = subpathy;
0398             }
0399             closed = true;
0400             svgClosePath();
0401             break;
0402         }
0403         case 'c':
0404             relative = true;
0405         case 'C': {
0406             if (!parseNumber(ptr, end, x1)  || !parseNumber(ptr, end, y1) ||
0407                     !parseNumber(ptr, end, x2)  || !parseNumber(ptr, end, y2) ||
0408                     !parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy)) {
0409                 return false;
0410             }
0411 
0412             if (process) {
0413                 px1 = relative ? curx + x1 : x1;
0414                 py1 = relative ? cury + y1 : y1;
0415                 px2 = relative ? curx + x2 : x2;
0416                 py2 = relative ? cury + y2 : y2;
0417                 px3 = relative ? curx + tox : tox;
0418                 py3 = relative ? cury + toy : toy;
0419 
0420                 svgCurveToCubic(narrowPrecisionToFloat(px1), narrowPrecisionToFloat(py1), narrowPrecisionToFloat(px2),
0421                                 narrowPrecisionToFloat(py2), narrowPrecisionToFloat(px3), narrowPrecisionToFloat(py3));
0422 
0423                 contrlx = relative ? curx + x2 : x2;
0424                 contrly = relative ? cury + y2 : y2;
0425                 curx = relative ? curx + tox : tox;
0426                 cury = relative ? cury + toy : toy;
0427             } else
0428                 svgCurveToCubic(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1), narrowPrecisionToFloat(x2),
0429                                 narrowPrecisionToFloat(y2), narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
0430 
0431             break;
0432         }
0433         case 's':
0434             relative = true;
0435         case 'S': {
0436             if (!parseNumber(ptr, end, x2)  || !parseNumber(ptr, end, y2) ||
0437                     !parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy)) {
0438                 return false;
0439             }
0440 
0441             if (!(lastCommand == 'c' || lastCommand == 'C' ||
0442                     lastCommand == 's' || lastCommand == 'S')) {
0443                 contrlx = curx;
0444                 contrly = cury;
0445             }
0446 
0447             if (process) {
0448                 px1 = 2 * curx - contrlx;
0449                 py1 = 2 * cury - contrly;
0450                 px2 = relative ? curx + x2 : x2;
0451                 py2 = relative ? cury + y2 : y2;
0452                 px3 = relative ? curx + tox : tox;
0453                 py3 = relative ? cury + toy : toy;
0454 
0455                 svgCurveToCubic(narrowPrecisionToFloat(px1), narrowPrecisionToFloat(py1), narrowPrecisionToFloat(px2),
0456                                 narrowPrecisionToFloat(py2), narrowPrecisionToFloat(px3), narrowPrecisionToFloat(py3));
0457 
0458                 contrlx = relative ? curx + x2 : x2;
0459                 contrly = relative ? cury + y2 : y2;
0460                 curx = relative ? curx + tox : tox;
0461                 cury = relative ? cury + toy : toy;
0462             } else
0463                 svgCurveToCubicSmooth(narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2),
0464                                       narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
0465             break;
0466         }
0467         case 'q':
0468             relative = true;
0469         case 'Q': {
0470             if (!parseNumber(ptr, end, x1)  || !parseNumber(ptr, end, y1) ||
0471                     !parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy)) {
0472                 return false;
0473             }
0474 
0475             if (process) {
0476                 px1 = relative ? (curx + 2 * (x1 + curx)) * (1.0 / 3.0) : (curx + 2 * x1) * (1.0 / 3.0);
0477                 py1 = relative ? (cury + 2 * (y1 + cury)) * (1.0 / 3.0) : (cury + 2 * y1) * (1.0 / 3.0);
0478                 px2 = relative ? ((curx + tox) + 2 * (x1 + curx)) * (1.0 / 3.0) : (tox + 2 * x1) * (1.0 / 3.0);
0479                 py2 = relative ? ((cury + toy) + 2 * (y1 + cury)) * (1.0 / 3.0) : (toy + 2 * y1) * (1.0 / 3.0);
0480                 px3 = relative ? curx + tox : tox;
0481                 py3 = relative ? cury + toy : toy;
0482 
0483                 svgCurveToCubic(narrowPrecisionToFloat(px1), narrowPrecisionToFloat(py1), narrowPrecisionToFloat(px2),
0484                                 narrowPrecisionToFloat(py2), narrowPrecisionToFloat(px3), narrowPrecisionToFloat(py3));
0485 
0486                 contrlx = relative ? curx + x1 : x1;
0487                 contrly = relative ? cury + y1 : y1;
0488                 curx = relative ? curx + tox : tox;
0489                 cury = relative ? cury + toy : toy;
0490             } else
0491                 svgCurveToQuadratic(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1),
0492                                     narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
0493             break;
0494         }
0495         case 't':
0496             relative = true;
0497         case 'T': {
0498             if (!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy)) {
0499                 return false;
0500             }
0501             if (!(lastCommand == 'q' || lastCommand == 'Q' ||
0502                     lastCommand == 't' || lastCommand == 'T')) {
0503                 contrlx = curx;
0504                 contrly = cury;
0505             }
0506 
0507             if (process) {
0508                 xc = 2 * curx - contrlx;
0509                 yc = 2 * cury - contrly;
0510 
0511                 px1 = relative ? (curx + 2 * xc) * (1.0 / 3.0) : (curx + 2 * xc) * (1.0 / 3.0);
0512                 py1 = relative ? (cury + 2 * yc) * (1.0 / 3.0) : (cury + 2 * yc) * (1.0 / 3.0);
0513                 px2 = relative ? ((curx + tox) + 2 * xc) * (1.0 / 3.0) : (tox + 2 * xc) * (1.0 / 3.0);
0514                 py2 = relative ? ((cury + toy) + 2 * yc) * (1.0 / 3.0) : (toy + 2 * yc) * (1.0 / 3.0);
0515                 px3 = relative ? curx + tox : tox;
0516                 py3 = relative ? cury + toy : toy;
0517 
0518                 svgCurveToCubic(narrowPrecisionToFloat(px1), narrowPrecisionToFloat(py1), narrowPrecisionToFloat(px2),
0519                                 narrowPrecisionToFloat(py2), narrowPrecisionToFloat(px3), narrowPrecisionToFloat(py3));
0520 
0521                 contrlx = xc;
0522                 contrly = yc;
0523                 curx = relative ? curx + tox : tox;
0524                 cury = relative ? cury + toy : toy;
0525             } else {
0526                 svgCurveToQuadraticSmooth(narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
0527             }
0528             break;
0529         }
0530         case 'a':
0531             relative = true;
0532         case 'A': {
0533             bool largeArc, sweep;
0534             double angle, rx, ry;
0535             if (!parseNumber(ptr, end, rx)    || !parseNumber(ptr, end, ry) ||
0536                     !parseNumber(ptr, end, angle) || !parseNumber(ptr, end, tox)) {
0537                 return false;
0538             }
0539             largeArc = tox == 1;
0540             if (!parseNumber(ptr, end, tox)) {
0541                 return false;
0542             }
0543             sweep = tox == 1;
0544             if (!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy)) {
0545                 return false;
0546             }
0547 
0548             // Spec: radii are nonnegative numbers
0549             rx = fabs(rx);
0550             ry = fabs(ry);
0551 
0552             if (process) {
0553                 calculateArc(relative, curx, cury, angle, tox, toy, rx, ry, largeArc, sweep);
0554             } else
0555                 svgArcTo(narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), narrowPrecisionToFloat(rx), narrowPrecisionToFloat(ry),
0556                          narrowPrecisionToFloat(angle), largeArc, sweep, !relative);
0557             break;
0558         }
0559         default:
0560             // FIXME: An error should go to the JavaScript console, or the like.
0561             return false;
0562         }
0563         lastCommand = command;
0564 
0565         if (ptr >= end) {
0566             return true;
0567         }
0568 
0569         // Check for remaining coordinates in the current command.
0570         if ((*ptr == '+' || *ptr == '-' || (*ptr >= '0' && *ptr <= '9')) &&
0571                 (command != 'z' && command != 'Z')) {
0572             if (command == 'M') {
0573                 command = 'L';
0574             } else if (command == 'm') {
0575                 command = 'l';
0576             }
0577         } else {
0578             command = (ptr++)->unicode();    // or toLatin1() instead of unicode()???
0579         }
0580 
0581         if (lastCommand != 'C' && lastCommand != 'c' &&
0582                 lastCommand != 'S' && lastCommand != 's' &&
0583                 lastCommand != 'Q' && lastCommand != 'q' &&
0584                 lastCommand != 'T' && lastCommand != 't') {
0585             contrlx = curx;
0586             contrly = cury;
0587         }
0588     }
0589 
0590     return false;
0591 }
0592 
0593 // This works by converting the SVG arc to "simple" beziers.
0594 // For each bezier found a svgToCurve call is done.
0595 // Adapted from Niko's code in kdelibs/kdecore/svgicons.
0596 // Maybe this can serve in some shared lib? (Rob)
0597 void SVGPathParser::calculateArc(bool relative, double &curx, double &cury, double angle, double x, double y, double r1, double r2, bool largeArcFlag, bool sweepFlag)
0598 {
0599     double sin_th, cos_th;
0600     double a00, a01, a10, a11;
0601     double x0, y0, x1, y1, xc, yc;
0602     double d, sfactor, sfactor_sq;
0603     double th0, th1, th_arc;
0604     int i, n_segs;
0605 
0606     sin_th = sin(angle * (piDouble / 180.0));
0607     cos_th = cos(angle * (piDouble / 180.0));
0608 
0609     double dx;
0610 
0611     if (!relative) {
0612         dx = (curx - x) / 2.0;
0613     } else {
0614         dx = -x / 2.0;
0615     }
0616 
0617     double dy;
0618 
0619     if (!relative) {
0620         dy = (cury - y) / 2.0;
0621     } else {
0622         dy = -y / 2.0;
0623     }
0624 
0625     double _x1 =  cos_th * dx + sin_th * dy;
0626     double _y1 = -sin_th * dx + cos_th * dy;
0627     double Pr1 = r1 * r1;
0628     double Pr2 = r2 * r2;
0629     double Px = _x1 * _x1;
0630     double Py = _y1 * _y1;
0631 
0632     // Spec : check if radii are large enough
0633     double check = Px / Pr1 + Py / Pr2;
0634     if (check > 1) {
0635         r1 = r1 * sqrt(check);
0636         r2 = r2 * sqrt(check);
0637     }
0638 
0639     a00 = cos_th / r1;
0640     a01 = sin_th / r1;
0641     a10 = -sin_th / r2;
0642     a11 = cos_th / r2;
0643 
0644     x0 = a00 * curx + a01 * cury;
0645     y0 = a10 * curx + a11 * cury;
0646 
0647     if (!relative) {
0648         x1 = a00 * x + a01 * y;
0649     } else {
0650         x1 = a00 * (curx + x) + a01 * (cury + y);
0651     }
0652 
0653     if (!relative) {
0654         y1 = a10 * x + a11 * y;
0655     } else {
0656         y1 = a10 * (curx + x) + a11 * (cury + y);
0657     }
0658 
0659     /* (x0, y0) is current point in transformed coordinate space.
0660        (x1, y1) is new point in transformed coordinate space.
0661 
0662        The arc fits a unit-radius circle in this space.
0663     */
0664 
0665     d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
0666 
0667     sfactor_sq = 1.0 / d - 0.25;
0668 
0669     if (sfactor_sq < 0) {
0670         sfactor_sq = 0;
0671     }
0672 
0673     sfactor = sqrt(sfactor_sq);
0674 
0675     if (sweepFlag == largeArcFlag) {
0676         sfactor = -sfactor;
0677     }
0678 
0679     xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
0680     yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
0681 
0682     /* (xc, yc) is center of the circle. */
0683     th0 = atan2(y0 - yc, x0 - xc);
0684     th1 = atan2(y1 - yc, x1 - xc);
0685 
0686     th_arc = th1 - th0;
0687     if (th_arc < 0 && sweepFlag) {
0688         th_arc += 2 * piDouble;
0689     } else if (th_arc > 0 && !sweepFlag) {
0690         th_arc -= 2 * piDouble;
0691     }
0692 
0693     n_segs = (int)(int) ceil(fabs(th_arc / (piDouble * 0.5 + 0.001)));
0694 
0695     for (i = 0; i < n_segs; i++) {
0696         double sin_th, cos_th;
0697         double a00, a01, a10, a11;
0698         double x1, y1, x2, y2, x3, y3;
0699         double t;
0700         double th_half;
0701 
0702         double _th0 = th0 + i * th_arc / n_segs;
0703         double _th1 = th0 + (i + 1) * th_arc / n_segs;
0704 
0705         sin_th = sin(angle * (piDouble / 180.0));
0706         cos_th = cos(angle * (piDouble / 180.0));
0707 
0708         /* inverse transform compared with rsvg_path_arc */
0709         a00 = cos_th * r1;
0710         a01 = -sin_th * r2;
0711         a10 = sin_th * r1;
0712         a11 = cos_th * r2;
0713 
0714         th_half = 0.5 * (_th1 - _th0);
0715         t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half);
0716         x1 = xc + cos(_th0) - t * sin(_th0);
0717         y1 = yc + sin(_th0) + t * cos(_th0);
0718         x3 = xc + cos(_th1);
0719         y3 = yc + sin(_th1);
0720         x2 = x3 + t * sin(_th1);
0721         y2 = y3 - t * cos(_th1);
0722 
0723         svgCurveToCubic(narrowPrecisionToFloat(a00 * x1 + a01 * y1), narrowPrecisionToFloat(a10 * x1 + a11 * y1),
0724                         narrowPrecisionToFloat(a00 * x2 + a01 * y2), narrowPrecisionToFloat(a10 * x2 + a11 * y2),
0725                         narrowPrecisionToFloat(a00 * x3 + a01 * y3), narrowPrecisionToFloat(a10 * x3 + a11 * y3));
0726     }
0727 
0728     if (!relative) {
0729         curx = x;
0730     } else {
0731         curx += x;
0732     }
0733 
0734     if (!relative) {
0735         cury = y;
0736     } else {
0737         cury += y;
0738     }
0739 }
0740 
0741 class PathBuilder : public SVGPathParser
0742 {
0743 public:
0744     bool build(Path *path, const String &d)
0745     {
0746         m_path = path;
0747         return parseSVG(d, true);
0748     }
0749 
0750 private:
0751     void svgMoveTo(double x1, double y1, bool closed, bool abs = true) override
0752     {
0753         current.setX(narrowPrecisionToFloat(abs ? x1 : current.x() + x1));
0754         current.setY(narrowPrecisionToFloat(abs ? y1 : current.y() + y1));
0755         if (closed) {
0756             m_path->closeSubpath();
0757         }
0758         m_path->moveTo(current);
0759     }
0760     void svgLineTo(double x1, double y1, bool abs = true) override
0761     {
0762         current.setX(narrowPrecisionToFloat(abs ? x1 : current.x() + x1));
0763         current.setY(narrowPrecisionToFloat(abs ? y1 : current.y() + y1));
0764         m_path->addLineTo(current);
0765     }
0766     void svgCurveToCubic(double x1, double y1, double x2, double y2, double x, double y, bool abs = true) override
0767     {
0768         if (!abs) {
0769             x1 += current.x();
0770             y1 += current.y();
0771             x2 += current.x();
0772             y2 += current.y();
0773         }
0774         current.setX(narrowPrecisionToFloat(abs ? x : current.x() + x));
0775         current.setY(narrowPrecisionToFloat(abs ? y : current.y() + y));
0776         m_path->addBezierCurveTo(FloatPoint::narrowPrecision(x1, y1), FloatPoint::narrowPrecision(x2, y2), current);
0777     }
0778     void svgClosePath() override
0779     {
0780         m_path->closeSubpath();
0781     }
0782     Path *m_path;
0783     FloatPoint current;
0784 };
0785 
0786 bool pathFromSVGData(Path &path, const String &d)
0787 {
0788     PathBuilder builder;
0789     return builder.build(&path, d);
0790 }
0791 
0792 class SVGPathSegListBuilder : public SVGPathParser
0793 {
0794 public:
0795     bool build(SVGPathSegList *segList, const String &d, bool process)
0796     {
0797         m_pathSegList = segList;
0798         return parseSVG(d, process);
0799     }
0800 
0801 private:
0802     void svgMoveTo(double x1, double y1, bool, bool abs = true) override
0803     {
0804         ExceptionCode ec = 0;
0805 
0806         if (abs) {
0807             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegMovetoAbs(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1)), ec);
0808         } else {
0809             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegMovetoRel(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1)), ec);
0810         }
0811     }
0812     void svgLineTo(double x1, double y1, bool abs = true) override
0813     {
0814         ExceptionCode ec = 0;
0815 
0816         if (abs) {
0817             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegLinetoAbs(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1)), ec);
0818         } else {
0819             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegLinetoRel(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1)), ec);
0820         }
0821     }
0822     void svgLineToHorizontal(double x, bool abs) override
0823     {
0824         ExceptionCode ec = 0;
0825 
0826         if (abs) {
0827             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegLinetoHorizontalAbs(narrowPrecisionToFloat(x)), ec);
0828         } else {
0829             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegLinetoHorizontalRel(narrowPrecisionToFloat(x)), ec);
0830         }
0831     }
0832     void svgLineToVertical(double y, bool abs) override
0833     {
0834         ExceptionCode ec = 0;
0835 
0836         if (abs) {
0837             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegLinetoVerticalAbs(narrowPrecisionToFloat(y)), ec);
0838         } else {
0839             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegLinetoVerticalRel(narrowPrecisionToFloat(y)), ec);
0840         }
0841     }
0842     void svgCurveToCubic(double x1, double y1, double x2, double y2, double x, double y, bool abs = true) override
0843     {
0844         ExceptionCode ec = 0;
0845 
0846         if (abs)
0847             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegCurvetoCubicAbs(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y),
0848                                       narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1),
0849                                       narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2)), ec);
0850         else
0851             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegCurvetoCubicRel(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y),
0852                                       narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1),
0853                                       narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2)), ec);
0854     }
0855     void svgCurveToCubicSmooth(double x, double y, double x2, double y2, bool abs) override
0856     {
0857         ExceptionCode ec = 0;
0858 
0859         if (abs)
0860             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegCurvetoCubicSmoothAbs(narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2),
0861                                       narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)), ec);
0862         else
0863             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegCurvetoCubicSmoothRel(narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2),
0864                                       narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)), ec);
0865     }
0866     void svgCurveToQuadratic(double x, double y, double x1, double y1, bool abs) override
0867     {
0868         ExceptionCode ec = 0;
0869 
0870         if (abs)
0871             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegCurvetoQuadraticAbs(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1),
0872                                       narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)), ec);
0873         else
0874             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegCurvetoQuadraticRel(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1),
0875                                       narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)), ec);
0876     }
0877     void svgCurveToQuadraticSmooth(double x, double y, bool abs) override
0878     {
0879         ExceptionCode ec = 0;
0880 
0881         if (abs) {
0882             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegCurvetoQuadraticSmoothAbs(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)), ec);
0883         } else {
0884             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegCurvetoQuadraticSmoothRel(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)), ec);
0885         }
0886     }
0887     void svgArcTo(double x, double y, double r1, double r2, double angle, bool largeArcFlag, bool sweepFlag, bool abs) override
0888     {
0889         ExceptionCode ec = 0;
0890 
0891         if (abs)
0892             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegArcAbs(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y),
0893                                       narrowPrecisionToFloat(r1), narrowPrecisionToFloat(r2),
0894                                       narrowPrecisionToFloat(angle), largeArcFlag, sweepFlag), ec);
0895         else
0896             m_pathSegList->appendItem(SVGPathElement::createSVGPathSegArcRel(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y),
0897                                       narrowPrecisionToFloat(r1), narrowPrecisionToFloat(r2),
0898                                       narrowPrecisionToFloat(angle), largeArcFlag, sweepFlag), ec);
0899     }
0900     void svgClosePath() override
0901     {
0902         ExceptionCode ec = 0;
0903         m_pathSegList->appendItem(SVGPathElement::createSVGPathSegClosePath(), ec);
0904     }
0905     SVGPathSegList *m_pathSegList;
0906 };
0907 
0908 bool pathSegListFromSVGData(SVGPathSegList *path, const String &d, bool process)
0909 {
0910     SVGPathSegListBuilder builder;
0911     return builder.build(path, d, process);
0912 }
0913 
0914 Vector<String> parseDelimitedString(const String &input, const char separator)
0915 {
0916     Vector<String> values;
0917 
0918     const UChar *ptr = input.characters();
0919     const UChar *end = ptr + input.length();
0920     skipOptionalSpaces(ptr, end);
0921 
0922     while (ptr < end) {
0923         // Leading and trailing white space, and white space before and after semicolon separators, will be ignored.
0924         const UChar *inputStart = ptr;
0925         while (ptr < end && *ptr != separator) { // careful not to ignore whitespace inside inputs
0926             ptr++;
0927         }
0928 
0929         if (ptr == inputStart) {
0930             break;
0931         }
0932 
0933         // walk backwards from the ; to ignore any whitespace
0934         const UChar *inputEnd = ptr - 1;
0935         while (inputStart < inputEnd && isWhitespace(*inputEnd)) {
0936             inputEnd--;
0937         }
0938 
0939         values.append(String(inputStart, inputEnd - inputStart + 1));
0940         skipOptionalSpacesOrDelimiter(ptr, end, separator);
0941     }
0942 
0943     return values;
0944 }
0945 
0946 }
0947 
0948 #endif // ENABLE(SVG)