使用Apache Batik将SVG图像转换为JPEG图像

时间:2016-06-18 17:15:32

标签: java maven batik

我正在尝试将SVG图像转换为JPEG,如https://xmlgraphics.apache.org/batik/using/transcoder.html#createImage示例所示。这是代码:

public void saveAsjpeg() throws Exception {

    // Create a JPEG transcoder
    JPEGTranscoder t = new JPEGTranscoder();

    // Set the transcoding hints.
    t.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, new Float(.8));

    // Create the transcoder input.
    String svgURI = new File(inputFilePath).toURL().toString();
    TranscoderInput input = new TranscoderInput(svgURI);

    // Create the transcoder output.
    OutputStream ostream = new FileOutputStream(outputFilePath);
    TranscoderOutput output = new TranscoderOutput(ostream);

    // Save the image.
    t.transcode(input, output);

    // Flush and close the stream.
    ostream.flush();
    ostream.close();
    System.exit(0);
}

下面是我的pom.xml。我正在尝试春季启动项目:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.4.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-transcoder</artifactId>
            <version>1.8</version>
        </dependency>

        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-codec</artifactId>
            <version>1.8</version>
        </dependency>

        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-svgpp</artifactId>
            <version>1.8</version>
        </dependency>

        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-rasterizer</artifactId>
            <version>1.8</version>
        </dependency>

        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-squiggle</artifactId>
            <version>1.8</version>
        </dependency>

        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>xmlgraphics-commons</artifactId>
            <version>2.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-ttf2svg</artifactId>
            <version>1.8</version>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <executions>
                    <execution>
                        <id>attach-sources</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
                <executions>
                    <execution>
                        <id>attach-javadocs</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>


</project>

我遇到了以下异常:

org.apache.batik.transcoder.TranscoderException: null
Enclosed Exception:
null
    at org.apache.batik.transcoder.image.ImageTranscoder.transcode(Unknown Source)
    at org.apache.batik.transcoder.XMLAbstractTranscoder.transcode(Unknown Source)
    at org.apache.batik.transcoder.SVGAbstractTranscoder.transcode(Unknown Source)
    at SaveToJPEG.saveAsjpeg(SaveToJPEG.java:31)
    at SaveToJPEG.main(SaveToJPEG.java:42)

我在这里有几个问题:

  1. 为什么异常堆栈跟踪显示“未知来源”并且异常是如此无法提供信息?我在Google上搜索并了解如果罐子上没有附加源,那么例外可能没有信息。我已将插件代码放在pom中以添加源代码。但这不起作用。
  2. 没有将svg图像转换为jpeg的蜡染代码有什么问题?

2 个答案:

答案 0 :(得分:6)

我不知道是谁发布了这个神器,但它已经为我解决了这个问题

<dependency>
    <groupId>fr.avianey.apache-xmlgraphics</groupId>
    <artifactId>batik</artifactId>
    <version>1.8</version>
</dependency>

这些类包含在此工件中

  • org.apache.batik.ext.awt.image.codec.imageio.ImageIOPNGImageWriter
  • org.apache.batik.ext.awt.image.codec.imageio.ImageIOTIFFImageWriter
  • org.apache.batik.ext.awt.image.codec.imageio.ImageIOJPEGImageWriter

答案 1 :(得分:3)

这是Apache Batik 1.8中的一个错误,在BATIK-1136中引用。

问题如下:JPEGTranscoder正在使用服务提供程序API来获取处理ImageWriter格式的"image/jpeg"实例。但是,在batik-codec points a class org.apache.batik.ext.awt.image.codec.imageio.ImageIOJPEGImageWriter内的工件META-INF/services中配置的类显然是wasn't released in the final package(因为它存在于源代码中)。

因此,有两种解决方案。

降级

使用以下标准降级至1.7版

<dependency>
    <groupId>org.apache.xmlgraphics</groupId>
    <artifactId>batik-transcoder</artifactId>
    <version>1.7</version>
</dependency>
<dependency>
    <groupId>org.apache.xmlgraphics</groupId>
    <artifactId>batik-codec</artifactId>
    <version>1.7</version>
</dependency>
<dependency>
    <groupId>org.apache.xmlgraphics</groupId>
    <artifactId>xmlgraphics-commons</artifactId>
    <version>2.1</version>
</dependency>

这个版本在这个版本中正确发布。

复制必要的课程

将必要的类从Apache Batik复制到您自己的源中。在包org.apache.batik.ext.awt.image.codec.imageio内,创建以下两个类。

首先,ImageIOImageWriter

package org.apache.batik.ext.awt.image.codec.imageio;

import java.awt.image.RenderedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.event.IIOWriteWarningListener;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageOutputStream;

import org.apache.batik.ext.awt.image.spi.ImageWriter;
import org.apache.batik.ext.awt.image.spi.ImageWriterParams;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class ImageIOImageWriter implements ImageWriter, IIOWriteWarningListener {

    private String targetMIME;

    /**
     * Main constructor.
     * @param mime the MIME type of the image format
     */
    public ImageIOImageWriter(String mime) {
        this.targetMIME = mime;
    }

    /**
     * @see ImageWriter#writeImage(java.awt.image.RenderedImage, java.io.OutputStream)
     */
    public void writeImage(RenderedImage image, OutputStream out) throws IOException {
        writeImage(image, out, null);
    }

    /**
     * @see ImageWriter#writeImage(java.awt.image.RenderedImage, java.io.OutputStream, ImageWriterParams)
     */
    public void writeImage(RenderedImage image, OutputStream out, 
            ImageWriterParams params) 
                throws IOException {
        Iterator iter;
        iter = ImageIO.getImageWritersByMIMEType(getMIMEType());
        javax.imageio.ImageWriter iiowriter = null;
        try {
            iiowriter = (javax.imageio.ImageWriter)iter.next();
            if (iiowriter != null) {
                iiowriter.addIIOWriteWarningListener(this);

                ImageOutputStream imgout = null;
                try {
                    imgout = ImageIO.createImageOutputStream(out);
                    ImageWriteParam iwParam = getDefaultWriteParam(iiowriter, image, params);

                    ImageTypeSpecifier type;
                    if (iwParam.getDestinationType() != null) {
                        type = iwParam.getDestinationType();
                    } else {
                        type = ImageTypeSpecifier.createFromRenderedImage(image);
                    }

                    //Handle metadata
                    IIOMetadata meta = iiowriter.getDefaultImageMetadata(
                            type, iwParam);
                    //meta might be null for some JAI codecs as they don't support metadata
                    if (params != null && meta != null) {
                        meta = updateMetadata(meta, params); 
                    }

                    //Write image
                    iiowriter.setOutput(imgout);
                    IIOImage iioimg = new IIOImage(image, null, meta);
                    iiowriter.write(null, iioimg, iwParam);
                } finally {
                    if (imgout != null) {
                        imgout.close();
                    }
                }
            } else {
                throw new UnsupportedOperationException("No ImageIO codec for writing " 
                        + getMIMEType() + " is available!");
            }
        } finally {
            if (iiowriter != null) {
                iiowriter.dispose();
            }
        }
    }

    /**
     * Returns the default write parameters for encoding the image.
     * @param iiowriter The IIO ImageWriter that will be used
     * @param image the image to be encoded
     * @param params the parameters for this writer instance
     * @return the IIO ImageWriteParam instance
     */
    protected ImageWriteParam getDefaultWriteParam(
            javax.imageio.ImageWriter iiowriter, RenderedImage image, 
            ImageWriterParams params) {
        ImageWriteParam param = iiowriter.getDefaultWriteParam();
        if ((params != null) && (params.getCompressionMethod() != null)) {
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            param.setCompressionType(params.getCompressionMethod());
        }
        return param; 
    }

    /**
     * Updates the metadata information based on the parameters to this writer.
     * @param meta the metadata
     * @param params the parameters
     * @return the updated metadata
     */
    protected IIOMetadata updateMetadata(IIOMetadata meta, ImageWriterParams params) {
        final String stdmeta = "javax_imageio_1.0";
        if (meta.isStandardMetadataFormatSupported()) {
            IIOMetadataNode root = (IIOMetadataNode)meta.getAsTree(stdmeta);
            IIOMetadataNode dim = getChildNode(root, "Dimension");
            IIOMetadataNode child;
            if (params.getResolution() != null) {
                child = getChildNode(dim, "HorizontalPixelSize");
                if (child == null) {
                    child = new IIOMetadataNode("HorizontalPixelSize");
                    dim.appendChild(child);
                }
                child.setAttribute("value", 
                        Double.toString(params.getResolution().doubleValue() / 25.4));
                child = getChildNode(dim, "VerticalPixelSize");
                if (child == null) {
                    child = new IIOMetadataNode("VerticalPixelSize");
                    dim.appendChild(child);
                }
                child.setAttribute("value", 
                        Double.toString(params.getResolution().doubleValue() / 25.4));
            }
            try {
                meta.mergeTree(stdmeta, root);
            } catch (IIOInvalidTreeException e) {
                throw new RuntimeException("Cannot update image metadata: " 
                            + e.getMessage());
            }
        }
        return meta;
    }

    /**
     * Returns a specific metadata child node
     * @param n the base node
     * @param name the name of the child
     * @return the requested child node
     */
    protected static IIOMetadataNode getChildNode(Node n, String name) {
        NodeList nodes = n.getChildNodes();
        for (int i = 0; i < nodes.getLength(); i++) {
            Node child = nodes.item(i);
            if (name.equals(child.getNodeName())) {
                return (IIOMetadataNode)child;
            }
        }
        return null;
    }

    /**
     * @see ImageWriter#getMIMEType()
     */
    public String getMIMEType() {
        return this.targetMIME;
    }

    /**
     * @see javax.imageio.event.IIOWriteWarningListener#warningOccurred(javax.imageio.ImageWriter, int, java.lang.String)
     */
    public void warningOccurred(javax.imageio.ImageWriter source, 
            int imageIndex, String warning) {
        System.err.println("Problem while writing image using ImageI/O: " 
                + warning);
    }
}

然后ImageIOJPEGImageWriter

package org.apache.batik.ext.awt.image.codec.imageio;

import java.awt.image.RenderedImage;

import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;

import org.apache.batik.ext.awt.image.codec.imageio.ImageIOImageWriter;
import org.apache.batik.ext.awt.image.spi.ImageWriterParams;

public class ImageIOJPEGImageWriter extends ImageIOImageWriter {

    private static final String JPEG_NATIVE_FORMAT = "javax_imageio_jpeg_image_1.0";

    /**
     * Main constructor.
     */
    public ImageIOJPEGImageWriter() {
        super("image/jpeg");
    }

    /** {@inheritDoc} */
    @Override
    protected IIOMetadata updateMetadata(IIOMetadata meta, ImageWriterParams params) {
        //ImageIODebugUtil.dumpMetadata(meta);
        if (JPEG_NATIVE_FORMAT.equals(meta.getNativeMetadataFormatName())) {
            meta = addAdobeTransform(meta);

            IIOMetadataNode root = (IIOMetadataNode)meta.getAsTree(JPEG_NATIVE_FORMAT);

            IIOMetadataNode jv = getChildNode(root, "JPEGvariety");
            if (jv == null) {
                jv = new IIOMetadataNode("JPEGvariety");
                root.appendChild(jv);
            }
            IIOMetadataNode child;
            if (params.getResolution() != null) {
                child = getChildNode(jv, "app0JFIF");
                if (child == null) {
                    child = new IIOMetadataNode("app0JFIF");
                    jv.appendChild(child);
                }
                //JPEG gets special treatment because there seems to be a bug in
                //the JPEG codec in ImageIO converting the pixel size incorrectly
                //(or not at all) when using standard metadata format.
                child.setAttribute("majorVersion", null);
                child.setAttribute("minorVersion", null);
                child.setAttribute("resUnits", "1"); //dots per inch
                child.setAttribute("Xdensity", params.getResolution().toString());
                child.setAttribute("Ydensity", params.getResolution().toString());
                child.setAttribute("thumbWidth", null);
                child.setAttribute("thumbHeight", null);

            }

            try {
                meta.setFromTree(JPEG_NATIVE_FORMAT, root);
            } catch (IIOInvalidTreeException e) {
                throw new RuntimeException("Cannot update image metadata: "
                            + e.getMessage(), e);
            }

            //ImageIODebugUtil.dumpMetadata(meta);
        }

        return meta;
    }

    private static IIOMetadata addAdobeTransform(IIOMetadata meta) {
        // add the adobe transformation (transform 1 -> to YCbCr)
        IIOMetadataNode root = (IIOMetadataNode)meta.getAsTree(JPEG_NATIVE_FORMAT);

        IIOMetadataNode markerSequence = getChildNode(root, "markerSequence");
        if (markerSequence == null) {
            throw new RuntimeException("Invalid metadata!");
        }

        IIOMetadataNode adobeTransform = getChildNode(markerSequence, "app14Adobe");
        if (adobeTransform == null) {
            adobeTransform = new IIOMetadataNode("app14Adobe");
            adobeTransform.setAttribute("transform" , "1"); // convert RGB to YCbCr
            adobeTransform.setAttribute("version", "101");
            adobeTransform.setAttribute("flags0", "0");
            adobeTransform.setAttribute("flags1", "0");

            markerSequence.appendChild(adobeTransform);
        } else {
            adobeTransform.setAttribute("transform" , "1");
        }

        try {
            meta.setFromTree(JPEG_NATIVE_FORMAT, root);
        } catch (IIOInvalidTreeException e) {
            throw new RuntimeException("Cannot update image metadata: "
                        + e.getMessage(), e);
        }
        return meta;
    }

    /** {@inheritDoc} */
    @Override
    protected ImageWriteParam getDefaultWriteParam(
            ImageWriter iiowriter, RenderedImage image,
            ImageWriterParams params) {
        JPEGImageWriteParam param = new JPEGImageWriteParam(iiowriter.getLocale());
        param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        param.setCompressionQuality(params.getJPEGQuality());
        if (params.getCompressionMethod() != null
                && !"JPEG".equals(params.getCompressionMethod())) {
            throw new IllegalArgumentException(
                    "No compression method other than JPEG is supported for JPEG output!");
        }
        if (params.getJPEGForceBaseline()) {
            param.setProgressiveMode(JPEGImageWriteParam.MODE_DISABLED);
        }
        return param;
    }


}

保留1.8版本并添加上面的类,代码将按原样运行。