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.awt.Shape;
022: import java.awt.geom.AffineTransform;
023: import java.awt.geom.GeneralPath;
024:
025: import org.apache.batik.dom.util.XLinkSupport;
026: import org.apache.batik.gvt.text.TextPath;
027: import org.apache.batik.parser.AWTPathProducer;
028: import org.apache.batik.parser.ParseException;
029: import org.apache.batik.parser.PathParser;
030: import org.w3c.dom.Element;
031:
032: /**
033: * Bridge class for the <textPath> element.
034: *
035: * @author <a href="mailto:bella.robinson@cmis.csiro.au">Bella Robinson</a>
036: * @version $Id: SVGTextPathElementBridge.java 501922 2007-01-31 17:47:47Z dvholten $
037: */
038: public class SVGTextPathElementBridge extends
039: AnimatableGenericSVGBridge implements ErrorConstants {
040:
041: /**
042: * Constructs a new bridge for the <textPath> element.
043: */
044: public SVGTextPathElementBridge() {
045: }
046:
047: /**
048: * Returns 'textPath'.
049: */
050: public String getLocalName() {
051: return SVG_TEXT_PATH_TAG;
052: }
053:
054: public void handleElement(BridgeContext ctx, Element e) {
055: // We don't want to take over from the text content element.
056: }
057:
058: /**
059: * Creates a TextPath object that represents the path along which the text
060: * is to be rendered.
061: *
062: * @param ctx The bridge context.
063: * @param textPathElement The <textPath> element.
064: *
065: * @return The new TextPath.
066: */
067: public TextPath createTextPath(BridgeContext ctx,
068: Element textPathElement) {
069:
070: // get the referenced element
071: String uri = XLinkSupport.getXLinkHref(textPathElement);
072: Element pathElement = ctx.getReferencedElement(textPathElement,
073: uri);
074:
075: if ((pathElement == null)
076: || (!SVG_NAMESPACE_URI.equals(pathElement
077: .getNamespaceURI()))
078: || (!pathElement.getLocalName().equals(SVG_PATH_TAG))) {
079: // couldn't find the referenced element
080: // or the referenced element was not a path
081: throw new BridgeException(ctx, textPathElement,
082: ERR_URI_BAD_TARGET, new Object[] { uri });
083: }
084:
085: // construct a shape for the referenced path element
086: String s = pathElement.getAttributeNS(null, SVG_D_ATTRIBUTE);
087: Shape pathShape = null;
088: if (s.length() != 0) {
089: AWTPathProducer app = new AWTPathProducer();
090: app.setWindingRule(CSSUtilities
091: .convertFillRule(pathElement));
092: try {
093: PathParser pathParser = new PathParser();
094: pathParser.setPathHandler(app);
095: pathParser.parse(s);
096: } catch (ParseException pEx) {
097: throw new BridgeException(ctx, pathElement, pEx,
098: ERR_ATTRIBUTE_VALUE_MALFORMED,
099: new Object[] { SVG_D_ATTRIBUTE });
100: } finally {
101: pathShape = app.getShape();
102: }
103: } else {
104: throw new BridgeException(ctx, pathElement,
105: ERR_ATTRIBUTE_MISSING,
106: new Object[] { SVG_D_ATTRIBUTE });
107: }
108:
109: // if the reference path element has a transform apply the transform
110: // to the path shape
111: s = pathElement.getAttributeNS(null, SVG_TRANSFORM_ATTRIBUTE);
112: if (s.length() != 0) {
113: AffineTransform tr = SVGUtilities.convertTransform(
114: pathElement, SVG_TRANSFORM_ATTRIBUTE, s, ctx);
115: pathShape = tr.createTransformedShape(pathShape);
116: }
117:
118: // create the TextPath object that we are going to return
119: TextPath textPath = new TextPath(new GeneralPath(pathShape));
120:
121: // set the start offset if specified
122: s = textPathElement.getAttributeNS(null,
123: SVG_START_OFFSET_ATTRIBUTE);
124: if (s.length() > 0) {
125: float startOffset = 0;
126: int percentIndex = s.indexOf('%');
127: if (percentIndex != -1) {
128: // its a percentage of the length of the path
129: float pathLength = textPath.lengthOfPath();
130: String percentString = s.substring(0, percentIndex);
131: float startOffsetPercent = 0;
132: try {
133: startOffsetPercent = SVGUtilities
134: .convertSVGNumber(percentString);
135: } catch (NumberFormatException e) {
136: startOffsetPercent = -1;
137: }
138: if (startOffsetPercent < 0) {
139: throw new BridgeException(ctx, textPathElement,
140: ERR_ATTRIBUTE_VALUE_MALFORMED,
141: new Object[] { SVG_START_OFFSET_ATTRIBUTE,
142: s });
143: }
144: startOffset = (float) (startOffsetPercent * pathLength / 100.0);
145:
146: } else {
147: // its an absolute length
148: UnitProcessor.Context uctx = UnitProcessor
149: .createContext(ctx, textPathElement);
150: startOffset = UnitProcessor.svgOtherLengthToUserSpace(
151: s, SVG_START_OFFSET_ATTRIBUTE, uctx);
152: }
153: textPath.setStartOffset(startOffset);
154: }
155:
156: return textPath;
157: }
158: }
|