001: /*
002: *
003: *
004: * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights
005: * Reserved. Use is subject to license terms.
006: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License version
010: * 2 only, as published by the Free Software Foundation.
011: *
012: * This program is distributed in the hope that it will be useful, but
013: * WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * General Public License version 2 for more details (a copy is
016: * included at /legal/license.txt).
017: *
018: * You should have received a copy of the GNU General Public License
019: * version 2 along with this work; if not, write to the Free Software
020: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
021: * 02110-1301 USA
022: *
023: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
024: * Clara, CA 95054 or visit www.sun.com if you need additional
025: * information or have any questions.
026: */
027:
028: /*****************************************************************************
029: * Copyright (C) The Apache Software Foundation. All rights reserved. *
030: * ------------------------------------------------------------------------- *
031: * This software is published under the terms of the Apache Software License *
032: * version 1.1, a copy of which has been included with this distribution in *
033: * the LICENSE file. *
034: *****************************************************************************/package com.sun.perseus.parser;
035:
036: import com.sun.perseus.j2d.Path;
037:
038: /**
039: * The <code>PathParser</code> class converts attributes conforming to the
040: * SVG <a href="http://www.w3.org/TR/SVG11/paths.html#PathDataBNF">path
041: * syntax</a> with the
042: * <a href="http://www.w3.org/TR/SVGMobile/#sec-shapes">limitation</a> of SVG
043: * Tiny which says that SVG Tiny does not support <code>arc to</code> commands.
044: *
045: * @version $Id: PathParser.java,v 1.4 2006/04/21 06:40:37 st125089 Exp $
046: */
047: public class PathParser extends NumberParser {
048: /**
049: * Current x and y positions in the path, set by
050: * commands such as moveTo or lineTo.
051: */
052: private float currentX, currentY;
053:
054: /**
055: * Last moveTo command.
056: */
057: private float lastMoveToX, lastMoveToY;
058:
059: /**
060: * The smoothQCenter point is used for smootg quad curves
061: */
062: private float smoothQCenterX, smoothQCenterY;
063:
064: /**
065: * The smoothQCenter point is used for smooth cubic curves
066: */
067: private float smoothCCenterX, smoothCCenterY;
068:
069: /**
070: * The GeneralPath under construction
071: */
072: private Path p;
073:
074: /**
075: * Returns the current working path. This can be used,
076: * for example, when the parsePath method throws an error
077: * to retrieve the state of the path at the time the error
078: * occured
079: * @return the <code>Path</code> built from the parsed
080: * <code>String</code>
081: */
082: public Path getPath() {
083: return p;
084: }
085:
086: /**
087: * Parses the input <code>String</code> and returns the corresponding
088: * <code>Path</code>.
089: *
090: * @param s the <code>String</code> to parse.
091: * @return the <code>GeneralPath</code> built from the parsed
092: * <code>String</code>.
093: */
094: public Path parsePoints(final String s) {
095: setString(s);
096: p = preparePath();
097:
098: setString(s);
099:
100: current = read();
101:
102: skipSpaces();
103: if (current == -1) {
104: // No coordinate pair
105: return p;
106: }
107:
108: // Initial moveTo
109: float x = parseNumber();
110: skipCommaSpaces();
111: float y = parseNumber();
112: p.moveTo(x, y);
113: lastMoveToX = x;
114: lastMoveToY = y;
115:
116: while (current != -1) {
117: skipSpaces();
118: if (current != -1) {
119: skipCommaSpaces();
120: x = parseNumber();
121: skipCommaSpaces();
122: y = parseNumber();
123: p.lineTo(x, y);
124: }
125: }
126:
127: return p;
128: }
129:
130: /**
131: * Pre-parses the path data to allocate the optimal number of commands and data
132: * for the path.
133: *
134: * @return p a path with the proper capacity for the comming path.
135: */
136: protected Path preparePath() {
137: int commandCapacity = 0;
138: int dataCapacity = 0;
139:
140: current = read();
141:
142: while (current != -1) {
143: skipCommaSpaces();
144: switch (current) {
145: case 'z':
146: case 'Z':
147: commandCapacity++;
148: break;
149: case 'm':
150: case 'l':
151: case 'M':
152: case 'L':
153: case 'h':
154: case 'H':
155: case 'v':
156: case 'V':
157: commandCapacity++;
158: dataCapacity += 1;
159: break;
160: case 'c':
161: case 'C':
162: case 's':
163: case 'S':
164: commandCapacity++;
165: dataCapacity += 3;
166: break;
167: case 'q':
168: case 'Q':
169: case 't':
170: case 'T':
171: commandCapacity++;
172: dataCapacity += 2;
173: break;
174: default:
175: break;
176: }
177: current = read();
178: }
179:
180: return new Path(commandCapacity, dataCapacity);
181: }
182:
183: /**
184: * Parses the input <code>String</code> and returns the corresponding
185: * <code>Path</code> if no error is found. If an error occurs,
186: * this method throws an <code>IllegalArgumentException</code>.
187: *
188: * @param s the <code>String</code> to parse.
189: * @return the <code>Path</code> built from the parsed
190: * <code>String</code>
191: */
192: public Path parsePath(final String s) {
193: setString(s);
194: p = preparePath();
195:
196: setString(s);
197:
198: currentX = 0;
199: currentY = 0;
200: smoothQCenterX = 0;
201: smoothQCenterY = 0;
202: smoothCCenterX = 0;
203: smoothCCenterY = 0;
204:
205: current = read();
206: skipSpaces();
207:
208: // Multiple coordinate pairs after a moveto
209: // are like a moveto followed by lineto
210: switch (current) {
211: case 'm':
212: parsem();
213: parsel();
214: break;
215: case 'M':
216: parseM();
217: parseL();
218: break;
219: case -1:
220: //an empty path is valid.
221: break;
222: default:
223: throw new IllegalArgumentException();
224: }
225:
226: loop: for (;;) {
227: switch (current) {
228: case 0xD:
229: case 0xA:
230: case 0x20:
231: case 0x9:
232: current = read();
233: break;
234: case 'z':
235: case 'Z':
236: current = read();
237: p.close();
238: currentX = lastMoveToX;
239: currentY = lastMoveToY;
240: break;
241: case 'm':
242: parsem();
243: case 'l':
244: parsel();
245: break;
246: case 'M':
247: parseM();
248: case 'L':
249: parseL();
250: break;
251: case 'h':
252: parseh();
253: break;
254: case 'H':
255: parseH();
256: break;
257: case 'v':
258: parsev();
259: break;
260: case 'V':
261: parseV();
262: break;
263: case 'c':
264: parsec();
265: break;
266: case 'C':
267: parseC();
268: break;
269: case 'q':
270: parseq();
271: break;
272: case 'Q':
273: parseQ();
274: break;
275: case 's':
276: parses();
277: break;
278: case 'S':
279: parseS();
280: break;
281: case 't':
282: parset();
283: break;
284: case 'T':
285: parseT();
286: break;
287: case -1:
288: break loop;
289: default:
290: throw new IllegalArgumentException();
291: }
292:
293: }
294:
295: skipSpaces();
296: if (current != -1) {
297: throw new IllegalArgumentException();
298: }
299:
300: return p;
301: }
302:
303: /**
304: * Parses a 'm' command.
305: */
306: protected final void parsem() {
307: current = read();
308: skipSpaces();
309:
310: final float x = parseNumber();
311: skipCommaSpaces();
312: final float y = parseNumber();
313:
314: currentX += x;
315: smoothQCenterX = currentX;
316: smoothCCenterX = currentX;
317: currentY += y;
318: smoothQCenterY = currentY;
319: smoothCCenterY = currentY;
320: p.moveTo(smoothCCenterX, smoothCCenterY);
321: lastMoveToX = smoothCCenterX;
322: lastMoveToY = smoothCCenterY;
323:
324: skipCommaSpaces();
325: }
326:
327: /**
328: * Parses a 'l' command.
329: */
330: protected final void parsel() {
331: if (current == 'l') {
332: current = read();
333: }
334: skipSpaces();
335: for (;;) {
336: switch (current) {
337: case '+':
338: case '-':
339: case '.':
340: case '0':
341: case '1':
342: case '2':
343: case '3':
344: case '4':
345: case '5':
346: case '6':
347: case '7':
348: case '8':
349: case '9':
350: float x = parseNumber();
351: skipCommaSpaces();
352: float y = parseNumber();
353:
354: currentX += x;
355: smoothQCenterX = currentX;
356: smoothCCenterX = currentX;
357:
358: currentY += y;
359: smoothQCenterY = currentY;
360: smoothCCenterY = currentY;
361: p.lineTo(smoothCCenterX, smoothCCenterY);
362: break;
363: default:
364: return;
365: }
366: skipCommaSpaces();
367: }
368: }
369:
370: /**
371: * Parses a 'M' command.
372: */
373: protected final void parseM() {
374: current = read();
375: skipSpaces();
376:
377: float x = parseNumber();
378: skipCommaSpaces();
379: float y = parseNumber();
380:
381: currentX = x;
382: smoothQCenterX = x;
383: smoothCCenterX = x;
384:
385: currentY = y;
386: smoothQCenterY = y;
387: smoothCCenterY = y;
388: p.moveTo(x, y);
389: lastMoveToX = x;
390: lastMoveToY = y;
391:
392: skipCommaSpaces();
393: }
394:
395: /**
396: * Parses a 'L' command.
397: */
398: protected final void parseL() {
399: if (current == 'L') {
400: current = read();
401: }
402: skipSpaces();
403: for (;;) {
404: switch (current) {
405: case '+':
406: case '-':
407: case '.':
408: case '0':
409: case '1':
410: case '2':
411: case '3':
412: case '4':
413: case '5':
414: case '6':
415: case '7':
416: case '8':
417: case '9':
418: float x = parseNumber();
419: skipCommaSpaces();
420: float y = parseNumber();
421:
422: currentX = x;
423: smoothQCenterX = x;
424: smoothCCenterX = x;
425:
426: currentY = y;
427: smoothQCenterY = y;
428: smoothCCenterY = y;
429:
430: p.lineTo(smoothCCenterX, smoothCCenterY);
431: break;
432: default:
433: return;
434: }
435: skipCommaSpaces();
436: }
437: }
438:
439: /**
440: * Parses a 'h' command.
441: */
442: protected final void parseh() {
443: current = read();
444: skipSpaces();
445:
446: for (;;) {
447: switch (current) {
448: case '+':
449: case '-':
450: case '.':
451: case '0':
452: case '1':
453: case '2':
454: case '3':
455: case '4':
456: case '5':
457: case '6':
458: case '7':
459: case '8':
460: case '9':
461: float x = parseNumber();
462: currentX += x;
463: smoothQCenterX = currentX;
464: smoothCCenterX = currentX;
465:
466: smoothQCenterY = currentY;
467: smoothCCenterY = currentY;
468: p.lineTo(smoothCCenterX, smoothCCenterY);
469: break;
470: default:
471: return;
472: }
473: skipCommaSpaces();
474: }
475: }
476:
477: /**
478: * Parses a 'H' command.
479: */
480: protected final void parseH() {
481: current = read();
482: skipSpaces();
483:
484: for (;;) {
485: switch (current) {
486: case '+':
487: case '-':
488: case '.':
489: case '0':
490: case '1':
491: case '2':
492: case '3':
493: case '4':
494: case '5':
495: case '6':
496: case '7':
497: case '8':
498: case '9':
499: float x = parseNumber();
500: currentX = x;
501: smoothQCenterX = x;
502: smoothCCenterX = x;
503:
504: smoothQCenterY = currentY;
505: smoothCCenterY = currentY;
506: p.lineTo(smoothCCenterX, smoothCCenterY);
507: break;
508: default:
509: return;
510: }
511: skipCommaSpaces();
512: }
513: }
514:
515: /**
516: * Parses a 'v' command.
517: */
518: protected final void parsev() {
519: current = read();
520: skipSpaces();
521:
522: for (;;) {
523: switch (current) {
524: case '+':
525: case '-':
526: case '.':
527: case '0':
528: case '1':
529: case '2':
530: case '3':
531: case '4':
532: case '5':
533: case '6':
534: case '7':
535: case '8':
536: case '9':
537: float y = parseNumber();
538: smoothQCenterX = currentX;
539: smoothCCenterX = currentX;
540:
541: currentY += y;
542: smoothQCenterY = currentY;
543: smoothCCenterY = currentY;
544: p.lineTo(smoothCCenterX, smoothCCenterY);
545: break;
546: default:
547: return;
548: }
549: skipCommaSpaces();
550: }
551: }
552:
553: /**
554: * Parses a 'V' command.
555: */
556: protected final void parseV() {
557: current = read();
558: skipSpaces();
559:
560: for (;;) {
561: switch (current) {
562: case '+':
563: case '-':
564: case '.':
565: case '0':
566: case '1':
567: case '2':
568: case '3':
569: case '4':
570: case '5':
571: case '6':
572: case '7':
573: case '8':
574: case '9':
575: float y = parseNumber();
576: smoothQCenterX = currentX;
577: smoothCCenterX = currentX;
578:
579: currentY = y;
580: smoothQCenterY = y;
581: smoothCCenterY = y;
582: p.lineTo(smoothCCenterX, smoothCCenterY);
583: break;
584: default:
585: return;
586: }
587: skipCommaSpaces();
588: }
589: }
590:
591: /**
592: * Parses a 'c' command.
593: */
594: protected final void parsec() {
595: current = read();
596: skipSpaces();
597:
598: for (;;) {
599: switch (current) {
600: default:
601: return;
602: case '+':
603: case '-':
604: case '.':
605: case '0':
606: case '1':
607: case '2':
608: case '3':
609: case '4':
610: case '5':
611: case '6':
612: case '7':
613: case '8':
614: case '9':
615: }
616:
617: float x1 = parseNumber();
618: skipCommaSpaces();
619: float y1 = parseNumber();
620: skipCommaSpaces();
621: float x2 = parseNumber();
622: skipCommaSpaces();
623: float y2 = parseNumber();
624: skipCommaSpaces();
625: float x = parseNumber();
626: skipCommaSpaces();
627: float y = parseNumber();
628:
629: smoothCCenterX = currentX + x2;
630: smoothCCenterY = currentY + y2;
631: smoothQCenterX = currentX + x;
632: smoothQCenterY = currentY + y;
633: p.curveTo(currentX + x1, currentY + y1, smoothCCenterX,
634: smoothCCenterY, smoothQCenterX, smoothQCenterY);
635: currentX = smoothQCenterX;
636: currentY = smoothQCenterY;
637: skipCommaSpaces();
638: }
639: }
640:
641: /**
642: * Parses a 'C' command.
643: */
644: protected final void parseC() {
645: current = read();
646: skipSpaces();
647:
648: for (;;) {
649: switch (current) {
650: default:
651: return;
652: case '+':
653: case '-':
654: case '.':
655: case '0':
656: case '1':
657: case '2':
658: case '3':
659: case '4':
660: case '5':
661: case '6':
662: case '7':
663: case '8':
664: case '9':
665: }
666:
667: float x1 = parseNumber();
668: skipCommaSpaces();
669: float y1 = parseNumber();
670: skipCommaSpaces();
671: float x2 = parseNumber();
672: skipCommaSpaces();
673: float y2 = parseNumber();
674: skipCommaSpaces();
675: float x = parseNumber();
676: skipCommaSpaces();
677: float y = parseNumber();
678:
679: smoothCCenterX = x2;
680: smoothCCenterY = y2;
681: currentX = x;
682: currentY = y;
683: p.curveTo(x1, y1, smoothCCenterX, smoothCCenterY, currentX,
684: currentY);
685: smoothQCenterX = currentX;
686: smoothQCenterY = currentY;
687: skipCommaSpaces();
688: }
689: }
690:
691: /**
692: * Parses a 'q' command.
693: */
694: protected final void parseq() {
695: current = read();
696: skipSpaces();
697:
698: for (;;) {
699: switch (current) {
700: default:
701: return;
702: case '+':
703: case '-':
704: case '.':
705: case '0':
706: case '1':
707: case '2':
708: case '3':
709: case '4':
710: case '5':
711: case '6':
712: case '7':
713: case '8':
714: case '9':
715: }
716:
717: float x1 = parseNumber();
718: skipCommaSpaces();
719: float y1 = parseNumber();
720: skipCommaSpaces();
721: float x = parseNumber();
722: skipCommaSpaces();
723: float y = parseNumber();
724:
725: smoothQCenterX = currentX + x1;
726: smoothQCenterY = currentY + y1;
727: currentX += x;
728: currentY += y;
729: p
730: .quadTo(smoothQCenterX, smoothQCenterY, currentX,
731: currentY);
732: smoothCCenterX = currentX;
733: smoothCCenterY = currentY;
734:
735: skipCommaSpaces();
736: }
737: }
738:
739: /**
740: * Parses a 'Q' command.
741: */
742: protected final void parseQ() {
743: current = read();
744: skipSpaces();
745:
746: for (;;) {
747: switch (current) {
748: default:
749: return;
750: case '+':
751: case '-':
752: case '.':
753: case '0':
754: case '1':
755: case '2':
756: case '3':
757: case '4':
758: case '5':
759: case '6':
760: case '7':
761: case '8':
762: case '9':
763: }
764:
765: float x1 = parseNumber();
766: skipCommaSpaces();
767: float y1 = parseNumber();
768: skipCommaSpaces();
769: float x = parseNumber();
770: skipCommaSpaces();
771: float y = parseNumber();
772:
773: smoothQCenterX = x1;
774: smoothQCenterY = y1;
775: currentX = x;
776: currentY = y;
777: p
778: .quadTo(smoothQCenterX, smoothQCenterY, currentX,
779: currentY);
780: smoothCCenterX = currentX;
781: smoothCCenterY = currentY;
782: skipCommaSpaces();
783: }
784: }
785:
786: /**
787: * Parses a 's' command.
788: */
789: protected final void parses() {
790: current = read();
791: skipSpaces();
792:
793: for (;;) {
794: switch (current) {
795: default:
796: return;
797: case '+':
798: case '-':
799: case '.':
800: case '0':
801: case '1':
802: case '2':
803: case '3':
804: case '4':
805: case '5':
806: case '6':
807: case '7':
808: case '8':
809: case '9':
810: }
811:
812: float x2 = parseNumber();
813: skipCommaSpaces();
814: float y2 = parseNumber();
815: skipCommaSpaces();
816: float x = parseNumber();
817: skipCommaSpaces();
818: float y = parseNumber();
819:
820: float smoothX = currentX * 2 - smoothCCenterX;
821: float smoothY = currentY * 2 - smoothCCenterY;
822: smoothCCenterX = currentX + x2;
823: smoothCCenterY = currentY + y2;
824: currentX += x;
825: currentY += y;
826:
827: p.curveTo(smoothX, smoothY, smoothCCenterX, smoothCCenterY,
828: currentX, currentY);
829:
830: smoothQCenterX = currentX;
831: smoothQCenterY = currentY;
832: skipCommaSpaces();
833: }
834: }
835:
836: /**
837: * Parses a 'S' command.
838: */
839: protected final void parseS() {
840: current = read();
841: skipSpaces();
842:
843: for (;;) {
844: switch (current) {
845: default:
846: return;
847: case '+':
848: case '-':
849: case '.':
850: case '0':
851: case '1':
852: case '2':
853: case '3':
854: case '4':
855: case '5':
856: case '6':
857: case '7':
858: case '8':
859: case '9':
860: }
861:
862: float x2 = parseNumber();
863: skipCommaSpaces();
864: float y2 = parseNumber();
865: skipCommaSpaces();
866: float x = parseNumber();
867: skipCommaSpaces();
868: float y = parseNumber();
869:
870: float smoothX = currentX * 2 - smoothCCenterX;
871: float smoothY = currentY * 2 - smoothCCenterY;
872: currentX = x;
873: currentY = y;
874: p.curveTo(smoothX, smoothY, x2, y2, currentX, currentY);
875: smoothCCenterX = x2;
876: smoothCCenterY = y2;
877: smoothQCenterX = currentX;
878: smoothQCenterY = currentY;
879:
880: skipCommaSpaces();
881: }
882: }
883:
884: /**
885: * Parses a 't' command.
886: */
887: protected final void parset() {
888: current = read();
889: skipSpaces();
890:
891: for (;;) {
892: switch (current) {
893: default:
894: return;
895: case '+':
896: case '-':
897: case '.':
898: case '0':
899: case '1':
900: case '2':
901: case '3':
902: case '4':
903: case '5':
904: case '6':
905: case '7':
906: case '8':
907: case '9':
908: }
909:
910: float x = parseNumber();
911: skipCommaSpaces();
912: float y = parseNumber();
913:
914: smoothQCenterX = currentX * 2 - smoothQCenterX;
915: smoothQCenterY = currentY * 2 - smoothQCenterY;
916: currentX += x;
917: currentY += y;
918: p
919: .quadTo(smoothQCenterX, smoothQCenterY, currentX,
920: currentY);
921: smoothCCenterX = currentX;
922: smoothCCenterY = currentY;
923: skipCommaSpaces();
924: }
925: }
926:
927: /**
928: * Parses a 'T' command.
929: */
930: protected final void parseT() {
931: current = read();
932: skipSpaces();
933:
934: for (;;) {
935: switch (current) {
936: default:
937: return;
938: case '+':
939: case '-':
940: case '.':
941: case '0':
942: case '1':
943: case '2':
944: case '3':
945: case '4':
946: case '5':
947: case '6':
948: case '7':
949: case '8':
950: case '9':
951: }
952:
953: float x = parseNumber();
954: skipCommaSpaces();
955: float y = parseNumber();
956:
957: smoothQCenterX = currentX * 2 - smoothQCenterX;
958: smoothQCenterY = currentY * 2 - smoothQCenterY;
959: currentX = x;
960: currentY = y;
961: p
962: .quadTo(smoothQCenterX, smoothQCenterY, currentX,
963: currentY);
964: smoothCCenterX = currentX;
965: smoothCCenterY = currentY;
966: skipCommaSpaces();
967: }
968: }
969: }
|