Saltar al contenido

¿Cómo obtener una imagen con búfer de un SVG?

Solución:

Usando Batik, algo como esto:

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];

Una forma muy sencilla es utilizar la biblioteca TwelveMonkeys, que añade soporte de tipo de imagen adicional a las ImageIO

Entonces, por ejemplo, simplemente agregue estos a su maven (o copie los frascos necesarios):

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

Y luego lo lees con

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

Para comprobar si el lector de svg está registrado correctamente, puede imprimir los lectores de imágenes:

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

La biblioteca también admite parámetros adicionales, consulte el archivo Léame en github.

Esto es lo que uso. Es una extensión de BufferedImage con su propio static factory que se puede utilizar siempre que se utilice una imagen Buffered. Lo escribí para que cualquier llamada a getScaledInstance (w, h, hint) se procese desde el SVG, no desde la imagen rasterizada. Un efecto secundario de esto es que el parámetro de sugerencia de escala no tiene significado; puede simplemente pasar 0 o DEFAULT a eso.Se procesa de manera perezosa, solo cuando se solicitan datos gráficos, por lo que el ciclo de carga / escala no debería generar demasiada sobrecarga.

Editar: agregué soporte usando la configuración CSS anterior para escalar sugerencias de calidad. Edición 2: el renderizado diferido no funcionaba de forma coherente; Pongo la llamada render () en el constructor.

Tiene las siguientes dependencias:

  • 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

Cuando hice esto, usé 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 
        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;
    

Aquí puedes ver las comentarios y valoraciones de los usuarios

Al final de todo puedes encontrar los informes de otros gestores de proyectos, tú además eres capaz insertar el tuyo si te apetece.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *