Home | History | Annotate | Download | only in svg
      1 /*
      2    Copyright (C) 2002, 2003 The Karbon Developers
      3                  2006       Alexander Kellett <lypanov (at) kde.org>
      4                  2006, 2007 Rob Buis <buis (at) kde.org>
      5    Copyrigth (C) 2007, 2009 Apple, Inc.  All rights reserved.
      6 
      7    This library is free software; you can redistribute it and/or
      8    modify it under the terms of the GNU Library General Public
      9    License as published by the Free Software Foundation; either
     10    version 2 of the License, or (at your option) any later version.
     11 
     12    This library is distributed in the hope that it will be useful,
     13    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15    Library General Public License for more details.
     16 
     17    You should have received a copy of the GNU Library General Public License
     18    along with this library; see the file COPYING.LIB.  If not, write to
     19    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20    Boston, MA 02110-1301, USA.
     21 */
     22 
     23 #include "config.h"
     24 #if ENABLE(SVG)
     25 #include "SVGParserUtilities.h"
     26 
     27 #include "ExceptionCode.h"
     28 #include "FloatConversion.h"
     29 #include "FloatPoint.h"
     30 #include "Path.h"
     31 #include "PlatformString.h"
     32 #include "SVGPathSegList.h"
     33 #include "SVGPathSegArc.h"
     34 #include "SVGPathSegClosePath.h"
     35 #include "SVGPathSegCurvetoCubic.h"
     36 #include "SVGPathSegCurvetoCubicSmooth.h"
     37 #include "SVGPathSegCurvetoQuadratic.h"
     38 #include "SVGPathSegCurvetoQuadraticSmooth.h"
     39 #include "SVGPathSegLineto.h"
     40 #include "SVGPathSegLinetoHorizontal.h"
     41 #include "SVGPathSegLinetoVertical.h"
     42 #include "SVGPathSegList.h"
     43 #include "SVGPathSegMoveto.h"
     44 #include "SVGPointList.h"
     45 #include "SVGPathElement.h"
     46 #include <math.h>
     47 #include <wtf/MathExtras.h>
     48 
     49 namespace WebCore {
     50 
     51 /* We use this generic _parseNumber function to allow the Path parsing code to work
     52  * at a higher precision internally, without any unnecessary runtime cost or code
     53  * complexity
     54  */
     55 template <typename FloatType> static bool _parseNumber(const UChar*& ptr, const UChar* end, FloatType& number, bool skip)
     56 {
     57     int integer, exponent;
     58     FloatType decimal, frac;
     59     int sign, expsign;
     60     const UChar* start = ptr;
     61 
     62     exponent = 0;
     63     integer = 0;
     64     frac = 1;
     65     decimal = 0;
     66     sign = 1;
     67     expsign = 1;
     68 
     69     // read the sign
     70     if (ptr < end && *ptr == '+')
     71         ptr++;
     72     else if (ptr < end && *ptr == '-') {
     73         ptr++;
     74         sign = -1;
     75     }
     76 
     77     if (ptr == end || ((*ptr < '0' || *ptr > '9') && *ptr != '.'))
     78         // The first character of a number must be one of [0-9+-.]
     79         return false;
     80 
     81     // read the integer part
     82     while (ptr < end && *ptr >= '0' && *ptr <= '9')
     83         integer = (integer * 10) + *(ptr++) - '0';
     84 
     85     if (ptr < end && *ptr == '.') { // read the decimals
     86         ptr++;
     87 
     88         // There must be a least one digit following the .
     89         if (ptr >= end || *ptr < '0' || *ptr > '9')
     90             return false;
     91 
     92         while (ptr < end && *ptr >= '0' && *ptr <= '9')
     93             decimal += (*(ptr++) - '0') * (frac *= static_cast<FloatType>(0.1));
     94     }
     95 
     96     // read the exponent part
     97     if (ptr != start && ptr + 1 < end && (*ptr == 'e' || *ptr == 'E')
     98         && (ptr[1] != 'x' && ptr[1] != 'm')) {
     99         ptr++;
    100 
    101         // read the sign of the exponent
    102         if (*ptr == '+')
    103             ptr++;
    104         else if (*ptr == '-') {
    105             ptr++;
    106             expsign = -1;
    107         }
    108 
    109         // There must be an exponent
    110         if (ptr >= end || *ptr < '0' || *ptr > '9')
    111             return false;
    112 
    113         while (ptr < end && *ptr >= '0' && *ptr <= '9') {
    114             exponent *= 10;
    115             exponent += *ptr - '0';
    116             ptr++;
    117         }
    118     }
    119 
    120     number = integer + decimal;
    121     number *= sign * static_cast<FloatType>(pow(10.0, expsign * exponent));
    122 
    123     if (start == ptr)
    124         return false;
    125 
    126     if (skip)
    127         skipOptionalSpacesOrDelimiter(ptr, end);
    128 
    129     return true;
    130 }
    131 
    132 bool parseNumber(const UChar*& ptr, const UChar* end, float& number, bool skip)
    133 {
    134     return _parseNumber(ptr, end, number, skip);
    135 }
    136 
    137 // Only used for parsing Paths
    138 static bool parseNumber(const UChar*& ptr, const UChar* end, double& number, bool skip = true)
    139 {
    140     return _parseNumber(ptr, end, number, skip);
    141 }
    142 
    143 bool parseNumberOptionalNumber(const String& s, float& x, float& y)
    144 {
    145     if (s.isEmpty())
    146         return false;
    147     const UChar* cur = s.characters();
    148     const UChar* end = cur + s.length();
    149 
    150     if (!parseNumber(cur, end, x))
    151         return false;
    152 
    153     if (cur == end)
    154         y = x;
    155     else if (!parseNumber(cur, end, y, false))
    156         return false;
    157 
    158     return cur == end;
    159 }
    160 
    161 bool pointsListFromSVGData(SVGPointList* pointsList, const String& points)
    162 {
    163     if (points.isEmpty())
    164         return true;
    165     const UChar* cur = points.characters();
    166     const UChar* end = cur + points.length();
    167 
    168     skipOptionalSpaces(cur, end);
    169 
    170     bool delimParsed = false;
    171     while (cur < end) {
    172         delimParsed = false;
    173         float xPos = 0.0f;
    174         if (!parseNumber(cur, end, xPos))
    175            return false;
    176 
    177         float yPos = 0.0f;
    178         if (!parseNumber(cur, end, yPos, false))
    179             return false;
    180 
    181         skipOptionalSpaces(cur, end);
    182 
    183         if (cur < end && *cur == ',') {
    184             delimParsed = true;
    185             cur++;
    186         }
    187         skipOptionalSpaces(cur, end);
    188 
    189         ExceptionCode ec = 0;
    190         pointsList->appendItem(FloatPoint(xPos, yPos), ec);
    191     }
    192     return cur == end && !delimParsed;
    193 }
    194 
    195     /**
    196      * Parser for svg path data, contained in the d attribute.
    197      *
    198      * The parser delivers encountered commands and parameters by calling
    199      * methods that correspond to those commands. Clients have to derive
    200      * from this class and implement the abstract command methods.
    201      *
    202      * There are two operating modes. By default the parser just delivers unaltered
    203      * svg path data commands and parameters. In the second mode, it will convert all
    204      * relative coordinates to absolute ones, and convert all curves to cubic beziers.
    205      */
    206     class SVGPathParser {
    207     public:
    208         virtual ~SVGPathParser() { }
    209         bool parseSVG(const String& d, bool process = false);
    210 
    211     protected:
    212         virtual void svgMoveTo(double x1, double y1, bool closed, bool abs = true) = 0;
    213         virtual void svgLineTo(double x1, double y1, bool abs = true) = 0;
    214         virtual void svgLineToHorizontal(double, bool /*abs*/ = true) { }
    215         virtual void svgLineToVertical(double /*y*/, bool /*abs*/ = true) { }
    216         virtual void svgCurveToCubic(double x1, double y1, double x2, double y2, double x, double y, bool abs = true) = 0;
    217         virtual void svgCurveToCubicSmooth(double /*x*/, double /*y*/, double /*x2*/, double /*y2*/, bool /*abs*/ = true) { }
    218         virtual void svgCurveToQuadratic(double /*x*/, double /*y*/, double /*x1*/, double /*y1*/, bool /*abs*/ = true) { }
    219         virtual void svgCurveToQuadraticSmooth(double /*x*/, double /*y*/, bool /*abs*/ = true) { }
    220         virtual void svgArcTo(double /*x*/, double /*y*/, double /*r1*/, double /*r2*/, double /*angle*/, bool /*largeArcFlag*/, bool /*sweepFlag*/, bool /*abs*/ = true) { }
    221         virtual void svgClosePath() = 0;
    222 
    223     private:
    224         void calculateArc(bool relative, double& curx, double& cury, double angle, double x, double y, double r1, double r2, bool largeArcFlag, bool sweepFlag);
    225     };
    226 
    227 bool SVGPathParser::parseSVG(const String& s, bool process)
    228 {
    229     const UChar* ptr = s.characters();
    230     const UChar* end = ptr + s.length();
    231 
    232     double contrlx, contrly, curx, cury, subpathx, subpathy, tox, toy, x1, y1, x2, y2, xc, yc;
    233     double px1, py1, px2, py2, px3, py3;
    234     bool closed = true;
    235 
    236     if (!skipOptionalSpaces(ptr, end)) // skip any leading spaces
    237         return false;
    238 
    239     char command = *(ptr++), lastCommand = ' ';
    240     if (command != 'm' && command != 'M') // path must start with moveto
    241         return false;
    242 
    243     subpathx = subpathy = curx = cury = contrlx = contrly = 0.0;
    244     while (1) {
    245         skipOptionalSpaces(ptr, end); // skip spaces between command and first coord
    246 
    247         bool relative = false;
    248 
    249         switch (command)
    250         {
    251             case 'm':
    252                 relative = true;
    253             case 'M':
    254             {
    255                 if (!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
    256                     return false;
    257 
    258                 if (process) {
    259                     subpathx = curx = relative ? curx + tox : tox;
    260                     subpathy = cury = relative ? cury + toy : toy;
    261 
    262                     svgMoveTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury), closed);
    263                 } else
    264                     svgMoveTo(narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), closed, !relative);
    265                 closed = false;
    266                 break;
    267             }
    268             case 'l':
    269                 relative = true;
    270             case 'L':
    271             {
    272                 if (!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
    273                     return false;
    274 
    275                 if (process) {
    276                     curx = relative ? curx + tox : tox;
    277                     cury = relative ? cury + toy : toy;
    278 
    279                     svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
    280                 }
    281                 else
    282                     svgLineTo(narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
    283                 break;
    284             }
    285             case 'h':
    286             {
    287                 if (!parseNumber(ptr, end, tox))
    288                     return false;
    289                 if (process) {
    290                     curx = curx + tox;
    291                     svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
    292                 }
    293                 else
    294                     svgLineToHorizontal(narrowPrecisionToFloat(tox), false);
    295                 break;
    296             }
    297             case 'H':
    298             {
    299                 if (!parseNumber(ptr, end, tox))
    300                     return false;
    301                 if (process) {
    302                     curx = tox;
    303                     svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
    304                 }
    305                 else
    306                     svgLineToHorizontal(narrowPrecisionToFloat(tox));
    307                 break;
    308             }
    309             case 'v':
    310             {
    311                 if (!parseNumber(ptr, end, toy))
    312                     return false;
    313                 if (process) {
    314                     cury = cury + toy;
    315                     svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
    316                 }
    317                 else
    318                     svgLineToVertical(narrowPrecisionToFloat(toy), false);
    319                 break;
    320             }
    321             case 'V':
    322             {
    323                 if (!parseNumber(ptr, end, toy))
    324                     return false;
    325                 if (process) {
    326                     cury = toy;
    327                     svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
    328                 }
    329                 else
    330                     svgLineToVertical(narrowPrecisionToFloat(toy));
    331                 break;
    332             }
    333             case 'z':
    334             case 'Z':
    335             {
    336                 // reset curx, cury for next path
    337                 if (process) {
    338                     curx = subpathx;
    339                     cury = subpathy;
    340                 }
    341                 closed = true;
    342                 svgClosePath();
    343                 break;
    344             }
    345             case 'c':
    346                 relative = true;
    347             case 'C':
    348             {
    349                 if (!parseNumber(ptr, end, x1)  || !parseNumber(ptr, end, y1) ||
    350                     !parseNumber(ptr, end, x2)  || !parseNumber(ptr, end, y2) ||
    351                     !parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
    352                     return false;
    353 
    354                 if (process) {
    355                     px1 = relative ? curx + x1 : x1;
    356                     py1 = relative ? cury + y1 : y1;
    357                     px2 = relative ? curx + x2 : x2;
    358                     py2 = relative ? cury + y2 : y2;
    359                     px3 = relative ? curx + tox : tox;
    360                     py3 = relative ? cury + toy : toy;
    361 
    362                     svgCurveToCubic(narrowPrecisionToFloat(px1), narrowPrecisionToFloat(py1), narrowPrecisionToFloat(px2),
    363                                     narrowPrecisionToFloat(py2), narrowPrecisionToFloat(px3), narrowPrecisionToFloat(py3));
    364 
    365                     contrlx = relative ? curx + x2 : x2;
    366                     contrly = relative ? cury + y2 : y2;
    367                     curx = relative ? curx + tox : tox;
    368                     cury = relative ? cury + toy : toy;
    369                 }
    370                 else
    371                     svgCurveToCubic(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1), narrowPrecisionToFloat(x2),
    372                                     narrowPrecisionToFloat(y2), narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
    373 
    374                 break;
    375             }
    376             case 's':
    377                 relative = true;
    378             case 'S':
    379             {
    380                 if (!parseNumber(ptr, end, x2)  || !parseNumber(ptr, end, y2) ||
    381                     !parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
    382                     return false;
    383 
    384                 if (!(lastCommand == 'c' || lastCommand == 'C' ||
    385                      lastCommand == 's' || lastCommand == 'S')) {
    386                     contrlx = curx;
    387                     contrly = cury;
    388                 }
    389 
    390                 if (process) {
    391                     px1 = 2 * curx - contrlx;
    392                     py1 = 2 * cury - contrly;
    393                     px2 = relative ? curx + x2 : x2;
    394                     py2 = relative ? cury + y2 : y2;
    395                     px3 = relative ? curx + tox : tox;
    396                     py3 = relative ? cury + toy : toy;
    397 
    398                     svgCurveToCubic(narrowPrecisionToFloat(px1), narrowPrecisionToFloat(py1), narrowPrecisionToFloat(px2),
    399                                     narrowPrecisionToFloat(py2), narrowPrecisionToFloat(px3), narrowPrecisionToFloat(py3));
    400 
    401                     contrlx = relative ? curx + x2 : x2;
    402                     contrly = relative ? cury + y2 : y2;
    403                     curx = relative ? curx + tox : tox;
    404                     cury = relative ? cury + toy : toy;
    405                 }
    406                 else
    407                     svgCurveToCubicSmooth(narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2),
    408                                           narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
    409                 break;
    410             }
    411             case 'q':
    412                 relative = true;
    413             case 'Q':
    414             {
    415                 if (!parseNumber(ptr, end, x1)  || !parseNumber(ptr, end, y1) ||
    416                     !parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
    417                     return false;
    418 
    419                 if (process) {
    420                     px1 = relative ? (curx + 2 * (x1 + curx)) * (1.0 / 3.0) : (curx + 2 * x1) * (1.0 / 3.0);
    421                     py1 = relative ? (cury + 2 * (y1 + cury)) * (1.0 / 3.0) : (cury + 2 * y1) * (1.0 / 3.0);
    422                     px2 = relative ? ((curx + tox) + 2 * (x1 + curx)) * (1.0 / 3.0) : (tox + 2 * x1) * (1.0 / 3.0);
    423                     py2 = relative ? ((cury + toy) + 2 * (y1 + cury)) * (1.0 / 3.0) : (toy + 2 * y1) * (1.0 / 3.0);
    424                     px3 = relative ? curx + tox : tox;
    425                     py3 = relative ? cury + toy : toy;
    426 
    427                     svgCurveToCubic(narrowPrecisionToFloat(px1), narrowPrecisionToFloat(py1), narrowPrecisionToFloat(px2),
    428                                     narrowPrecisionToFloat(py2), narrowPrecisionToFloat(px3), narrowPrecisionToFloat(py3));
    429 
    430                     contrlx = relative ? curx + x1 : x1;
    431                     contrly = relative ? cury + y1 : y1;
    432                     curx = relative ? curx + tox : tox;
    433                     cury = relative ? cury + toy : toy;
    434                 }
    435                 else
    436                     svgCurveToQuadratic(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1),
    437                                         narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
    438                 break;
    439             }
    440             case 't':
    441                 relative = true;
    442             case 'T':
    443             {
    444                 if (!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
    445                     return false;
    446                 if (!(lastCommand == 'q' || lastCommand == 'Q' ||
    447                      lastCommand == 't' || lastCommand == 'T')) {
    448                     contrlx = curx;
    449                     contrly = cury;
    450                 }
    451 
    452                 if (process) {
    453                     xc = 2 * curx - contrlx;
    454                     yc = 2 * cury - contrly;
    455 
    456                     px1 = relative ? (curx + 2 * xc) * (1.0 / 3.0) : (curx + 2 * xc) * (1.0 / 3.0);
    457                     py1 = relative ? (cury + 2 * yc) * (1.0 / 3.0) : (cury + 2 * yc) * (1.0 / 3.0);
    458                     px2 = relative ? ((curx + tox) + 2 * xc) * (1.0 / 3.0) : (tox + 2 * xc) * (1.0 / 3.0);
    459                     py2 = relative ? ((cury + toy) + 2 * yc) * (1.0 / 3.0) : (toy + 2 * yc) * (1.0 / 3.0);
    460                     px3 = relative ? curx + tox : tox;
    461                     py3 = relative ? cury + toy : toy;
    462 
    463                     svgCurveToCubic(narrowPrecisionToFloat(px1), narrowPrecisionToFloat(py1), narrowPrecisionToFloat(px2),
    464                                     narrowPrecisionToFloat(py2), narrowPrecisionToFloat(px3), narrowPrecisionToFloat(py3));
    465 
    466                     contrlx = xc;
    467                     contrly = yc;
    468                     curx = relative ? curx + tox : tox;
    469                     cury = relative ? cury + toy : toy;
    470                 }
    471                 else
    472                     svgCurveToQuadraticSmooth(narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
    473                 break;
    474             }
    475             case 'a':
    476                 relative = true;
    477             case 'A':
    478             {
    479                 bool largeArc, sweep;
    480                 double angle, rx, ry;
    481                 if (!parseNumber(ptr, end, rx)    || !parseNumber(ptr, end, ry) ||
    482                     !parseNumber(ptr, end, angle) || !parseNumber(ptr, end, tox))
    483                     return false;
    484                 largeArc = tox == 1;
    485                 if (!parseNumber(ptr, end, tox))
    486                     return false;
    487                 sweep = tox == 1;
    488                 if (!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
    489                     return false;
    490 
    491                 // Spec: radii are nonnegative numbers
    492                 rx = fabs(rx);
    493                 ry = fabs(ry);
    494 
    495                 if (process)
    496                     calculateArc(relative, curx, cury, angle, tox, toy, rx, ry, largeArc, sweep);
    497                 else
    498                     svgArcTo(narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), narrowPrecisionToFloat(rx), narrowPrecisionToFloat(ry),
    499                              narrowPrecisionToFloat(angle), largeArc, sweep, !relative);
    500                 break;
    501             }
    502             default:
    503                 // FIXME: An error should go to the JavaScript console, or the like.
    504                 return false;
    505         }
    506         lastCommand = command;
    507 
    508         if (ptr >= end)
    509             return true;
    510 
    511         // Check for remaining coordinates in the current command.
    512         if ((*ptr == '+' || *ptr == '-' || *ptr == '.' || (*ptr >= '0' && *ptr <= '9'))
    513             && (command != 'z' && command != 'Z')) {
    514             if (command == 'M')
    515                 command = 'L';
    516             else if (command == 'm')
    517                 command = 'l';
    518         } else
    519             command = *(ptr++);
    520 
    521         if (lastCommand != 'C' && lastCommand != 'c' &&
    522             lastCommand != 'S' && lastCommand != 's' &&
    523             lastCommand != 'Q' && lastCommand != 'q' &&
    524             lastCommand != 'T' && lastCommand != 't') {
    525             contrlx = curx;
    526             contrly = cury;
    527         }
    528     }
    529 
    530     return false;
    531 }
    532 
    533 // This works by converting the SVG arc to "simple" beziers.
    534 // For each bezier found a svgToCurve call is done.
    535 // Adapted from Niko's code in kdelibs/kdecore/svgicons.
    536 // Maybe this can serve in some shared lib? (Rob)
    537 void SVGPathParser::calculateArc(bool relative, double& curx, double& cury, double angle, double x, double y, double r1, double r2, bool largeArcFlag, bool sweepFlag)
    538 {
    539     double sin_th, cos_th;
    540     double a00, a01, a10, a11;
    541     double x0, y0, x1, y1, xc, yc;
    542     double d, sfactor, sfactor_sq;
    543     double th0, th1, th_arc;
    544     int i, n_segs;
    545 
    546     sin_th = sin(angle * (piDouble / 180.0));
    547     cos_th = cos(angle * (piDouble / 180.0));
    548 
    549     double dx;
    550 
    551     if (!relative)
    552         dx = (curx - x) / 2.0;
    553     else
    554         dx = -x / 2.0;
    555 
    556     double dy;
    557 
    558     if (!relative)
    559         dy = (cury - y) / 2.0;
    560     else
    561         dy = -y / 2.0;
    562 
    563     double _x1 =  cos_th * dx + sin_th * dy;
    564     double _y1 = -sin_th * dx + cos_th * dy;
    565     double Pr1 = r1 * r1;
    566     double Pr2 = r2 * r2;
    567     double Px = _x1 * _x1;
    568     double Py = _y1 * _y1;
    569 
    570     // Spec : check if radii are large enough
    571     double check = Px / Pr1 + Py / Pr2;
    572     if (check > 1) {
    573         r1 = r1 * sqrt(check);
    574         r2 = r2 * sqrt(check);
    575     }
    576 
    577     a00 = cos_th / r1;
    578     a01 = sin_th / r1;
    579     a10 = -sin_th / r2;
    580     a11 = cos_th / r2;
    581 
    582     x0 = a00 * curx + a01 * cury;
    583     y0 = a10 * curx + a11 * cury;
    584 
    585     if (!relative)
    586         x1 = a00 * x + a01 * y;
    587     else
    588         x1 = a00 * (curx + x) + a01 * (cury + y);
    589 
    590     if (!relative)
    591         y1 = a10 * x + a11 * y;
    592     else
    593         y1 = a10 * (curx + x) + a11 * (cury + y);
    594 
    595     /* (x0, y0) is current point in transformed coordinate space.
    596        (x1, y1) is new point in transformed coordinate space.
    597 
    598        The arc fits a unit-radius circle in this space.
    599     */
    600 
    601     d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
    602 
    603     sfactor_sq = 1.0 / d - 0.25;
    604 
    605     if (sfactor_sq < 0)
    606         sfactor_sq = 0;
    607 
    608     sfactor = sqrt(sfactor_sq);
    609 
    610     if (sweepFlag == largeArcFlag)
    611         sfactor = -sfactor;
    612 
    613     xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
    614     yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
    615 
    616     /* (xc, yc) is center of the circle. */
    617     th0 = atan2(y0 - yc, x0 - xc);
    618     th1 = atan2(y1 - yc, x1 - xc);
    619 
    620     th_arc = th1 - th0;
    621     if (th_arc < 0 && sweepFlag)
    622         th_arc += 2 * piDouble;
    623     else if (th_arc > 0 && !sweepFlag)
    624         th_arc -= 2 * piDouble;
    625 
    626     n_segs = (int) (int) ceil(fabs(th_arc / (piDouble * 0.5 + 0.001)));
    627 
    628     for (i = 0; i < n_segs; i++) {
    629         double sin_th, cos_th;
    630         double a00, a01, a10, a11;
    631         double x1, y1, x2, y2, x3, y3;
    632         double t;
    633         double th_half;
    634 
    635         double _th0 = th0 + i * th_arc / n_segs;
    636         double _th1 = th0 + (i + 1) * th_arc / n_segs;
    637 
    638         sin_th = sin(angle * (piDouble / 180.0));
    639         cos_th = cos(angle * (piDouble / 180.0));
    640 
    641         /* inverse transform compared with rsvg_path_arc */
    642         a00 = cos_th * r1;
    643         a01 = -sin_th * r2;
    644         a10 = sin_th * r1;
    645         a11 = cos_th * r2;
    646 
    647         th_half = 0.5 * (_th1 - _th0);
    648         t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half);
    649         x1 = xc + cos(_th0) - t * sin(_th0);
    650         y1 = yc + sin(_th0) + t * cos(_th0);
    651         x3 = xc + cos(_th1);
    652         y3 = yc + sin(_th1);
    653         x2 = x3 + t * sin(_th1);
    654         y2 = y3 - t * cos(_th1);
    655 
    656         svgCurveToCubic(narrowPrecisionToFloat(a00 * x1 + a01 * y1), narrowPrecisionToFloat(a10 * x1 + a11 * y1),
    657                         narrowPrecisionToFloat(a00 * x2 + a01 * y2), narrowPrecisionToFloat(a10 * x2 + a11 * y2),
    658                         narrowPrecisionToFloat(a00 * x3 + a01 * y3), narrowPrecisionToFloat(a10 * x3 + a11 * y3));
    659     }
    660 
    661     if (!relative)
    662         curx = x;
    663     else
    664         curx += x;
    665 
    666     if (!relative)
    667         cury = y;
    668     else
    669         cury += y;
    670 }
    671 
    672 class PathBuilder : private SVGPathParser {
    673 public:
    674     bool build(Path* path, const String& d)
    675     {
    676         Path temporaryPath;
    677         m_path = &temporaryPath;
    678         if (!parseSVG(d, true))
    679             return false;
    680         temporaryPath.swap(*path);
    681         return true;
    682     }
    683 
    684 private:
    685     virtual void svgMoveTo(double x1, double y1, bool closed, bool abs = true)
    686     {
    687         current.setX(narrowPrecisionToFloat(abs ? x1 : current.x() + x1));
    688         current.setY(narrowPrecisionToFloat(abs ? y1 : current.y() + y1));
    689         if (closed)
    690             m_path->closeSubpath();
    691         m_path->moveTo(current);
    692     }
    693     virtual void svgLineTo(double x1, double y1, bool abs = true)
    694     {
    695         current.setX(narrowPrecisionToFloat(abs ? x1 : current.x() + x1));
    696         current.setY(narrowPrecisionToFloat(abs ? y1 : current.y() + y1));
    697         m_path->addLineTo(current);
    698     }
    699     virtual void svgCurveToCubic(double x1, double y1, double x2, double y2, double x, double y, bool abs = true)
    700     {
    701         if (!abs) {
    702             x1 += current.x();
    703             y1 += current.y();
    704             x2 += current.x();
    705             y2 += current.y();
    706         }
    707         current.setX(narrowPrecisionToFloat(abs ? x : current.x() + x));
    708         current.setY(narrowPrecisionToFloat(abs ? y : current.y() + y));
    709         m_path->addBezierCurveTo(FloatPoint::narrowPrecision(x1, y1), FloatPoint::narrowPrecision(x2, y2), current);
    710     }
    711     virtual void svgClosePath()
    712     {
    713         m_path->closeSubpath();
    714     }
    715 
    716     Path* m_path;
    717     FloatPoint current;
    718 };
    719 
    720 bool pathFromSVGData(Path& path, const String& d)
    721 {
    722     PathBuilder builder;
    723     return builder.build(&path, d);
    724 }
    725 
    726 class SVGPathSegListBuilder : private SVGPathParser {
    727 public:
    728     bool build(SVGPathSegList* segList, const String& d, bool process)
    729     {
    730         if (!parseSVG(d, process))
    731             return false;
    732         size_t size = m_vector.size();
    733         for (size_t i = 0; i < size; ++i) {
    734             ExceptionCode ec;
    735             segList->appendItem(m_vector[i].release(), ec);
    736         }
    737         m_vector.clear();
    738         return true;
    739     }
    740 
    741 private:
    742     virtual void svgMoveTo(double x1, double y1, bool, bool abs = true)
    743     {
    744         if (abs)
    745             m_vector.append(SVGPathElement::createSVGPathSegMovetoAbs(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1)));
    746         else
    747             m_vector.append(SVGPathElement::createSVGPathSegMovetoRel(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1)));
    748     }
    749     virtual void svgLineTo(double x1, double y1, bool abs = true)
    750     {
    751         if (abs)
    752             m_vector.append(SVGPathElement::createSVGPathSegLinetoAbs(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1)));
    753         else
    754             m_vector.append(SVGPathElement::createSVGPathSegLinetoRel(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1)));
    755     }
    756     virtual void svgLineToHorizontal(double x, bool abs)
    757     {
    758         if (abs)
    759             m_vector.append(SVGPathElement::createSVGPathSegLinetoHorizontalAbs(narrowPrecisionToFloat(x)));
    760         else
    761             m_vector.append(SVGPathElement::createSVGPathSegLinetoHorizontalRel(narrowPrecisionToFloat(x)));
    762     }
    763     virtual void svgLineToVertical(double y, bool abs)
    764     {
    765         if (abs)
    766             m_vector.append(SVGPathElement::createSVGPathSegLinetoVerticalAbs(narrowPrecisionToFloat(y)));
    767         else
    768             m_vector.append(SVGPathElement::createSVGPathSegLinetoVerticalRel(narrowPrecisionToFloat(y)));
    769     }
    770     virtual void svgCurveToCubic(double x1, double y1, double x2, double y2, double x, double y, bool abs = true)
    771     {
    772         if (abs)
    773             m_vector.append(SVGPathElement::createSVGPathSegCurvetoCubicAbs(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y),
    774                                                                                       narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1),
    775                                                                                       narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2)));
    776         else
    777             m_vector.append(SVGPathElement::createSVGPathSegCurvetoCubicRel(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y),
    778                                                                                       narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1),
    779                                                                                       narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2)));
    780     }
    781     virtual void svgCurveToCubicSmooth(double x, double y, double x2, double y2, bool abs)
    782     {
    783         if (abs)
    784             m_vector.append(SVGPathElement::createSVGPathSegCurvetoCubicSmoothAbs(narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2),
    785                                                                                             narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)));
    786         else
    787             m_vector.append(SVGPathElement::createSVGPathSegCurvetoCubicSmoothRel(narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2),
    788                                                                                             narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)));
    789     }
    790     virtual void svgCurveToQuadratic(double x, double y, double x1, double y1, bool abs)
    791     {
    792         if (abs)
    793             m_vector.append(SVGPathElement::createSVGPathSegCurvetoQuadraticAbs(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1),
    794                                                                                           narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)));
    795         else
    796             m_vector.append(SVGPathElement::createSVGPathSegCurvetoQuadraticRel(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1),
    797                                                                                           narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)));
    798     }
    799     virtual void svgCurveToQuadraticSmooth(double x, double y, bool abs)
    800     {
    801         if (abs)
    802             m_vector.append(SVGPathElement::createSVGPathSegCurvetoQuadraticSmoothAbs(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)));
    803         else
    804             m_vector.append(SVGPathElement::createSVGPathSegCurvetoQuadraticSmoothRel(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)));
    805     }
    806     virtual void svgArcTo(double x, double y, double r1, double r2, double angle, bool largeArcFlag, bool sweepFlag, bool abs)
    807     {
    808         if (abs)
    809             m_vector.append(SVGPathElement::createSVGPathSegArcAbs(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y),
    810                                                                              narrowPrecisionToFloat(r1), narrowPrecisionToFloat(r2),
    811                                                                              narrowPrecisionToFloat(angle), largeArcFlag, sweepFlag));
    812         else
    813             m_vector.append(SVGPathElement::createSVGPathSegArcRel(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y),
    814                                                                              narrowPrecisionToFloat(r1), narrowPrecisionToFloat(r2),
    815                                                                              narrowPrecisionToFloat(angle), largeArcFlag, sweepFlag));
    816     }
    817     virtual void svgClosePath()
    818     {
    819         m_vector.append(SVGPathElement::createSVGPathSegClosePath());
    820     }
    821 
    822     Vector<RefPtr<SVGPathSeg> > m_vector;
    823 };
    824 
    825 bool pathSegListFromSVGData(SVGPathSegList* path, const String& d, bool process)
    826 {
    827     SVGPathSegListBuilder builder;
    828     return builder.build(path, d, process);
    829 }
    830 
    831 Vector<String> parseDelimitedString(const String& input, const char seperator)
    832 {
    833     Vector<String> values;
    834 
    835     const UChar* ptr = input.characters();
    836     const UChar* end = ptr + input.length();
    837     skipOptionalSpaces(ptr, end);
    838 
    839     while (ptr < end) {
    840         // Leading and trailing white space, and white space before and after semicolon separators, will be ignored.
    841         const UChar* inputStart = ptr;
    842         while (ptr < end && *ptr != seperator) // careful not to ignore whitespace inside inputs
    843             ptr++;
    844 
    845         if (ptr == inputStart)
    846             break;
    847 
    848         // walk backwards from the ; to ignore any whitespace
    849         const UChar* inputEnd = ptr - 1;
    850         while (inputStart < inputEnd && isWhitespace(*inputEnd))
    851             inputEnd--;
    852 
    853         values.append(String(inputStart, inputEnd - inputStart + 1));
    854         skipOptionalSpacesOrDelimiter(ptr, end, seperator);
    855     }
    856 
    857     return values;
    858 }
    859 
    860 }
    861 
    862 #endif // ENABLE(SVG)
    863