Esto es lo que yo uso. Es una extensión de BufferedImage con su propia fábrica estática que se puede utilizar en cualquier lugar donde se utilice una imagen Buffered. Lo escribí para que cualquier llamada a getScaledInstance (w, h, hint) se realice desde SVG, no desde la imagen rasterizada.
Un efecto secundario de esto es que el parámetro de sugerencia de escala no tiene significado; simplemente puede pasar 0 o DEFAULT a eso.
Se renderiza de forma perezosa, solo cuando se solicitan datos de gráficos, por lo que el ciclo de carga/escala no debe generar demasiada sobrecarga.
Editar: He añadido soporte utilizando la configuración de CSS anterior para escalar consejos de calidad. Editar 2: La prestación de latencia no funcionaba de forma coherente; Pongo la llamada a render() en el constructor.
Tiene las siguientes dependencias:
- org.apache.xmlgraphics:
- org.apache.xmlgraphics batik-anim batik-puente
- org.apache.xmlgraphics batik-GVT
- org.apache.xmlgraphics batik-transcodificador
- org.apache.xmlgraphics: batik-util
- xml-apis: XML-API-ext
- commons-logging: commons-logging
Cuando hice esto, yo solía batik 1,8; YMMV.
import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.apache.batik.anim.dom.SAXSVGDocumentFactory;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.TranscodingHints;
import org.apache.batik.transcoder.image.ImageTranscoder;
import org.apache.batik.util.SVGConstants;
import org.apache.batik.util.XMLResourceDescriptor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.svg.SVGDocument;
public class SVGImage extends BufferedImage {
private static class BufferedImageTranscoder extends ImageTranscoder {
private BufferedImage image = null;
@Override
public BufferedImage createImage(int arg0, int arg1) {
return image;
}
private void setImage(BufferedImage image) {
this.image = image;
}
@Override
public void writeImage(BufferedImage arg0, TranscoderOutput arg1) throws TranscoderException {
}
}
final static GVTBuilder builder = new GVTBuilder();
final static SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName());
final static UserAgent userAgent = new UserAgentAdapter();
final static DocumentLoader loader = new DocumentLoader(userAgent);
final static BridgeContext bridgeContext = new BridgeContext(userAgent, loader);
static {
bridgeContext.setDynamicState(BridgeContext.STATIC);
}
final static private Log log = LogFactory.getLog(SVGImage.class);
private static final Map<Integer, String> scaleQuality = new HashMap<Integer, String>();
static {
String css = "svg {" +
"shape-rendering: %s;" +
"text-rendering: %s;" +
"color-rendering: %s;" +
"image-rendering: %s;" +
"}";
String precise = "geometricPrecision";
String quality = "optimizeQuality";
String speed = "optimizeSpeed";
String crisp = "crispEdges";
String legible = "optimizeLegibility";
String auto = "auto";
scaleQuality.put(SCALE_DEFAULT, String.format(css, auto, auto, auto, auto));
scaleQuality.put(SCALE_SMOOTH, String.format(css, precise, precise, quality, quality));
scaleQuality.put(SCALE_REPLICATE, String.format(css, speed, speed, speed, speed));
scaleQuality.put(SCALE_AREA_AVERAGING, String.format(css, crisp, legible, auto, auto));
scaleQuality.put(SCALE_FAST, String.format(css, speed, speed, speed, speed));
}
final static BufferedImageTranscoder transcoder = new BufferedImageTranscoder();
public static SVGImage fromSvg(URL resource) throws IOException {
InputStream rs = null;
try {
rs = resource.openStream();
SVGDocument svg = factory.createSVGDocument(resource.toString(), rs);
return fromSvgDocument(resource, svg);
} finally {
if (rs != null) {
try { rs.close(); } catch (IOException ioe) {}
}
}
}
public static SVGImage fromSvgDocument(URL resource, SVGDocument doc) {
GraphicsNode graphicsNode = builder.build(bridgeContext, doc);
Double width = graphicsNode.getBounds().getWidth();
Double height = graphicsNode.getBounds().getHeight();
return new SVGImage(resource, doc, width.intValue(), height.intValue(), SCALE_DEFAULT);
}
boolean hasRendered = false;
private int scalingHint = SCALE_DEFAULT;
final SVGDocument svg;
final URL svgUrl;
private SVGImage(URL resource, SVGDocument doc, int width, int height, int hints) {
super(width, height, TYPE_INT_ARGB);
scalingHint = hints;
svgUrl = resource;
svg = doc;
render();
}
@Override
public void coerceData(boolean isAlphaPremultiplied) {
if (!hasRendered) { render(); }
super.coerceData(isAlphaPremultiplied);
}
@Override
public WritableRaster copyData(WritableRaster outRaster) {
if (!hasRendered) { render(); }
return super.copyData(outRaster);
}
private File createCSS(String css) {
FileWriter cssWriter = null;
File cssFile = null;
try {
cssFile = File.createTempFile("batik-default-override-", ".css");
cssFile.deleteOnExit();
cssWriter = new FileWriter(cssFile);
cssWriter.write(css);
} catch(IOException ioe) {
log.warn("Couldn't write stylesheet; SVG rendered with Batik defaults");
} finally {
if (cssWriter != null) {
try {
cssWriter.flush();
cssWriter.close();
} catch (IOException ioe) {}
}
}
return cssFile;
}
@Override
public WritableRaster getAlphaRaster() {
if (!hasRendered) { render(); }
return super.getAlphaRaster();
}
@Override
public Raster getData() {
if (!hasRendered) { render(); }
return super.getData();
}
@Override
public Graphics getGraphics() {
if (!hasRendered) { render(); }
return super.getGraphics();
}
public Image getScaledInstance(int width, int height, int hints) {
SVGImage newImage = new SVGImage(svgUrl, svg, width, height, hints);
return newImage;
}
private void render() {
TranscodingHints hints = new TranscodingHints();
hints.put(ImageTranscoder.KEY_WIDTH, new Float(getWidth()));
hints.put(ImageTranscoder.KEY_HEIGHT, new Float(getHeight()));
hints.put(ImageTranscoder.KEY_XML_PARSER_VALIDATING, Boolean.FALSE);
hints.put(ImageTranscoder.KEY_DOM_IMPLEMENTATION, svg.getImplementation());
hints.put(ImageTranscoder.KEY_DOCUMENT_ELEMENT_NAMESPACE_URI, SVGConstants.SVG_NAMESPACE_URI);
hints.put(ImageTranscoder.KEY_DOCUMENT_ELEMENT, "svg");
String css = scaleQuality.get(scalingHint);
File cssFile = null;
if (css != null) {
cssFile = createCSS(css);
if (cssFile != null) {
hints.put(ImageTranscoder.KEY_USER_STYLESHEET_URI, cssFile.toURI().toString());
}
}
transcoder.setTranscodingHints(hints);
transcoder.setImage(this);
// This may be a re-render, if the scaling quality hint has changed.
// As such, we force the image into overwrite mode, and kick it back when we're done/fail
Graphics2D gfx = (Graphics2D) super.getGraphics();
Composite savedComposite = gfx.getComposite();
gfx.setComposite(AlphaComposite.Clear);
try {
transcoder.transcode(new TranscoderInput(svg), null);
hasRendered = true;
} catch (TranscoderException te) {
log.warn("Could not transcode " + svgUrl.getPath() + " to raster image; you're going to get a blank BufferedImage of the correct size.");
} finally {
gfx.setComposite(savedComposite);
if (cssFile != null) {
cssFile.delete();
}
}
}
public void setScalingHint(int hint) {
this.scalingHint = hint;
// Forces a re-render
this.hasRendered = false;
}
}
existe la ventaja de crear una matriz de imagePointer frente a la asignación de imagePointer a una variable local como esta http://bbgen.net/blog/2011/06/java-svg-to-bufferedimage/? –
Realmente no. Es solo una solución para evitar errores de compilación en el método 'writeImage'. – elias
Ah, genial. ¡Gracias por verificar! –