Source Code Cross Referenced for SVGTextElementBridge.java in  » Graphic-Library » batik » org » apache » batik » bridge » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Graphic Library » batik » org.apache.batik.bridge 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:
0003:           Licensed to the Apache Software Foundation (ASF) under one or more
0004:           contributor license agreements.  See the NOTICE file distributed with
0005:           this work for additional information regarding copyright ownership.
0006:           The ASF licenses this file to You under the Apache License, Version 2.0
0007:           (the "License"); you may not use this file except in compliance with
0008:           the License.  You may obtain a copy of the License at
0009:
0010:               http://www.apache.org/licenses/LICENSE-2.0
0011:
0012:           Unless required by applicable law or agreed to in writing, software
0013:           distributed under the License is distributed on an "AS IS" BASIS,
0014:           WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0015:           See the License for the specific language governing permissions and
0016:           limitations under the License.
0017:
0018:         */
0019:        package org.apache.batik.bridge;
0020:
0021:        import java.awt.AlphaComposite;
0022:        import java.awt.Color;
0023:        import java.awt.RenderingHints;
0024:        import java.awt.Shape;
0025:        import java.awt.font.TextAttribute;
0026:        import java.awt.geom.AffineTransform;
0027:        import java.awt.geom.GeneralPath;
0028:        import java.awt.geom.Point2D;
0029:        import java.awt.geom.Rectangle2D;
0030:        import java.lang.ref.SoftReference;
0031:        import java.text.AttributedCharacterIterator;
0032:        import java.text.AttributedString;
0033:        import java.text.AttributedCharacterIterator.Attribute;
0034:        import java.util.ArrayList;
0035:        import java.util.HashMap;
0036:        import java.util.HashSet;
0037:        import java.util.Iterator;
0038:        import java.util.List;
0039:        import java.util.Map;
0040:        import java.util.Set;
0041:        import java.util.WeakHashMap;
0042:
0043:        import org.apache.batik.css.engine.CSSEngineEvent;
0044:        import org.apache.batik.css.engine.CSSStylableElement;
0045:        import org.apache.batik.css.engine.SVGCSSEngine;
0046:        import org.apache.batik.css.engine.StyleMap;
0047:        import org.apache.batik.css.engine.value.ListValue;
0048:        import org.apache.batik.css.engine.value.Value;
0049:        import org.apache.batik.dom.events.NodeEventTarget;
0050:        import org.apache.batik.dom.svg.AbstractSVGAnimatedLength;
0051:        import org.apache.batik.dom.svg.AnimatedLiveAttributeValue;
0052:        import org.apache.batik.dom.svg.LiveAttributeException;
0053:        import org.apache.batik.dom.svg.SVGContext;
0054:        import org.apache.batik.dom.svg.SVGOMElement;
0055:        import org.apache.batik.dom.svg.SVGOMTextPositioningElement;
0056:        import org.apache.batik.dom.svg.SVGTextContent;
0057:        import org.apache.batik.dom.util.XLinkSupport;
0058:        import org.apache.batik.dom.util.XMLSupport;
0059:        import org.apache.batik.gvt.GraphicsNode;
0060:        import org.apache.batik.gvt.TextNode;
0061:        import org.apache.batik.gvt.font.FontFamilyResolver;
0062:        import org.apache.batik.gvt.font.GVTFont;
0063:        import org.apache.batik.gvt.font.GVTFontFamily;
0064:        import org.apache.batik.gvt.font.GVTGlyphMetrics;
0065:        import org.apache.batik.gvt.font.GVTGlyphVector;
0066:        import org.apache.batik.gvt.font.UnresolvedFontFamily;
0067:        import org.apache.batik.gvt.renderer.StrokingTextPainter;
0068:        import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
0069:        import org.apache.batik.gvt.text.Mark;
0070:        import org.apache.batik.gvt.text.TextHit;
0071:        import org.apache.batik.gvt.text.TextPaintInfo;
0072:        import org.apache.batik.gvt.text.TextPath;
0073:        import org.apache.batik.gvt.text.TextSpanLayout;
0074:        import org.apache.batik.util.XMLConstants;
0075:
0076:        import org.w3c.dom.Element;
0077:        import org.w3c.dom.Node;
0078:        import org.w3c.dom.css.CSSPrimitiveValue;
0079:        import org.w3c.dom.css.CSSValue;
0080:        import org.w3c.dom.events.Event;
0081:        import org.w3c.dom.events.EventListener;
0082:        import org.w3c.dom.events.MutationEvent;
0083:        import org.w3c.dom.svg.SVGLengthList;
0084:        import org.w3c.dom.svg.SVGNumberList;
0085:        import org.w3c.dom.svg.SVGTextContentElement;
0086:        import org.w3c.dom.svg.SVGTextPositioningElement;
0087:
0088:        /**
0089:         * Bridge class for the <text> element.
0090:         *
0091:         * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
0092:         * @author <a href="mailto:bill.haneman@ireland.sun.com">Bill Haneman</a>
0093:         * @version $Id: SVGTextElementBridge.java 516914 2007-03-11 14:53:18Z dvholten $
0094:         */
0095:        public class SVGTextElementBridge extends AbstractGraphicsNodeBridge
0096:                implements  SVGTextContent {
0097:
0098:            protected static final Integer ZERO = new Integer(0);
0099:
0100:            public static final AttributedCharacterIterator.Attribute TEXT_COMPOUND_DELIMITER = GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER;
0101:
0102:            public static final AttributedCharacterIterator.Attribute TEXT_COMPOUND_ID = GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_ID;
0103:
0104:            public static final AttributedCharacterIterator.Attribute PAINT_INFO = GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO;
0105:
0106:            public static final AttributedCharacterIterator.Attribute ALT_GLYPH_HANDLER = GVTAttributedCharacterIterator.TextAttribute.ALT_GLYPH_HANDLER;
0107:
0108:            public static final AttributedCharacterIterator.Attribute TEXTPATH = GVTAttributedCharacterIterator.TextAttribute.TEXTPATH;
0109:
0110:            public static final AttributedCharacterIterator.Attribute ANCHOR_TYPE = GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE;
0111:
0112:            public static final AttributedCharacterIterator.Attribute GVT_FONT_FAMILIES = GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES;
0113:
0114:            public static final AttributedCharacterIterator.Attribute GVT_FONTS = GVTAttributedCharacterIterator.TextAttribute.GVT_FONTS;
0115:
0116:            public static final AttributedCharacterIterator.Attribute BASELINE_SHIFT = GVTAttributedCharacterIterator.TextAttribute.BASELINE_SHIFT;
0117:
0118:            protected AttributedString laidoutText;
0119:
0120:            // This is used to track the TextPainterInfo for each element
0121:            // in this text element.
0122:            protected WeakHashMap elemTPI = new WeakHashMap();
0123:
0124:            // This is true if any of the spans of this text element
0125:            // use a 'complex' SVG font (meaning the font uses more
0126:            // and just the 'd' attribute on the glyph element.
0127:            // In this case we need to recreate the font when ever
0128:            // CSS attributes change on the text - so we can capture
0129:            // the effects of CSS inheritence.
0130:            protected boolean usingComplexSVGFont = false;
0131:
0132:            /**
0133:             * Constructs a new bridge for the &lt;text> element.
0134:             */
0135:            public SVGTextElementBridge() {
0136:            }
0137:
0138:            /**
0139:             * Returns 'text'.
0140:             */
0141:            public String getLocalName() {
0142:                return SVG_TEXT_TAG;
0143:            }
0144:
0145:            /**
0146:             * Returns a new instance of this bridge.
0147:             */
0148:            public Bridge getInstance() {
0149:                return new SVGTextElementBridge();
0150:            }
0151:
0152:            protected TextNode getTextNode() {
0153:                return (TextNode) node;
0154:            }
0155:
0156:            /**
0157:             * Creates a <tt>GraphicsNode</tt> according to the specified parameters.
0158:             *
0159:             * @param ctx the bridge context to use
0160:             * @param e the element that describes the graphics node to build
0161:             * @return a graphics node that represents the specified element
0162:             */
0163:            public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) {
0164:                TextNode node = (TextNode) super .createGraphicsNode(ctx, e);
0165:                if (node == null)
0166:                    return null;
0167:
0168:                associateSVGContext(ctx, e, node);
0169:
0170:                // traverse the children to add context on
0171:                // <tspan>, <tref> and <textPath>
0172:                Node child = getFirstChild(e);
0173:                while (child != null) {
0174:                    if (child.getNodeType() == Node.ELEMENT_NODE) {
0175:                        addContextToChild(ctx, (Element) child);
0176:                    }
0177:                    child = getNextSibling(child);
0178:                }
0179:
0180:                // specify the text painter to use
0181:                if (ctx.getTextPainter() != null)
0182:                    node.setTextPainter(ctx.getTextPainter());
0183:
0184:                // 'text-rendering' and 'color-rendering'
0185:                RenderingHints hints = null;
0186:                hints = CSSUtilities.convertColorRendering(e, hints);
0187:                hints = CSSUtilities.convertTextRendering(e, hints);
0188:                if (hints != null)
0189:                    node.setRenderingHints(hints);
0190:
0191:                node.setLocation(getLocation(ctx, e));
0192:
0193:                return node;
0194:            }
0195:
0196:            /**
0197:             * Creates the GraphicsNode depending on the GraphicsNodeBridge
0198:             * implementation.
0199:             */
0200:            protected GraphicsNode instantiateGraphicsNode() {
0201:                return new TextNode();
0202:            }
0203:
0204:            /**
0205:             * Returns the text node location according to the 'x' and 'y'
0206:             * attributes of the specified text element.
0207:             *
0208:             * @param ctx the bridge context to use
0209:             * @param e the text element
0210:             */
0211:            protected Point2D getLocation(BridgeContext ctx, Element e) {
0212:                try {
0213:                    SVGOMTextPositioningElement te = (SVGOMTextPositioningElement) e;
0214:
0215:                    // 'x' attribute - default is 0
0216:                    SVGLengthList xs = te.getX().getAnimVal();
0217:                    float x = 0;
0218:                    if (xs.getNumberOfItems() > 0) {
0219:                        x = xs.getItem(0).getValue();
0220:                    }
0221:
0222:                    // 'y' attribute - default is 0
0223:                    SVGLengthList ys = te.getY().getAnimVal();
0224:                    float y = 0;
0225:                    if (ys.getNumberOfItems() > 0) {
0226:                        y = ys.getItem(0).getValue();
0227:                    }
0228:
0229:                    return new Point2D.Float(x, y);
0230:                } catch (LiveAttributeException ex) {
0231:                    throw new BridgeException(ctx, ex);
0232:                }
0233:            }
0234:
0235:            protected boolean isTextElement(Element e) {
0236:                if (!SVG_NAMESPACE_URI.equals(e.getNamespaceURI()))
0237:                    return false;
0238:                String nodeName = e.getLocalName();
0239:                return (nodeName.equals(SVG_TEXT_TAG)
0240:                        || nodeName.equals(SVG_TSPAN_TAG)
0241:                        || nodeName.equals(SVG_ALT_GLYPH_TAG)
0242:                        || nodeName.equals(SVG_A_TAG)
0243:                        || nodeName.equals(SVG_TEXT_PATH_TAG) || nodeName
0244:                        .equals(SVG_TREF_TAG));
0245:            }
0246:
0247:            protected boolean isTextChild(Element e) {
0248:                if (!SVG_NAMESPACE_URI.equals(e.getNamespaceURI()))
0249:                    return false;
0250:                String nodeName = e.getLocalName();
0251:                return (nodeName.equals(SVG_TSPAN_TAG)
0252:                        || nodeName.equals(SVG_ALT_GLYPH_TAG)
0253:                        || nodeName.equals(SVG_A_TAG)
0254:                        || nodeName.equals(SVG_TEXT_PATH_TAG) || nodeName
0255:                        .equals(SVG_TREF_TAG));
0256:            }
0257:
0258:            /**
0259:             * Builds using the specified BridgeContext and element, the
0260:             * specified graphics node.
0261:             *
0262:             * @param ctx the bridge context to use
0263:             * @param e the element that describes the graphics node to build
0264:             * @param node the graphics node to build
0265:             */
0266:            public void buildGraphicsNode(BridgeContext ctx, Element e,
0267:                    GraphicsNode node) {
0268:                e.normalize();
0269:                computeLaidoutText(ctx, e, node);
0270:
0271:                //
0272:                // DO NOT CALL super, 'opacity' is handle during addPaintAttributes()
0273:                //
0274:                // 'opacity'
0275:                node.setComposite(CSSUtilities.convertOpacity(e));
0276:                // 'filter'
0277:                node.setFilter(CSSUtilities.convertFilter(e, node, ctx));
0278:                // 'mask'
0279:                node.setMask(CSSUtilities.convertMask(e, node, ctx));
0280:                // 'clip-path'
0281:                node.setClip(CSSUtilities.convertClipPath(e, node, ctx));
0282:                // 'pointer-events'
0283:                node.setPointerEventType(CSSUtilities.convertPointerEvents(e));
0284:
0285:                initializeDynamicSupport(ctx, e, node);
0286:                if (!ctx.isDynamic()) {
0287:                    elemTPI.clear();
0288:                }
0289:            }
0290:
0291:            /**
0292:             * Returns false as text is not a container.
0293:             */
0294:            public boolean isComposite() {
0295:                return false;
0296:            }
0297:
0298:            // Tree navigation ------------------------------------------------------
0299:
0300:            /**
0301:             * Returns the first child node of the given node that should be
0302:             * processed by the text bridge.
0303:             */
0304:            protected Node getFirstChild(Node n) {
0305:                return n.getFirstChild();
0306:            }
0307:
0308:            /**
0309:             * Returns the next sibling node of the given node that should be
0310:             * processed by the text bridge.
0311:             */
0312:            protected Node getNextSibling(Node n) {
0313:                return n.getNextSibling();
0314:            }
0315:
0316:            /**
0317:             * Returns the parent node of the given node that should be
0318:             * processed by the text bridge.
0319:             */
0320:            protected Node getParentNode(Node n) {
0321:                return n.getParentNode();
0322:            }
0323:
0324:            // Listener implementation ----------------------------------------------
0325:
0326:            /**
0327:             * The DOM EventListener to receive 'DOMNodeRemoved' event.
0328:             */
0329:            protected DOMChildNodeRemovedEventListener childNodeRemovedEventListener;
0330:
0331:            /**
0332:             * The DOM EventListener invoked when a node is removed.
0333:             */
0334:            protected class DOMChildNodeRemovedEventListener implements 
0335:                    EventListener {
0336:
0337:                /**
0338:                 * Handles 'DOMNodeRemoved' event type.
0339:                 */
0340:                public void handleEvent(Event evt) {
0341:                    handleDOMChildNodeRemovedEvent((MutationEvent) evt);
0342:                }
0343:            }
0344:
0345:            /**
0346:             * The DOM EventListener to receive 'DOMSubtreeModified' event.
0347:             */
0348:            protected DOMSubtreeModifiedEventListener subtreeModifiedEventListener;
0349:
0350:            /**
0351:             * The DOM EventListener invoked when the subtree is modified.
0352:             */
0353:            protected class DOMSubtreeModifiedEventListener implements 
0354:                    EventListener {
0355:
0356:                /**
0357:                 * Handles 'DOMSubtreeModified' event type.
0358:                 */
0359:                public void handleEvent(Event evt) {
0360:                    handleDOMSubtreeModifiedEvent((MutationEvent) evt);
0361:                }
0362:            }
0363:
0364:            // BridgeUpdateHandler implementation -----------------------------------
0365:
0366:            /**
0367:             * This method ensures that any modification to a text
0368:             * element and its children is going to be reflected
0369:             * into the GVT tree.
0370:             */
0371:            protected void initializeDynamicSupport(BridgeContext ctx,
0372:                    Element e, GraphicsNode node) {
0373:                super .initializeDynamicSupport(ctx, e, node);
0374:
0375:                if (ctx.isDynamic()) {
0376:                    // Only add the listeners if we are dynamic.
0377:                    addTextEventListeners(ctx, (NodeEventTarget) e);
0378:                }
0379:            }
0380:
0381:            /**
0382:             * Adds the DOM listeners for this text bridge.
0383:             */
0384:            protected void addTextEventListeners(BridgeContext ctx,
0385:                    NodeEventTarget e) {
0386:                if (childNodeRemovedEventListener == null) {
0387:                    childNodeRemovedEventListener = new DOMChildNodeRemovedEventListener();
0388:                }
0389:                if (subtreeModifiedEventListener == null) {
0390:                    subtreeModifiedEventListener = new DOMSubtreeModifiedEventListener();
0391:                }
0392:
0393:                //to be notified when a child is removed from the
0394:                //<text> element.
0395:                e.addEventListenerNS(XMLConstants.XML_EVENTS_NAMESPACE_URI,
0396:                        "DOMNodeRemoved", childNodeRemovedEventListener, true,
0397:                        null);
0398:                ctx.storeEventListenerNS(e,
0399:                        XMLConstants.XML_EVENTS_NAMESPACE_URI,
0400:                        "DOMNodeRemoved", childNodeRemovedEventListener, true);
0401:
0402:                //to be notified when the modification of the subtree
0403:                //of the <text> element is done
0404:                e.addEventListenerNS(XMLConstants.XML_EVENTS_NAMESPACE_URI,
0405:                        "DOMSubtreeModified", subtreeModifiedEventListener,
0406:                        false, null);
0407:                ctx.storeEventListenerNS(e,
0408:                        XMLConstants.XML_EVENTS_NAMESPACE_URI,
0409:                        "DOMSubtreeModified", subtreeModifiedEventListener,
0410:                        false);
0411:            }
0412:
0413:            /**
0414:             * Removes the DOM listeners for this text bridge.
0415:             */
0416:            protected void removeTextEventListeners(BridgeContext ctx,
0417:                    NodeEventTarget e) {
0418:                e.removeEventListenerNS(XMLConstants.XML_EVENTS_NAMESPACE_URI,
0419:                        "DOMNodeRemoved", childNodeRemovedEventListener, true);
0420:                e.removeEventListenerNS(XMLConstants.XML_EVENTS_NAMESPACE_URI,
0421:                        "DOMSubtreeModified", subtreeModifiedEventListener,
0422:                        false);
0423:            }
0424:
0425:            /**
0426:             * Add to the element children of the node, a
0427:             * <code>SVGContext</code> to support dynamic update. This is
0428:             * recursive, the children of the nodes are also traversed to add
0429:             * to the support elements their context
0430:             *
0431:             * @param ctx a <code>BridgeContext</code> value
0432:             * @param e an <code>Element</code> value
0433:             *
0434:             * @see org.apache.batik.dom.svg.SVGContext
0435:             * @see org.apache.batik.bridge.BridgeUpdateHandler
0436:             */
0437:            protected void addContextToChild(BridgeContext ctx, Element e) {
0438:                if (SVG_NAMESPACE_URI.equals(e.getNamespaceURI())) {
0439:                    if (e.getLocalName().equals(SVG_TSPAN_TAG)) {
0440:                        ((SVGOMElement) e).setSVGContext(new TspanBridge(ctx,
0441:                                this , e));
0442:                    } else if (e.getLocalName().equals(SVG_TEXT_PATH_TAG)) {
0443:                        ((SVGOMElement) e).setSVGContext(new TextPathBridge(
0444:                                ctx, this , e));
0445:                    } else if (e.getLocalName().equals(SVG_TREF_TAG)) {
0446:                        ((SVGOMElement) e).setSVGContext(new TRefBridge(ctx,
0447:                                this , e));
0448:                    }
0449:                }
0450:
0451:                Node child = getFirstChild(e);
0452:                while (child != null) {
0453:                    if (child.getNodeType() == Node.ELEMENT_NODE) {
0454:                        addContextToChild(ctx, (Element) child);
0455:                    }
0456:                    child = getNextSibling(child);
0457:                }
0458:            }
0459:
0460:            /**
0461:             * Invoked when an MutationEvent of type 'DOMNodeInserted' is fired.
0462:             */
0463:            public void handleDOMNodeInsertedEvent(MutationEvent evt) {
0464:                Node childNode = (Node) evt.getTarget();
0465:
0466:                //check the type of the node inserted before discard the layout
0467:                //in the case of <title> or <desc> or <metadata>, the layout
0468:                //is unchanged
0469:                switch (childNode.getNodeType()) {
0470:                case Node.TEXT_NODE: // fall-through is intended
0471:                case Node.CDATA_SECTION_NODE:
0472:                    laidoutText = null;
0473:                    break;
0474:                case Node.ELEMENT_NODE: {
0475:                    Element childElement = (Element) childNode;
0476:                    if (isTextChild(childElement)) {
0477:                        addContextToChild(ctx, childElement);
0478:                        laidoutText = null;
0479:                    }
0480:                }
0481:                    break;
0482:                default:
0483:                }
0484:                if (laidoutText == null) {
0485:                    computeLaidoutText(ctx, e, getTextNode());
0486:                }
0487:            }
0488:
0489:            /**
0490:             * Invoked when an MutationEvent of type 'DOMNodeRemoved' is fired.
0491:             */
0492:            public void handleDOMNodeRemovedEvent(MutationEvent evt) {
0493:                removeTextEventListeners(ctx, (NodeEventTarget) evt.getTarget());
0494:                super .handleDOMNodeRemovedEvent(evt);
0495:            }
0496:
0497:            /**
0498:             * Invoked when an MutationEvent of type 'DOMNodeRemoved' is fired.
0499:             */
0500:            public void handleDOMChildNodeRemovedEvent(MutationEvent evt) {
0501:                Node childNode = (Node) evt.getTarget();
0502:
0503:                //check the type of the node inserted before discard the layout
0504:                //in the case of <title> or <desc> or <metadata>, the layout
0505:                //is unchanged
0506:                switch (childNode.getNodeType()) {
0507:                case Node.TEXT_NODE: // fall-through is intended
0508:                case Node.CDATA_SECTION_NODE:
0509:                    //the parent has to be a displayed node
0510:                    if (isParentDisplayed(childNode)) {
0511:                        laidoutText = null;
0512:                    }
0513:                    break;
0514:                case Node.ELEMENT_NODE:
0515:                    if (isTextChild((Element) childNode)) {
0516:                        laidoutText = null;
0517:                    }
0518:                    break;
0519:                default:
0520:                }
0521:                //if the laidoutText was set to null,
0522:                //then wait for DOMSubtreeChange to recompute it.
0523:            }
0524:
0525:            /**
0526:             * Invoked when an MutationEvent of type 'DOMSubtree' is fired.
0527:             */
0528:            public void handleDOMSubtreeModifiedEvent(MutationEvent evt) {
0529:                //an operation occured onto the children of the
0530:                //text element, check if the layout was discarded
0531:                if (laidoutText == null) {
0532:                    computeLaidoutText(ctx, e, getTextNode());
0533:                }
0534:            }
0535:
0536:            /**
0537:             * Invoked when an MutationEvent of type 'DOMCharacterDataModified'
0538:             * is fired.
0539:             */
0540:            public void handleDOMCharacterDataModified(MutationEvent evt) {
0541:                Node childNode = (Node) evt.getTarget();
0542:                //if the parent is displayed, then discard the layout.
0543:                if (isParentDisplayed(childNode)) {
0544:                    laidoutText = null;
0545:                }
0546:            }
0547:
0548:            /**
0549:             * Indicate of the parent of a node is
0550:             * a displayed element.
0551:             * &lt;title&gt;, &lt;desc&gt; and &lt;metadata&gt;
0552:             * are non displayable elements.
0553:             *
0554:             * @return true if the parent of the node is &lt;text&gt;,
0555:             *   &lt;tspan&gt;, &lt;tref&gt;, &lt;textPath&gt;, &lt;a&gt;,
0556:             *   &lt;altGlyph&gt;
0557:             */
0558:            protected boolean isParentDisplayed(Node childNode) {
0559:                Node parentNode = getParentNode(childNode);
0560:                return isTextElement((Element) parentNode);
0561:            }
0562:
0563:            /**
0564:             * Recompute the layout of the &lt;text&gt; node.
0565:             *
0566:             * Assign onto the TextNode pending to the element
0567:             * the new recomputed AttributedString. Also
0568:             * update <code>laidoutText</code> with the new
0569:             * value.
0570:             */
0571:            protected void computeLaidoutText(BridgeContext ctx, Element e,
0572:                    GraphicsNode node) {
0573:                TextNode tn = (TextNode) node;
0574:                elemTPI.clear();
0575:
0576:                AttributedString as = buildAttributedString(ctx, e);
0577:                if (as == null) {
0578:                    tn.setAttributedCharacterIterator(null);
0579:                    return;
0580:                }
0581:
0582:                addGlyphPositionAttributes(as, e, ctx);
0583:                if (ctx.isDynamic()) {
0584:                    laidoutText = new AttributedString(as.getIterator());
0585:                }
0586:
0587:                // Install the ACI in the text node.
0588:                tn.setAttributedCharacterIterator(as.getIterator());
0589:
0590:                // Now get the real paint into - this needs to
0591:                // wait until the text node is laidout so we can get
0592:                // objectBoundingBox info.
0593:                TextPaintInfo pi = new TextPaintInfo();
0594:                setBaseTextPaintInfo(pi, e, node, ctx);
0595:                // This get's Overline/underline info.
0596:                setDecorationTextPaintInfo(pi, e);
0597:                // Install the attributes.
0598:                addPaintAttributes(as, e, tn, pi, ctx);
0599:
0600:                if (usingComplexSVGFont) {
0601:                    // Force Complex SVG fonts to be recreated, if we have them.
0602:                    tn.setAttributedCharacterIterator(as.getIterator());
0603:                }
0604:
0605:                if (ctx.isDynamic()) {
0606:                    checkBBoxChange();
0607:                }
0608:            }
0609:
0610:            /**
0611:             * This flag bit indicates if a new ACI has been created in
0612:             * response to a CSSEngineEvent.
0613:             * Avoid creating one ShapePainter per CSS property change
0614:             */
0615:            private boolean hasNewACI;
0616:
0617:            /**
0618:             * This is the element a CSS property has changed.
0619:             */
0620:            private Element cssProceedElement;
0621:
0622:            /**
0623:             * Invoked when the animated value of an animatable attribute has changed.
0624:             */
0625:            public void handleAnimatedAttributeChanged(
0626:                    AnimatedLiveAttributeValue alav) {
0627:                if (alav.getNamespaceURI() == null) {
0628:                    String ln = alav.getLocalName();
0629:                    if (ln.equals(SVG_X_ATTRIBUTE)
0630:                            || ln.equals(SVG_Y_ATTRIBUTE)
0631:                            || ln.equals(SVG_DX_ATTRIBUTE)
0632:                            || ln.equals(SVG_DY_ATTRIBUTE)
0633:                            || ln.equals(SVG_ROTATE_ATTRIBUTE)
0634:                            || ln.equals(SVG_TEXT_LENGTH_ATTRIBUTE)
0635:                            || ln.equals(SVG_LENGTH_ADJUST_ATTRIBUTE)) {
0636:                        char c = ln.charAt(0);
0637:                        if (c == 'x' || c == 'y') {
0638:                            getTextNode().setLocation(getLocation(ctx, e));
0639:                        }
0640:                        computeLaidoutText(ctx, e, getTextNode());
0641:                        return;
0642:                    }
0643:                }
0644:                super .handleAnimatedAttributeChanged(alav);
0645:            }
0646:
0647:            /**
0648:             * Invoked when CSS properties have changed on an element.
0649:             *
0650:             * @param evt the CSSEngine event that describes the update
0651:             */
0652:            public void handleCSSEngineEvent(CSSEngineEvent evt) {
0653:                hasNewACI = false;
0654:                int[] properties = evt.getProperties();
0655:                // first try to find CSS properties that change the layout
0656:                for (int i = 0; i < properties.length; ++i) {
0657:                    switch (properties[i]) { // fall-through is intended
0658:                    case SVGCSSEngine.BASELINE_SHIFT_INDEX:
0659:                    case SVGCSSEngine.DIRECTION_INDEX:
0660:                    case SVGCSSEngine.DISPLAY_INDEX:
0661:                    case SVGCSSEngine.FONT_FAMILY_INDEX:
0662:                    case SVGCSSEngine.FONT_SIZE_INDEX:
0663:                    case SVGCSSEngine.FONT_STRETCH_INDEX:
0664:                    case SVGCSSEngine.FONT_STYLE_INDEX:
0665:                    case SVGCSSEngine.FONT_WEIGHT_INDEX:
0666:                    case SVGCSSEngine.GLYPH_ORIENTATION_HORIZONTAL_INDEX:
0667:                    case SVGCSSEngine.GLYPH_ORIENTATION_VERTICAL_INDEX:
0668:                    case SVGCSSEngine.KERNING_INDEX:
0669:                    case SVGCSSEngine.LETTER_SPACING_INDEX:
0670:                    case SVGCSSEngine.TEXT_ANCHOR_INDEX:
0671:                    case SVGCSSEngine.UNICODE_BIDI_INDEX:
0672:                    case SVGCSSEngine.WORD_SPACING_INDEX:
0673:                    case SVGCSSEngine.WRITING_MODE_INDEX: {
0674:                        if (!hasNewACI) {
0675:                            hasNewACI = true;
0676:                            computeLaidoutText(ctx, e, getTextNode());
0677:                        }
0678:                        break;
0679:                    }
0680:                    }
0681:                }
0682:                //optimize the calculation of
0683:                //the painting attributes and decoration
0684:                //by only recomputing the section for the element
0685:                cssProceedElement = evt.getElement();
0686:                // go for the other CSS properties
0687:                super .handleCSSEngineEvent(evt);
0688:                cssProceedElement = null;
0689:            }
0690:
0691:            /**
0692:             * Invoked for each CSS property that has changed.
0693:             */
0694:            protected void handleCSSPropertyChanged(int property) {
0695:                switch (property) { // fall-through is intended
0696:                case SVGCSSEngine.FILL_INDEX:
0697:                case SVGCSSEngine.FILL_OPACITY_INDEX:
0698:                case SVGCSSEngine.STROKE_INDEX:
0699:                case SVGCSSEngine.STROKE_OPACITY_INDEX:
0700:                case SVGCSSEngine.STROKE_WIDTH_INDEX:
0701:                case SVGCSSEngine.STROKE_LINECAP_INDEX:
0702:                case SVGCSSEngine.STROKE_LINEJOIN_INDEX:
0703:                case SVGCSSEngine.STROKE_MITERLIMIT_INDEX:
0704:                case SVGCSSEngine.STROKE_DASHARRAY_INDEX:
0705:                case SVGCSSEngine.STROKE_DASHOFFSET_INDEX:
0706:                case SVGCSSEngine.TEXT_DECORATION_INDEX:
0707:                    rebuildACI();
0708:                    break;
0709:
0710:                case SVGCSSEngine.VISIBILITY_INDEX:
0711:                    rebuildACI();
0712:                    super .handleCSSPropertyChanged(property);
0713:                    break;
0714:                case SVGCSSEngine.TEXT_RENDERING_INDEX: {
0715:                    RenderingHints hints = node.getRenderingHints();
0716:                    hints = CSSUtilities.convertTextRendering(e, hints);
0717:                    if (hints != null) {
0718:                        node.setRenderingHints(hints);
0719:                    }
0720:                    break;
0721:                }
0722:                case SVGCSSEngine.COLOR_RENDERING_INDEX: {
0723:                    RenderingHints hints = node.getRenderingHints();
0724:                    hints = CSSUtilities.convertColorRendering(e, hints);
0725:                    if (hints != null) {
0726:                        node.setRenderingHints(hints);
0727:                    }
0728:                    break;
0729:                }
0730:                default:
0731:                    super .handleCSSPropertyChanged(property);
0732:                }
0733:            }
0734:
0735:            protected void rebuildACI() {
0736:                if (hasNewACI)
0737:                    return;
0738:
0739:                TextNode textNode = getTextNode();
0740:                if (textNode.getAttributedCharacterIterator() == null)
0741:                    return;
0742:
0743:                TextPaintInfo pi, oldPI;
0744:                if (cssProceedElement == e) {
0745:                    pi = new TextPaintInfo();
0746:                    setBaseTextPaintInfo(pi, e, node, ctx);
0747:                    setDecorationTextPaintInfo(pi, e);
0748:                    oldPI = (TextPaintInfo) elemTPI.get(e);
0749:                } else {
0750:                    //if a child CSS property has changed, then
0751:                    //retrieve the parent text decoration
0752:                    //and only update the section of the AtrtibutedString of
0753:                    //the child
0754:                    TextPaintInfo parentPI;
0755:                    parentPI = getParentTextPaintInfo(cssProceedElement);
0756:                    pi = getTextPaintInfo(cssProceedElement, textNode,
0757:                            parentPI, ctx);
0758:                    oldPI = (TextPaintInfo) elemTPI.get(cssProceedElement);
0759:                }
0760:                if (oldPI == null)
0761:                    return;
0762:
0763:                textNode.swapTextPaintInfo(pi, oldPI);
0764:                if (usingComplexSVGFont)
0765:                    // Force Complex SVG fonts to be recreated
0766:                    textNode.setAttributedCharacterIterator(textNode
0767:                            .getAttributedCharacterIterator());
0768:            }
0769:
0770:            int getElementStartIndex(Element element) {
0771:                TextPaintInfo tpi = (TextPaintInfo) elemTPI.get(element);
0772:                if (tpi == null)
0773:                    return -1;
0774:                return tpi.startChar;
0775:            }
0776:
0777:            int getElementEndIndex(Element element) {
0778:                TextPaintInfo tpi = (TextPaintInfo) elemTPI.get(element);
0779:                if (tpi == null)
0780:                    return -1;
0781:                return tpi.endChar;
0782:            }
0783:
0784:            // -----------------------------------------------------------------------
0785:            // -----------------------------------------------------------------------
0786:            // -----------------------------------------------------------------------
0787:            // -----------------------------------------------------------------------
0788:
0789:            /**
0790:             * Creates the attributed string which represents the given text
0791:             * element children.
0792:             *
0793:             * @param ctx the bridge context to use
0794:             * @param element the text element
0795:             */
0796:            protected AttributedString buildAttributedString(BridgeContext ctx,
0797:                    Element element) {
0798:
0799:                AttributedStringBuffer asb = new AttributedStringBuffer();
0800:                fillAttributedStringBuffer(ctx, element, true, null, null, asb);
0801:                return asb.toAttributedString();
0802:            }
0803:
0804:            /**
0805:             * This is used to store the end of the last piece of text
0806:             * content from an element with xml:space="preserve".  When
0807:             * we are stripping trailing spaces we need to make sure
0808:             * we don't strip anything before this point.
0809:             */
0810:            protected int endLimit;
0811:
0812:            /**
0813:             * Fills the given AttributedStringBuffer.
0814:             */
0815:            protected void fillAttributedStringBuffer(BridgeContext ctx,
0816:                    Element element, boolean top, TextPath textPath,
0817:                    Integer bidiLevel, AttributedStringBuffer asb) {
0818:                // 'requiredFeatures', 'requiredExtensions', 'systemLanguage' &
0819:                // 'display="none".
0820:                if ((!SVGUtilities.matchUserAgent(element, ctx.getUserAgent()))
0821:                        || (!CSSUtilities.convertDisplay(element))) {
0822:                    return;
0823:                }
0824:
0825:                String s = XMLSupport.getXMLSpace(element);
0826:                boolean preserve = s.equals(SVG_PRESERVE_VALUE);
0827:                boolean prevEndsWithSpace;
0828:                Element nodeElement = element;
0829:                int elementStartChar = asb.length();
0830:
0831:                if (top)
0832:                    endLimit = 0;
0833:                if (preserve)
0834:                    endLimit = asb.length();
0835:
0836:                Map map = getAttributeMap(ctx, element, textPath, bidiLevel);
0837:                Object o = map.get(TextAttribute.BIDI_EMBEDDING);
0838:                Integer subBidiLevel = bidiLevel;
0839:                if (o != null)
0840:                    subBidiLevel = ((Integer) o);
0841:
0842:                for (Node n = getFirstChild(element); n != null; n = getNextSibling(n)) {
0843:
0844:                    if (preserve) {
0845:                        prevEndsWithSpace = false;
0846:                    } else {
0847:                        if (asb.length() == 0)
0848:                            prevEndsWithSpace = true;
0849:                        else
0850:                            prevEndsWithSpace = (asb.getLastChar() == ' ');
0851:                    }
0852:
0853:                    switch (n.getNodeType()) {
0854:                    case Node.ELEMENT_NODE:
0855:                        if (!SVG_NAMESPACE_URI.equals(n.getNamespaceURI()))
0856:                            break;
0857:
0858:                        nodeElement = (Element) n;
0859:
0860:                        String ln = n.getLocalName();
0861:
0862:                        if (ln.equals(SVG_TSPAN_TAG)
0863:                                || ln.equals(SVG_ALT_GLYPH_TAG)) {
0864:                            fillAttributedStringBuffer(ctx, nodeElement, false,
0865:                                    textPath, subBidiLevel, asb);
0866:                        } else if (ln.equals(SVG_TEXT_PATH_TAG)) {
0867:                            SVGTextPathElementBridge textPathBridge = (SVGTextPathElementBridge) ctx
0868:                                    .getBridge(nodeElement);
0869:                            TextPath newTextPath = textPathBridge
0870:                                    .createTextPath(ctx, nodeElement);
0871:                            if (newTextPath != null) {
0872:                                fillAttributedStringBuffer(ctx, nodeElement,
0873:                                        false, newTextPath, subBidiLevel, asb);
0874:                            }
0875:                        } else if (ln.equals(SVG_TREF_TAG)) {
0876:                            String uriStr = XLinkSupport
0877:                                    .getXLinkHref((Element) n);
0878:                            Element ref = ctx.getReferencedElement((Element) n,
0879:                                    uriStr);
0880:                            s = TextUtilities.getElementContent(ref);
0881:                            s = normalizeString(s, preserve, prevEndsWithSpace);
0882:                            if (s != null) {
0883:                                int trefStart = asb.length();
0884:                                Map m = getAttributeMap(ctx, nodeElement,
0885:                                        textPath, bidiLevel);
0886:                                asb.append(s, m);
0887:                                int trefEnd = asb.length() - 1;
0888:                                TextPaintInfo tpi;
0889:                                tpi = (TextPaintInfo) elemTPI.get(nodeElement);
0890:                                tpi.startChar = trefStart;
0891:                                tpi.endChar = trefEnd;
0892:                            }
0893:                        } else if (ln.equals(SVG_A_TAG)) {
0894:                            NodeEventTarget target = (NodeEventTarget) nodeElement;
0895:                            UserAgent ua = ctx.getUserAgent();
0896:                            SVGAElementBridge.CursorHolder ch;
0897:                            ch = new SVGAElementBridge.CursorHolder(
0898:                                    CursorManager.DEFAULT_CURSOR);
0899:                            EventListener l;
0900:                            l = new SVGAElementBridge.AnchorListener(ua, ch);
0901:                            target.addEventListenerNS(
0902:                                    XMLConstants.XML_EVENTS_NAMESPACE_URI,
0903:                                    SVG_EVENT_CLICK, l, false, null);
0904:                            ctx.storeEventListenerNS(target,
0905:                                    XMLConstants.XML_EVENTS_NAMESPACE_URI,
0906:                                    SVG_EVENT_CLICK, l, false);
0907:
0908:                            fillAttributedStringBuffer(ctx, nodeElement, false,
0909:                                    textPath, subBidiLevel, asb);
0910:                        }
0911:                        break;
0912:                    case Node.TEXT_NODE: // fall-through is intended
0913:                    case Node.CDATA_SECTION_NODE:
0914:                        s = n.getNodeValue();
0915:                        s = normalizeString(s, preserve, prevEndsWithSpace);
0916:                        asb.append(s, map);
0917:                        if (preserve)
0918:                            endLimit = asb.length();
0919:                    }
0920:                }
0921:
0922:                if (top) {
0923:                    boolean strippedSome = false;
0924:                    while ((endLimit < asb.length())
0925:                            && (asb.getLastChar() == ' ')) {
0926:                        asb.stripLast();
0927:                        strippedSome = true;
0928:                    }
0929:                    if (strippedSome) {
0930:                        Iterator iter = elemTPI.values().iterator();
0931:                        while (iter.hasNext()) {
0932:                            TextPaintInfo tpi = (TextPaintInfo) iter.next();
0933:                            if (tpi.endChar >= asb.length()) {
0934:                                tpi.endChar = asb.length() - 1;
0935:                                if (tpi.startChar > tpi.endChar)
0936:                                    tpi.startChar = tpi.endChar;
0937:                            }
0938:                        }
0939:                    }
0940:                }
0941:                int elementEndChar = asb.length() - 1;
0942:                TextPaintInfo tpi = (TextPaintInfo) elemTPI.get(element);
0943:                tpi.startChar = elementStartChar;
0944:                tpi.endChar = elementEndChar;
0945:            }
0946:
0947:            /**
0948:             * Normalizes the given string.
0949:             */
0950:            protected String normalizeString(String s, boolean preserve,
0951:                    boolean stripfirst) {
0952:                StringBuffer sb = new StringBuffer(s.length());
0953:                if (preserve) {
0954:                    for (int i = 0; i < s.length(); i++) {
0955:                        char c = s.charAt(i);
0956:                        switch (c) { // fall-through is intended
0957:                        case 10:
0958:                        case 13:
0959:                        case '\t':
0960:                            sb.append(' ');
0961:                            break;
0962:                        default:
0963:                            sb.append(c);
0964:                        }
0965:                    }
0966:                    return sb.toString();
0967:                }
0968:
0969:                int idx = 0;
0970:                if (stripfirst) {
0971:                    loop: while (idx < s.length()) {
0972:                        switch (s.charAt(idx)) {
0973:                        default:
0974:                            break loop;
0975:                        case 10: // fall-through is intended
0976:                        case 13:
0977:                        case ' ':
0978:                        case '\t':
0979:                            idx++;
0980:                        }
0981:                    }
0982:                }
0983:
0984:                boolean space = false;
0985:                for (int i = idx; i < s.length(); i++) {
0986:                    char c = s.charAt(i);
0987:                    switch (c) {
0988:                    case 10: // fall-through is intended
0989:                    case 13:
0990:                        break;
0991:                    case ' ': // fall-through is intended
0992:                    case '\t':
0993:                        if (!space) {
0994:                            sb.append(' ');
0995:                            space = true;
0996:                        }
0997:                        break;
0998:                    default:
0999:                        sb.append(c);
1000:                        space = false;
1001:                    }
1002:                }
1003:
1004:                return sb.toString();
1005:            }
1006:
1007:            /**
1008:             * This class is used to build an AttributedString.
1009:             */
1010:            protected static class AttributedStringBuffer {
1011:
1012:                /**
1013:                 * The strings.
1014:                 */
1015:                protected List strings;
1016:
1017:                /**
1018:                 * The attributes.
1019:                 */
1020:                protected List attributes;
1021:
1022:                /**
1023:                 * The number of items.
1024:                 */
1025:                protected int count;
1026:
1027:                /**
1028:                 * The length of the attributed string.
1029:                 */
1030:                protected int length;
1031:
1032:                /**
1033:                 * Creates a new empty AttributedStringBuffer.
1034:                 */
1035:                public AttributedStringBuffer() {
1036:                    strings = new ArrayList();
1037:                    attributes = new ArrayList();
1038:                    count = 0;
1039:                    length = 0;
1040:                }
1041:
1042:                /**
1043:                 * Tells whether this AttributedStringBuffer is empty.
1044:                 */
1045:                public boolean isEmpty() {
1046:                    return count == 0;
1047:                }
1048:
1049:                /**
1050:                 * Returns the length in chars of the current Attributed String
1051:                 */
1052:                public int length() {
1053:                    return length;
1054:                }
1055:
1056:                /**
1057:                 * Appends a String and its associated attributes.
1058:                 */
1059:                public void append(String s, Map m) {
1060:                    if (s.length() == 0)
1061:                        return;
1062:                    strings.add(s);
1063:                    attributes.add(m);
1064:                    count++;
1065:                    length += s.length();
1066:                }
1067:
1068:                /**
1069:                 * Returns the value of the last char or -1.
1070:                 */
1071:                public int getLastChar() {
1072:                    if (count == 0) {
1073:                        return -1;
1074:                    }
1075:                    String s = (String) strings.get(count - 1);
1076:                    return s.charAt(s.length() - 1);
1077:                }
1078:
1079:                /**
1080:                 * Strips the last string character.
1081:                 */
1082:                public void stripFirst() {
1083:                    String s = (String) strings.get(0);
1084:                    if (s.charAt(s.length() - 1) != ' ')
1085:                        return;
1086:
1087:                    length--;
1088:
1089:                    if (s.length() == 1) {
1090:                        attributes.remove(0);
1091:                        strings.remove(0);
1092:                        count--;
1093:                        return;
1094:                    }
1095:
1096:                    strings.set(0, s.substring(1));
1097:                }
1098:
1099:                /**
1100:                 * Strips the last string character.
1101:                 */
1102:                public void stripLast() {
1103:                    String s = (String) strings.get(count - 1);
1104:                    if (s.charAt(s.length() - 1) != ' ')
1105:                        return;
1106:
1107:                    length--;
1108:
1109:                    if (s.length() == 1) {
1110:                        attributes.remove(--count);
1111:                        strings.remove(count);
1112:                        return;
1113:                    }
1114:
1115:                    strings.set(count - 1, s.substring(0, s.length() - 1));
1116:                }
1117:
1118:                /**
1119:                 * Builds an attributed string from the content of this
1120:                 * buffer.
1121:                 */
1122:                public AttributedString toAttributedString() {
1123:                    switch (count) {
1124:                    case 0:
1125:                        return null;
1126:                    case 1:
1127:                        return new AttributedString((String) strings.get(0),
1128:                                (Map) attributes.get(0));
1129:                    }
1130:
1131:                    StringBuffer sb = new StringBuffer(strings.size() * 5);
1132:                    Iterator it = strings.iterator();
1133:                    while (it.hasNext()) {
1134:                        sb.append((String) it.next());
1135:                    }
1136:
1137:                    AttributedString result = new AttributedString(sb
1138:                            .toString());
1139:
1140:                    // Set the attributes
1141:
1142:                    Iterator sit = strings.iterator();
1143:                    Iterator ait = attributes.iterator();
1144:                    int idx = 0;
1145:                    while (sit.hasNext()) {
1146:                        String s = (String) sit.next();
1147:                        int nidx = idx + s.length();
1148:                        Map m = (Map) ait.next();
1149:                        Iterator kit = m.keySet().iterator();
1150:                        Iterator vit = m.values().iterator();
1151:                        while (kit.hasNext()) {
1152:                            Attribute attr = (Attribute) kit.next();
1153:                            Object val = vit.next();
1154:                            result.addAttribute(attr, val, idx, nidx);
1155:                        }
1156:                        idx = nidx;
1157:                    }
1158:
1159:                    return result;
1160:                }
1161:
1162:                public String toString() {
1163:                    switch (count) {
1164:                    case 0:
1165:                        return "";
1166:                    case 1:
1167:                        return (String) strings.get(0);
1168:                    }
1169:
1170:                    StringBuffer sb = new StringBuffer(strings.size() * 5);
1171:                    Iterator it = strings.iterator();
1172:                    while (it.hasNext()) {
1173:                        sb.append((String) it.next());
1174:                    }
1175:                    return sb.toString();
1176:                }
1177:            }
1178:
1179:            /**
1180:             * Returns true if node1 is an ancestor of node2
1181:             */
1182:            protected boolean nodeAncestorOf(Node node1, Node node2) {
1183:                if (node2 == null || node1 == null) {
1184:                    return false;
1185:                }
1186:                Node parent = getParentNode(node2);
1187:                while (parent != null && parent != node1) {
1188:                    parent = getParentNode(parent);
1189:                }
1190:                return (parent == node1);
1191:            }
1192:
1193:            /**
1194:             * Adds glyph position attributes to an AttributedString.
1195:             */
1196:            protected void addGlyphPositionAttributes(AttributedString as,
1197:                    Element element, BridgeContext ctx) {
1198:
1199:                // 'requiredFeatures', 'requiredExtensions' and 'systemLanguage'
1200:                if ((!SVGUtilities.matchUserAgent(element, ctx.getUserAgent()))
1201:                        || (!CSSUtilities.convertDisplay(element))) {
1202:                    return;
1203:                }
1204:                if (element.getLocalName().equals(SVG_TEXT_PATH_TAG)) {
1205:                    // 'textPath' doesn't support position attributes.
1206:                    addChildGlyphPositionAttributes(as, element, ctx);
1207:                    return;
1208:                }
1209:
1210:                // calculate which chars in the string belong to this element
1211:                int firstChar = getElementStartIndex(element);
1212:                // No match so no chars to annotate.
1213:                if (firstChar == -1)
1214:                    return;
1215:
1216:                int lastChar = getElementEndIndex(element);
1217:
1218:                // 'a' elements aren't SVGTextPositioningElements, so don't process
1219:                // their positioning attributes on them.
1220:                if (!(element instanceof  SVGTextPositioningElement)) {
1221:                    addChildGlyphPositionAttributes(as, element, ctx);
1222:                    return;
1223:                }
1224:
1225:                // get all of the glyph position attribute values
1226:                SVGTextPositioningElement te = (SVGTextPositioningElement) element;
1227:
1228:                try {
1229:                    SVGLengthList xs = te.getX().getAnimVal();
1230:                    SVGLengthList ys = te.getY().getAnimVal();
1231:                    SVGLengthList dxs = te.getDx().getAnimVal();
1232:                    SVGLengthList dys = te.getDy().getAnimVal();
1233:                    SVGNumberList rs = te.getRotate().getAnimVal();
1234:
1235:                    int len;
1236:
1237:                    // process the x attribute
1238:                    len = xs.getNumberOfItems();
1239:                    for (int i = 0; i < len && firstChar + i <= lastChar; i++) {
1240:                        as.addAttribute(
1241:                                GVTAttributedCharacterIterator.TextAttribute.X,
1242:                                new Float(xs.getItem(i).getValue()), firstChar
1243:                                        + i, firstChar + i + 1);
1244:                    }
1245:
1246:                    // process the y attribute
1247:                    len = ys.getNumberOfItems();
1248:                    for (int i = 0; i < len && firstChar + i <= lastChar; i++) {
1249:                        as.addAttribute(
1250:                                GVTAttributedCharacterIterator.TextAttribute.Y,
1251:                                new Float(ys.getItem(i).getValue()), firstChar
1252:                                        + i, firstChar + i + 1);
1253:                    }
1254:
1255:                    // process dx attribute
1256:                    len = dxs.getNumberOfItems();
1257:                    for (int i = 0; i < len && firstChar + i <= lastChar; i++) {
1258:                        as
1259:                                .addAttribute(
1260:                                        GVTAttributedCharacterIterator.TextAttribute.DX,
1261:                                        new Float(dxs.getItem(i).getValue()),
1262:                                        firstChar + i, firstChar + i + 1);
1263:                    }
1264:
1265:                    // process dy attribute
1266:                    len = dys.getNumberOfItems();
1267:                    for (int i = 0; i < len && firstChar + i <= lastChar; i++) {
1268:                        as
1269:                                .addAttribute(
1270:                                        GVTAttributedCharacterIterator.TextAttribute.DY,
1271:                                        new Float(dys.getItem(i).getValue()),
1272:                                        firstChar + i, firstChar + i + 1);
1273:                    }
1274:
1275:                    // process rotate attribute
1276:                    len = rs.getNumberOfItems();
1277:                    if (len == 1) { // not a list
1278:                        // each char will have the same rotate value
1279:                        Float rad = new Float(Math.toRadians(rs.getItem(0)
1280:                                .getValue()));
1281:                        as
1282:                                .addAttribute(
1283:                                        GVTAttributedCharacterIterator.TextAttribute.ROTATION,
1284:                                        rad, firstChar, lastChar + 1);
1285:
1286:                    } else if (len > 1) { // it's a list
1287:                        // set each rotate value from the list
1288:                        for (int i = 0; i < len && firstChar + i <= lastChar; i++) {
1289:                            Float rad = new Float(Math.toRadians(rs.getItem(i)
1290:                                    .getValue()));
1291:                            as
1292:                                    .addAttribute(
1293:                                            GVTAttributedCharacterIterator.TextAttribute.ROTATION,
1294:                                            rad, firstChar + i, firstChar + i
1295:                                                    + 1);
1296:                        }
1297:                    }
1298:
1299:                    addChildGlyphPositionAttributes(as, element, ctx);
1300:                } catch (LiveAttributeException ex) {
1301:                    throw new BridgeException(ctx, ex);
1302:                }
1303:            }
1304:
1305:            protected void addChildGlyphPositionAttributes(AttributedString as,
1306:                    Element element, BridgeContext ctx) {
1307:                // do the same for each child element
1308:                for (Node child = getFirstChild(element); child != null; child = getNextSibling(child)) {
1309:                    if (child.getNodeType() != Node.ELEMENT_NODE)
1310:                        continue;
1311:
1312:                    Element childElement = (Element) child;
1313:                    if (isTextChild(childElement)) {
1314:                        addGlyphPositionAttributes(as, childElement, ctx);
1315:                    }
1316:                }
1317:            }
1318:
1319:            /**
1320:             * Adds painting attributes to an AttributedString.
1321:             */
1322:            protected void addPaintAttributes(AttributedString as,
1323:                    Element element, TextNode node, TextPaintInfo pi,
1324:                    BridgeContext ctx) {
1325:                // 'requiredFeatures', 'requiredExtensions' and 'systemLanguage'
1326:                if ((!SVGUtilities.matchUserAgent(element, ctx.getUserAgent()))
1327:                        || (!CSSUtilities.convertDisplay(element))) {
1328:                    return;
1329:                }
1330:                Object o = elemTPI.get(element);
1331:                if (o != null) {
1332:                    node.swapTextPaintInfo(pi, (TextPaintInfo) o);
1333:                }
1334:                addChildPaintAttributes(as, element, node, pi, ctx);
1335:            }
1336:
1337:            protected void addChildPaintAttributes(AttributedString as,
1338:                    Element element, TextNode node, TextPaintInfo parentPI,
1339:                    BridgeContext ctx) {
1340:                // Add Paint attributres for children of text element
1341:                for (Node child = getFirstChild(element); child != null; child = getNextSibling(child)) {
1342:                    if (child.getNodeType() != Node.ELEMENT_NODE) {
1343:                        continue;
1344:                    }
1345:                    Element childElement = (Element) child;
1346:                    if (isTextChild(childElement)) {
1347:                        TextPaintInfo pi = getTextPaintInfo(childElement, node,
1348:                                parentPI, ctx);
1349:                        addPaintAttributes(as, childElement, node, pi, ctx);
1350:                    }
1351:                }
1352:            }
1353:
1354:            /**
1355:             * This method adds all the font related properties to <tt>result</tt>
1356:             * It also builds a List of the GVTFonts and returns it.
1357:             */
1358:            protected List getFontList(BridgeContext ctx, Element element,
1359:                    Map result) {
1360:
1361:                // Unique value for text element - used for run identification.
1362:                result.put(TEXT_COMPOUND_ID, new SoftReference(element));
1363:
1364:                Float fsFloat = TextUtilities.convertFontSize(element);
1365:                float fontSize = fsFloat.floatValue();
1366:                // Font size.
1367:                result.put(TextAttribute.SIZE, fsFloat);
1368:
1369:                // Font stretch
1370:                result.put(TextAttribute.WIDTH, TextUtilities
1371:                        .convertFontStretch(element));
1372:
1373:                // Font style
1374:                result.put(TextAttribute.POSTURE, TextUtilities
1375:                        .convertFontStyle(element));
1376:
1377:                // Font weight
1378:                result.put(TextAttribute.WEIGHT, TextUtilities
1379:                        .convertFontWeight(element));
1380:
1381:                // Font weight
1382:                Value v = CSSUtilities.getComputedStyle(element,
1383:                        SVGCSSEngine.FONT_WEIGHT_INDEX);
1384:                String fontWeightString = v.getCssText();
1385:
1386:                // Font style
1387:                String fontStyleString = CSSUtilities.getComputedStyle(element,
1388:                        SVGCSSEngine.FONT_STYLE_INDEX).getStringValue();
1389:
1390:                // Needed for SVG fonts (also for dynamic documents).
1391:                result.put(TEXT_COMPOUND_DELIMITER, element);
1392:
1393:                //  make a list of GVTFont objects
1394:                Value val = CSSUtilities.getComputedStyle(element,
1395:                        SVGCSSEngine.FONT_FAMILY_INDEX);
1396:                List fontFamilyList = new ArrayList();
1397:                List fontList = new ArrayList();
1398:                int len = val.getLength();
1399:                for (int i = 0; i < len; i++) {
1400:                    Value it = val.item(i);
1401:                    String fontFamilyName = it.getStringValue();
1402:                    GVTFontFamily fontFamily;
1403:                    fontFamily = SVGFontUtilities.getFontFamily(element, ctx,
1404:                            fontFamilyName, fontWeightString, fontStyleString);
1405:                    if (fontFamily == null)
1406:                        continue;
1407:                    if (fontFamily instanceof  UnresolvedFontFamily) {
1408:                        fontFamily = FontFamilyResolver
1409:                                .resolve((UnresolvedFontFamily) fontFamily);
1410:                        if (fontFamily == null)
1411:                            continue;
1412:                    }
1413:                    fontFamilyList.add(fontFamily);
1414:                    if (fontFamily instanceof  SVGFontFamily) {
1415:                        SVGFontFamily svgFF = (SVGFontFamily) fontFamily;
1416:                        if (svgFF.isComplex()) {
1417:                            usingComplexSVGFont = true;
1418:                        }
1419:                    }
1420:                    GVTFont ft = fontFamily.deriveFont(fontSize, result);
1421:                    fontList.add(ft);
1422:                }
1423:
1424:                // Eventually this will need to go for SVG fonts it
1425:                // holds hard ref to DOM.
1426:                result.put(GVT_FONT_FAMILIES, fontFamilyList);
1427:
1428:                if (!ctx.isDynamic()) {
1429:                    // Only leave this in the map for dynamic documents.
1430:                    // Otherwise it will cause the whole DOM to stay when
1431:                    // we don't really need it.
1432:                    result.remove(TEXT_COMPOUND_DELIMITER);
1433:                }
1434:                return fontList;
1435:            }
1436:
1437:            /**
1438:             * Returns the map to pass to the current characters.
1439:             */
1440:            protected Map getAttributeMap(BridgeContext ctx, Element element,
1441:                    TextPath textPath, Integer bidiLevel) {
1442:                SVGTextContentElement tce = null;
1443:                if (element instanceof  SVGTextContentElement) {
1444:                    // 'a' elements aren't SVGTextContentElements, so they shouldn't
1445:                    // be checked for 'textLength' or 'lengthAdjust' attributes.
1446:                    tce = (SVGTextContentElement) element;
1447:                }
1448:
1449:                Map result = new HashMap();
1450:                String s;
1451:
1452:                if (SVG_NAMESPACE_URI.equals(element.getNamespaceURI())
1453:                        && element.getLocalName().equals(SVG_ALT_GLYPH_TAG)) {
1454:                    result.put(ALT_GLYPH_HANDLER, new SVGAltGlyphHandler(ctx,
1455:                            element));
1456:                }
1457:
1458:                // Add null TPI objects to the text (after we set it on the
1459:                // Text we will swap in the correct values.
1460:                TextPaintInfo pi = new TextPaintInfo();
1461:                // Set some basic props so we can get bounds info for complex paints.
1462:                pi.visible = true;
1463:                pi.fillPaint = Color.black;
1464:                result.put(PAINT_INFO, pi);
1465:                elemTPI.put(element, pi);
1466:
1467:                if (textPath != null) {
1468:                    result.put(TEXTPATH, textPath);
1469:                }
1470:
1471:                // Text-anchor
1472:                TextNode.Anchor a = TextUtilities.convertTextAnchor(element);
1473:                result.put(ANCHOR_TYPE, a);
1474:
1475:                // Font family
1476:                List fontList = getFontList(ctx, element, result);
1477:                result.put(GVT_FONTS, fontList);
1478:
1479:                // Text baseline adjustment.
1480:                Object bs = TextUtilities.convertBaselineShift(element);
1481:                if (bs != null) {
1482:                    result.put(BASELINE_SHIFT, bs);
1483:                }
1484:
1485:                // Unicode-bidi mode
1486:                Value val = CSSUtilities.getComputedStyle(element,
1487:                        SVGCSSEngine.UNICODE_BIDI_INDEX);
1488:                s = val.getStringValue();
1489:                if (s.charAt(0) == 'n') {
1490:                    if (bidiLevel != null)
1491:                        result.put(TextAttribute.BIDI_EMBEDDING, bidiLevel);
1492:                } else {
1493:
1494:                    // Text direction
1495:                    // XXX: this needs to coordinate with the unicode-bidi
1496:                    // property, so that when an explicit reversal
1497:                    // occurs, the BIDI_EMBEDDING level is
1498:                    // appropriately incremented or decremented.
1499:                    // Note that direction is implicitly handled by unicode
1500:                    // BiDi algorithm in most cases, this property
1501:                    // is only needed when one wants to override the
1502:                    // normal writing direction for a string/substring.
1503:
1504:                    val = CSSUtilities.getComputedStyle(element,
1505:                            SVGCSSEngine.DIRECTION_INDEX);
1506:                    String rs = val.getStringValue();
1507:                    int cbidi = 0;
1508:                    if (bidiLevel != null)
1509:                        cbidi = bidiLevel.intValue();
1510:
1511:                    // We don't care if it was embed or override we just want
1512:                    // it's level here. So map override to positive value.
1513:                    if (cbidi < 0)
1514:                        cbidi = -cbidi;
1515:
1516:                    switch (rs.charAt(0)) {
1517:                    case 'l':
1518:                        result.put(TextAttribute.RUN_DIRECTION,
1519:                                TextAttribute.RUN_DIRECTION_LTR);
1520:                        if ((cbidi & 0x1) == 1)
1521:                            cbidi++; // was odd now even
1522:                        else
1523:                            cbidi += 2; // next greater even number
1524:                        break;
1525:                    case 'r':
1526:                        result.put(TextAttribute.RUN_DIRECTION,
1527:                                TextAttribute.RUN_DIRECTION_RTL);
1528:                        if ((cbidi & 0x1) == 1)
1529:                            cbidi += 2; // next greater odd number
1530:                        else
1531:                            cbidi++; // was even now odd
1532:                        break;
1533:                    }
1534:
1535:                    switch (s.charAt(0)) {
1536:                    case 'b': // bidi-override
1537:                        cbidi = -cbidi; // For bidi-override we want a negative number.
1538:                        break;
1539:                    }
1540:
1541:                    result
1542:                            .put(TextAttribute.BIDI_EMBEDDING, new Integer(
1543:                                    cbidi));
1544:                }
1545:
1546:                // Writing mode
1547:
1548:                val = CSSUtilities.getComputedStyle(element,
1549:                        SVGCSSEngine.WRITING_MODE_INDEX);
1550:                s = val.getStringValue();
1551:                switch (s.charAt(0)) {
1552:                case 'l':
1553:                    result
1554:                            .put(
1555:                                    GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE,
1556:                                    GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR);
1557:                    break;
1558:                case 'r':
1559:                    result
1560:                            .put(
1561:                                    GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE,
1562:                                    GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_RTL);
1563:                    break;
1564:                case 't':
1565:                    result
1566:                            .put(
1567:                                    GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE,
1568:                                    GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_TTB);
1569:                    break;
1570:                }
1571:
1572:                // glyph-orientation-vertical
1573:
1574:                val = CSSUtilities.getComputedStyle(element,
1575:                        SVGCSSEngine.GLYPH_ORIENTATION_VERTICAL_INDEX);
1576:                int primitiveType = val.getPrimitiveType();
1577:                switch (primitiveType) {
1578:                case CSSPrimitiveValue.CSS_IDENT: // auto
1579:                    result
1580:                            .put(
1581:                                    GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION,
1582:                                    GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_AUTO);
1583:                    break;
1584:                case CSSPrimitiveValue.CSS_DEG:
1585:                    result
1586:                            .put(
1587:                                    GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION,
1588:                                    GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE);
1589:                    result
1590:                            .put(
1591:                                    GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION_ANGLE,
1592:                                    new Float(val.getFloatValue()));
1593:                    break;
1594:                case CSSPrimitiveValue.CSS_RAD:
1595:                    result
1596:                            .put(
1597:                                    GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION,
1598:                                    GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE);
1599:                    result
1600:                            .put(
1601:                                    GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION_ANGLE,
1602:                                    new Float(Math.toDegrees(val
1603:                                            .getFloatValue())));
1604:                    break;
1605:                case CSSPrimitiveValue.CSS_GRAD:
1606:                    result
1607:                            .put(
1608:                                    GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION,
1609:                                    GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE);
1610:                    result
1611:                            .put(
1612:                                    GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION_ANGLE,
1613:                                    new Float(val.getFloatValue() * 9 / 5));
1614:                    break;
1615:                default:
1616:                    // Cannot happen
1617:                    throw new IllegalStateException(
1618:                            "unexpected primitiveType (V):" + primitiveType);
1619:                }
1620:
1621:                // glyph-orientation-horizontal
1622:
1623:                val = CSSUtilities.getComputedStyle(element,
1624:                        SVGCSSEngine.GLYPH_ORIENTATION_HORIZONTAL_INDEX);
1625:                primitiveType = val.getPrimitiveType();
1626:                switch (primitiveType) {
1627:                case CSSPrimitiveValue.CSS_DEG:
1628:                    result
1629:                            .put(
1630:                                    GVTAttributedCharacterIterator.TextAttribute.HORIZONTAL_ORIENTATION_ANGLE,
1631:                                    new Float(val.getFloatValue()));
1632:                    break;
1633:                case CSSPrimitiveValue.CSS_RAD:
1634:                    result
1635:                            .put(
1636:                                    GVTAttributedCharacterIterator.TextAttribute.HORIZONTAL_ORIENTATION_ANGLE,
1637:                                    new Float(Math.toDegrees(val
1638:                                            .getFloatValue())));
1639:                    break;
1640:                case CSSPrimitiveValue.CSS_GRAD:
1641:                    result
1642:                            .put(
1643:                                    GVTAttributedCharacterIterator.TextAttribute.HORIZONTAL_ORIENTATION_ANGLE,
1644:                                    new Float(val.getFloatValue() * 9 / 5));
1645:                    break;
1646:                default:
1647:                    // Cannot happen
1648:                    throw new IllegalStateException(
1649:                            "unexpected primitiveType (H):" + primitiveType);
1650:                }
1651:
1652:                // text spacing properties...
1653:
1654:                // Letter Spacing
1655:                Float sp = TextUtilities.convertLetterSpacing(element);
1656:                if (sp != null) {
1657:                    result
1658:                            .put(
1659:                                    GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING,
1660:                                    sp);
1661:                    result
1662:                            .put(
1663:                                    GVTAttributedCharacterIterator.TextAttribute.CUSTOM_SPACING,
1664:                                    Boolean.TRUE);
1665:                }
1666:
1667:                // Word spacing
1668:                sp = TextUtilities.convertWordSpacing(element);
1669:                if (sp != null) {
1670:                    result
1671:                            .put(
1672:                                    GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING,
1673:                                    sp);
1674:                    result
1675:                            .put(
1676:                                    GVTAttributedCharacterIterator.TextAttribute.CUSTOM_SPACING,
1677:                                    Boolean.TRUE);
1678:                }
1679:
1680:                // Kerning
1681:                sp = TextUtilities.convertKerning(element);
1682:                if (sp != null) {
1683:                    result
1684:                            .put(
1685:                                    GVTAttributedCharacterIterator.TextAttribute.KERNING,
1686:                                    sp);
1687:                    result
1688:                            .put(
1689:                                    GVTAttributedCharacterIterator.TextAttribute.CUSTOM_SPACING,
1690:                                    Boolean.TRUE);
1691:                }
1692:
1693:                if (tce == null) {
1694:                    return result;
1695:                }
1696:
1697:                try {
1698:                    // textLength
1699:                    AbstractSVGAnimatedLength textLength = (AbstractSVGAnimatedLength) tce
1700:                            .getTextLength();
1701:                    if (textLength.isSpecified()) {
1702:                        result
1703:                                .put(
1704:                                        GVTAttributedCharacterIterator.TextAttribute.BBOX_WIDTH,
1705:                                        new Float(textLength.getAnimVal()
1706:                                                .getValue()));
1707:
1708:                        // lengthAdjust
1709:                        if (tce.getLengthAdjust().getAnimVal() == SVGTextContentElement.LENGTHADJUST_SPACINGANDGLYPHS) {
1710:                            result
1711:                                    .put(
1712:                                            GVTAttributedCharacterIterator.TextAttribute.LENGTH_ADJUST,
1713:                                            GVTAttributedCharacterIterator.TextAttribute.ADJUST_ALL);
1714:                        } else {
1715:                            result
1716:                                    .put(
1717:                                            GVTAttributedCharacterIterator.TextAttribute.LENGTH_ADJUST,
1718:                                            GVTAttributedCharacterIterator.TextAttribute.ADJUST_SPACING);
1719:                            result
1720:                                    .put(
1721:                                            GVTAttributedCharacterIterator.TextAttribute.CUSTOM_SPACING,
1722:                                            Boolean.TRUE);
1723:                        }
1724:                    }
1725:                } catch (LiveAttributeException ex) {
1726:                    throw new BridgeException(ctx, ex);
1727:                }
1728:
1729:                return result;
1730:            }
1731:
1732:            /**
1733:             * Retrieve in the AttributeString the closest parent
1734:             * of the node 'child' and extract the text decorations
1735:             * of the parent.
1736:             *
1737:             * @param child an <code>Element</code> value
1738:             * @return a <code>TextDecoration</code> value
1739:             */
1740:            protected TextPaintInfo getParentTextPaintInfo(Element child) {
1741:                Node parent = getParentNode(child);
1742:                while (parent != null) {
1743:                    TextPaintInfo tpi = (TextPaintInfo) elemTPI.get(parent);
1744:                    if (tpi != null)
1745:                        return tpi;
1746:                    parent = getParentNode(parent);
1747:                }
1748:                return null;
1749:            }
1750:
1751:            /**
1752:             * Constructs a TextDecoration object for the specified element. This will
1753:             * contain all of the decoration properties to be used when drawing the
1754:             * text.
1755:             */
1756:            protected TextPaintInfo getTextPaintInfo(Element element,
1757:                    GraphicsNode node, TextPaintInfo parentTPI,
1758:                    BridgeContext ctx) {
1759:                // Force the engine to update stuff..
1760:                CSSUtilities.getComputedStyle(element,
1761:                        SVGCSSEngine.TEXT_DECORATION_INDEX);
1762:
1763:                TextPaintInfo pi = new TextPaintInfo(parentTPI);
1764:
1765:                // Was text-decoration explicity set on this element?
1766:                StyleMap sm = ((CSSStylableElement) element)
1767:                        .getComputedStyleMap(null);
1768:                if ((sm.isNullCascaded(SVGCSSEngine.TEXT_DECORATION_INDEX))
1769:                        && (sm.isNullCascaded(SVGCSSEngine.FILL_INDEX))
1770:                        && (sm.isNullCascaded(SVGCSSEngine.STROKE_INDEX))
1771:                        && (sm.isNullCascaded(SVGCSSEngine.STROKE_WIDTH_INDEX))
1772:                        && (sm.isNullCascaded(SVGCSSEngine.OPACITY_INDEX))) {
1773:                    // If not, keep the same decorations.
1774:                    return pi;
1775:                }
1776:
1777:                setBaseTextPaintInfo(pi, element, node, ctx);
1778:
1779:                if (!sm.isNullCascaded(SVGCSSEngine.TEXT_DECORATION_INDEX))
1780:                    setDecorationTextPaintInfo(pi, element);
1781:
1782:                return pi;
1783:            }
1784:
1785:            public void setBaseTextPaintInfo(TextPaintInfo pi, Element element,
1786:                    GraphicsNode node, BridgeContext ctx) {
1787:                if (!element.getLocalName().equals(SVG_TEXT_TAG))
1788:                    pi.composite = CSSUtilities.convertOpacity(element);
1789:                else
1790:                    pi.composite = AlphaComposite.SrcOver;
1791:
1792:                pi.visible = CSSUtilities.convertVisibility(element);
1793:                pi.fillPaint = PaintServer.convertFillPaint(element, node, ctx);
1794:                pi.strokePaint = PaintServer.convertStrokePaint(element, node,
1795:                        ctx);
1796:                pi.strokeStroke = PaintServer.convertStroke(element);
1797:            }
1798:
1799:            public void setDecorationTextPaintInfo(TextPaintInfo pi,
1800:                    Element element) {
1801:                Value val = CSSUtilities.getComputedStyle(element,
1802:                        SVGCSSEngine.TEXT_DECORATION_INDEX);
1803:
1804:                switch (val.getCssValueType()) {
1805:                case CSSValue.CSS_VALUE_LIST:
1806:                    ListValue lst = (ListValue) val;
1807:
1808:                    int len = lst.getLength();
1809:                    for (int i = 0; i < len; i++) {
1810:                        Value v = lst.item(i);
1811:                        String s = v.getStringValue();
1812:                        switch (s.charAt(0)) {
1813:                        case 'u':
1814:                            if (pi.fillPaint != null) {
1815:                                pi.underlinePaint = pi.fillPaint;
1816:                            }
1817:                            if (pi.strokePaint != null) {
1818:                                pi.underlineStrokePaint = pi.strokePaint;
1819:                            }
1820:                            if (pi.strokeStroke != null) {
1821:                                pi.underlineStroke = pi.strokeStroke;
1822:                            }
1823:                            break;
1824:                        case 'o':
1825:                            if (pi.fillPaint != null) {
1826:                                pi.overlinePaint = pi.fillPaint;
1827:                            }
1828:                            if (pi.strokePaint != null) {
1829:                                pi.overlineStrokePaint = pi.strokePaint;
1830:                            }
1831:                            if (pi.strokeStroke != null) {
1832:                                pi.overlineStroke = pi.strokeStroke;
1833:                            }
1834:                            break;
1835:                        case 'l':
1836:                            if (pi.fillPaint != null) {
1837:                                pi.strikethroughPaint = pi.fillPaint;
1838:                            }
1839:                            if (pi.strokePaint != null) {
1840:                                pi.strikethroughStrokePaint = pi.strokePaint;
1841:                            }
1842:                            if (pi.strokeStroke != null) {
1843:                                pi.strikethroughStroke = pi.strokeStroke;
1844:                            }
1845:                            break;
1846:                        }
1847:                    }
1848:                    break;
1849:
1850:                default: // None
1851:                    pi.underlinePaint = null;
1852:                    pi.underlineStrokePaint = null;
1853:                    pi.underlineStroke = null;
1854:
1855:                    pi.overlinePaint = null;
1856:                    pi.overlineStrokePaint = null;
1857:                    pi.overlineStroke = null;
1858:
1859:                    pi.strikethroughPaint = null;
1860:                    pi.strikethroughStrokePaint = null;
1861:                    pi.strikethroughStroke = null;
1862:                    break;
1863:                }
1864:            }
1865:
1866:            /**
1867:             * Implementation of <code>SVGContext</code> for
1868:             * the children of &lt;text&gt;
1869:             */
1870:            public abstract class AbstractTextChildSVGContext extends
1871:                    AnimatableSVGBridge {
1872:
1873:                /** Text bridge parent */
1874:                protected SVGTextElementBridge textBridge;
1875:
1876:                /**
1877:                 * Initialize the <code>SVGContext</code> implementation
1878:                 * with the bridgeContext, the parent bridge, and the
1879:                 * element supervised by this context
1880:                 */
1881:                public AbstractTextChildSVGContext(BridgeContext ctx,
1882:                        SVGTextElementBridge parent, Element e) {
1883:                    this .ctx = ctx;
1884:                    this .textBridge = parent;
1885:                    this .e = e;
1886:                }
1887:
1888:                /**
1889:                 * Returns the namespace URI of the element this <tt>Bridge</tt> is
1890:                 * dedicated to.
1891:                 */
1892:                public String getNamespaceURI() {
1893:                    return null;
1894:                }
1895:
1896:                /**
1897:                 * Returns the local name of the element this <tt>Bridge</tt> is dedicated
1898:                 * to.
1899:                 */
1900:                public String getLocalName() {
1901:                    return null;
1902:                }
1903:
1904:                /**
1905:                 * Returns a new instance of this bridge.
1906:                 */
1907:                public Bridge getInstance() {
1908:                    return null;
1909:                }
1910:
1911:                public SVGTextElementBridge getTextBridge() {
1912:                    return textBridge;
1913:                }
1914:
1915:                /**
1916:                 * Returns the size of a px CSS unit in millimeters.
1917:                 */
1918:                public float getPixelUnitToMillimeter() {
1919:                    return ctx.getUserAgent().getPixelUnitToMillimeter();
1920:                }
1921:
1922:                /**
1923:                 * Returns the size of a px CSS unit in millimeters.
1924:                 * This will be removed after next release.
1925:                 * @see #getPixelUnitToMillimeter()
1926:                 */
1927:                public float getPixelToMM() {
1928:                    return getPixelUnitToMillimeter();
1929:
1930:                }
1931:
1932:                /**
1933:                 * Returns the tight bounding box in current user space (i.e.,
1934:                 * after application of the transform attribute, if any) on the
1935:                 * geometry of all contained graphics elements, exclusive of
1936:                 * stroke-width and filter effects).
1937:                 */
1938:                public Rectangle2D getBBox() {
1939:                    //text children does not support getBBox
1940:                    //return textBridge.getBBox();
1941:                    return null;
1942:                }
1943:
1944:                /**
1945:                 * Returns the transformation matrix from current user units
1946:                 * (i.e., after application of the transform attribute, if any) to
1947:                 * the viewport coordinate system for the nearestViewportElement.
1948:                 */
1949:                public AffineTransform getCTM() {
1950:                    // text children does not support transform attribute
1951:                    //return textBridge.getCTM();
1952:                    return null;
1953:                }
1954:
1955:                /**
1956:                 * Returns the global transformation matrix from the current
1957:                 * element to the root.
1958:                 */
1959:                public AffineTransform getGlobalTransform() {
1960:                    //return node.getGlobalTransform();
1961:                    return null;
1962:                }
1963:
1964:                /**
1965:                 * Returns the transformation matrix from the userspace of
1966:                 * the root element to the screen.
1967:                 */
1968:                public AffineTransform getScreenTransform() {
1969:                    //return node.getScreenTransform();
1970:                    return null;
1971:                }
1972:
1973:                /**
1974:                 * Sets the transformation matrix to be used from the
1975:                 * userspace of the root element to the screen.
1976:                 */
1977:                public void setScreenTransform(AffineTransform at) {
1978:                    //return node.setScreenTransform(at);
1979:                    return;
1980:                }
1981:
1982:                /**
1983:                 * Returns the width of the viewport which directly contains the
1984:                 * given element.
1985:                 */
1986:                public float getViewportWidth() {
1987:                    return ctx.getBlockWidth(e);
1988:                }
1989:
1990:                /**
1991:                 * Returns the height of the viewport which directly contains the
1992:                 * given element.
1993:                 */
1994:                public float getViewportHeight() {
1995:                    return ctx.getBlockHeight(e);
1996:                }
1997:
1998:                /**
1999:                 * Returns the font-size on the associated element.
2000:                 */
2001:                public float getFontSize() {
2002:                    return CSSUtilities.getComputedStyle(e,
2003:                            SVGCSSEngine.FONT_SIZE_INDEX).getFloatValue();
2004:                }
2005:            }
2006:
2007:            /**
2008:             * Implementation for the <code>BridgeUpdateHandler</code>
2009:             * for the child elements of &lt;text&gt;.
2010:             * This implementation relies on the parent bridge
2011:             * which contains the <code>TextNode</code>
2012:             * representing the node this context supervised.
2013:             * All operations are done by the parent bridge
2014:             * <code>SVGTextElementBridge</code> which can determine
2015:             * the impact of a change of one of its children for the others.
2016:             */
2017:            protected abstract class AbstractTextChildBridgeUpdateHandler
2018:                    extends AbstractTextChildSVGContext implements 
2019:                    BridgeUpdateHandler {
2020:
2021:                /**
2022:                 * Initialize the BridgeUpdateHandler implementation.
2023:                 */
2024:                protected AbstractTextChildBridgeUpdateHandler(
2025:                        BridgeContext ctx, SVGTextElementBridge parent,
2026:                        Element e) {
2027:
2028:                    super (ctx, parent, e);
2029:                }
2030:
2031:                /**
2032:                 * Invoked when an MutationEvent of type 'DOMAttrModified' is fired.
2033:                 */
2034:                public void handleDOMAttrModifiedEvent(MutationEvent evt) {
2035:                    //nothing to do
2036:                }
2037:
2038:                /**
2039:                 * Invoked when an MutationEvent of type 'DOMNodeInserted' is fired.
2040:                 */
2041:                public void handleDOMNodeInsertedEvent(MutationEvent evt) {
2042:                    textBridge.handleDOMNodeInsertedEvent(evt);
2043:                }
2044:
2045:                /**
2046:                 * Invoked when an MutationEvent of type 'DOMNodeRemoved' is fired.
2047:                 */
2048:                public void handleDOMNodeRemovedEvent(MutationEvent evt) {
2049:                    //nothing to do
2050:                    dispose();
2051:                }
2052:
2053:                /**
2054:                 * Invoked when an MutationEvent of type 'DOMCharacterDataModified'
2055:                 * is fired.
2056:                 */
2057:                public void handleDOMCharacterDataModified(MutationEvent evt) {
2058:                    textBridge.handleDOMCharacterDataModified(evt);
2059:                }
2060:
2061:                /**
2062:                 * Invoked when an CSSEngineEvent is fired.
2063:                 */
2064:                public void handleCSSEngineEvent(CSSEngineEvent evt) {
2065:                    textBridge.handleCSSEngineEvent(evt);
2066:                }
2067:
2068:                /**
2069:                 * Invoked when the animated value of an animatable attribute has
2070:                 * changed.
2071:                 */
2072:                public void handleAnimatedAttributeChanged(
2073:                        AnimatedLiveAttributeValue alav) {
2074:                }
2075:
2076:                /**
2077:                 * Invoked when an 'other' animation value has changed.
2078:                 */
2079:                public void handleOtherAnimationChanged(String type) {
2080:                }
2081:
2082:                /**
2083:                 * Disposes this BridgeUpdateHandler and releases all resources.
2084:                 */
2085:                public void dispose() {
2086:                    ((SVGOMElement) e).setSVGContext(null);
2087:                    elemTPI.remove(e);
2088:                }
2089:            }
2090:
2091:            protected class AbstractTextChildTextContent extends
2092:                    AbstractTextChildBridgeUpdateHandler implements 
2093:                    SVGTextContent {
2094:
2095:                /**
2096:                 * Initialize the AbstractTextChildBridgeUpdateHandler implementation.
2097:                 */
2098:                protected AbstractTextChildTextContent(BridgeContext ctx,
2099:                        SVGTextElementBridge parent, Element e) {
2100:
2101:                    super (ctx, parent, e);
2102:                }
2103:
2104:                //Implementation of TextContent
2105:
2106:                public int getNumberOfChars() {
2107:                    return textBridge.getNumberOfChars(e);
2108:                }
2109:
2110:                public Rectangle2D getExtentOfChar(int charnum) {
2111:                    return textBridge.getExtentOfChar(e, charnum);
2112:                }
2113:
2114:                public Point2D getStartPositionOfChar(int charnum) {
2115:                    return textBridge.getStartPositionOfChar(e, charnum);
2116:                }
2117:
2118:                public Point2D getEndPositionOfChar(int charnum) {
2119:                    return textBridge.getEndPositionOfChar(e, charnum);
2120:                }
2121:
2122:                public void selectSubString(int charnum, int nchars) {
2123:                    textBridge.selectSubString(e, charnum, nchars);
2124:                }
2125:
2126:                public float getRotationOfChar(int charnum) {
2127:                    return textBridge.getRotationOfChar(e, charnum);
2128:                }
2129:
2130:                public float getComputedTextLength() {
2131:                    return textBridge.getComputedTextLength(e);
2132:                }
2133:
2134:                public float getSubStringLength(int charnum, int nchars) {
2135:                    return textBridge.getSubStringLength(e, charnum, nchars);
2136:                }
2137:
2138:                public int getCharNumAtPosition(float x, float y) {
2139:                    return textBridge.getCharNumAtPosition(e, x, y);
2140:                }
2141:            }
2142:
2143:            /**
2144:             * BridgeUpdateHandle for &lt;tref&gt; element.
2145:             */
2146:            protected class TRefBridge extends AbstractTextChildTextContent {
2147:
2148:                protected TRefBridge(BridgeContext ctx,
2149:                        SVGTextElementBridge parent, Element e) {
2150:                    super (ctx, parent, e);
2151:                }
2152:
2153:                /**
2154:                 * Invoked when the animated value of an animatable attribute has
2155:                 * changed on a 'tref' element.
2156:                 */
2157:                public void handleAnimatedAttributeChanged(
2158:                        AnimatedLiveAttributeValue alav) {
2159:                    if (alav.getNamespaceURI() == null) {
2160:                        String ln = alav.getLocalName();
2161:                        if (ln.equals(SVG_X_ATTRIBUTE)
2162:                                || ln.equals(SVG_Y_ATTRIBUTE)
2163:                                || ln.equals(SVG_DX_ATTRIBUTE)
2164:                                || ln.equals(SVG_DY_ATTRIBUTE)
2165:                                || ln.equals(SVG_ROTATE_ATTRIBUTE)
2166:                                || ln.equals(SVG_TEXT_LENGTH_ATTRIBUTE)
2167:                                || ln.equals(SVG_LENGTH_ADJUST_ATTRIBUTE)) {
2168:                            // Recompute the layout of the text node.
2169:                            textBridge.computeLaidoutText(ctx, textBridge.e,
2170:                                    textBridge.getTextNode());
2171:                            return;
2172:                        }
2173:                    }
2174:                    super .handleAnimatedAttributeChanged(alav);
2175:                }
2176:            }
2177:
2178:            /**
2179:             * BridgeUpdateHandle for &lt;textPath&gt; element.
2180:             */
2181:            protected class TextPathBridge extends AbstractTextChildTextContent {
2182:
2183:                protected TextPathBridge(BridgeContext ctx,
2184:                        SVGTextElementBridge parent, Element e) {
2185:                    super (ctx, parent, e);
2186:                }
2187:            }
2188:
2189:            /**
2190:             * BridgeUpdateHandle for &lt;tspan&gt; element.
2191:             */
2192:            protected class TspanBridge extends AbstractTextChildTextContent {
2193:
2194:                protected TspanBridge(BridgeContext ctx,
2195:                        SVGTextElementBridge parent, Element e) {
2196:                    super (ctx, parent, e);
2197:                }
2198:
2199:                /**
2200:                 * Invoked when the animated value of an animatable attribute has
2201:                 * changed on a 'tspan' element.
2202:                 */
2203:                public void handleAnimatedAttributeChanged(
2204:                        AnimatedLiveAttributeValue alav) {
2205:                    if (alav.getNamespaceURI() == null) {
2206:                        String ln = alav.getLocalName();
2207:                        if (ln.equals(SVG_X_ATTRIBUTE)
2208:                                || ln.equals(SVG_Y_ATTRIBUTE)
2209:                                || ln.equals(SVG_DX_ATTRIBUTE)
2210:                                || ln.equals(SVG_DY_ATTRIBUTE)
2211:                                || ln.equals(SVG_ROTATE_ATTRIBUTE)
2212:                                || ln.equals(SVG_TEXT_LENGTH_ATTRIBUTE)
2213:                                || ln.equals(SVG_LENGTH_ADJUST_ATTRIBUTE)) {
2214:                            // Recompute the layout of the text node.
2215:                            textBridge.computeLaidoutText(ctx, textBridge.e,
2216:                                    textBridge.getTextNode());
2217:                            return;
2218:                        }
2219:                    }
2220:                    super .handleAnimatedAttributeChanged(alav);
2221:                }
2222:            }
2223:
2224:            //Implementation of TextContent
2225:            public int getNumberOfChars() {
2226:                return getNumberOfChars(e);
2227:            }
2228:
2229:            public Rectangle2D getExtentOfChar(int charnum) {
2230:                return getExtentOfChar(e, charnum);
2231:            }
2232:
2233:            public Point2D getStartPositionOfChar(int charnum) {
2234:                return getStartPositionOfChar(e, charnum);
2235:            }
2236:
2237:            public Point2D getEndPositionOfChar(int charnum) {
2238:                return getEndPositionOfChar(e, charnum);
2239:            }
2240:
2241:            public void selectSubString(int charnum, int nchars) {
2242:                selectSubString(e, charnum, nchars);
2243:            }
2244:
2245:            public float getRotationOfChar(int charnum) {
2246:                return getRotationOfChar(e, charnum);
2247:            }
2248:
2249:            public float getComputedTextLength() {
2250:                return getComputedTextLength(e);
2251:            }
2252:
2253:            public float getSubStringLength(int charnum, int nchars) {
2254:                return getSubStringLength(e, charnum, nchars);
2255:            }
2256:
2257:            public int getCharNumAtPosition(float x, float y) {
2258:                return getCharNumAtPosition(e, x, y);
2259:            }
2260:
2261:            /**
2262:             * Implementation of {@link
2263:             * org.w3c.dom.svg.SVGTextContentElement#getNumberOfChars()}.
2264:             */
2265:            protected int getNumberOfChars(Element element) {
2266:
2267:                AttributedCharacterIterator aci;
2268:                aci = getTextNode().getAttributedCharacterIterator();
2269:                if (aci == null)
2270:                    return 0;
2271:
2272:                //get the index in the aci for the first character
2273:                //of the element
2274:                int firstChar = getElementStartIndex(element);
2275:
2276:                if (firstChar == -1)
2277:                    return 0; // Element not part of aci (no chars in elem usually)
2278:
2279:                int lastChar = getElementEndIndex(element);
2280:
2281:                return (lastChar - firstChar + 1);
2282:            }
2283:
2284:            /**
2285:             * Implementation of {@link
2286:             * org.w3c.dom.svg.SVGTextContentElement#getExtentOfChar(int charnum)}.
2287:             */
2288:            protected Rectangle2D getExtentOfChar(Element element, int charnum) {
2289:                TextNode textNode = getTextNode();
2290:
2291:                AttributedCharacterIterator aci;
2292:                aci = textNode.getAttributedCharacterIterator();
2293:                if (aci == null)
2294:                    return null;
2295:
2296:                int firstChar = getElementStartIndex(element);
2297:
2298:                if (firstChar == -1)
2299:                    return null;
2300:
2301:                //retrieve the text run for the text node
2302:                List list = getTextRuns(textNode);
2303:
2304:                //find the character 'charnum' in the text run
2305:                CharacterInformation info;
2306:                info = getCharacterInformation(list, firstChar, charnum, aci);
2307:
2308:                if (info == null)
2309:                    return null;
2310:
2311:                //retrieve the glyphvector containing the glyph
2312:                //for 'charnum'
2313:                GVTGlyphVector it = info.layout.getGlyphVector();
2314:
2315:                Shape b = null;
2316:
2317:                if (info.glyphIndexStart == info.glyphIndexEnd) {
2318:                    if (it.isGlyphVisible(info.glyphIndexStart)) {
2319:                        b = it.getGlyphCellBounds(info.glyphIndexStart);
2320:                    }
2321:                } else {
2322:                    GeneralPath path = null;
2323:                    for (int k = info.glyphIndexStart; k <= info.glyphIndexEnd; k++) {
2324:                        if (it.isGlyphVisible(k)) {
2325:                            Rectangle2D gb = it.getGlyphCellBounds(k);
2326:                            if (path == null) {
2327:                                path = new GeneralPath(gb);
2328:                            } else {
2329:                                path.append(gb, false);
2330:                            }
2331:                        }
2332:                    }
2333:                    b = path;
2334:                }
2335:
2336:                if (b == null) {
2337:                    return null;
2338:                }
2339:
2340:                //return the bounding box of the outline
2341:                return b.getBounds2D();
2342:            }
2343:
2344:            /**
2345:             * Implementation of {@link
2346:             * org.w3c.dom.svg.SVGTextContentElement#getStartPositionOfChar(int charnum)}.
2347:             */
2348:            protected Point2D getStartPositionOfChar(Element element,
2349:                    int charnum) {
2350:                TextNode textNode = getTextNode();
2351:
2352:                AttributedCharacterIterator aci;
2353:                aci = textNode.getAttributedCharacterIterator();
2354:                if (aci == null)
2355:                    return null;
2356:
2357:                int firstChar = getElementStartIndex(element);
2358:                if (firstChar == -1)
2359:                    return null;
2360:
2361:                //retrieve the text run for the text node
2362:                List list = getTextRuns(textNode);
2363:
2364:                //find the character 'charnum' in the text run
2365:                CharacterInformation info;
2366:                info = getCharacterInformation(list, firstChar, charnum, aci);
2367:
2368:                if (info == null)
2369:                    return null;
2370:
2371:                return getStartPoint(info);
2372:            }
2373:
2374:            protected Point2D getStartPoint(CharacterInformation info) {
2375:
2376:                GVTGlyphVector it = info.layout.getGlyphVector();
2377:                if (!it.isGlyphVisible(info.glyphIndexStart))
2378:                    return null;
2379:
2380:                Point2D b = it.getGlyphPosition(info.glyphIndexStart);
2381:
2382:                AffineTransform glyphTransform;
2383:                glyphTransform = it.getGlyphTransform(info.glyphIndexStart);
2384:
2385:                //glyph are defined starting at position (0,0)
2386:                Point2D.Float result = new Point2D.Float(0, 0);
2387:                if (glyphTransform != null)
2388:                    //apply the glyph transformation to the start point
2389:                    glyphTransform.transform(result, result);
2390:
2391:                result.x += b.getX();
2392:                result.y += b.getY();
2393:                return result;
2394:            }
2395:
2396:            /**
2397:             * Implementation of {@link
2398:             * org.w3c.dom.svg.SVGTextContentElement#getEndPositionOfChar(int charnum)}.
2399:             */
2400:            protected Point2D getEndPositionOfChar(Element element, int charnum) {
2401:                TextNode textNode = getTextNode();
2402:
2403:                AttributedCharacterIterator aci;
2404:                aci = textNode.getAttributedCharacterIterator();
2405:                if (aci == null)
2406:                    return null;
2407:
2408:                int firstChar = getElementStartIndex(element);
2409:                if (firstChar == -1)
2410:                    return null;
2411:
2412:                //retrieve the text run for the text node
2413:                List list = getTextRuns(textNode);
2414:
2415:                //find the glyph information for the character 'charnum'
2416:                CharacterInformation info;
2417:                info = getCharacterInformation(list, firstChar, charnum, aci);
2418:
2419:                if (info == null)
2420:                    return null;
2421:                return getEndPoint(info);
2422:            }
2423:
2424:            protected Point2D getEndPoint(CharacterInformation info) {
2425:
2426:                GVTGlyphVector it = info.layout.getGlyphVector();
2427:                if (!it.isGlyphVisible(info.glyphIndexEnd))
2428:                    return null;
2429:
2430:                Point2D b = it.getGlyphPosition(info.glyphIndexEnd);
2431:
2432:                AffineTransform glyphTransform;
2433:                glyphTransform = it.getGlyphTransform(info.glyphIndexEnd);
2434:
2435:                GVTGlyphMetrics metrics = it
2436:                        .getGlyphMetrics(info.glyphIndexEnd);
2437:
2438:                Point2D.Float result = new Point2D.Float(metrics
2439:                        .getHorizontalAdvance(), 0);
2440:
2441:                if (glyphTransform != null)
2442:                    glyphTransform.transform(result, result);
2443:
2444:                result.x += b.getX();
2445:                result.y += b.getY();
2446:                return result;
2447:            }
2448:
2449:            /**
2450:             * Implementation of {@link
2451:             * org.w3c.dom.svg.SVGTextContentElement#getRotationOfChar(int charnum)}.
2452:             */
2453:            protected float getRotationOfChar(Element element, int charnum) {
2454:                TextNode textNode = getTextNode();
2455:
2456:                AttributedCharacterIterator aci;
2457:                aci = textNode.getAttributedCharacterIterator();
2458:                if (aci == null)
2459:                    return 0;
2460:
2461:                //first the first character for the element
2462:                int firstChar = getElementStartIndex(element);
2463:                if (firstChar == -1)
2464:                    return 0;
2465:
2466:                //retrieve the text run for the text node
2467:                List list = getTextRuns(textNode);
2468:
2469:                //find the glyph information for the character 'charnum'
2470:                CharacterInformation info;
2471:                info = getCharacterInformation(list, firstChar, charnum, aci);
2472:
2473:                double angle = 0.0;
2474:                int nbGlyphs = 0;
2475:
2476:                if (info != null) {
2477:                    GVTGlyphVector it = info.layout.getGlyphVector();
2478:
2479:                    for (int k = info.glyphIndexStart; k <= info.glyphIndexEnd; k++) {
2480:                        if (!it.isGlyphVisible(k))
2481:                            continue;
2482:
2483:                        nbGlyphs++;
2484:
2485:                        //the glyph transform contains only a scale and a rotate.
2486:                        AffineTransform glyphTransform = it
2487:                                .getGlyphTransform(k);
2488:                        if (glyphTransform == null)
2489:                            continue;
2490:
2491:                        double glyphAngle = 0.0;
2492:                        double cosTheta = glyphTransform.getScaleX();
2493:                        double sinTheta = glyphTransform.getShearX();
2494:
2495:                        //extract the angle
2496:                        if (cosTheta == 0.0) {
2497:                            if (sinTheta > 0)
2498:                                glyphAngle = Math.PI;
2499:                            else
2500:                                glyphAngle = -Math.PI;
2501:                        } else {
2502:                            glyphAngle = Math.atan(sinTheta / cosTheta); // todo is this safe??
2503:                            if (cosTheta < 0)
2504:                                glyphAngle += Math.PI;
2505:                        }
2506:                        //get a degrees value for the angle
2507:                        //SVG angle are clock wise java anticlockwise
2508:
2509:                        glyphAngle = (Math.toDegrees(-glyphAngle)) % 360.0;
2510:
2511:                        //remove the orientation from the value
2512:                        angle += glyphAngle
2513:                                - info.getComputedOrientationAngle();
2514:                    }
2515:                }
2516:                if (nbGlyphs == 0)
2517:                    return 0;
2518:                return (float) (angle / nbGlyphs);
2519:            }
2520:
2521:            /**
2522:             * Implementation of {@link
2523:             * org.w3c.dom.svg.SVGTextContentElement#getComputedTextLength()}.
2524:             */
2525:            protected float getComputedTextLength(Element e) {
2526:                return getSubStringLength(e, 0, getNumberOfChars(e));
2527:            }
2528:
2529:            /**
2530:             * Implementation of {@link
2531:             * org.w3c.dom.svg.SVGTextContentElement#getSubStringLength(int charnum,int nchars)}.
2532:             */
2533:            protected float getSubStringLength(Element element, int charnum,
2534:                    int nchars) {
2535:                if (nchars == 0) {
2536:                    return 0;
2537:                }
2538:
2539:                float length = 0;
2540:
2541:                TextNode textNode = getTextNode();
2542:
2543:                AttributedCharacterIterator aci;
2544:                aci = textNode.getAttributedCharacterIterator();
2545:                if (aci == null)
2546:                    return -1;
2547:
2548:                int firstChar = getElementStartIndex(element);
2549:
2550:                if (firstChar == -1)
2551:                    return -1;
2552:
2553:                List list = getTextRuns(textNode);
2554:
2555:                CharacterInformation currentInfo;
2556:                currentInfo = getCharacterInformation(list, firstChar, charnum,
2557:                        aci);
2558:                CharacterInformation lastCharacterInRunInfo = null;
2559:                int chIndex = currentInfo.characterIndex + 1;
2560:                GVTGlyphVector vector = currentInfo.layout.getGlyphVector();
2561:                float[] advs = currentInfo.layout.getGlyphAdvances();
2562:                boolean[] glyphTrack = new boolean[advs.length];
2563:                for (int k = charnum + 1; k < charnum + nchars; k++) {
2564:                    if (currentInfo.layout.isOnATextPath()) {
2565:                        for (int gi = currentInfo.glyphIndexStart; gi <= currentInfo.glyphIndexEnd; gi++) {
2566:                            if ((vector.isGlyphVisible(gi)) && !glyphTrack[gi])
2567:                                length += advs[gi + 1] - advs[gi];
2568:                            glyphTrack[gi] = true;
2569:                        }
2570:                        CharacterInformation newInfo;
2571:                        newInfo = getCharacterInformation(list, firstChar, k,
2572:                                aci);
2573:                        if (newInfo.layout != currentInfo.layout) {
2574:                            vector = newInfo.layout.getGlyphVector();
2575:                            advs = newInfo.layout.getGlyphAdvances();
2576:                            glyphTrack = new boolean[advs.length];
2577:                            chIndex = currentInfo.characterIndex + 1;
2578:                        }
2579:                        currentInfo = newInfo;
2580:                    } else {
2581:                        //reach the next run
2582:                        if (currentInfo.layout.hasCharacterIndex(chIndex)) {
2583:                            chIndex++;
2584:                            continue;
2585:                        }
2586:
2587:                        lastCharacterInRunInfo = getCharacterInformation(list,
2588:                                firstChar, k - 1, aci);
2589:
2590:                        //if the text run change compute the distance between the
2591:                        //first character of the run and the last
2592:                        length += distanceFirstLastCharacterInRun(currentInfo,
2593:                                lastCharacterInRunInfo);
2594:
2595:                        currentInfo = getCharacterInformation(list, firstChar,
2596:                                k, aci);
2597:                        chIndex = currentInfo.characterIndex + 1;
2598:                        vector = currentInfo.layout.getGlyphVector();
2599:                        advs = currentInfo.layout.getGlyphAdvances();
2600:                        glyphTrack = new boolean[advs.length];
2601:                        lastCharacterInRunInfo = null;
2602:                    }
2603:                }
2604:
2605:                if (currentInfo.layout.isOnATextPath()) {
2606:                    for (int gi = currentInfo.glyphIndexStart; gi <= currentInfo.glyphIndexEnd; gi++) {
2607:                        if ((vector.isGlyphVisible(gi)) && !glyphTrack[gi])
2608:                            length += advs[gi + 1] - advs[gi];
2609:                        glyphTrack[gi] = true;
2610:                    }
2611:                } else {
2612:                    if (lastCharacterInRunInfo == null) {
2613:                        lastCharacterInRunInfo = getCharacterInformation(list,
2614:                                firstChar, charnum + nchars - 1, aci);
2615:                    }
2616:                    //add the length between the end position of the last character
2617:                    //and the first character in the run
2618:                    length += distanceFirstLastCharacterInRun(currentInfo,
2619:                            lastCharacterInRunInfo);
2620:                }
2621:
2622:                return length;
2623:            }
2624:
2625:            protected float distanceFirstLastCharacterInRun(
2626:                    CharacterInformation first, CharacterInformation last) {
2627:
2628:                float[] advs = first.layout.getGlyphAdvances();
2629:
2630:                int firstStart = first.glyphIndexStart;
2631:                int firstEnd = first.glyphIndexEnd;
2632:                int lastStart = last.glyphIndexStart;
2633:                int lastEnd = last.glyphIndexEnd;
2634:
2635:                int start = (firstStart < lastStart) ? firstStart : lastStart;
2636:                int end = (firstEnd < lastEnd) ? lastEnd : firstEnd;
2637:                return advs[end + 1] - advs[start];
2638:            }
2639:
2640:            protected float distanceBetweenRun(CharacterInformation last,
2641:                    CharacterInformation first) {
2642:
2643:                float distance;
2644:                Point2D startPoint;
2645:                Point2D endPoint;
2646:                CharacterInformation info = new CharacterInformation();
2647:
2648:                //determine where the last run stops
2649:
2650:                info.layout = last.layout;
2651:                info.glyphIndexEnd = last.layout.getGlyphCount() - 1;
2652:
2653:                startPoint = getEndPoint(info);
2654:
2655:                //determine where the next run starts
2656:                info.layout = first.layout;
2657:                info.glyphIndexStart = 0;
2658:
2659:                endPoint = getStartPoint(info);
2660:
2661:                if (first.isVertical()) {
2662:                    distance = (float) (endPoint.getY() - startPoint.getY());
2663:                } else {
2664:                    distance = (float) (endPoint.getX() - startPoint.getX());
2665:                }
2666:
2667:                return distance;
2668:            }
2669:
2670:            /**
2671:             * Select an ensemble of characters for that element.
2672:             *
2673:             * TODO : report the selection to the selection
2674:             *  manager in JSVGComponent.
2675:             */
2676:            protected void selectSubString(Element element, int charnum,
2677:                    int nchars) {
2678:                TextNode textNode = getTextNode();
2679:
2680:                AttributedCharacterIterator aci;
2681:                aci = textNode.getAttributedCharacterIterator();
2682:                if (aci == null)
2683:                    return;
2684:
2685:                int firstChar = getElementStartIndex(element);
2686:
2687:                if (firstChar == -1)
2688:                    return;
2689:
2690:                List list = getTextRuns(textNode);
2691:
2692:                int lastChar = getElementEndIndex(element);
2693:
2694:                CharacterInformation firstInfo, lastInfo;
2695:                firstInfo = getCharacterInformation(list, firstChar, charnum,
2696:                        aci);
2697:                lastInfo = getCharacterInformation(list, firstChar, charnum
2698:                        + nchars - 1, aci);
2699:
2700:                Mark firstMark, lastMark;
2701:                firstMark = textNode.getMarkerForChar(firstInfo.characterIndex,
2702:                        true);
2703:
2704:                if (lastInfo != null && lastInfo.characterIndex <= lastChar) {
2705:                    lastMark = textNode.getMarkerForChar(
2706:                            lastInfo.characterIndex, false);
2707:                } else {
2708:                    lastMark = textNode.getMarkerForChar(lastChar, false);
2709:                }
2710:
2711:                ctx.getUserAgent().setTextSelection(firstMark, lastMark);
2712:            }
2713:
2714:            protected int getCharNumAtPosition(Element e, float x, float y) {
2715:                TextNode textNode = getTextNode();
2716:
2717:                AttributedCharacterIterator aci;
2718:                aci = textNode.getAttributedCharacterIterator();
2719:                if (aci == null)
2720:                    return -1;
2721:
2722:                //check if there is an hit
2723:                List list = getTextRuns(textNode);
2724:
2725:                //going backward in the list to catch the last character
2726:                // displayed at that position
2727:                TextHit hit = null;
2728:
2729:                for (int i = list.size() - 1; i >= 0 && hit == null; i--) {
2730:                    StrokingTextPainter.TextRun textRun;
2731:                    textRun = (StrokingTextPainter.TextRun) list.get(i);
2732:                    hit = textRun.getLayout().hitTestChar(x, y);
2733:                }
2734:
2735:                if (hit == null)
2736:                    return -1;
2737:
2738:                //found an hit, check if it belong to the element
2739:                int first = getElementStartIndex(e);
2740:                int last = getElementEndIndex(e);
2741:
2742:                int hitIndex = hit.getCharIndex();
2743:
2744:                if (hitIndex >= first && hitIndex <= last)
2745:                    return hitIndex - first;
2746:
2747:                return -1;
2748:            }
2749:
2750:            /**
2751:             * Retrieve the list of layout for the
2752:             * text node.
2753:             */
2754:            protected List getTextRuns(TextNode node) {
2755:                //System.out.println(node.getTextRuns());
2756:                if (node.getTextRuns() == null) {
2757:                    //TODO : need to work out a solution
2758:                    //to compute the text runs
2759:                    node.getPrimitiveBounds();
2760:                }
2761:                //System.out.println(node.getTextRuns());
2762:                return node.getTextRuns();
2763:            }
2764:
2765:            /**
2766:             * Retrieve the information about a character
2767:             * of en element. The element first character in
2768:             * the ACI is 'firstChar' and the character
2769:             * look for is the charnum th character in the
2770:             * element
2771:             *
2772:             * @param list list of the layouts
2773:             * @param startIndex index in the ACI of the first
2774:             *   character for the element
2775:             * @param charnum index of the character (among the
2776:             *   characters of the element) looked for.
2777:             *
2778:             * @return information about the glyph representing the
2779:             *  character
2780:             */
2781:            protected CharacterInformation getCharacterInformation(List list,
2782:                    int startIndex, int charnum, AttributedCharacterIterator aci) {
2783:                CharacterInformation info = new CharacterInformation();
2784:                info.characterIndex = startIndex + charnum;
2785:
2786:                for (int i = 0; i < list.size(); i++) {
2787:                    StrokingTextPainter.TextRun run;
2788:                    run = (StrokingTextPainter.TextRun) list.get(i);
2789:
2790:                    if (!run.getLayout().hasCharacterIndex(info.characterIndex))
2791:                        continue;
2792:
2793:                    info.layout = run.getLayout();
2794:
2795:                    aci.setIndex(info.characterIndex);
2796:
2797:                    //check is it is a altGlyph
2798:                    if (aci.getAttribute(ALT_GLYPH_HANDLER) != null) {
2799:                        info.glyphIndexStart = 0;
2800:                        info.glyphIndexEnd = info.layout.getGlyphCount() - 1;
2801:                    } else {
2802:                        info.glyphIndexStart = info.layout
2803:                                .getGlyphIndex(info.characterIndex);
2804:
2805:                        //special case when the glyph does not have a unicode
2806:                        //associated to it, it will return -1
2807:                        if (info.glyphIndexStart == -1) {
2808:                            info.glyphIndexStart = 0;
2809:                            info.glyphIndexEnd = info.layout.getGlyphCount() - 1;
2810:                        } else {
2811:                            info.glyphIndexEnd = info.glyphIndexStart;
2812:                        }
2813:                    }
2814:                    return info;
2815:                }
2816:                return null;
2817:            }
2818:
2819:            /**
2820:             * Helper class to collect information about one Glyph
2821:             * in the GlyphVector
2822:             */
2823:            protected static class CharacterInformation {
2824:                ///layout associated to the Glyph
2825:                TextSpanLayout layout;
2826:                ///GlyphIndex in the vector
2827:                int glyphIndexStart;
2828:
2829:                int glyphIndexEnd;
2830:
2831:                ///Character index in the ACI.
2832:                int characterIndex;
2833:
2834:                /// Indicates is the glyph is vertical
2835:                public boolean isVertical() {
2836:                    return layout.isVertical();
2837:                }
2838:
2839:                /// Retrieve the orientation angle for the Glyph
2840:                public double getComputedOrientationAngle() {
2841:                    return layout.getComputedOrientationAngle(characterIndex);
2842:                }
2843:            }
2844:
2845:            public Set getTextIntersectionSet(AffineTransform at,
2846:                    Rectangle2D rect) {
2847:                Set elems = new HashSet();
2848:
2849:                TextNode tn = getTextNode();
2850:
2851:                List list = tn.getTextRuns();
2852:                if (list == null)
2853:                    return elems;
2854:
2855:                for (int i = 0; i < list.size(); i++) {
2856:                    StrokingTextPainter.TextRun run;
2857:                    run = (StrokingTextPainter.TextRun) list.get(i);
2858:                    TextSpanLayout layout = run.getLayout();
2859:                    AttributedCharacterIterator aci = run.getACI();
2860:                    aci.first();
2861:                    SoftReference sr;
2862:                    sr = (SoftReference) aci.getAttribute(TEXT_COMPOUND_ID);
2863:                    Element elem = (Element) sr.get();
2864:
2865:                    if (elem == null)
2866:                        continue;
2867:                    if (elems.contains(elem))
2868:                        continue;
2869:                    if (!isTextSensitive(elem))
2870:                        continue;
2871:
2872:                    Rectangle2D glBounds = layout.getBounds2D();
2873:                    if (glBounds != null)
2874:                        glBounds = at.createTransformedShape(glBounds)
2875:                                .getBounds2D();
2876:
2877:                    if (!rect.intersects(glBounds))
2878:                        continue;
2879:
2880:                    GVTGlyphVector gv = layout.getGlyphVector();
2881:                    for (int g = 0; g < gv.getNumGlyphs(); g++) {
2882:                        Shape gBounds = gv.getGlyphLogicalBounds(g);
2883:                        if (gBounds != null)
2884:                            gBounds = at.createTransformedShape(gBounds)
2885:                                    .getBounds2D();
2886:
2887:                        if (gBounds.intersects(rect)) {
2888:                            elems.add(elem);
2889:                            break;
2890:                        }
2891:                    }
2892:                }
2893:                return elems;
2894:            }
2895:
2896:            public Set getTextEnclosureSet(AffineTransform at, Rectangle2D rect) {
2897:                TextNode tn = getTextNode();
2898:
2899:                Set elems = new HashSet();
2900:                List list = tn.getTextRuns();
2901:                if (list == null)
2902:                    return elems;
2903:
2904:                Set reject = new HashSet();
2905:                for (int i = 0; i < list.size(); i++) {
2906:                    StrokingTextPainter.TextRun run;
2907:                    run = (StrokingTextPainter.TextRun) list.get(i);
2908:                    TextSpanLayout layout = run.getLayout();
2909:                    AttributedCharacterIterator aci = run.getACI();
2910:                    aci.first();
2911:                    SoftReference sr;
2912:                    sr = (SoftReference) aci.getAttribute(TEXT_COMPOUND_ID);
2913:                    Element elem = (Element) sr.get();
2914:
2915:                    if (elem == null)
2916:                        continue;
2917:                    if (reject.contains(elem))
2918:                        continue;
2919:                    if (!isTextSensitive(elem)) {
2920:                        reject.add(elem);
2921:                        continue;
2922:                    }
2923:
2924:                    Rectangle2D glBounds = layout.getBounds2D();
2925:                    if (glBounds == null) {
2926:                        continue;
2927:                    }
2928:
2929:                    glBounds = at.createTransformedShape(glBounds)
2930:                            .getBounds2D();
2931:
2932:                    if (rect.contains(glBounds)) {
2933:                        elems.add(elem);
2934:                    } else {
2935:                        reject.add(elem);
2936:                        elems.remove(elem);
2937:                    }
2938:                }
2939:
2940:                return elems;
2941:            }
2942:
2943:            public static boolean getTextIntersection(BridgeContext ctx,
2944:                    Element elem, AffineTransform ati, Rectangle2D rect,
2945:                    boolean checkSensitivity) {
2946:                SVGContext svgCtx = null;
2947:                if (elem instanceof  SVGOMElement)
2948:                    svgCtx = ((SVGOMElement) elem).getSVGContext();
2949:                if (svgCtx == null)
2950:                    return false;
2951:
2952:                SVGTextElementBridge txtBridge = null;
2953:                if (svgCtx instanceof  SVGTextElementBridge)
2954:                    txtBridge = (SVGTextElementBridge) svgCtx;
2955:                else if (svgCtx instanceof  AbstractTextChildSVGContext) {
2956:                    AbstractTextChildSVGContext childCtx;
2957:                    childCtx = (AbstractTextChildSVGContext) svgCtx;
2958:                    txtBridge = childCtx.getTextBridge();
2959:                }
2960:                if (txtBridge == null)
2961:                    return false;
2962:
2963:                TextNode tn = txtBridge.getTextNode();
2964:                List list = tn.getTextRuns();
2965:                if (list == null)
2966:                    return false;
2967:
2968:                Element txtElem = txtBridge.e;
2969:
2970:                AffineTransform at = tn.getGlobalTransform();
2971:                at.preConcatenate(ati);
2972:
2973:                Rectangle2D tnRect;
2974:                tnRect = tn.getBounds();
2975:                tnRect = at.createTransformedShape(tnRect).getBounds2D();
2976:                if (!rect.intersects(tnRect))
2977:                    return false;
2978:
2979:                for (int i = 0; i < list.size(); i++) {
2980:                    StrokingTextPainter.TextRun run;
2981:                    run = (StrokingTextPainter.TextRun) list.get(i);
2982:                    TextSpanLayout layout = run.getLayout();
2983:                    AttributedCharacterIterator aci = run.getACI();
2984:                    aci.first();
2985:                    SoftReference sr;
2986:                    sr = (SoftReference) aci.getAttribute(TEXT_COMPOUND_ID);
2987:                    Element runElem = (Element) sr.get();
2988:                    if (runElem == null)
2989:                        continue;
2990:
2991:                    // Only consider runElem if it is sensitive.
2992:                    if (checkSensitivity && !isTextSensitive(runElem))
2993:                        continue;
2994:
2995:                    Element p = runElem;
2996:                    while ((p != null) && (p != txtElem) && (p != elem)) {
2997:                        p = (Element) txtBridge.getParentNode(p);
2998:                    }
2999:                    if (p != elem)
3000:                        continue;
3001:
3002:                    // runElem is a child of elem so check it out.
3003:                    Rectangle2D glBounds = layout.getBounds2D();
3004:                    if (glBounds == null)
3005:                        continue;
3006:                    glBounds = at.createTransformedShape(glBounds)
3007:                            .getBounds2D();
3008:                    if (!rect.intersects(glBounds))
3009:                        continue;
3010:
3011:                    GVTGlyphVector gv = layout.getGlyphVector();
3012:                    for (int g = 0; g < gv.getNumGlyphs(); g++) {
3013:                        Shape gBounds = gv.getGlyphLogicalBounds(g);
3014:                        if (gBounds != null)
3015:                            gBounds = at.createTransformedShape(gBounds)
3016:                                    .getBounds2D();
3017:
3018:                        if (gBounds.intersects(rect))
3019:                            return true;
3020:                    }
3021:                }
3022:                return false;
3023:            }
3024:
3025:            public static Rectangle2D getTextBounds(BridgeContext ctx,
3026:                    Element elem, boolean checkSensitivity) {
3027:                SVGContext svgCtx = null;
3028:                if (elem instanceof  SVGOMElement)
3029:                    svgCtx = ((SVGOMElement) elem).getSVGContext();
3030:                if (svgCtx == null)
3031:                    return null;
3032:
3033:                SVGTextElementBridge txtBridge = null;
3034:                if (svgCtx instanceof  SVGTextElementBridge)
3035:                    txtBridge = (SVGTextElementBridge) svgCtx;
3036:                else if (svgCtx instanceof  AbstractTextChildSVGContext) {
3037:                    AbstractTextChildSVGContext childCtx;
3038:                    childCtx = (AbstractTextChildSVGContext) svgCtx;
3039:                    txtBridge = childCtx.getTextBridge();
3040:                }
3041:                if (txtBridge == null)
3042:                    return null;
3043:
3044:                TextNode tn = txtBridge.getTextNode();
3045:                List list = tn.getTextRuns();
3046:                if (list == null)
3047:                    return null;
3048:
3049:                Element txtElem = txtBridge.e;
3050:                Rectangle2D ret = null;
3051:
3052:                for (int i = 0; i < list.size(); i++) {
3053:                    StrokingTextPainter.TextRun run;
3054:                    run = (StrokingTextPainter.TextRun) list.get(i);
3055:                    TextSpanLayout layout = run.getLayout();
3056:                    AttributedCharacterIterator aci = run.getACI();
3057:                    aci.first();
3058:                    SoftReference sr;
3059:                    sr = (SoftReference) aci.getAttribute(TEXT_COMPOUND_ID);
3060:                    Element runElem = (Element) sr.get();
3061:                    if (runElem == null)
3062:                        continue;
3063:
3064:                    // Only consider runElem if it is sensitive.
3065:                    if (checkSensitivity && !isTextSensitive(runElem))
3066:                        continue;
3067:
3068:                    Element p = runElem;
3069:                    while ((p != null) && (p != txtElem) && (p != elem)) {
3070:                        p = (Element) txtBridge.getParentNode(p);
3071:                    }
3072:                    if (p != elem)
3073:                        continue;
3074:
3075:                    // runElem is a child of elem so include it's bounds.
3076:                    Rectangle2D glBounds = layout.getBounds2D();
3077:                    if (glBounds != null) {
3078:                        if (ret == null)
3079:                            ret = (Rectangle2D) glBounds.clone();
3080:                        else
3081:                            ret.add(glBounds);
3082:                    }
3083:                }
3084:                return ret;
3085:            }
3086:
3087:            public static boolean isTextSensitive(Element e) {
3088:                int ptrEvts = CSSUtilities.convertPointerEvents(e);
3089:                switch (ptrEvts) {
3090:                case GraphicsNode.VISIBLE_PAINTED: // fall-through is intended
3091:                case GraphicsNode.VISIBLE_FILL:
3092:                case GraphicsNode.VISIBLE_STROKE:
3093:                case GraphicsNode.VISIBLE:
3094:                    return CSSUtilities.convertVisibility(e);
3095:                case GraphicsNode.PAINTED:
3096:                case GraphicsNode.FILL: // fall-through is intended
3097:                case GraphicsNode.STROKE:
3098:                case GraphicsNode.ALL:
3099:                    return true;
3100:                case GraphicsNode.NONE:
3101:                default:
3102:                    return false;
3103:                }
3104:            }
3105:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.