I want to add some custom metadata to a multipage tiff for further processing steps, like
- identifier1 = XYZ1
- identifier2 = XYZ2
- ...
My idea was to update (see code/TODO below)
- IIOMetadata streamMetadata [option 1]
- IIOMetadata imageMetadata [option 2]
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
public class TiffMetadataExample {
public static void addMetadata(File tiff, File out, Object metadata2Add)
throws FileNotFoundException, IOException {
try (FileInputStream fis = new FileInputStream(tiff);
FileOutputStream fos = new FileOutputStream(out)) {
addMetadata(fis, fos, metadata2Add);
}
}
public static void addMetadata(InputStream inputImage, OutputStream out, Object metadata2Add)
throws IOException {
List<IIOMetadata> metadata = new ArrayList<>();
List<BufferedImage> images = getImages(inputImage, metadata);
if (metadata.size() != images.size()) {
throw new IllegalStateException();
}
// Obtain a TIFF writer
ImageWriter writer = ImageIO.getImageWritersByFormatName("TIFF").next();
try (ImageOutputStream output = ImageIO.createImageOutputStream(out)) {
writer.setOutput(output);
ImageWriteParam params = writer.getDefaultWriteParam();
params.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
// Compression: None, PackBits, ZLib, Deflate, LZW, JPEG and CCITT variants allowed
// (different plugins may use a different set of compression type names)
params.setCompressionType("Deflate");
// streamMetadata is null here
IIOMetadata streamMetadata = writer.getDefaultStreamMetadata(params);
// TODO: add custom metadata fields [option 1]
writer.prepareWriteSequence(streamMetadata);
for (int i = 0; i < images.size(); i++) {
BufferedImage image = images.get(i);
IIOMetadata imageMetadata = metadata.get(i);
// TODO: add custom metadata fields [option 2]
writer.writeToSequence(new IIOImage(image, null, imageMetadata), params);
}
writer.endWriteSequence();
} finally {
writer.dispose();
}
}
private static List<BufferedImage> getImages(final InputStream inputImage,
final List<IIOMetadata> metadata) throws IOException {
List<BufferedImage> images = new ArrayList<>();
ImageReader reader = null;
try (ImageInputStream is = ImageIO.createImageInputStream(inputImage)) {
Iterator<ImageReader> iterator = ImageIO.getImageReaders(is);
reader = iterator.next();
reader.setInput(is);
int numPages = reader.getNumImages(true);
for (int numPage = 0; numPage < numPages; numPage++) {
BufferedImage pageImage = reader.read(numPage);
IIOMetadata imageMetadata = reader.getImageMetadata(numPage);
metadata.add(imageMetadata);
images.add(pageImage);
}
return images;
} finally {
if (reader != null) {
reader.dispose();
}
}
}
}
Try to update imageMetadata [option 2] with following code does not work. What is wrong here?
IIOMetadataNode textEntry = new IIOMetadataNode("tEXtEntry");
textEntry.setAttribute("keyword", "aaaaaa");
textEntry.setAttribute("value", "bbb");
IIOMetadataNode text = new IIOMetadataNode("tEXt");
text.appendChild(textEntry);
Node root = meta.getAsTree(formatName);
root.appendChild(text);
//e.g. formatName = "javax_imageio_1.0"
imageMetadata.setFromTree(imageMetadata.getNativeMetadataFormatName(), root);
Or is there a nicer/other way to store some further processing informations within the tiff?
This is my working solution.
Usage