Source code: org/apache/batik/bridge/SVGAltGlyphElementBridge.java
1 /*
2
3 Copyright 2001-2003 The Apache Software Foundation
4
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16
17 */
18 package org.apache.batik.bridge;
19
20 import java.awt.Paint;
21 import java.awt.Stroke;
22 import java.awt.font.TextAttribute;
23 import java.text.AttributedCharacterIterator;
24
25 import org.apache.batik.dom.svg.SVGOMDocument;
26 import org.apache.batik.dom.svg.XMLBaseSupport;
27 import org.apache.batik.dom.util.XLinkSupport;
28 import org.apache.batik.gvt.font.Glyph;
29 import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
30 import org.apache.batik.gvt.text.TextPaintInfo;
31 import org.w3c.dom.Element;
32 import org.w3c.dom.Node;
33 import org.w3c.dom.NodeList;
34
35
36 /**
37 * Bridge class for the <altGlyph> element.
38 *
39 * @author <a href="mailto:bella.robinson@cmis.csiro.au">Bella Robinson</a>
40 * @version $Id: SVGAltGlyphElementBridge.java,v 1.18 2005/03/03 01:19:52 deweese Exp $
41 */
42 public class SVGAltGlyphElementBridge extends AbstractSVGBridge
43 implements ErrorConstants {
44
45 public static final AttributedCharacterIterator.Attribute PAINT_INFO
46 = GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO;
47
48 /**
49 * Constructs a new bridge for the <altGlyph> element.
50 */
51 public SVGAltGlyphElementBridge() {
52 }
53
54 /**
55 * Returns 'altGlyph'.
56 */
57 public String getLocalName() {
58 return SVG_ALT_GLYPH_TAG;
59 }
60
61 /**
62 * Constructs an array of Glyphs that represents the specified
63 * <altGlyph> element at the requested size.
64 *
65 * @param ctx The current bridge context.
66 * @param altGlyphElement The altGlyph element to base the SVGGVTGlyphVector
67 * construction on.
68 * @param fontSize The font size of the Glyphs to create.
69 *
70 * @return The new SVGGVTGlyphVector or null if any of the glyphs are
71 * unavailable.
72 */
73 public Glyph[] createAltGlyphArray(BridgeContext ctx,
74 Element altGlyphElement,
75 float fontSize,
76 AttributedCharacterIterator aci) {
77
78 // get the referenced element
79 String uri = XLinkSupport.getXLinkHref(altGlyphElement);
80
81 Element refElement = null;
82
83 try {
84 refElement = ctx.getReferencedElement(altGlyphElement, uri);
85 } catch (BridgeException e) {
86 if (ERR_URI_UNSECURE.equals(e.getCode())) {
87 ctx.getUserAgent().displayError(e);
88 }
89 }
90
91 if (refElement == null) {
92 // couldn't find the referenced element
93 return null;
94 }
95 if (!SVG_NAMESPACE_URI.equals(refElement.getNamespaceURI()))
96 return null; // Not an SVG element.
97
98 // if the referenced element is a glyph
99 if (refElement.getLocalName().equals(SVG_GLYPH_TAG)) {
100
101 Glyph glyph = getGlyph(ctx, uri, altGlyphElement, fontSize, aci);
102
103 if (glyph == null) {
104 // failed to create a glyph for the specified glyph uri
105 return null;
106 }
107
108 Glyph[] glyphArray = new Glyph[1];
109 glyphArray[0] = glyph;
110 return glyphArray;
111 }
112
113 // else should be an altGlyphDef element
114 if (refElement.getLocalName().equals(SVG_ALT_GLYPH_DEF_TAG)) {
115
116 // if not local import the referenced altGlyphDef
117 // into the current document
118 SVGOMDocument document
119 = (SVGOMDocument)altGlyphElement.getOwnerDocument();
120 SVGOMDocument refDocument
121 = (SVGOMDocument)refElement.getOwnerDocument();
122 boolean isLocal = (refDocument == document);
123
124 Element localRefElement = (isLocal) ? refElement
125 : (Element)document.importNode(refElement, true);
126 if (!isLocal) {
127 // need to attach the imported element to the document and
128 // then compute the styles and uris
129 String base = XMLBaseSupport.getCascadedXMLBase(altGlyphElement);
130 Element g = document.createElementNS(SVG_NAMESPACE_URI, SVG_G_TAG);
131 g.appendChild(localRefElement);
132 g.setAttributeNS(XMLBaseSupport.XML_NAMESPACE_URI,
133 "xml:base",
134 base);
135 CSSUtilities.computeStyleAndURIs(refElement,
136 localRefElement,
137 uri);
138 }
139
140 // look for glyphRef children
141 NodeList altGlyphDefChildren = localRefElement.getChildNodes();
142 boolean containsGlyphRefNodes = false;
143 int numAltGlyphDefChildren = altGlyphDefChildren.getLength();
144 for (int i = 0; i < numAltGlyphDefChildren; i++) {
145 Node altGlyphChild = altGlyphDefChildren.item(i);
146 if (altGlyphChild.getNodeType() == Node.ELEMENT_NODE) {
147 Element agc = (Element)altGlyphChild;
148 if (SVG_NAMESPACE_URI.equals(agc.getNamespaceURI()) &&
149 SVG_GLYPH_REF_TAG.equals(agc.getLocalName())) {
150 containsGlyphRefNodes = true;
151 break;
152 }
153 }
154 }
155 if (containsGlyphRefNodes) { // process the glyphRef children
156
157 NodeList glyphRefNodes
158 = localRefElement.getElementsByTagNameNS(SVG_NAMESPACE_URI,
159 SVG_GLYPH_REF_TAG);
160 int numGlyphRefNodes = glyphRefNodes.getLength();
161 Glyph[] glyphArray = new Glyph[numGlyphRefNodes];
162 for (int i = 0; i < numGlyphRefNodes; i++) {
163 // get the referenced glyph element
164 Element glyphRefElement = (Element)glyphRefNodes.item(i);
165 String glyphUri = XLinkSupport.getXLinkHref(glyphRefElement);
166
167 Glyph glyph
168 = getGlyph(ctx, glyphUri, glyphRefElement, fontSize, aci);
169 if (glyph == null) {
170 // failed to create a glyph for the specified glyph uri
171 return null;
172 }
173 glyphArray[i] = glyph;
174 }
175 return glyphArray;
176
177 } else { // try looking for altGlyphItem children
178
179 NodeList altGlyphItemNodes
180 = localRefElement.getElementsByTagNameNS
181 (SVG_NAMESPACE_URI, SVG_ALT_GLYPH_ITEM_TAG);
182 int numAltGlyphItemNodes = altGlyphItemNodes.getLength();
183 if (numAltGlyphItemNodes > 0) {
184 boolean foundMatchingGlyph = false;
185 Glyph[] glyphArray = null;
186
187 //look through all altGlyphItem to find the one
188 //that have all its glyphs available
189
190 for (int i = 0; i < numAltGlyphItemNodes && !foundMatchingGlyph ; i++) {
191
192 // try to find a resolvable glyphRef
193 Element altGlyphItemElement = (Element)altGlyphItemNodes.item(i);
194 NodeList altGlyphRefNodes
195 = altGlyphItemElement.getElementsByTagNameNS
196 (SVG_NAMESPACE_URI, SVG_GLYPH_REF_TAG);
197 int numAltGlyphRefNodes = altGlyphRefNodes.getLength();
198
199 glyphArray = new Glyph[numAltGlyphRefNodes];
200
201 // consider that all glyphs are available
202 // and check if they can be found
203 foundMatchingGlyph = true;
204
205 for (int j = 0; j < numAltGlyphRefNodes; j++) {
206 // get the referenced glyph element
207 Element glyphRefElement = (Element)altGlyphRefNodes.item(j);
208 String glyphUri = XLinkSupport.getXLinkHref(glyphRefElement);
209
210 Glyph glyph = getGlyph(ctx, glyphUri, glyphRefElement, fontSize, aci);
211 if (glyph != null) {
212 // found a matching glyph for this altGlyphItem
213 glyphArray[j] = glyph;
214 }
215 else{
216 //this altGlyphItem is not good
217 //seek for the next one
218 foundMatchingGlyph = false;
219 break;
220 }
221 }
222 }
223 if (!foundMatchingGlyph) {
224 // couldn't find a alGlyphItem
225 // with all its glyphs available
226 // so stop and return null
227 return null;
228 }
229
230 return glyphArray;
231 }
232 }
233 }
234
235
236 /*
237 // reference is not to a valid element type, throw an exception
238 throw new BridgeException(altGlyphElement, ERR_URI_BAD_TARGET,
239 new Object[] {uri});
240 */
241 //reference not valid, no altGlyph created
242 return null;
243 }
244
245
246 /**
247 * Returns a Glyph object that represents the glyph at the specified URI
248 * scaled to the required font size.
249 *
250 * @param ctx The bridge context.
251 * @param glyphUri The URI of the glyph to retreive.
252 * @param altGlyphElement The element that references the glyph.
253 * @param fontSize Indicates the required size of the glyph.
254 * @return The Glyph or null if the glyph URI is not available.
255 */
256 private Glyph getGlyph(BridgeContext ctx,
257 String glyphUri,
258 Element altGlyphElement,
259 float fontSize,
260 AttributedCharacterIterator aci) {
261
262 Element refGlyphElement = null;
263 try {
264 refGlyphElement = ctx.getReferencedElement(altGlyphElement,
265 glyphUri);
266 } catch (BridgeException e) {
267 // this is ok, it is possible that the glyph at the given
268 // uri is not available.
269
270 // Display an error message if a security exception occured
271 if (ERR_URI_UNSECURE.equals(e.getCode())) {
272 ctx.getUserAgent().displayError(e);
273 }
274 }
275
276 if ((refGlyphElement == null) ||
277 (!SVG_NAMESPACE_URI.equals(refGlyphElement.getNamespaceURI())) ||
278 (!SVG_GLYPH_TAG.equals(refGlyphElement.getLocalName())))
279 // couldn't find the referenced glyph element,
280 // or referenced element not a glyph
281 return null;
282
283 // see if the referenced glyph element is local
284 SVGOMDocument document
285 = (SVGOMDocument)altGlyphElement.getOwnerDocument();
286 SVGOMDocument refDocument
287 = (SVGOMDocument)refGlyphElement.getOwnerDocument();
288 boolean isLocal = (refDocument == document);
289
290 // if not local, import both the glyph and its font-face element
291 Element localGlyphElement = null;
292 Element localFontFaceElement = null;
293 Element localFontElement = null;
294 if (isLocal) {
295 localGlyphElement = refGlyphElement;
296 localFontElement = (Element)localGlyphElement.getParentNode();
297 NodeList fontFaceElements
298 = localFontElement.getElementsByTagNameNS
299 (SVG_NAMESPACE_URI, SVG_FONT_FACE_TAG);
300 if (fontFaceElements.getLength() > 0) {
301 localFontFaceElement = (Element)fontFaceElements.item(0);
302 }
303
304 } else {
305 // import the whole font
306 localFontElement = (Element)document.importNode
307 (refGlyphElement.getParentNode(), true);
308 String base = XMLBaseSupport.getCascadedXMLBase(altGlyphElement);
309 Element g = document.createElementNS(SVG_NAMESPACE_URI, SVG_G_TAG);
310 g.appendChild(localFontElement);
311 g.setAttributeNS(XMLBaseSupport.XML_NAMESPACE_URI,
312 "xml:base",
313 base);
314 CSSUtilities.computeStyleAndURIs(
315 (Element)refGlyphElement.getParentNode(),
316 localFontElement, glyphUri);
317
318 // get the local glyph element
319 String glyphId = refGlyphElement.getAttributeNS
320 (null, SVG_ID_ATTRIBUTE);
321 NodeList glyphElements = localFontElement.getElementsByTagNameNS
322 (SVG_NAMESPACE_URI, SVG_GLYPH_TAG);
323 for (int i = 0; i < glyphElements.getLength(); i++) {
324 Element glyphElem = (Element)glyphElements.item(i);
325 if (glyphElem.getAttributeNS(null, SVG_ID_ATTRIBUTE).equals(glyphId)) {
326 localGlyphElement = glyphElem;
327 break;
328 }
329 }
330 // get the local font-face element
331 NodeList fontFaceElements
332 = localFontElement.getElementsByTagNameNS
333 (SVG_NAMESPACE_URI, SVG_FONT_FACE_TAG);
334 if (fontFaceElements.getLength() > 0) {
335 localFontFaceElement = (Element)fontFaceElements.item(0);
336 }
337 }
338
339 // if couldn't find the glyph or its font-face return null
340 if (localGlyphElement == null || localFontFaceElement == null) {
341 return null;
342 }
343
344 SVGFontFaceElementBridge fontFaceBridge
345 = (SVGFontFaceElementBridge)ctx.getBridge(localFontFaceElement);
346 SVGFontFace fontFace = fontFaceBridge.createFontFace
347 (ctx, localFontFaceElement);
348 SVGGlyphElementBridge glyphBridge
349 = (SVGGlyphElementBridge)ctx.getBridge(localGlyphElement);
350
351 aci.first();
352 TextPaintInfo tpi = (TextPaintInfo)aci.getAttribute(PAINT_INFO);
353
354 return glyphBridge.createGlyph(ctx, localGlyphElement, altGlyphElement,
355 -1, fontSize, fontFace, tpi);
356 }
357 }