1
/* This file is part of the KDE project
2
Copyright (C) 2002, 2003 The Karbon Developers
3
2006 Alexander Kellett <lypanov@kde.org>
4
2006, 2007 Rob Buis <buis@kde.org>
6
This library is free software; you can redistribute it and/or
7
modify it under the terms of the GNU Library General Public
8
License as published by the Free Software Foundation; either
9
version 2 of the License, or (at your option) any later version.
11
This library is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
Library General Public License for more details.
16
You should have received a copy of the GNU Library General Public License
17
along with this library; see the file COPYING.LIB. If not, write to
18
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19
Boston, MA 02111-1307, USA.
24
#include "SVGParserUtilities.h"
26
#include "FloatConversion.h"
27
#include "PlatformString.h"
29
#include <wtf/MathExtras.h>
33
bool parseNumber(const UChar*& ptr, const UChar* end, double& number, bool skip)
35
int integer, exponent;
38
const UChar* start = ptr;
48
if (ptr < end && *ptr == '+')
50
else if (ptr < end && *ptr == '-') {
54
// read the integer part
55
while (ptr < end && *ptr >= '0' && *ptr <= '9')
56
integer = (integer * 10) + *(ptr++) - '0';
58
if (ptr < end && *ptr == '.') { // read the decimals
60
while(ptr < end && *ptr >= '0' && *ptr <= '9')
61
decimal += (*(ptr++) - '0') * (frac *= 0.1);
64
if (ptr < end && (*ptr == 'e' || *ptr == 'E')) { // read the exponent part
67
// read the sign of the exponent
68
if (ptr < end && *ptr == '+')
70
else if (ptr < end && *ptr == '-') {
75
while (ptr < end && *ptr >= '0' && *ptr <= '9') {
77
exponent += *ptr - '0';
82
number = integer + decimal;
83
number *= sign * pow(10.0, expsign * exponent);
89
skipOptionalSpacesOrDelimiter(ptr, end);
94
bool parseNumberOptionalNumber(const String& s, double& x, double& y)
98
const UChar* cur = s.characters();
99
const UChar* end = cur + s.length();
101
if (!parseNumber(cur, end, x))
106
else if (!parseNumber(cur, end, y, false))
112
bool SVGPolyParser::parsePoints(const String& s) const
116
const UChar* cur = s.characters();
117
const UChar* end = cur + s.length();
119
skipOptionalSpaces(cur, end);
121
bool delimParsed = false;
126
if (!parseNumber(cur, end, xPos))
130
if (!parseNumber(cur, end, yPos, false))
133
skipOptionalSpaces(cur, end);
135
if (cur < end && *cur == ',') {
139
skipOptionalSpaces(cur, end);
141
svgPolyTo(xPos, yPos, segmentNum++);
143
return cur == end && !delimParsed;
146
bool SVGPathParser::parseSVG(const String& s, bool process)
151
const UChar* ptr = s.characters();
152
const UChar* end = ptr + s.length();
154
double contrlx, contrly, curx, cury, subpathx, subpathy, tox, toy, x1, y1, x2, y2, xc, yc;
155
double px1, py1, px2, py2, px3, py3;
158
if (!skipOptionalSpaces(ptr, end)) // skip any leading spaces
161
char command = *(ptr++), lastCommand = ' ';
162
if (command != 'm' && command != 'M') // path must start with moveto
165
subpathx = subpathy = curx = cury = contrlx = contrly = 0.0;
167
skipOptionalSpaces(ptr, end); // skip spaces between command and first coord
169
bool relative = false;
177
if (!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
181
subpathx = curx = relative ? curx + tox : tox;
182
subpathy = cury = relative ? cury + toy : toy;
184
svgMoveTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury), closed);
186
svgMoveTo(narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), closed, !relative);
194
if (!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
198
curx = relative ? curx + tox : tox;
199
cury = relative ? cury + toy : toy;
201
svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
204
svgLineTo(narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
209
if (!parseNumber(ptr, end, tox))
213
svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
216
svgLineToHorizontal(narrowPrecisionToFloat(tox), false);
221
if (!parseNumber(ptr, end, tox))
225
svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
228
svgLineToHorizontal(narrowPrecisionToFloat(tox));
233
if (!parseNumber(ptr, end, toy))
237
svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
240
svgLineToVertical(narrowPrecisionToFloat(toy), false);
245
if (!parseNumber(ptr, end, toy))
249
svgLineTo(narrowPrecisionToFloat(curx), narrowPrecisionToFloat(cury));
252
svgLineToVertical(narrowPrecisionToFloat(toy));
258
// reset curx, cury for next path
271
if (!parseNumber(ptr, end, x1) || !parseNumber(ptr, end, y1) ||
272
!parseNumber(ptr, end, x2) || !parseNumber(ptr, end, y2) ||
273
!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
277
px1 = relative ? curx + x1 : x1;
278
py1 = relative ? cury + y1 : y1;
279
px2 = relative ? curx + x2 : x2;
280
py2 = relative ? cury + y2 : y2;
281
px3 = relative ? curx + tox : tox;
282
py3 = relative ? cury + toy : toy;
284
svgCurveToCubic(narrowPrecisionToFloat(px1), narrowPrecisionToFloat(py1), narrowPrecisionToFloat(px2),
285
narrowPrecisionToFloat(py2), narrowPrecisionToFloat(px3), narrowPrecisionToFloat(py3));
287
contrlx = relative ? curx + x2 : x2;
288
contrly = relative ? cury + y2 : y2;
289
curx = relative ? curx + tox : tox;
290
cury = relative ? cury + toy : toy;
293
svgCurveToCubic(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1), narrowPrecisionToFloat(x2),
294
narrowPrecisionToFloat(y2), narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
302
if (!parseNumber(ptr, end, x2) || !parseNumber(ptr, end, y2) ||
303
!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
306
if (!(lastCommand == 'c' || lastCommand == 'C' ||
307
lastCommand == 's' || lastCommand == 'S')) {
313
px1 = 2 * curx - contrlx;
314
py1 = 2 * cury - contrly;
315
px2 = relative ? curx + x2 : x2;
316
py2 = relative ? cury + y2 : y2;
317
px3 = relative ? curx + tox : tox;
318
py3 = relative ? cury + toy : toy;
320
svgCurveToCubic(narrowPrecisionToFloat(px1), narrowPrecisionToFloat(py1), narrowPrecisionToFloat(px2),
321
narrowPrecisionToFloat(py2), narrowPrecisionToFloat(px3), narrowPrecisionToFloat(py3));
323
contrlx = relative ? curx + x2 : x2;
324
contrly = relative ? cury + y2 : y2;
325
curx = relative ? curx + tox : tox;
326
cury = relative ? cury + toy : toy;
329
svgCurveToCubicSmooth(narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2),
330
narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
337
if (!parseNumber(ptr, end, x1) || !parseNumber(ptr, end, y1) ||
338
!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
342
px1 = relative ? (curx + 2 * (x1 + curx)) * (1.0 / 3.0) : (curx + 2 * x1) * (1.0 / 3.0);
343
py1 = relative ? (cury + 2 * (y1 + cury)) * (1.0 / 3.0) : (cury + 2 * y1) * (1.0 / 3.0);
344
px2 = relative ? ((curx + tox) + 2 * (x1 + curx)) * (1.0 / 3.0) : (tox + 2 * x1) * (1.0 / 3.0);
345
py2 = relative ? ((cury + toy) + 2 * (y1 + cury)) * (1.0 / 3.0) : (toy + 2 * y1) * (1.0 / 3.0);
346
px3 = relative ? curx + tox : tox;
347
py3 = relative ? cury + toy : toy;
349
svgCurveToCubic(narrowPrecisionToFloat(px1), narrowPrecisionToFloat(py1), narrowPrecisionToFloat(px2),
350
narrowPrecisionToFloat(py2), narrowPrecisionToFloat(px3), narrowPrecisionToFloat(py3));
352
contrlx = relative ? curx + x1 : x1;
353
contrly = relative ? cury + y1 : y1;
354
curx = relative ? curx + tox : tox;
355
cury = relative ? cury + toy : toy;
358
svgCurveToQuadratic(narrowPrecisionToFloat(x1), narrowPrecisionToFloat(y1),
359
narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
366
if (!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
368
if (!(lastCommand == 'q' || lastCommand == 'Q' ||
369
lastCommand == 't' || lastCommand == 'T')) {
375
xc = 2 * curx - contrlx;
376
yc = 2 * cury - contrly;
378
px1 = relative ? (curx + 2 * xc) * (1.0 / 3.0) : (curx + 2 * xc) * (1.0 / 3.0);
379
py1 = relative ? (cury + 2 * yc) * (1.0 / 3.0) : (cury + 2 * yc) * (1.0 / 3.0);
380
px2 = relative ? ((curx + tox) + 2 * xc) * (1.0 / 3.0) : (tox + 2 * xc) * (1.0 / 3.0);
381
py2 = relative ? ((cury + toy) + 2 * yc) * (1.0 / 3.0) : (toy + 2 * yc) * (1.0 / 3.0);
382
px3 = relative ? curx + tox : tox;
383
py3 = relative ? cury + toy : toy;
385
svgCurveToCubic(narrowPrecisionToFloat(px1), narrowPrecisionToFloat(py1), narrowPrecisionToFloat(px2),
386
narrowPrecisionToFloat(py2), narrowPrecisionToFloat(px3), narrowPrecisionToFloat(py3));
390
curx = relative ? curx + tox : tox;
391
cury = relative ? cury + toy : toy;
394
svgCurveToQuadraticSmooth(narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), !relative);
401
bool largeArc, sweep;
402
double angle, rx, ry;
403
if (!parseNumber(ptr, end, rx) || !parseNumber(ptr, end, ry) ||
404
!parseNumber(ptr, end, angle) || !parseNumber(ptr, end, tox))
407
if (!parseNumber(ptr, end, tox))
410
if (!parseNumber(ptr, end, tox) || !parseNumber(ptr, end, toy))
413
// Spec: radii are nonnegative numbers
418
calculateArc(relative, curx, cury, angle, tox, toy, rx, ry, largeArc, sweep);
420
svgArcTo(narrowPrecisionToFloat(tox), narrowPrecisionToFloat(toy), narrowPrecisionToFloat(rx), narrowPrecisionToFloat(ry),
421
narrowPrecisionToFloat(angle), largeArc, sweep, !relative);
425
// FIXME: An error should go to the JavaScript console, or the like.
428
lastCommand = command;
433
// Check for remaining coordinates in the current command.
434
if ((*ptr == '+' || *ptr == '-' || (*ptr >= '0' && *ptr <= '9')) &&
435
(command != 'z' && command !='a' && command != 'A')) {
438
else if (command == 'm')
443
if (lastCommand != 'C' && lastCommand != 'c' &&
444
lastCommand != 'S' && lastCommand != 's' &&
445
lastCommand != 'Q' && lastCommand != 'q' &&
446
lastCommand != 'T' && lastCommand != 't') {
455
// This works by converting the SVG arc to "simple" beziers.
456
// For each bezier found a svgToCurve call is done.
457
// Adapted from Niko's code in kdelibs/kdecore/svgicons.
458
// Maybe this can serve in some shared lib? (Rob)
459
void SVGPathParser::calculateArc(bool relative, double& curx, double& cury, double angle, double x, double y, double r1, double r2, bool largeArcFlag, bool sweepFlag)
461
double sin_th, cos_th;
462
double a00, a01, a10, a11;
463
double x0, y0, x1, y1, xc, yc;
464
double d, sfactor, sfactor_sq;
465
double th0, th1, th_arc;
468
sin_th = sin(angle * (piDouble / 180.0));
469
cos_th = cos(angle * (piDouble / 180.0));
474
dx = (curx - x) / 2.0;
481
dy = (cury - y) / 2.0;
485
double _x1 = cos_th * dx + sin_th * dy;
486
double _y1 = -sin_th * dx + cos_th * dy;
487
double Pr1 = r1 * r1;
488
double Pr2 = r2 * r2;
489
double Px = _x1 * _x1;
490
double Py = _y1 * _y1;
492
// Spec : check if radii are large enough
493
double check = Px / Pr1 + Py / Pr2;
495
r1 = r1 * sqrt(check);
496
r2 = r2 * sqrt(check);
504
x0 = a00 * curx + a01 * cury;
505
y0 = a10 * curx + a11 * cury;
508
x1 = a00 * x + a01 * y;
510
x1 = a00 * (curx + x) + a01 * (cury + y);
513
y1 = a10 * x + a11 * y;
515
y1 = a10 * (curx + x) + a11 * (cury + y);
517
/* (x0, y0) is current point in transformed coordinate space.
518
(x1, y1) is new point in transformed coordinate space.
520
The arc fits a unit-radius circle in this space.
523
d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
525
sfactor_sq = 1.0 / d - 0.25;
530
sfactor = sqrt(sfactor_sq);
532
if (sweepFlag == largeArcFlag)
535
xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
536
yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
538
/* (xc, yc) is center of the circle. */
539
th0 = atan2(y0 - yc, x0 - xc);
540
th1 = atan2(y1 - yc, x1 - xc);
543
if (th_arc < 0 && sweepFlag)
544
th_arc += 2 * piDouble;
545
else if (th_arc > 0 && !sweepFlag)
546
th_arc -= 2 * piDouble;
548
n_segs = (int) (int) ceil(fabs(th_arc / (piDouble * 0.5 + 0.001)));
550
for(i = 0; i < n_segs; i++) {
551
double sin_th, cos_th;
552
double a00, a01, a10, a11;
553
double x1, y1, x2, y2, x3, y3;
557
double _th0 = th0 + i * th_arc / n_segs;
558
double _th1 = th0 + (i + 1) * th_arc / n_segs;
560
sin_th = sin(angle * (piDouble / 180.0));
561
cos_th = cos(angle * (piDouble / 180.0));
563
/* inverse transform compared with rsvg_path_arc */
569
th_half = 0.5 * (_th1 - _th0);
570
t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half);
571
x1 = xc + cos(_th0) - t * sin(_th0);
572
y1 = yc + sin(_th0) + t * cos(_th0);
575
x2 = x3 + t * sin(_th1);
576
y2 = y3 - t * cos(_th1);
578
svgCurveToCubic(narrowPrecisionToFloat(a00 * x1 + a01 * y1), narrowPrecisionToFloat(a10 * x1 + a11 * y1),
579
narrowPrecisionToFloat(a00 * x2 + a01 * y2), narrowPrecisionToFloat(a10 * x2 + a11 * y2),
580
narrowPrecisionToFloat(a00 * x3 + a01 * y3), narrowPrecisionToFloat(a10 * x3 + a11 * y3));
594
void SVGPathParser::svgLineToHorizontal(float, bool)
598
void SVGPathParser::svgLineToVertical(float, bool)
602
void SVGPathParser::svgCurveToCubicSmooth(float, float, float, float, bool)
606
void SVGPathParser::svgCurveToQuadratic(float, float, float, float, bool)
610
void SVGPathParser::svgCurveToQuadraticSmooth(float, float, bool)
614
void SVGPathParser::svgArcTo(float, float, float, float, float, bool, bool, bool)
621
#endif // ENABLE(SVG)