?
Solved

Using Batik to obtain a BufferedImage of an SVG

Posted on 2003-03-27
17
Medium Priority
?
2,768 Views
Last Modified: 2007-12-19
We have a project which requires us to us SVG files as icons. We want to render the SVG to BufferedImages to allow the one time render of the Icons (the icons will not animate during their lifetime on the screen). We have investigated ways of trying this, and have kinda hit our heads against a brick wall. We have tried using JSVGCanvas, and using the get offscreen method provided, which does work, but that means for each icon, we have the bloat of a JSVGCanvas to contend with, which is not a good thing. We then looked at a way of using just one JSVGCanvas as a render platform for all icons, which was a better solution, but not ideal, as we don't need the full bloat of swing, just the SVG image rendered into a BufferedImage. Next we looked at ways of using the Rasterizer App, which rasterizes SVG to Jpeg etc... which is almost what we after, and sure enough, inside the code of JPEGTranscoder, it actually uses a BufferedImage to render, then saves this to JPEG file. We tried creating our own BufferedImageTranscoder, but fell over quite early.
Has anybody got any solutions to offer. Big money (ok points) is on offer!
0
Comment
Question by:DavyBoyHayes
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 8
  • 8
17 Comments
 
LVL 86

Expert Comment

by:CEHJ
ID: 8217009
>>We then looked at a way of using just one JSVGCanvas as a render platform for all icons

Can't you just use that to get a BufferedImage and then clone each one?
0
 

Author Comment

by:DavyBoyHayes
ID: 8217046
Each icon is different. What I meant here is that we had 1 JSVGCanvas, and we loaded the first SVG Document, then captured the BufferedImage, then loaded the next SVG Documetnt, and captured the next BufferedImage. It is a solution, but I am not happy with using Swing in the solution, as Swing can be quite bloated (although we are using Swing for the gui). I am trying to reduce the speed of loading and rendering.
0
 
LVL 86

Expert Comment

by:CEHJ
ID: 8217051
It seems to me you can just do the following:

a. add a listener to the JSVGCanvas to tell when a source is fully loaded
b. get the graphics context
c. create a new BufferedImage
d. get *its* graphics context
e. use the source context to draw the image to the destination (the BufferedImage)

Obviously that would be done in a loop
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 86

Expert Comment

by:CEHJ
ID: 8217062
Well, JSVGCanvas is inherently Swing isn't it? I'm not sure what other means are available. A lot of the overhead is probably xml parsing, which is a hit you'll take Swing or not.
0
 

Author Comment

by:DavyBoyHayes
ID: 8217074
Yes, that is the method we have at the minute. Unfortunately, we don't want to use a JSVGCanvas. We want to mimick the way that JSVGCanvas works, without having the Swing extra memory taken up.

Yes we want a single class whose responsibility is to take SVG documents and to return a BufferedImage. Ideally, the class would have a method

public BufferedImage renderSVGDocument(String strURI)

Who would return a BufferedImage on successful completion of a render, or null on non successful completion.
0
 
LVL 86

Expert Comment

by:CEHJ
ID: 8217107
I don't know if you saw my comment at 03:51 before posting your last, but i don't know of any non-Swing stuff.
0
 

Author Comment

by:DavyBoyHayes
ID: 8217118
One thought we have is to have all the SVG files pre-read and parsed, so we already SVGOMDocuments loaded, and all we have to do is maybe

public BufferedImage renderSVGDocument(SVGOMDocument doc)

So the process at run-time would be:

Application Start-up
~~~~~~~~~~~~~~~~~~~~
Load SVG Files
Convert each into SVGOMDocument

Icons Required
~~~~~~~~~~~~~~
Has BufferedImage been rendered for current resolution?
N: buffImage = RenderSVGDocument(doc);
Y: draw buffImage onto desktop

The only time the Icons will be required to get re-rendered is when the output resolution is changed (ie zoom-in/out or printing).
0
 
LVL 86

Expert Comment

by:CEHJ
ID: 8217154
Seems OK, but you don't make explicit whether *SVG* is to be re-rendered. That shouldn't be necessary, as you should be able to carry out transformations on the BufferedImages themselves i would have thought. Therefore i think SVG rendering would be a one time hit only.
0
 

Author Comment

by:DavyBoyHayes
ID: 8217198
The point behind the need for SVG icons rather than just standard raster ones, is that when the user zooms into the viewspace, the icon should not appear blocky, it should be smooth. Similarly, when printing out, if the print DPI is 720, then you have 10x's as much quality depth you can use. But if you are just dumping the BufferedImage at a transformed size, you are not using the full resolution of the printer, and this is bad for us. The point though for rendering the SVG to a BufferedImage is that while the user is moving round the screen in the current zoom level, they haven't got to wait for each icon to get re-rendered.
0
 
LVL 86

Expert Comment

by:CEHJ
ID: 8217221
Yes i see. You could *try* the approach i mentioned, using anti-aliasing and setting appropriate hints though.
0
 

Author Comment

by:DavyBoyHayes
ID: 8217255
We have tried this method, and we are looking for a more low level method. We haven't used the anti-aliasing and hints though in our method, but that will come when we finalise the method we will use.
0
 
LVL 86

Accepted Solution

by:
CEHJ earned 1000 total points
ID: 8217290
>>We haven't used the anti-aliasing and hints though in our method

You would only do that if you were *not* rescaling using SVG re-rendering. It wouldn't be necessary otherwise, as you'd still be in vector mode.
0
 

Author Comment

by:DavyBoyHayes
ID: 8219512
I have found the solution, and I am offering it to the global community. I thank you CEHJ for your suggestions, and whilst I don't feel that you've earned 500 points worth (though your answers are good), I cannot reduce the points now, so you will get them. Anyhoos, here goes...

Note the following code has been modified from org.apache.batik.apps.transcoder.* which was written by the Apache Software Foundation.

BufferedImageTranscoder.java
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/*
 * BufferedImageTranscoder.java
 *
 * Created on 13 March 2003, 14:09
 */

/**
 *
 * @author  hayesd
 */
/*****************************************************************************
 * Copyright (C) The Apache Software Foundation. All rights reserved.        *
 * ------------------------------------------------------------------------- *
 * This software is published under the terms of the Apache Software License *
 * version 1.1, a copy of which has been included with this distribution in  *
 * the LICENSE file.                                                         *
 *****************************************************************************/

//package org.apache.batik.transcoder.image;

import java.awt.Color;
import java.awt.image.BufferedImage;

import org.apache.batik.transcoder.image.ImageTranscoder;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.TranscodingHints;
import org.apache.batik.transcoder.image.resources.Messages;

/**
 * This class is an <tt>ImageTranscoder</tt> that produces a BufferedImage.
 *
 * @author <a href="mailto:d.hayes@abm-uk.com">David Hayes</a>
 *
 */
public class BufferedImageTranscoder extends ImageTranscoder {

    // Contains the last rendered BufferedImage. A solution found to obtain the image after the Transcode() step in ImageTranscoder

    private BufferedImage biLast = null;

    /**
     * Constructs a new transcoder that produces BufferedImage images.
     */

    public BufferedImageTranscoder() {
        hints.put(ImageTranscoder.KEY_BACKGROUND_COLOR, Color.white);
    }

    /**
     * Creates a new ARGB image with the specified dimension.
     * @param width the image width in pixels
     * @param height the image height in pixels
     */
    public BufferedImage createImage(int width, int height) {
        return new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    }

    // Note this method does not need the Transcoder Output. It allows you to loosly assume that TranscoderOutput is of type BufferedImageTranscoderOutput, purely because it doesn't really care.

    /**
     * Writes the specified image to the specified output.
     * @param img the image to write
     * @param output the output where to store the image
     * @param TranscoderException if an error occured while storing the image
     */
    public void writeImage(BufferedImage img, TranscoderOutput output)
    throws TranscoderException {
        biLast = img;
    }

    public BufferedImage getLastRendered(){
        return biLast;
    }
}

BufferedImageTranscoderOutput.java
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/*
 * BufferedImageTranscoderOutput.java
 *
 * Created on 13 March 2003, 15:59
 */

/**
 *
 * @author  hayesd
 */

//package org.apache.batik.transcoder;

import java.awt.image.*;

import org.apache.batik.transcoder.*;

public class BufferedImageTranscoderOutput extends TranscoderOutput {

    /** Creates a new instance of BufferedImageTranscoderOutput */
    public BufferedImageTranscoderOutput() {
    }

}

SVGtoBufferedImageConverter.java
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/*
 * SVGtoBufferedImageConverter.java
 *
 * Created on 14 March 2003, 10:58
 */

/**
 *
 * @author  hayesd
 */

import org.apache.batik.transcoder.Transcoder;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.FileOutputStream;

import java.awt.image.BufferedImage;

public class SVGtoBufferedImageConverter{
   
    /** Creates a new instance of SVGtoBufferedImageConverter */
    public SVGtoBufferedImageConverter() {
    }
   
    // Takes the inputfile, strFileName, and renders the file to a BufferedImage;
    public BufferedImage renderSVG(String strFileName) throws Exception {
       
        BufferedImageTranscoder transcoder = new BufferedImageTranscoder();
        BufferedImage outputImage = null;
        transcode(strFileName, transcoder);
        outputImage = ((BufferedImageTranscoder) transcoder).getLastRendered();
        return outputImage;
    }
   
    // The method which calls the transcode method in BufferedImageTranscoder
    protected void transcode(String inputFile,
    BufferedImageTranscoder transcoder)
    throws Exception {
        TranscoderInput input = null;
        TranscoderOutput output = null;
       
        InputStream in = new FileInputStream(inputFile);
        input = new TranscoderInput(in);
        output = new BufferedImageTranscoderOutput();
        transcoder.transcode(input, output);    
    }
 
}


And finally, your code which calls it...

myprogram.java (snippet)
~~~~~~~~~~~~~~~~~~~~~~~~

SVGtoBufferedImageConverter converter = new SVGtoBufferedImageConverter();
BufferedImage biSVG = null;
try{
   biSVG = converter.renderSVG("c:\myfilename.svg");
}
catch(Exception e)
{
}

Et voila! Jobs a good un!
0
 

Author Comment

by:DavyBoyHayes
ID: 8219523
I feel I must point out that this is far from a complete solution, but is infact a good starting block for most people to work from. If I find a more complete solution, I will try and let the community know!
0
 

Author Comment

by:DavyBoyHayes
ID: 8219544
Not the final answer I was after unfortunately, though not due to CEHJ's fault. I finally figured out the answer, and posted. I couldn't reduce the points to reflect the level of help CEHJ provided (would have happily given 50), so 500 it is. Ah well, I guess there are some games in life you win, and there are some you don't...

Thanks for the help CEHJ!
0
 
LVL 35

Expert Comment

by:girionis
ID: 8219757
 A bit late but it could be of help to anyone with similar problems. There is a package that deals with SVG graphics and it is compatible with Batik 1.1.1. Not sure if it will be of any help but you might be able to consult the source code (it's opne source anyway) and come up with a solution:

http://openmap.bbn.com/
and for the API: http://openmap.bbn.com/doc/api/ (you might want to take a look at the SVGFormatter).
0
 
LVL 86

Expert Comment

by:CEHJ
ID: 8220032
Interesting. Thanks for the points.
0

Featured Post

[Webinar] Lessons on Recovering from Petya

Skyport is working hard to help customers recover from recent attacks, like the Petya worm. This work has brought to light some important lessons. New malware attacks like this can take down your entire environment. Learn from others mistakes on how to prevent Petya like worms.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Java had always been an easily readable and understandable language.  Some relatively recent changes in the language seem to be changing this pretty fast, and anyone that had not seen any Java code for the last 5 years will possibly have issues unde…
Introduction This article is the first of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article explains our test automation goals. Then rationale is given for the tools we use to a…
Viewers learn how to read error messages and identify possible mistakes that could cause hours of frustration. Coding is as much about debugging your code as it is about writing it. Define Error Message: Line Numbers: Type of Error: Break Down…
This tutorial covers a step-by-step guide to install VisualVM launcher in eclipse.
Suggested Courses
Course of the Month8 days, 15 hours left to enroll

764 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question