/*
* Copyright (c) 2005, 2015, 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 {@link 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 {@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.
When writing, an {@link IIOMetadata} object for use by one of the
* write()
methods of {@link javax.imageio.ImageWriter} may be
* created from a TIFFDirectory
by {@link #getAsMetadata()}.
* The TIFFDirectory
itself may be created by construction or
* from the IIOMetadata
object returned by
* {@link javax.imageio.ImageWriter#getDefaultImageMetadata
* 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 {@link 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 {@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 TIFFDirectory
. The
* {@link 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 {@link ExifParentTIFFTagSet#TAG_EXIF_IFD_POINTER TAG_EXIF_IFD_POINTER}
* and would include {@link 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.
TIFFTagSets
associated with this directory. */
private ListTIFFTag
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.
*/
private TIFFField[] lowFields = new TIFFField[MAX_LOW_FIELD_TAG_NUM + 1];
/** 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.
*/
private MapTIFFDirectory
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 javax_imageio_tiff_image_1.0 or the Java
* Image I/O standard metadata format javax_imageio_1.0.
*
* @param tiffImageMetadata A metadata object which supports a compatible
* image metadata format.
*
* @return A TIFFDirectory
populated from the contents of
* the supplied metadata object.
*
* @throws NullPointerException if tiffImageMetadata
* is null
.
* @throws IllegalArgumentException if 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.
ArrayListTIFFDirectory
to a TIFFIFD
.
*/
private static TIFFIFD getDirectoryAsIFD(TIFFDirectory dir) {
if(dir instanceof TIFFIFD) {
return (TIFFIFD)dir;
}
TIFFIFD ifd = new TIFFIFD(Arrays.asList(dir.getTagSets()),
dir.getParentTag());
TIFFField[] fields = dir.getTIFFFields();
int numFields = fields.length;
for(int i = 0; i < numFields; i++) {
TIFFField f = fields[i];
TIFFTag tag = f.getTag();
if(tag.isIFDPointer()) {
TIFFDirectory subIFD =
getDirectoryAsIFD((TIFFDirectory)f.getData());
f = new TIFFField(tag, f.getType(), (long)f.getCount(), subIFD);
}
ifd.addTIFFField(f);
}
return ifd;
}
/**
* Constructs a TIFFDirectory
which is aware of a given
* group of {@link TIFFTagSet}s. An optional parent {@link TIFFTag}
* may also be specified.
*
* @param tagSets The TIFFTagSets
associated with this
* directory.
* @param parentTag The parent TIFFTag
of this directory;
* may be null
.
* @throws NullPointerException if tagSets
is
* null
.
*/
public TIFFDirectory(TIFFTagSet[] tagSets, TIFFTag parentTag) {
if(tagSets == null) {
throw new NullPointerException("tagSets == null!");
}
this.tagSets = new ArrayListTIFFTagSet
s associated with this
* TIFFDirectory
.
*/
public TIFFTagSet[] getTagSets() {
return tagSets.toArray(new TIFFTagSet[tagSets.size()]);
}
/**
* Adds an element to the group of {@link TIFFTagSet}s of which this
* directory is aware.
*
* @param tagSet The TIFFTagSet
to add.
* @throws NullPointerException if tagSet
is
* 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 {@link TIFFTagSet}s of which this
* directory is aware.
*
* @param tagSet The TIFFTagSet
to remove.
* @throws NullPointerException if tagSet
is
* null
.
*/
public void removeTagSet(TIFFTagSet tagSet) {
if(tagSet == null) {
throw new NullPointerException("tagSet == null");
}
if(tagSets.contains(tagSet)) {
tagSets.remove(tagSet);
}
}
/**
* Returns the parent {@link TIFFTag} of this directory if one
* has been defined or null
otherwise.
*
* @return The parent TIFFTag
of this
* TIFFDiectory
or null
.
*/
public TIFFTag getParentTag() {
return parentTag;
}
/**
* Returns the {@link TIFFTag} which has tag number equal to
* tagNumber
or 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 TIFFTag
or null
.
*/
public TIFFTag getTag(int tagNumber) {
return TIFFIFD.getTag(tagNumber, tagSets);
}
/**
* Returns the number of {@link TIFFField}s in this directory.
*
* @return The number of TIFFField
s in this
* TIFFDirectory
.
*/
public int getNumTIFFFields() {
return numLowFields + highFields.size();
}
/**
* 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
* tagNumber
is present in this 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.
*
* @param f The field to add.
* @throws NullPointerException if f
is 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.
*
* @param tagNumber The tag number of the tag associated with the field.
* @return A TIFFField
with the requested tag number of
* 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.
*
* @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.
*
* @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()) {
IteratorTIFFDirectory
.
*/
public IIOMetadata getAsMetadata() {
return new TIFFImageMetadata(getDirectoryAsIFD(this));
}
/**
* Clones the directory and all the fields contained therein.
*
* @return A clone of this TIFFDirectory
.
* @throws CloneNotSupportedException if the instance cannot be cloned.
*/
@Override
public TIFFDirectory clone() throws CloneNotSupportedException {
TIFFDirectory dir = (TIFFDirectory) super.clone();
dir.tagSets = new ArrayList