Hi Thomas!

Your added suggestions did the trick.  Also, I agree with your
conclusion that the approach suggested in the tread (>
http://mail-archives.apache.org/mod_mbox/xmlgraphics-batik-users/200112.
) is inefficient compared to the code in this thread.  

I have taken all of the suggestions of this thread and what we have
learned an applied it to a small, immutable class.  For the benefit of
others here is a way to efficiently load a SVG from a URL or document
and retrieve it as a java.awt.Image.  

Hopefully some form of this code can be integrated into Batik.

- Jonathan

Usages: java.awt.Image image = new SvgImage(url).getImage(50, 50);
        java.awt.Image image = new SvgImage(doc).getImage(50, 50);

Code:

import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.bridge.ViewBox;
import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.util.XMLResourceDescriptor;
import org.w3c.dom.Element;
import org.w3c.dom.svg.SVGDocument;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

/**
 * Immutable class to get the Image representation of a svg resource.
 */
public final class SvgImage
{
    /** Root node of svg document */
    private final GraphicsNode rootSvgNode;

    /** Loaded SVG document */
    private final SVGDocument svgDocument;

    /**
     * Load the svg resource from a URL into a document.
     * @param url location of svg resource.
     * @throws java.io.IOException when svg resource cannot be read.
     */
    public SvgImage(URL url)
        throws IOException
    {
        String parser = XMLResourceDescriptor.getXMLParserClassName();
        SAXSVGDocumentFactory factory = new
SAXSVGDocumentFactory(parser);
        svgDocument =
(SVGDocument)factory.createDocument(url.toString());
        rootSvgNode = getRootNode(svgDocument);
    }

    /**
     * Load the svg from a document.
     *
     * @param document svg resource
     */
    public SvgImage(SVGDocument document)
    {
        svgDocument = document;
        rootSvgNode = getRootNode(svgDocument);
    }

    /**
     * Get svg root from the given document.
     *
     * @param document svg resource
     */
    private static GraphicsNode getRootNode(SVGDocument document)
    {
        // Build the tree and get the document dimensions
        UserAgentAdapter userAgentAdapter = new UserAgentAdapter();
        BridgeContext bridgeContext = new
BridgeContext(userAgentAdapter);
        GVTBuilder builder = new GVTBuilder();

        return builder.build(bridgeContext, document);
    }

    /**
     * Get the svg root node of the document.
     *
     * @return svg root node.
     */
    public GraphicsNode getRootSvgNode()
    {
        return rootSvgNode;
    }

    /**
     * Get the svg document.
     * @return the svg document.
     */
    public SVGDocument getSvgDocument()
    {
        return svgDocument;
    }

    /**
     * Renders and returns the svg based image.
     *
     * @param width desired width
     * @param height desired height
     * @return image of the rendered svg.
     */
    public Image getImage(int width, int height)
    {
        // Paint svg into image buffer
        BufferedImage bufferedImage = new BufferedImage(width,
            height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = (Graphics2D) bufferedImage.getGraphics();

        // For a smooth graphic with no jagged edges or rastorized look.
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
            RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        // Scale image to desired size
        Element elt = svgDocument.getRootElement();
        AffineTransform usr2dev = ViewBox.getViewTransform(null, elt,
width, height);
        g2d.transform(usr2dev);

        rootSvgNode.paint(g2d);

        // Cleanup and return image
        g2d.dispose();
        return bufferedImage;
    }
}


-----Original Message-----
From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED] 
Sent: Saturday, January 21, 2006 7:13 AM
To: [email protected]
Subject: RE: How to load an SVG resource into a java.awt.Image?

Hi Jonathan,

"Johnson, Jonathan" <[EMAIL PROTECTED]> wrote on 01/20/2006 04:54:13 PM:

> I still have two problems.  The svg image does not scale itself into
the
> dimension I specified and the image rasterizes with jagged edges.
What
> controls the scaling of the image to the image size and the edge
> smoothness?

   An affine transform controls the scaling:
           import java.awt.geom.AffineTransform;
           import org.apache.batik.bridge.ViewBox;

         Element elt = ((SVGDocument)svgDoc).getRootElement();
         AffineTransform usr2dev = ViewBox.getViewTransform
               (null, elt, dimension.width, dimension.height)); 
           g2d.transform(usr2dev);

   Rendering Hints control how a document is rendered 'by default':
          import java.awt.RenderingHints;

          g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                             RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
 
RenderingHints.VALUE_INTERPOLATION_BILINEAR);


> For a smoother out-of-the-box experience for new batik users this 
> would be a nice, basic utility method (with perhaps complementary 
> methods to accept a File, SVGDocument or a GraphicsNode instead of a 
URL).

     This is a nice start, thanks for posting it. I suspect it will help
others.


>     /**
>      * Loads a svg resource from a URL, renders and returns the image.
>      * @param url location of svg resource.
>      * @param dimension desired size
>      * @return image of the rendered svg
>      * @throws IOException when svg resource cannot be read.
>      */
>     public static Image getImageFromSvg(URL url, Dimension dimension)
>             throws IOException
>     {
>         // Load SVG resource into a document
>         String parser = XMLResourceDescriptor.getXMLParserClassName();
>         SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);
>         SVGDocument document = (SVGDocument)
> f.createDocument(url.toString());
> 
>         // Build the tree and get the document dimensions
>         UserAgentAdapter userAgentAdapter = new UserAgentAdapter();
>         BridgeContext bridgeContext = new
> BridgeContext(userAgentAdapter);
>         GVTBuilder builder = new GVTBuilder();
>         GraphicsNode graphicsNode = builder.build(bridgeContext,
> document);
> 
>         // Paint svg into image buffer
>         BufferedImage bufferedImage = new
BufferedImage(dimension.width,
> dimension.height, BufferedImage.TYPE_INT_ARGB);
>         Graphics2D g2d = (Graphics2D) bufferedImage.getGraphics();
>         graphicsNode.paint(g2d);
> 
>         // Cleanup and return image
>         g2d.dispose();
>         return bufferedImage;
>     }
> 
> -----Original Message-----
> From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED] 
> Sent: Friday, January 20, 2006 6:03 AM
> To: [email protected]
> Cc: [email protected]
> Subject: Re: How to load an SVG resource into a java.awt.Image?
> 
> Hi Johnson,
> 
> "Johnson, Jonathan" <[EMAIL PROTECTED]> wrote on 01/18/2006 04:55:48
PM:
> 
> > Swing has a simple utility method to load a gif, jpeg or png file 
> > 
> > Image Toolkit.getDefaultToolkit().getImage(url)
> > 
> > and place it in a java.awt.Image object.  At this point you can
create
> a 
> swing
> > JButton and hand it the Image.
> > 
> > Using Batik I'm looking for a way to load a svg into a
java.awt.Image,
> 
> Icon, 
> > BufferedImage or into some object that I can convert to an 
> java.awt.Image. 
> > While preserving the transparent background as well.
> 
> > Using a JSVGComponent is not the right solution since a JButton
> accepts 
> an 
> > Icon and not a component for attaching a common button image.  I
also 
> want to 
> > avoid transcoding the svg to a png file.
> 
>    BTW there isn't much difference between this route and using a PNG
> (for mid/high resolution the SVG will be smaller and in many
> cases more flexible).
> 
> > Is there a Batik API call to achieve this?
> 
>    If you mean a single 'high level' java call to do this, No.
> The code you have below is roughly the simplest version of
> the code.
> 
> > All I could find is this relatively complex solution...
> 
>    So I've often considered adding such a call but given
> than at least on the surface 90% of users of the transcoders
> API's want to tweak this or that parameter of the transcoding
> processes (at least width/height, often what part of canvas,
> contents of document, run onload handlers etc).  It's always 
> seemed like many users would end up needing something more. 
> Even below you seem to have some form of scale you are 
> manipulating (scaleX/Y), so such a simple call would not 
> have helped you much...
> 
>    SVG is not like a PNG, there is no real inherent pixel size.
> 
>    BTW if you want to keep the transparent background then
> I would suggest using TYPE_INT_ARGB instead of just RGB.
> 
> > String parser = XMLResourceDescriptor.getXMLParserClassName();
> > SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);
> > 
> > File file = new
File(MyClass.class.getResource("test.svg").getFile());
> > document = (SVGDocument) f.createDocument(file.toURL().toString());
> > 
> > // Build the tree and get the document dimensions
> > UserAgentAdapter userAgentAdapter = new UserAgentAdapter();
> > BridgeContext bridgeContext = new BridgeContext(userAgentAdapter);
> > GVTBuilder builder = new GVTBuilder();
> > GraphicsNode graphicsNode = builder.build(bridgeContext, document);
> > 
> > BufferedImage bufferedImage = new BufferedImage
> >          ((int)(bounds.getWidth() * scaleX), 
> >           (int)(bounds.getHeight() * scaleY), 
> >           BufferedImage.TYPE_INT_RGB);
> > Graphics2D g2d = (Graphics2D) bufferedImage.getGraphics();
> > graphicsNode.paint(g2d);
> > g2d.dispose();
> > 
> > return bufferedImage;
> > 
> > LEGAL NOTICE:
> > Unless expressly stated otherwise, this message is confidential and
> may 
> be 
> > privileged. It is intended for the addressee(s) only. Access to this

> e-mail by
> > anyone else is unauthorized. If you are not an addressee, any
> disclosure 
> or 
> > copying of the contents or any action taken (or not taken) in
reliance
> 
> on it 
> > is unauthorized and may be unlawful. If you are not an addressee,
> please 
> 
> > inform the sender immediately.
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [EMAIL PROTECTED]
> For additional commands, e-mail:
[EMAIL PROTECTED]
> 


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to