/*
 * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package javax.imageio.plugins.tiff;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import com.sun.imageio.plugins.tiff.TIFFIFD;
import com.sun.imageio.plugins.tiff.TIFFImageMetadata;

A convenience class for simplifying interaction with TIFF native image metadata. A TIFF image metadata tree represents an Image File Directory (IFD) from a TIFF 6.0 stream. An IFD consists of a number of IFD Entries each of which associates an identifying tag number with a compatible value. A TIFFDirectory instance corresponds to an IFD and contains a set of TIFFFields each of which corresponds to an IFD Entry in the IFD.

When reading, a TIFFDirectory may be created by passing the value returned by ImageReader.getImageMetadata() to createFromMetadata(). The TIFFFields in the directory may then be obtained using the accessor methods provided in this class.

When writing, an IIOMetadata object for use by one of the write() methods of ImageWriter may be created from a TIFFDirectory by getAsMetadata(). The TIFFDirectory itself may be created by construction or from the IIOMetadata object returned by ImageWriter.getDefaultImageMetadata(). The TIFFFields in the directory may be set using the mutator methods provided in this class.

A TIFFDirectory is aware of the tag numbers in the group of TIFFTagSets associated with it. When a TIFFDirectory is created from a native image metadata object, these tag sets are derived from the tagSets attribute of the TIFFIFD node.

A TIFFDirectory might also have a parent TIFFTag. This will occur if the directory represents an IFD other than the root IFD of the image. The parent tag is the tag of the IFD Entry which is a pointer to the IFD represented by this TIFFDirectory. The TIFFTag.isIFDPointer method of this parent TIFFTag must return true. When a TIFFDirectory is created from a native image metadata object, the parent tag set is set from the parentTagName attribute of the corresponding TIFFIFD node. Note that a TIFFDirectory instance which has a non-null parent tag will be contained in the data field of a TIFFField instance which has a tag field equal to the contained directory's parent tag.

As an example consider an Exif image. The TIFFDirectory instance corresponding to the Exif IFD in the Exif stream would have parent tag TAG_EXIF_IFD_POINTER and would include ExifTIFFTagSet in its group of known tag sets. The TIFFDirectory corresponding to this Exif IFD will be contained in the data field of a TIFFField which will in turn be contained in the TIFFDirectory corresponding to the primary IFD of the Exif image which will itself have a null-valued parent tag.

Note that this implementation is not synchronized. If multiple threads use a TIFFDirectory instance concurrently, and at least one of the threads modifies the directory, for example, by adding or removing TIFFFields or TIFFTagSets, it must be synchronized externally.

See Also:
Since:9
/** * A convenience class for simplifying interaction with TIFF native * image metadata. A TIFF image metadata tree represents an Image File * Directory (IFD) from a TIFF 6.0 stream. An IFD consists of a number of * IFD Entries each of which associates an identifying tag number with * a compatible value. A {@code TIFFDirectory} instance corresponds * to an IFD and contains a set of {@link TIFFField}s each of which * corresponds to an IFD Entry in the IFD. * * <p>When reading, a {@code TIFFDirectory} may be created by passing * the value returned by {@link javax.imageio.ImageReader#getImageMetadata * ImageReader.getImageMetadata()} to {@link #createFromMetadata * createFromMetadata()}. The {@link TIFFField}s in the directory may then * be obtained using the accessor methods provided in this class.</p> * * <p>When writing, an {@link IIOMetadata} object for use by one of the * {@code write()} methods of {@link javax.imageio.ImageWriter} may be * created from a {@code TIFFDirectory} by {@link #getAsMetadata()}. * The {@code TIFFDirectory} itself may be created by construction or * from the {@code IIOMetadata} object returned by * {@link javax.imageio.ImageWriter#getDefaultImageMetadata * ImageWriter.getDefaultImageMetadata()}. The {@code TIFFField}s in the * directory may be set using the mutator methods provided in this class.</p> * * <p>A {@code TIFFDirectory} is aware of the tag numbers in the * group of {@link TIFFTagSet}s associated with it. When * a {@code TIFFDirectory} is created from a native image metadata * object, these tag sets are derived from the {@code tagSets} attribute * of the {@code TIFFIFD} node.</p> * * <p>A {@code TIFFDirectory} might also have a parent {@link TIFFTag}. * This will occur if the directory represents an IFD other than the root * IFD of the image. The parent tag is the tag of the IFD Entry which is a * pointer to the IFD represented by this {@code TIFFDirectory}. The * {@link TIFFTag#isIFDPointer} method of this parent {@code TIFFTag} * must return {@code true}. When a {@code TIFFDirectory} is * created from a native image metadata object, the parent tag set is set * from the {@code parentTagName} attribute of the corresponding * {@code TIFFIFD} node. Note that a {@code TIFFDirectory} instance * which has a non-{@code null} parent tag will be contained in the * data field of a {@code TIFFField} instance which has a tag field * equal to the contained directory's parent tag.</p> * * <p>As an example consider an Exif image. The {@code TIFFDirectory} * instance corresponding to the Exif IFD in the Exif stream would have parent * tag {@link ExifParentTIFFTagSet#TAG_EXIF_IFD_POINTER TAG_EXIF_IFD_POINTER} * and would include {@link ExifTIFFTagSet} in its group of known tag sets. * The {@code TIFFDirectory} corresponding to this Exif IFD will be * contained in the data field of a {@code TIFFField} which will in turn * be contained in the {@code TIFFDirectory} corresponding to the primary * IFD of the Exif image which will itself have a {@code null}-valued * parent tag.</p> * * <p><b>Note that this implementation is not synchronized. </b>If multiple * threads use a {@code TIFFDirectory} instance concurrently, and at * least one of the threads modifies the directory, for example, by adding * or removing {@code TIFFField}s or {@code TIFFTagSet}s, it * <i>must</i> be synchronized externally.</p> * * @since 9 * @see IIOMetadata * @see TIFFField * @see TIFFTag * @see TIFFTagSet */
public class TIFFDirectory implements Cloneable {
The largest low-valued tag number in the TIFF 6.0 specification.
/** The largest low-valued tag number in the TIFF 6.0 specification. */
private static final int MAX_LOW_FIELD_TAG_NUM = BaselineTIFFTagSet.TAG_REFERENCE_BLACK_WHITE;
The TIFFTagSets associated with this directory.
/** The {@code TIFFTagSets} associated with this directory. */
private List<TIFFTagSet> tagSets;
The parent TIFFTag of this directory.
/** The parent {@code TIFFTag} of this directory. */
private TIFFTag parentTag;
The fields in this directory which have a low tag number. These are managed as an array for efficiency as they are the most common fields.
/** * The fields in this directory which have a low tag number. These are * managed as an array for efficiency as they are the most common fields. */
private TIFFField[] lowFields = new TIFFField[MAX_LOW_FIELD_TAG_NUM + 1];
The number of low tag numbered fields in the directory.
/** The number of low tag numbered fields in the directory. */
private int numLowFields = 0;
A mapping of Integer tag numbers to TIFFFields for fields which are not low tag numbered.
/** * A mapping of {@code Integer} tag numbers to {@code TIFFField}s * for fields which are not low tag numbered. */
private Map<Integer,TIFFField> highFields = new TreeMap<Integer,TIFFField>();
Creates a TIFFDirectory instance from the contents of an image metadata object. The supplied object must support an image metadata format supported by the TIFF ImageWriter plug-in. This will usually be either the TIFF native image metadata format javax_imageio_tiff_image_1.0 or the Java Image I/O standard metadata format javax_imageio_1.0.
Params:
  • tiffImageMetadata – A metadata object which supports a compatible image metadata format.
Throws:
Returns:A TIFFDirectory populated from the contents of the supplied metadata object.
/** * Creates a {@code TIFFDirectory} instance from the contents of * an image metadata object. The supplied object must support an image * metadata format supported by the TIFF {@link javax.imageio.ImageWriter} * plug-in. This will usually be either the TIFF native image metadata * format {@code javax_imageio_tiff_image_1.0} or the Java * Image I/O standard metadata format {@code javax_imageio_1.0}. * * @param tiffImageMetadata A metadata object which supports a compatible * image metadata format. * * @return A {@code TIFFDirectory} populated from the contents of * the supplied metadata object. * * @throws NullPointerException if {@code tiffImageMetadata} * is {@code null}. * @throws IllegalArgumentException if {@code tiffImageMetadata} * does not support a compatible image metadata format. * @throws IIOInvalidTreeException if the supplied metadata object * cannot be parsed. */
public static TIFFDirectory createFromMetadata(IIOMetadata tiffImageMetadata) throws IIOInvalidTreeException { if(tiffImageMetadata == null) { throw new NullPointerException("tiffImageMetadata == null"); } TIFFImageMetadata tim; if(tiffImageMetadata instanceof TIFFImageMetadata) { tim = (TIFFImageMetadata)tiffImageMetadata; } else { // Create a native metadata object. ArrayList<TIFFTagSet> l = new ArrayList<TIFFTagSet>(1); l.add(BaselineTIFFTagSet.getInstance()); tim = new TIFFImageMetadata(l); // Determine the format name to use. String formatName = null; if(TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME.equals (tiffImageMetadata.getNativeMetadataFormatName())) { formatName = TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME; } else { String[] extraNames = tiffImageMetadata.getExtraMetadataFormatNames(); if(extraNames != null) { for(int i = 0; i < extraNames.length; i++) { if(TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME.equals (extraNames[i])) { formatName = extraNames[i]; break; } } } if(formatName == null) { if(tiffImageMetadata.isStandardMetadataFormatSupported()) { formatName = IIOMetadataFormatImpl.standardMetadataFormatName; } else { throw new IllegalArgumentException ("Parameter does not support required metadata format!"); } } } // Set the native metadata object from the tree. tim.setFromTree(formatName, tiffImageMetadata.getAsTree(formatName)); } return tim.getRootIFD(); }
Constructs a TIFFDirectory which is aware of a given group of TIFFTagSets. An optional parent TIFFTag may also be specified.
Params:
  • tagSets – The TIFFTagSets associated with this directory.
  • parentTag – The parent TIFFTag of this directory; may be null.
Throws:
/** * Constructs a {@code TIFFDirectory} which is aware of a given * group of {@link TIFFTagSet}s. An optional parent {@link TIFFTag} * may also be specified. * * @param tagSets The {@code TIFFTagSets} associated with this * directory. * @param parentTag The parent {@code TIFFTag} of this directory; * may be {@code null}. * @throws NullPointerException if {@code tagSets} is * {@code null}. */
public TIFFDirectory(TIFFTagSet[] tagSets, TIFFTag parentTag) { if(tagSets == null) { throw new NullPointerException("tagSets == null!"); } this.tagSets = new ArrayList<TIFFTagSet>(tagSets.length); int numTagSets = tagSets.length; for(int i = 0; i < numTagSets; i++) { this.tagSets.add(tagSets[i]); } this.parentTag = parentTag; }
Returns the TIFFTagSets of which this directory is aware.
Returns:The TIFFTagSets associated with this TIFFDirectory.
/** * Returns the {@link TIFFTagSet}s of which this directory is aware. * * @return The {@code TIFFTagSet}s associated with this * {@code TIFFDirectory}. */
public TIFFTagSet[] getTagSets() { return tagSets.toArray(new TIFFTagSet[tagSets.size()]); }
Adds an element to the group of TIFFTagSets of which this directory is aware.
Params:
  • tagSet – The TIFFTagSet to add.
Throws:
/** * Adds an element to the group of {@link TIFFTagSet}s of which this * directory is aware. * * @param tagSet The {@code TIFFTagSet} to add. * @throws NullPointerException if {@code tagSet} is * {@code null}. */
public void addTagSet(TIFFTagSet tagSet) { if(tagSet == null) { throw new NullPointerException("tagSet == null"); } if(!tagSets.contains(tagSet)) { tagSets.add(tagSet); } }
Removes an element from the group of TIFFTagSets of which this directory is aware.
Params:
  • tagSet – The TIFFTagSet to remove.
Throws:
/** * Removes an element from the group of {@link TIFFTagSet}s of which this * directory is aware. * * @param tagSet The {@code TIFFTagSet} to remove. * @throws NullPointerException if {@code tagSet} is * {@code null}. */
public void removeTagSet(TIFFTagSet tagSet) { if(tagSet == null) { throw new NullPointerException("tagSet == null"); } if(tagSets.contains(tagSet)) { tagSets.remove(tagSet); } }
Returns the parent TIFFTag of this directory if one has been defined or null otherwise.
Returns:The parent TIFFTag of this TIFFDiectory or null.
/** * Returns the parent {@link TIFFTag} of this directory if one * has been defined or {@code null} otherwise. * * @return The parent {@code TIFFTag} of this * {@code TIFFDiectory} or {@code null}. */
public TIFFTag getParentTag() { return parentTag; }
Returns the TIFFTag which has tag number equal to tagNumber or null if no such tag exists in the TIFFTagSets associated with this directory.
Params:
  • tagNumber – The tag number of interest.
Returns:The corresponding TIFFTag or null.
/** * Returns the {@link TIFFTag} which has tag number equal to * {@code tagNumber} or {@code null} if no such tag * exists in the {@link TIFFTagSet}s associated with this * directory. * * @param tagNumber The tag number of interest. * @return The corresponding {@code TIFFTag} or {@code null}. */
public TIFFTag getTag(int tagNumber) { return TIFFIFD.getTag(tagNumber, tagSets); }
Returns the number of TIFFFields in this directory.
Returns:The number of TIFFFields in this TIFFDirectory.
/** * Returns the number of {@link TIFFField}s in this directory. * * @return The number of {@code TIFFField}s in this * {@code TIFFDirectory}. */
public int getNumTIFFFields() { return numLowFields + highFields.size(); }
Determines whether a TIFF field with the given tag number is contained in this directory.
Params:
  • tagNumber – The tag number.
Returns:Whether a TIFFTag with tag number equal to tagNumber is present in this TIFFDirectory.
/** * Determines whether a TIFF field with the given tag number is * contained in this directory. * * @param tagNumber The tag number. * @return Whether a {@link TIFFTag} with tag number equal to * {@code tagNumber} is present in this {@code TIFFDirectory}. */
public boolean containsTIFFField(int tagNumber) { return (tagNumber >= 0 && tagNumber <= MAX_LOW_FIELD_TAG_NUM && lowFields[tagNumber] != null) || highFields.containsKey(Integer.valueOf(tagNumber)); }
Adds a TIFF field to the directory.
Params:
  • f – The field to add.
Throws:
/** * Adds a TIFF field to the directory. * * @param f The field to add. * @throws NullPointerException if {@code f} is {@code null}. */
public void addTIFFField(TIFFField f) { if(f == null) { throw new NullPointerException("f == null"); } int tagNumber = f.getTagNumber(); if(tagNumber >= 0 && tagNumber <= MAX_LOW_FIELD_TAG_NUM) { if(lowFields[tagNumber] == null) { numLowFields++; } lowFields[tagNumber] = f; } else { highFields.put(Integer.valueOf(tagNumber), f); } }
Retrieves a TIFF field from the directory.
Params:
  • tagNumber – The tag number of the tag associated with the field.
Returns:A TIFFField with the requested tag number of null if no such field is present.
/** * Retrieves a TIFF field from the directory. * * @param tagNumber The tag number of the tag associated with the field. * @return A {@code TIFFField} with the requested tag number of * {@code null} if no such field is present. */
public TIFFField getTIFFField(int tagNumber) { TIFFField f; if(tagNumber >= 0 && tagNumber <= MAX_LOW_FIELD_TAG_NUM) { f = lowFields[tagNumber]; } else { f = highFields.get(Integer.valueOf(tagNumber)); } return f; }
Removes a TIFF field from the directory.
Params:
  • tagNumber – The tag number of the tag associated with the field.
/** * Removes a TIFF field from the directory. * * @param tagNumber The tag number of the tag associated with the field. */
public void removeTIFFField(int tagNumber) { if(tagNumber >= 0 && tagNumber <= MAX_LOW_FIELD_TAG_NUM) { if(lowFields[tagNumber] != null) { numLowFields--; lowFields[tagNumber] = null; } } else { highFields.remove(Integer.valueOf(tagNumber)); } }
Retrieves all TIFF fields from the directory.
Returns:An array of all TIFF fields in order of numerically increasing tag number.
/** * Retrieves all TIFF fields from the directory. * * @return An array of all TIFF fields in order of numerically increasing * tag number. */
public TIFFField[] getTIFFFields() { // Allocate return value. TIFFField[] fields = new TIFFField[numLowFields + highFields.size()]; // Copy any low-index fields. int nextIndex = 0; for(int i = 0; i <= MAX_LOW_FIELD_TAG_NUM; i++) { if(lowFields[i] != null) { fields[nextIndex++] = lowFields[i]; if(nextIndex == numLowFields) break; } } // Copy any high-index fields. if(!highFields.isEmpty()) { Iterator<Integer> keys = highFields.keySet().iterator(); while(keys.hasNext()) { fields[nextIndex++] = highFields.get(keys.next()); } } return fields; }
Removes all TIFF fields from the directory.
/** * Removes all TIFF fields from the directory. */
public void removeTIFFFields() { Arrays.fill(lowFields, (Object)null); numLowFields = 0; highFields.clear(); }
Converts the directory to a metadata object.
Returns:A metadata instance initialized from the contents of this TIFFDirectory.
/** * Converts the directory to a metadata object. * * @return A metadata instance initialized from the contents of this * {@code TIFFDirectory}. */
public IIOMetadata getAsMetadata() { return new TIFFImageMetadata(TIFFIFD.getDirectoryAsIFD(this)); }
Clones the directory and all the fields contained therein.
Throws:
Returns:A clone of this TIFFDirectory.
/** * Clones the directory and all the fields contained therein. * * @return A clone of this {@code TIFFDirectory}. * @throws CloneNotSupportedException if the instance cannot be cloned. */
@Override public TIFFDirectory clone() throws CloneNotSupportedException { TIFFDirectory dir = (TIFFDirectory) super.clone(); dir.tagSets = new ArrayList<TIFFTagSet>(tagSets); dir.parentTag = getParentTag(); TIFFField[] fields = getTIFFFields(); for(TIFFField field : fields) { dir.addTIFFField(field.clone()); } return dir; } }