Very rudimentary support for XHTML in <foreignObject>.


git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/batik/branches/foreignObject@658527 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/build.xml b/build.xml
index 75e6f3a..ca30e42 100644
--- a/build.xml
+++ b/build.xml
@@ -158,6 +158,9 @@
     <available property="jacl.present" classname="tcl.lang.Interp">
       <classpath refid="libs-classpath"/>
     </available>
+    <available property="xhtmlrenderer.present" classname="org.xhtmlrenderer.Version">
+      <classpath refid="libs-classpath"/>
+    </available>
     <available property="jdk14.present" classname="java.lang.CharSequence"/>
     
     <!-- When compiling Batik under GNU Classpath, the Sun codecs are not available. -->
@@ -1017,6 +1020,8 @@
                unless="sun-codecs.present"/>
       <exclude name="${package-prefix}/ext/awt/image/codec/tiff/*"
                unless="sun-codecs.present"/>
+      <exclude name="${package-prefix}/bridge/XHTMLForeignObjectHandler.java"
+               unless="xhtmlrenderer.present"/>
     </javac>
     <property name="compile.done" value="true"/>
   </target>
@@ -1530,6 +1535,7 @@
       <fileset dir="${resources}" excludes="**/.svn/">
         <include name="${package-prefix}/bridge/BrokenLink.svg"/>
         <include name="${package-prefix}/bridge/**/resources/*"/>
+        <include name="META-INF/services/org.apache.batik.bridge.ForeignObjectHandlerFactory"/>
       </fileset>
     </jar>
   </target>
diff --git a/resources/META-INF/services/org.apache.batik.bridge.ForeignObjectHandlerFactory b/resources/META-INF/services/org.apache.batik.bridge.ForeignObjectHandlerFactory
new file mode 100644
index 0000000..6db62f1
--- /dev/null
+++ b/resources/META-INF/services/org.apache.batik.bridge.ForeignObjectHandlerFactory
@@ -0,0 +1,26 @@
+# -----------------------------------------------------------------------------
+#
+#   Licensed to the Apache Software Foundation (ASF) under one or more
+#   contributor license agreements.  See the NOTICE file distributed with
+#   this work for additional information regarding copyright ownership.
+#   The ASF licenses this file to You under the Apache License, Version 2.0
+#   (the "License"); you may not use this file except in compliance with
+#   the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# This file registers factories for handlers of <foreignObject>
+# element content.
+#
+# $Id: org.apache.batik.dom.DomExtension 498556 2007-01-22 08:16:03Z cam $
+# -----------------------------------------------------------------------------
+
+# XHTML content, using the Flying Saucer XHTML renderer
+# (https://xhtmlrenderer.dev.java.net/)
+org.apache.batik.bridge.XHTMLForeignObjectHandlerFactory
diff --git a/sources/org/apache/batik/bridge/BridgeContext.java b/sources/org/apache/batik/bridge/BridgeContext.java
index 655e043..e28bcdc 100644
--- a/sources/org/apache/batik/bridge/BridgeContext.java
+++ b/sources/org/apache/batik/bridge/BridgeContext.java
@@ -193,6 +193,11 @@
     protected InterpreterPool interpreterPool;
 
     /**
+     * The pool of ForeignObjectHandler factories.
+     */
+    protected ForeignObjectHandlerPool foreignObjectHandlerPool;
+
+    /**
      * The document loader used to load/create Document.
      */
     protected DocumentLoader documentLoader;
@@ -277,6 +282,12 @@
     private static InterpreterPool sharedPool = new InterpreterPool();
 
     /**
+     * By default we share a unique instance of ForeignObjectHandlerPool.
+     */
+    private static ForeignObjectHandlerPool sharedFOHPool =
+        new ForeignObjectHandlerPool();
+
+    /**
      * Constructs a new empty bridge context.
      */
     protected BridgeContext() {}
@@ -298,21 +309,48 @@
      */
     public BridgeContext(UserAgent userAgent,
                          DocumentLoader loader) {
-        this(userAgent, sharedPool, loader);
+        this(userAgent, sharedPool, sharedFOHPool, loader);
     }
 
     /**
      * Constructs a new bridge context.
      * @param userAgent the user agent
      * @param interpreterPool the interpreter pool
+     * @param loader document loader
+     */
+    public BridgeContext(UserAgent userAgent,
+                         InterpreterPool interpreterPool,
+                         DocumentLoader loader) {
+        this(userAgent, interpreterPool, sharedFOHPool, loader);
+    }
+
+    /**
+     * Constructs a new bridge context.
+     * @param userAgent the user agent
+     * @param fohPool the pool of ForeignObjectHandler factories
+     * @param loader document loader
+     */
+    public BridgeContext(UserAgent userAgent,
+                         ForeignObjectHandlerPool fohPool,
+                         DocumentLoader loader) {
+        this(userAgent, sharedPool, fohPool, loader);
+    }
+
+    /**
+     * Constructs a new bridge context.
+     * @param userAgent the user agent
+     * @param interpreterPool the interpreter pool
+     * @param fohPool the pool of ForeignObjectHandler factories
      * @param documentLoader document loader
      */
     public BridgeContext(UserAgent userAgent,
                          InterpreterPool interpreterPool,
+                         ForeignObjectHandlerPool fohPool,
                          DocumentLoader documentLoader) {
         this.userAgent = userAgent;
         this.viewportMap.put(userAgent, new UserAgentViewport(userAgent));
         this.interpreterPool = interpreterPool;
+        this.foreignObjectHandlerPool = fohPool;
         this.documentLoader = documentLoader;
     }
 
@@ -526,6 +564,13 @@
     }
 
     /**
+     * Returns the ForeignObjectHandlerFactory pool.
+     */
+    public ForeignObjectHandlerPool getForeignObjectHandlerPool() {
+        return foreignObjectHandlerPool;
+    }
+
+    /**
      * Returns the focus manager.
      */
     public FocusManager getFocusManager() {
@@ -580,6 +625,22 @@
     }
 
     /**
+     * Returns a ForeignObjectHandler for the given namespace URI.
+     * @param ns the namespace URI of the element to be handled as
+     *           foreign object content
+     */
+    public ForeignObjectHandler createForeignObjectHandler(String ns) {
+        try {
+            return foreignObjectHandlerPool.createForeignObjectHandler(ns);
+        } catch (Exception e) {
+            if (userAgent != null) {
+                userAgent.displayError(e);
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns the document loader used to load external documents.
      */
     public DocumentLoader getDocumentLoader() {
diff --git a/sources/org/apache/batik/bridge/ForeignObjectHandler.java b/sources/org/apache/batik/bridge/ForeignObjectHandler.java
new file mode 100644
index 0000000..a7331a9
--- /dev/null
+++ b/sources/org/apache/batik/bridge/ForeignObjectHandler.java
@@ -0,0 +1,40 @@
+/*
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.bridge;
+
+import org.apache.batik.gvt.GraphicsNode;
+
+import org.w3c.dom.Element;
+
+/**
+ * An interface for classes that can handle content inside a 'foreignObject'
+ * element.
+ *
+ * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
+ * @version $Id$
+ */
+public interface ForeignObjectHandler {
+
+    /**
+     * Returns a new GraphicsNode that represents the foreign object content
+     * in the given element.
+     */
+    GraphicsNode createGraphicsNode(BridgeContext ctx, Element e,
+                                    float w, float h);
+}
diff --git a/sources/org/apache/batik/bridge/ForeignObjectHandlerFactory.java b/sources/org/apache/batik/bridge/ForeignObjectHandlerFactory.java
new file mode 100644
index 0000000..d04f3f2
--- /dev/null
+++ b/sources/org/apache/batik/bridge/ForeignObjectHandlerFactory.java
@@ -0,0 +1,39 @@
+/*
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.bridge;
+
+/**
+ * An interface for factory classes that can create ForeignObjectHandlers.
+ *
+ * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
+ * @version $Id$
+ */
+public interface ForeignObjectHandlerFactory {
+
+    /**
+     * Returns the namespace URI of the elements ForeignObjectHandlers created
+     * by this factory can handle.
+     */
+    String getNamespaceURI();
+
+    /**
+     * Creates a new ForeignObjectHandler.
+     */
+    ForeignObjectHandler createHandler();
+}
diff --git a/sources/org/apache/batik/bridge/ForeignObjectHandlerPool.java b/sources/org/apache/batik/bridge/ForeignObjectHandlerPool.java
new file mode 100644
index 0000000..8b85242
--- /dev/null
+++ b/sources/org/apache/batik/bridge/ForeignObjectHandlerPool.java
@@ -0,0 +1,92 @@
+/*
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.bridge;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.batik.util.Service;
+
+/**
+ * A class that can construct {@link ForeignObjectHandler}s for a given
+ * namespace URI.
+ *
+ * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
+ * @version $Id$
+ */
+public class ForeignObjectHandlerPool {
+
+    /**
+     * The default ForeignObjectHandlerFactory map.
+     */
+    protected static Map defaultFactories = new HashMap(3);
+
+    /**
+     * The ForeignObjectHandlerFactory map.
+     */
+    protected Map factories = new HashMap(3);
+
+    static {
+        Iterator it = Service.providers(ForeignObjectHandlerFactory.class);
+        while (it.hasNext()) {
+            ForeignObjectHandlerFactory factory =
+                (ForeignObjectHandlerFactory) it.next();
+            defaultFactories.put(factory.getNamespaceURI(), factory);
+        }
+    }
+
+    /**
+     * Creates a new ForeignObjectHandlerPool.
+     */
+    public ForeignObjectHandlerPool() {
+        factories.putAll(defaultFactories);
+    }
+
+    /**
+     * Creates a new ForeignObjectHandler for the given namespace URI.
+     * @param ns the namespace URI of the elements the returned
+     *           ForeignObjectHandler should be able to handle
+     */
+    public ForeignObjectHandler createForeignObjectHandler(String ns) {
+        ForeignObjectHandlerFactory factory =
+            (ForeignObjectHandlerFactory) factories.get(ns);
+        if (factory == null) {
+            return null;
+        }
+        return factory.createHandler();
+    }
+
+    /**
+     * Adds for the specified namespace URI, the specified
+     * ForeignObjectHandlerFactory.
+     */
+    public void putForeignObjectHandlerFactory
+            (String ns, ForeignObjectHandlerFactory factory) {
+        factories.put(ns, factory);
+    }
+
+    /**
+     * Removes the ForeignObjectHandlerFactory associated with the specified
+     * namespace URI.
+     */
+    public void removeForeignObjectHandlerFactory(String ns) {
+        factories.remove(ns);
+    }
+}
diff --git a/sources/org/apache/batik/bridge/SVGBridgeExtension.java b/sources/org/apache/batik/bridge/SVGBridgeExtension.java
index 84011d9..eab6512 100644
--- a/sources/org/apache/batik/bridge/SVGBridgeExtension.java
+++ b/sources/org/apache/batik/bridge/SVGBridgeExtension.java
@@ -127,6 +127,7 @@
         ctx.putBridge(new SVGFeTurbulenceElementBridge());
         ctx.putBridge(new SVGFontElementBridge());
         ctx.putBridge(new SVGFontFaceElementBridge());
+        ctx.putBridge(new SVGForeignObjectElementBridge());
         ctx.putBridge(new SVGFilterElementBridge());
         ctx.putBridge(new SVGGElementBridge());
         ctx.putBridge(new SVGGlyphElementBridge());
diff --git a/sources/org/apache/batik/bridge/SVGForeignObjectElementBridge.java b/sources/org/apache/batik/bridge/SVGForeignObjectElementBridge.java
new file mode 100644
index 0000000..08f4341
--- /dev/null
+++ b/sources/org/apache/batik/bridge/SVGForeignObjectElementBridge.java
@@ -0,0 +1,268 @@
+/*
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.bridge;
+
+import java.awt.geom.AffineTransform;
+
+import org.apache.batik.dom.svg.SVGOMElement;
+import org.apache.batik.gvt.CompositeGraphicsNode;
+import org.apache.batik.gvt.GraphicsNode;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.events.MutationEvent;
+
+/**
+ * Bridge class for the &lt;foreignObject&gt; element.
+ *
+ * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
+ * @version $Id$
+ */
+public class SVGForeignObjectElementBridge extends AbstractGraphicsNodeBridge {
+
+    /**
+     * The ForeignObjectHandler object that will construct the GVT
+     * representation of the foreign content.
+     */
+    protected ForeignObjectHandler handler;
+
+    /**
+     * The foreign content.
+     */
+    protected Element foreignContent;
+
+    /**
+     * Constructs a new bridge for a &lt;foreignObject&gt; element.
+     */
+    public SVGForeignObjectElementBridge() {
+    }
+
+    /**
+     * Returns 'foreignObject'.
+     */
+    public String getLocalName() {
+        return SVG_FOREIGN_OBJECT_TAG;
+    }
+
+    /**
+     * Returns a new instance of this bridge.
+     */
+    public Bridge getInstance() {
+        return new SVGForeignObjectElementBridge();
+    }
+
+    /**
+     * Creates a graphics node using the specified BridgeContext and for the
+     * specified element.
+     *
+     * @param ctx the bridge context to use
+     * @param e the element that describes the graphics node to build
+     * @return a graphics node that represents the specified element
+     */
+    public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) {
+        Node n = e.getFirstChild();
+        while (n != null && n.getNodeType() != Node.ELEMENT_NODE) {
+            n = n.getNextSibling();
+        }
+        if (n == null) {
+            return null;
+        }
+
+        Element elt = (Element) n;
+
+        CompositeGraphicsNode cgn =
+            (CompositeGraphicsNode) super.createGraphicsNode(ctx, e);
+        if (cgn == null) {
+            return null;
+        }
+
+        ForeignObjectHandler handler =
+            ctx.createForeignObjectHandler(elt.getNamespaceURI());
+
+        if (handler == null) {
+            return null;
+        }
+
+        buildForeignObjectNode(ctx, e, elt, cgn, handler);
+
+        if (ctx.isDynamic()) {
+            this.handler = handler;
+            this.foreignContent = elt;
+        }
+
+        return cgn;
+    }
+
+    /**
+     * Builds the graphics node according to the ForeignObjectHandler.
+     */
+    public void buildForeignObjectNode(BridgeContext ctx,
+                                       Element e,
+                                       Element foreignContent,
+                                       CompositeGraphicsNode cgn,
+                                       ForeignObjectHandler handler) {
+
+        while (!cgn.isEmpty()) {
+            cgn.remove(cgn.size() - 1);
+        }
+
+        UnitProcessor.Context uctx = updatePosition(ctx, e, cgn);
+        String s;
+
+        // 'width' attribute - required
+        s = e.getAttributeNS(null, SVG_WIDTH_ATTRIBUTE);
+        float w;
+        if (s.length() != 0) {
+            w = UnitProcessor.svgHorizontalLengthToUserSpace
+                (s, SVG_WIDTH_ATTRIBUTE, uctx);
+        } else {
+            throw new BridgeException(ctx, e, ERR_ATTRIBUTE_MISSING,
+                                      new Object[] {SVG_WIDTH_ATTRIBUTE, s});
+        }
+
+        // 'height' attribute - required
+        s = e.getAttributeNS(null, SVG_HEIGHT_ATTRIBUTE);
+        float h;
+        if (s.length() != 0) {
+            h = UnitProcessor.svgVerticalLengthToUserSpace
+                (s, SVG_HEIGHT_ATTRIBUTE, uctx);
+        } else {
+            throw new BridgeException(ctx, e, ERR_ATTRIBUTE_MISSING,
+                                      new Object[] {SVG_HEIGHT_ATTRIBUTE, s});
+        }
+
+        if (w == 0 || h == 0) {
+            return;
+        }
+
+        GraphicsNode gn = handler.createGraphicsNode(ctx, foreignContent, w, h);
+
+        if (gn != null) {
+            cgn.add(gn);
+        }
+    }
+
+    /**
+     * Updates the transform on the graphics node to reflect the 'x' and 'y'
+     * attributes on the element.
+     */
+    protected UnitProcessor.Context updatePosition
+            (BridgeContext ctx,
+             Element e,
+             CompositeGraphicsNode cgn) {
+
+        UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, e);
+        String s;
+
+        // 'x' attribute - default is 0
+        s = e.getAttributeNS(null, SVG_X_ATTRIBUTE);
+        float x = 0;
+        if (s.length() != 0) {
+            x = UnitProcessor.svgHorizontalCoordinateToUserSpace
+                (s, SVG_X_ATTRIBUTE, uctx);
+        }
+
+        // 'y' attribute - default is 0
+        s = e.getAttributeNS(null, SVG_Y_ATTRIBUTE);
+        float y = 0;
+        if (s.length() != 0) {
+            y = UnitProcessor.svgVerticalCoordinateToUserSpace
+                (s, SVG_Y_ATTRIBUTE, uctx);
+        }
+
+        AffineTransform at = AffineTransform.getTranslateInstance(x, y);
+        cgn.setTransform(at);
+
+        return uctx;
+    }
+
+    /**
+     * Creates a {@link CompositeGraphicsNode}.
+     */
+    protected GraphicsNode instantiateGraphicsNode() {
+        return new CompositeGraphicsNode();
+    }
+
+    /**
+     * Returns false as 'foreignObject' elements do not have SVG content.
+     */
+    public boolean isComposite() {
+        return false;
+    }
+
+    /**
+     * This method is invoked during the build phase if the document
+     * is dynamic. The responsability of this method is to ensure that
+     * any dynamic modifications of the element this bridge is
+     * dedicated to, happen on its associated GVT product.
+     */
+    protected void initializeDynamicSupport(BridgeContext ctx,
+                                            Element e,
+                                            GraphicsNode node) {
+        if (!ctx.isInteractive()) {
+            return;
+        }
+
+        if (ctx.isDynamic()) {
+            // Only do this for dynamic not interactive.
+            this.e = e;
+            this.node = node;
+            this.ctx = ctx;
+            ((SVGOMElement) e).setSVGContext(this);
+        }
+    }
+
+    // BridgeUpdateHandler implementation ////////////////////////////////////
+
+    /**
+     * Invoked when an MutationEvent of type 'DOMAttrModified' is fired.
+     */
+    public void handleDOMAttrModifiedEvent(MutationEvent evt) {
+        String attrName = evt.getAttrName();
+        Node evtNode = evt.getRelatedNode();
+
+        if (attrName.equals(SVG_X_ATTRIBUTE)
+                || attrName.equals(SVG_Y_ATTRIBUTE)) {
+            updatePosition(ctx, e, (CompositeGraphicsNode) node);
+        } else if (attrName.equals(SVG_WIDTH_ATTRIBUTE)
+                || attrName.equals(SVG_HEIGHT_ATTRIBUTE)) {
+            float oldV = 0, newV = 0;
+            String s = evt.getPrevValue();
+            UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, e);
+
+            if (s.length() != 0) {
+                oldV = UnitProcessor.svgHorizontalCoordinateToUserSpace
+                    (s, attrName, uctx);
+            }
+            s = evt.getNewValue();
+            if (s.length() != 0) {
+                newV = UnitProcessor.svgHorizontalCoordinateToUserSpace
+                    (s, attrName, uctx);
+            }
+            if (oldV == newV) {
+                return;
+            }
+
+            buildForeignObjectNode
+                (ctx, e, foreignContent, (CompositeGraphicsNode) node, handler);
+        } else {
+            super.handleDOMAttrModifiedEvent(evt);
+        }
+    }
+}
diff --git a/sources/org/apache/batik/bridge/TestForeignObjectHandlerFactory.java b/sources/org/apache/batik/bridge/TestForeignObjectHandlerFactory.java
new file mode 100644
index 0000000..fe6feaf
--- /dev/null
+++ b/sources/org/apache/batik/bridge/TestForeignObjectHandlerFactory.java
@@ -0,0 +1,65 @@
+/*
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.bridge;
+
+import java.awt.Color;
+import java.awt.geom.Rectangle2D;
+
+import org.apache.batik.gvt.FillShapePainter;
+import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.gvt.ShapeNode;
+import org.apache.batik.gvt.ShapePainter;
+
+import org.w3c.dom.Element;
+
+/**
+ * A test &lt;foreignObject> handler factory.  The factory creates a simple
+ * &lt;foreignObject> handler that just displays a yellow rectangle.
+ *
+ * @author <a href='mailto:cam%40mcc%2eid%2eau'>Cameron McCormack</a>
+ * @version $Id$
+ */
+public class TestForeignObjectHandlerFactory implements ForeignObjectHandlerFactory {
+
+    /**
+     * Returns the namespace URI of the elements ForeignObjectHandlers created
+     * by this factory can handle.
+     */
+    public String getNamespaceURI() {
+        return "http://example.org/foreign";
+    }
+
+    /**
+     * Creates a new ForeignObjectHandler.
+     */
+    public ForeignObjectHandler createHandler() {
+        return new ForeignObjectHandler() {
+            public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e,
+                                            float w, float h) {
+                Rectangle2D r = new Rectangle2D.Float(0, 0, w, h);
+                ShapeNode n = new ShapeNode();
+                n.setShape(r);
+                FillShapePainter p = new FillShapePainter(r);
+                p.setPaint(Color.YELLOW);
+                n.setShapePainter(p);
+                return n;
+            }
+        };
+    }
+}
diff --git a/sources/org/apache/batik/bridge/XHTMLForeignObjectHandlerFactory.java b/sources/org/apache/batik/bridge/XHTMLForeignObjectHandlerFactory.java
new file mode 100644
index 0000000..b51435c
--- /dev/null
+++ b/sources/org/apache/batik/bridge/XHTMLForeignObjectHandlerFactory.java
@@ -0,0 +1,146 @@
+/*
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.bridge;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.geom.Rectangle2D;
+
+import org.apache.batik.dom.AbstractDocument;
+import org.apache.batik.dom.GenericDOMImplementation;
+import org.apache.batik.dom.svg.SVGDOMImplementation;
+import org.apache.batik.gvt.CompositeGraphicsNode;
+import org.apache.batik.gvt.FillShapePainter;
+import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.gvt.RootGraphicsNode;
+import org.apache.batik.gvt.ShapeNode;
+import org.apache.batik.gvt.ShapePainter;
+import org.apache.batik.svggen.SVGGraphics2D;
+import org.apache.batik.util.SVGConstants;
+
+import org.xhtmlrenderer.extend.FontContext;
+import org.xhtmlrenderer.extend.OutputDevice;
+import org.xhtmlrenderer.layout.SharedContext;
+import org.xhtmlrenderer.simple.Graphics2DRenderer;
+import org.xhtmlrenderer.swing.Java2DOutputDevice;
+import org.xhtmlrenderer.swing.Java2DTextRenderer;
+
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * A factory class for the XHTML foreign object handler.
+ *
+ * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
+ * @version $Id$
+ */
+public class XHTMLForeignObjectHandlerFactory
+    implements ForeignObjectHandlerFactory {
+
+    /**
+     * Returns the XHTML namespace URI.
+     */
+    public String getNamespaceURI() {
+        return "http://www.w3.org/1999/xhtml";
+    }
+
+    /**
+     * Creates a new ForeignObjectHandler.
+     */
+    public ForeignObjectHandler createHandler() {
+        return new Handler();
+    }
+
+    /**
+     * The handler class for XHTML foreign object content.
+     */
+    protected static class Handler implements ForeignObjectHandler {
+
+        /**
+         * Creates a new Handler.
+         */
+        public Handler() {
+            DOMImplementation impl =
+                SVGDOMImplementation.getDOMImplementation();
+        }
+
+        /**
+         * Returns a new GraphicsNode that represents the foreign object content
+         * in the given element.
+         */
+        public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e,
+                                               float w, float h) {
+            if (!e.getLocalName().equals("html")) {
+                return null;
+            }
+
+            DOMImplementation impl =
+                GenericDOMImplementation.getDOMImplementation();
+            Document htmlDoc = impl.createDocument
+                ("http://www.w3.org/1999/xhtml", "html", null);
+            htmlDoc.replaceChild
+                (htmlDoc.importNode(e, true), htmlDoc.getDocumentElement());
+
+            impl = SVGDOMImplementation.getDOMImplementation();
+            Document svgDoc = impl.createDocument
+                (SVGConstants.SVG_NAMESPACE_URI, SVGConstants.SVG_SVG_TAG, null);
+
+            SVGGraphics2D g2d = new SVGGraphics2D(svgDoc);
+
+            Graphics2DRenderer renderer = new Graphics2DRenderer();
+            renderer.getPanel().setOpaque(false);
+            SharedContext sc = renderer.getSharedContext();
+            sc.setTextRenderer(new TextRenderer());
+            renderer.setDocument(htmlDoc, AbstractDocument.getBaseURI(ctx.getDocument()));
+            renderer.layout(g2d, new Dimension(Math.round(w), Math.round(h)));
+            renderer.render(g2d);
+
+//             try {
+//                 g2d.stream(new java.io.OutputStreamWriter(System.out), false);
+//             } catch (Exception ex) {
+//             }
+
+            g2d.getRoot(svgDoc.getDocumentElement());
+
+            BridgeContext cx = new BridgeContext(ctx.getUserAgent());
+            GVTBuilder builder = new GVTBuilder();
+            RootGraphicsNode rgn = (RootGraphicsNode) builder.build(cx, svgDoc);
+            Object[] nodes = rgn.toArray();
+            CompositeGraphicsNode cgn = new CompositeGraphicsNode();
+            for (int i = 0; i < nodes.length; i++) {
+                cgn.add(nodes[i]);
+                ((GraphicsNode) nodes[i]).setClip(null);
+            }
+            return cgn;
+        }
+    }
+
+    protected static class TextRenderer extends Java2DTextRenderer {
+
+        public void drawString(OutputDevice outputDevice, String string, float x, float y) {
+            Graphics2D graphics = ((Java2DOutputDevice) outputDevice).getGraphics();
+            graphics.drawString(string, x, y);
+        }
+
+        public void setup(FontContext fontContext) {
+        }
+    }
+}
diff --git a/sources/org/apache/batik/svggen/SVGGraphics2D.java b/sources/org/apache/batik/svggen/SVGGraphics2D.java
index 66053a4..a372281 100644
--- a/sources/org/apache/batik/svggen/SVGGraphics2D.java
+++ b/sources/org/apache/batik/svggen/SVGGraphics2D.java
@@ -27,8 +27,10 @@
 import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
 import java.awt.Image;
 import java.awt.Paint;
+import java.awt.Rectangle;
 import java.awt.Shape;
 import java.awt.Stroke;
 import java.awt.font.GlyphVector;
@@ -38,8 +40,10 @@
 import java.awt.geom.NoninvertibleTransformException;
 import java.awt.image.BufferedImage;
 import java.awt.image.BufferedImageOp;
+import java.awt.image.ColorModel;
 import java.awt.image.ImageObserver;
 import java.awt.image.RenderedImage;
+import java.awt.image.VolatileImage;
 import java.awt.image.renderable.RenderableImage;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -143,6 +147,11 @@
     protected Dimension svgCanvasSize;
 
     /**
+     * The GraphicsConfiguration that describes this Graphics2D.
+     */
+    protected GraphicsConfiguration config;
+
+    /**
      * Used to create proper font metrics
      */
     protected Graphics2D fmg;
@@ -1484,8 +1493,10 @@
      * <code>Graphics2D</code>.
      */
     public GraphicsConfiguration getDeviceConfiguration(){
-        // TO BE DONE.
-        return null;
+        if (config == null) {
+            config = new Config();
+        }
+        return config;
     }
 
     /* This is the list of attributes that can't currently be
@@ -1536,4 +1547,49 @@
         return false;
     }
 
+    /**
+     * Implementation of GraphicsConfiguration that describes this Graphics2D.
+     */
+    public class Config extends GraphicsConfiguration {
+
+        public BufferedImage createCompatibleImage(int w, int h) {
+            return new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
+        }
+
+        public BufferedImage createCompatibleImage(int w, int h, int tr) {
+            return null;
+        }
+
+        public VolatileImage createCompatibleVolatileImage(int w, int h) {
+            return null;
+        }
+
+        public VolatileImage createCompatibleVolatileImage(int w, int h, int transparency) {
+            return null;
+        }
+
+        public Rectangle getBounds() {
+            return null;
+        }
+
+        public ColorModel getColorModel() {
+            return null;
+        }
+
+        public ColorModel getColorModel(int transparency) {
+            return null;
+        }
+
+        public AffineTransform getDefaultTransform() {
+            return new AffineTransform();
+        }
+
+        public GraphicsDevice getDevice() {
+            return null;
+        }
+
+        public AffineTransform getNormalizingTransform() {
+            return new AffineTransform();
+        }
+    }
 }