/*
* 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 TIFFField
s 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 TIFFField
s 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 TIFFField
s 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 TIFFTagSet
s 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 TIFFField
s or TIFFTagSet
s, 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 TIFFField
s 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: - NullPointerException – if
tiffImageMetadata
is null
. - IllegalArgumentException – if
tiffImageMetadata
does not support a compatible image metadata format. - IIOInvalidTreeException – if the supplied metadata object
cannot be parsed.
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 TIFFTagSet
s. 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: - NullPointerException – if
tagSets
is null
.
/**
* 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 TIFFTagSet
s of which this directory is aware. Returns: The TIFFTagSet
s 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 TIFFTagSet
s of which this directory is aware. Params: - tagSet – The
TIFFTagSet
to add.
Throws: - NullPointerException – if
tagSet
is null
.
/**
* 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 TIFFTagSet
s of which this directory is aware. Params: - tagSet – The
TIFFTagSet
to remove.
Throws: - NullPointerException – if
tagSet
is null
.
/**
* 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 TIFFTagSet
s 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 TIFFField
s in this directory. Returns: The number of TIFFField
s 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: - NullPointerException – if
f
is null
.
/**
* 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: - CloneNotSupportedException – if the instance cannot be cloned.
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;
}
}