如何从SVG获取BuffererImage?

我正在使用Batik来处理SVG图像。 有没有办法从SVG文件中获取java.awt.image.BufferedImage?

我知道有转码器,我可以将SVG转码为例如PNG,然后用ImageIO.read()加载PNG但我不想拥有临时文件。

使用Batik ,这样的事情:

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. String css = "svg {" + "shape-rendering: geometricPrecision;" + "text-rendering: geometricPrecision;" + "color-rendering: optimizeQuality;" + "image-rendering: optimizeQuality;" + "}"; File cssFile = File.createTempFile("batik-default-override-", ".css"); FileUtils.writeStringToFile(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().toString()); 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]; } 

一种非常简单的方法是使用TwelveMonkeys lib ,它为java的ImageIO添加了额外的图像类型支持

例如,您只需将这些添加到您的maven(或复制所需的jar子):

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

然后你就读了

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

要检查svg阅读器是否已正确注册,您可以打印出图像阅读器:

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

lib还支持其他参数,请参阅github上的自述文件 。

这就是我使用的。 它是BufferedImage的扩展,具有自己的静态工厂,可以在使用BufferedImage的任何地方使用。 我编写它以便任何对getScaledInstance(w,h,hint)的调用都将从SVG渲染,而不是栅格化图像。 这样做的副作用是缩放提示参数没有意义; 你可以传递0或DEFAULT。 它只是在请求图形数据时才会延迟渲染 – 所以加载/缩放周期不应该给你带来太多的开销。

编辑:我使用上面的CSS配置添加了支持以缩放质量提示。 编辑2:懒惰渲染不能始终如一地工作; 我将render()调用放入构造函数中。

它具有以下依赖项:

  • org.apache.xmlgraphics:蜡染阿尼姆
  • org.apache.xmlgraphics:蜡染桥
  • org.apache.xmlgraphics:蜡染GVT
  • org.apache.xmlgraphics:蜡染代码转换器
  • org.apache.xmlgraphics:蜡染UTIL
  • XML的API:XML的API-EXT
  • 共享记录:共享记录

当我做这个时,我用了蜡染1.8; 因人而异。

 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 { 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; } }