KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > bridge > SVGTextElementBridge


1 /*
2
3    Copyright 2001-2004 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.AlphaComposite JavaDoc;
21 import java.awt.Color JavaDoc;
22 import java.awt.Composite JavaDoc;
23 import java.awt.Paint JavaDoc;
24 import java.awt.RenderingHints JavaDoc;
25 import java.awt.Shape JavaDoc;
26 import java.awt.Stroke JavaDoc;
27 import java.awt.font.TextAttribute JavaDoc;
28 import java.awt.geom.AffineTransform JavaDoc;
29 import java.awt.geom.GeneralPath JavaDoc;
30 import java.awt.geom.Point2D JavaDoc;
31 import java.awt.geom.Rectangle2D JavaDoc;
32 import java.text.AttributedCharacterIterator JavaDoc;
33 import java.text.AttributedString JavaDoc;
34 import java.text.AttributedCharacterIterator.Attribute;
35 import java.util.ArrayList JavaDoc;
36 import java.util.HashMap JavaDoc;
37 import java.util.HashSet JavaDoc;
38 import java.util.Iterator JavaDoc;
39 import java.util.List JavaDoc;
40 import java.util.Map JavaDoc;
41 import java.util.Set JavaDoc;
42 import java.util.StringTokenizer JavaDoc;
43 import java.util.Vector JavaDoc;
44 import java.util.WeakHashMap JavaDoc;
45
46 import org.apache.batik.css.engine.CSSEngineEvent;
47 import org.apache.batik.css.engine.CSSStylableElement;
48 import org.apache.batik.css.engine.SVGCSSEngine;
49 import org.apache.batik.css.engine.StyleMap;
50 import org.apache.batik.css.engine.value.ListValue;
51 import org.apache.batik.css.engine.value.Value;
52 import org.apache.batik.dom.svg.SVGContext;
53 import org.apache.batik.dom.svg.SVGOMElement;
54 import org.apache.batik.dom.svg.SVGTextContent;
55 import org.apache.batik.dom.util.XLinkSupport;
56 import org.apache.batik.dom.util.XMLSupport;
57 import org.apache.batik.gvt.GraphicsNode;
58 import org.apache.batik.gvt.TextNode;
59 import org.apache.batik.gvt.font.GVTFontFamily;
60 import org.apache.batik.gvt.font.GVTGlyphMetrics;
61 import org.apache.batik.gvt.font.GVTGlyphVector;
62 import org.apache.batik.gvt.renderer.StrokingTextPainter;
63 import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
64 import org.apache.batik.gvt.text.Mark;
65 import org.apache.batik.gvt.text.TextHit;
66 import org.apache.batik.gvt.text.TextPaintInfo;
67 import org.apache.batik.gvt.text.TextPath;
68 import org.apache.batik.gvt.text.TextSpanLayout;
69 import org.w3c.dom.Element JavaDoc;
70 import org.w3c.dom.Node JavaDoc;
71 import org.w3c.dom.css.CSSPrimitiveValue;
72 import org.w3c.dom.css.CSSValue;
73 import org.w3c.dom.events.Event JavaDoc;
74 import org.w3c.dom.events.EventListener JavaDoc;
75 import org.w3c.dom.events.EventTarget JavaDoc;
76 import org.w3c.dom.events.MutationEvent JavaDoc;
77
78 /**
79  * Bridge class for the <text> element.
80  *
81  * @author <a HREF="mailto:stephane@hillion.org">Stephane Hillion</a>
82  * @author <a HREF="mailto:bill.haneman@ireland.sun.com">Bill Haneman</a>
83  * @version $Id: SVGTextElementBridge.java,v 1.106 2005/03/27 08:58:30 cam Exp $
84  */

85 public class SVGTextElementBridge extends AbstractGraphicsNodeBridge
86     implements SVGTextContent {
87
88     protected final static Integer JavaDoc ZERO = new Integer JavaDoc(0);
89
90     public static final
91         AttributedCharacterIterator.Attribute JavaDoc TEXT_COMPOUND_DELIMITER =
92         GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER;
93
94     public static final AttributedCharacterIterator.Attribute JavaDoc PAINT_INFO =
95          GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO;
96
97     public static final
98         AttributedCharacterIterator.Attribute JavaDoc ALT_GLYPH_HANDLER =
99         GVTAttributedCharacterIterator.TextAttribute.ALT_GLYPH_HANDLER;
100         
101
102     protected AttributedString JavaDoc laidoutText;
103
104     // This is used to track the TextPainterInfo for each element
105
// in this text element.
106
protected WeakHashMap JavaDoc elemTPI = new WeakHashMap JavaDoc();
107
108     // This s true if any of the spans of this text element
109
// use a 'complex' SVG font (meaning the font uses more
110
// and just the 'd' attribute on the glyph element.
111
// In this case we need to recreate the font when ever
112
// CSS attributes change on the text - so we can capture
113
// the effects of CSS inheritence.
114
protected boolean usingComplexSVGFont = false;
115
116     /**
117      * Constructs a new bridge for the &lt;text> element.
118      */

119     public SVGTextElementBridge() {}
120
121     /**
122      * Returns 'text'.
123      */

124     public String JavaDoc getLocalName() {
125         return SVG_TEXT_TAG;
126     }
127
128     /**
129      * Returns a new instance of this bridge.
130      */

131     public Bridge getInstance() {
132         return new SVGTextElementBridge();
133     }
134
135     /**
136      * Creates a <tt>GraphicsNode</tt> according to the specified parameters.
137      *
138      * @param ctx the bridge context to use
139      * @param e the element that describes the graphics node to build
140      * @return a graphics node that represents the specified element
141      */

142     public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) {
143         TextNode node = (TextNode)super.createGraphicsNode(ctx, e);
144         if (node == null)
145             return null;
146
147         // specify the text painter to use
148
if (ctx.getTextPainter() != null)
149             node.setTextPainter(ctx.getTextPainter());
150
151         // 'text-rendering' and 'color-rendering'
152
RenderingHints JavaDoc hints = null;
153         hints = CSSUtilities.convertColorRendering(e, hints);
154         hints = CSSUtilities.convertTextRendering (e, hints);
155         if (hints != null)
156             node.setRenderingHints(hints);
157
158         node.setLocation(getLocation(ctx, e));
159
160         return node;
161     }
162
163     /**
164      * Creates the GraphicsNode depending on the GraphicsNodeBridge
165      * implementation.
166      */

167     protected GraphicsNode instantiateGraphicsNode() {
168         return new TextNode();
169     }
170
171     /**
172      * Returns the text node location according to the 'x' and 'y'
173      * attributes of the specified text element.
174      *
175      * @param ctx the bridge context to use
176      * @param e the text element
177      */

178     protected Point2D JavaDoc getLocation(BridgeContext ctx, Element e) {
179         UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, e);
180
181         // 'x' attribute - default is 0
182
String JavaDoc s = e.getAttributeNS(null, SVG_X_ATTRIBUTE);
183         float x = 0;
184         if (s.length() != 0) {
185             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(s, ", ", false);
186             x = UnitProcessor.svgHorizontalCoordinateToUserSpace
187                 (st.nextToken(), SVG_X_ATTRIBUTE, uctx);
188         }
189
190         // 'y' attribute - default is 0
191
s = e.getAttributeNS(null, SVG_Y_ATTRIBUTE);
192         float y = 0;
193         if (s.length() != 0) {
194             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(s, ", ", false);
195             y = UnitProcessor.svgVerticalCoordinateToUserSpace
196                 (st.nextToken(), SVG_Y_ATTRIBUTE, uctx);
197         }
198
199         return new Point2D.Float JavaDoc(x, y);
200     }
201
202     protected boolean isTextElement(Element e) {
203         if (!SVG_NAMESPACE_URI.equals(e.getNamespaceURI()))
204             return false;
205         String JavaDoc nodeName = e.getLocalName();
206         return (nodeName.equals(SVG_TEXT_TAG) ||
207                 nodeName.equals(SVG_TSPAN_TAG) ||
208                 nodeName.equals(SVG_ALT_GLYPH_TAG) ||
209                 nodeName.equals(SVG_A_TAG) ||
210                 nodeName.equals(SVG_TEXT_PATH_TAG) ||
211                 nodeName.equals(SVG_TREF_TAG));
212     }
213
214     protected boolean isTextChild(Element e) {
215         if (!SVG_NAMESPACE_URI.equals(e.getNamespaceURI()))
216             return false;
217         String JavaDoc nodeName = e.getLocalName();
218         return (nodeName.equals(SVG_TSPAN_TAG) ||
219                 nodeName.equals(SVG_ALT_GLYPH_TAG) ||
220                 nodeName.equals(SVG_A_TAG) ||
221                 nodeName.equals(SVG_TEXT_PATH_TAG) ||
222                 nodeName.equals(SVG_TREF_TAG));
223     }
224
225     /**
226      * Builds using the specified BridgeContext and element, the
227      * specified graphics node.
228      *
229      * @param ctx the bridge context to use
230      * @param e the element that describes the graphics node to build
231      * @param node the graphics node to build
232      */

233     public void buildGraphicsNode(BridgeContext ctx,
234                                   Element e,
235                                   GraphicsNode node) {
236         e.normalize();
237         computeLaidoutText(ctx, e, node);
238
239         //
240
// DO NOT CALL super, 'opacity' is handle during addPaintAttributes()
241
//
242
// 'opacity'
243
node.setComposite(CSSUtilities.convertOpacity(e));
244         // 'filter'
245
node.setFilter(CSSUtilities.convertFilter(e, node, ctx));
246         // 'mask'
247
node.setMask(CSSUtilities.convertMask(e, node, ctx));
248         // 'clip-path'
249
node.setClip(CSSUtilities.convertClipPath(e, node, ctx));
250         // 'pointer-events'
251
node.setPointerEventType(CSSUtilities.convertPointerEvents(e));
252
253         initializeDynamicSupport(ctx, e, node);
254     }
255
256     /**
257      * Returns false as text is not a container.
258      */

259     public boolean isComposite() {
260         return false;
261     }
262
263     // Listener implementation ----------------------------------------------
264

265     /**
266      * The DOM EventListener to receive 'DOMNodeRemoved' event.
267      */

268     protected DOMChildNodeRemovedEventListener childNodeRemovedEventListener =
269         new DOMChildNodeRemovedEventListener();
270
271     /**
272      * The DOM EventListener invoked when a node is removed.
273      */

274     protected class DOMChildNodeRemovedEventListener implements EventListener JavaDoc {
275
276         /**
277          * Handles 'DOMNodeRemoved' event type.
278          */

279         public void handleEvent(Event evt) {
280             handleDOMChildNodeRemovedEvent((MutationEvent JavaDoc)evt);
281         }
282     }
283
284     /**
285      * The DOM EventListener to receive 'DOMSubtreeModified' event.
286      */

287     protected DOMSubtreeModifiedEventListener subtreeModifiedEventListener =
288         new DOMSubtreeModifiedEventListener();
289
290     /**
291      * The DOM EventListener invoked when the subtree is modified.
292      */

293     protected class DOMSubtreeModifiedEventListener implements EventListener JavaDoc {
294
295         /**
296          * Handles 'DOMSubtreeModified' event type.
297          */

298         public void handleEvent(Event evt) {
299             handleDOMSubtreeModifiedEvent((MutationEvent JavaDoc)evt);
300         }
301     }
302
303     // BridgeUpdateHandler implementation -----------------------------------
304

305     /**
306      * This method insures that any modification to a text
307      * element and its children is going to be reflected
308      * into the GVT tree.
309      */

310     protected void initializeDynamicSupport(BridgeContext ctx,
311                                             Element e,
312                                             GraphicsNode node) {
313         super.initializeDynamicSupport(ctx,e,node);
314
315         if (!ctx.isDynamic())
316             return; // Only add the listeners if we are dynamic
317

318
319         EventTarget JavaDoc evtTarget = (EventTarget JavaDoc)e;
320
321         //to be notified when a child is removed from the
322
//<text> element.
323
evtTarget.addEventListener
324             ("DOMNodeRemoved", childNodeRemovedEventListener, true);
325         ctx.storeEventListener
326             (evtTarget, "DOMNodeRemoved", childNodeRemovedEventListener, true);
327         
328         //to be notified when the modification of the subtree
329
//of the <text> element is done
330
evtTarget.addEventListener
331             ("DOMSubtreeModified", subtreeModifiedEventListener, false);
332         ctx.storeEventListener
333             (evtTarget, "DOMSubtreeModified", subtreeModifiedEventListener, false);
334
335         // traverse the children to add context on
336
// <tspan>, <tref> and <textPath>
337
Node child = e.getFirstChild();
338         while (child != null) {
339             if (child.getNodeType() == Node.ELEMENT_NODE) {
340                 addContextToChild(ctx,(Element)child);
341             }
342             child = child.getNextSibling();
343         }
344     }
345
346     /**
347      * Add to the element children of the node, a
348      * <code>SVGContext</code> to support dynamic updated . This is
349      * recurssive, the children of the nodes are also traversed to add
350      * to the support elements their context
351      *
352      * @param ctx a <code>BridgeContext</code> value
353      * @param e an <code>Element</code> value
354      *
355      * @see org.apache.batik.dom.svg.SVGContext
356      * @see org.apache.batik.bridge.BridgeUpdateHandler
357      */

358     protected void addContextToChild(BridgeContext ctx,Element e) {
359         if (SVG_NAMESPACE_URI.equals(e.getNamespaceURI())) {
360             if (e.getLocalName().equals(SVG_TSPAN_TAG)) {
361                 ((SVGOMElement)e).setSVGContext
362                     (new TspanBridge(ctx, this, e));
363             } else if (e.getLocalName().equals(SVG_TEXT_PATH_TAG)) {
364                 ((SVGOMElement)e).setSVGContext
365                     (new TextPathBridge(ctx, this, e));
366             } else if (e.getLocalName().equals(SVG_TREF_TAG)) {
367                 ((SVGOMElement)e).setSVGContext
368                     (new TRefBridge(ctx, this, e));
369             }
370         }
371
372         Node child = e.getFirstChild();
373         while (child != null) {
374             if (child.getNodeType() == Node.ELEMENT_NODE) {
375                 addContextToChild(ctx, (Element)child);
376             }
377             child = child.getNextSibling();
378         }
379     }
380
381     /**
382      * Invoked when an MutationEvent of type 'DOMNodeInserted' is fired.
383      */

384     public void handleDOMNodeInsertedEvent(MutationEvent JavaDoc evt) {
385         Node childNode = (Node)evt.getTarget();
386         
387         //check the type of the node inserted before discard the layout
388
//in the case of <title> or <desc> or <metadata>, the layout
389
//is unchanged
390
switch( childNode.getNodeType() ){
391         case Node.TEXT_NODE:
392         case Node.CDATA_SECTION_NODE:
393             laidoutText = null;
394             break;
395         case Node.ELEMENT_NODE: {
396             Element childElement = (Element)childNode;
397             if (isTextChild(childElement)) {
398                 addContextToChild( ctx, childElement);
399                 laidoutText = null;
400             }
401         }
402             break;
403         default:
404         }
405         if (laidoutText == null) {
406             computeLaidoutText(ctx, e, node);
407         }
408     }
409
410     /**
411      * Invoked when an MutationEvent of type 'DOMNodeInserted' is fired.
412      */

413     public void handleDOMNodeRemovedEvent(MutationEvent JavaDoc evt) {
414         EventTarget JavaDoc evtTarget = evt.getTarget();
415         evtTarget.removeEventListener("DOMNodeRemoved",
416                                       childNodeRemovedEventListener,
417                                       true);
418         evtTarget.removeEventListener("DOMSubtreeModified",
419                                       subtreeModifiedEventListener,
420                                       false);
421         super.handleDOMNodeRemovedEvent(evt);
422     }
423
424     /**
425      * Invoked when an MutationEvent of type 'DOMNodeRemoved' is fired.
426      */

427     public void handleDOMChildNodeRemovedEvent(MutationEvent JavaDoc evt) {
428         Node childNode = (Node)evt.getTarget();
429
430         //check the type of the node inserted before discard the layout
431
//in the case of <title> or <desc> or <metadata>, the layout
432
//is unchanged
433
switch (childNode.getNodeType()) {
434             case Node.TEXT_NODE:
435             case Node.CDATA_SECTION_NODE:
436                 //the parent has to be a displayed node
437
if (isParentDisplayed( childNode)) {
438                     laidoutText = null;
439                 }
440                 break;
441             case Node.ELEMENT_NODE:
442                 if (isTextChild((Element)childNode)) {
443                     laidoutText = null;
444                 }
445                 break;
446             default:
447         }
448         //if the laidoutText was set to null,
449
//then wait for DOMSubtreeChange to recompute it.
450
}
451
452     /**
453      * Invoked when an MutationEvent of type 'DOMSubtree' is fired.
454      */

455     public void handleDOMSubtreeModifiedEvent(MutationEvent JavaDoc evt){
456         //an operation occured onto the children of the
457
//text element, check if the layout was discarded
458
if (laidoutText == null) {
459             computeLaidoutText(ctx, e, node);
460         }
461     }
462
463     /**
464      * Invoked when an MutationEvent of type 'DOMCharacterDataModified'
465      * is fired.
466      */

467     public void handleDOMCharacterDataModified(MutationEvent JavaDoc evt){
468         Node childNode = (Node)evt.getTarget();
469         //if the parent is displayed, then discard the layout.
470
if (isParentDisplayed(childNode)) {
471             laidoutText = null;
472         }
473     }
474
475     /**
476      * Indicate of the parent of a node is
477      * a displayed element.
478      * &lt;title&gt;, &lt;desc&gt; and &lt;metadata&gt;
479      * are non displayable elements.
480      *
481      * @return true if the parent of the node is &lt;text&gt;,
482      * &lt;tspan&gt;, &lt;tref&gt;, &lt;textPath&gt;, &lt;a&gt;,
483      * &lt;altGlyph&gt;
484      */

485     protected boolean isParentDisplayed(Node childNode) {
486         Node parentNode = childNode.getParentNode();
487         return isTextElement((Element)parentNode);
488     }
489
490     /**
491      * Recompute the layout of the &lt;text&gt; node.
492      *
493      * Assign onto the TextNode pending to the element
494      * the new recomputed AtrributedString. Also
495      * update <code>laidoutText</code> with the new
496      * value.
497      */

498     protected void computeLaidoutText(BridgeContext ctx,
499                                        Element e,
500                                        GraphicsNode node) {
501         AttributedString JavaDoc as = buildAttributedString(ctx, e);
502         addGlyphPositionAttributes(as, e, ctx);
503         if (ctx.isDynamic()) {
504             laidoutText = new AttributedString JavaDoc(as.getIterator());
505         }
506         TextNode tn = (TextNode)node;
507         elemTPI.clear();
508         // Add null TPI objects to the text (after we set it on the
509
// Text we will swap in the correct values.
510
addNullPaintAttributes(as, e, ctx);
511
512         // Install the ACI in the text node.
513
tn.setAttributedCharacterIterator(as.getIterator());
514
515         // Now get the real paint into - this needs to
516
// wait until the text node is laidout so we can get
517
// objectBoundingBox info.
518
TextPaintInfo pi = new TextPaintInfo();
519         setBaseTextPaintInfo(pi, e, node, ctx);
520         // This get's Overline/underline info.
521
setDecorationTextPaintInfo(pi, e);
522         // Install the attributes.
523
addPaintAttributes(as, e, tn, pi, ctx);
524
525         if (usingComplexSVGFont) {
526             // Force Complex SVG fonts to be recreated, if we have them.
527
tn.setAttributedCharacterIterator(as.getIterator());
528         }
529     }
530
531     /**
532      * This creates 'dummy' TPI objects for each element and records
533      * them in the elemTPI map so we can later update them with
534      * the correct paint attributes.
535      */

536     protected void addNullPaintAttributes(AttributedString JavaDoc as,
537                                           Element element,
538                                           BridgeContext ctx) {
539         // 'requiredFeatures', 'requiredExtensions' and 'systemLanguage'
540
if ((!SVGUtilities.matchUserAgent(element, ctx.getUserAgent())) ||
541             (!CSSUtilities.convertDisplay(element))) {
542             return;
543         }
544
545         AttributedCharacterIterator JavaDoc aci = as.getIterator();
546
547         // calculate which chars in the string belong to this element
548
int firstChar = getElementStartIndex(aci, element);
549         if (firstChar == -1)
550             return; // Element not part of aci (no chars in elem usually)
551

552         int lastChar = getElementEndIndex(aci, element);
553         TextPaintInfo pi = new TextPaintInfo();
554         // Set some basic props so we can get bounds info for complex paints.
555
pi.visible = true;
556         pi.fillPaint = Color.black;
557
558         as.addAttribute(PAINT_INFO, pi, firstChar, lastChar+1);
559         elemTPI.put(element, pi);
560
561         addChildNullPaintAttributes(as, element, ctx);
562     }
563     
564     protected void addChildNullPaintAttributes(AttributedString JavaDoc as,
565                                                Element element,
566                                                BridgeContext ctx) {
567         // Add Paint attributres for children of text element
568
for (Node child = element.getFirstChild();
569              child != null;
570              child = child.getNextSibling()) {
571             if (child.getNodeType() != Node.ELEMENT_NODE)
572                 continue;
573
574             Element childElement = (Element)child;
575             if (isTextChild(childElement)) {
576                 addNullPaintAttributes(as, childElement, ctx);
577             }
578         }
579     }
580
581     /**
582      * This flag bit indicates if a new ACI has been created in
583      * response to a CSSEngineEvent.
584      * Avoid creating one ShapePainter per CSS property change
585      */

586     private boolean hasNewACI;
587
588     /**
589      * This is the element a CSS property has changed.
590      */

591     private Element cssProceedElement;
592
593     /**
594      * Invoked when an MutationEvent of type 'DOMAttrModified' is fired.
595      */

596     public void handleDOMAttrModifiedEvent(MutationEvent JavaDoc evt) {
597         String JavaDoc attrName = evt.getAttrName();
598         if (attrName.equals(SVG_X_ATTRIBUTE) ||
599             attrName.equals(SVG_Y_ATTRIBUTE) ||
600             attrName.equals(SVG_DX_ATTRIBUTE) ||
601             attrName.equals(SVG_DY_ATTRIBUTE) ||
602             attrName.equals(SVG_ROTATE_ATTRIBUTE) ){
603
604             if ( attrName.equals(SVG_X_ATTRIBUTE) ||
605                  attrName.equals(SVG_Y_ATTRIBUTE)){
606                 ((TextNode)node).setLocation(getLocation(ctx, e));
607             }
608
609             computeLaidoutText(ctx, e, node);
610         }
611         else{
612             super.handleDOMAttrModifiedEvent(evt);
613         }
614     }
615
616     /**
617      * Invoked when CSS properties have changed on an element.
618      *
619      * @param evt the CSSEngine event that describes the update
620      */

621     public void handleCSSEngineEvent(CSSEngineEvent evt) {
622         hasNewACI = false;
623         int [] properties = evt.getProperties();
624         // first try to find CSS properties that change the layout
625
for (int i=0; i < properties.length; ++i) {
626             switch(properties[i]) {
627             case SVGCSSEngine.BASELINE_SHIFT_INDEX:
628             case SVGCSSEngine.DIRECTION_INDEX:
629             case SVGCSSEngine.DISPLAY_INDEX:
630             case SVGCSSEngine.FONT_FAMILY_INDEX:
631             case SVGCSSEngine.FONT_SIZE_INDEX:
632             case SVGCSSEngine.FONT_STRETCH_INDEX:
633             case SVGCSSEngine.FONT_STYLE_INDEX:
634             case SVGCSSEngine.FONT_WEIGHT_INDEX:
635             case SVGCSSEngine.GLYPH_ORIENTATION_HORIZONTAL_INDEX:
636             case SVGCSSEngine.GLYPH_ORIENTATION_VERTICAL_INDEX:
637             case SVGCSSEngine.KERNING_INDEX:
638             case SVGCSSEngine.LETTER_SPACING_INDEX:
639             case SVGCSSEngine.TEXT_ANCHOR_INDEX:
640             case SVGCSSEngine.UNICODE_BIDI_INDEX:
641             case SVGCSSEngine.WORD_SPACING_INDEX:
642             case SVGCSSEngine.WRITING_MODE_INDEX: {
643                 if (!hasNewACI) {
644                     hasNewACI = true;
645                     computeLaidoutText(ctx, e, node);
646                 }
647                 break;
648             }
649             }
650         }
651         //optimize the calculation of
652
//the painting attributes and decoration
653
//by only recomputing the section for the element
654
cssProceedElement = evt.getElement();
655         // go for the other CSS properties
656
super.handleCSSEngineEvent(evt);
657         cssProceedElement = null;
658     }
659
660     /**
661      * Invoked for each CSS property that has changed.
662      */

663     protected void handleCSSPropertyChanged(int property) {
664         switch(property) {
665         case SVGCSSEngine.FILL_INDEX:
666         case SVGCSSEngine.FILL_OPACITY_INDEX:
667         case SVGCSSEngine.STROKE_INDEX:
668         case SVGCSSEngine.STROKE_OPACITY_INDEX:
669         case SVGCSSEngine.STROKE_WIDTH_INDEX:
670         case SVGCSSEngine.STROKE_LINECAP_INDEX:
671         case SVGCSSEngine.STROKE_LINEJOIN_INDEX:
672         case SVGCSSEngine.STROKE_MITERLIMIT_INDEX:
673         case SVGCSSEngine.STROKE_DASHARRAY_INDEX:
674         case SVGCSSEngine.STROKE_DASHOFFSET_INDEX:
675         case SVGCSSEngine.TEXT_DECORATION_INDEX:
676             rebuildACI();
677             break;
678  
679         case SVGCSSEngine.VISIBILITY_INDEX:
680             rebuildACI();
681             super.handleCSSPropertyChanged(property);
682             break;
683         case SVGCSSEngine.TEXT_RENDERING_INDEX: {
684             RenderingHints JavaDoc hints = node.getRenderingHints();
685             hints = CSSUtilities.convertTextRendering(e, hints);
686             if (hints != null) {
687                 node.setRenderingHints(hints);
688             }
689             break;
690         }
691         case SVGCSSEngine.COLOR_RENDERING_INDEX: {
692             RenderingHints JavaDoc hints = node.getRenderingHints();
693             hints = CSSUtilities.convertColorRendering(e, hints);
694             if (hints != null) {
695                 node.setRenderingHints(hints);
696             }
697             break;
698         }
699         default:
700             super.handleCSSPropertyChanged(property);
701         }
702     }
703
704     protected void rebuildACI() {
705         if (hasNewACI)
706             return;
707
708         TextNode tn = (TextNode)node;
709
710         TextPaintInfo pi, oldPI;
711         if ( cssProceedElement == e ){
712             pi = new TextPaintInfo();
713             setBaseTextPaintInfo(pi, e, node, ctx);
714             setDecorationTextPaintInfo(pi, e);
715             oldPI = (TextPaintInfo)elemTPI.get(e);
716         } else {
717             //if a child CSS property has changed, then
718
//retrieve the parent text decoration
719
//and only update the section of the AtrtibutedString of
720
//the child
721
TextPaintInfo parentPI;
722             parentPI = getParentTextPaintInfo
723                 (tn.getAttributedCharacterIterator(), cssProceedElement);
724             pi = getTextPaintInfo(cssProceedElement, tn, parentPI, ctx);
725             oldPI = (TextPaintInfo)elemTPI.get(cssProceedElement);
726         }
727         if (oldPI == null) return;
728
729         tn.swapTextPaintInfo(pi, oldPI);
730         if (usingComplexSVGFont)
731             // Force Complex SVG fonts to be recreated
732
tn.setAttributedCharacterIterator
733                 (tn.getAttributedCharacterIterator());
734     }
735
736     int getElementStartIndex
737         (AttributedCharacterIterator JavaDoc aci, Element element) {
738         // calculate which chars in the string belong to this element
739
for (int i = 0; i < aci.getEndIndex();) {
740             aci.setIndex(i);
741             Element delimeter;
742             delimeter = (Element)aci.getAttribute(TEXT_COMPOUND_DELIMITER);
743             if (delimeter == element || nodeAncestorOf(element, delimeter))
744                 return i;
745             i = aci.getRunLimit(TEXT_COMPOUND_DELIMITER);
746         }
747         return -1;
748     }
749
750     int getElementEndIndex
751         (AttributedCharacterIterator JavaDoc aci, Element element) {
752         // calculate which chars in the string belong to this element
753
for (int i = aci.getEndIndex()-1; i >= 0;) {
754             aci.setIndex(i);
755             Element delimeter;
756             delimeter = (Element)aci.getAttribute(TEXT_COMPOUND_DELIMITER);
757             if (delimeter == element || nodeAncestorOf(element, delimeter))
758                 return i;
759             i = aci.getRunStart(TEXT_COMPOUND_DELIMITER)-1;
760         }
761         return -1;
762     }
763
764     // -----------------------------------------------------------------------
765
// -----------------------------------------------------------------------
766
// -----------------------------------------------------------------------
767
// -----------------------------------------------------------------------
768

769     /**
770      * Creates the attributed string which represents the given text
771      * element children.
772      *
773      * @param ctx the bridge context to use
774      * @param element the text element
775      */

776     protected AttributedString JavaDoc buildAttributedString(BridgeContext ctx,
777                                                      Element element) {
778         
779         AttributedStringBuffer asb = new AttributedStringBuffer();
780         fillAttributedStringBuffer(ctx, element, true, null, null, asb);
781         return asb.toAttributedString();
782     }
783
784
785     /**
786      * This is used to store the end of the last piece of text
787      * content from an element with xml:space="preserve". When
788      * we are stripping trailing spaces we need to make sure
789      * we don't strip anything before this point.
790      */

791     protected int endLimit;
792
793     /**
794      * Fills the given AttributedStringBuffer.
795      */

796     protected void fillAttributedStringBuffer(BridgeContext ctx,
797                                               Element element,
798                                               boolean top,
799                                               TextPath textPath,
800                                               Integer JavaDoc bidiLevel,
801                                               AttributedStringBuffer asb) {
802         // 'requiredFeatures', 'requiredExtensions', 'systemLanguage' &
803
// 'display="none".
804
if ((!SVGUtilities.matchUserAgent(element, ctx.getUserAgent())) ||
805             (!CSSUtilities.convertDisplay(element))) {
806             return;
807         }
808         
809         String JavaDoc s = XMLSupport.getXMLSpace(element);
810         boolean preserve = s.equals(SVG_PRESERVE_VALUE);
811         boolean prevEndsWithSpace;
812         Element nodeElement = element;
813
814         if (top)
815             endLimit = 0;
816         if (preserve)
817             endLimit = asb.length();
818         
819     Map JavaDoc map = getAttributeMap(ctx, element, textPath, bidiLevel);
820     Object JavaDoc o = map.get(TextAttribute.BIDI_EMBEDDING);
821         Integer JavaDoc subBidiLevel = bidiLevel;
822     if (o != null)
823         subBidiLevel = ((Integer JavaDoc)o);
824
825         for (Node n = element.getFirstChild();
826              n != null;
827              n = n.getNextSibling()) {
828
829             if (preserve) {
830                 prevEndsWithSpace = false;
831             } else {
832                 if (asb.length() == 0)
833                     prevEndsWithSpace = true;
834                 else
835                     prevEndsWithSpace = (asb.getLastChar() == ' ');
836             }
837
838             switch (n.getNodeType()) {
839             case Node.ELEMENT_NODE:
840                 if (!SVG_NAMESPACE_URI.equals(n.getNamespaceURI()))
841                     break;
842                 
843                 nodeElement = (Element)n;
844
845                 String JavaDoc ln = n.getLocalName();
846                 
847                 if (ln.equals(SVG_TSPAN_TAG) ||
848                     ln.equals(SVG_ALT_GLYPH_TAG)) {
849                     fillAttributedStringBuffer(ctx,
850                                                nodeElement,
851                                                false,
852                                                textPath,
853                                                subBidiLevel,
854                                                asb);
855                 } else if (ln.equals(SVG_TEXT_PATH_TAG)) {
856                     SVGTextPathElementBridge textPathBridge
857                         = (SVGTextPathElementBridge)ctx.getBridge(nodeElement);
858                     TextPath newTextPath
859                         = textPathBridge.createTextPath(ctx, nodeElement);
860                     if (newTextPath != null) {
861                         fillAttributedStringBuffer(ctx,
862                                                    nodeElement,
863                                                    false,
864                                                    newTextPath,
865                                                    subBidiLevel,
866                                                    asb);
867                     }
868                 } else if (ln.equals(SVG_TREF_TAG)) {
869                     String JavaDoc uriStr = XLinkSupport.getXLinkHref((Element)n);
870                     Element ref = ctx.getReferencedElement((Element)n, uriStr);
871                     s = TextUtilities.getElementContent(ref);
872                     s = normalizeString(s, preserve, prevEndsWithSpace);
873                     if (s != null) {
874                         Map JavaDoc m = getAttributeMap(ctx, nodeElement,
875                                                 textPath, bidiLevel);
876                         asb.append(s, m);
877                     }
878                 } else if (ln.equals(SVG_A_TAG)) {
879                     EventTarget JavaDoc target = (EventTarget JavaDoc)nodeElement;
880                     UserAgent ua = ctx.getUserAgent();
881                     EventListener JavaDoc l = new SVGAElementBridge.AnchorListener(ua);
882                     target.addEventListener(SVG_EVENT_CLICK, l, false);
883                     ctx.storeEventListener(target, SVG_EVENT_CLICK, l, false);
884                     
885                     fillAttributedStringBuffer(ctx,
886                                                nodeElement,
887                                                false,
888                                                textPath,
889                                                subBidiLevel,
890                                                asb);
891                 }
892                 break;
893             case Node.TEXT_NODE:
894             case Node.CDATA_SECTION_NODE:
895                 s = n.getNodeValue();
896                 s = normalizeString(s, preserve, prevEndsWithSpace);
897                 asb.append(s, map);
898                 if (preserve)
899                     endLimit = asb.length();
900             }
901         }
902
903         if (top) {
904             while ((endLimit < asb.length()) && (asb.getLastChar() == ' '))
905                 asb.stripLast();
906         }
907     }
908
909     /**
910      * Normalizes the given string.
911      */

912     protected String JavaDoc normalizeString(String JavaDoc s,
913                                      boolean preserve,
914                                      boolean stripfirst) {
915         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(s.length());
916         if (preserve) {
917             for (int i = 0; i < s.length(); i++) {
918                 char c = s.charAt(i);
919                 switch (c) {
920                 case 10:
921                 case 13:
922                 case '\t':
923                     sb.append(' ');
924                     break;
925                 default:
926                     sb.append(c);
927                 }
928             }
929             return sb.toString();
930         }
931
932         int idx = 0;
933         if (stripfirst) {
934             loop: while (idx < s.length()) {
935                 switch (s.charAt(idx)) {
936                 default:
937                     break loop;
938                 case 10:
939                 case 13:
940                 case ' ':
941                 case '\t':
942                     idx++;
943                 }
944             }
945         }
946
947         boolean space = false;
948         for (int i = idx; i < s.length(); i++) {
949             char c = s.charAt(i);
950             switch (c) {
951             case 10:
952             case 13:
953                 break;
954             case ' ':
955             case '\t':
956                 if (!space) {
957                     sb.append(' ');
958                     space = true;
959                 }
960                 break;
961             default:
962                 sb.append(c);
963                 space = false;
964             }
965         }
966
967         return sb.toString();
968     }
969
970     /**
971      * This class is used to build an AttributedString.
972      */

973     protected static class AttributedStringBuffer {
974
975         /**
976          * The strings.
977          */

978         protected List JavaDoc strings;
979
980         /**
981          * The attributes.
982          */

983         protected List JavaDoc attributes;
984         
985         /**
986          * The number of items.
987          */

988         protected int count;
989
990         /**
991          * The length of the attributed string.
992          */

993         protected int length;
994         
995         /**
996          * Creates a new empty AttributedStringBuffer.
997          */

998         public AttributedStringBuffer() {
999             strings = new ArrayList JavaDoc();
1000            attributes = new ArrayList JavaDoc();
1001            count = 0;
1002            length = 0;
1003        }
1004
1005        /**
1006         * Tells whether this AttributedStringBuffer is empty.
1007         */

1008        public boolean isEmpty() {
1009            return count == 0;
1010        }
1011        
1012        /**
1013         * Returns the length in chars of the current Attributed String
1014         */

1015        public int length() {
1016            return length;
1017        }
1018
1019        /**
1020         * Appends a String and its associated attributes.
1021         */

1022        public void append(String JavaDoc s, Map JavaDoc m) {
1023          if (s.length() == 0) return;
1024          strings.add(s);
1025          attributes.add(m);
1026          count++;
1027          length += s.length();
1028        }
1029
1030        /**
1031         * Returns the value of the last char or -1.
1032         */

1033        public int getLastChar() {
1034            if (count == 0) {
1035                return -1;
1036            }
1037            String JavaDoc s = (String JavaDoc)strings.get(count - 1);
1038            return s.charAt(s.length() - 1);
1039        }
1040
1041        /**
1042         * Strips the last string character.
1043         */

1044        public void stripFirst() {
1045            String JavaDoc s = (String JavaDoc)strings.get(0);
1046            if (s.charAt(s.length() - 1) != ' ')
1047                return;
1048
1049            length--;
1050
1051            if (s.length() == 1) {
1052                attributes.remove(0);
1053                strings.remove(0);
1054                count--;
1055                return;
1056            }
1057
1058            strings.set(0, s.substring(1));
1059        }
1060
1061        /**
1062         * Strips the last string character.
1063         */

1064        public void stripLast() {
1065            String JavaDoc s = (String JavaDoc)strings.get(count - 1);
1066            if (s.charAt(s.length() - 1) != ' ')
1067                return;
1068
1069            length--;
1070
1071            if (s.length() == 1) {
1072                attributes.remove(--count);
1073                strings.remove(count);
1074                return;
1075            }
1076
1077            strings.set(count-1, s.substring(0, s.length() - 1));
1078        }
1079
1080        /**
1081         * Builds an attributed string from the content of this
1082         * buffer.
1083         */

1084        public AttributedString JavaDoc toAttributedString() {
1085            switch (count) {
1086            case 0:
1087                return new AttributedString JavaDoc(" ");
1088            case 1:
1089                return new AttributedString JavaDoc((String JavaDoc)strings.get(0),
1090                                            (Map JavaDoc)attributes.get(0));
1091            }
1092
1093            StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1094            Iterator JavaDoc it = strings.iterator();
1095            while (it.hasNext()) {
1096                sb.append((String JavaDoc)it.next());
1097            }
1098
1099            AttributedString JavaDoc result = new AttributedString JavaDoc(sb.toString());
1100
1101            // Set the attributes
1102

1103            Iterator JavaDoc sit = strings.iterator();
1104            Iterator JavaDoc ait = attributes.iterator();
1105            int idx = 0;
1106            while (sit.hasNext()) {
1107                String JavaDoc s = (String JavaDoc)sit.next();
1108                int nidx = idx + s.length();
1109                Map JavaDoc m = (Map JavaDoc)ait.next();
1110                Iterator JavaDoc kit = m.keySet().iterator();
1111                Iterator JavaDoc vit = m.values().iterator();
1112                while (kit.hasNext()) {
1113                    Attribute JavaDoc attr = (Attribute JavaDoc)kit.next();
1114                    Object JavaDoc val = vit.next();
1115                    result.addAttribute(attr, val, idx, nidx);
1116                }
1117                idx = nidx;
1118            }
1119
1120            return result;
1121        }
1122
1123        public String JavaDoc toString() {
1124            switch (count) {
1125            case 0:
1126                return "";
1127            case 1:
1128                return (String JavaDoc)strings.get(0);
1129            }
1130
1131            StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1132            Iterator JavaDoc it = strings.iterator();
1133            while (it.hasNext()) {
1134                sb.append((String JavaDoc)it.next());
1135            }
1136            return sb.toString();
1137        }
1138    }
1139
1140    /**
1141     * Returns true if node1 is an ancestor of node2
1142     */

1143    protected boolean nodeAncestorOf(Node node1, Node node2) {
1144        if (node2 == null || node1 == null) {
1145            return false;
1146        }
1147        Node parent = node2.getParentNode();
1148        while (parent != null && parent != node1) {
1149            parent = parent.getParentNode();
1150        }
1151        return (parent == node1);
1152    }
1153
1154
1155    /**
1156     * Adds glyph position attributes to an AttributedString.
1157     */

1158    protected void addGlyphPositionAttributes(AttributedString JavaDoc as,
1159                                              Element element,
1160                                              BridgeContext ctx) {
1161
1162        // 'requiredFeatures', 'requiredExtensions' and 'systemLanguage'
1163
if ((!SVGUtilities.matchUserAgent(element, ctx.getUserAgent())) ||
1164            (!CSSUtilities.convertDisplay(element))) {
1165            return;
1166        }
1167        if (element.getLocalName().equals(SVG_TEXT_PATH_TAG)) {
1168            // 'textPath' doesn't support position attributes.
1169
addChildGlyphPositionAttributes(as, element, ctx);
1170            return;
1171        }
1172
1173        AttributedCharacterIterator JavaDoc aci = as.getIterator();
1174
1175        // calculate which chars in the string belong to this element
1176
int firstChar = getElementStartIndex(aci, element);
1177        // No match so no chars to annotate.
1178
if (firstChar == -1) return;
1179
1180        int lastChar = getElementEndIndex(aci, element);
1181
1182        // get all of the glyph position attribute values
1183
String JavaDoc xAtt = element.getAttributeNS(null, SVG_X_ATTRIBUTE);
1184        String JavaDoc yAtt = element.getAttributeNS(null, SVG_Y_ATTRIBUTE);
1185        String JavaDoc dxAtt = element.getAttributeNS(null, SVG_DX_ATTRIBUTE);
1186        String JavaDoc dyAtt = element.getAttributeNS(null, SVG_DY_ATTRIBUTE);
1187        String JavaDoc rotateAtt = element.getAttributeNS(null, SVG_ROTATE_ATTRIBUTE);
1188
1189        ArrayList JavaDoc al;
1190        int len;
1191
1192        // process the x attribute
1193
if (xAtt.length() != 0) {
1194            al = TextUtilities.svgHorizontalCoordinateArrayToUserSpace
1195                (element, SVG_X_ATTRIBUTE, xAtt, ctx);
1196            len = al.size();
1197            for (int i = 0; i < len; i++) {
1198                if (firstChar + i <= lastChar) {
1199                    as.addAttribute
1200                        (GVTAttributedCharacterIterator.TextAttribute.X,
1201                         al.get(i), firstChar+i, firstChar+i+1);
1202                }
1203            }
1204        }
1205
1206
1207       // process the y attribute
1208
if (yAtt.length() != 0) {
1209            al = TextUtilities.svgVerticalCoordinateArrayToUserSpace
1210                (element, SVG_Y_ATTRIBUTE, yAtt, ctx);
1211            len = al.size();
1212
1213            for (int i = 0; i < len; i++) {
1214                if (firstChar+i <= lastChar) {
1215                    as.addAttribute
1216                        (GVTAttributedCharacterIterator.TextAttribute.Y,
1217                         al.get(i), firstChar+i, firstChar+i+1);
1218                }
1219            }
1220        }
1221
1222        // process dx attribute
1223
if (dxAtt.length() != 0) {
1224            al = TextUtilities.svgHorizontalCoordinateArrayToUserSpace
1225                (element, SVG_DX_ATTRIBUTE, dxAtt, ctx);
1226            len = al.size();
1227
1228            for (int i = 0; i < len; i++) {
1229                if (firstChar+i <= lastChar) {
1230                    as.addAttribute
1231                        (GVTAttributedCharacterIterator.TextAttribute.DX,
1232                         al.get(i), firstChar+i, firstChar+i+1);
1233                }
1234            }
1235        }
1236
1237        // process dy attribute
1238
if (dyAtt.length() != 0) {
1239            al = TextUtilities.svgVerticalCoordinateArrayToUserSpace
1240                (element, SVG_DY_ATTRIBUTE, dyAtt, ctx);
1241            len = al.size();
1242
1243            for (int i = 0; i < len; i++) {
1244                if (firstChar+i <= lastChar) {
1245                    as.addAttribute
1246                        (GVTAttributedCharacterIterator.TextAttribute.DY,
1247                         al.get(i), firstChar+i, firstChar+i+1);
1248                }
1249            }
1250        }
1251
1252        // process rotate attribute
1253
if (rotateAtt.length() != 0) {
1254            al = TextUtilities.svgRotateArrayToFloats
1255                (element, SVG_ROTATE_ATTRIBUTE, rotateAtt, ctx);
1256            len = al.size();
1257
1258            if (len == 1) { // not a list
1259
// each char will have the same rotate value
1260
as.addAttribute
1261                    (GVTAttributedCharacterIterator.TextAttribute.ROTATION,
1262                     al.get(0), firstChar, lastChar + 1);
1263
1264            } else { // its a list
1265
// set each rotate value from the list
1266
for (int i = 0; i < len; i++) {
1267                    if (firstChar+i <= lastChar) {
1268                        as.addAttribute
1269                            (GVTAttributedCharacterIterator.
1270                             TextAttribute.ROTATION,
1271                             al.get(i), firstChar+i, firstChar+i+1);
1272                    }
1273                }
1274            }
1275        }
1276
1277        addChildGlyphPositionAttributes(as, element, ctx);
1278    }
1279
1280    protected void addChildGlyphPositionAttributes(AttributedString JavaDoc as,
1281                                                   Element element,
1282                                                   BridgeContext ctx) {
1283        // do the same for each child element
1284
for (Node child = element.getFirstChild();
1285             child != null;
1286             child = child.getNextSibling()) {
1287            if (child.getNodeType() != Node.ELEMENT_NODE) continue;
1288
1289            Element childElement = (Element)child;
1290            if (isTextChild(childElement)) {
1291                addGlyphPositionAttributes(as, childElement, ctx);
1292            }
1293        }
1294    }
1295
1296    /**
1297     * Adds painting attributes to an AttributedString.
1298     */

1299    protected void addPaintAttributes(AttributedString JavaDoc as,
1300                                      Element element,
1301                                      TextNode node,
1302                                      TextPaintInfo pi,
1303                                      BridgeContext ctx) {
1304        // 'requiredFeatures', 'requiredExtensions' and 'systemLanguage'
1305
if ((!SVGUtilities.matchUserAgent(element, ctx.getUserAgent())) ||
1306            (!CSSUtilities.convertDisplay(element))) {
1307            return;
1308        }
1309        Object JavaDoc o = elemTPI.get(element);
1310        if (o == null) return;
1311        node.swapTextPaintInfo(pi, (TextPaintInfo)o);
1312        addChildPaintAttributes(as, element, node, pi, ctx);
1313    }
1314
1315    protected void addChildPaintAttributes(AttributedString JavaDoc as,
1316                                           Element element,
1317                                           TextNode node,
1318                                           TextPaintInfo parentPI,
1319                                           BridgeContext ctx) {
1320        // Add Paint attributres for children of text element
1321
for (Node child = element.getFirstChild();
1322             child != null;
1323             child = child.getNextSibling()) {
1324            if (child.getNodeType() != Node.ELEMENT_NODE) {
1325                continue;
1326            }
1327            Element childElement = (Element)child;
1328            if (isTextChild(childElement)) {
1329                TextPaintInfo pi = getTextPaintInfo(childElement, node,
1330                                                        parentPI, ctx);
1331                addPaintAttributes(as, childElement, node, pi, ctx);
1332            }
1333        }
1334    }
1335
1336    protected Map JavaDoc getFontProperties(BridgeContext ctx, Element element,
1337                                    Map JavaDoc map) {
1338        if (map == null) map = new HashMap JavaDoc(4);
1339
1340        // Needed for SVG fonts.
1341
map.put(TEXT_COMPOUND_DELIMITER, element);
1342
1343        // Font size.
1344
map.put(TextAttribute.SIZE, TextUtilities.convertFontSize(element));
1345
1346        // Font stretch
1347
map.put(TextAttribute.WIDTH, TextUtilities.convertFontStretch(element));
1348
1349        // Font weight
1350
map.put(TextAttribute.WEIGHT, TextUtilities.convertFontWeight(element));
1351
1352        // Font style
1353
map.put(TextAttribute.POSTURE, TextUtilities.convertFontStyle(element));
1354
1355        return map;
1356    }
1357
1358    /**
1359     * Returns the map to pass to the current characters.
1360     */

1361    protected Map JavaDoc getAttributeMap(BridgeContext ctx,
1362                                  Element element,
1363                                  TextPath textPath,
1364                                  Integer JavaDoc bidiLevel) {
1365        UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, element);
1366
1367        Map JavaDoc result = new HashMap JavaDoc();
1368        String JavaDoc s;
1369        float f;
1370        
1371        if (SVG_NAMESPACE_URI.equals(element.getNamespaceURI()) &&
1372            element.getLocalName().equals(SVG_ALT_GLYPH_TAG)) {
1373            result.put(ALT_GLYPH_HANDLER,
1374                       new SVGAltGlyphHandler(ctx, element));
1375        }
1376
1377        if (textPath != null) {
1378            result.put(GVTAttributedCharacterIterator.TextAttribute.TEXTPATH,
1379                       textPath);
1380        }
1381
1382        // Text-anchor
1383
TextNode.Anchor a = TextUtilities.convertTextAnchor(element);
1384        result.put(GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE,
1385                   a);
1386
1387        // get font size/width/weight/posture properties
1388
getFontProperties(ctx, element, result);
1389
1390        // Font family
1391
List JavaDoc fontFamilyList = getFontFamilyList(element, ctx);
1392        result.put
1393            (GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES,
1394             fontFamilyList);
1395
1396        // Text baseline adjustment.
1397
Object JavaDoc bs = TextUtilities.convertBaselineShift(element);
1398        if (bs != null) {
1399            result.put(GVTAttributedCharacterIterator.
1400                       TextAttribute.BASELINE_SHIFT, bs);
1401        }
1402
1403        // Unicode-bidi mode
1404
Value val = CSSUtilities.getComputedStyle
1405            (element, SVGCSSEngine.UNICODE_BIDI_INDEX);
1406        s = val.getStringValue();
1407        if (s.charAt(0) == 'n') {
1408            if (bidiLevel != null)
1409                result.put(TextAttribute.BIDI_EMBEDDING, bidiLevel);
1410        } else {
1411
1412            // Text direction
1413
// XXX: this needs to coordinate with the unicode-bidi
1414
// property, so that when an explicit reversal
1415
// occurs, the BIDI_EMBEDDING level is
1416
// appropriately incremented or decremented.
1417
// Note that direction is implicitly handled by unicode
1418
// BiDi algorithm in most cases, this property
1419
// is only needed when one wants to override the
1420
// normal writing direction for a string/substring.
1421

1422            val = CSSUtilities.getComputedStyle
1423                (element, SVGCSSEngine.DIRECTION_INDEX);
1424            String JavaDoc rs = val.getStringValue();
1425        int cbidi = 0;
1426        if (bidiLevel != null) cbidi = bidiLevel.intValue();
1427
1428        // We don't care if it was embed or override we just want
1429
// it's level here. So map override to positive value.
1430
if (cbidi < 0) cbidi = -cbidi;
1431
1432            switch (rs.charAt(0)) {
1433            case 'l':
1434                result.put(TextAttribute.RUN_DIRECTION,
1435                           TextAttribute.RUN_DIRECTION_LTR);
1436        if ((cbidi & 0x1) == 1) cbidi++; // was odd now even
1437
else cbidi+=2; // next greater even number
1438
break;
1439            case 'r':
1440                result.put(TextAttribute.RUN_DIRECTION,
1441                           TextAttribute.RUN_DIRECTION_RTL);
1442        if ((cbidi & 0x1) == 1) cbidi+=2; // next greater odd number
1443
else cbidi++; // was even now odd
1444
break;
1445        }
1446        
1447        switch (s.charAt(0)) {
1448        case 'b': // bidi-override
1449
cbidi = -cbidi; // For bidi-override we want a negative number.
1450
break;
1451        }
1452
1453        result.put(TextAttribute.BIDI_EMBEDDING, new Integer JavaDoc(cbidi));
1454        }
1455
1456        // Writing mode
1457

1458        val = CSSUtilities.getComputedStyle
1459            (element, SVGCSSEngine.WRITING_MODE_INDEX);
1460        s = val.getStringValue();
1461        switch (s.charAt(0)) {
1462        case 'l':
1463            result.put(GVTAttributedCharacterIterator.
1464                       TextAttribute.WRITING_MODE,
1465                       GVTAttributedCharacterIterator.
1466                       TextAttribute.WRITING_MODE_LTR);
1467            break;
1468        case 'r':
1469            result.put(GVTAttributedCharacterIterator.
1470                       TextAttribute.WRITING_MODE,
1471                       GVTAttributedCharacterIterator.
1472                       TextAttribute.WRITING_MODE_RTL);
1473            break;
1474        case 't':
1475                result.put(GVTAttributedCharacterIterator.
1476                       TextAttribute.WRITING_MODE,
1477                       GVTAttributedCharacterIterator.
1478                       TextAttribute.WRITING_MODE_TTB);
1479            break;
1480        }
1481
1482        // glyph-orientation-vertical
1483

1484        val = CSSUtilities.getComputedStyle
1485            (element, SVGCSSEngine.GLYPH_ORIENTATION_VERTICAL_INDEX);
1486        switch (val.getPrimitiveType()) {
1487        case CSSPrimitiveValue.CSS_IDENT: // auto
1488
result.put(GVTAttributedCharacterIterator.
1489                       TextAttribute.VERTICAL_ORIENTATION,
1490                       GVTAttributedCharacterIterator.
1491                       TextAttribute.ORIENTATION_AUTO);
1492            break;
1493        case CSSPrimitiveValue.CSS_DEG:
1494            result.put(GVTAttributedCharacterIterator.
1495                       TextAttribute.VERTICAL_ORIENTATION,
1496                       GVTAttributedCharacterIterator.
1497                       TextAttribute.ORIENTATION_ANGLE);
1498            result.put(GVTAttributedCharacterIterator.
1499                       TextAttribute.VERTICAL_ORIENTATION_ANGLE,
1500                       new Float JavaDoc(val.getFloatValue()));
1501            break;
1502        case CSSPrimitiveValue.CSS_RAD:
1503            result.put(GVTAttributedCharacterIterator.
1504                       TextAttribute.VERTICAL_ORIENTATION,
1505                       GVTAttributedCharacterIterator.
1506                       TextAttribute.ORIENTATION_ANGLE);
1507            result.put(GVTAttributedCharacterIterator.
1508                       TextAttribute.VERTICAL_ORIENTATION_ANGLE,
1509                       new Float JavaDoc(val.getFloatValue() * 180 / Math.PI));
1510            break;
1511        case CSSPrimitiveValue.CSS_GRAD:
1512            result.put(GVTAttributedCharacterIterator.
1513                       TextAttribute.VERTICAL_ORIENTATION,
1514                       GVTAttributedCharacterIterator.
1515                       TextAttribute.ORIENTATION_ANGLE);
1516            result.put(GVTAttributedCharacterIterator.
1517                       TextAttribute.VERTICAL_ORIENTATION_ANGLE,
1518                       new Float JavaDoc(val.getFloatValue() * 9 / 5));
1519            break;
1520        default:
1521            // Cannot happen
1522
throw new InternalError JavaDoc();
1523        }
1524
1525        // glyph-orientation-horizontal
1526

1527        val = CSSUtilities.getComputedStyle
1528            (element, SVGCSSEngine.GLYPH_ORIENTATION_HORIZONTAL_INDEX);
1529        switch (val.getPrimitiveType()) {
1530        case CSSPrimitiveValue.CSS_DEG:
1531            result.put(GVTAttributedCharacterIterator.
1532                       TextAttribute.HORIZONTAL_ORIENTATION_ANGLE,
1533                       new Float JavaDoc(val.getFloatValue()));
1534            break;
1535        case CSSPrimitiveValue.CSS_RAD:
1536            result.put(GVTAttributedCharacterIterator.
1537                       TextAttribute.HORIZONTAL_ORIENTATION_ANGLE,
1538                       new Float JavaDoc(val.getFloatValue() * 180 / Math.PI));
1539            break;
1540        case CSSPrimitiveValue.CSS_GRAD:
1541            result.put(GVTAttributedCharacterIterator.
1542                       TextAttribute.HORIZONTAL_ORIENTATION_ANGLE,
1543                       new Float JavaDoc(val.getFloatValue() * 9 / 5));
1544            break;
1545        default:
1546            // Cannot happen
1547
throw new InternalError JavaDoc();
1548        }
1549
1550        // text spacing properties...
1551

1552        // Letter Spacing
1553
Float JavaDoc sp = TextUtilities.convertLetterSpacing(element);
1554        if (sp != null) {
1555            result.put(GVTAttributedCharacterIterator.
1556                       TextAttribute.LETTER_SPACING,
1557                       sp);
1558            result.put(GVTAttributedCharacterIterator.
1559                       TextAttribute.CUSTOM_SPACING,
1560                       Boolean.TRUE);
1561        }
1562
1563        // Word spacing
1564
sp = TextUtilities.convertWordSpacing(element);
1565        if (sp != null) {
1566            result.put(GVTAttributedCharacterIterator.
1567                       TextAttribute.WORD_SPACING,
1568                       sp);
1569            result.put(GVTAttributedCharacterIterator.
1570                       TextAttribute.CUSTOM_SPACING,
1571                       Boolean.TRUE);
1572        }
1573
1574        // Kerning
1575
sp = TextUtilities.convertKerning(element);
1576        if (sp != null) {
1577            result.put(GVTAttributedCharacterIterator.TextAttribute.KERNING,
1578                       sp);
1579            result.put(GVTAttributedCharacterIterator.
1580                       TextAttribute.CUSTOM_SPACING,
1581                       Boolean.TRUE);
1582        }
1583
1584        // textLength
1585
s = element.getAttributeNS(null, SVG_TEXT_LENGTH_ATTRIBUTE);
1586        if (s.length() != 0) {
1587            f = UnitProcessor.svgOtherLengthToUserSpace
1588                (s, SVG_TEXT_LENGTH_ATTRIBUTE, uctx);
1589            result.put(GVTAttributedCharacterIterator.TextAttribute.BBOX_WIDTH,
1590                       new Float JavaDoc(f));
1591
1592            // lengthAdjust
1593
s = element.getAttributeNS(null, SVG_LENGTH_ADJUST_ATTRIBUTE);
1594
1595            if (s.length() < 10) {
1596                result.put(GVTAttributedCharacterIterator.
1597                           TextAttribute.LENGTH_ADJUST,
1598                           GVTAttributedCharacterIterator.
1599                           TextAttribute.ADJUST_SPACING);
1600                result.put(GVTAttributedCharacterIterator.
1601                           TextAttribute.CUSTOM_SPACING,
1602                           Boolean.TRUE);
1603            } else {
1604                result.put(GVTAttributedCharacterIterator.
1605                           TextAttribute.LENGTH_ADJUST,
1606                           GVTAttributedCharacterIterator.
1607                           TextAttribute.ADJUST_ALL);
1608            }
1609        }
1610
1611        return result;
1612    }
1613
1614
1615    protected List JavaDoc getFontFamilyList(Element element, BridgeContext ctx) {
1616        // Font weight
1617
Value v = CSSUtilities.getComputedStyle
1618            (element, SVGCSSEngine.FONT_WEIGHT_INDEX);
1619        String JavaDoc fontWeightString = v.getCssText();
1620
1621        // Font style
1622
String JavaDoc fontStyleString = CSSUtilities.getComputedStyle
1623            (element, SVGCSSEngine.FONT_STYLE_INDEX).getStringValue();
1624
1625        Value val = CSSUtilities.getComputedStyle
1626            (element, SVGCSSEngine.FONT_FAMILY_INDEX);
1627        // make a list of GVTFontFamily objects
1628
List JavaDoc fontFamilyList = new ArrayList JavaDoc();
1629        int len = val.getLength();
1630        for (int i = 0; i < len; i++) {
1631            Value it = val.item(i);
1632            String JavaDoc fontFamilyName = it.getStringValue();
1633            GVTFontFamily fontFamily
1634                = SVGFontUtilities.getFontFamily(element, ctx, fontFamilyName,
1635                                                 fontWeightString,
1636                                                 fontStyleString);
1637            if (fontFamily instanceof SVGFontFamily) {
1638                SVGFontFamily svgFF = (SVGFontFamily)fontFamily;
1639                if (svgFF.isComplex()) {
1640                    usingComplexSVGFont = true;
1641                }
1642            }
1643            fontFamilyList.add(fontFamily);
1644        }
1645        return fontFamilyList;
1646    }
1647
1648    /**
1649     * Retrieve in the AttributeString the closest parent
1650     * of the node 'child' and extract the text decorations
1651     * of the parent.
1652     *
1653     * @param aci an <code>AttributedCharacterIterator</code> value
1654     * @param child an <code>Element</code> value
1655     * @return a <code>TextDecoration</code> value
1656     */

1657    protected TextPaintInfo getParentTextPaintInfo
1658        (AttributedCharacterIterator JavaDoc aci, Element child) {
1659        Element parent = null;
1660
1661        // calculate which chars in the string belong to the parent
1662
int firstChar = -1;
1663        for (int i = 0; i < aci.getEndIndex();) {
1664            aci.setIndex(i);
1665            Element delimeter;
1666            delimeter = (Element)aci.getAttribute(TEXT_COMPOUND_DELIMITER);
1667            if (nodeAncestorOf(delimeter,child) &&
1668                ((parent == null) || nodeAncestorOf(parent, delimeter))) {
1669                parent = delimeter;
1670                firstChar = i;
1671            }
1672            if (delimeter == child || nodeAncestorOf(child, delimeter)) {
1673                break;
1674            }
1675            i = aci.getRunLimit(TEXT_COMPOUND_DELIMITER);
1676        }
1677
1678        if ( parent == null)
1679            return new TextPaintInfo(); //no parent
1680

1681        aci.setIndex(firstChar);
1682        return (TextPaintInfo)aci.getAttribute(PAINT_INFO);
1683    }
1684
1685    /**
1686     * Constructs a TextDecoration object for the specified element. This will
1687     * contain all of the decoration properties to be used when drawing the
1688     * text.
1689     */

1690    protected TextPaintInfo getTextPaintInfo(Element element,
1691                                             GraphicsNode node,
1692                                             TextPaintInfo parent,
1693                                             BridgeContext ctx) {
1694        // Force the engine to update stuff..
1695
Value val = CSSUtilities.getComputedStyle
1696            (element, SVGCSSEngine.TEXT_DECORATION_INDEX);
1697
1698        TextPaintInfo pi = new TextPaintInfo(parent);
1699
1700        // Was text-decoration explicity set on this element?
1701
StyleMap sm = ((CSSStylableElement)element).getComputedStyleMap(null);
1702        if ((sm.isNullCascaded(SVGCSSEngine.TEXT_DECORATION_INDEX)) &&
1703            (sm.isNullCascaded(SVGCSSEngine.FILL_INDEX)) &&
1704            (sm.isNullCascaded(SVGCSSEngine.STROKE_INDEX)) &&
1705            (sm.isNullCascaded(SVGCSSEngine.STROKE_WIDTH_INDEX)) &&
1706            (sm.isNullCascaded(SVGCSSEngine.OPACITY_INDEX))) {
1707            // If not, keep the same decorations.
1708
return pi;
1709        }
1710
1711        setBaseTextPaintInfo(pi, element, node, ctx);
1712
1713        if (!sm.isNullCascaded(SVGCSSEngine.TEXT_DECORATION_INDEX))
1714            setDecorationTextPaintInfo(pi, element);
1715
1716        return pi;
1717    }
1718
1719    public void setBaseTextPaintInfo(TextPaintInfo pi, Element element,
1720                                     GraphicsNode node, BridgeContext ctx) {
1721        if (!element.getLocalName().equals(SVG_TEXT_TAG))
1722            pi.composite = CSSUtilities.convertOpacity (element);
1723        else
1724            pi.composite = AlphaComposite.SrcOver;
1725
1726        pi.visible = CSSUtilities.convertVisibility(element);
1727        pi.fillPaint = PaintServer.convertFillPaint (element, node, ctx);
1728        pi.strokePaint = PaintServer.convertStrokePaint(element, node, ctx);
1729        pi.strokeStroke = PaintServer.convertStroke (element);
1730    }
1731
1732    public void setDecorationTextPaintInfo(TextPaintInfo pi, Element element) {
1733        Value val = CSSUtilities.getComputedStyle
1734            (element, SVGCSSEngine.TEXT_DECORATION_INDEX);
1735
1736        switch (val.getCssValueType()) {
1737        case CSSValue.CSS_VALUE_LIST:
1738            ListValue lst = (ListValue)val;
1739
1740            int len = lst.getLength();
1741            for (int i = 0; i < len; i++) {
1742                Value v = lst.item(i);
1743                String JavaDoc s = v.getStringValue();
1744                switch (s.charAt(0)) {
1745                case 'u':
1746                    if (pi.fillPaint != null) {
1747                        pi.underlinePaint = pi.fillPaint;
1748                    }
1749                    if (pi.strokePaint != null) {
1750                        pi.underlineStrokePaint = pi.strokePaint;
1751                    }
1752                    if (pi.strokeStroke != null) {
1753                        pi.underlineStroke = pi.strokeStroke;
1754                    }
1755                    break;
1756                case 'o':
1757                    if (pi.fillPaint != null) {
1758                        pi.overlinePaint = pi.fillPaint;
1759                    }
1760                    if (pi.strokePaint != null) {
1761                        pi.overlineStrokePaint = pi.strokePaint;
1762                    }
1763                    if (pi.strokeStroke != null) {
1764                        pi.overlineStroke = pi.strokeStroke;
1765                    }
1766                    break;
1767                case 'l':
1768                    if (pi.fillPaint != null) {
1769                        pi.strikethroughPaint = pi.fillPaint;
1770                    }
1771                    if (pi.strokePaint != null) {
1772                        pi.strikethroughStrokePaint = pi.strokePaint;
1773                    }
1774                    if (pi.strokeStroke != null) {
1775                        pi.strikethroughStroke = pi.strokeStroke;
1776                    }
1777                    break;
1778                }
1779            }
1780            break;
1781
1782        default: // None
1783
pi.underlinePaint = null;
1784            pi.underlineStrokePaint = null;
1785            pi.underlineStroke = null;
1786            
1787            pi.overlinePaint = null;
1788            pi.overlineStrokePaint = null;
1789            pi.overlineStroke = null;
1790            
1791            pi.strikethroughPaint = null;
1792            pi.strikethroughStrokePaint = null;
1793            pi.strikethroughStroke = null;
1794            break;
1795        }
1796    }
1797
1798    /**
1799     * Implementation of <ode>SVGContext</code> for
1800     * the children of &lt;text&gt;
1801     */

1802    public abstract class AbstractTextChildSVGContext
1803        implements SVGContext {
1804
1805        /** Bridge Context */
1806        protected BridgeContext ctx;
1807
1808        /** Text bridge parent */
1809        protected SVGTextElementBridge textBridge;
1810
1811        /** Element */
1812        protected Element e;
1813
1814        /**
1815         * Initialize the <code>SVGContext</code> implementation
1816         * with the bridgeContext, the parent bridge, and the
1817         * element supervised by this context
1818         */

1819        public AbstractTextChildSVGContext(BridgeContext ctx,
1820                                           SVGTextElementBridge parent,
1821                                           Element e) {
1822            this.ctx = ctx;
1823            this.textBridge = parent;
1824            this.e = e;
1825        }
1826
1827        public SVGTextElementBridge getTextBridge() { return textBridge; }
1828        
1829        /**
1830         * Returns the size of a px CSS unit in millimeters.
1831         */

1832        public float getPixelUnitToMillimeter() {
1833            return ctx.getUserAgent().getPixelUnitToMillimeter();
1834        }
1835
1836        /**
1837         * Returns the size of a px CSS unit in millimeters.
1838         * This will be removed after next release.
1839         * @see #getPixelUnitToMillimeter()
1840         */

1841        public float getPixelToMM() {
1842            return getPixelUnitToMillimeter();
1843            
1844        }
1845        /**
1846         * Returns the tight bounding box in current user space (i.e.,
1847         * after application of the transform attribute, if any) on the
1848         * geometry of all contained graphics elements, exclusive of
1849         * stroke-width and filter effects).
1850         */

1851        public Rectangle2D JavaDoc getBBox() {
1852            //text children does not support getBBox
1853
//return textBridge.getBBox();
1854
return null;
1855        }
1856
1857        /**
1858         * Returns the transformation matrix from current user units
1859         * (i.e., after application of the transform attribute, if any) to
1860         * the viewport coordinate system for the nearestViewportElement.
1861         */

1862        public AffineTransform JavaDoc getCTM() {
1863            // text children does not support transform attribute
1864
//return textBridge.getCTM();
1865
return null;
1866        }
1867
1868        /**
1869         * Returns the global transformation matrix from the current
1870         * element to the root.
1871         */

1872        public AffineTransform JavaDoc getGlobalTransform() {
1873            //return node.getGlobalTransform();
1874
return null;
1875        }
1876
1877        /**
1878         * Returns the transformation matrix from the userspace of
1879         * the root element to the screen.
1880         */

1881        public AffineTransform JavaDoc getScreenTransform() {
1882            //return node.getScreenTransform();
1883
return null;
1884        }
1885
1886        /**
1887         * Sets the transformation matrix to be used from the
1888         * userspace of the root element to the screen.
1889         */

1890        public void setScreenTransform(AffineTransform JavaDoc at) {
1891            //return node.setScreenTransform(at);
1892
return;
1893        }
1894
1895        /**
1896         * Returns the width of the viewport which directly contains the
1897         * given element.
1898         */

1899        public float getViewportWidth() {
1900            return ctx.getBlockWidth(e);
1901        }
1902        
1903        /**
1904         * Returns the height of the viewport which directly contains the
1905         * given element.
1906         */

1907        public float getViewportHeight() {
1908            return ctx.getBlockHeight(e);
1909        }
1910        
1911        /**
1912         * Returns the font-size on the associated element.
1913         */

1914        public float getFontSize() {
1915            return CSSUtilities.getComputedStyle
1916                (e, SVGCSSEngine.FONT_SIZE_INDEX).getFloatValue();
1917        }
1918    }
1919
1920    /**
1921     * Implementation for the <code>BridgeUpdateHandler</code>
1922     * for the child elements of &lt;text&gt;.
1923     * This implementation relies on the parent bridge
1924     * which contains the <code>TextNode</code>
1925     * representing the node this context supervised.
1926     * All operations are done by the parent bridge
1927     * <code>SVGTextElementBridge</code> which can determine
1928     * the impact of a change of one of its children for the others.
1929     */

1930    protected abstract class AbstractTextChildBridgeUpdateHandler
1931        extends AbstractTextChildSVGContext implements BridgeUpdateHandler {
1932
1933        /**
1934         * Initialize the BridgeUpdateHandler implementation.
1935         */

1936        public AbstractTextChildBridgeUpdateHandler
1937            (BridgeContext ctx,
1938             SVGTextElementBridge parent,
1939             Element e) {
1940
1941            super(ctx,parent,e);
1942        }
1943        /**
1944         * Invoked when an MutationEvent of type 'DOMAttrModified' is fired.
1945         */

1946        public void handleDOMAttrModifiedEvent(MutationEvent JavaDoc evt) {
1947            //nothing to do
1948
}
1949
1950        /**
1951         * Invoked when an MutationEvent of type 'DOMNodeInserted' is fired.
1952         */

1953        public void handleDOMNodeInsertedEvent(MutationEvent JavaDoc evt) {
1954            textBridge.handleDOMNodeInsertedEvent(evt);
1955        }
1956
1957        /**
1958         * Invoked when an MutationEvent of type 'DOMNodeRemoved' is fired.
1959         */

1960        public void handleDOMNodeRemovedEvent(MutationEvent JavaDoc evt) {
1961            //nothing to do
1962
dispose();
1963        }
1964
1965        /**
1966         * Invoked when an MutationEvent of type 'DOMCharacterDataModified'
1967         * is fired.
1968         */

1969        public void handleDOMCharacterDataModified(MutationEvent JavaDoc evt) {
1970            textBridge.handleDOMCharacterDataModified(evt);
1971        }
1972
1973        /**
1974         * Invoked when an CSSEngineEvent is fired.
1975         */

1976        public void handleCSSEngineEvent(CSSEngineEvent evt) {
1977            textBridge.handleCSSEngineEvent(evt);
1978        }
1979
1980        /**
1981         * Disposes this BridgeUpdateHandler and releases all resources.
1982         */

1983        public void dispose(){
1984            ((SVGOMElement)e).setSVGContext(null);
1985            elemTPI.remove(e);
1986        }
1987    }
1988
1989    protected class AbstractTextChildTextContent
1990        extends AbstractTextChildBridgeUpdateHandler
1991        implements SVGTextContent{
1992
1993        /**
1994         * Initialize the AbstractTextChildBridgeUpdateHandler implementation.
1995         */

1996        public AbstractTextChildTextContent
1997            (BridgeContext ctx,
1998             SVGTextElementBridge parent,
1999             Element e) {
2000
2001            super(ctx,parent,e);
2002        }
2003
2004        //Implementation of TextContent
2005

2006        public int getNumberOfChars(){
2007            return textBridge.getNumberOfChars(e);
2008        }
2009        
2010        public Rectangle2D JavaDoc getExtentOfChar(int charnum ){
2011            return textBridge.getExtentOfChar(e,charnum);
2012        }
2013
2014        public Point2D JavaDoc getStartPositionOfChar(int charnum){
2015            return textBridge.getStartPositionOfChar(e,charnum);
2016        }
2017
2018        public Point2D JavaDoc getEndPositionOfChar(int charnum){
2019            return textBridge.getEndPositionOfChar(e,charnum);
2020        }
2021
2022        public void selectSubString(int charnum, int nchars){
2023            textBridge.selectSubString(e,charnum,nchars);
2024        }
2025
2026        public float getRotationOfChar(int charnum){
2027            return textBridge.getRotationOfChar(e,charnum);
2028        }
2029
2030        public float getComputedTextLength(){
2031            return textBridge.getComputedTextLength(e);
2032        }
2033        
2034        public float getSubStringLength(int charnum, int nchars){
2035            return textBridge.getSubStringLength(e,charnum,nchars);
2036        }
2037
2038        public int getCharNumAtPosition(float x , float y){
2039            return textBridge.getCharNumAtPosition(e,x,y);
2040        }
2041    }
2042
2043    /**
2044     * BridgeUpdateHandle for &lt;tref&gt; element.
2045     */

2046    protected class TRefBridge
2047        extends AbstractTextChildTextContent {
2048
2049        public TRefBridge(BridgeContext ctx,
2050                          SVGTextElementBridge parent,
2051                          Element e) {
2052            super(ctx,parent,e);
2053        }
2054
2055        /**
2056         * Handle the dynamic update for the attributes of
2057         * &lt;tspan&gt; : 'x', 'y', 'dx', 'dy' and 'rotate'.
2058         */

2059        public void handleDOMAttrModifiedEvent(MutationEvent JavaDoc evt){
2060            String JavaDoc attrName = evt.getAttrName();
2061            if (attrName.equals(SVG_X_ATTRIBUTE) ||
2062                attrName.equals(SVG_Y_ATTRIBUTE) ||
2063                attrName.equals(SVG_DX_ATTRIBUTE) ||
2064                attrName.equals(SVG_DY_ATTRIBUTE) ||
2065                attrName.equals(SVG_ROTATE_ATTRIBUTE)) {
2066                //recompute the layout of the text node
2067
textBridge.computeLaidoutText(ctx, textBridge.e,
2068                                               textBridge.node);
2069            }
2070        }
2071    }
2072
2073    /**
2074     * BridgeUpdateHandle for &lt;textPath&gt; element.
2075     */

2076    protected class TextPathBridge
2077        extends AbstractTextChildTextContent{
2078
2079        public TextPathBridge(BridgeContext ctx,
2080                              SVGTextElementBridge parent,
2081                              Element e){
2082            super(ctx,parent,e);
2083        }
2084    }
2085
2086    /**
2087     * BridgeUpdateHandle for &lt;tspan&gt; element.
2088     */

2089    protected class TspanBridge
2090        extends AbstractTextChildTextContent {
2091
2092        public TspanBridge(BridgeContext ctx,
2093                           SVGTextElementBridge parent,
2094                           Element e){
2095            super(ctx,parent,e);
2096        }
2097        
2098        /**
2099         * Handle the dynamic update for the attributes of
2100         * &lt;tspan&gt; : 'x', 'y', 'dx', 'dy' and 'rotate'.
2101         */

2102        public void handleDOMAttrModifiedEvent(MutationEvent JavaDoc evt){
2103            String JavaDoc attrName = evt.getAttrName();
2104            if (attrName.equals(SVG_X_ATTRIBUTE) ||
2105                attrName.equals(SVG_Y_ATTRIBUTE) ||
2106                attrName.equals(SVG_DX_ATTRIBUTE) ||
2107                attrName.equals(SVG_DY_ATTRIBUTE) ||
2108                attrName.equals(SVG_ROTATE_ATTRIBUTE)) {
2109                //recompute the layout of the text node
2110
textBridge.computeLaidoutText(ctx, textBridge.e,
2111                                               textBridge.node);
2112            }
2113        }
2114    }
2115
2116    //Implementation of TextContent
2117
public int getNumberOfChars(){
2118        return getNumberOfChars(e);
2119    }
2120
2121    public Rectangle2D JavaDoc getExtentOfChar(int charnum ){
2122        return getExtentOfChar(e,charnum);
2123    }
2124
2125    public Point2D JavaDoc getStartPositionOfChar(int charnum){
2126        return getStartPositionOfChar(e,charnum);
2127    }
2128
2129    public Point2D JavaDoc getEndPositionOfChar(int charnum){
2130        return getEndPositionOfChar(e,charnum);
2131    }
2132    
2133    public void selectSubString(int charnum, int nchars){
2134        selectSubString(e,charnum,nchars);
2135    }
2136
2137    public float getRotationOfChar(int charnum){
2138        return getRotationOfChar(e,charnum);
2139    }
2140
2141    public float getComputedTextLength(){
2142        return getComputedTextLength(e);
2143    }
2144
2145    public float getSubStringLength(int charnum, int nchars){
2146        return getSubStringLength(e,charnum,nchars);
2147    }
2148
2149    public int getCharNumAtPosition(float x , float y){
2150        return getCharNumAtPosition(e,x,y);
2151    }
2152
2153    /**
2154     * Implementation of {@link
2155     * org.w3c.dom.svg.SVGTextContentElement#getNumberOfChars()}.
2156     */

2157    protected int getNumberOfChars(Element element){
2158
2159        AttributedCharacterIterator JavaDoc aci = ((TextNode)node).getAttributedCharacterIterator();
2160
2161        //get the index in the aci for the first character
2162
//of the element
2163
int firstChar = getElementStartIndex(aci,element);
2164
2165        if (firstChar == -1)
2166            return 0; // Element not part of aci (no chars in elem usually)
2167

2168        int lastChar = getElementEndIndex(aci,element);
2169
2170        return( lastChar - firstChar + 1 );
2171    }
2172
2173
2174    /**
2175     * Implementation of {@link
2176     * org.w3c.dom.svg.SVGTextContentElement#getExtentOfChar(int charnum)}.
2177     */

2178    protected Rectangle2D JavaDoc getExtentOfChar(Element element,int charnum ){
2179
2180        AttributedCharacterIterator JavaDoc aci;
2181        aci = ((TextNode)node).getAttributedCharacterIterator();
2182
2183        int firstChar = getElementStartIndex(aci,element);
2184
2185        if ( firstChar == -1 )
2186            return null;
2187
2188        //retrieve the text run for the text node
2189
List JavaDoc list = getTextRuns((TextNode)node);
2190
2191        //find the character 'charnum' in the text run
2192
CharacterInformation info;
2193        info = getCharacterInformation(list, firstChar,charnum, aci);
2194            
2195        if ( info == null )
2196            return null;
2197
2198        //retrieve the glyphvector containing the glyph
2199
//for 'charnum'
2200
GVTGlyphVector it = info.layout.getGlyphVector();
2201
2202        Shape JavaDoc b = null;
2203
2204        if ( info.glyphIndexStart == info.glyphIndexEnd ){
2205            if (it.isGlyphVisible(info.glyphIndexStart))
2206                b = it.getGlyphOutline(info.glyphIndexStart);
2207        } else {
2208            GeneralPath JavaDoc path = null;
2209            for( int k = info.glyphIndexStart ;
2210                 k <= info.glyphIndexEnd;
2211                 k++){
2212                if (it.isGlyphVisible(k)) {
2213                    if (path == null)
2214                        path = new GeneralPath JavaDoc(it.getGlyphOutline(k));
2215                    else
2216                        path.append(it.getGlyphOutline(k),false);
2217                }
2218            }
2219            b = path;
2220        }
2221
2222        if (b == null)
2223            return null;
2224                
2225        //return the bounding box of the outline
2226
return b.getBounds2D();
2227    }
2228
2229
2230    /**
2231     * Implementation of {@link
2232     * org.w3c.dom.svg.SVGTextContentElement#getStartPositionOfChar(int charnum)}.
2233     */

2234    protected Point2D JavaDoc getStartPositionOfChar(Element element,int charnum){
2235
2236        AttributedCharacterIterator JavaDoc aci = ((TextNode)node).getAttributedCharacterIterator();
2237
2238        int firstChar = getElementStartIndex(aci,element);
2239
2240        if ( firstChar == -1 )
2241            return null;
2242
2243        //retrieve the text run for the text node
2244
List JavaDoc list = getTextRuns((TextNode)node);
2245
2246        //find the character 'charnum' in the text run
2247
CharacterInformation info;
2248        info = getCharacterInformation(list, firstChar,charnum, aci);
2249
2250        if ( info == null )
2251            return null;
2252        
2253        return getStartPoint( info );
2254    }
2255
2256    protected Point2D JavaDoc getStartPoint(CharacterInformation info){
2257
2258        GVTGlyphVector it = info.layout.getGlyphVector();
2259        if (!it.isGlyphVisible(info.glyphIndexStart))
2260            return null;
2261
2262        Point2D JavaDoc b = it.getGlyphPosition(info.glyphIndexStart);
2263
2264        AffineTransform JavaDoc glyphTransform;
2265        glyphTransform = it.getGlyphTransform(info.glyphIndexStart);
2266
2267
2268        //glyph are defined starting at position (0,0)
2269
Point2D.Float JavaDoc result = new Point2D.Float JavaDoc(0, 0);
2270        if ( glyphTransform != null )
2271            //apply the glyph transformation to the start point
2272
glyphTransform.transform(result,result);
2273
2274        result.x += b.getX();
2275        result.y += b.getY();
2276        return result;
2277    }
2278
2279    /**
2280     * Implementation of {@link
2281     * org.w3c.dom.svg.SVGTextContentElement#getEndPositionOfChar(int charnum)}.
2282     */

2283    protected Point2D JavaDoc getEndPositionOfChar(Element element,int charnum ){
2284
2285        AttributedCharacterIterator JavaDoc aci;
2286        aci = ((TextNode)node).getAttributedCharacterIterator();
2287
2288        int firstChar = getElementStartIndex(aci,element);
2289
2290        if ( firstChar == -1 )
2291            return null;
2292
2293        //retrieve the text run for the text node
2294
List JavaDoc list = getTextRuns((TextNode)node);
2295
2296        //find the glyph information for the character 'charnum'
2297
CharacterInformation info;
2298        info = getCharacterInformation(list, firstChar,charnum, aci);
2299            
2300        if ( info == null )
2301            return null;
2302        return getEndPoint(info);
2303    }
2304
2305    protected Point2D JavaDoc getEndPoint(CharacterInformation info){
2306
2307        GVTGlyphVector it = info.layout.getGlyphVector();
2308        if (!it.isGlyphVisible(info.glyphIndexEnd))
2309            return null;
2310        
2311        Point2D JavaDoc b = it.getGlyphPosition(info.glyphIndexEnd);
2312        
2313        AffineTransform JavaDoc glyphTransform;
2314        glyphTransform = it.getGlyphTransform(info.glyphIndexEnd);
2315        
2316        GVTGlyphMetrics metrics = it.getGlyphMetrics(info.glyphIndexEnd);
2317        
2318        
2319        Point2D.Float JavaDoc result = new Point2D.Float JavaDoc
2320            (metrics.getHorizontalAdvance(), 0);
2321            
2322        if ( glyphTransform != null )
2323            glyphTransform.transform(result,result);
2324
2325        result.x += b.getX();
2326        result.y += b.getY();
2327        return result;
2328    }
2329
2330    /**
2331     * Implementation of {@link
2332     * org.w3c.dom.svg.SVGTextContentElement#getRotationOfChar(int charnum)}.
2333     */

2334    protected float getRotationOfChar(Element element, int charnum){
2335
2336        AttributedCharacterIterator JavaDoc aci;
2337        aci = ((TextNode)node).getAttributedCharacterIterator();
2338        //first the first character for the element
2339
int firstChar = getElementStartIndex(aci,element);
2340
2341        if ( firstChar == -1 )
2342            return 0;
2343
2344        //retrieve the text run for the text node
2345
List JavaDoc list = getTextRuns((TextNode)node);
2346
2347        //find the glyph information for the character 'charnum'
2348
CharacterInformation info;
2349        info = getCharacterInformation(list, firstChar,charnum, aci);
2350            
2351        double angle = 0.0;
2352        int nbGlyphs = 0;
2353
2354        if ( info != null ){
2355            GVTGlyphVector it = info.layout.getGlyphVector();
2356
2357            for( int k = info.glyphIndexStart ;
2358                 k <= info.glyphIndexEnd ;
2359                 k++ ){
2360                if (!it.isGlyphVisible(k)) continue;
2361
2362                nbGlyphs++;
2363                
2364                //the glyph transform contains only a scale and a rotate.
2365
AffineTransform JavaDoc glyphTransform = it.getGlyphTransform(k);
2366                if ( glyphTransform == null ) continue;
2367
2368                double glyphAngle = 0.0;
2369                double cosTheta = glyphTransform.getScaleX();
2370                double sinTheta = glyphTransform.getShearX();
2371                
2372                //extract the angle
2373
if ( cosTheta == 0.0 ){
2374                    if ( sinTheta > 0 ) glyphAngle = Math.PI;
2375                    else glyphAngle = -Math.PI;
2376                } else {
2377                    glyphAngle = Math.atan(sinTheta/cosTheta);
2378                    if ( cosTheta < 0 )
2379                        glyphAngle += Math.PI;
2380                }
2381                //get a degrees value for the angle
2382
//SVG angle are clock wise java anticlockwise
2383

2384                glyphAngle = (Math.toDegrees( - glyphAngle ) ) % 360.0;
2385                
2386                //remove the orientation from the value
2387
angle += glyphAngle - info.getComputedOrientationAngle();
2388            }
2389        }
2390        if (nbGlyphs == 0) return 0;
2391        return (float)(angle / nbGlyphs );
2392    }
2393
2394    /**
2395     * Implementation of {@link
2396     * org.w3c.dom.svg.SVGTextContentElement#getComputedTextLength()}.
2397     */

2398    protected float getComputedTextLength(Element e) {
2399        return getSubStringLength(e,0,getNumberOfChars(e));
2400    }
2401
2402    /**
2403     * Implementation of {@link
2404     * org.w3c.dom.svg.SVGTextContentElement#getSubStringLength(int charnum,int nchars)}.
2405     */

2406    protected float getSubStringLength(Element element,
2407                                       int charnum,
2408                                       int nchars){
2409        if (nchars == 0) {
2410            return 0;
2411        }
2412
2413        float length = 0;
2414
2415        AttributedCharacterIterator JavaDoc aci;
2416        aci = ((TextNode)node).getAttributedCharacterIterator();
2417        TextNode textNode = (TextNode)node;
2418
2419        int firstChar = getElementStartIndex(aci,element);
2420
2421        if ( firstChar == -1 )
2422            return -1;
2423
2424        List JavaDoc list = getTextRuns(textNode);
2425
2426        CharacterInformation currentInfo;
2427        currentInfo = getCharacterInformation(list, firstChar,charnum,aci);
2428        CharacterInformation lastCharacterInRunInfo = null;
2429        int chIndex = currentInfo.characterIndex+1;
2430        GVTGlyphVector vector = currentInfo.layout.getGlyphVector();
2431        float [] advs = currentInfo.layout.getGlyphAdvances();
2432        boolean [] glyphTrack = new boolean[advs.length];
2433        for( int k = charnum +1; k < charnum +nchars ; k++) {
2434            if (currentInfo.layout.isOnATextPath() ){
2435                for (int gi = currentInfo.glyphIndexStart;
2436                     gi <= currentInfo.glyphIndexEnd; gi++) {
2437                    if ((vector.isGlyphVisible(gi)) && !glyphTrack[gi])
2438                        length += advs[gi+1]-advs[gi];
2439                    glyphTrack[gi] = true;
2440                }
2441                CharacterInformation newInfo;
2442                newInfo = getCharacterInformation(list, firstChar, k, aci);
2443                if (newInfo.layout != currentInfo.layout) {
2444                    vector = newInfo.layout.getGlyphVector();
2445                    advs = newInfo.layout.getGlyphAdvances();
2446                    glyphTrack = new boolean[advs.length];
2447                    chIndex = currentInfo.characterIndex+1;
2448                }
2449                currentInfo = newInfo;
2450            } else {
2451                //reach the next run
2452
if ( currentInfo.layout.hasCharacterIndex(chIndex) ){
2453                    chIndex++;
2454                    continue;
2455                }
2456
2457                lastCharacterInRunInfo = getCharacterInformation
2458                    (list,firstChar,k-1,aci);
2459
2460                //if the text run change compute the distance between the
2461
//first character of the run and the last
2462
length += distanceFirstLastCharacterInRun
2463                    (currentInfo,lastCharacterInRunInfo);
2464
2465                currentInfo = getCharacterInformation(list,firstChar,k,aci);
2466                chIndex = currentInfo.characterIndex+1;
2467                vector = currentInfo.layout.getGlyphVector();
2468                advs = currentInfo.layout.getGlyphAdvances();
2469                glyphTrack = new boolean[advs.length];
2470                lastCharacterInRunInfo = null;
2471            }
2472        }
2473
2474        if (currentInfo.layout.isOnATextPath() ){
2475            for (int gi = currentInfo.glyphIndexStart;
2476                 gi <= currentInfo.glyphIndexEnd; gi++) {
2477                if ((vector.isGlyphVisible(gi)) && !glyphTrack[gi])
2478                    length += advs[gi+1]-advs[gi];
2479                glyphTrack[gi] = true;
2480            }
2481        } else {
2482            if ( lastCharacterInRunInfo == null ){
2483                lastCharacterInRunInfo = getCharacterInformation
2484                    (list,firstChar,charnum+nchars-1,aci);
2485            }
2486            //add the length between the end position of the last character
2487
//and the first character in the run
2488
length += distanceFirstLastCharacterInRun
2489                (currentInfo,lastCharacterInRunInfo);
2490        }
2491
2492        return length;
2493    }
2494
2495    protected float distanceFirstLastCharacterInRun
2496        (CharacterInformation first, CharacterInformation last){
2497        
2498        float [] advs = first.layout.getGlyphAdvances();
2499        
2500        int firstStart = first.glyphIndexStart;
2501        int firstEnd = first.glyphIndexEnd;
2502        int lastStart = last.glyphIndexStart;
2503        int lastEnd = last.glyphIndexEnd;
2504
2505        int start = (firstStart<lastStart)?firstStart:lastStart;
2506        int end = (firstEnd<lastEnd)?lastEnd:firstEnd;
2507        return advs[end+1] - advs[start];
2508    }
2509
2510    protected float distanceBetweenRun
2511        (CharacterInformation last, CharacterInformation first){
2512        
2513        float distance;
2514        Point2D JavaDoc startPoint;
2515        Point2D JavaDoc endPoint;
2516        CharacterInformation info = new CharacterInformation();
2517
2518        //determine where the last run stops
2519

2520        info.layout = last.layout;
2521        info.glyphIndexEnd = last.layout.getGlyphCount()-1;
2522
2523        startPoint = getEndPoint(info);
2524
2525        //determine where the next run starts
2526
info.layout = first.layout;
2527        info.glyphIndexStart = 0;
2528
2529        endPoint = getStartPoint(info);
2530
2531        if( first.isVertical() ){
2532            distance = (float)(endPoint.getY() - startPoint.getY());
2533        }
2534        else{
2535            distance = (float)(endPoint.getX() - startPoint.getX());
2536        }
2537    
2538        return distance;
2539    }
2540                                                    
2541
2542    /**
2543     * Select an ensemble of characters for that element.
2544     *
2545     * TODO : report the selection to the selection
2546     * manager in JSVGComponent.
2547     */

2548    protected void selectSubString(Element element, int charnum, int nchars) {
2549        AttributedCharacterIterator JavaDoc aci;
2550        aci = ((TextNode)node).getAttributedCharacterIterator();
2551        TextNode textNode = (TextNode)node;
2552
2553        int firstChar = getElementStartIndex(aci,element);
2554
2555        if ( firstChar == -1 )
2556            return;
2557
2558        List JavaDoc list = getTextRuns(textNode);
2559
2560        int lastChar = getElementEndIndex(aci,element);
2561
2562        CharacterInformation firstInfo, lastInfo;
2563        firstInfo = getCharacterInformation(list, firstChar,charnum,aci);
2564        lastInfo = getCharacterInformation(list, firstChar,charnum+nchars-1,aci);
2565        
2566        Mark firstMark, lastMark;
2567        firstMark = textNode.getMarkerForChar(firstInfo.characterIndex,true);
2568
2569        if ( lastInfo != null && lastInfo.characterIndex <= lastChar ){
2570            lastMark = textNode.getMarkerForChar(lastInfo.characterIndex,false);
2571        }
2572        else{
2573            lastMark = textNode.getMarkerForChar(lastChar,false);
2574        }
2575
2576        ctx.getUserAgent().setTextSelection(firstMark,lastMark);
2577    }
2578
2579    protected int getCharNumAtPosition(Element e, float x, float y){
2580        
2581        TextNode textNode = (TextNode)node;
2582
2583        //check if there is an hit
2584
List JavaDoc list = getTextRuns(textNode);
2585
2586        //going backward in the list to catch the last character
2587
// displayed at that position
2588
TextHit hit = null;
2589
2590        for( int i = list.size()-1 ; i>= 0 && hit == null; i-- ){
2591
2592            hit = ((StrokingTextPainter.TextRun)list.get(i)).getLayout().hitTestChar(x,y);
2593        }
2594
2595        if ( hit == null ){
2596            return -1;
2597        }
2598
2599        AttributedCharacterIterator JavaDoc aci = ((TextNode)node).getAttributedCharacterIterator();
2600
2601        //found an hit, check if it belong to the element
2602
int first = getElementStartIndex( aci, e );
2603        int last = getElementEndIndex( aci, e );
2604
2605        int hitIndex = hit.getCharIndex();
2606
2607        if ( hitIndex >= first && hitIndex <= last ){
2608            
2609            return hitIndex - first;
2610        }
2611        else{
2612            return -1;
2613        }
2614    }
2615
2616    /**
2617     * Retrieve the list of layout for the
2618     * text node.
2619     */

2620    protected List JavaDoc getTextRuns(TextNode node){
2621        //System.out.println(node.getTextRuns());
2622
if ( node.getTextRuns() == null ){
2623            //TODO : need to work out a solution
2624
//to compute the text runs
2625
node.getPrimitiveBounds();
2626        }
2627        //System.out.println(node.getTextRuns());
2628
return node.getTextRuns();
2629    }
2630
2631    /**
2632     * Retrieve the information about a character
2633     * of en element. The element first character in
2634     * the ACI is 'firstChar' and the character
2635     * look for is the charnum th character in the
2636     * element
2637     *
2638     * @param list list of the layouts
2639     * @param startIndex index in the ACI of the first
2640     * character for the element
2641     * @param charnum index of the character (among the
2642     * characters of the element) looked for.
2643     *
2644     * @return information about the glyph representing the
2645     * character
2646     */

2647    protected CharacterInformation getCharacterInformation
2648        (List JavaDoc list,int startIndex, int charnum,
2649         AttributedCharacterIterator JavaDoc aci)
2650    {
2651        CharacterInformation info = new CharacterInformation();
2652        info.characterIndex = startIndex+charnum;
2653
2654        for (int i = 0; i < list.size(); i++) {
2655            StrokingTextPainter.TextRun run =
2656                (StrokingTextPainter.TextRun)list.get(i);
2657
2658            if ( run.getLayout().hasCharacterIndex(info.characterIndex) ){
2659                info.layout = run.getLayout();
2660
2661                aci.setIndex(info.characterIndex);
2662
2663                //check is it is a altGlyph
2664
if (aci.getAttribute(ALT_GLYPH_HANDLER) != null){
2665                    info.glyphIndexStart = 0;
2666                    info.glyphIndexEnd = info.layout.getGlyphCount()-1;
2667                } else {
2668                    info.glyphIndexStart = info.layout.getGlyphIndex
2669                        (info.characterIndex);
2670
2671                    //special case when the glyph does not have a unicode
2672
//associated to it, it will return -1
2673
if ( info.glyphIndexStart == -1 ){
2674                        info.glyphIndexStart = 0;
2675                        info.glyphIndexEnd = info.layout.getGlyphCount()-1;
2676                    }
2677                    else{
2678                        info.glyphIndexEnd = info.glyphIndexStart;
2679                    }
2680                }
2681                return info;
2682            }
2683        }
2684        return null;
2685    }
2686
2687    /**
2688     * Helper class to collect information about one Glyph
2689     * in the GlyphVector
2690     */

2691    protected static class CharacterInformation{
2692        ///layout associated to the Glyph
2693
TextSpanLayout layout;
2694        ///GlyphIndex in the vector
2695
int glyphIndexStart;
2696
2697        int glyphIndexEnd;
2698
2699        ///Character index in the ACI.
2700
int characterIndex;
2701
2702        /// Indicates is the glyph is vertical
2703
public boolean isVertical(){
2704            return layout.isVertical();
2705        }
2706        /// Retrieve the orientation angle for the Glyph
2707
public double getComputedOrientationAngle(){
2708            return layout.getComputedOrientationAngle(characterIndex);
2709        }
2710    }
2711
2712
2713    public Set JavaDoc getTextIntersectionSet(AffineTransform JavaDoc at,
2714                                       Rectangle2D JavaDoc rect) {
2715        TextNode tn = (TextNode)node;
2716        List JavaDoc list = tn.getTextRuns();
2717        Set JavaDoc elems = new HashSet JavaDoc();
2718        for (int i = 0 ; i < list.size(); i++) {
2719            StrokingTextPainter.TextRun run;
2720            run = (StrokingTextPainter.TextRun)list.get(i);
2721            TextSpanLayout layout = run.getLayout();
2722            AttributedCharacterIterator JavaDoc aci = run.getACI();
2723            aci.first();
2724            Element elem = (Element)aci.getAttribute(TEXT_COMPOUND_DELIMITER);
2725
2726            if (elem == null) continue;
2727            if (elems.contains(elem)) continue;
2728            if (!isTextSensitive(elem)) continue;
2729
2730            Rectangle2D JavaDoc glBounds = layout.getBounds2D();
2731            if (glBounds != null)
2732                glBounds = at.createTransformedShape(glBounds).getBounds2D();
2733
2734            if (!rect.intersects(glBounds))
2735                continue;
2736
2737            GVTGlyphVector gv = layout.getGlyphVector();
2738            for (int g = 0; g < gv.getNumGlyphs(); g++) {
2739                Shape JavaDoc gBounds = gv.getGlyphLogicalBounds(g);
2740                if (gBounds != null)
2741                    gBounds = at.createTransformedShape
2742                        (gBounds).getBounds2D();
2743
2744                if (gBounds.intersects(rect)) {
2745                    elems.add(elem);
2746                    break;
2747                }
2748            }
2749        }
2750        return elems;
2751    }
2752
2753    public Set JavaDoc getTextEnclosureSet(AffineTransform JavaDoc at,
2754                                    Rectangle2D JavaDoc rect) {
2755        TextNode tn = (TextNode)node;
2756
2757        Set JavaDoc elems = new HashSet JavaDoc();
2758        Set JavaDoc reject = new HashSet JavaDoc();
2759        List JavaDoc list = tn.getTextRuns();
2760        for (int i = 0 ; i < list.size(); i++) {
2761            StrokingTextPainter.TextRun run;
2762            run = (StrokingTextPainter.TextRun)list.get(i);
2763            TextSpanLayout layout = run.getLayout();
2764            AttributedCharacterIterator JavaDoc aci = run.getACI();
2765            aci.first();
2766            Element elem = (Element)aci.getAttribute(TEXT_COMPOUND_DELIMITER);
2767
2768            if (elem == null) continue;
2769            if (reject.contains(elem)) continue;
2770            if (!isTextSensitive(elem)) {
2771                reject.add(elem);
2772                continue;
2773            }
2774                        
2775            Rectangle2D JavaDoc glBounds = layout.getBounds2D();
2776            if (glBounds != null)
2777                glBounds = at.createTransformedShape
2778                    (glBounds).getBounds2D();
2779
2780            if (rect.contains(glBounds)) {
2781                elems.add(elem);
2782            } else {
2783                reject.add(elem);
2784                elems.remove(elem);
2785            }
2786        }
2787
2788        return elems;
2789    }
2790
2791    public static boolean getTextIntersection(BridgeContext ctx,
2792                                              Element elem,
2793                                              AffineTransform JavaDoc ati,
2794                                              Rectangle2D JavaDoc rect,
2795                                              boolean checkSensitivity) {
2796        SVGContext svgCtx = null;
2797        if (elem instanceof SVGOMElement)
2798            svgCtx = ((SVGOMElement)elem).getSVGContext();
2799        if (svgCtx == null) return false;
2800
2801        SVGTextElementBridge txtBridge = null;
2802        if (svgCtx instanceof SVGTextElementBridge)
2803            txtBridge = (SVGTextElementBridge)svgCtx;
2804        else if (svgCtx instanceof AbstractTextChildSVGContext) {
2805            AbstractTextChildSVGContext childCtx;
2806            childCtx = (AbstractTextChildSVGContext)svgCtx;
2807            txtBridge = childCtx.getTextBridge();
2808        }
2809        if (txtBridge == null) return false;
2810
2811        TextNode tn = (TextNode)txtBridge.node;
2812        Element txtElem = txtBridge.e;
2813
2814        AffineTransform JavaDoc at = tn.getGlobalTransform();
2815        at.preConcatenate(ati);
2816
2817        Rectangle2D JavaDoc tnRect;
2818        tnRect = tn.getBounds();
2819        tnRect = at.createTransformedShape(tnRect).getBounds2D();
2820        if (!rect.intersects(tnRect)) return false;
2821
2822        List JavaDoc list = tn.getTextRuns();
2823        for (int i = 0 ; i < list.size(); i++) {
2824            StrokingTextPainter.TextRun run;
2825            run = (StrokingTextPainter.TextRun)list.get(i);
2826            TextSpanLayout layout = run.getLayout();
2827            AttributedCharacterIterator JavaDoc aci = run.getACI();
2828            aci.first();
2829            Element runElem = (Element)aci.getAttribute
2830                (TEXT_COMPOUND_DELIMITER);
2831            if (runElem == null) continue;
2832
2833            // Only consider runElem if it is sensitive.
2834
if (checkSensitivity && !isTextSensitive(runElem)) continue;
2835
2836            Element p = runElem;
2837            while ((p != null) && (p != txtElem) && (p != elem)) {
2838                p = (Element)p.getParentNode();
2839            }
2840            if (p != elem) continue;
2841
2842            // runElem is a child of elem so check it out.
2843
Rectangle2D JavaDoc glBounds = layout.getBounds2D();
2844            if (glBounds == null) continue;
2845            glBounds = at.createTransformedShape(glBounds).getBounds2D();
2846            if (!rect.intersects(glBounds)) continue;
2847            
2848            GVTGlyphVector gv = layout.getGlyphVector();
2849            for (int g = 0; g < gv.getNumGlyphs(); g++) {
2850                Shape JavaDoc gBounds = gv.getGlyphLogicalBounds(g);
2851                if (gBounds != null)
2852                    gBounds = at.createTransformedShape
2853                        (gBounds).getBounds2D();
2854
2855                if (gBounds.intersects(rect))
2856                    return true;
2857            }
2858        }
2859        return false;
2860    }
2861
2862    public static Rectangle2D JavaDoc getTextBounds(BridgeContext ctx, Element elem,
2863                                            boolean checkSensitivity) {
2864        SVGContext svgCtx = null;
2865        if (elem instanceof SVGOMElement)
2866            svgCtx = ((SVGOMElement)elem).getSVGContext();
2867        if (svgCtx == null) return null;
2868
2869        SVGTextElementBridge txtBridge = null;
2870        if (svgCtx instanceof SVGTextElementBridge)
2871            txtBridge = (SVGTextElementBridge)svgCtx;
2872        else if (svgCtx instanceof AbstractTextChildSVGContext) {
2873            AbstractTextChildSVGContext childCtx;
2874            childCtx = (AbstractTextChildSVGContext)svgCtx;
2875            txtBridge = childCtx.getTextBridge();
2876        }
2877        if (txtBridge == null) return null;
2878
2879        TextNode tn = (TextNode)txtBridge.node;
2880        Element txtElem = txtBridge.e;
2881
2882        Rectangle2D JavaDoc ret = null;
2883        List JavaDoc list = tn.getTextRuns();
2884        if (list == null) return null;
2885        for (int i = 0 ; i < list.size(); i++) {
2886            StrokingTextPainter.TextRun run;
2887            run = (StrokingTextPainter.TextRun)list.get(i);
2888            TextSpanLayout layout = run.getLayout();
2889            AttributedCharacterIterator JavaDoc aci = run.getACI();
2890            aci.first();
2891            Element runElem = (Element)aci.getAttribute
2892                (TEXT_COMPOUND_DELIMITER);
2893            if (runElem == null) continue;
2894
2895            // Only consider runElem if it is sensitive.
2896
if (checkSensitivity && !isTextSensitive(runElem)) continue;
2897
2898            Element p = runElem;
2899            while ((p != null) && (p != txtElem) && (p != elem)) {
2900                p = (Element)p.getParentNode();
2901            }
2902            if (p != elem) continue;
2903
2904            // runElem is a child of elem so include it's bounds.
2905
Rectangle2D JavaDoc glBounds = layout.getBounds2D();
2906            if (glBounds != null) {
2907                if (ret == null) ret = (Rectangle2D JavaDoc)glBounds.clone();
2908                else ret.add(glBounds);
2909            }
2910        }
2911        return ret;
2912    }
2913         
2914
2915    public static boolean isTextSensitive(Element e) {
2916        int ptrEvts = CSSUtilities.convertPointerEvents(e);
2917        switch (ptrEvts) {
2918        case GraphicsNode.VISIBLE_PAINTED:
2919        case GraphicsNode.VISIBLE_FILL:
2920        case GraphicsNode.VISIBLE_STROKE:
2921        case GraphicsNode.VISIBLE:
2922            return CSSUtilities.convertVisibility(e);
2923        case GraphicsNode.PAINTED:
2924        case GraphicsNode.FILL:
2925        case GraphicsNode.STROKE:
2926        case GraphicsNode.ALL:
2927            return true;
2928        case GraphicsNode.NONE:
2929        default:
2930            return false;
2931        }
2932    }
2933}
2934
Popular Tags