2010-03-22 10 views
15

In poche parole, sto cercando un modo per creare un ImageIcon da un file SVG usando la libreria batik. Non voglio dover rasterizzare prima l'SVG su disco, voglio solo essere in grado di estrarre un file svg dal file jar e farlo atterrare come elemento dell'interfaccia utente.Swing & Batik: crea un ImageIcon da un file SVG?

Credo che questo dovrebbe essere ragionevolmente facile, ma i javadoc batik non mi dicono cosa devo sapere.

(Perché batik? Beh, stiamo già usando, quindi non c'è bisogno di correre un'altra libreria passato legale.)

risposta

18

E 'davvero molto semplice, basta non molto intuitiva.

È necessario estendere ImageTranscoder. Nel metodo createImage si assegna uno BufferedImage, lo si memorizza come variabile membro e lo si restituisce. Il metodo writeImage è vuoto. E dovrai aggiungere un getter per recuperare lo BufferedImage.

Si sarà simile a questo:

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

Ora, per creare un'immagine si crea un'istanza del transcoder e passare la larghezza e l'altezza desiderata impostando TranscodingHints. Infine si transcodifica da un TranscoderInput a un target Null. Quindi chiama il getter sul tuo transcoder per ottenere l'immagine.

La chiamata sembra qualcosa di simile:

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(); 

Semplice, no? (Sì, giusto. Solo mi ha portato 2 settimane per capirlo. Sigh.)

+3

Sì, quello che Devon ha detto. :) Ecco la mia classe SVGIcon che praticamente lo fa: http://mcc.id.au/2005/04/SVGIcon.java – heycam

+0

La riga di apertura di questa risposta lo sigilla per me. Perché non posso dare più di +1 per questa risposta? Questo è ** esattamente ** ciò che rende così buono. – kevinarpe

3

ho appena seguito l'approccio di Devon con Batik-1.7

Tuttavia, al fine di farlo funzionare ho dovuto fare la seguente aggiunte ai suggerimenti oggetto:

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(); 

Sembra che qualcosa è stato aggiornato in XMLAbstractTranscoder di batik (http://svn.apache.org/repos/asf/xmlgraphics/batik/tags/batik-1_7/sources/org/apache/batik/transcoder/XMLAbstractTranscoder.java) con la versione 1.7.

2

Se mai non si desidera includere la dipendenza Batik nella vostra applicazione è possibile trasformare un file in formato SVG direttamente nella Java2D con il Transcoder Flamingo SVG:

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

Genera classi icona o meno equivalente a dimensione in un file SVG compresso. Il codice generato non ha dipendenze esterne.

+0

Per verificare, la tua risposta è che invece di provare a leggere e visualizzare il file SVG in runtime (e includere tutte le dipendenze) un altro approccio è quello di transcodificare SVG in un file di classe in fase di compilazione (solo il sistema di compilazione avrebbe bisogno di batik). Il file di classe viene quindi pacchettizzato con l'applicazione. – Ryan

+0

Sì, questo è esattamente lo scopo del transcodificatore. –

0

Per evitare i parametri di passaggio dom: transcoder.setTranscodingHints((Map<?, ?>) hints);

+0

Questo dovrebbe essere un commento per @John Doppelmann giusto? Quindi non "rispondere" per favore, usa i commenti se possibile. – Manuel

0

Ho provato ad utilizzare i suggerimenti del Devon e Giovanni, che ha quasi funzionato per me. Ho dovuto apportare alcune modifiche come segue, sentitevi liberi di usare:

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; 
    } 
    } 
} 
Problemi correlati