2010-03-22 5 views
15

Проще говоря, я ищу способ сделать ImageIcon из SVG-файла, используя библиотеку batik. Я не хочу сначала растрировать SVG на диск, я просто хочу, чтобы вытащить svg из файла jar и заставить его приземляться как элемент пользовательского интерфейса.Swing & Batik: Создайте ImageIcon из SVG-файла?

Я чувствую, что это должно быть достаточно легко, но батик-джавадоки не говорят мне, что мне нужно знать.

(Почему батик? Ну, мы уже использовали, поэтому мы не должны запустить другую библиотеку в прошлом легальным.)

ответ

18

Это действительно очень легко, просто не очень интуитивным.

Необходимо удлинить ImageTranscoder. В методе createImage вы выделяете BufferedImage, кешируете его как переменную-член и возвращаете. Метод writeImage пуст. И вам нужно будет добавить получателя для извлечения BufferedImage.

Это будет выглядеть примерно так:

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

Теперь, чтобы создать изображение, которое вы создаете экземпляр вашего транскодер и передать ему нужную ширину и высоту, установив TranscodingHints. Наконец, вы перекодируете из TranscoderInput в нулевую цель. Затем позвоните получателю на транскодер, чтобы получить изображение.

вызов выглядит примерно так:

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

Простой, правильно? (Да, верно. Только взял меня за 2 недели, чтобы понять это. Вздох.)

+3

Да, что сказал Девон. :) Вот мой класс SVGIcon, который в значительной степени делает это: http://mcc.id.au/2005/04/SVGIcon.java – heycam

+0

Начальная строка этого ответа запечатывает его для меня. Почему я не могу дать больше, чем +1 для этого ответа? Это ** точно **, что делает SO настолько хорошим. – kevinarpe

3

Я просто следовал подходу Девона с батиком 1.7

Однако для того, чтобы заставить его работать, я должен был сделать следующее дополнения к подсказкам объекта:

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

Похоже что-то было обновлено в XMLAbstractTranscoder батик (в http://svn.apache.org/repos/asf/xmlgraphics/batik/tags/batik-1_7/sources/org/apache/batik/transcoder/XMLAbstractTranscoder.java) с версией 1.7.

2

Если бы вы больше не хотите, чтобы включить зависимость от батика в вашем приложении вы можете преобразовать файл SVG непосредственно в Java2D с Flamingo SVG транскодер:

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

Он генерирует классы значок примерно эквивалент в размер для сжатого SVG-файла. Сгенерированный код не имеет внешней зависимости.

+0

Чтобы убедиться, ваш ответ заключается в том, что вместо того, чтобы пытаться читать и отображать svg во время выполнения (и включать все зависимости), другой подход заключается в перекодировке SVG в файл класса во время компиляции (только для системы сборки потребуется батик). Затем файл класса упаковывается вместе с приложением. – Ryan

+0

Да, это точно цель транскодера. –

0

Чтобы избежать параметров прохождения Dóm: transcoder.setTranscodingHints((Map<?, ?>) hints);

+0

Это должен быть комментарий для @John Doppelmann? Тогда не отвечайте, пожалуйста, используйте комментарии, если это возможно. – Manuel

0

Я попытался с помощью Девон и Джонсе предложения, которые почти работали для меня. Я должен был сделать некоторые трюки следующим образом:

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; 
    } 
    } 
} 
Смежные вопросы