From 790875d4101f90ab3419025717b581c255d87fe1 Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Sun, 7 Dec 2008 18:01:24 +0000 Subject: [PATCH] Bugzilla #46360: Fixed a multi-threading issue when rendering SVG. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@724163 13f79535-47bb-0310-9956-ffa450edef68 --- .../loader/batik/ImageConverterSVG2G2D.java | 13 ++++++++++-- .../fop/render/AbstractGenericSVGHandler.java | 21 ++++++++++++------- .../apache/fop/render/afp/AFPSVGHandler.java | 20 ++++++++++++------ .../fop/render/java2d/Java2DSVGHandler.java | 13 +++++++++--- .../apache/fop/render/pdf/PDFRenderer.java | 1 + .../apache/fop/render/pdf/PDFSVGHandler.java | 15 ++++++++----- .../apache/fop/render/ps/PSSVGHandler.java | 10 ++++++--- status.xml | 3 +++ 8 files changed, 70 insertions(+), 26 deletions(-) diff --git a/src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java b/src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java index 81c12a628..2bb521dc9 100644 --- a/src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java +++ b/src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java @@ -23,6 +23,8 @@ import java.awt.Dimension; import java.awt.geom.AffineTransform; import java.util.Map; +import org.w3c.dom.Document; + import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.bridge.UserAgent; @@ -30,7 +32,7 @@ import org.apache.batik.dom.svg.SVGDOMImplementation; import org.apache.batik.gvt.GraphicsNode; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.fop.svg.SimpleSVGUserAgent; + import org.apache.xmlgraphics.image.loader.Image; import org.apache.xmlgraphics.image.loader.ImageException; import org.apache.xmlgraphics.image.loader.ImageFlavor; @@ -43,6 +45,8 @@ import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; import org.apache.xmlgraphics.java2d.Graphics2DImagePainter; import org.apache.xmlgraphics.util.UnitConv; +import org.apache.fop.svg.SimpleSVGUserAgent; + /** * This ImageConverter converts SVG images to Java2D. *

@@ -75,10 +79,15 @@ public class ImageConverterSVG2G2D extends AbstractImageConverter { GVTBuilder builder = new GVTBuilder(); final BridgeContext ctx = new BridgeContext(ua); + Document doc = svg.getDocument(); + //Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine) + //to it. + Document clonedDoc = BatikUtil.cloneSVGDocument(doc); + //Build the GVT tree final GraphicsNode root; try { - root = builder.build(ctx, svg.getDocument()); + root = builder.build(ctx, clonedDoc); } catch (Exception e) { throw new ImageException("GVT tree could not be built for SVG graphic", e); } diff --git a/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java b/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java index 5c253fe94..35876a728 100644 --- a/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java +++ b/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java @@ -24,22 +24,25 @@ import java.awt.Dimension; import java.awt.geom.AffineTransform; import java.io.IOException; +import org.w3c.dom.Document; + import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.dom.AbstractDocument; import org.apache.batik.dom.svg.SVGDOMImplementation; +import org.apache.batik.dom.util.DOMUtilities; import org.apache.batik.gvt.GraphicsNode; + +import org.apache.xmlgraphics.java2d.Graphics2DImagePainter; +import org.apache.xmlgraphics.util.QName; + import org.apache.fop.apps.FOUserAgent; import org.apache.fop.events.EventBroadcaster; import org.apache.fop.fo.extensions.ExtensionElementMapping; import org.apache.fop.image.loader.batik.Graphics2DImagePainterImpl; import org.apache.fop.render.RendererContext.RendererContextWrapper; -import org.apache.fop.render.afp.AFPGraphics2DAdapter; import org.apache.fop.svg.SVGEventProducer; import org.apache.fop.svg.SVGUserAgent; -import org.apache.xmlgraphics.java2d.Graphics2DImagePainter; -import org.apache.xmlgraphics.util.QName; -import org.w3c.dom.Document; /** * Generic XML handler for SVG. Uses Apache Batik for SVG processing and simply paints to @@ -133,15 +136,19 @@ public abstract class AbstractGenericSVGHandler implements XMLHandler, RendererC //Create Batik BridgeContext final BridgeContext bridgeContext = new BridgeContext(svgUserAgent); - //Build the GVT tree + //Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine) + //to it. + Document clonedDoc = DOMUtilities.deepCloneDocument(doc, doc.getImplementation()); - final GraphicsNode root = buildGraphicsNode(userAgent, bridgeContext, doc); + //Build the GVT tree + final GraphicsNode root = buildGraphicsNode(userAgent, bridgeContext, clonedDoc); // Create Graphics2DImagePainter final RendererContextWrapper wrappedContext = RendererContext.wrapRendererContext( rendererContext); Dimension imageSize = getImageSize(wrappedContext); - final Graphics2DImagePainter painter = createGraphics2DImagePainter(root, bridgeContext, imageSize); + final Graphics2DImagePainter painter = createGraphics2DImagePainter( + root, bridgeContext, imageSize); //Let the painter paint the SVG on the Graphics2D instance Graphics2DAdapter g2dAdapter = rendererContext.getRenderer().getGraphics2DAdapter(); diff --git a/src/java/org/apache/fop/render/afp/AFPSVGHandler.java b/src/java/org/apache/fop/render/afp/AFPSVGHandler.java index 9deea77b4..ca64d6bc2 100644 --- a/src/java/org/apache/fop/render/afp/AFPSVGHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPSVGHandler.java @@ -24,9 +24,18 @@ import java.awt.Dimension; import java.awt.geom.AffineTransform; import java.io.IOException; +import org.w3c.dom.Document; + import org.apache.batik.bridge.BridgeContext; import org.apache.batik.dom.svg.SVGDOMImplementation; +import org.apache.batik.dom.util.DOMUtilities; import org.apache.batik.gvt.GraphicsNode; + +import org.apache.xmlgraphics.image.loader.ImageManager; +import org.apache.xmlgraphics.image.loader.ImageSessionContext; +import org.apache.xmlgraphics.java2d.Graphics2DImagePainter; +import org.apache.xmlgraphics.util.MimeConstants; + import org.apache.fop.afp.AFPGraphics2D; import org.apache.fop.afp.AFPGraphicsObjectInfo; import org.apache.fop.afp.AFPObjectAreaInfo; @@ -44,11 +53,6 @@ import org.apache.fop.render.RendererContext; import org.apache.fop.render.RendererContext.RendererContextWrapper; import org.apache.fop.svg.SVGEventProducer; import org.apache.fop.svg.SVGUserAgent; -import org.apache.xmlgraphics.image.loader.ImageManager; -import org.apache.xmlgraphics.image.loader.ImageSessionContext; -import org.apache.xmlgraphics.java2d.Graphics2DImagePainter; -import org.apache.xmlgraphics.util.MimeConstants; -import org.w3c.dom.Document; /** * AFP XML handler for SVG. Uses Apache Batik for SVG processing. @@ -107,8 +111,12 @@ public class AFPSVGHandler extends AbstractGenericSVGHandler { // Create an AFPBridgeContext BridgeContext bridgeContext = createBridgeContext(userAgent, g2d); + //Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine) + //to it. + Document clonedDoc = DOMUtilities.deepCloneDocument(doc, doc.getImplementation()); + // Build the SVG DOM and provide the painter with it - GraphicsNode root = buildGraphicsNode(userAgent, bridgeContext, doc); + GraphicsNode root = buildGraphicsNode(userAgent, bridgeContext, clonedDoc); // Create Graphics2DImagePainter final RendererContextWrapper wrappedContext diff --git a/src/java/org/apache/fop/render/java2d/Java2DSVGHandler.java b/src/java/org/apache/fop/render/java2d/Java2DSVGHandler.java index ba5c86059..2d16b120d 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DSVGHandler.java +++ b/src/java/org/apache/fop/render/java2d/Java2DSVGHandler.java @@ -23,18 +23,21 @@ import java.awt.geom.AffineTransform; import java.io.IOException; import java.util.Map; +import org.w3c.dom.Document; + import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.GVTBuilder; +import org.apache.batik.dom.util.DOMUtilities; import org.apache.batik.gvt.GraphicsNode; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.render.AbstractGenericSVGHandler; import org.apache.fop.render.Renderer; import org.apache.fop.render.RendererContext; import org.apache.fop.render.RendererContextConstants; import org.apache.fop.svg.SVGEventProducer; import org.apache.fop.svg.SVGUserAgent; -import org.w3c.dom.Document; /** * Java2D XML handler for SVG (uses Apache Batik). @@ -128,12 +131,16 @@ public class Java2DSVGHandler extends AbstractGenericSVGHandler SVGUserAgent ua = new SVGUserAgent(context.getUserAgent(), new AffineTransform()); - GVTBuilder builder = new GVTBuilder(); BridgeContext ctx = new BridgeContext(ua); + //Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine) + //to it. + Document clonedDoc = DOMUtilities.deepCloneDocument(doc, doc.getImplementation()); + GraphicsNode root; try { - root = builder.build(ctx, doc); + GVTBuilder builder = new GVTBuilder(); + root = builder.build(ctx, clonedDoc); } catch (Exception e) { SVGEventProducer eventProducer = SVGEventProducer.Provider.get( context.getUserAgent().getEventBroadcaster()); diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java index e31f1eaea..dda7e2083 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java @@ -1635,6 +1635,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { (int)pos.getHeight()); uri = URISpecification.getURL(uri); + uri = URISpecification.preResolveURI(uri, userAgent.getBaseURL()); PDFXObject xobject = pdfDoc.getXObject(uri); if (xobject != null) { float w = (float) pos.getWidth() / 1000f; diff --git a/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java b/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java index 5d027aefe..0bf9bd95b 100644 --- a/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java @@ -25,14 +25,18 @@ import java.io.IOException; import java.io.OutputStream; import java.util.Map; +import org.w3c.dom.Document; + import org.apache.avalon.framework.configuration.Configuration; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.dom.svg.SVGDOMImplementation; +import org.apache.batik.dom.util.DOMUtilities; import org.apache.batik.gvt.GraphicsNode; import org.apache.batik.util.SVGConstants; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.apps.FOUserAgent; import org.apache.fop.fonts.FontInfo; import org.apache.fop.pdf.PDFDocument; @@ -49,7 +53,6 @@ import org.apache.fop.svg.PDFBridgeContext; import org.apache.fop.svg.PDFGraphics2D; import org.apache.fop.svg.SVGEventProducer; import org.apache.fop.svg.SVGUserAgent; -import org.w3c.dom.Document; /** * PDF XML handler for SVG (uses Apache Batik). @@ -164,8 +167,6 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler AffineTransform resolutionScaling = new AffineTransform(); resolutionScaling.scale(s, s); - GVTBuilder builder = new GVTBuilder(); - //Controls whether text painted by Batik is generated using text or path operations boolean strokeText = false; Configuration cfg = pdfInfo.cfg; @@ -179,10 +180,14 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler userAgent.getImageSessionContext(), new AffineTransform()); + //Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine) + //to it. + Document clonedDoc = DOMUtilities.deepCloneDocument(doc, doc.getImplementation()); + GraphicsNode root; try { - root = builder.build(ctx, doc); - builder = null; + GVTBuilder builder = new GVTBuilder(); + root = builder.build(ctx, clonedDoc); } catch (Exception e) { SVGEventProducer eventProducer = SVGEventProducer.Provider.get( context.getUserAgent().getEventBroadcaster()); diff --git a/src/java/org/apache/fop/render/ps/PSSVGHandler.java b/src/java/org/apache/fop/render/ps/PSSVGHandler.java index 1e65dfb98..646cd3823 100644 --- a/src/java/org/apache/fop/render/ps/PSSVGHandler.java +++ b/src/java/org/apache/fop/render/ps/PSSVGHandler.java @@ -29,6 +29,7 @@ import org.w3c.dom.Document; import org.apache.avalon.framework.configuration.Configuration; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.GVTBuilder; +import org.apache.batik.dom.util.DOMUtilities; import org.apache.batik.gvt.GraphicsNode; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -258,7 +259,6 @@ public class PSSVGHandler extends AbstractGenericSVGHandler PSGraphics2D graphics = new PSGraphics2D(strokeText, gen); graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); - GVTBuilder builder = new GVTBuilder(); NativeTextHandler nativeTextHandler = null; BridgeContext ctx = new BridgeContext(ua); if (!strokeText) { @@ -271,9 +271,14 @@ public class PSSVGHandler extends AbstractGenericSVGHandler ctx.putBridge(tBridge); } + //Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine) + //to it. + Document clonedDoc = DOMUtilities.deepCloneDocument(doc, doc.getImplementation()); + GraphicsNode root; try { - root = builder.build(ctx, doc); + GVTBuilder builder = new GVTBuilder(); + root = builder.build(ctx, clonedDoc); } catch (Exception e) { SVGEventProducer eventProducer = SVGEventProducer.Provider.get( context.getUserAgent().getEventBroadcaster()); @@ -288,7 +293,6 @@ public class PSSVGHandler extends AbstractGenericSVGHandler float sy = psInfo.getHeight() / h; ctx = null; - builder = null; try { gen.commentln("%FOPBeginSVG"); diff --git a/status.xml b/status.xml index 0f139fba7..b2e37dd33 100644 --- a/status.xml +++ b/status.xml @@ -53,6 +53,9 @@ + + Fixed a multi-threading issue when rendering SVG. + AFP Output: An AFPGraphics2D implementation which provides the ability to use Batik to drive the production of AFP Graphics (GOCA) output from SVG. -- 2.39.5