You are viewing a plain text version of this content. The canonical link for it is here.
Posted to batik-commits@xmlgraphics.apache.org by ca...@apache.org on 2006/06/23 15:32:46 UTC

svn commit: r416716 [2/8] - in /xmlgraphics/batik/branches/anim: ./ contrib/scroll/ lib/ resources/META-INF/services/ resources/org/apache/batik/apps/svgbrowser/resources/ resources/org/apache/batik/apps/svgpp/resources/ resources/org/apache/batik/svgg...

Modified: xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/SVGTextElementBridge.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/SVGTextElementBridge.java?rev=416716&r1=416715&r2=416716&view=diff
==============================================================================
--- xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/SVGTextElementBridge.java (original)
+++ xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/SVGTextElementBridge.java Fri Jun 23 06:32:40 2006
@@ -1,6 +1,6 @@
 /*
 
-   Copyright 2001-2005  The Apache Software Foundation 
+   Copyright 2001-2005  The Apache Software Foundation
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
 import java.awt.geom.GeneralPath;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
+import java.lang.ref.SoftReference;
 import java.text.AttributedCharacterIterator;
 import java.text.AttributedString;
 import java.text.AttributedCharacterIterator.Attribute;
@@ -54,9 +55,12 @@
 import org.apache.batik.dom.util.XMLSupport;
 import org.apache.batik.gvt.GraphicsNode;
 import org.apache.batik.gvt.TextNode;
+import org.apache.batik.gvt.font.FontFamilyResolver;
+import org.apache.batik.gvt.font.GVTFont;
 import org.apache.batik.gvt.font.GVTFontFamily;
 import org.apache.batik.gvt.font.GVTGlyphMetrics;
 import org.apache.batik.gvt.font.GVTGlyphVector;
+import org.apache.batik.gvt.font.UnresolvedFontFamily;
 import org.apache.batik.gvt.renderer.StrokingTextPainter;
 import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
 import org.apache.batik.gvt.text.Mark;
@@ -82,22 +86,45 @@
  * @author <a href="mailto:bill.haneman@ireland.sun.com">Bill Haneman</a>
  * @version $Id$
  */
-public class SVGTextElementBridge extends AbstractGraphicsNodeBridge 
+public class SVGTextElementBridge extends AbstractGraphicsNodeBridge
     implements SVGTextContent {
 
     protected final static Integer ZERO = new Integer(0);
 
-    public static final 
+    public static final
         AttributedCharacterIterator.Attribute TEXT_COMPOUND_DELIMITER =
         GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER;
 
+    public static final
+        AttributedCharacterIterator.Attribute TEXT_COMPOUND_ID =
+        GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_ID;
+
     public static final AttributedCharacterIterator.Attribute PAINT_INFO =
          GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO;
 
-    public static final 
+    public static final
         AttributedCharacterIterator.Attribute ALT_GLYPH_HANDLER =
         GVTAttributedCharacterIterator.TextAttribute.ALT_GLYPH_HANDLER;
-        
+
+    public static final
+        AttributedCharacterIterator.Attribute TEXTPATH
+        = GVTAttributedCharacterIterator.TextAttribute.TEXTPATH;
+
+    public static final
+        AttributedCharacterIterator.Attribute ANCHOR_TYPE
+        = GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE;
+
+    public static final
+        AttributedCharacterIterator.Attribute GVT_FONT_FAMILIES
+        = GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES;
+
+    public static final
+        AttributedCharacterIterator.Attribute GVT_FONTS
+        = GVTAttributedCharacterIterator.TextAttribute.GVT_FONTS;
+
+    public static final
+        AttributedCharacterIterator.Attribute BASELINE_SHIFT
+        = GVTAttributedCharacterIterator.TextAttribute.BASELINE_SHIFT;
 
     protected AttributedString laidoutText;
 
@@ -263,6 +290,9 @@
         node.setPointerEventType(CSSUtilities.convertPointerEvents(e));
 
         initializeDynamicSupport(ctx, e, node);
+        if (!ctx.isDynamic()) {
+            elemTPI.clear();
+        }
     }
 
     /**
@@ -314,7 +344,7 @@
          * Handles 'DOMNodeRemoved' event type.
          */
         public void handleEvent(Event evt) {
-            handleDOMChildNodeRemovedEvent((MutationEvent)evt);            
+            handleDOMChildNodeRemovedEvent((MutationEvent)evt);
         }
     }
 
@@ -340,7 +370,7 @@
 
     /**
      * This method ensures that any modification to a text
-     * element and its children is going to be reflected 
+     * element and its children is going to be reflected
      * into the GVT tree.
      */
     protected void initializeDynamicSupport(BridgeContext ctx,
@@ -367,7 +397,7 @@
                 new DOMSubtreeModifiedEventListener();
         }
 
-        //to be notified when a child is removed from the 
+        //to be notified when a child is removed from the
         //<text> element.
         e.addEventListenerNS
             (XMLConstants.XML_EVENTS_NAMESPACE_URI, "DOMNodeRemoved",
@@ -375,7 +405,7 @@
         ctx.storeEventListenerNS
             (e, XMLConstants.XML_EVENTS_NAMESPACE_URI, "DOMNodeRemoved",
              childNodeRemovedEventListener, true);
-        
+
         //to be notified when the modification of the subtree
         //of the <text> element is done
         e.addEventListenerNS
@@ -429,7 +459,7 @@
         while (child != null) {
             if (child.getNodeType() == Node.ELEMENT_NODE) {
                 addContextToChild(ctx, (Element)child);
-            }        
+            }
             child = getNextSibling(child);
         }
     }
@@ -439,12 +469,12 @@
      */
     public void handleDOMNodeInsertedEvent(MutationEvent evt) {
         Node childNode = (Node)evt.getTarget();
-        
+
         //check the type of the node inserted before discard the layout
         //in the case of <title> or <desc> or <metadata>, the layout
         //is unchanged
         switch( childNode.getNodeType() ){
-        case Node.TEXT_NODE:
+        case Node.TEXT_NODE:        // fall-through is intended
         case Node.CDATA_SECTION_NODE:
             laidoutText = null;
             break;
@@ -481,14 +511,14 @@
         //in the case of <title> or <desc> or <metadata>, the layout
         //is unchanged
         switch (childNode.getNodeType()) {
-            case Node.TEXT_NODE:
+            case Node.TEXT_NODE:           // fall-through is intended
             case Node.CDATA_SECTION_NODE:
                 //the parent has to be a displayed node
                 if (isParentDisplayed( childNode)) {
                     laidoutText = null;
                 }
                 break;
-            case Node.ELEMENT_NODE: 
+            case Node.ELEMENT_NODE:
                 if (isTextChild((Element)childNode)) {
                     laidoutText = null;
                 }
@@ -511,7 +541,7 @@
     }
 
     /**
-     * Invoked when an MutationEvent of type 'DOMCharacterDataModified' 
+     * Invoked when an MutationEvent of type 'DOMCharacterDataModified'
      * is fired.
      */
     public void handleDOMCharacterDataModified(MutationEvent evt){
@@ -528,7 +558,7 @@
      * &lt;title&gt;, &lt;desc&gt; and &lt;metadata&gt;
      * are non displayable elements.
      *
-     * @return true if the parent of the node is &lt;text&gt;, 
+     * @return true if the parent of the node is &lt;text&gt;,
      *   &lt;tspan&gt;, &lt;tref&gt;, &lt;textPath&gt;, &lt;a&gt;,
      *   &lt;altGlyph&gt;
      */
@@ -545,10 +575,12 @@
      * update <code>laidoutText</code> with the new
      * value.
      */
-    protected void computeLaidoutText(BridgeContext ctx, 
+    protected void computeLaidoutText(BridgeContext ctx,
                                       Element e,
                                       GraphicsNode node) {
         TextNode tn = (TextNode)node;
+        elemTPI.clear();
+
         AttributedString as = buildAttributedString(ctx, e);
         if (as == null) {
             tn.setAttributedCharacterIterator(null);
@@ -559,10 +591,6 @@
         if (ctx.isDynamic()) {
             laidoutText = new AttributedString(as.getIterator());
         }
-        elemTPI.clear();
-        // Add null TPI objects to the text (after we set it on the
-        // Text we will swap in the correct values.
-        addNullPaintAttributes(as, e, ctx);
 
         // Install the ACI in the text node.
         tn.setAttributedCharacterIterator(as.getIterator());
@@ -588,56 +616,6 @@
     }
 
     /**
-     * This creates 'dummy' TPI objects for each element and records
-     * them in the elemTPI map so we can later update them with 
-     * the correct paint attributes.
-     */
-    protected void addNullPaintAttributes(AttributedString as, 
-                                          Element element,
-                                          BridgeContext ctx) {
-        // 'requiredFeatures', 'requiredExtensions' and 'systemLanguage'
-        if ((!SVGUtilities.matchUserAgent(element, ctx.getUserAgent())) ||
-            (!CSSUtilities.convertDisplay(element))) {
-            return;
-        }
-
-        AttributedCharacterIterator aci = as.getIterator();
-
-        // calculate which chars in the string belong to this element
-        int firstChar = getElementStartIndex(aci, element);
-        if (firstChar == -1)
-            return; // Element not part of aci (no chars in elem usually)
-
-        int lastChar = getElementEndIndex(aci, element);
-        TextPaintInfo pi = new TextPaintInfo();
-        // Set some basic props so we can get bounds info for complex paints.
-        pi.visible   = true;        
-        pi.fillPaint = Color.black;
-
-        as.addAttribute(PAINT_INFO, pi, firstChar, lastChar+1);
-        elemTPI.put(element, pi);
-
-        addChildNullPaintAttributes(as, element, ctx);
-    }
-    
-    protected void addChildNullPaintAttributes(AttributedString as,
-                                               Element element,
-                                               BridgeContext ctx) {
-        // Add Paint attributres for children of text element
-        for (Node child = getFirstChild(element);
-             child != null;
-             child = getNextSibling(child)) {
-            if (child.getNodeType() != Node.ELEMENT_NODE) 
-                continue;
-
-            Element childElement = (Element)child;
-            if (isTextChild(childElement)) {
-                addNullPaintAttributes(as, childElement, ctx);
-            }
-        }
-    }
-
-    /**
      * This flag bit indicates if a new ACI has been created in
      * response to a CSSEngineEvent.
      * Avoid creating one ShapePainter per CSS property change
@@ -682,7 +660,7 @@
         int [] properties = evt.getProperties();
         // first try to find CSS properties that change the layout
         for (int i=0; i < properties.length; ++i) {
-            switch(properties[i]) {
+            switch(properties[i]) {         // fall-through is intended
             case SVGCSSEngine.BASELINE_SHIFT_INDEX:
             case SVGCSSEngine.DIRECTION_INDEX:
             case SVGCSSEngine.DISPLAY_INDEX:
@@ -693,7 +671,7 @@
             case SVGCSSEngine.FONT_WEIGHT_INDEX:
             case SVGCSSEngine.GLYPH_ORIENTATION_HORIZONTAL_INDEX:
             case SVGCSSEngine.GLYPH_ORIENTATION_VERTICAL_INDEX:
-            case SVGCSSEngine.KERNING_INDEX: 
+            case SVGCSSEngine.KERNING_INDEX:
             case SVGCSSEngine.LETTER_SPACING_INDEX:
             case SVGCSSEngine.TEXT_ANCHOR_INDEX:
             case SVGCSSEngine.UNICODE_BIDI_INDEX:
@@ -720,7 +698,7 @@
      * Invoked for each CSS property that has changed.
      */
     protected void handleCSSPropertyChanged(int property) {
-        switch(property) {
+        switch(property) {                  // fall-through is intended
         case SVGCSSEngine.FILL_INDEX:
         case SVGCSSEngine.FILL_OPACITY_INDEX:
         case SVGCSSEngine.STROKE_INDEX:
@@ -734,8 +712,8 @@
         case SVGCSSEngine.TEXT_DECORATION_INDEX:
             rebuildACI();
             break;
- 
-        case SVGCSSEngine.VISIBILITY_INDEX: 
+
+        case SVGCSSEngine.VISIBILITY_INDEX:
             rebuildACI();
             super.handleCSSPropertyChanged(property);
             break;
@@ -754,7 +732,7 @@
                 node.setRenderingHints(hints);
             }
             break;
-        } 
+        }
         default:
             super.handleCSSPropertyChanged(property);
         }
@@ -780,50 +758,29 @@
             //and only update the section of the AtrtibutedString of
             //the child
             TextPaintInfo parentPI;
-            parentPI = getParentTextPaintInfo
-                (tn.getAttributedCharacterIterator(), cssProceedElement);
+            parentPI = getParentTextPaintInfo(cssProceedElement);
             pi = getTextPaintInfo(cssProceedElement, tn, parentPI, ctx);
             oldPI = (TextPaintInfo)elemTPI.get(cssProceedElement);
         }
         if (oldPI == null) return;
 
         tn.swapTextPaintInfo(pi, oldPI);
-        if (usingComplexSVGFont) 
+        if (usingComplexSVGFont)
             // Force Complex SVG fonts to be recreated
             tn.setAttributedCharacterIterator
                 (tn.getAttributedCharacterIterator());
     }
 
-    int getElementStartIndex
-        (AttributedCharacterIterator aci, Element element) {
-        if (aci == null) return -1;
-
-        // calculate which chars in the string belong to this element
-        for (int i = 0; i < aci.getEndIndex();) {
-            aci.setIndex(i);
-            Element delimeter;
-            delimeter = (Element)aci.getAttribute(TEXT_COMPOUND_DELIMITER);
-            if (delimeter == element || nodeAncestorOf(element, delimeter))
-                return i;
-            i = aci.getRunLimit(TEXT_COMPOUND_DELIMITER);
-        }
-        return -1;  
+    int getElementStartIndex(Element element) {
+        TextPaintInfo tpi = (TextPaintInfo)elemTPI.get(element);
+        if (tpi == null) return -1;
+        return tpi.startChar;
     }
 
-    int getElementEndIndex
-        (AttributedCharacterIterator aci, Element element) {
-        if (aci == null) return -1;
-
-        // calculate which chars in the string belong to this element
-        for (int i = aci.getEndIndex()-1; i >= 0;) {
-            aci.setIndex(i);
-            Element delimeter;
-            delimeter = (Element)aci.getAttribute(TEXT_COMPOUND_DELIMITER);
-            if (delimeter == element || nodeAncestorOf(element, delimeter))
-                return i;
-            i = aci.getRunStart(TEXT_COMPOUND_DELIMITER)-1;
-        }
-        return -1;  
+    int getElementEndIndex(Element element) {
+        TextPaintInfo tpi = (TextPaintInfo)elemTPI.get(element);
+        if (tpi == null) return -1;
+        return tpi.endChar;
     }
 
     // -----------------------------------------------------------------------
@@ -840,7 +797,7 @@
      */
     protected AttributedString buildAttributedString(BridgeContext ctx,
                                                      Element element) {
-        
+
         AttributedStringBuffer asb = new AttributedStringBuffer();
         fillAttributedStringBuffer(ctx, element, true, null, null, asb);
         return asb.toAttributedString();
@@ -870,17 +827,18 @@
             (!CSSUtilities.convertDisplay(element))) {
             return;
         }
-        
+
         String s = XMLSupport.getXMLSpace(element);
         boolean preserve = s.equals(SVG_PRESERVE_VALUE);
         boolean prevEndsWithSpace;
         Element nodeElement = element;
+        int elementStartChar = asb.length();
 
         if (top)
             endLimit = 0;
         if (preserve)
             endLimit = asb.length();
-        
+
         Map map = getAttributeMap(ctx, element, textPath, bidiLevel);
         Object o = map.get(TextAttribute.BIDI_EMBEDDING);
         Integer subBidiLevel = bidiLevel;
@@ -894,7 +852,7 @@
             if (preserve) {
                 prevEndsWithSpace = false;
             } else {
-                if (asb.length() == 0) 
+                if (asb.length() == 0)
                     prevEndsWithSpace = true;
                 else
                     prevEndsWithSpace = (asb.getLastChar() == ' ');
@@ -904,11 +862,11 @@
             case Node.ELEMENT_NODE:
                 if (!SVG_NAMESPACE_URI.equals(n.getNamespaceURI()))
                     break;
-                
+
                 nodeElement = (Element)n;
 
                 String ln = n.getLocalName();
-                
+
                 if (ln.equals(SVG_TSPAN_TAG) ||
                     ln.equals(SVG_ALT_GLYPH_TAG)) {
                     fillAttributedStringBuffer(ctx,
@@ -936,9 +894,15 @@
                     s = TextUtilities.getElementContent(ref);
                     s = normalizeString(s, preserve, prevEndsWithSpace);
                     if (s != null) {
-                        Map m = getAttributeMap(ctx, nodeElement, 
+                        int trefStart = asb.length();
+                        Map m = getAttributeMap(ctx, nodeElement,
                                                 textPath, bidiLevel);
                         asb.append(s, m);
+                        int trefEnd = asb.length()-1;
+                        TextPaintInfo tpi;
+                        tpi = (TextPaintInfo)elemTPI.get(nodeElement);
+                        tpi.startChar = trefStart;
+                        tpi.endChar   = trefEnd;
                     }
                 } else if (ln.equals(SVG_A_TAG)) {
                     NodeEventTarget target = (NodeEventTarget)nodeElement;
@@ -950,7 +914,7 @@
                     ctx.storeEventListenerNS
                         (target, XMLConstants.XML_EVENTS_NAMESPACE_URI,
                          SVG_EVENT_CLICK, l, false);
-                    
+
                     fillAttributedStringBuffer(ctx,
                                                nodeElement,
                                                false,
@@ -959,7 +923,7 @@
                                                asb);
                 }
                 break;
-            case Node.TEXT_NODE:
+            case Node.TEXT_NODE:                     // fall-through is intended
             case Node.CDATA_SECTION_NODE:
                 s = n.getNodeValue();
                 s = normalizeString(s, preserve, prevEndsWithSpace);
@@ -970,9 +934,27 @@
         }
 
         if (top) {
-            while ((endLimit < asb.length()) && (asb.getLastChar() == ' '))
+            boolean strippedSome = false;
+            while ((endLimit < asb.length()) && (asb.getLastChar() == ' ')) {
                 asb.stripLast();
+                strippedSome = true;
+            }
+            if (strippedSome) {
+                Iterator iter = elemTPI.values().iterator();
+                while (iter.hasNext()) {
+                    TextPaintInfo tpi = (TextPaintInfo)iter.next();
+                    if (tpi.endChar >= asb.length()) {
+                        tpi.endChar = asb.length()-1;
+                        if (tpi.startChar > tpi.endChar)
+                            tpi.startChar = tpi.endChar;
+                    }
+                }
+            }
         }
+        int elementEndChar = asb.length()-1;
+        TextPaintInfo tpi = (TextPaintInfo)elemTPI.get(element);
+        tpi.startChar = elementStartChar;
+        tpi.endChar   = elementEndChar;
     }
 
     /**
@@ -985,7 +967,7 @@
         if (preserve) {
             for (int i = 0; i < s.length(); i++) {
                 char c = s.charAt(i);
-                switch (c) {
+                switch (c) {                // fall-through is intended
                 case 10:
                 case 13:
                 case '\t':
@@ -1004,7 +986,7 @@
                 switch (s.charAt(idx)) {
                 default:
                     break loop;
-                case 10:
+                case 10:                   // fall-through is intended
                 case 13:
                 case ' ':
                 case '\t':
@@ -1017,10 +999,10 @@
         for (int i = idx; i < s.length(); i++) {
             char c = s.charAt(i);
             switch (c) {
-            case 10:
+            case 10:                      // fall-through is intended
             case 13:
                 break;
-            case ' ':
+            case ' ':                     // fall-through is intended
             case '\t':
                 if (!space) {
                     sb.append(' ');
@@ -1050,7 +1032,7 @@
          * The attributes.
          */
         protected List attributes;
-        
+
         /**
          * The number of items.
          */
@@ -1060,7 +1042,7 @@
          * The length of the attributed string.
          */
         protected int length;
-        
+
         /**
          * Creates a new empty AttributedStringBuffer.
          */
@@ -1077,7 +1059,7 @@
         public boolean isEmpty() {
             return count == 0;
         }
-        
+
         /**
          * Returns the length in chars of the current Attributed String
          */
@@ -1112,7 +1094,7 @@
          */
         public void stripFirst() {
             String s = (String)strings.get(0);
-            if (s.charAt(s.length() - 1) != ' ') 
+            if (s.charAt(s.length() - 1) != ' ')
                 return;
 
             length--;
@@ -1132,7 +1114,7 @@
          */
         public void stripLast() {
             String s = (String)strings.get(count - 1);
-            if (s.charAt(s.length() - 1) != ' ') 
+            if (s.charAt(s.length() - 1) != ' ')
                 return;
 
             length--;
@@ -1242,11 +1224,11 @@
         AttributedCharacterIterator aci = as.getIterator();
 
         // calculate which chars in the string belong to this element
-        int firstChar = getElementStartIndex(aci, element);
+        int firstChar = getElementStartIndex(element);
         // No match so no chars to annotate.
         if (firstChar == -1) return;
 
-        int lastChar = getElementEndIndex(aci, element);
+        int lastChar = getElementEndIndex(element);
 
         // get all of the glyph position attribute values
         String xAtt      = element.getAttributeNS(null, SVG_X_ATTRIBUTE);
@@ -1376,8 +1358,9 @@
             return;
         }
         Object o = elemTPI.get(element);
-        if (o == null) return;
-        node.swapTextPaintInfo(pi, (TextPaintInfo)o);
+        if (o != null) {
+            node.swapTextPaintInfo(pi, (TextPaintInfo)o);
+        }
         addChildPaintAttributes(as, element, node, pi, ctx);
     }
 
@@ -1402,26 +1385,87 @@
         }
     }
 
-    protected Map getFontProperties(BridgeContext ctx, Element element, 
-                                    Map map) {
-        if (map == null) map = new HashMap(4);
+    /**
+     * This method adds all the font related properties to <tt>result</tt>
+     * It also builds a List of the GVTFonts and returns it.
+     */
+    protected List getFontList(BridgeContext ctx,
+                               Element       element,
+                               Map           result) {
 
-        // Needed for SVG fonts.
-        map.put(TEXT_COMPOUND_DELIMITER, element);
+        // Unique value for text element - used for run identification.
+        result.put(TEXT_COMPOUND_ID, new SoftReference(element));
 
+        Float fsFloat = TextUtilities.convertFontSize(element);
+        float fontSize = fsFloat.floatValue();
         // Font size.
-        map.put(TextAttribute.SIZE, TextUtilities.convertFontSize(element));
+        result.put(TextAttribute.SIZE, fsFloat);
 
         // Font stretch
-        map.put(TextAttribute.WIDTH, TextUtilities.convertFontStretch(element));
+        result.put(TextAttribute.WIDTH,
+                TextUtilities.convertFontStretch(element));
+
+        // Font style
+        result.put(TextAttribute.POSTURE,
+                TextUtilities.convertFontStyle(element));
 
         // Font weight
-        map.put(TextAttribute.WEIGHT, TextUtilities.convertFontWeight(element));
+        result.put(TextAttribute.WEIGHT,
+                TextUtilities.convertFontWeight(element));
+
+        // Font weight
+        Value v = CSSUtilities.getComputedStyle
+            (element, SVGCSSEngine.FONT_WEIGHT_INDEX);
+        String fontWeightString = v.getCssText();
 
         // Font style
-        map.put(TextAttribute.POSTURE, TextUtilities.convertFontStyle(element));
+        String fontStyleString = CSSUtilities.getComputedStyle
+            (element, SVGCSSEngine.FONT_STYLE_INDEX).getStringValue();
 
-        return map;
+        // Needed for SVG fonts (also for dynamic documents).
+        result.put(TEXT_COMPOUND_DELIMITER, element);
+
+        //  make a list of GVTFont objects
+        Value val = CSSUtilities.getComputedStyle
+            (element, SVGCSSEngine.FONT_FAMILY_INDEX);
+        List fontFamilyList = new ArrayList();
+        List fontList = new ArrayList();
+        int len = val.getLength();
+        for (int i = 0; i < len; i++) {
+            Value it = val.item(i);
+            String fontFamilyName = it.getStringValue();
+            GVTFontFamily fontFamily;
+            fontFamily = SVGFontUtilities.getFontFamily
+                (element, ctx, fontFamilyName,
+                 fontWeightString, fontStyleString);
+            if (fontFamily == null) continue;
+            if (fontFamily instanceof UnresolvedFontFamily) {
+                fontFamily = FontFamilyResolver.resolve
+                    ((UnresolvedFontFamily)fontFamily);
+                if (fontFamily == null) continue;
+            }
+            fontFamilyList.add(fontFamily);
+            if (fontFamily instanceof SVGFontFamily) {
+                SVGFontFamily svgFF = (SVGFontFamily)fontFamily;
+                if (svgFF.isComplex()) {
+                    usingComplexSVGFont = true;
+                }
+            }
+            GVTFont ft = fontFamily.deriveFont(fontSize, result);
+            fontList.add(ft);
+        }
+
+        // Eventually this will need to go for SVG fonts it
+        // holds hard ref to DOM.
+        result.put(GVT_FONT_FAMILIES, fontFamilyList);
+
+        if (!ctx.isDynamic()) {
+            // Only leave this in the map for dynamic documents.
+            // Otherwise it will cause the whole DOM to stay when
+            // we don't really need it.
+            result.remove(TEXT_COMPOUND_DELIMITER);
+        }
+        return fontList;
     }
 
     /**
@@ -1436,37 +1480,39 @@
         Map result = new HashMap();
         String s;
         float f;
-        
+
         if (SVG_NAMESPACE_URI.equals(element.getNamespaceURI()) &&
             element.getLocalName().equals(SVG_ALT_GLYPH_TAG)) {
-            result.put(ALT_GLYPH_HANDLER, 
+            result.put(ALT_GLYPH_HANDLER,
                        new SVGAltGlyphHandler(ctx, element));
         }
 
+        // Add null TPI objects to the text (after we set it on the
+        // Text we will swap in the correct values.
+        TextPaintInfo pi = new TextPaintInfo();
+        // Set some basic props so we can get bounds info for complex paints.
+        pi.visible   = true;
+        pi.fillPaint = Color.black;
+        result.put(PAINT_INFO, pi);
+        elemTPI.put(element, pi);
+
         if (textPath != null) {
-            result.put(GVTAttributedCharacterIterator.TextAttribute.TEXTPATH,
-                       textPath);
+            result.put(TEXTPATH, textPath);
         }
 
         // Text-anchor
         TextNode.Anchor a = TextUtilities.convertTextAnchor(element);
-        result.put(GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE,
-                   a);
-
-        // get font size/width/weight/posture properties
-        getFontProperties(ctx, element, result);
+        result.put(ANCHOR_TYPE, a);
 
         // Font family
-        List fontFamilyList = getFontFamilyList(element, ctx);
-        result.put
-            (GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES,
-             fontFamilyList);
+        List fontList = getFontList(ctx, element, result);
+        result.put(GVT_FONTS, fontList);
+
 
         // Text baseline adjustment.
         Object bs = TextUtilities.convertBaselineShift(element);
         if (bs != null) {
-            result.put(GVTAttributedCharacterIterator.
-                       TextAttribute.BASELINE_SHIFT, bs);
+            result.put(BASELINE_SHIFT, bs);
         }
 
         // Unicode-bidi mode
@@ -1512,7 +1558,7 @@
 		else                    cbidi++; // was even now odd
                 break;
 	    }
-	    
+
 	    switch (s.charAt(0)) {
 	    case 'b': // bidi-override
 		cbidi = -cbidi; // For bidi-override we want a negative number.
@@ -1552,7 +1598,8 @@
 
         val = CSSUtilities.getComputedStyle
             (element, SVGCSSEngine.GLYPH_ORIENTATION_VERTICAL_INDEX);
-        switch (val.getPrimitiveType()) {
+        int primitiveType = val.getPrimitiveType();
+        switch ( primitiveType ) {
         case CSSPrimitiveValue.CSS_IDENT: // auto
             result.put(GVTAttributedCharacterIterator.
                        TextAttribute.VERTICAL_ORIENTATION,
@@ -1588,14 +1635,15 @@
             break;
         default:
             // Cannot happen
-            throw new InternalError();
+            throw new IllegalStateException("unexpected primitiveType (V):" + primitiveType );
         }
 
         // glyph-orientation-horizontal
 
         val = CSSUtilities.getComputedStyle
             (element, SVGCSSEngine.GLYPH_ORIENTATION_HORIZONTAL_INDEX);
-        switch (val.getPrimitiveType()) {
+        primitiveType = val.getPrimitiveType();
+        switch ( primitiveType ) {
         case CSSPrimitiveValue.CSS_DEG:
             result.put(GVTAttributedCharacterIterator.
                        TextAttribute.HORIZONTAL_ORIENTATION_ANGLE,
@@ -1613,7 +1661,7 @@
             break;
         default:
             // Cannot happen
-            throw new InternalError();
+            throw new IllegalStateException("unexpected primitiveType (H):" + primitiveType );
         }
 
         // text spacing properties...
@@ -1681,76 +1729,22 @@
     }
 
 
-    protected List getFontFamilyList(Element element, BridgeContext ctx) {
-        // Font weight
-        Value v = CSSUtilities.getComputedStyle
-            (element, SVGCSSEngine.FONT_WEIGHT_INDEX);
-        String fontWeightString = v.getCssText();
-
-        // Font style
-        String fontStyleString = CSSUtilities.getComputedStyle
-            (element, SVGCSSEngine.FONT_STYLE_INDEX).getStringValue();
-
-        Value val = CSSUtilities.getComputedStyle
-            (element, SVGCSSEngine.FONT_FAMILY_INDEX);
-        //  make a list of GVTFontFamily objects
-        List fontFamilyList = new ArrayList();
-        int len = val.getLength();
-        for (int i = 0; i < len; i++) {
-            Value it = val.item(i);
-            String fontFamilyName = it.getStringValue();
-            GVTFontFamily fontFamily
-                = SVGFontUtilities.getFontFamily(element, ctx, fontFamilyName,
-                                                 fontWeightString, 
-                                                 fontStyleString);
-            if (fontFamily instanceof SVGFontFamily) {
-                SVGFontFamily svgFF = (SVGFontFamily)fontFamily;
-                if (svgFF.isComplex()) {
-                    usingComplexSVGFont = true;
-                }
-            }
-            fontFamilyList.add(fontFamily);
-        }
-        return fontFamilyList;
-    }
-
     /**
      * Retrieve in the AttributeString the closest parent
      * of the node 'child' and extract the text decorations
      * of the parent.
      *
-     * @param aci an <code>AttributedCharacterIterator</code> value
      * @param child an <code>Element</code> value
      * @return a <code>TextDecoration</code> value
      */
-    protected TextPaintInfo getParentTextPaintInfo
-        (AttributedCharacterIterator aci, Element child) {
-        if (aci == null)
-            return new TextPaintInfo();
-
-        Element parent = null;
-        // calculate which chars in the string belong to the parent
-        int firstChar = -1;
-        for (int i = 0; i < aci.getEndIndex();) {
-            aci.setIndex(i);
-            Element delimeter;
-            delimeter = (Element)aci.getAttribute(TEXT_COMPOUND_DELIMITER);
-            if (nodeAncestorOf(delimeter,child) &&
-                ((parent == null) || nodeAncestorOf(parent, delimeter))) {
-                parent = delimeter;
-                firstChar = i;
-            }
-            if (delimeter == child || nodeAncestorOf(child, delimeter)) {
-                break;
-            }
-            i = aci.getRunLimit(TEXT_COMPOUND_DELIMITER);
+    protected TextPaintInfo getParentTextPaintInfo(Element child) {
+        Node parent = getParentNode(child);
+        while (parent != null) {
+            TextPaintInfo tpi = (TextPaintInfo)elemTPI.get(parent);
+            if (tpi != null) return tpi;
+            parent = getParentNode(parent);
         }
-
-        if ( parent == null)
-            return new TextPaintInfo(); //no parent
-
-        aci.setIndex(firstChar);
-        return (TextPaintInfo)aci.getAttribute(PAINT_INFO);
+        return null;
     }
 
     /**
@@ -1760,13 +1754,13 @@
      */
     protected TextPaintInfo getTextPaintInfo(Element element,
                                              GraphicsNode node,
-                                             TextPaintInfo parent,
+                                             TextPaintInfo parentTPI,
                                              BridgeContext ctx) {
         // Force the engine to update stuff..
         CSSUtilities.getComputedStyle
             (element, SVGCSSEngine.TEXT_DECORATION_INDEX);
 
-        TextPaintInfo pi = new TextPaintInfo(parent);
+        TextPaintInfo pi = new TextPaintInfo(parentTPI);
 
         // Was text-decoration explicity set on this element?
         StyleMap sm = ((CSSStylableElement)element).getComputedStyleMap(null);
@@ -1854,11 +1848,11 @@
             pi.underlinePaint = null;
             pi.underlineStrokePaint = null;
             pi.underlineStroke = null;
-            
+
             pi.overlinePaint = null;
             pi.overlineStrokePaint = null;
             pi.overlineStroke = null;
-            
+
             pi.strikethroughPaint = null;
             pi.strikethroughStrokePaint = null;
             pi.strikethroughStroke = null;
@@ -1867,10 +1861,10 @@
     }
 
     /**
-     * Implementation of <ode>SVGContext</code> for
+     * Implementation of <code>SVGContext</code> for
      * the children of &lt;text&gt;
      */
-    public abstract class AbstractTextChildSVGContext 
+    public abstract class AbstractTextChildSVGContext
         implements SVGContext {
 
         /** Bridge Context */
@@ -1896,7 +1890,7 @@
         }
 
         public SVGTextElementBridge getTextBridge() { return textBridge; }
-        
+
         /**
          * Returns the size of a px CSS unit in millimeters.
          */
@@ -1911,7 +1905,7 @@
          */
         public float getPixelToMM() {
             return getPixelUnitToMillimeter();
-            
+
         }
         /**
          * Returns the tight bounding box in current user space (i.e.,
@@ -1970,7 +1964,7 @@
         public float getViewportWidth() {
             return ctx.getBlockWidth(e);
         }
-        
+
         /**
          * Returns the height of the viewport which directly contains the
          * given element.
@@ -1978,7 +1972,7 @@
         public float getViewportHeight() {
             return ctx.getBlockHeight(e);
         }
-        
+
         /**
          * Returns the font-size on the associated element.
          */
@@ -2010,13 +2004,13 @@
      * <code>SVGTextElementBridge</code> which can determine
      * the impact of a change of one of its children for the others.
      */
-    protected abstract class AbstractTextChildBridgeUpdateHandler 
+    protected abstract class AbstractTextChildBridgeUpdateHandler
         extends AbstractTextChildSVGContext implements BridgeUpdateHandler {
 
         /**
          * Initialize the BridgeUpdateHandler implementation.
          */
-        public AbstractTextChildBridgeUpdateHandler
+        protected AbstractTextChildBridgeUpdateHandler
             (BridgeContext ctx,
              SVGTextElementBridge parent,
              Element e) {
@@ -2047,7 +2041,7 @@
         }
 
         /**
-         * Invoked when an MutationEvent of type 'DOMCharacterDataModified' 
+         * Invoked when an MutationEvent of type 'DOMCharacterDataModified'
          * is fired.
          */
         public void handleDOMCharacterDataModified(MutationEvent evt) {
@@ -2078,14 +2072,14 @@
         }
     }
 
-    protected class AbstractTextChildTextContent 
+    protected class AbstractTextChildTextContent
         extends AbstractTextChildBridgeUpdateHandler
         implements SVGTextContent {
 
         /**
          * Initialize the AbstractTextChildBridgeUpdateHandler implementation.
          */
-        public AbstractTextChildTextContent
+        protected AbstractTextChildTextContent
             (BridgeContext ctx,
              SVGTextElementBridge parent,
              Element e) {
@@ -2098,7 +2092,7 @@
         public int getNumberOfChars(){
             return textBridge.getNumberOfChars(e);
         }
-        
+
         public Rectangle2D getExtentOfChar(int charnum ){
             return textBridge.getExtentOfChar(e,charnum);
         }
@@ -2122,7 +2116,7 @@
         public float getComputedTextLength(){
             return textBridge.getComputedTextLength(e);
         }
-        
+
         public float getSubStringLength(int charnum, int nchars){
             return textBridge.getSubStringLength(e,charnum,nchars);
         }
@@ -2135,17 +2129,17 @@
     /**
      * BridgeUpdateHandle for &lt;tref&gt; element.
      */
-    protected class TRefBridge 
+    protected class TRefBridge
         extends AbstractTextChildTextContent {
 
-        public TRefBridge(BridgeContext ctx,
+        protected TRefBridge(BridgeContext ctx,
                           SVGTextElementBridge parent,
                           Element e) {
             super(ctx,parent,e);
         }
 
         /**
-         * Handle the dynamic update for the attributes of 
+         * Handle the dynamic update for the attributes of
          * &lt;tspan&gt; : 'x', 'y', 'dx', 'dy' and 'rotate'.
          */
         public void handleDOMAttrModifiedEvent(MutationEvent evt){
@@ -2156,19 +2150,19 @@
                 attrName.equals(SVG_DY_ATTRIBUTE) ||
                 attrName.equals(SVG_ROTATE_ATTRIBUTE)) {
                 //recompute the layout of the text node
-                textBridge.computeLaidoutText(ctx, textBridge.e, 
+                textBridge.computeLaidoutText(ctx, textBridge.e,
                                                textBridge.node);
             }
-        }        
+        }
     }
 
     /**
      * BridgeUpdateHandle for &lt;textPath&gt; element.
      */
-    protected class TextPathBridge 
+    protected class TextPathBridge
         extends AbstractTextChildTextContent{
 
-        public TextPathBridge(BridgeContext ctx,
+        protected TextPathBridge(BridgeContext ctx,
                               SVGTextElementBridge parent,
                               Element e){
             super(ctx,parent,e);
@@ -2178,17 +2172,17 @@
     /**
      * BridgeUpdateHandle for &lt;tspan&gt; element.
      */
-    protected class TspanBridge 
+    protected class TspanBridge
         extends AbstractTextChildTextContent {
 
-        public TspanBridge(BridgeContext ctx,
+        protected TspanBridge(BridgeContext ctx,
                            SVGTextElementBridge parent,
                            Element e){
             super(ctx,parent,e);
         }
-        
+
         /**
-         * Handle the dynamic update for the attributes of 
+         * Handle the dynamic update for the attributes of
          * &lt;tspan&gt; : 'x', 'y', 'dx', 'dy' and 'rotate'.
          */
         public void handleDOMAttrModifiedEvent(MutationEvent evt){
@@ -2199,10 +2193,10 @@
                 attrName.equals(SVG_DY_ATTRIBUTE) ||
                 attrName.equals(SVG_ROTATE_ATTRIBUTE)) {
                 //recompute the layout of the text node
-                textBridge.computeLaidoutText(ctx, textBridge.e, 
+                textBridge.computeLaidoutText(ctx, textBridge.e,
                                                textBridge.node);
             }
-        }        
+        }
     }
 
     //Implementation of TextContent
@@ -2221,7 +2215,7 @@
     public Point2D getEndPositionOfChar(int charnum){
         return getEndPositionOfChar(e,charnum);
     }
-    
+
     public void selectSubString(int charnum, int nchars){
         selectSubString(e,charnum,nchars);
     }
@@ -2255,12 +2249,12 @@
 
         //get the index in the aci for the first character
         //of the element
-        int firstChar = getElementStartIndex(aci,element);
+        int firstChar = getElementStartIndex(element);
 
         if (firstChar == -1)
             return 0; // Element not part of aci (no chars in elem usually)
 
-        int lastChar = getElementEndIndex(aci,element);
+        int lastChar = getElementEndIndex(element);
 
         return( lastChar - firstChar + 1 );
     }
@@ -2276,7 +2270,7 @@
         aci = ((TextNode)node).getAttributedCharacterIterator();
         if (aci == null) return null;
 
-        int firstChar = getElementStartIndex(aci,element);
+        int firstChar = getElementStartIndex(element);
 
         if ( firstChar == -1 )
             return null;
@@ -2287,7 +2281,7 @@
         //find the character 'charnum' in the text run
         CharacterInformation info;
         info = getCharacterInformation(list, firstChar,charnum, aci);
-            
+
         if ( info == null )
             return null;
 
@@ -2297,27 +2291,29 @@
 
         Shape b = null;
 
-        if ( info.glyphIndexStart == info.glyphIndexEnd ){
-            if (it.isGlyphVisible(info.glyphIndexStart))
-                b = it.getGlyphOutline(info.glyphIndexStart);
+        if (info.glyphIndexStart == info.glyphIndexEnd) {
+            if (it.isGlyphVisible(info.glyphIndexStart)) {
+                b = it.getGlyphCellBounds(info.glyphIndexStart);
+            }
         } else {
             GeneralPath path = null;
-            for( int k = info.glyphIndexStart ; 
-                 k <= info.glyphIndexEnd;
-                 k++){
+            for (int k = info.glyphIndexStart; k <= info.glyphIndexEnd; k++) {
                 if (it.isGlyphVisible(k)) {
-                    if (path == null) 
-                        path = new GeneralPath(it.getGlyphOutline(k));
-                    else
-                        path.append(it.getGlyphOutline(k),false);
+                    Rectangle2D gb = it.getGlyphCellBounds(k);
+                    if (path == null) {
+                        path = new GeneralPath(gb);
+                    } else {
+                        path.append(gb, false);
+                    }
                 }
             }
             b = path;
         }
 
-        if (b == null) 
+        if (b == null) {
             return null;
-                
+        }
+
         //return the bounding box of the outline
         return b.getBounds2D();
     }
@@ -2331,10 +2327,10 @@
 
         AttributedCharacterIterator aci;
         aci = ((TextNode)node).getAttributedCharacterIterator();
-        if (aci == null) 
+        if (aci == null)
             return null;
 
-        int firstChar = getElementStartIndex(aci,element);
+        int firstChar = getElementStartIndex(element);
         if ( firstChar == -1 )
             return null;
 
@@ -2347,8 +2343,8 @@
 
         if ( info == null )
             return null;
-        
-        return getStartPoint( info );            
+
+        return getStartPoint( info );
     }
 
     protected Point2D getStartPoint(CharacterInformation info){
@@ -2385,7 +2381,7 @@
         if (aci == null)
             return null;
 
-        int firstChar = getElementStartIndex(aci,element);
+        int firstChar = getElementStartIndex(element);
         if ( firstChar == -1 )
             return null;
 
@@ -2395,7 +2391,7 @@
         //find the glyph information for the character 'charnum'
         CharacterInformation info;
         info = getCharacterInformation(list, firstChar,charnum, aci);
-            
+
         if ( info == null )
             return null;
         return getEndPoint(info);
@@ -2406,18 +2402,18 @@
         GVTGlyphVector it = info.layout.getGlyphVector();
         if (!it.isGlyphVisible(info.glyphIndexEnd))
             return null;
-        
+
         Point2D b = it.getGlyphPosition(info.glyphIndexEnd);
-        
+
         AffineTransform glyphTransform;
         glyphTransform = it.getGlyphTransform(info.glyphIndexEnd);
-        
+
         GVTGlyphMetrics metrics = it.getGlyphMetrics(info.glyphIndexEnd);
-        
-        
+
+
         Point2D.Float result = new Point2D.Float
             (metrics.getHorizontalAdvance(), 0);
-            
+
         if ( glyphTransform != null )
             glyphTransform.transform(result,result);
 
@@ -2438,7 +2434,7 @@
             return 0;
 
         //first the first character for the element
-        int firstChar = getElementStartIndex(aci,element);
+        int firstChar = getElementStartIndex(element);
         if ( firstChar == -1 )
             return 0;
 
@@ -2448,7 +2444,7 @@
         //find the glyph information for the character 'charnum'
         CharacterInformation info;
         info = getCharacterInformation(list, firstChar,charnum, aci);
-            
+
         double angle = 0.0;
         int nbGlyphs = 0;
 
@@ -2461,7 +2457,7 @@
                 if (!it.isGlyphVisible(k)) continue;
 
                 nbGlyphs++;
-                
+
                 //the glyph transform contains only a scale and a rotate.
                 AffineTransform glyphTransform = it.getGlyphTransform(k);
                 if ( glyphTransform == null ) continue;
@@ -2469,7 +2465,7 @@
                 double glyphAngle = 0.0;
                 double cosTheta = glyphTransform.getScaleX();
                 double sinTheta = glyphTransform.getShearX();
-                
+
                 //extract the angle
                 if ( cosTheta == 0.0 ){
                     if ( sinTheta > 0 ) glyphAngle = Math.PI;
@@ -2480,10 +2476,10 @@
                         glyphAngle += Math.PI;
                 }
                 //get a degrees value for the angle
-                //SVG angle are clock wise java anticlockwise 
-                
+                //SVG angle are clock wise java anticlockwise
+
                 glyphAngle = (Math.toDegrees( - glyphAngle ) ) % 360.0;
-                
+
                 //remove the orientation from the value
                 angle += glyphAngle - info.getComputedOrientationAngle();
             }
@@ -2505,7 +2501,7 @@
      * org.w3c.dom.svg.SVGTextContentElement#getSubStringLength(int charnum,int nchars)}.
      */
     protected float getSubStringLength(Element element,
-                                       int charnum, 
+                                       int charnum,
                                        int nchars){
         if (nchars == 0) {
             return 0;
@@ -2519,7 +2515,7 @@
             return -1;
 
         TextNode textNode = (TextNode)node;
-        int firstChar = getElementStartIndex(aci,element);
+        int firstChar = getElementStartIndex(element);
 
         if ( firstChar == -1 )
             return -1;
@@ -2560,7 +2556,7 @@
                 lastCharacterInRunInfo = getCharacterInformation
                     (list,firstChar,k-1,aci);
 
-                //if the text run change compute the distance between the 
+                //if the text run change compute the distance between the
                 //first character of the run and the last
                 length += distanceFirstLastCharacterInRun
                     (currentInfo,lastCharacterInRunInfo);
@@ -2597,9 +2593,9 @@
 
     protected float distanceFirstLastCharacterInRun
         (CharacterInformation first, CharacterInformation last){
-        
+
         float [] advs = first.layout.getGlyphAdvances();
-        
+
         int firstStart = first.glyphIndexStart;
         int firstEnd   = first.glyphIndexEnd;
         int lastStart  = last.glyphIndexStart;
@@ -2612,7 +2608,7 @@
 
     protected float distanceBetweenRun
         (CharacterInformation last, CharacterInformation first){
-        
+
         float distance;
         Point2D startPoint;
         Point2D endPoint;
@@ -2637,10 +2633,10 @@
         else{
             distance = (float)(endPoint.getX() - startPoint.getX());
         }
-    
+
         return distance;
     }
-                                                    
+
 
     /**
      * Select an ensemble of characters for that element.
@@ -2655,19 +2651,19 @@
             return;
 
         TextNode textNode = (TextNode)node;
-        int firstChar = getElementStartIndex(aci,element);
+        int firstChar = getElementStartIndex(element);
 
         if ( firstChar == -1 )
             return;
 
         List list = getTextRuns(textNode);
 
-        int lastChar = getElementEndIndex(aci,element);
+        int lastChar = getElementEndIndex(element);
 
         CharacterInformation firstInfo, lastInfo;
         firstInfo = getCharacterInformation(list, firstChar,charnum,aci);
         lastInfo  = getCharacterInformation(list, firstChar,charnum+nchars-1,aci);
-        
+
         Mark firstMark, lastMark;
         firstMark = textNode.getMarkerForChar(firstInfo.characterIndex,true);
 
@@ -2682,7 +2678,7 @@
     }
 
     protected int getCharNumAtPosition(Element e, float x, float y){
-        
+
         TextNode textNode = (TextNode)node;
         AttributedCharacterIterator aci;
         aci = textNode.getAttributedCharacterIterator();
@@ -2707,8 +2703,8 @@
 
 
         //found an hit, check if it belong to the element
-        int first = getElementStartIndex( aci, e );
-        int last  = getElementEndIndex( aci, e );
+        int first = getElementStartIndex( e );
+        int last  = getElementEndIndex( e );
 
         int hitIndex = hit.getCharIndex();
 
@@ -2719,7 +2715,7 @@
     }
 
     /**
-     * Retrieve the list of layout for the 
+     * Retrieve the list of layout for the
      * text node.
      */
     protected List getTextRuns(TextNode node){
@@ -2737,20 +2733,20 @@
      * Retrieve the information about a character
      * of en element. The element first character in
      * the ACI is 'firstChar' and the character
-     * look for is the charnum th character in the 
+     * look for is the charnum th character in the
      * element
      *
      * @param list list of the layouts
      * @param startIndex index in the ACI of the first
      *   character for the element
-     * @param charnum index of the character (among the 
+     * @param charnum index of the character (among the
      *   characters of the element) looked for.
      *
      * @return information about the glyph representing the
      *  character
      */
     protected CharacterInformation getCharacterInformation
-        (List list,int startIndex, int charnum, 
+        (List list,int startIndex, int charnum,
          AttributedCharacterIterator aci)
     {
         CharacterInformation info = new CharacterInformation();
@@ -2764,9 +2760,9 @@
                 continue;
 
             info.layout = run.getLayout();
-            
+
             aci.setIndex(info.characterIndex);
-            
+
             //check is it is a altGlyph
             if (aci.getAttribute(ALT_GLYPH_HANDLER) != null){
                 info.glyphIndexStart = 0;
@@ -2774,7 +2770,7 @@
             } else {
                 info.glyphIndexStart = info.layout.getGlyphIndex
                     (info.characterIndex);
-                
+
                 //special case when the glyph does not have a unicode
                 //associated to it, it will return -1
                 if ( info.glyphIndexStart == -1 ){
@@ -2831,7 +2827,9 @@
             TextSpanLayout layout = run.getLayout();
             AttributedCharacterIterator aci = run.getACI();
             aci.first();
-            Element elem = (Element)aci.getAttribute(TEXT_COMPOUND_DELIMITER);
+            SoftReference sr;
+            sr =(SoftReference)aci.getAttribute(TEXT_COMPOUND_ID);
+            Element elem = (Element)sr.get();
 
             if (elem == null) continue;
             if (elems.contains(elem)) continue;
@@ -2876,7 +2874,9 @@
             TextSpanLayout layout = run.getLayout();
             AttributedCharacterIterator aci = run.getACI();
             aci.first();
-            Element elem = (Element)aci.getAttribute(TEXT_COMPOUND_DELIMITER);
+            SoftReference sr;
+            sr =(SoftReference)aci.getAttribute(TEXT_COMPOUND_ID);
+            Element elem = (Element)sr.get();
 
             if (elem == null) continue;
             if (reject.contains(elem)) continue;
@@ -2884,7 +2884,7 @@
                 reject.add(elem);
                 continue;
             }
-                        
+
             Rectangle2D glBounds = layout.getBounds2D();
             if (glBounds != null)
                 glBounds = at.createTransformedShape
@@ -2907,12 +2907,12 @@
                                               Rectangle2D rect,
                                               boolean checkSensitivity) {
         SVGContext svgCtx = null;
-        if (elem instanceof SVGOMElement) 
+        if (elem instanceof SVGOMElement)
             svgCtx  = ((SVGOMElement)elem).getSVGContext();
         if (svgCtx == null) return false;
 
         SVGTextElementBridge txtBridge = null;
-        if (svgCtx instanceof SVGTextElementBridge) 
+        if (svgCtx instanceof SVGTextElementBridge)
             txtBridge = (SVGTextElementBridge)svgCtx;
         else if (svgCtx instanceof AbstractTextChildSVGContext) {
             AbstractTextChildSVGContext childCtx;
@@ -2923,7 +2923,7 @@
 
         TextNode tn      = (TextNode)txtBridge.node;
         List list = tn.getTextRuns();
-        if (list == null) 
+        if (list == null)
             return false;
 
         Element  txtElem = txtBridge.e;
@@ -2942,8 +2942,9 @@
             TextSpanLayout layout = run.getLayout();
             AttributedCharacterIterator aci = run.getACI();
             aci.first();
-            Element runElem = (Element)aci.getAttribute
-                (TEXT_COMPOUND_DELIMITER);
+            SoftReference sr;
+            sr =(SoftReference)aci.getAttribute(TEXT_COMPOUND_ID);
+            Element runElem = (Element)sr.get();
             if (runElem == null) continue;
 
             // Only consider runElem if it is sensitive.
@@ -2960,7 +2961,7 @@
             if (glBounds == null) continue;
             glBounds = at.createTransformedShape(glBounds).getBounds2D();
             if (!rect.intersects(glBounds)) continue;
-            
+
             GVTGlyphVector gv = layout.getGlyphVector();
             for (int g = 0; g < gv.getNumGlyphs(); g++) {
                 Shape gBounds = gv.getGlyphLogicalBounds(g);
@@ -2978,12 +2979,12 @@
     public static Rectangle2D getTextBounds(BridgeContext ctx, Element elem,
                                             boolean checkSensitivity) {
         SVGContext svgCtx = null;
-        if (elem instanceof SVGOMElement) 
+        if (elem instanceof SVGOMElement)
             svgCtx  = ((SVGOMElement)elem).getSVGContext();
         if (svgCtx == null) return null;
 
         SVGTextElementBridge txtBridge = null;
-        if (svgCtx instanceof SVGTextElementBridge) 
+        if (svgCtx instanceof SVGTextElementBridge)
             txtBridge = (SVGTextElementBridge)svgCtx;
         else if (svgCtx instanceof AbstractTextChildSVGContext) {
             AbstractTextChildSVGContext childCtx;
@@ -2994,7 +2995,7 @@
 
         TextNode tn      = (TextNode)txtBridge.node;
         List list = tn.getTextRuns();
-        if (list == null) 
+        if (list == null)
             return null;
 
         Element  txtElem = txtBridge.e;
@@ -3006,8 +3007,9 @@
             TextSpanLayout layout = run.getLayout();
             AttributedCharacterIterator aci = run.getACI();
             aci.first();
-            Element runElem = (Element)aci.getAttribute
-                (TEXT_COMPOUND_DELIMITER);
+            SoftReference sr;
+            sr =(SoftReference)aci.getAttribute(TEXT_COMPOUND_ID);
+            Element runElem = (Element)sr.get();
             if (runElem == null) continue;
 
             // Only consider runElem if it is sensitive.
@@ -3028,18 +3030,18 @@
         }
         return ret;
     }
-         
+
 
     public static boolean isTextSensitive(Element e) {
         int     ptrEvts = CSSUtilities.convertPointerEvents(e);
         switch (ptrEvts) {
-        case GraphicsNode.VISIBLE_PAINTED:
+        case GraphicsNode.VISIBLE_PAINTED:   // fall-through is intended
         case GraphicsNode.VISIBLE_FILL:
         case GraphicsNode.VISIBLE_STROKE:
         case GraphicsNode.VISIBLE:
             return CSSUtilities.convertVisibility(e);
         case GraphicsNode.PAINTED:
-        case GraphicsNode.FILL:
+        case GraphicsNode.FILL:              // fall-through is intended
         case GraphicsNode.STROKE:
         case GraphicsNode.ALL:
             return true;

Modified: xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/UpdateManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/UpdateManager.java?rev=416716&r1=416715&r2=416716&view=diff
==============================================================================
--- xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/UpdateManager.java (original)
+++ xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/UpdateManager.java Fri Jun 23 06:32:40 2006
@@ -25,6 +25,8 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
 
 import org.apache.batik.bridge.svg12.DefaultXBLManager;
 import org.apache.batik.bridge.svg12.SVG12BridgeContext;
@@ -51,13 +53,13 @@
  */
 public class UpdateManager  {
 
-    static final long MIN_REPAINT_TIME;
+    static final int MIN_REPAINT_TIME;
     static {
-        long value = 20;
+        int value = 20;
         try {
             String s = System.getProperty
             ("org.apache.batik.min_repaint_time", "20");
-            value = Long.parseLong(s);
+            value = Integer.parseInt(s);
         } catch (SecurityException se) {
         } catch (NumberFormatException nfe){
         } finally {
@@ -137,6 +139,11 @@
     protected ScriptingEnvironment[] secondaryScriptingEnvironments;
 
     /**
+     * The current minRepaintTime
+     */
+    protected int minRepaintTime;
+
+    /**
      * Creates a new update manager.
      * @param ctx The bridge context.
      * @param gn GraphicsNode whose updates are to be tracked.
@@ -175,6 +182,15 @@
             ScriptingEnvironment se = initializeScriptingEnvironment(resCtx);
             secondaryScriptingEnvironments[i] = se;
         }
+        minRepaintTime = MIN_REPAINT_TIME;
+    }
+
+    public int getMinRepaintTime() {
+        return minRepaintTime;
+    }
+
+    public void setMinRepaintTime(int minRepaintTime) {
+        this.minRepaintTime = minRepaintTime;
     }
 
     /**
@@ -515,10 +531,23 @@
      * Repaints the dirty areas, if needed.
      */
     protected void repaint() {
-        if (!updateTracker.hasChanged()) 
+        if (!updateTracker.hasChanged()) { 
+            // No changes, nothing to repaint.
+            outOfDateTime = 0;
             return;
+        }
+
         long ctime = System.currentTimeMillis();
-        if (ctime-outOfDateTime < MIN_REPAINT_TIME) {
+        if (ctime < allResumeTime) {
+            createRepaintTimer();
+            return;
+        }
+        if (allResumeTime > 0) {
+            // All suspendRedraw requests have expired.
+            releaseAllRedrawSuspension();
+        }
+
+        if (ctime-outOfDateTime < minRepaintTime) {
             // We very recently did a repaint check if other 
             // repaint runnables are pending.
             synchronized (updateRunnableQueue.getIteratorLock()) {
@@ -541,6 +570,169 @@
         outOfDateTime = 0;
     }
 
+    /**
+     * Users of Batik should essentially never call 
+     * this directly from Java.  If the Canvas is not
+     * updating when you change the SVG Document it is almost
+     * certainly because you are not making your changes
+     * in the RunnableQueue (getUpdateRunnableQueue()).
+     * You will have problems if you are not making all
+     * changes to the document in the UpdateManager's
+     * RunnableQueue.
+     *
+     * This method exists to implement the 
+     * 'SVGSVGElement.forceRedraw()' method.
+     */
+    public void forceRepaint() {
+        if (!updateTracker.hasChanged()) { 
+            // No changes, nothing to repaint.
+            outOfDateTime = 0;
+            return;
+        }
+
+        List dirtyAreas = updateTracker.getDirtyAreas();
+        updateTracker.clear();
+        if (dirtyAreas != null) {
+            updateRendering(dirtyAreas, false);
+        }
+        outOfDateTime = 0;
+    }
+
+    protected class SuspensionInfo {
+        /** 
+         * The index of this redraw suspension
+         */
+        int index;
+        /**
+         * The system time in millisec that this suspension
+         * will expire and redraws can resume (at least for
+         * this suspension.
+         */
+        long resumeMilli;
+        public SuspensionInfo(int index, long resumeMilli) {
+            this.index = index;
+            this.resumeMilli = resumeMilli;
+        }
+        public int getIndex() { return index; }
+        public long getResumeMilli() { return resumeMilli; }
+    }
+
+    protected class RepaintTimerTask extends TimerTask {
+        UpdateManager um;
+        RepaintTimerTask(UpdateManager um) {
+            this.um = um;
+        }
+        public void run() {
+            RunnableQueue rq = um.getUpdateRunnableQueue();
+            if (rq == null) return;
+            rq.invokeLater(new Runnable() {
+                    public void run() { }
+                });
+        }
+    }
+
+    List suspensionList = new ArrayList();
+    int nextSuspensionIndex = 1;
+    long allResumeTime = -1;
+    Timer repaintTriggerTimer = null;
+    TimerTask repaintTimerTask = null;
+
+    void createRepaintTimer() {
+        if (repaintTimerTask != null) return;
+        if (allResumeTime < 0)        return;
+        if (repaintTriggerTimer == null)
+            repaintTriggerTimer = new Timer(true);
+
+        long delay = allResumeTime - System.currentTimeMillis();
+        if (delay < 0) delay = 0;
+        repaintTimerTask = new RepaintTimerTask(this);
+        repaintTriggerTimer.schedule(repaintTimerTask, delay);
+        // System.err.println("CTimer delay: " + delay);
+    }
+    /**
+     * Sets up a timer that will trigger a repaint
+     * when it fires.
+     * If create is true it will construct a timer even
+     * if one 
+     */
+    void resetRepaintTimer() {
+        if (repaintTimerTask == null) return;
+        if (allResumeTime < 0)        return;
+        if (repaintTriggerTimer == null)
+            repaintTriggerTimer = new Timer(true);
+        
+        long delay = allResumeTime - System.currentTimeMillis();
+        if (delay < 0) delay = 0;
+        repaintTimerTask = new RepaintTimerTask(this);
+        repaintTriggerTimer.schedule(repaintTimerTask, delay);
+        // System.err.println("Timer delay: " + delay);
+    }
+                
+    int addRedrawSuspension(int max_wait_milliseconds) {
+        long resumeTime = System.currentTimeMillis() + max_wait_milliseconds;
+        SuspensionInfo si = new SuspensionInfo(nextSuspensionIndex++,
+                                               resumeTime);
+        if (resumeTime > allResumeTime) {
+            allResumeTime = resumeTime;
+            // System.err.println("Added AllRes Time: " + allResumeTime);
+            resetRepaintTimer();
+        }
+        suspensionList.add(si);
+        return si.getIndex();
+    }
+
+    void releaseAllRedrawSuspension() {
+        suspensionList.clear();
+        allResumeTime = -1;
+        resetRepaintTimer();
+    }
+
+    boolean releaseRedrawSuspension(int index) {
+        if (index > nextSuspensionIndex) return false;
+        if (suspensionList.size() == 0) return true;
+
+        int lo = 0, hi=suspensionList.size()-1;
+        while (lo < hi) {
+            int mid = (lo+hi)>>1;
+            SuspensionInfo si = (SuspensionInfo)suspensionList.get(mid);
+            int idx = si.getIndex();
+            if      (idx == index) { lo = hi = mid; }
+            else if (idx <  index) { lo = mid+1; }
+            else                   { hi = mid-1; } 
+        }
+
+        SuspensionInfo si = (SuspensionInfo)suspensionList.get(lo);
+        int idx = si.getIndex();
+        if (idx != index) 
+            return true;  // currently not in list but was at some point...
+        
+        suspensionList.remove(lo);
+        if (suspensionList.size() == 0) {
+            // No more active suspensions
+            allResumeTime = -1;
+            resetRepaintTimer();
+        } else {
+            // Check if we need to find a new 'bounding' suspension.
+            long resumeTime = si.getResumeMilli();
+            if (resumeTime == allResumeTime) {
+                allResumeTime = findNewAllResumeTime();
+                // System.err.println("New AllRes Time: " + allResumeTime);
+                resetRepaintTimer();
+            }
+        }
+        return true;
+    }
+
+    long findNewAllResumeTime() {
+        long ret = -1;
+        Iterator i = suspensionList.iterator();
+        while (i.hasNext()) {
+            SuspensionInfo si = (SuspensionInfo)i.next();
+            long t = si.getResumeMilli();
+            if (t > ret) ret = t;
+        }
+        return ret;
+    }
 
     /**
      * Adds a UpdateManagerListener to this UpdateManager.

Modified: xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/svg12/SVG12BridgeContext.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/svg12/SVG12BridgeContext.java?rev=416716&r1=416715&r2=416716&view=diff
==============================================================================
--- xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/svg12/SVG12BridgeContext.java (original)
+++ xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/svg12/SVG12BridgeContext.java Fri Jun 23 06:32:40 2006
@@ -376,18 +376,6 @@
         }
     }
 
-    /** 
-     * This function creates a new BridgeContext, it mostly
-     * exists so subclasses can provide an instance of 
-     * themselves when a sub BridgeContext is needed.
-     */
-    public BridgeContext createBridgeContext(SVGOMDocument doc) {
-        if (doc.isSVG12()) {
-            return new SVG12BridgeContext(getUserAgent(), getDocumentLoader());
-        }
-        return new BridgeContext(getUserAgent(), getDocumentLoader());
-    }
-
     public BridgeContext createSubBridgeContext(SVGOMDocument newDoc) {
         CSSEngine eng = newDoc.getCSSEngine();
         if (eng != null) {

Modified: xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/svg12/SVG12BridgeEventSupport.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/svg12/SVG12BridgeEventSupport.java?rev=416716&r1=416715&r2=416716&view=diff
==============================================================================
--- xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/svg12/SVG12BridgeEventSupport.java (original)
+++ xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/svg12/SVG12BridgeEventSupport.java Fri Jun 23 06:32:40 2006
@@ -57,6 +57,8 @@
  */
 public abstract class SVG12BridgeEventSupport extends BridgeEventSupport {
 
+    protected SVG12BridgeEventSupport() {}
+
     /**
      * Is called only for the root element in order to dispatch GVT
      * events to the DOM.

Modified: xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/svg12/SVGFlowRootElementBridge.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/svg12/SVGFlowRootElementBridge.java?rev=416716&r1=416715&r2=416716&view=diff
==============================================================================
--- xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/svg12/SVGFlowRootElementBridge.java (original)
+++ xmlgraphics/batik/branches/anim/sources/org/apache/batik/bridge/svg12/SVGFlowRootElementBridge.java Fri Jun 23 06:32:40 2006
@@ -18,6 +18,7 @@
 
 package org.apache.batik.bridge.svg12;
 
+import java.awt.Color;
 import java.awt.RenderingHints;
 import java.awt.Shape;
 import java.awt.font.TextAttribute;
@@ -69,6 +70,7 @@
 import org.apache.batik.gvt.flow.TextLineBreaks;
 
 import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
+import org.apache.batik.gvt.text.TextPaintInfo;
 import org.apache.batik.gvt.text.TextPath;
 
 import org.apache.batik.util.SVGConstants;
@@ -364,6 +366,8 @@
      */
     protected AttributedString buildAttributedString(BridgeContext ctx,
                                                      Element element) {
+        if (element == null) return null;
+
         List rgns = getRegions(ctx, element);
         AttributedString ret = getFlowDiv(ctx, element);
         if (ret == null) return ret;
@@ -429,6 +433,12 @@
 
     protected AttributedString gatherFlowPara
         (BridgeContext ctx, Element div) {
+        TextPaintInfo divTPI = new TextPaintInfo();
+        // Set some basic props so we can get bounds info for complex paints.
+        divTPI.visible   = true;        
+        divTPI.fillPaint = Color.black;
+        elemTPI.put(div, divTPI);
+
         AttributedStringBuffer asb = new AttributedStringBuffer();
         List paraEnds  = new ArrayList();
         List paraElems = new ArrayList();
@@ -452,6 +462,8 @@
                 paraEnds.add(new Integer(asb.length()));
             }
         }
+        divTPI.startChar = 0;
+        divTPI.endChar   = asb.length()-1;
 
         // Layer in the PARAGRAPH/LINE_BREAK Attributes so we can
         // break up text chunks.
@@ -572,6 +584,7 @@
         boolean preserve = s.equals(SVG_PRESERVE_VALUE);
         boolean prevEndsWithSpace;
         Element nodeElement = element;
+        int elementStartChar = asb.length();
 
         if (top)
             endLimit = startLen = asb.length();
@@ -665,9 +678,15 @@
                     s = TextUtilities.getElementContent(ref);
                     s = normalizeString(s, preserve, prevEndsWithSpace);
                     if (s != null) {
+                        int trefStart = asb.length();
                         Map m = getAttributeMap(ctx, nodeElement, null, 
                                                 bidiLevel);
                         asb.append(s, m);
+                        int trefEnd = asb.length()-1;
+                        TextPaintInfo tpi;
+                        tpi = (TextPaintInfo)elemTPI.get(nodeElement);
+                        tpi.startChar = trefStart;
+                        tpi.endChar   = trefEnd;
                     }
                 }
                 break;
@@ -683,6 +702,7 @@
         }
 
         if (top) {
+            boolean strippedSome = false;
             while ((endLimit < asb.length()) && (asb.getLastChar() == ' ')) {
                 int idx = lnLocs.size()-1;
                 int len = asb.length();
@@ -702,8 +722,25 @@
                     }
                 }
                 asb.stripLast();
+                strippedSome = true;
+            }
+            if (strippedSome) {
+                Iterator iter = elemTPI.values().iterator();
+                while (iter.hasNext()) {
+                    TextPaintInfo tpi = (TextPaintInfo)iter.next();
+                    if (tpi.endChar >= asb.length()) {
+                        tpi.endChar = asb.length()-1;
+                        if (tpi.startChar > tpi.endChar)
+                            tpi.startChar = tpi.endChar;
+                    }
+                }
             }
         }
+
+        int elementEndChar = asb.length()-1;
+        TextPaintInfo tpi = (TextPaintInfo)elemTPI.get(element);
+        tpi.startChar = elementStartChar;
+        tpi.endChar   = elementEndChar;
     }
 
     /**
@@ -801,18 +838,17 @@
         else
             textAlign = BlockInfo.ALIGN_FULL;
 
-        Map   fontAttrs      = getFontProperties(ctx, element, null);
+        Map   fontAttrs      = new HashMap(20);
+        List  fontList       = getFontList(ctx, element, fontAttrs);
         Float fs             = (Float)fontAttrs.get(TextAttribute.SIZE);
         float fontSize       = fs.floatValue();
         float lineHeight     = getLineHeight(ctx, element, fontSize);
-        List  fontFamilyList = getFontFamilyList(element, ctx);
 
         String ln = element.getLocalName();
         boolean rgnBr;
         rgnBr = ln.equals(SVG12Constants.SVG_FLOW_REGION_BREAK_TAG);
         return new BlockInfo(top, right, bottom, left, indent, textAlign, 
-                             lineHeight, fontFamilyList, fontAttrs,
-                             rgnBr);
+                             lineHeight, fontList, fontAttrs, rgnBr);
     }
 
     protected float getLineHeight(BridgeContext ctx, Element element,