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.text.AttributedCharacterIterator;
022:
023: import org.apache.batik.dom.AbstractNode;
024: import org.apache.batik.dom.svg.SVGOMDocument;
025: import org.apache.batik.dom.util.XLinkSupport;
026: import org.apache.batik.gvt.font.Glyph;
027: import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
028: import org.apache.batik.gvt.text.TextPaintInfo;
029: import org.apache.batik.util.XMLConstants;
030: import org.w3c.dom.Element;
031: import org.w3c.dom.Node;
032: import org.w3c.dom.NodeList;
033:
034: /**
035: * Bridge class for the <altGlyph> element.
036: *
037: * @author <a href="mailto:bella.robinson@cmis.csiro.au">Bella Robinson</a>
038: * @version $Id: SVGAltGlyphElementBridge.java 475685 2006-11-16 11:16:05Z cam $
039: */
040: public class SVGAltGlyphElementBridge extends AbstractSVGBridge
041: implements ErrorConstants {
042:
043: public static final AttributedCharacterIterator.Attribute PAINT_INFO = GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO;
044:
045: /**
046: * Constructs a new bridge for the <altGlyph> element.
047: */
048: public SVGAltGlyphElementBridge() {
049: }
050:
051: /**
052: * Returns 'altGlyph'.
053: */
054: public String getLocalName() {
055: return SVG_ALT_GLYPH_TAG;
056: }
057:
058: /**
059: * Constructs an array of Glyphs that represents the specified
060: * <altGlyph> element at the requested size.
061: *
062: * @param ctx The current bridge context.
063: * @param altGlyphElement The altGlyph element to base the SVGGVTGlyphVector
064: * construction on.
065: * @param fontSize The font size of the Glyphs to create.
066: *
067: * @return The new SVGGVTGlyphVector or null if any of the glyphs are
068: * unavailable.
069: */
070: public Glyph[] createAltGlyphArray(BridgeContext ctx,
071: Element altGlyphElement, float fontSize,
072: AttributedCharacterIterator aci) {
073:
074: // get the referenced element
075: String uri = XLinkSupport.getXLinkHref(altGlyphElement);
076:
077: Element refElement = null;
078:
079: try {
080: refElement = ctx.getReferencedElement(altGlyphElement, uri);
081: } catch (BridgeException e) {
082: if (ERR_URI_UNSECURE.equals(e.getCode())) {
083: ctx.getUserAgent().displayError(e);
084: }
085: }
086:
087: if (refElement == null) {
088: // couldn't find the referenced element
089: return null;
090: }
091: if (!SVG_NAMESPACE_URI.equals(refElement.getNamespaceURI()))
092: return null; // Not an SVG element.
093:
094: // if the referenced element is a glyph
095: if (refElement.getLocalName().equals(SVG_GLYPH_TAG)) {
096:
097: Glyph glyph = getGlyph(ctx, uri, altGlyphElement, fontSize,
098: aci);
099:
100: if (glyph == null) {
101: // failed to create a glyph for the specified glyph uri
102: return null;
103: }
104:
105: Glyph[] glyphArray = new Glyph[1];
106: glyphArray[0] = glyph;
107: return glyphArray;
108: }
109:
110: // else should be an altGlyphDef element
111: if (refElement.getLocalName().equals(SVG_ALT_GLYPH_DEF_TAG)) {
112:
113: // if not local import the referenced altGlyphDef
114: // into the current document
115: SVGOMDocument document = (SVGOMDocument) altGlyphElement
116: .getOwnerDocument();
117: SVGOMDocument refDocument = (SVGOMDocument) refElement
118: .getOwnerDocument();
119: boolean isLocal = (refDocument == document);
120:
121: Element localRefElement = (isLocal) ? refElement
122: : (Element) document.importNode(refElement, true);
123: if (!isLocal) {
124: // need to attach the imported element to the document and
125: // then compute the styles and uris
126: String base = AbstractNode.getBaseURI(altGlyphElement);
127: Element g = document.createElementNS(SVG_NAMESPACE_URI,
128: SVG_G_TAG);
129: g.appendChild(localRefElement);
130: g.setAttributeNS(XMLConstants.XML_NAMESPACE_URI,
131: "xml:base", base);
132: CSSUtilities.computeStyleAndURIs(refElement,
133: localRefElement, uri);
134: }
135:
136: // look for glyphRef children
137: NodeList altGlyphDefChildren = localRefElement
138: .getChildNodes();
139: boolean containsGlyphRefNodes = false;
140: int numAltGlyphDefChildren = altGlyphDefChildren
141: .getLength();
142: for (int i = 0; i < numAltGlyphDefChildren; i++) {
143: Node altGlyphChild = altGlyphDefChildren.item(i);
144: if (altGlyphChild.getNodeType() == Node.ELEMENT_NODE) {
145: Element agc = (Element) altGlyphChild;
146: if (SVG_NAMESPACE_URI.equals(agc.getNamespaceURI())
147: && SVG_GLYPH_REF_TAG.equals(agc
148: .getLocalName())) {
149: containsGlyphRefNodes = true;
150: break;
151: }
152: }
153: }
154: if (containsGlyphRefNodes) { // process the glyphRef children
155:
156: NodeList glyphRefNodes = localRefElement
157: .getElementsByTagNameNS(SVG_NAMESPACE_URI,
158: SVG_GLYPH_REF_TAG);
159: int numGlyphRefNodes = glyphRefNodes.getLength();
160: Glyph[] glyphArray = new Glyph[numGlyphRefNodes];
161: for (int i = 0; i < numGlyphRefNodes; i++) {
162: // get the referenced glyph element
163: Element glyphRefElement = (Element) glyphRefNodes
164: .item(i);
165: String glyphUri = XLinkSupport
166: .getXLinkHref(glyphRefElement);
167:
168: Glyph glyph = getGlyph(ctx, glyphUri,
169: glyphRefElement, fontSize, aci);
170: if (glyph == null) {
171: // failed to create a glyph for the specified glyph uri
172: return null;
173: }
174: glyphArray[i] = glyph;
175: }
176: return glyphArray;
177:
178: } else { // try looking for altGlyphItem children
179:
180: NodeList altGlyphItemNodes = localRefElement
181: .getElementsByTagNameNS(SVG_NAMESPACE_URI,
182: SVG_ALT_GLYPH_ITEM_TAG);
183: int numAltGlyphItemNodes = altGlyphItemNodes
184: .getLength();
185: if (numAltGlyphItemNodes > 0) {
186: boolean foundMatchingGlyph = false;
187: Glyph[] glyphArray = null;
188:
189: //look through all altGlyphItem to find the one
190: //that have all its glyphs available
191:
192: for (int i = 0; i < numAltGlyphItemNodes
193: && !foundMatchingGlyph; i++) {
194:
195: // try to find a resolvable glyphRef
196: Element altGlyphItemElement = (Element) altGlyphItemNodes
197: .item(i);
198: NodeList altGlyphRefNodes = altGlyphItemElement
199: .getElementsByTagNameNS(
200: SVG_NAMESPACE_URI,
201: SVG_GLYPH_REF_TAG);
202: int numAltGlyphRefNodes = altGlyphRefNodes
203: .getLength();
204:
205: glyphArray = new Glyph[numAltGlyphRefNodes];
206:
207: // consider that all glyphs are available
208: // and check if they can be found
209: foundMatchingGlyph = true;
210:
211: for (int j = 0; j < numAltGlyphRefNodes; j++) {
212: // get the referenced glyph element
213: Element glyphRefElement = (Element) altGlyphRefNodes
214: .item(j);
215: String glyphUri = XLinkSupport
216: .getXLinkHref(glyphRefElement);
217:
218: Glyph glyph = getGlyph(ctx, glyphUri,
219: glyphRefElement, fontSize, aci);
220: if (glyph != null) {
221: // found a matching glyph for this altGlyphItem
222: glyphArray[j] = glyph;
223: } else {
224: //this altGlyphItem is not good
225: //seek for the next one
226: foundMatchingGlyph = false;
227: break;
228: }
229: }
230: }
231: if (!foundMatchingGlyph) {
232: // couldn't find a alGlyphItem
233: // with all its glyphs available
234: // so stop and return null
235: return null;
236: }
237:
238: return glyphArray;
239: }
240: }
241: }
242:
243: /*
244: // reference is not to a valid element type, throw an exception
245: throw new BridgeException(altGlyphElement, ERR_URI_BAD_TARGET,
246: new Object[] {uri});
247: */
248: //reference not valid, no altGlyph created
249: return null;
250: }
251:
252: /**
253: * Returns a Glyph object that represents the glyph at the specified URI
254: * scaled to the required font size.
255: *
256: * @param ctx The bridge context.
257: * @param glyphUri The URI of the glyph to retreive.
258: * @param altGlyphElement The element that references the glyph.
259: * @param fontSize Indicates the required size of the glyph.
260: * @return The Glyph or null if the glyph URI is not available.
261: */
262: private Glyph getGlyph(BridgeContext ctx, String glyphUri,
263: Element altGlyphElement, float fontSize,
264: AttributedCharacterIterator aci) {
265:
266: Element refGlyphElement = null;
267: try {
268: refGlyphElement = ctx.getReferencedElement(altGlyphElement,
269: glyphUri);
270: } catch (BridgeException e) {
271: // this is ok, it is possible that the glyph at the given
272: // uri is not available.
273:
274: // Display an error message if a security exception occured
275: if (ERR_URI_UNSECURE.equals(e.getCode())) {
276: ctx.getUserAgent().displayError(e);
277: }
278: }
279:
280: if ((refGlyphElement == null)
281: || (!SVG_NAMESPACE_URI.equals(refGlyphElement
282: .getNamespaceURI()))
283: || (!SVG_GLYPH_TAG.equals(refGlyphElement
284: .getLocalName())))
285: // couldn't find the referenced glyph element,
286: // or referenced element not a glyph
287: return null;
288:
289: // see if the referenced glyph element is local
290: SVGOMDocument document = (SVGOMDocument) altGlyphElement
291: .getOwnerDocument();
292: SVGOMDocument refDocument = (SVGOMDocument) refGlyphElement
293: .getOwnerDocument();
294: boolean isLocal = (refDocument == document);
295:
296: // if not local, import both the glyph and its font-face element
297: Element localGlyphElement = null;
298: Element localFontFaceElement = null;
299: Element localFontElement = null;
300: if (isLocal) {
301: localGlyphElement = refGlyphElement;
302: localFontElement = (Element) localGlyphElement
303: .getParentNode();
304: NodeList fontFaceElements = localFontElement
305: .getElementsByTagNameNS(SVG_NAMESPACE_URI,
306: SVG_FONT_FACE_TAG);
307: if (fontFaceElements.getLength() > 0) {
308: localFontFaceElement = (Element) fontFaceElements
309: .item(0);
310: }
311:
312: } else {
313: // import the whole font
314: localFontElement = (Element) document.importNode(
315: refGlyphElement.getParentNode(), true);
316: String base = AbstractNode.getBaseURI(altGlyphElement);
317: Element g = document.createElementNS(SVG_NAMESPACE_URI,
318: SVG_G_TAG);
319: g.appendChild(localFontElement);
320: g.setAttributeNS(XMLConstants.XML_NAMESPACE_URI,
321: "xml:base", base);
322: CSSUtilities.computeStyleAndURIs((Element) refGlyphElement
323: .getParentNode(), localFontElement, glyphUri);
324:
325: // get the local glyph element
326: String glyphId = refGlyphElement.getAttributeNS(null,
327: SVG_ID_ATTRIBUTE);
328: NodeList glyphElements = localFontElement
329: .getElementsByTagNameNS(SVG_NAMESPACE_URI,
330: SVG_GLYPH_TAG);
331: for (int i = 0; i < glyphElements.getLength(); i++) {
332: Element glyphElem = (Element) glyphElements.item(i);
333: if (glyphElem.getAttributeNS(null, SVG_ID_ATTRIBUTE)
334: .equals(glyphId)) {
335: localGlyphElement = glyphElem;
336: break;
337: }
338: }
339: // get the local font-face element
340: NodeList fontFaceElements = localFontElement
341: .getElementsByTagNameNS(SVG_NAMESPACE_URI,
342: SVG_FONT_FACE_TAG);
343: if (fontFaceElements.getLength() > 0) {
344: localFontFaceElement = (Element) fontFaceElements
345: .item(0);
346: }
347: }
348:
349: // if couldn't find the glyph or its font-face return null
350: if (localGlyphElement == null || localFontFaceElement == null) {
351: return null;
352: }
353:
354: SVGFontFaceElementBridge fontFaceBridge = (SVGFontFaceElementBridge) ctx
355: .getBridge(localFontFaceElement);
356: SVGFontFace fontFace = fontFaceBridge.createFontFace(ctx,
357: localFontFaceElement);
358: SVGGlyphElementBridge glyphBridge = (SVGGlyphElementBridge) ctx
359: .getBridge(localGlyphElement);
360:
361: aci.first();
362: TextPaintInfo tpi = (TextPaintInfo) aci
363: .getAttribute(PAINT_INFO);
364:
365: return glyphBridge.createGlyph(ctx, localGlyphElement,
366: altGlyphElement, -1, fontSize, fontFace, tpi);
367: }
368: }
|