001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.bridge;
020:
021: import java.util.ArrayList;
022: import java.util.List;
023:
024: import org.apache.batik.anim.AbstractAnimation;
025: import org.apache.batik.anim.AnimationEngine;
026: import org.apache.batik.dom.anim.AnimationTarget;
027: import org.apache.batik.anim.MotionAnimation;
028: import org.apache.batik.anim.values.AnimatableMotionPointValue;
029: import org.apache.batik.anim.values.AnimatableValue;
030: import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
031: import org.apache.batik.dom.svg.SVGAnimatedPathDataSupport;
032: import org.apache.batik.dom.svg.SVGOMElement;
033: import org.apache.batik.dom.svg.SVGOMPathElement;
034: import org.apache.batik.dom.util.XLinkSupport;
035: import org.apache.batik.parser.AWTPathProducer;
036: import org.apache.batik.parser.AngleHandler;
037: import org.apache.batik.parser.AngleParser;
038: import org.apache.batik.parser.LengthArrayProducer;
039: import org.apache.batik.parser.LengthPairListParser;
040: import org.apache.batik.parser.PathParser;
041: import org.apache.batik.parser.ParseException;
042:
043: import org.w3c.dom.Element;
044: import org.w3c.dom.Node;
045: import org.w3c.dom.svg.SVGAngle;
046:
047: /**
048: * Bridge class for the 'animateMotion' animation element.
049: *
050: * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
051: * @version $Id: SVGAnimateMotionElementBridge.java 501922 2007-01-31 17:47:47Z dvholten $
052: */
053: public class SVGAnimateMotionElementBridge extends
054: SVGAnimateElementBridge {
055:
056: /**
057: * Returns 'animateMotion'.
058: */
059: public String getLocalName() {
060: return SVG_ANIMATE_MOTION_TAG;
061: }
062:
063: /**
064: * Returns a new instance of this bridge.
065: */
066: public Bridge getInstance() {
067: return new SVGAnimateMotionElementBridge();
068: }
069:
070: /**
071: * Creates the animation object for the animation element.
072: */
073: protected AbstractAnimation createAnimation(AnimationTarget target) {
074: animationType = AnimationEngine.ANIM_TYPE_OTHER;
075: attributeLocalName = "motion";
076:
077: AnimatableValue from = parseLengthPair(SVG_FROM_ATTRIBUTE);
078: AnimatableValue to = parseLengthPair(SVG_TO_ATTRIBUTE);
079: AnimatableValue by = parseLengthPair(SVG_BY_ATTRIBUTE);
080:
081: boolean rotateAuto = false, rotateAutoReverse = false;
082: float rotateAngle = 0;
083: short rotateAngleUnit = SVGAngle.SVG_ANGLETYPE_UNKNOWN;
084: String rotateString = element.getAttributeNS(null,
085: SVG_ROTATE_ATTRIBUTE);
086: if (rotateString.length() != 0) {
087: if (rotateString.equals("auto")) {
088: rotateAuto = true;
089: } else if (rotateString.equals("auto-reverse")) {
090: rotateAuto = true;
091: rotateAutoReverse = true;
092: } else {
093: class Handler implements AngleHandler {
094: float theAngle;
095: short theUnit = SVGAngle.SVG_ANGLETYPE_UNSPECIFIED;
096:
097: public void startAngle() throws ParseException {
098: }
099:
100: public void angleValue(float v)
101: throws ParseException {
102: theAngle = v;
103: }
104:
105: public void deg() throws ParseException {
106: theUnit = SVGAngle.SVG_ANGLETYPE_DEG;
107: }
108:
109: public void grad() throws ParseException {
110: theUnit = SVGAngle.SVG_ANGLETYPE_GRAD;
111: }
112:
113: public void rad() throws ParseException {
114: theUnit = SVGAngle.SVG_ANGLETYPE_RAD;
115: }
116:
117: public void endAngle() throws ParseException {
118: }
119: }
120: AngleParser ap = new AngleParser();
121: Handler h = new Handler();
122: ap.setAngleHandler(h);
123: try {
124: ap.parse(rotateString);
125: } catch (ParseException pEx) {
126: throw new BridgeException(
127: ctx,
128: element,
129: pEx,
130: ErrorConstants.ERR_ATTRIBUTE_VALUE_MALFORMED,
131: new Object[] { SVG_ROTATE_ATTRIBUTE,
132: rotateString });
133: }
134: rotateAngle = h.theAngle;
135: rotateAngleUnit = h.theUnit;
136: }
137: }
138: return new MotionAnimation(timedElement, this , parseCalcMode(),
139: parseKeyTimes(), parseKeySplines(), parseAdditive(),
140: parseAccumulate(), parseValues(), from, to, by,
141: parsePath(), parseKeyPoints(), rotateAuto,
142: rotateAutoReverse, rotateAngle, rotateAngleUnit);
143: }
144:
145: /**
146: * Returns the parsed 'path' attribute (or the path from a referencing
147: * 'mpath') from the animation element.
148: */
149: protected ExtendedGeneralPath parsePath() {
150: Node n = element.getFirstChild();
151: while (n != null) {
152: if (n.getNodeType() == Node.ELEMENT_NODE
153: && SVG_NAMESPACE_URI.equals(n.getNamespaceURI())
154: && SVG_MPATH_TAG.equals(n.getLocalName())) {
155: String uri = XLinkSupport.getXLinkHref((Element) n);
156: Element path = ctx.getReferencedElement(element, uri);
157: if (!SVG_NAMESPACE_URI.equals(path.getNamespaceURI())
158: || !SVG_PATH_TAG.equals(path.getLocalName())) {
159: throw new BridgeException(ctx, element,
160: ErrorConstants.ERR_URI_BAD_TARGET,
161: new Object[] { uri });
162: }
163: SVGOMPathElement pathElt = (SVGOMPathElement) path;
164: AWTPathProducer app = new AWTPathProducer();
165: SVGAnimatedPathDataSupport.handlePathSegList(pathElt
166: .getPathSegList(), app);
167: return (ExtendedGeneralPath) app.getShape();
168: }
169: n = n.getNextSibling();
170: }
171: String pathString = element.getAttributeNS(null,
172: SVG_PATH_ATTRIBUTE);
173: if (pathString.length() == 0) {
174: return null;
175: }
176: try {
177: AWTPathProducer app = new AWTPathProducer();
178: PathParser pp = new PathParser();
179: pp.setPathHandler(app);
180: pp.parse(pathString);
181: return (ExtendedGeneralPath) app.getShape();
182: } catch (ParseException pEx) {
183: throw new BridgeException(ctx, element, pEx,
184: ErrorConstants.ERR_ATTRIBUTE_VALUE_MALFORMED,
185: new Object[] { SVG_PATH_ATTRIBUTE, pathString });
186: }
187: }
188:
189: /**
190: * Returns the parsed 'keyPoints' attribute from the animation element.
191: */
192: protected float[] parseKeyPoints() {
193: String keyPointsString = element.getAttributeNS(null,
194: SVG_KEY_POINTS_ATTRIBUTE);
195: int len = keyPointsString.length();
196: if (len == 0) {
197: return null;
198: }
199: List keyPoints = new ArrayList(7);
200: int i = 0, start = 0, end;
201: char c;
202: outer: while (i < len) {
203: while (keyPointsString.charAt(i) == ' ') {
204: i++;
205: if (i == len) {
206: break outer;
207: }
208: }
209: start = i++;
210: if (i != len) {
211: c = keyPointsString.charAt(i);
212: while (c != ' ' && c != ';' && c != ',') {
213: i++;
214: if (i == len) {
215: break;
216: }
217: c = keyPointsString.charAt(i);
218: }
219: }
220: end = i++;
221: try {
222: float keyPointCoord = Float.parseFloat(keyPointsString
223: .substring(start, end));
224: keyPoints.add(new Float(keyPointCoord));
225: } catch (NumberFormatException nfEx) {
226: throw new BridgeException(ctx, element, nfEx,
227: ErrorConstants.ERR_ATTRIBUTE_VALUE_MALFORMED,
228: new Object[] { SVG_KEY_POINTS_ATTRIBUTE,
229: keyPointsString });
230: }
231: }
232: len = keyPoints.size();
233: float[] ret = new float[len];
234: for (int j = 0; j < len; j++) {
235: ret[j] = ((Float) keyPoints.get(j)).floatValue();
236: }
237: return ret;
238: }
239:
240: /**
241: * Returns the calcMode that the animation defaults to if none is specified.
242: */
243: protected int getDefaultCalcMode() {
244: return MotionAnimation.CALC_MODE_PACED;
245: }
246:
247: /**
248: * Returns the parsed 'values' attribute from the animation element.
249: */
250: protected AnimatableValue[] parseValues() {
251: String valuesString = element.getAttributeNS(null,
252: SVG_VALUES_ATTRIBUTE);
253: int len = valuesString.length();
254: if (len == 0) {
255: return null;
256: }
257: return parseValues(valuesString);
258: }
259:
260: protected AnimatableValue[] parseValues(String s) {
261: try {
262: LengthPairListParser lplp = new LengthPairListParser();
263: LengthArrayProducer lap = new LengthArrayProducer();
264: lplp.setLengthListHandler(lap);
265: lplp.parse(s);
266: short[] types = lap.getLengthTypeArray();
267: float[] values = lap.getLengthValueArray();
268: AnimatableValue[] ret = new AnimatableValue[types.length / 2];
269: for (int i = 0; i < types.length; i += 2) {
270: float x = animationTarget.svgToUserSpace(values[i],
271: types[i],
272: AnimationTarget.PERCENTAGE_VIEWPORT_WIDTH);
273: float y = animationTarget.svgToUserSpace(values[i + 1],
274: types[i + 1],
275: AnimationTarget.PERCENTAGE_VIEWPORT_HEIGHT);
276: ret[i / 2] = new AnimatableMotionPointValue(
277: animationTarget, x, y, 0);
278: }
279: return ret;
280: } catch (ParseException pEx) {
281: throw new BridgeException(ctx, element, pEx,
282: ErrorConstants.ERR_ATTRIBUTE_VALUE_MALFORMED,
283: new Object[] { SVG_VALUES_ATTRIBUTE, s });
284: }
285: }
286:
287: /**
288: * Parses a single comma-separated length pair.
289: */
290: protected AnimatableValue parseLengthPair(String ln) {
291: String s = element.getAttributeNS(null, ln);
292: if (s.length() == 0) {
293: return null;
294: }
295: return parseValues(s)[0];
296: }
297:
298: // AnimatableElement /////////////////////////////////////////////////////
299:
300: /**
301: * Returns the underlying value of the animated attribute. Used for
302: * composition of additive animations.
303: */
304: public AnimatableValue getUnderlyingValue() {
305: return new AnimatableMotionPointValue(animationTarget, 0f, 0f,
306: 0f);
307: }
308:
309: /**
310: * Parses the animation element's target attributes and adds it to the
311: * document's AnimationEngine.
312: */
313: protected void initializeAnimation() {
314: // Determine the target element.
315: String uri = XLinkSupport.getXLinkHref(element);
316: Node t;
317: if (uri.length() == 0) {
318: t = element.getParentNode();
319: } else {
320: t = ctx.getReferencedElement(element, uri);
321: if (t.getOwnerDocument() != element.getOwnerDocument()) {
322: throw new BridgeException(ctx, element,
323: ErrorConstants.ERR_URI_BAD_TARGET,
324: new Object[] { uri });
325: }
326: }
327: animationTarget = null;
328: if (t instanceof SVGOMElement) {
329: targetElement = (SVGOMElement) t;
330: animationTarget = targetElement;
331: }
332: if (animationTarget == null) {
333: throw new BridgeException(ctx, element,
334: ErrorConstants.ERR_URI_BAD_TARGET,
335: new Object[] { uri });
336: }
337:
338: // Add the animation.
339: timedElement = createTimedElement();
340: animation = createAnimation(animationTarget);
341: eng.addAnimation(animationTarget,
342: AnimationEngine.ANIM_TYPE_OTHER, attributeNamespaceURI,
343: attributeLocalName, animation);
344: }
345: }
|