package org.apache.xmlgraphics.image.writer.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.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.apache.xmlgraphics.image.writer.ImageWriter;
import org.apache.xmlgraphics.image.writer.ImageWriterParams;
import org.apache.xmlgraphics.image.writer.MultiImageWriter;
import org.apache.xmlgraphics.image.writer.ResolutionUnit;
import org.apache.xmlgraphics.util.UnitConv;
public class ImageIOImageWriter implements ImageWriter, IIOWriteWarningListener {
private static final String DIMENSION = "Dimension";
private static final String VERTICAL_PIXEL_SIZE = "VerticalPixelSize";
private static final String HORIZONTAL_PIXEL_SIZE = "HorizontalPixelSize";
private static final String STANDARD_METADATA_FORMAT = "javax_imageio_1.0";
private String targetMIME;
public ImageIOImageWriter(String mime) {
this.targetMIME = mime;
}
public void writeImage(RenderedImage image, OutputStream out) throws IOException {
writeImage(image, out, null);
}
public void writeImage(RenderedImage image, OutputStream out,
ImageWriterParams params)
throws IOException {
javax.imageio.ImageWriter iiowriter = getIIOImageWriter();
iiowriter.addIIOWriteWarningListener(this);
ImageOutputStream imgout = ImageIO.createImageOutputStream(out);
try {
ImageWriteParam iwParam = getDefaultWriteParam(iiowriter, image, params);
IIOMetadata streamMetadata = createStreamMetadata(iiowriter, iwParam, params);
ImageTypeSpecifier type;
if (iwParam.getDestinationType() != null) {
type = iwParam.getDestinationType();
} else {
type = ImageTypeSpecifier.createFromRenderedImage(image);
}
IIOMetadata meta = iiowriter.getDefaultImageMetadata(
type, iwParam);
if (params != null && meta != null) {
meta = updateMetadata(image, meta, params);
}
iiowriter.setOutput(imgout);
IIOImage iioimg = new IIOImage(image, null, meta);
iiowriter.write(streamMetadata, iioimg, iwParam);
} finally {
imgout.close();
iiowriter.dispose();
}
}
protected IIOMetadata createStreamMetadata(javax.imageio.ImageWriter writer,
ImageWriteParam writeParam, ImageWriterParams params) {
return null;
}
private javax.imageio.ImageWriter getIIOImageWriter() {
Iterator<javax.imageio.ImageWriter> iter = ImageIO.getImageWritersByMIMEType(getMIMEType());
javax.imageio.ImageWriter iiowriter = null;
if (iter.hasNext()) {
iiowriter = iter.next();
}
if (iiowriter == null) {
throw new UnsupportedOperationException("No ImageIO codec for writing "
+ getMIMEType() + " is available!");
}
return iiowriter;
}
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;
}
protected IIOMetadata updateMetadata(RenderedImage image, IIOMetadata meta,
ImageWriterParams params) {
if (meta.isStandardMetadataFormatSupported() && params.getResolution() != null) {
float multiplier = (ResolutionUnit.CENTIMETER == params.getResolutionUnit()) ? 10f : UnitConv.IN2MM;
double pixelWidthInMillimeters = multiplier / params.getXResolution().doubleValue();
double pixelHeightInMillimeters = multiplier / params.getYResolution().doubleValue();
updatePixelSize(meta, pixelWidthInMillimeters, pixelHeightInMillimeters);
double checkMerged = getHorizontalPixelSize(meta);
if (!equals(checkMerged, pixelWidthInMillimeters, 0.00001)) {
double horzValue = 1 / pixelWidthInMillimeters;
double vertValue = 1 / pixelHeightInMillimeters;
updatePixelSize(meta, horzValue, vertValue);
}
}
return meta;
}
private static boolean equals(double d1, double d2, double maxDelta) {
return Math.abs(d1 - d2) <= maxDelta;
}
private double getHorizontalPixelSize(IIOMetadata meta) {
double result = 0;
IIOMetadataNode root = (IIOMetadataNode)meta.getAsTree(STANDARD_METADATA_FORMAT);
IIOMetadataNode dim = getChildNode(root, DIMENSION);
if (dim != null) {
IIOMetadataNode horz = getChildNode(dim, HORIZONTAL_PIXEL_SIZE);
if (horz != null) {
result = Double.parseDouble(horz.getAttribute("value"));
}
}
return result;
}
private void updatePixelSize(IIOMetadata meta, double horzValue, double vertValue) {
IIOMetadataNode root = (IIOMetadataNode)meta.getAsTree(STANDARD_METADATA_FORMAT);
IIOMetadataNode dim = getChildNode(root, DIMENSION);
IIOMetadataNode child;
child = getChildNode(dim, HORIZONTAL_PIXEL_SIZE);
if (child == null) {
child = new IIOMetadataNode(HORIZONTAL_PIXEL_SIZE);
dim.appendChild(child);
}
child.setAttribute("value", Double.toString(horzValue));
child = getChildNode(dim, VERTICAL_PIXEL_SIZE);
if (child == null) {
child = new IIOMetadataNode(VERTICAL_PIXEL_SIZE);
dim.appendChild(child);
}
child.setAttribute("value", Double.toString(vertValue));
try {
meta.mergeTree(STANDARD_METADATA_FORMAT, root);
} catch (IIOInvalidTreeException e) {
throw new RuntimeException("Cannot update image metadata: "
+ e.getMessage());
}
}
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;
}
public String getMIMEType() {
return this.targetMIME;
}
public boolean isFunctional() {
Iterator<javax.imageio.ImageWriter> iter = ImageIO.getImageWritersByMIMEType(getMIMEType());
return (iter.hasNext());
}
public void warningOccurred(javax.imageio.ImageWriter source,
int imageIndex, String warning) {
System.err.println("Problem while writing image using ImageI/O: "
+ warning);
}
public MultiImageWriter createMultiImageWriter(OutputStream out) throws IOException {
return new IIOMultiImageWriter(out);
}
public boolean supportsMultiImageWriter() {
javax.imageio.ImageWriter iiowriter = getIIOImageWriter();
try {
return iiowriter.canWriteSequence();
} finally {
iiowriter.dispose();
}
}
private class IIOMultiImageWriter implements MultiImageWriter {
private javax.imageio.ImageWriter iiowriter;
private ImageOutputStream imageStream;
private boolean prepared;
public IIOMultiImageWriter(OutputStream out) throws IOException {
this.iiowriter = getIIOImageWriter();
if (!iiowriter.canWriteSequence()) {
throw new UnsupportedOperationException("This ImageWriter does not support writing"
+ " multiple images to a single image file.");
}
iiowriter.addIIOWriteWarningListener(ImageIOImageWriter.this);
imageStream = ImageIO.createImageOutputStream(out);
iiowriter.setOutput(imageStream);
}
public void writeImage(RenderedImage image, ImageWriterParams params) throws IOException {
if (iiowriter == null) {
throw new IllegalStateException("MultiImageWriter already closed!");
}
ImageWriteParam iwParam = getDefaultWriteParam(iiowriter, image, params);
if (!prepared) {
IIOMetadata streamMetadata = createStreamMetadata(iiowriter, iwParam, params);
iiowriter.prepareWriteSequence(streamMetadata);
prepared = true;
}
ImageTypeSpecifier type;
if (iwParam.getDestinationType() != null) {
type = iwParam.getDestinationType();
} else {
type = ImageTypeSpecifier.createFromRenderedImage(image);
}
IIOMetadata meta = iiowriter.getDefaultImageMetadata(
type, iwParam);
if (params != null && meta != null) {
meta = updateMetadata(image, meta, params);
}
IIOImage iioimg = new IIOImage(image, null, meta);
iiowriter.writeToSequence(iioimg, iwParam);
}
public void close() throws IOException {
imageStream.close();
imageStream = null;
iiowriter.dispose();
iiowriter = null;
}
}
}