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:
- 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.
- Qu'est-ce qui ne va pas dans le code batik pour ne pas convertir l'image svg en jpeg?
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
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.