2010-03-22 11 views
15

En pocas palabras, estoy buscando una manera de hacer un ImageIcon desde un archivo SVG utilizando la biblioteca batik. No quiero tener que trazar primero el SVG en el disco, solo quiero sacar un svg del archivo jar y hacer que aterrice como un elemento UI.Swing & Batik: ¿Crear un ImageIcon desde un archivo SVG?

Siento que esto debería ser razonablemente fácil, pero los batik javadocs no me dicen lo que necesito saber.

(¿Por qué batik? Bueno, ya estamos a usarlo, así que no tienen que ejecutar otra biblioteca pasado legal.)

Respuesta

18

En realidad es muy fácil, pero no es muy intuitiva.

Necesita extender ImageTranscoder. En el método createImage asigna un BufferedImage, lo almacena en caché como una variable miembro y lo devuelve. El método writeImage está vacío. Y deberá agregar un getter para recuperar el BufferedImage.

Se verá algo como esto:

class MyTranscoder extends ImageTranscoder { 
     private BufferedImage image = null; 
     public BufferedImage createImage(int w, int h) { 
      image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); 
      return image; 
     } 
     public void writeImage(BufferedImage img, TranscoderOutput out) { 
     } 
     public BufferedImage getImage() { 
      return image; 
     } 
    } 

Ahora, para crear una imagen se crea una instancia de su transcodificador y pasarlo el ancho y la altura deseada mediante el establecimiento de TranscodingHints. Finalmente, transcodifica desde un TranscoderInput a un objetivo nulo. Luego, llame al getter en su transcodificador para obtener la imagen.

La llamada se ve algo como esto:

MyTranscoder transcoder = new MyTransCoder(); 
    TranscodingHints hints = new TranscodingHints(); 
    hints.put(ImageTranscoder.KEY_WIDTH, width); 
    hints.put(ImageTranscoder.KEY_HEIGHT, height); 
    transcoder.setTranscodingHints(hints); 
    transcoder.transcode(new TranscoderInput(url), null); 
    BufferedImage image = transcoder.getImage(); 

simple, ¿verdad? (Sí, claro. Sólo me tomó 2 semanas para darse cuenta de eso. Suspiro.)

+3

Sí, lo que Devon dijo. :) Aquí está mi clase SVGIcon, que prácticamente hace eso: http://mcc.id.au/2005/04/SVGIcon.java – heycam

+0

La línea de apertura de esta respuesta me la sella. ¿Por qué no puedo dar más de +1 por esta respuesta? Esto es ** exactamente ** lo que hace SO tan bueno. – kevinarpe

3

acabo seguido el enfoque de Devon con batik-1,7

Sin embargo, con el fin de hacer que funcione tuviera que hacer lo siguiente adiciones a los consejos objeto:

MyTranscoder transcoder =new MyTranscoder() 

DOMImplementation impl = SVGDOMImplementation.getDOMImplementation(); 
TranscodingHints hints = new TranscodingHints(); 
hints.put(ImageTranscoder.KEY_WIDTH, width); // e.g. width=new Float(300) 
hints.put(ImageTranscoder.KEY_HEIGHT,height);// e.g. height=new Float(75) 
hints.put(ImageTranscoder.KEY_DOM_IMPLEMENTATION, impl.getDOMImplementation()); 
hints.put(ImageTranscoder.KEY_DOCUMENT_ELEMENT_NAMESPACE_URI,SVGConstants.SVG_NAMESPACE_URI); 
hints.put(ImageTranscoder.KEY_DOCUMENT_ELEMENT_NAMESPACE_URI,SVGConstants.SVG_NAMESPACE_URI); 
hints.put(ImageTranscoder.KEY_DOCUMENT_ELEMENT, SVGConstants.SVG_SVG_TAG); 
hints.put(ImageTranscoder.KEY_XML_PARSER_VALIDATING, false); 

transcoder.setTranscodingHints(hints); 
TranscoderInput ti=new TranscoderInput(uri) 
transcoder.transcode(ti, null); 
BufferedImage image = transcoder.getImage(); 

parece que algo ha sido actualizado en XMLAbstractTranscoder de batik (http://svn.apache.org/repos/asf/xmlgraphics/batik/tags/batik-1_7/sources/org/apache/batik/transcoder/XMLAbstractTranscoder.java) con la versión 1.7.

2

Si alguna vez ya no desea incluir la dependencia de batik en su aplicación que puede transformar un archivo SVG directamente en Java2D con el Transcoder flamenco SVG:

http://ebourg.github.com/flamingo-svg-transcoder

Genera clases icono más o menos equivalentes en tamaño a un archivo SVG comprimido. El código generado no tiene dependencia externa.

+0

Para verificar, su respuesta es que en lugar de tratar de leer y representar svg en tiempo de ejecución (e incluir todas las dependencias) otro enfoque es transcodificar el SVG en un archivo de clase en tiempo de compilación (solo el sistema de compilación necesitaría batik). El archivo de clase se empaqueta con la aplicación. – Ryan

+0

Sí, ese es exactamente el propósito del transcodificador. –

0

Para evitar parámetros que pasa dom: transcoder.setTranscodingHints((Map<?, ?>) hints);

+0

Eso debería ser un comentario para @John Doppelmann ¿no? Entonces no lo "conteste", por favor, use comentarios si es posible. – Manuel

0

He intentado utilizar las sugerencias de Devon y John, que casi se trabajó para mí. Tuve que hacer algunos ajustes de la siguiente manera, siéntase libre de usar:

package com.corp.util; 

import static org.apache.batik.transcoder.SVGAbstractTranscoder.KEY_WIDTH; 
import static org.apache.batik.transcoder.XMLAbstractTranscoder.KEY_DOCUMENT_ELEMENT; 
import static org.apache.batik.transcoder.XMLAbstractTranscoder.KEY_DOCUMENT_ELEMENT_NAMESPACE_URI; 
import static org.apache.batik.transcoder.XMLAbstractTranscoder.KEY_DOM_IMPLEMENTATION; 
import static org.apache.batik.util.SVGConstants.SVG_NAMESPACE_URI; 
import static org.apache.batik.util.SVGConstants.SVG_SVG_TAG; 

import com.google.common.flogger.GoogleLogger; 

import org.apache.batik.anim.dom.SVGDOMImplementation; 
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 java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.IOException; 
import java.net.URL; 

import javax.annotation.concurrent.ThreadSafe; 
import javax.inject.Singleton; 

/** Loads SVG images from disk. See https://en.wikipedia.org/wiki/Scalable_Vector_Graphics. */ 
@Singleton 
@ThreadSafe 
public class SvgImageLoader { 

    private static final GoogleLogger logger = GoogleLogger.forEnclosingClass(); 

    /** 
    * Reads in an SVG image file and return it as a BufferedImage with the given width and a height 
    * where the original aspect ratio is preserved. 
    * 
    * @param url URL referencing the SVG image file, which is typically an XML file 
    * @param width width in pixels the returned BufferedImage should be 
    * 
    * @return a valid image representing the SVG file 
    * @throws IOException if the file cannot be parsed as valid SVG 
    */ 
    public static BufferedImage loadSvg(URL url, float width) throws IOException { 
    SvgTranscoder transcoder = new SvgTranscoder(); 
    transcoder.setTranscodingHints(getHints(width)); 
    try { 
     TranscoderInput input = new TranscoderInput(url.openStream()); 
     transcoder.transcode(input, null); 
    } catch (TranscoderException e) { 
     throw new IOException("Error parsing SVG file " + url, e); 
    } 
    BufferedImage image = transcoder.getImage(); 
    logger.atInfo().log("Read '%s' SVG image from disk requested with width=%.1f, sized as %dx%d pixels.", 
     new File(url.getFile()).getName(), width, image.getWidth(), image.getHeight()); 
    return image; 
    } 

    private static TranscodingHints getHints(float width) { 
    TranscodingHints hints = new TranscodingHints(); 
    hints.put(KEY_DOM_IMPLEMENTATION, SVGDOMImplementation.getDOMImplementation()); 
    hints.put(KEY_DOCUMENT_ELEMENT_NAMESPACE_URI, SVG_NAMESPACE_URI); 
    hints.put(KEY_DOCUMENT_ELEMENT, SVG_SVG_TAG); 
    hints.put(KEY_WIDTH, width); 
    return hints; 
    } 

    private static class SvgTranscoder extends ImageTranscoder { 

    private BufferedImage image = null; 

    @Override 
    public BufferedImage createImage(int width, int height) { 
     image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 
     return image; 
    } 

    @Override 
    public void writeImage(BufferedImage img, TranscoderOutput out) {} 

    BufferedImage getImage() { 
     return image; 
    } 
    } 
} 
Cuestiones relacionadas