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 @@
* <title>, <desc> and <metadata>
* are non displayable elements.
*
- * @return true if the parent of the node is <text>,
+ * @return true if the parent of the node is <text>,
* <tspan>, <tref>, <textPath>, <a>,
* <altGlyph>
*/
@@ -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 <text>
*/
- 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 <tref> 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
* <tspan> : '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 <textPath> 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 <tspan> 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
* <tspan> : '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,