Swing & Batik: создать ImageIcon из файла SVG?

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

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

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


person Electrons_Ahoy    schedule 22.03.2010    source источник


Ответы (5)


Это действительно очень просто, просто не очень интуитивно понятно.

Вам нужно расширить 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 недели, чтобы понять это. Эх.)

person Devon_C_Miller    schedule 22.03.2010
comment
Да, то, что сказал Девон. :) Вот мой класс SVGIcon, который в значительной степени делает это: mcc.id.au/2005/ 04/SVGIcon.java - person heycam; 23.03.2010
comment
Было бы неплохо узнать, в каком пакете должны быть эти классы. - person mmm; 27.04.2020
comment
@ ммм, это было давно, но это должно быть что-то вроде org.apache.batik.transcoder или его подпакета. - person Devon_C_Miller; 04.06.2020

Я только что последовал за подходом Девона с Batik-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 батика ( .java" rel="nofollow">http://svn.apache.org/repos/asf/xmlgraphics/batik/tags/batik-1_7/sources/org/apache/batik/transcoder/XMLAbstractTranscoder.java ) с версией 1.7.

person John Doppelmann    schedule 09.07.2011

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

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

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

person Emmanuel Bourg    schedule 17.06.2012
comment
Чтобы проверить, ваш ответ заключается в том, что вместо того, чтобы пытаться читать и отображать SVG во время выполнения (и включать все зависимости), другой подход состоит в том, чтобы перекодировать SVG в файл класса во время компиляции (батик потребуется только для системы сборки). Затем файл класса упаковывается вместе с приложением. - person Ryan; 15.04.2015

Чтобы избежать передачи параметров dom : transcoder.setTranscodingHints((Map<?, ?>) hints);

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

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

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;
    }
  }
}
person Dov Wasserman    schedule 20.04.2016