2012-07-11 19 views
14

Tôi đang sử dụng Batik để xử lý hình ảnh SVG. Có cách nào để có được một java.awt.image.BufferedImage từ một tập tin SVG?Làm thế nào để có được một BuffererImage từ một SVG?

Tôi biết có bộ chuyển mã, mà tôi có thể chuyển mã SVG thành, ví dụ, PNG và sau đó tải PNG đó bằng ImageIO.read() · Nhưng tôi không muốn có tệp tạm thời.

Trả lời

17

Sử dụng Batik, một cái gì đó như thế này:

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]; 
} 
+0

có lợi thế khi tạo một mảng imagePointer hay chỉ gán imagePointer cho biến cục bộ như http://bbgen.net/blog/2011/06/java-svg-to-bufferedimage/ này? –

+0

Thực tế là không. Nó chỉ là một cách giải quyết để tránh các lỗi biên dịch trong phương thức 'writeImage'. – elias

+0

Ah tuyệt. Cảm ơn bạn đã xác minh! –

2

Đây là những gì tôi sử dụng. Đó là một phần mở rộng của BufferedImage với nhà máy tĩnh riêng của nó có thể được sử dụng bất cứ nơi nào một BufferedImage được sử dụng. Tôi đã viết nó để bất kỳ cuộc gọi nào đến getScaledInstance (w, h, gợi ý) sẽ hiển thị từ SVG, chứ không phải hình ảnh bị rasterized. Một tác dụng phụ của điều này là tham số gợi ý mở rộng không có ý nghĩa; bạn chỉ có thể vượt qua 0 hoặc DEFAULT cho điều đó. Nó ám chỉ một cách lười biếng - chỉ khi dữ liệu đồ họa được yêu cầu - vì vậy chu kỳ tải/tỷ lệ không nên cung cấp cho bạn quá nhiều chi phí.

Chỉnh sửa: Tôi đã thêm hỗ trợ bằng cách sử dụng cấu hình CSS ở trên để gợi ý chất lượng mở rộng. Chỉnh sửa 2: Hiển thị hình ảnh không hoạt động liên tục; Tôi đặt gọi render() vào hàm tạo.

Nó có phụ thuộc sau:

  • org.apache.xmlgraphics: batik-anim
  • org.apache.xmlgraphics: batik cầu
  • org.apache.xmlgraphics: batik-GVT
  • org.apache.xmlgraphics: batik-chuyển mã
  • org.apache.xmlgraphics: batik-util
  • xml-apis: xml-apis-ext
  • commons-logging: commons-logging

Khi tôi thực hiện điều này, tôi đã sử dụng 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; 
    } 
} 
+0

Ngẫu nhiên, tôi nghĩ rằng tôi sẽ ăn cắp gợi ý chuyển mã CSS ở trên để mô phỏng các gợi ý mở rộng từ BufferedImage. – Fordi

10

Một cách rất dễ dàng là sử dụng TwelveMonkeys lib mà thêm hỗ trợ thêm loại hình ảnh để ImageIO

Vì vậy, ví dụ bạn chỉ cần thêm chúng vào maven của bạn (hoặc sao chép các lọ cần thiết) của java:

<dependency> 
     <groupId>com.twelvemonkeys.imageio</groupId> 
     <artifactId>imageio-batik</artifactId> <!-- svg --> 
     <version>3.2.1</version> 
    </dependency> 
    <dependency> 
     <groupId>batik</groupId> 
     <artifactId>batik-transcoder</artifactId> 
     <version>1.6-1</version> 
    </dependency> 

Và sau đó bạn chỉ đọc nó với

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

Để kiểm tra xem người đọc svg được đăng ký một cách chính xác bạn có thể in ra các độc giả hình ảnh:

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

Các lib cũng hỗ trợ params bổ sung, xem readme on github.

+0

Đây là thư điện tử lạ. Nó không có tài liệu trực tuyến. Nó không có ví dụ. Nó có vấn đề với sự phụ thuộc (xem bài viết của bạn với bên thứ ba). Và nó có vấn đề với biểu tượng thay đổi kích cỡ tải. Tôi đã được dẫn đến một số lượng nhỏ mã và hối hận. – JayDi

Các vấn đề liên quan