Comment obtenir une BuffererImage à partir d’un SVG?

J’utilise Batik pour gérer les images SVG. Est-il possible d’obtenir un fichier java.awt.image.BufferedImage à partir d’un fichier SVG?

Je sais qu’il existe des transcodeurs, avec lesquels je pourrais transcoder le SVG en, par exemple, un PNG et ensuite charger ce PNG avec ImageIO.read () · Mais je ne veux pas avoir le fichier temporaire.

    En utilisant Batik , quelque chose comme ça:

    public static BufferedImage rasterize(File svgFile) throws IOException { final BufferedImage[] imagePointer = new BufferedImage[1]; // Rendering hints can't be set programatically, so // we override defaults with a temporary stylesheet. // These defaults emphasize quality and precision, and // are more similar to the defaults of other SVG viewers. // SVG documents can still override these defaults. Ssortingng css = "svg {" + "shape-rendering: geomesortingcPrecision;" + "text-rendering: geomesortingcPrecision;" + "color-rendering: optimizeQuality;" + "image-rendering: optimizeQuality;" + "}"; File cssFile = File.createTempFile("batik-default-override-", ".css"); FileUtils.writeSsortingngToFile(cssFile, css); TranscodingHints transcoderHints = new TranscodingHints(); transcoderHints.put(ImageTranscoder.KEY_XML_PARSER_VALIDATING, Boolean.FALSE); transcoderHints.put(ImageTranscoder.KEY_DOM_IMPLEMENTATION, SVGDOMImplementation.getDOMImplementation()); transcoderHints.put(ImageTranscoder.KEY_DOCUMENT_ELEMENT_NAMESPACE_URI, SVGConstants.SVG_NAMESPACE_URI); transcoderHints.put(ImageTranscoder.KEY_DOCUMENT_ELEMENT, "svg"); transcoderHints.put(ImageTranscoder.KEY_USER_STYLESHEET_URI, cssFile.toURI().toSsortingng()); try { TranscoderInput input = new TranscoderInput(new FileInputStream(svgFile)); ImageTranscoder t = new ImageTranscoder() { @Override public BufferedImage createImage(int w, int h) { return new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); } @Override public void writeImage(BufferedImage image, TranscoderOutput out) throws TranscoderException { imagePointer[0] = image; } }; t.setTranscodingHints(transcoderHints); t.transcode(input, null); } catch (TranscoderException ex) { // Requires Java 6 ex.printStackTrace(); throw new IOException("Couldn't convert " + svgFile); } finally { cssFile.delete(); } return imagePointer[0]; } 

    Un moyen très simple consiste à utiliser la bibliothèque TwelveMonkeys, qui ajoute une prise en charge de type d’image supplémentaire au java

    Ainsi, par exemple, il vous suffit d’append ces éléments à votre maven (ou de copier les pots nécessaires):

       com.twelvemonkeys.imageio imageio-batik  3.2.1   batik batik-transcoder 1.6-1  

    Et puis vous venez de le lire avec

     BufferedImage image = ImageIO.read(svg-file); 

    Pour vérifier si le lecteur svg est correctement enregistré, vous pouvez imprimer les lecteurs d’images:

     Iterator readers = ImageIO.getImageReadersByFormatName("SVG"); while (readers.hasNext()) { System.out.println("reader: " + readers.next()); } 

    La lib supporte également des parameters supplémentaires, voir readme sur github .

    C’est ce que j’utilise. C’est une extension de BufferedImage avec sa propre usine statique qui peut être utilisée partout où une image BufferedImage est utilisée. Je l’ai écrit pour que tout appel à getScaledInstance (w, h, hint) soit rendu à partir du fichier SVG, et non de l’image pixellisée. Un effet secondaire de cela est que le paramètre d’indicateur de mise à l’échelle n’a aucune signification; vous pouvez simplement passer 0 ou DEFAULT à cela. Le rendu est lent – uniquement lorsque des données graphiques sont demandées – le cycle de chargement / mise à l’échelle ne devrait donc pas vous occasionner trop de temps système.

    Edit: J’ai ajouté le support en utilisant la configuration CSS ci-dessus pour la mise à l’échelle des indicateurs de qualité. Edit 2: Le rendu paresseux ne fonctionnait pas de manière cohérente; Je mets l’appel render () dans le constructeur.

    Il a les dépendances suivantes:

    • org.apache.xmlgraphics: batik-anim
    • org.apache.xmlgraphics: batik-bridge
    • org.apache.xmlgraphics: batik-gvt
    • org.apache.xmlgraphics: batik-transcoder
    • org.apache.xmlgraphics: batik-util
    • xml-apis: xml-apis-ext
    • commons-logging: commons-logging

    Quand j’ai fait ça, j’ai utilisé le 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 scaleQuality = new HashMap(); static { Ssortingng css = "svg {" + "shape-rendering: %s;" + "text-rendering: %s;" + "color-rendering: %s;" + "image-rendering: %s;" + "}"; Ssortingng precise = "geomesortingcPrecision"; Ssortingng quality = "optimizeQuality"; Ssortingng speed = "optimizeSpeed"; Ssortingng crisp = "crispEdges"; Ssortingng legible = "optimizeLegibility"; Ssortingng auto = "auto"; scaleQuality.put(SCALE_DEFAULT, Ssortingng.format(css, auto, auto, auto, auto)); scaleQuality.put(SCALE_SMOOTH, Ssortingng.format(css, precise, precise, quality, quality)); scaleQuality.put(SCALE_REPLICATE, Ssortingng.format(css, speed, speed, speed, speed)); scaleQuality.put(SCALE_AREA_AVERAGING, Ssortingng.format(css, crisp, legible, auto, auto)); scaleQuality.put(SCALE_FAST, Ssortingng.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.toSsortingng(), 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(Ssortingng 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"); Ssortingng 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().toSsortingng()); } } 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; } }