Hello people from stackoverflow,
I've spent some time googling for a solution to my problem, but nothing great came out.
I would like to write metadata to an image that I create from scratch.
My understanding of Java Advanced Image API is that I should use IIOMetadata but code snippets I found seemed to be overly complicated.
Then I searched a library and found Sanselan but it seems a bit old, and not very handy for writing metadata.
I actually create the image using
ImageIO.write(image, "png", baos);
I understand image metadatas are a bit complex to handle due to its XML-like structure.
Could anybody point me to a tutorial, a solution, a library... ?
Thanks,
Alexis.
I had to do the the same thing some days ago.. I have not found the exact solution on the internet either but looking at the com.sun.imageio.plugins.png.PNGMetadata
class I could achieve some results..
To write a custom metadata to a PNG file:
public byte[] writeCustomData(BufferedImage buffImg, String key, String value) throws Exception {
ImageWriter writer = ImageIO.getImageWritersByFormatName("png").next();
ImageWriteParam writeParam = writer.getDefaultWriteParam();
ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
//adding metadata
IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam);
IIOMetadataNode textEntry = new IIOMetadataNode("tEXtEntry");
textEntry.setAttribute("keyword", key);
textEntry.setAttribute("value", value);
IIOMetadataNode text = new IIOMetadataNode("tEXt");
text.appendChild(textEntry);
IIOMetadataNode root = new IIOMetadataNode("javax_imageio_png_1.0");
root.appendChild(text);
metadata.mergeTree("javax_imageio_png_1.0", root);
//writing the data
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageOutputStream stream = ImageIO.createImageOutputStream(baos);
writer.setOutput(stream);
writer.write(metadata, new IIOImage(buffImg, null, metadata), writeParam);
stream.close();
return baos.toByteArray();
}
Then, to read the data:
public String readCustomData(byte[] imageData, String key) throws IOException{
ImageReader imageReader = ImageIO.getImageReadersByFormatName("png").next();
imageReader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(imageData)), true);
// read metadata of first image
IIOMetadata metadata = imageReader.getImageMetadata(0);
//this cast helps getting the contents
PNGMetadata pngmeta = (PNGMetadata) metadata;
NodeList childNodes = pngmeta.getStandardTextNode().getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
String keyword = node.getAttributes().getNamedItem("keyword").getNodeValue();
String value = node.getAttributes().getNamedItem("value").getNodeValue();
if(key.equals(keyword)){
return value;
}
}
return null;
}
Java provides the metadata
package and the ImageWriter
class along with the ImageIO
package.
You create your IIOMetadata
object, then getImageWriters
for your BufferedImage
or IIOImage
and use them to write
the metadata.
To add to other answer, you can also try the PNGJ library, it has full metadata support.
BTW, I don't understand what you are refering to with the "XML-like" structure of metadata.
Using the method from posted by the OP gets most of the way there; the only issue is that PNGMetadata
is proprietary and so causes compiler warnings.
There is a method of doing it without using the proprietary API, by searching the metadata tree for tEXtEntry
nodes:
private List<Node> findNodesWithName(String name, Node root) {
List<Node> found = new ArrayList<>();
Node n = root.getFirstChild();
while (n != null) {
if (n.getNodeName().equals(name)) {
found.add(n);
}
found.addAll(findNodesWithName(name, n));
n = n.getNextSibling();
}
}
// ...
// To use it:
IIOMetadata metadata = ...;
List<Node> tEXtNodes = findNodesWithName(
"tEXtEntry",
metadata.getAsTree(metadata.getNativeMetadataFormatName()));
for (Node n : tEXtNodes) {
String keyword = node.getAttributes().getNamedItem("keyword");
String value = node.getAttributes().getNamedItem("value");
System.out.println("keyword: " + keyword + "; value: " + value);
}