Goal

Java Image embedded framework allows to handle plenty of images types and content in a generic way. The concept is to read the image from byte[], using an ImageReader. This image reader has to be obtained through available readers in the java loaded classpath. There is a binding system that allows external libraries to register against the general framework.

In this example, the library from [https://github.com/jai-imageio/jai-imageio-core]() will be used, one can find this dependency thanks to this depency coordinates:

        <dependency>
            <groupId>com.github.jai-imageio</groupId>
            <artifactId>jai-imageio-core</artifactId>
            <version>1.3.1</version>
        </dependency>

Thanks to the JAI embedded frameworks, one can access to either regular (common) metadata of the images. Unfortunately in some occasions, it occurs that specific format, such as TIFF, require more metadata settings in order to render as expected.

Accessing Regular Metadata

reading the image

    private static IIOMetadata extractMetadata(String path) throws IOException {
        byte[] currentContent = Files.readAllBytes(new File(path).toPath());
        ImageReader reader = ImageIO.getImageReadersByFormatName("tiff").next();
        ImageInputStream iis = ImageIO.createImageInputStream(new ByteArrayInputStream(currentContent));
        reader.setInput(iis);
        //get the first image metadata
        IIOMetadata metadata = reader.getImageMetadata(0);

        //display available format names
        System.out.println(Arrays.asList(metadata.getMetadataFormatNames()));
        return metadata;
    }

This will display the list of possible metadata usable for this reader, in our case :

[com_sun_media_imageio_plugins_tiff_image_1.0, javax_imageio_1.0]

Notice that we have one generic representation javax_imageio_1.0 and one specificcom_sun_media_imageio_plugins_tiff_image_1.0

metadata output

One can get the metadata displayed under a tree format using the getAsTree(formatName) method.

        Node regularMetadataTree = iioMetadata.getAsTree("javax_imageio_1.0");
        Node extendedMetadataTree = iioMetadata.getAsTree("com_sun_media_imageio_plugins_tiff_image_1.0");
        System.out.println(getStringFromDocument(regularMetadataTree));
        System.out.println(getStringFromDocument(extendedMetadataTree));

with the utility method to convert org.w3c.dom.Node to string :


    // method to convert Document to String
    public static String getStringFromDocument(Node doc) {
        try {
            DOMSource domSource = new DOMSource(doc);
            StringWriter writer = new StringWriter();
            StreamResult result = new StreamResult(writer);
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer transformer = tf.newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
            transformer.transform(domSource, result);
            return writer.toString();
        } catch (TransformerException ex) {
            ex.printStackTrace();
            return null;
        }
    }

will display successively :

<?xml version="1.0" encoding="UTF-8"?><javax_imageio_1.0>
  <Chroma>
    <ColorSpaceType name="GRAY"/>
    <BlackIsZero value="TRUE"/>
    <NumChannels value="1"/>
  </Chroma>
  <Compression>
    <CompressionTypeName value="LZW"/>
    <Lossless value="TRUE"/>
    <NumProgressiveScans value="1"/>
  </Compression>
  <Data>
    <PlanarConfiguration value="PixelInterleaved"/>
    <SampleFormat value="UnsignedIntegral"/>
    <BitsPerSample value="8"/>
    <SampleMSB value="7"/>
  </Data>
  <Dimension>
    <PixelAspectRatio value="1.0"/>
    <HorizontalPixelSize value="0.08466667"/>
    <VerticalPixelSize value="0.08466667"/>
  </Dimension>
  <Document>
    <FormatVersion value="6.0"/>
  </Document>
  <Transparency>
    <Alpha value="none"/>
  </Transparency>
</javax_imageio_1.0>

and

<?xml version="1.0" encoding="UTF-8"?><com_sun_media_imageio_plugins_tiff_image_1.0>
  <TIFFIFD tagSets="com.github.jaiimageio.plugins.tiff.BaselineTIFFTagSet,com.github.jaiimageio.plugins.tiff.FaxTIFFTagSet,com.github.jaiimageio.plugins.tiff.EXIFParentTIFFTagSet,com.github.jaiimageio.plugins.tiff.GeoTIFFTagSet">
    <TIFFField number="254" name="NewSubfileType">
      <TIFFLongs>
        <TIFFLong value="0" description="Default"/>
      </TIFFLongs>
    </TIFFField>
    <TIFFField number="256" name="ImageWidth">
      <TIFFShorts>
        <TIFFShort value="1419"/>
      </TIFFShorts>
    </TIFFField>
    <TIFFField number="257" name="ImageLength">
      <TIFFShorts>
        <TIFFShort value="1001"/>
      </TIFFShorts>
    </TIFFField>
    <TIFFField number="258" name="BitsPerSample">
      <TIFFShorts>
        <TIFFShort value="8"/>
      </TIFFShorts>
    </TIFFField>
    <TIFFField number="259" name="Compression">
      <TIFFShorts>
        <TIFFShort value="5" description="LZW"/>
      </TIFFShorts>
    </TIFFField>
    <TIFFField number="262" name="PhotometricInterpretation">
      <TIFFShorts>
        <TIFFShort value="1" description="BlackIsZero"/>
      </TIFFShorts>
    </TIFFField>
    <TIFFField number="273" name="StripOffsets">
      <TIFFLongs>
        <TIFFLong value="2090"/>
        <TIFFLong value="2228"/>
        <TIFFLong value="2366"/>
        <TIFFLong value="5286"/>
        <TIFFLong value="9726"/>
        <TIFFLong value="13978"/>
        <TIFFLong value="18136"/>
        <!-- ... -->
      </TIFFLongs>
    </TIFFField>
    <TIFFField number="277" name="SamplesPerPixel">
      <TIFFShorts>
        <TIFFShort value="1"/>
      </TIFFShorts>
    </TIFFField>
    <TIFFField number="278" name="RowsPerStrip">
      <TIFFShorts>
        <TIFFShort value="5"/>
      </TIFFShorts>
    </TIFFField>
    <TIFFField number="279" name="StripByteCounts">
      <TIFFLongs>
        <TIFFLong value="137"/>
        <TIFFLong value="137"/>
        <TIFFLong value="2919"/>
        <!-- ... -->
      </TIFFLongs>
    </TIFFField>
    <TIFFField number="282" name="XResolution">
      <TIFFRationals>
        <TIFFRational value="300/1"/>
      </TIFFRationals>
    </TIFFField>
    <TIFFField number="283" name="YResolution">
      <TIFFRationals>
        <TIFFRational value="300/1"/>
      </TIFFRationals>
    </TIFFField>
    <TIFFField number="296" name="ResolutionUnit">
      <TIFFShorts>
        <TIFFShort value="2" description="Inch"/>
      </TIFFShorts>
    </TIFFField>
    <TIFFField number="317" name="Predictor">
      <TIFFShorts>
        <TIFFShort value="2" description="Horizontal Differencing"/>
      </TIFFShorts>
    </TIFFField>
    <TIFFField number="34377" name="unknown">
      <TIFFBytes>
        <TIFFByte value="56"/>
        <TIFFByte value="66"/>
        <TIFFByte value="73"/>
        <!-- ... -->
</TIFFBytes>
    </TIFFField>
  </TIFFIFD>
</com_sun_media_imageio_plugins_tiff_image_1.0>

You can see that there are slight differences between the two outputs! The second is to be preferred, as it represents the tiff metadata with the fields that are described in the specification

More information can be found here:

[https://docs.oracle.com/javase/10/docs/api/javax/imageio/metadata/doc-files/tiff_metadata.html]()

Full example:

import org.w3c.dom.Element;
import org.w3c.dom.Node;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageInputStream;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.file.Files;
import java.util.Arrays;

public class TiffMetaData {

    public static void main(String[] args) throws IOException {
        System.out.println(System.getProperty("user.dir"));
        IIOMetadata iioMetadata = extractMetadata(System.getProperty("user.dir") + "/src/main/resources/gmarbles.tiff");
        Node regularMetadataTree = iioMetadata.getAsTree("javax_imageio_1.0");
        Node extendedMetadataTree = iioMetadata.getAsTree("com_sun_media_imageio_plugins_tiff_image_1.0");
        System.out.println(getStringFromDocument(regularMetadataTree));
        System.out.println(getStringFromDocument(extendedMetadataTree));
    }

    // method to convert Document to String
    public static String getStringFromDocument(Node doc) {
        try {
            DOMSource domSource = new DOMSource(doc);
            StringWriter writer = new StringWriter();
            StreamResult result = new StreamResult(writer);
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer transformer = tf.newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
            transformer.transform(domSource, result);
            return writer.toString();
        } catch (TransformerException ex) {
            ex.printStackTrace();
            return null;
        }
    }
    private static IIOMetadata extractMetadata(String path) throws IOException {
        byte[] currentContent = Files.readAllBytes(new File(path).toPath());
        ImageReader reader = ImageIO.getImageReadersByFormatName("tiff").next();
        ImageInputStream iis = ImageIO.createImageInputStream(new ByteArrayInputStream(currentContent));
        reader.setInput(iis);
        //get the first image metadata
        IIOMetadata metadata = reader.getImageMetadata(0);

        //display available format names
        System.out.println(Arrays.asList(metadata.getMetadataFormatNames()));

        //create a document that represents the metadata

        return metadata;
    }

}

with attached image "gmarbles.tiff"

Previous Post Next Post