1 /*
2 Copyright (C) 2002, 2003 The Karbon Developers
3 2006 Alexander Kellett <lypanov@kde.org>
4 2006, 2007 Rob Buis <buis@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 */
_parseNumber(const UChar * & ptr,const UChar * end,FloatType & number,bool skip)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
parseNumber(const UChar * & ptr,const UChar * end,float & number,bool skip)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
parseNumber(const UChar * & ptr,const UChar * end,double & number,bool skip=true)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
parseNumberOptionalNumber(const String & s,float & x,float & y)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
pointsListFromSVGData(SVGPointList * pointsList,const String & points)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:
~SVGPathParser()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;
svgLineToHorizontal(double,bool=true)214 virtual void svgLineToHorizontal(double, bool /*abs*/ = true) { }
svgLineToVertical(double,bool=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;
svgCurveToCubicSmooth(double,double,double,double,bool=true)217 virtual void svgCurveToCubicSmooth(double /*x*/, double /*y*/, double /*x2*/, double /*y2*/, bool /*abs*/ = true) { }
svgCurveToQuadratic(double,double,double,double,bool=true)218 virtual void svgCurveToQuadratic(double /*x*/, double /*y*/, double /*x1*/, double /*y1*/, bool /*abs*/ = true) { }
svgCurveToQuadraticSmooth(double,double,bool=true)219 virtual void svgCurveToQuadraticSmooth(double /*x*/, double /*y*/, bool /*abs*/ = true) { }
svgArcTo(double,double,double,double,double,bool,bool,bool=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
parseSVG(const String & s,bool process)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 >= '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)
calculateArc(bool relative,double & curx,double & cury,double angle,double x,double y,double r1,double r2,bool largeArcFlag,bool sweepFlag)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:
build(Path * path,const String & d)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:
svgMoveTo(double x1,double y1,bool closed,bool abs=true)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 }
svgLineTo(double x1,double y1,bool abs=true)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 }
svgCurveToCubic(double x1,double y1,double x2,double y2,double x,double y,bool abs=true)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 }
svgClosePath()711 virtual void svgClosePath()
712 {
713 m_path->closeSubpath();
714 }
715
716 Path* m_path;
717 FloatPoint current;
718 };
719
pathFromSVGData(Path & path,const String & d)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:
build(SVGPathSegList * segList,const String & d,bool process)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:
svgMoveTo(double x1,double y1,bool,bool abs=true)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 }
svgLineTo(double x1,double y1,bool abs=true)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 }
svgLineToHorizontal(double x,bool abs)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 }
svgLineToVertical(double y,bool abs)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 }
svgCurveToCubic(double x1,double y1,double x2,double y2,double x,double y,bool abs=true)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 }
svgCurveToCubicSmooth(double x,double y,double x2,double y2,bool abs)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 }
svgCurveToQuadratic(double x,double y,double x1,double y1,bool abs)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 }
svgCurveToQuadraticSmooth(double x,double y,bool abs)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 }
svgArcTo(double x,double y,double r1,double r2,double angle,bool largeArcFlag,bool sweepFlag,bool abs)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 }
svgClosePath()817 virtual void svgClosePath()
818 {
819 m_vector.append(SVGPathElement::createSVGPathSegClosePath());
820 }
821
822 Vector<RefPtr<SVGPathSeg> > m_vector;
823 };
824
pathSegListFromSVGData(SVGPathSegList * path,const String & d,bool process)825 bool pathSegListFromSVGData(SVGPathSegList* path, const String& d, bool process)
826 {
827 SVGPathSegListBuilder builder;
828 return builder.build(path, d, process);
829 }
830
parseDelimitedString(const String & input,const char seperator)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