1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Mozilla SVG project.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Crocodile Clips Ltd..
19 : * Portions created by the Initial Developer are Copyright (C) 2001
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsSVGPathDataParser.h"
40 : #include "nsSVGDataParser.h"
41 : #include "nsSVGPathElement.h"
42 : #include "prdtoa.h"
43 : #include "nsSVGUtils.h"
44 : #include "nsIDOMSVGPathSeg.h"
45 : #include <stdlib.h>
46 : #include <math.h>
47 :
48 : using namespace mozilla;
49 :
50 0 : nsresult nsSVGPathDataParser::Match()
51 : {
52 0 : return MatchSvgPath();
53 : }
54 :
55 : //----------------------------------------------------------------------
56 :
57 0 : nsresult nsSVGPathDataParser::MatchCoordPair(float* aX, float* aY)
58 : {
59 0 : ENSURE_MATCHED(MatchCoord(aX));
60 :
61 0 : if (IsTokenCommaWspStarter()) {
62 0 : ENSURE_MATCHED(MatchCommaWsp());
63 : }
64 :
65 0 : ENSURE_MATCHED(MatchCoord(aY));
66 :
67 0 : return NS_OK;
68 : }
69 :
70 0 : bool nsSVGPathDataParser::IsTokenCoordPairStarter()
71 : {
72 0 : return IsTokenCoordStarter();
73 : }
74 :
75 : //----------------------------------------------------------------------
76 :
77 0 : nsresult nsSVGPathDataParser::MatchCoord(float* aX)
78 : {
79 0 : ENSURE_MATCHED(MatchNumber(aX));
80 :
81 0 : return NS_OK;
82 : }
83 :
84 0 : bool nsSVGPathDataParser::IsTokenCoordStarter()
85 : {
86 0 : return IsTokenNumberStarter();
87 : }
88 :
89 : //----------------------------------------------------------------------
90 :
91 0 : nsresult nsSVGPathDataParser::MatchFlag(bool* f)
92 : {
93 0 : switch (mTokenVal) {
94 : case '0':
95 0 : *f = false;
96 0 : break;
97 : case '1':
98 0 : *f = true;
99 0 : break;
100 : default:
101 0 : return NS_ERROR_FAILURE;
102 : }
103 0 : GetNextToken();
104 0 : return NS_OK;
105 : }
106 :
107 : //----------------------------------------------------------------------
108 :
109 0 : nsresult nsSVGPathDataParser::MatchSvgPath()
110 : {
111 0 : while (IsTokenWspStarter()) {
112 0 : ENSURE_MATCHED(MatchWsp());
113 : }
114 :
115 0 : if (IsTokenSubPathsStarter()) {
116 0 : ENSURE_MATCHED(MatchSubPaths());
117 : }
118 :
119 0 : while (IsTokenWspStarter()) {
120 0 : ENSURE_MATCHED(MatchWsp());
121 : }
122 :
123 0 : return NS_OK;
124 : }
125 :
126 : //----------------------------------------------------------------------
127 :
128 0 : nsresult nsSVGPathDataParser::MatchSubPaths()
129 : {
130 0 : ENSURE_MATCHED(MatchSubPath());
131 :
132 0 : while (1) {
133 0 : const char* pos = mTokenPos;
134 :
135 0 : while (IsTokenWspStarter()) {
136 0 : ENSURE_MATCHED(MatchWsp());
137 : }
138 :
139 0 : if (IsTokenSubPathStarter()) {
140 0 : ENSURE_MATCHED(MatchSubPath());
141 : }
142 : else {
143 0 : if (pos != mTokenPos) RewindTo(pos);
144 : break;
145 : }
146 : }
147 :
148 0 : return NS_OK;
149 : }
150 :
151 0 : bool nsSVGPathDataParser::IsTokenSubPathsStarter()
152 : {
153 0 : return IsTokenSubPathStarter();
154 : }
155 :
156 : //----------------------------------------------------------------------
157 :
158 0 : nsresult nsSVGPathDataParser::MatchSubPath()
159 : {
160 0 : ENSURE_MATCHED(MatchMoveto());
161 :
162 0 : while (IsTokenWspStarter()) {
163 0 : ENSURE_MATCHED(MatchWsp());
164 : }
165 :
166 0 : if (IsTokenSubPathElementsStarter())
167 0 : ENSURE_MATCHED(MatchSubPathElements());
168 :
169 0 : return NS_OK;
170 : }
171 :
172 0 : bool nsSVGPathDataParser::IsTokenSubPathStarter()
173 : {
174 0 : return (tolower(mTokenVal) == 'm');
175 : }
176 :
177 : //----------------------------------------------------------------------
178 :
179 0 : nsresult nsSVGPathDataParser::MatchSubPathElements()
180 : {
181 0 : ENSURE_MATCHED(MatchSubPathElement());
182 :
183 0 : while (1) {
184 0 : const char* pos = mTokenPos;
185 :
186 0 : while (IsTokenWspStarter()) {
187 0 : ENSURE_MATCHED(MatchWsp());
188 : }
189 :
190 :
191 0 : if (IsTokenSubPathElementStarter()) {
192 0 : ENSURE_MATCHED(MatchSubPathElement());
193 : }
194 : else {
195 0 : if (pos != mTokenPos) RewindTo(pos);
196 0 : return NS_OK;
197 : }
198 : }
199 :
200 : return NS_OK;
201 : }
202 :
203 0 : bool nsSVGPathDataParser::IsTokenSubPathElementsStarter()
204 : {
205 0 : return IsTokenSubPathElementStarter();
206 : }
207 :
208 : //----------------------------------------------------------------------
209 :
210 0 : nsresult nsSVGPathDataParser::MatchSubPathElement()
211 : {
212 0 : switch (tolower(mTokenVal)) {
213 : case 'z':
214 0 : ENSURE_MATCHED(MatchClosePath());
215 0 : break;
216 : case 'l':
217 0 : ENSURE_MATCHED(MatchLineto());
218 0 : break;
219 : case 'h':
220 0 : ENSURE_MATCHED(MatchHorizontalLineto());
221 0 : break;
222 : case 'v':
223 0 : ENSURE_MATCHED(MatchVerticalLineto());
224 0 : break;
225 : case 'c':
226 0 : ENSURE_MATCHED(MatchCurveto());
227 0 : break;
228 : case 's':
229 0 : ENSURE_MATCHED(MatchSmoothCurveto());
230 0 : break;
231 : case 'q':
232 0 : ENSURE_MATCHED(MatchQuadBezierCurveto());
233 0 : break;
234 : case 't':
235 0 : ENSURE_MATCHED(MatchSmoothQuadBezierCurveto());
236 0 : break;
237 : case 'a':
238 0 : ENSURE_MATCHED(MatchEllipticalArc());
239 0 : break;
240 : default:
241 0 : return NS_ERROR_FAILURE;
242 : break;
243 : }
244 0 : return NS_OK;
245 : }
246 :
247 0 : bool nsSVGPathDataParser::IsTokenSubPathElementStarter()
248 : {
249 0 : switch (tolower(mTokenVal)) {
250 : case 'z': case 'l': case 'h': case 'v': case 'c':
251 : case 's': case 'q': case 't': case 'a':
252 0 : return true;
253 : break;
254 : default:
255 0 : return false;
256 : break;
257 : }
258 : return false;
259 : }
260 :
261 : //----------------------------------------------------------------------
262 :
263 0 : nsresult nsSVGPathDataParser::MatchMoveto()
264 : {
265 : bool absCoords;
266 :
267 0 : switch (mTokenVal) {
268 : case 'M':
269 0 : absCoords = true;
270 0 : break;
271 : case 'm':
272 0 : absCoords = false;
273 0 : break;
274 : default:
275 0 : return NS_ERROR_FAILURE;
276 : }
277 :
278 0 : GetNextToken();
279 :
280 0 : while (IsTokenWspStarter()) {
281 0 : ENSURE_MATCHED(MatchWsp());
282 : }
283 :
284 0 : ENSURE_MATCHED(MatchMovetoArgSeq(absCoords));
285 :
286 0 : return NS_OK;
287 : }
288 :
289 : // typedef nsresult MovetoSegCreationFunc(nsIDOMSVGPathSeg** res, float x, float y);
290 : // MovetoSegCreationFunc *creationFunc;
291 :
292 :
293 0 : nsresult nsSVGPathDataParser::MatchMovetoArgSeq(bool absCoords)
294 : {
295 :
296 : float x, y;
297 0 : ENSURE_MATCHED(MatchCoordPair(&x, &y));
298 :
299 0 : nsresult rv = StoreMoveTo(absCoords, x, y);
300 0 : NS_ENSURE_SUCCESS(rv, rv);
301 :
302 0 : const char* pos = mTokenPos;
303 :
304 0 : if (IsTokenCommaWspStarter()) {
305 0 : ENSURE_MATCHED(MatchCommaWsp());
306 : }
307 :
308 0 : if (IsTokenLinetoArgSeqStarter()) {
309 0 : ENSURE_MATCHED(MatchLinetoArgSeq(absCoords));
310 : }
311 : else {
312 0 : if (pos != mTokenPos) RewindTo(pos);
313 : }
314 :
315 0 : return NS_OK;
316 : }
317 :
318 : //----------------------------------------------------------------------
319 :
320 0 : nsresult nsSVGPathDataParser::MatchClosePath()
321 : {
322 0 : switch (mTokenVal) {
323 : case 'Z':
324 : case 'z':
325 0 : GetNextToken();
326 : break;
327 : default:
328 0 : return NS_ERROR_FAILURE;
329 : }
330 :
331 0 : return StoreClosePath();
332 : }
333 :
334 : //----------------------------------------------------------------------
335 :
336 0 : nsresult nsSVGPathDataParser::MatchLineto()
337 : {
338 : bool absCoords;
339 :
340 0 : switch (mTokenVal) {
341 : case 'L':
342 0 : absCoords = true;
343 0 : break;
344 : case 'l':
345 0 : absCoords = false;
346 0 : break;
347 : default:
348 0 : return NS_ERROR_FAILURE;
349 : }
350 :
351 0 : GetNextToken();
352 :
353 0 : while (IsTokenWspStarter()) {
354 0 : ENSURE_MATCHED(MatchWsp());
355 : }
356 :
357 0 : ENSURE_MATCHED(MatchLinetoArgSeq(absCoords));
358 :
359 0 : return NS_OK;
360 : }
361 :
362 0 : nsresult nsSVGPathDataParser::MatchLinetoArgSeq(bool absCoords)
363 : {
364 0 : while(1) {
365 : float x, y;
366 0 : ENSURE_MATCHED(MatchCoordPair(&x, &y));
367 :
368 0 : nsresult rv = StoreLineTo(absCoords, x, y);
369 0 : NS_ENSURE_SUCCESS(rv, rv);
370 :
371 0 : const char* pos = mTokenPos;
372 :
373 0 : if (IsTokenCommaWspStarter()) {
374 0 : ENSURE_MATCHED(MatchCommaWsp());
375 : }
376 :
377 0 : if (!IsTokenCoordPairStarter()) {
378 0 : if (pos != mTokenPos) RewindTo(pos);
379 0 : return NS_OK;
380 : }
381 : }
382 :
383 : return NS_OK;
384 : }
385 :
386 0 : bool nsSVGPathDataParser::IsTokenLinetoArgSeqStarter()
387 : {
388 0 : return IsTokenCoordPairStarter();
389 : }
390 :
391 : //----------------------------------------------------------------------
392 :
393 0 : nsresult nsSVGPathDataParser::MatchHorizontalLineto()
394 : {
395 : bool absCoords;
396 :
397 0 : switch (mTokenVal) {
398 : case 'H':
399 0 : absCoords = true;
400 0 : break;
401 : case 'h':
402 0 : absCoords = false;
403 0 : break;
404 : default:
405 0 : return NS_ERROR_FAILURE;
406 : }
407 :
408 0 : GetNextToken();
409 :
410 0 : while (IsTokenWspStarter()) {
411 0 : ENSURE_MATCHED(MatchWsp());
412 : }
413 :
414 0 : ENSURE_MATCHED(MatchHorizontalLinetoArgSeq(absCoords));
415 :
416 0 : return NS_OK;
417 : }
418 :
419 0 : nsresult nsSVGPathDataParser::MatchHorizontalLinetoArgSeq(bool absCoords)
420 : {
421 0 : while(1) {
422 : float x;
423 0 : ENSURE_MATCHED(MatchCoord(&x));
424 :
425 0 : nsresult rv = StoreHLineTo(absCoords, x);
426 0 : NS_ENSURE_SUCCESS(rv, rv);
427 :
428 0 : const char* pos = mTokenPos;
429 :
430 0 : if (IsTokenCommaWspStarter()) {
431 0 : ENSURE_MATCHED(MatchCommaWsp());
432 : }
433 :
434 0 : if (!IsTokenCoordStarter()) {
435 0 : if (pos != mTokenPos) RewindTo(pos);
436 0 : return NS_OK;
437 : }
438 : }
439 :
440 : return NS_OK;
441 : }
442 :
443 : //----------------------------------------------------------------------
444 :
445 0 : nsresult nsSVGPathDataParser::MatchVerticalLineto()
446 : {
447 : bool absCoords;
448 :
449 0 : switch (mTokenVal) {
450 : case 'V':
451 0 : absCoords = true;
452 0 : break;
453 : case 'v':
454 0 : absCoords = false;
455 0 : break;
456 : default:
457 0 : return NS_ERROR_FAILURE;
458 : }
459 :
460 0 : GetNextToken();
461 :
462 0 : while (IsTokenWspStarter()) {
463 0 : ENSURE_MATCHED(MatchWsp());
464 : }
465 :
466 0 : ENSURE_MATCHED(MatchVerticalLinetoArgSeq(absCoords));
467 :
468 0 : return NS_OK;
469 : }
470 :
471 0 : nsresult nsSVGPathDataParser::MatchVerticalLinetoArgSeq(bool absCoords)
472 : {
473 0 : while(1) {
474 : float y;
475 0 : ENSURE_MATCHED(MatchCoord(&y));
476 :
477 0 : nsresult rv = StoreVLineTo(absCoords, y);
478 0 : NS_ENSURE_SUCCESS(rv, rv);
479 :
480 0 : const char* pos = mTokenPos;
481 :
482 0 : if (IsTokenCommaWspStarter()) {
483 0 : ENSURE_MATCHED(MatchCommaWsp());
484 : }
485 :
486 0 : if (!IsTokenCoordStarter()) {
487 0 : if (pos != mTokenPos) RewindTo(pos);
488 0 : return NS_OK;
489 : }
490 : }
491 :
492 : return NS_OK;
493 : }
494 :
495 : //----------------------------------------------------------------------
496 :
497 0 : nsresult nsSVGPathDataParser::MatchCurveto()
498 : {
499 : bool absCoords;
500 :
501 0 : switch (mTokenVal) {
502 : case 'C':
503 0 : absCoords = true;
504 0 : break;
505 : case 'c':
506 0 : absCoords = false;
507 0 : break;
508 : default:
509 0 : return NS_ERROR_FAILURE;
510 : }
511 :
512 0 : GetNextToken();
513 :
514 0 : while (IsTokenWspStarter()) {
515 0 : ENSURE_MATCHED(MatchWsp());
516 : }
517 :
518 0 : ENSURE_MATCHED(MatchCurvetoArgSeq(absCoords));
519 :
520 0 : return NS_OK;
521 : }
522 :
523 :
524 0 : nsresult nsSVGPathDataParser::MatchCurvetoArgSeq(bool absCoords)
525 : {
526 0 : while(1) {
527 : float x, y, x1, y1, x2, y2;
528 0 : ENSURE_MATCHED(MatchCurvetoArg(&x, &y, &x1, &y1, &x2, &y2));
529 :
530 0 : nsresult rv = StoreCurveTo(absCoords, x, y, x1, y1, x2, y2);
531 0 : NS_ENSURE_SUCCESS(rv, rv);
532 :
533 0 : const char* pos = mTokenPos;
534 :
535 0 : if (IsTokenCommaWspStarter()) {
536 0 : ENSURE_MATCHED(MatchCommaWsp());
537 : }
538 :
539 0 : if (!IsTokenCurvetoArgStarter()) {
540 0 : if (pos != mTokenPos) RewindTo(pos);
541 0 : return NS_OK;
542 : }
543 : }
544 :
545 : return NS_OK;
546 : }
547 :
548 : nsresult
549 0 : nsSVGPathDataParser::MatchCurvetoArg(float* x, float* y, float* x1,
550 : float* y1, float* x2, float* y2)
551 : {
552 0 : ENSURE_MATCHED(MatchCoordPair(x1, y1));
553 :
554 0 : if (IsTokenCommaWspStarter()) {
555 0 : ENSURE_MATCHED(MatchCommaWsp());
556 : }
557 :
558 0 : ENSURE_MATCHED(MatchCoordPair(x2, y2));
559 :
560 0 : if (IsTokenCommaWspStarter()) {
561 0 : ENSURE_MATCHED(MatchCommaWsp());
562 : }
563 :
564 0 : ENSURE_MATCHED(MatchCoordPair(x, y));
565 :
566 0 : return NS_OK;
567 : }
568 :
569 0 : bool nsSVGPathDataParser::IsTokenCurvetoArgStarter()
570 : {
571 0 : return IsTokenCoordPairStarter();
572 : }
573 :
574 : //----------------------------------------------------------------------
575 :
576 0 : nsresult nsSVGPathDataParser::MatchSmoothCurveto()
577 : {
578 : bool absCoords;
579 :
580 0 : switch (mTokenVal) {
581 : case 'S':
582 0 : absCoords = true;
583 0 : break;
584 : case 's':
585 0 : absCoords = false;
586 0 : break;
587 : default:
588 0 : return NS_ERROR_FAILURE;
589 : }
590 :
591 0 : GetNextToken();
592 :
593 0 : while (IsTokenWspStarter()) {
594 0 : ENSURE_MATCHED(MatchWsp());
595 : }
596 :
597 0 : ENSURE_MATCHED(MatchSmoothCurvetoArgSeq(absCoords));
598 :
599 0 : return NS_OK;
600 : }
601 :
602 0 : nsresult nsSVGPathDataParser::MatchSmoothCurvetoArgSeq(bool absCoords)
603 : {
604 0 : while(1) {
605 : float x, y, x2, y2;
606 0 : ENSURE_MATCHED(MatchSmoothCurvetoArg(&x, &y, &x2, &y2));
607 :
608 0 : nsresult rv = StoreSmoothCurveTo(absCoords, x, y, x2, y2);
609 0 : NS_ENSURE_SUCCESS(rv, rv);
610 :
611 0 : const char* pos = mTokenPos;
612 :
613 0 : if (IsTokenCommaWspStarter()) {
614 0 : ENSURE_MATCHED(MatchCommaWsp());
615 : }
616 :
617 0 : if (!IsTokenSmoothCurvetoArgStarter()) {
618 0 : if (pos != mTokenPos) RewindTo(pos);
619 0 : return NS_OK;
620 : }
621 : }
622 :
623 : return NS_OK;
624 : }
625 :
626 0 : nsresult nsSVGPathDataParser::MatchSmoothCurvetoArg(float* x, float* y, float* x2, float* y2)
627 : {
628 0 : ENSURE_MATCHED(MatchCoordPair(x2, y2));
629 :
630 0 : if (IsTokenCommaWspStarter()) {
631 0 : ENSURE_MATCHED(MatchCommaWsp());
632 : }
633 :
634 0 : ENSURE_MATCHED(MatchCoordPair(x, y));
635 :
636 0 : return NS_OK;
637 : }
638 :
639 0 : bool nsSVGPathDataParser::IsTokenSmoothCurvetoArgStarter()
640 : {
641 0 : return IsTokenCoordPairStarter();
642 : }
643 :
644 : //----------------------------------------------------------------------
645 :
646 0 : nsresult nsSVGPathDataParser::MatchQuadBezierCurveto()
647 : {
648 : bool absCoords;
649 :
650 0 : switch (mTokenVal) {
651 : case 'Q':
652 0 : absCoords = true;
653 0 : break;
654 : case 'q':
655 0 : absCoords = false;
656 0 : break;
657 : default:
658 0 : return NS_ERROR_FAILURE;
659 : }
660 :
661 0 : GetNextToken();
662 :
663 0 : while (IsTokenWspStarter()) {
664 0 : ENSURE_MATCHED(MatchWsp());
665 : }
666 :
667 0 : ENSURE_MATCHED(MatchQuadBezierCurvetoArgSeq(absCoords));
668 :
669 0 : return NS_OK;
670 : }
671 :
672 0 : nsresult nsSVGPathDataParser::MatchQuadBezierCurvetoArgSeq(bool absCoords)
673 : {
674 0 : while(1) {
675 : float x, y, x1, y1;
676 0 : ENSURE_MATCHED(MatchQuadBezierCurvetoArg(&x, &y, &x1, &y1));
677 :
678 0 : nsresult rv = StoreQuadCurveTo(absCoords, x, y, x1, y1);
679 0 : NS_ENSURE_SUCCESS(rv, rv);
680 :
681 0 : const char* pos = mTokenPos;
682 :
683 0 : if (IsTokenCommaWspStarter()) {
684 0 : ENSURE_MATCHED(MatchCommaWsp());
685 : }
686 :
687 0 : if (!IsTokenQuadBezierCurvetoArgStarter()) {
688 0 : if (pos != mTokenPos) RewindTo(pos);
689 0 : return NS_OK;
690 : }
691 : }
692 :
693 : return NS_OK;
694 : }
695 :
696 0 : nsresult nsSVGPathDataParser::MatchQuadBezierCurvetoArg(float* x, float* y, float* x1, float* y1)
697 : {
698 0 : ENSURE_MATCHED(MatchCoordPair(x1, y1));
699 :
700 0 : if (IsTokenCommaWspStarter()) {
701 0 : ENSURE_MATCHED(MatchCommaWsp());
702 : }
703 :
704 0 : ENSURE_MATCHED(MatchCoordPair(x, y));
705 :
706 0 : return NS_OK;
707 : }
708 :
709 0 : bool nsSVGPathDataParser::IsTokenQuadBezierCurvetoArgStarter()
710 : {
711 0 : return IsTokenCoordPairStarter();
712 : }
713 :
714 : //----------------------------------------------------------------------
715 :
716 0 : nsresult nsSVGPathDataParser::MatchSmoothQuadBezierCurveto()
717 : {
718 : bool absCoords;
719 :
720 0 : switch (mTokenVal) {
721 : case 'T':
722 0 : absCoords = true;
723 0 : break;
724 : case 't':
725 0 : absCoords = false;
726 0 : break;
727 : default:
728 0 : return NS_ERROR_FAILURE;
729 : }
730 :
731 0 : GetNextToken();
732 :
733 0 : while (IsTokenWspStarter()) {
734 0 : ENSURE_MATCHED(MatchWsp());
735 : }
736 :
737 0 : ENSURE_MATCHED(MatchSmoothQuadBezierCurvetoArgSeq(absCoords));
738 :
739 0 : return NS_OK;
740 : }
741 :
742 0 : nsresult nsSVGPathDataParser::MatchSmoothQuadBezierCurvetoArgSeq(bool absCoords)
743 : {
744 0 : while(1) {
745 : float x, y;
746 0 : ENSURE_MATCHED(MatchCoordPair(&x, &y));
747 :
748 0 : nsresult rv = StoreSmoothQuadCurveTo(absCoords, x, y);
749 0 : NS_ENSURE_SUCCESS(rv, rv);
750 :
751 0 : const char* pos = mTokenPos;
752 :
753 0 : if (IsTokenCommaWspStarter()) {
754 0 : ENSURE_MATCHED(MatchCommaWsp());
755 : }
756 :
757 0 : if (!IsTokenCoordPairStarter()) {
758 0 : if (pos != mTokenPos) RewindTo(pos);
759 0 : return NS_OK;
760 : }
761 : }
762 :
763 : return NS_OK;
764 : }
765 :
766 : //----------------------------------------------------------------------
767 :
768 0 : nsresult nsSVGPathDataParser::MatchEllipticalArc()
769 : {
770 : bool absCoords;
771 :
772 0 : switch (mTokenVal) {
773 : case 'A':
774 0 : absCoords = true;
775 0 : break;
776 : case 'a':
777 0 : absCoords = false;
778 0 : break;
779 : default:
780 0 : return NS_ERROR_FAILURE;
781 : }
782 :
783 0 : GetNextToken();
784 :
785 0 : while (IsTokenWspStarter()) {
786 0 : ENSURE_MATCHED(MatchWsp());
787 : }
788 :
789 0 : ENSURE_MATCHED(MatchEllipticalArcArgSeq(absCoords));
790 :
791 0 : return NS_OK;
792 : }
793 :
794 :
795 0 : nsresult nsSVGPathDataParser::MatchEllipticalArcArgSeq(bool absCoords)
796 : {
797 0 : while(1) {
798 : float x, y, r1, r2, angle;
799 : bool largeArcFlag, sweepFlag;
800 :
801 0 : ENSURE_MATCHED(MatchEllipticalArcArg(&x, &y, &r1, &r2, &angle, &largeArcFlag, &sweepFlag));
802 :
803 : nsresult rv = StoreEllipticalArc(absCoords, x, y, r1, r2, angle,
804 0 : largeArcFlag, sweepFlag);
805 0 : NS_ENSURE_SUCCESS(rv, rv);
806 :
807 0 : const char* pos = mTokenPos;
808 :
809 0 : if (IsTokenCommaWspStarter()) {
810 0 : ENSURE_MATCHED(MatchCommaWsp());
811 : }
812 :
813 0 : if (!IsTokenEllipticalArcArgStarter()) {
814 0 : if (pos != mTokenPos) RewindTo(pos);
815 0 : return NS_OK;
816 : }
817 : }
818 :
819 : return NS_OK;
820 : }
821 :
822 0 : nsresult nsSVGPathDataParser::MatchEllipticalArcArg(float* x, float* y,
823 : float* r1, float* r2, float* angle,
824 : bool* largeArcFlag, bool* sweepFlag)
825 : {
826 0 : ENSURE_MATCHED(MatchNonNegativeNumber(r1));
827 :
828 0 : if (IsTokenCommaWspStarter()) {
829 0 : ENSURE_MATCHED(MatchCommaWsp());
830 : }
831 :
832 0 : ENSURE_MATCHED(MatchNonNegativeNumber(r2));
833 :
834 0 : if (IsTokenCommaWspStarter()) {
835 0 : ENSURE_MATCHED(MatchCommaWsp());
836 : }
837 :
838 0 : ENSURE_MATCHED(MatchNumber(angle));
839 :
840 0 : if (IsTokenCommaWspStarter()) {
841 0 : ENSURE_MATCHED(MatchCommaWsp());
842 : }
843 :
844 0 : ENSURE_MATCHED(MatchFlag(largeArcFlag));
845 :
846 0 : if (IsTokenCommaWspStarter()) {
847 0 : ENSURE_MATCHED(MatchCommaWsp());
848 : }
849 :
850 0 : ENSURE_MATCHED(MatchFlag(sweepFlag));
851 :
852 0 : if (IsTokenCommaWspStarter()) {
853 0 : ENSURE_MATCHED(MatchCommaWsp());
854 : }
855 :
856 0 : ENSURE_MATCHED(MatchCoordPair(x, y));
857 :
858 0 : return NS_OK;
859 :
860 : }
861 :
862 0 : bool nsSVGPathDataParser::IsTokenEllipticalArcArgStarter()
863 : {
864 0 : return IsTokenNonNegativeNumberStarter();
865 : }
866 :
867 :
868 : //-----------------------------------------------------------------------
869 :
870 :
871 :
872 :
873 : static double
874 0 : CalcVectorAngle(double ux, double uy, double vx, double vy)
875 : {
876 0 : double ta = atan2(uy, ux);
877 0 : double tb = atan2(vy, vx);
878 0 : if (tb >= ta)
879 0 : return tb-ta;
880 0 : return 2 * M_PI - (ta-tb);
881 : }
882 :
883 :
884 0 : nsSVGArcConverter::nsSVGArcConverter(const gfxPoint &from,
885 : const gfxPoint &to,
886 : const gfxPoint &radii,
887 : double angle,
888 : bool largeArcFlag,
889 0 : bool sweepFlag)
890 : {
891 0 : const double radPerDeg = M_PI/180.0;
892 :
893 : // Convert to center parameterization as shown in
894 : // http://www.w3.org/TR/SVG/implnote.html
895 0 : mRx = fabs(radii.x);
896 0 : mRy = fabs(radii.y);
897 :
898 0 : mSinPhi = sin(angle*radPerDeg);
899 0 : mCosPhi = cos(angle*radPerDeg);
900 :
901 0 : double x1dash = mCosPhi * (from.x-to.x)/2.0 + mSinPhi * (from.y-to.y)/2.0;
902 0 : double y1dash = -mSinPhi * (from.x-to.x)/2.0 + mCosPhi * (from.y-to.y)/2.0;
903 :
904 : double root;
905 : double numerator = mRx*mRx*mRy*mRy - mRx*mRx*y1dash*y1dash -
906 0 : mRy*mRy*x1dash*x1dash;
907 :
908 0 : if (numerator < 0.0) {
909 : // If mRx , mRy and are such that there is no solution (basically,
910 : // the ellipse is not big enough to reach from 'from' to 'to'
911 : // then the ellipse is scaled up uniformly until there is
912 : // exactly one solution (until the ellipse is just big enough).
913 :
914 : // -> find factor s, such that numerator' with mRx'=s*mRx and
915 : // mRy'=s*mRy becomes 0 :
916 0 : double s = sqrt(1.0 - numerator/(mRx*mRx*mRy*mRy));
917 :
918 0 : mRx *= s;
919 0 : mRy *= s;
920 0 : root = 0.0;
921 :
922 : }
923 : else {
924 : root = (largeArcFlag == sweepFlag ? -1.0 : 1.0) *
925 0 : sqrt( numerator/(mRx*mRx*y1dash*y1dash + mRy*mRy*x1dash*x1dash) );
926 : }
927 :
928 0 : double cxdash = root*mRx*y1dash/mRy;
929 0 : double cydash = -root*mRy*x1dash/mRx;
930 :
931 0 : mC.x = mCosPhi * cxdash - mSinPhi * cydash + (from.x+to.x)/2.0;
932 0 : mC.y = mSinPhi * cxdash + mCosPhi * cydash + (from.y+to.y)/2.0;
933 0 : mTheta = CalcVectorAngle(1.0, 0.0, (x1dash-cxdash)/mRx, (y1dash-cydash)/mRy);
934 : double dtheta = CalcVectorAngle((x1dash-cxdash)/mRx, (y1dash-cydash)/mRy,
935 0 : (-x1dash-cxdash)/mRx, (-y1dash-cydash)/mRy);
936 0 : if (!sweepFlag && dtheta>0)
937 0 : dtheta -= 2.0*M_PI;
938 0 : else if (sweepFlag && dtheta<0)
939 0 : dtheta += 2.0*M_PI;
940 :
941 : // Convert into cubic bezier segments <= 90deg
942 0 : mNumSegs = static_cast<int>(ceil(fabs(dtheta/(M_PI/2.0))));
943 0 : mDelta = dtheta/mNumSegs;
944 0 : mT = 8.0/3.0 * sin(mDelta/4.0) * sin(mDelta/4.0) / sin(mDelta/2.0);
945 :
946 0 : mFrom = from;
947 0 : mSegIndex = 0;
948 0 : }
949 :
950 : bool
951 0 : nsSVGArcConverter::GetNextSegment(gfxPoint *cp1, gfxPoint *cp2, gfxPoint *to)
952 : {
953 0 : if (mSegIndex == mNumSegs) {
954 0 : return false;
955 : }
956 :
957 0 : double cosTheta1 = cos(mTheta);
958 0 : double sinTheta1 = sin(mTheta);
959 0 : double theta2 = mTheta + mDelta;
960 0 : double cosTheta2 = cos(theta2);
961 0 : double sinTheta2 = sin(theta2);
962 :
963 : // a) calculate endpoint of the segment:
964 0 : to->x = mCosPhi * mRx*cosTheta2 - mSinPhi * mRy*sinTheta2 + mC.x;
965 0 : to->y = mSinPhi * mRx*cosTheta2 + mCosPhi * mRy*sinTheta2 + mC.y;
966 :
967 : // b) calculate gradients at start/end points of segment:
968 0 : cp1->x = mFrom.x + mT * ( - mCosPhi * mRx*sinTheta1 - mSinPhi * mRy*cosTheta1);
969 0 : cp1->y = mFrom.y + mT * ( - mSinPhi * mRx*sinTheta1 + mCosPhi * mRy*cosTheta1);
970 :
971 0 : cp2->x = to->x + mT * ( mCosPhi * mRx*sinTheta2 + mSinPhi * mRy*cosTheta2);
972 0 : cp2->y = to->y + mT * ( mSinPhi * mRx*sinTheta2 - mCosPhi * mRy*cosTheta2);
973 :
974 : // do next segment
975 0 : mTheta = theta2;
976 0 : mFrom = *to;
977 0 : ++mSegIndex;
978 :
979 0 : return true;
980 : }
981 :
982 :
983 : // ---------------------------------------------------------------
984 : // nsSVGPathDataParserToInternal
985 :
986 : nsresult
987 0 : nsSVGPathDataParserToInternal::Parse(const nsAString &aValue)
988 : {
989 0 : mPathSegList->Clear();
990 0 : return nsSVGPathDataParser::Parse(aValue);
991 : }
992 :
993 : nsresult
994 0 : nsSVGPathDataParserToInternal::StoreMoveTo(bool absCoords, float x, float y)
995 : {
996 : // Because our IDL compiler doesn't know any better, each seg type constant
997 : // in nsIDOMSVGPathSeg is in a separate enum. This results in "warning:
998 : // enumeral mismatch in conditional expression" under GCC if two bare
999 : // nsIDOMSVGPathSeg constants are used as operands of the ?: operator below.
1000 : // In newer versions of GCC we would be able to turn off this warning using:
1001 : //
1002 : //#pragma GCC diagnostic push
1003 : //#pragma GCC diagnostic ignored "-Wenum-compare"
1004 : //...
1005 : //#pragma GCC diagnostic pop
1006 : //
1007 : // Unfortunately we need to support older versions of GCC. Instead, to
1008 : // eliminate this warning noise being sent to the console, we wrap the
1009 : // operands with PRUint32(...).
1010 :
1011 : PRUint32 type = absCoords ?
1012 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS) :
1013 0 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL);
1014 :
1015 0 : return mPathSegList->AppendSeg(type, x, y);
1016 : }
1017 :
1018 : nsresult
1019 0 : nsSVGPathDataParserToInternal::StoreClosePath()
1020 : {
1021 0 : return mPathSegList->AppendSeg(nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH);
1022 : }
1023 :
1024 : nsresult
1025 0 : nsSVGPathDataParserToInternal::StoreLineTo(bool absCoords, float x, float y)
1026 : {
1027 : PRUint32 type = absCoords ?
1028 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS) :
1029 0 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_REL);
1030 :
1031 0 : return mPathSegList->AppendSeg(type, x, y);
1032 : }
1033 :
1034 : nsresult
1035 0 : nsSVGPathDataParserToInternal::StoreHLineTo(bool absCoords, float x)
1036 : {
1037 : PRUint32 type = absCoords ?
1038 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS) :
1039 0 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL);
1040 :
1041 0 : return mPathSegList->AppendSeg(type, x);
1042 : }
1043 :
1044 : nsresult
1045 0 : nsSVGPathDataParserToInternal::StoreVLineTo(bool absCoords, float y)
1046 : {
1047 : PRUint32 type = absCoords ?
1048 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS) :
1049 0 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL);
1050 :
1051 0 : return mPathSegList->AppendSeg(type, y);
1052 : }
1053 :
1054 : nsresult
1055 0 : nsSVGPathDataParserToInternal::StoreCurveTo(bool absCoords,
1056 : float x, float y,
1057 : float x1, float y1,
1058 : float x2, float y2)
1059 : {
1060 : PRUint32 type = absCoords ?
1061 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS) :
1062 0 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL);
1063 :
1064 0 : return mPathSegList->AppendSeg(type, x1, y1, x2, y2, x, y);
1065 : }
1066 :
1067 : nsresult
1068 0 : nsSVGPathDataParserToInternal::StoreSmoothCurveTo(bool absCoords,
1069 : float x, float y,
1070 : float x2, float y2)
1071 : {
1072 : PRUint32 type = absCoords ?
1073 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS) :
1074 0 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL);
1075 :
1076 0 : return mPathSegList->AppendSeg(type, x2, y2, x, y);
1077 : }
1078 :
1079 : nsresult
1080 0 : nsSVGPathDataParserToInternal::StoreQuadCurveTo(bool absCoords,
1081 : float x, float y,
1082 : float x1, float y1)
1083 : {
1084 : PRUint32 type = absCoords ?
1085 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS) :
1086 0 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL);
1087 :
1088 0 : return mPathSegList->AppendSeg(type, x1, y1, x, y);
1089 : }
1090 :
1091 : nsresult
1092 0 : nsSVGPathDataParserToInternal::StoreSmoothQuadCurveTo(bool absCoords,
1093 : float x, float y)
1094 : {
1095 : PRUint32 type = absCoords ?
1096 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS) :
1097 0 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL);
1098 :
1099 0 : return mPathSegList->AppendSeg(type, x, y);
1100 : }
1101 :
1102 : nsresult
1103 0 : nsSVGPathDataParserToInternal::StoreEllipticalArc(bool absCoords,
1104 : float x, float y,
1105 : float r1, float r2,
1106 : float angle,
1107 : bool largeArcFlag,
1108 : bool sweepFlag)
1109 : {
1110 : PRUint32 type = absCoords ?
1111 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_ARC_ABS) :
1112 0 : PRUint32(nsIDOMSVGPathSeg::PATHSEG_ARC_REL);
1113 :
1114 : // We can only pass floats after 'type', and per the SVG spec for arc,
1115 : // non-zero args are treated at 'true'.
1116 : return mPathSegList->AppendSeg(type, r1, r2, angle,
1117 : largeArcFlag ? 1.0f : 0.0f,
1118 : sweepFlag ? 1.0f : 0.0f,
1119 0 : x, y);
1120 : }
1121 :
|