DavyBoyHayes
asked on
Using Batik to obtain a BufferedImage of an SVG
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!
Has anybody got any solutions to offer. Big money (ok points) is on offer!
ASKER
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.
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
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
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.
ASKER
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.
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.
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.
ASKER
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(SVGOMDoc ument 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).
public BufferedImage renderSVGDocument(SVGOMDoc
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).
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.
ASKER
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.
Yes i see. You could *try* the approach i mentioned, using anti-aliasing and setting appropriate hints though.
ASKER
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.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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.tran scoder.* which was written by the Apache Software Foundation.
BufferedImageTranscoder.ja va
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~
/*
* BufferedImageTranscoder.ja va
*
* 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.transcode r.image;
import java.awt.Color;
import java.awt.image.BufferedIma ge;
import org.apache.batik.transcode r.image.Im ageTransco der;
import org.apache.batik.transcode r.Transcod erExceptio n;
import org.apache.batik.transcode r.Transcod erOutput;
import org.apache.batik.transcode r.Transcod ingHints;
import org.apache.batik.transcode r.image.re sources.Me ssages;
/**
* This class is an <tt>ImageTranscoder</tt> that produces a BufferedImage.
*
* @author <a href="mailto:d.hayes@abm-u k.com">Dav id 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_BACKGR OUND_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 BufferedImageTranscoderOut put, 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;
}
}
BufferedImageTranscoderOut put.java
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~
/*
* BufferedImageTranscoderOut put.java
*
* Created on 13 March 2003, 15:59
*/
/**
*
* @author hayesd
*/
//package org.apache.batik.transcode r;
import java.awt.image.*;
import org.apache.batik.transcode r.*;
public class BufferedImageTranscoderOut put extends TranscoderOutput {
/** Creates a new instance of BufferedImageTranscoderOut put */
public BufferedImageTranscoderOut put() {
}
}
SVGtoBufferedImageConverte r.java
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~
/*
* SVGtoBufferedImageConverte r.java
*
* Created on 14 March 2003, 10:58
*/
/**
*
* @author hayesd
*/
import org.apache.batik.transcode r.Transcod er;
import org.apache.batik.transcode r.Transcod erInput;
import org.apache.batik.transcode r.Transcod erOutput;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.FileOutputStream;
import java.awt.image.BufferedIma ge;
public class SVGtoBufferedImageConverte r{
/** Creates a new instance of SVGtoBufferedImageConverte r */
public SVGtoBufferedImageConverte r() {
}
// 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).getLastRendere d();
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 BufferedImageTranscoderOut put();
transcoder.transcode(input , output);
}
}
And finally, your code which calls it...
myprogram.java (snippet)
~~~~~~~~~~~~~~~~~~~~~~~~
SVGtoBufferedImageConverte r converter = new SVGtoBufferedImageConverte r();
BufferedImage biSVG = null;
try{
biSVG = converter.renderSVG("c:\my filename.s vg");
}
catch(Exception e)
{
}
Et voila! Jobs a good un!
Note the following code has been modified from org.apache.batik.apps.tran
BufferedImageTranscoder.ja
~~~~~~~~~~~~~~~~~~~~~~~~~~
/*
* BufferedImageTranscoder.ja
*
* 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.transcode
import java.awt.Color;
import java.awt.image.BufferedIma
import org.apache.batik.transcode
import org.apache.batik.transcode
import org.apache.batik.transcode
import org.apache.batik.transcode
import org.apache.batik.transcode
/**
* This class is an <tt>ImageTranscoder</tt> that produces a BufferedImage.
*
* @author <a href="mailto:d.hayes@abm-u
*
*/
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.
}
/**
* 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 BufferedImageTranscoderOut
/**
* 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;
}
}
BufferedImageTranscoderOut
~~~~~~~~~~~~~~~~~~~~~~~~~~
/*
* BufferedImageTranscoderOut
*
* Created on 13 March 2003, 15:59
*/
/**
*
* @author hayesd
*/
//package org.apache.batik.transcode
import java.awt.image.*;
import org.apache.batik.transcode
public class BufferedImageTranscoderOut
/** Creates a new instance of BufferedImageTranscoderOut
public BufferedImageTranscoderOut
}
}
SVGtoBufferedImageConverte
~~~~~~~~~~~~~~~~~~~~~~~~~~
/*
* SVGtoBufferedImageConverte
*
* Created on 14 March 2003, 10:58
*/
/**
*
* @author hayesd
*/
import org.apache.batik.transcode
import org.apache.batik.transcode
import org.apache.batik.transcode
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.FileOutputStream;
import java.awt.image.BufferedIma
public class SVGtoBufferedImageConverte
/** Creates a new instance of SVGtoBufferedImageConverte
public SVGtoBufferedImageConverte
}
// 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)
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 BufferedImageTranscoderOut
transcoder.transcode(input
}
}
And finally, your code which calls it...
myprogram.java (snippet)
~~~~~~~~~~~~~~~~~~~~~~~~
SVGtoBufferedImageConverte
BufferedImage biSVG = null;
try{
biSVG = converter.renderSVG("c:\my
}
catch(Exception e)
{
}
Et voila! Jobs a good un!
ASKER
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!
ASKER
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!
Thanks for the help CEHJ!
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).
http://openmap.bbn.com/
and for the API: http://openmap.bbn.com/doc/api/ (you might want to take a look at the SVGFormatter).
Interesting. Thanks for the points.
Can't you just use that to get a BufferedImage and then clone each one?