Convertir une image SVG en image JPEG à l'aide d'Apache Batik


J'essaie de convertir une image SVG en JPEG comme indiqué dans https://xmlgraphics.apache.org/batik/using/transcoder.html#createImage exemple. Voici le code:

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

Ci-Dessous est mon pompon.XML. J'essaie dans le projet spring boot:

<?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>

Je reçois l'exception suivante:

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)

J'ai plusieurs problèmes ici:

  1. Pourquoi la stacktrace d'exception dit "Source inconnue" et l'exception est si peu informative? J'ai googlé à ce sujet et lu que si les pots ne le font pas avoir des sources attachées à eux, l'exception peut être non informative. J'ai mis le code du plugin dans le pom pour ajouter la source. Mais ce n'est pas de travail.
  2. Qu'est-ce qui ne va pas dans le code batik pour ne pas convertir l'image svg en jpeg?
Author: Tunaki, 2016-06-18

2 answers

Je ne sais pas qui a publié cet artefact mais il a résolu ce problème pour moi

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

Ces classes sont incluses avec cet artefact

  • 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
 5
Author: ievgen, 2016-09-11 13:15:14

Ceci est un bug dans Apache Batik 1.8, référencé dans BATIK-1136 .

Le problème est le suivant: le JPEGTranscoder utilise l'API du fournisseur de services pour acquérir une instance d'un ImageWriter qui gère le format "image/jpeg". Cependant, la classe configurée dans l'artefact batik-codec à l'intérieur META-INF/services points une classe org.apache.batik.ext.awt.image.codec.imageio.ImageIOJPEGImageWriter cela apparemment n'a pas été publié dans le paquet final (car il existe dans le code source).

En tant que tel, il y a 2 solution.

Déclassement

Rétrograder à la version 1.7 avec:

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

Les classes ont été correctement publiées dans cette version.

Copier les classes nécessaires

Copiez les classes nécessaires d'Apache Batik dans vos propres sources. Dans un paquet org.apache.batik.ext.awt.image.codec.imageio, créez les deux classes suivantes.

Premier, 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);
    }
}

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


}

En conservant la version 1.8 et en ajoutant les classes ci-dessus, le code fonctionnera tel quel.

 2
Author: Tunaki, 2016-06-18 18:18:42