/* * 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.StringTokenizer; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import com.sun.imageio.plugins.tiff.TIFFFieldNode; import com.sun.imageio.plugins.tiff.TIFFIFD; /** * A class representing a field in a TIFF 6.0 Image File Directory. * *

A field in a TIFF Image File Directory (IFD) is defined as a * tag number accompanied by a sequence of values of identical data type. * TIFF 6.0 defines 12 data types; a 13th type IFD is * defined in TIFF Tech Note 1 of TIFF Specification Supplement 1. These * TIFF data types are referred to by Java constants and mapped internally * onto Java language data types and type names as follows: * *
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
TIFF Data Type to Java Data Type Mapping
* TIFF Data Type * * Java Constant * * Java Data Type * * Java Type Name *
* BYTE * * {@link TIFFTag#TIFF_BYTE} * * byte * * "Byte" *
* ASCII * * {@link TIFFTag#TIFF_ASCII} * * String * * "Ascii" *
* SHORT * * {@link TIFFTag#TIFF_SHORT} * * char * * "Short" *
* LONG * * {@link TIFFTag#TIFF_LONG} * * long * * "Long" *
* RATIONAL * * {@link TIFFTag#TIFF_RATIONAL} * * long[2] {numerator, denominator} * * "Rational" *
* SBYTE * * {@link TIFFTag#TIFF_SBYTE} * * byte * * "SByte" *
* UNDEFINED * * {@link TIFFTag#TIFF_UNDEFINED} * * byte * * "Undefined" *
* SSHORT * * {@link TIFFTag#TIFF_SSHORT} * * short * * "SShort" *
* SLONG * * {@link TIFFTag#TIFF_SLONG} * * int * * "SLong" *
* SRATIONAL * * {@link TIFFTag#TIFF_SRATIONAL} * * int[2] {numerator, denominator} * * "SRational" *
* FLOAT * * {@link TIFFTag#TIFF_FLOAT} * * float * * "Float" *
* DOUBLE * * {@link TIFFTag#TIFF_DOUBLE} * * double * * "Double" *
* IFD * * {@link TIFFTag#TIFF_IFD_POINTER} * * long * * "IFDPointer" *
* * @since 9 * @see TIFFDirectory * @see TIFFTag */ public class TIFFField implements Cloneable { private static final String[] typeNames = { null, "Byte", "Ascii", "Short", "Long", "Rational", "SByte", "Undefined", "SShort", "SLong", "SRational", "Float", "Double", "IFDPointer" }; private static final boolean[] isIntegral = { false, true, false, true, true, false, true, true, true, true, false, false, false, false }; /** The tag. */ private TIFFTag tag; /** The tag number. */ private int tagNumber; /** The tag type. */ private int type; /** The number of data items present in the field. */ private int count; /** The field data. */ private Object data; /** The IFD contents if available. This will usually be a TIFFIFD. */ private TIFFDirectory dir; /** The default constructor. */ private TIFFField() {} private static String getAttribute(Node node, String attrName) { NamedNodeMap attrs = node.getAttributes(); return attrs.getNamedItem(attrName).getNodeValue(); } private static void initData(Node node, int[] otype, int[] ocount, Object[] odata) { int type; int count; Object data = null; String typeName = node.getNodeName(); typeName = typeName.substring(4); typeName = typeName.substring(0, typeName.length() - 1); type = TIFFField.getTypeByName(typeName); if (type == -1) { throw new IllegalArgumentException("typeName = " + typeName); } Node child = node.getFirstChild(); count = 0; while (child != null) { String childTypeName = child.getNodeName().substring(4); if (!typeName.equals(childTypeName)) { // warning } ++count; child = child.getNextSibling(); } if (count > 0) { data = createArrayForType(type, count); child = node.getFirstChild(); int idx = 0; while (child != null) { String value = getAttribute(child, "value"); String numerator, denominator; int slashPos; switch (type) { case TIFFTag.TIFF_ASCII: ((String[])data)[idx] = value; break; case TIFFTag.TIFF_BYTE: case TIFFTag.TIFF_SBYTE: ((byte[])data)[idx] = (byte)Integer.parseInt(value); break; case TIFFTag.TIFF_SHORT: ((char[])data)[idx] = (char)Integer.parseInt(value); break; case TIFFTag.TIFF_SSHORT: ((short[])data)[idx] = (short)Integer.parseInt(value); break; case TIFFTag.TIFF_SLONG: ((int[])data)[idx] = Integer.parseInt(value); break; case TIFFTag.TIFF_LONG: case TIFFTag.TIFF_IFD_POINTER: ((long[])data)[idx] = Long.parseLong(value); break; case TIFFTag.TIFF_FLOAT: ((float[])data)[idx] = Float.parseFloat(value); break; case TIFFTag.TIFF_DOUBLE: ((double[])data)[idx] = Double.parseDouble(value); break; case TIFFTag.TIFF_SRATIONAL: slashPos = value.indexOf("/"); numerator = value.substring(0, slashPos); denominator = value.substring(slashPos + 1); ((int[][])data)[idx] = new int[2]; ((int[][])data)[idx][0] = Integer.parseInt(numerator); ((int[][])data)[idx][1] = Integer.parseInt(denominator); break; case TIFFTag.TIFF_RATIONAL: slashPos = value.indexOf("/"); numerator = value.substring(0, slashPos); denominator = value.substring(slashPos + 1); ((long[][])data)[idx] = new long[2]; ((long[][])data)[idx][0] = Long.parseLong(numerator); ((long[][])data)[idx][1] = Long.parseLong(denominator); break; default: // error } idx++; child = child.getNextSibling(); } } otype[0] = type; ocount[0] = count; odata[0] = data; } /** * Creates a TIFFField from a TIFF native image * metadata node. If the value of the "tagNumber" attribute * of the node is not found in tagSet then a new * TIFFTag with name TIFFTag.UNKNOWN_TAG_NAME * will be created and assigned to the field. * * @param tagSet The TIFFTagSet to which the * TIFFTag of the field belongs. * @param node A native TIFF image metadata TIFFField node. * @throws NullPointerException if node is * null. * @throws IllegalArgumentException if the name of the node is not * "TIFFField". * @return A new {@code TIFFField}. */ public static TIFFField createFromMetadataNode(TIFFTagSet tagSet, Node node) { if (node == null) { throw new NullPointerException("node == null!"); } String name = node.getNodeName(); if (!name.equals("TIFFField")) { throw new IllegalArgumentException("!name.equals(\"TIFFField\")"); } int tagNumber = Integer.parseInt(getAttribute(node, "number")); TIFFTag tag = null; if (tagSet != null) { tag = tagSet.getTag(tagNumber); } int type = TIFFTag.TIFF_UNDEFINED; int count = 0; Object data = null; Node child = node.getFirstChild(); if (child != null) { String typeName = child.getNodeName(); if (typeName.equals("TIFFUndefined")) { String values = getAttribute(child, "value"); StringTokenizer st = new StringTokenizer(values, ","); count = st.countTokens(); byte[] bdata = new byte[count]; for (int i = 0; i < count; i++) { bdata[i] = (byte)Integer.parseInt(st.nextToken()); } type = TIFFTag.TIFF_UNDEFINED; data = bdata; } else { int[] otype = new int[1]; int[] ocount = new int[1]; Object[] odata = new Object[1]; initData(node.getFirstChild(), otype, ocount, odata); type = otype[0]; count = ocount[0]; data = odata[0]; } } else if (tag != null) { int t = TIFFTag.MAX_DATATYPE; while(t >= TIFFTag.MIN_DATATYPE && !tag.isDataTypeOK(t)) { t--; } type = t; } if (tag == null) { tag = new TIFFTag(TIFFTag.UNKNOWN_TAG_NAME, tagNumber, 1 << type); } return new TIFFField(tag, type, count, data); } /** * Constructs a TIFFField with arbitrary data. The * type parameter must be a value for which * {@link TIFFTag#isDataTypeOK tag.isDataTypeOK()} * returns true. The data parameter must * be an array of a Java type appropriate for the type of the TIFF * field. * *

Note that the value (data) of the TIFFField * will always be the actual field value regardless of the number of * bytes required for that value. This is the case despite the fact * that the TIFF IFD Entry corresponding to the field may * actually contain the offset to the value of the field rather than * the value itself (the latter occurring if and only if the * value fits into 4 bytes). In other words, the value of the * field will already have been read from the TIFF stream. (An exception * to this case may occur when the field represents the contents of a * non-baseline IFD. In that case the data will be a long[] * containing the offset to the IFD and the TIFFDirectory * returned by {@link #getDirectory()} will be its contents.) * * @param tag The tag to associated with this field. * @param type One of the TIFFTag.TIFF_* constants * indicating the data type of the field as written to the TIFF stream. * @param count The number of data values. * @param data The actual data content of the field. * * @throws NullPointerException if tag == null. * @throws IllegalArgumentException if type is not * one of the TIFFTag.TIFF_* data type constants. * @throws IllegalArgumentException if type is an unacceptable * data type for the supplied TIFFTag. * @throws IllegalArgumentException if count < 0. * @throws NullPointerException if data == null. * @throws IllegalArgumentException if data is an instance of * a class incompatible with the specified type. * @throws IllegalArgumentException if the size of the data array is wrong. */ public TIFFField(TIFFTag tag, int type, int count, Object data) { if(tag == null) { throw new NullPointerException("tag == null!"); } else if(type < TIFFTag.MIN_DATATYPE || type > TIFFTag.MAX_DATATYPE) { throw new IllegalArgumentException("Unknown data type "+type); } else if(!tag.isDataTypeOK(type)) { throw new IllegalArgumentException("Illegal data type " + type + " for " + tag.getName() + " tag"); } else if(count < 0) { throw new IllegalArgumentException("count < 0!"); } else if(data == null) { throw new NullPointerException("data == null!"); } boolean isDataArrayCorrect = false; switch (type) { case TIFFTag.TIFF_BYTE: case TIFFTag.TIFF_SBYTE: case TIFFTag.TIFF_UNDEFINED: isDataArrayCorrect = data instanceof byte[] && ((byte[])data).length == count; break; case TIFFTag.TIFF_ASCII: isDataArrayCorrect = data instanceof String[] && ((String[])data).length == count; break; case TIFFTag.TIFF_SHORT: isDataArrayCorrect = data instanceof char[] && ((char[])data).length == count; break; case TIFFTag.TIFF_LONG: isDataArrayCorrect = data instanceof long[] && ((long[])data).length == count; break; case TIFFTag.TIFF_IFD_POINTER: isDataArrayCorrect = data instanceof long[] && ((long[])data).length == 1; break; case TIFFTag.TIFF_RATIONAL: isDataArrayCorrect = data instanceof long[][] && ((long[][])data).length == count && ((long[][])data)[0].length == 2; break; case TIFFTag.TIFF_SSHORT: isDataArrayCorrect = data instanceof short[] && ((short[])data).length == count; break; case TIFFTag.TIFF_SLONG: isDataArrayCorrect = data instanceof int[] && ((int[])data).length == count; break; case TIFFTag.TIFF_SRATIONAL: isDataArrayCorrect = data instanceof int[][] && ((int[][])data).length == count && ((int[][])data)[0].length == 2; break; case TIFFTag.TIFF_FLOAT: isDataArrayCorrect = data instanceof float[] && ((float[])data).length == count; break; case TIFFTag.TIFF_DOUBLE: isDataArrayCorrect = data instanceof double[] && ((double[])data).length == count; break; default: throw new IllegalArgumentException("Unknown data type "+type); } if (!isDataArrayCorrect) { throw new IllegalArgumentException ("Illegal class or length for data array"); } this.tag = tag; this.tagNumber = tag.getNumber(); this.type = type; this.count = count; this.data = data; } /** * Constructs a data array using {@link #createArrayForType * createArrayForType()} and invokes * {@link #TIFFField(TIFFTag,int,int,Object)} with the supplied * parameters and the created array. * * @param tag The tag to associated with this field. * @param type One of the TIFFTag.TIFF_* constants * indicating the data type of the field as written to the TIFF stream. * @param count The number of data values. * @throws NullPointerException if tag == null. * @throws IllegalArgumentException if type is not * one of the TIFFTag.TIFF_* data type constants. * @throws IllegalArgumentException if type is an unacceptable * data type for the supplied TIFFTag. * @throws IllegalArgumentException if count < 0. * @see #TIFFField(TIFFTag,int,int,Object) */ public TIFFField(TIFFTag tag, int type, int count) { this(tag, type, count, createArrayForType(type, count)); } /** * Constructs a TIFFField with a single non-negative integral * value. * The field will have type * {@link TIFFTag#TIFF_SHORT TIFF_SHORT} if * val < 65536 and type * {@link TIFFTag#TIFF_LONG TIFF_LONG} otherwise. The count * of the field will be unity. * * @param tag The tag to associate with this field. * @param value The value to associate with this field. * @throws NullPointerException if tag == null. * @throws IllegalArgumentException if the derived type is unacceptable * for the supplied TIFFTag. * @throws IllegalArgumentException if value < 0. */ public TIFFField(TIFFTag tag, int value) { if(tag == null) { throw new NullPointerException("tag == null!"); } if (value < 0) { throw new IllegalArgumentException("value < 0!"); } this.tag = tag; this.tagNumber = tag.getNumber(); this.count = 1; if (value < 65536) { if (!tag.isDataTypeOK(TIFFTag.TIFF_SHORT)) { throw new IllegalArgumentException("Illegal data type " + TIFFTag.TIFF_SHORT + " for " + tag.getName() + " tag"); } this.type = TIFFTag.TIFF_SHORT; char[] cdata = new char[1]; cdata[0] = (char)value; this.data = cdata; } else { if (!tag.isDataTypeOK(TIFFTag.TIFF_LONG)) { throw new IllegalArgumentException("Illegal data type " + TIFFTag.TIFF_LONG + " for " + tag.getName() + " tag"); } this.type = TIFFTag.TIFF_LONG; long[] ldata = new long[1]; ldata[0] = value; this.data = ldata; } } /** * Constructs a TIFFField with an IFD offset and contents. * The offset will be stored as the data of this field as * long[] {offset}. The directory will not be cloned. The count * of the field will be unity. * * @param tag The tag to associated with this field. * @param type One of the constants TIFFTag.TIFF_LONG or * TIFFTag.TIFF_IFD_POINTER. * @param offset The IFD offset. * @param dir The directory. * * @throws NullPointerException if tag == null. * @throws IllegalArgumentException if type is neither * TIFFTag.TIFF_LONG nor TIFFTag.TIFF_IFD_POINTER. * @throws IllegalArgumentException if type is an unacceptable * data type for the supplied TIFFTag. * @throws IllegalArgumentException if offset is non-positive. * @throws NullPointerException if dir == null. * * @see #TIFFField(TIFFTag,int,int,Object) */ public TIFFField(TIFFTag tag, int type, long offset, TIFFDirectory dir) { this(tag, type, 1, new long[] {offset}); if (type != TIFFTag.TIFF_LONG && type != TIFFTag.TIFF_IFD_POINTER) { throw new IllegalArgumentException("type " + type + " is neither TIFFTag.TIFF_LONG nor TIFFTag.TIFF_IFD_POINTER"); } else if (offset <= 0) { throw new IllegalArgumentException("offset " + offset + " is non-positive"); } else if (dir == null) { throw new NullPointerException("dir == null"); } this.dir = dir; } /** * Retrieves the tag associated with this field. * * @return The associated TIFFTag. */ public TIFFTag getTag() { return tag; } /** * Retrieves the tag number in the range [0, 65535]. * * @return The tag number. */ public int getTagNumber() { return tagNumber; } /** * Returns the type of the data stored in the field. For a TIFF 6.0 * stream, the value will equal one of the TIFFTag.TIFF_* * constants. For future revisions of TIFF, higher values are possible. * * @return The data type of the field value. */ public int getType() { return type; } /** * Returns the name of the supplied data type constant. * * @param dataType One of the TIFFTag.TIFF_* constants * indicating the data type of the field as written to the TIFF stream. * @return The type name corresponding to the supplied type constant. * @throws IllegalArgumentException if dataType is not * one of the TIFFTag.TIFF_* data type constants. */ public static String getTypeName(int dataType) { if (dataType < TIFFTag.MIN_DATATYPE || dataType > TIFFTag.MAX_DATATYPE) { throw new IllegalArgumentException("Unknown data type "+dataType); } return typeNames[dataType]; } /** * Returns the data type constant corresponding to the supplied data * type name. If the name is unknown -1 will be returned. * * @param typeName The type name. * @return One of the TIFFTag.TIFF_* constants or * -1 if the name is not recognized. */ public static int getTypeByName(String typeName) { for (int i = TIFFTag.MIN_DATATYPE; i <= TIFFTag.MAX_DATATYPE; i++) { if (typeName.equals(typeNames[i])) { return i; } } return -1; } /** * Creates an array appropriate for the indicated data type. * * @param dataType One of the TIFFTag.TIFF_* data type * constants. * @param count The number of values in the array. * @return An array appropriate for the specified data type. * * @throws IllegalArgumentException if dataType is not * one of the TIFFTag.TIFF_* data type constants. * @throws IllegalArgumentException if count < 0. */ public static Object createArrayForType(int dataType, int count) { if(count < 0) { throw new IllegalArgumentException("count < 0!"); } switch (dataType) { case TIFFTag.TIFF_BYTE: case TIFFTag.TIFF_SBYTE: case TIFFTag.TIFF_UNDEFINED: return new byte[count]; case TIFFTag.TIFF_ASCII: return new String[count]; case TIFFTag.TIFF_SHORT: return new char[count]; case TIFFTag.TIFF_LONG: case TIFFTag.TIFF_IFD_POINTER: return new long[count]; case TIFFTag.TIFF_RATIONAL: return new long[count][2]; case TIFFTag.TIFF_SSHORT: return new short[count]; case TIFFTag.TIFF_SLONG: return new int[count]; case TIFFTag.TIFF_SRATIONAL: return new int[count][2]; case TIFFTag.TIFF_FLOAT: return new float[count]; case TIFFTag.TIFF_DOUBLE: return new double[count]; default: throw new IllegalArgumentException("Unknown data type "+dataType); } } /** * Returns the TIFFField as a node named either * "TIFFField" or "TIFFIFD" as described in the * TIFF native image metadata specification. The node will be named * "TIFFIFD" if and only if the field's data object is an * instance of {@link TIFFDirectory} or equivalently * {@link TIFFTag#isIFDPointer getTag.isIFDPointer()} returns * true. * * @return a Node named "TIFFField" or * "TIFFIFD". */ public Node getAsNativeNode() { return new TIFFFieldNode(this); } /** * Indicates whether the value associated with the field is of * integral data type. * * @return Whether the field type is integral. */ public boolean isIntegral() { return isIntegral[type]; } /** * Returns the number of data items present in the field. For * TIFFTag.TIFF_ASCII fields, the value returned is the * number of Strings, not the total length of the * data as in the file representation. * * @return The number of data items present in the field. */ public int getCount() { return count; } /** * Returns a reference to the data object associated with the field. * * @return The data object of the field. */ public Object getData() { return data; } /** * Returns the data as an uninterpreted array of * bytes. The type of the field must be one of * TIFFTag.TIFF_BYTE, TIFF_SBYTE, or * TIFF_UNDEFINED. * *

For data in TIFFTag.TIFF_BYTE format, the application * must take care when promoting the data to longer integral types * to avoid sign extension. * * @throws ClassCastException if the field is not of type * TIFF_BYTE, TIFF_SBYTE, or * TIFF_UNDEFINED. * @return The data as an uninterpreted array of bytes. */ public byte[] getAsBytes() { return (byte[])data; } /** * Returns TIFFTag.TIFF_SHORT data as an array of * chars (unsigned 16-bit integers). * * @throws ClassCastException if the field is not of type * TIFF_SHORT. * @return The data as an array of {@code char}s. */ public char[] getAsChars() { return (char[])data; } /** * Returns TIFFTag.TIFF_SSHORT data as an array of * shorts (signed 16-bit integers). * * @throws ClassCastException if the field is not of type * TIFF_SSHORT. * @return The data as an array of {@code short}s. */ public short[] getAsShorts() { return (short[])data; } /** * Returns TIFFTag.TIFF_SLONG data as an array of * ints (signed 32-bit integers). * * @throws ClassCastException if the field is not of type * TIFF_SHORT, TIFF_SSHORT, or * TIFF_SLONG. * @return The data as an array of {@code int}s. */ public int[] getAsInts() { if (data instanceof int[]) { return (int[])data; } else if (data instanceof char[]){ char[] cdata = (char[])data; int[] idata = new int[cdata.length]; for (int i = 0; i < cdata.length; i++) { idata[i] = cdata[i] & 0xffff; } return idata; } else if (data instanceof short[]){ short[] sdata = (short[])data; int[] idata = new int[sdata.length]; for (int i = 0; i < sdata.length; i++) { idata[i] = (int)sdata[i]; } return idata; } else { throw new ClassCastException("Data not char[], short[], or int[]!"); } } /** * Returns TIFFTag.TIFF_LONG or * TIFF_IFD_POINTER data as an array of * longs (signed 64-bit integers). * * @throws ClassCastException if the field is not of type * TIFF_LONG or TIFF_IFD_POINTER. * @return The data as an array of {@code long}s. */ public long[] getAsLongs() { return (long[])data; } /** * Returns TIFFTag.TIFF_FLOAT data as an array of * floats (32-bit floating-point values). * * @throws ClassCastException if the field is not of type * TIFF_FLOAT. * @return The data as an array of {@code float}s. */ public float[] getAsFloats() { return (float[])data; } /** * Returns TIFFTag.TIFF_DOUBLE data as an array of * doubles (64-bit floating-point values). * * @throws ClassCastException if the field is not of type * TIFF_DOUBLE. * @return The data as an array of {@code double}s. */ public double[] getAsDoubles() { return (double[])data; } /** * Returns TIFFTag.TIFF_SRATIONAL data as an array of * 2-element arrays of ints. * * @throws ClassCastException if the field is not of type * TIFF_SRATIONAL. * @return The data as an array of signed rationals. */ public int[][] getAsSRationals() { return (int[][])data; } /** * Returns TIFFTag.TIFF_RATIONAL data as an array of * 2-element arrays of longs. * * @throws ClassCastException if the field is not of type * TIFF_RATIONAL. * @return The data as an array of unsigned rationals. */ public long[][] getAsRationals() { return (long[][])data; } /** * Returns data in any format as an int. * *

TIFFTag.TIFF_BYTE values are treated as unsigned; that * is, no sign extension will take place and the returned value * will be in the range [0, 255]. TIFF_SBYTE data * will be returned in the range [-128, 127]. * *

A TIFF_UNDEFINED value is treated as though * it were a TIFF_BYTE. * *

Data in TIFF_SLONG, TIFF_LONG, * TIFF_FLOAT, TIFF_DOUBLE or * TIFF_IFD_POINTER format are simply cast to * int and may suffer from truncation. * *

Data in TIFF_SRATIONAL or * TIFF_RATIONAL format are evaluated by dividing the * numerator into the denominator using double-precision * arithmetic and then casting to int. Loss of * precision and truncation may occur. * *

Data in TIFF_ASCII format will be parsed as by * the Double.parseDouble method, with the result * case to int. * * @param index The index of the data. * @return The data at the given index as an {@code int}. */ public int getAsInt(int index) { switch (type) { case TIFFTag.TIFF_BYTE: case TIFFTag.TIFF_UNDEFINED: return ((byte[])data)[index] & 0xff; case TIFFTag.TIFF_SBYTE: return ((byte[])data)[index]; case TIFFTag.TIFF_SHORT: return ((char[])data)[index] & 0xffff; case TIFFTag.TIFF_SSHORT: return ((short[])data)[index]; case TIFFTag.TIFF_SLONG: return ((int[])data)[index]; case TIFFTag.TIFF_LONG: case TIFFTag.TIFF_IFD_POINTER: return (int)((long[])data)[index]; case TIFFTag.TIFF_FLOAT: return (int)((float[])data)[index]; case TIFFTag.TIFF_DOUBLE: return (int)((double[])data)[index]; case TIFFTag.TIFF_SRATIONAL: int[] ivalue = getAsSRational(index); return (int)((double)ivalue[0]/ivalue[1]); case TIFFTag.TIFF_RATIONAL: long[] lvalue = getAsRational(index); return (int)((double)lvalue[0]/lvalue[1]); case TIFFTag.TIFF_ASCII: String s = ((String[])data)[index]; return (int)Double.parseDouble(s); default: throw new ClassCastException(); // should never happen } } /** * Returns data in any format as a long. * *

TIFFTag.TIFF_BYTE and TIFF_UNDEFINED data * are treated as unsigned; that is, no sign extension will take * place and the returned value will be in the range [0, 255]. * TIFF_SBYTE data will be returned in the range * [-128, 127]. * *

Data in TIFF_ASCII format will be parsed as by * the Double.parseDouble method, with the result * cast to long. * * @param index The index of the data. * @return The data at the given index as a {@code long}. */ public long getAsLong(int index) { switch (type) { case TIFFTag.TIFF_BYTE: case TIFFTag.TIFF_UNDEFINED: return ((byte[])data)[index] & 0xff; case TIFFTag.TIFF_SBYTE: return ((byte[])data)[index]; case TIFFTag.TIFF_SHORT: return ((char[])data)[index] & 0xffff; case TIFFTag.TIFF_SSHORT: return ((short[])data)[index]; case TIFFTag.TIFF_SLONG: return ((int[])data)[index]; case TIFFTag.TIFF_LONG: case TIFFTag.TIFF_IFD_POINTER: return ((long[])data)[index]; case TIFFTag.TIFF_SRATIONAL: int[] ivalue = getAsSRational(index); return (long)((double)ivalue[0]/ivalue[1]); case TIFFTag.TIFF_RATIONAL: long[] lvalue = getAsRational(index); return (long)((double)lvalue[0]/lvalue[1]); case TIFFTag.TIFF_ASCII: String s = ((String[])data)[index]; return (long)Double.parseDouble(s); default: throw new ClassCastException(); // should never happen } } /** * Returns data in any format as a float. * *

TIFFTag.TIFF_BYTE and TIFF_UNDEFINED data * are treated as unsigned; that is, no sign extension will take * place and the returned value will be in the range [0, 255]. * TIFF_SBYTE data will be returned in the range * [-128, 127]. * *

Data in TIFF_SLONG, TIFF_LONG, * TIFF_DOUBLE, or TIFF_IFD_POINTER format are * simply cast to float and may suffer from * truncation. * *

Data in TIFF_SRATIONAL or * TIFF_RATIONAL format are evaluated by dividing the * numerator into the denominator using double-precision * arithmetic and then casting to float. * *

Data in TIFF_ASCII format will be parsed as by * the Double.parseDouble method, with the result * cast to float. * * @param index The index of the data. * @return The data at the given index as a {@code float}. */ public float getAsFloat(int index) { switch (type) { case TIFFTag.TIFF_BYTE: case TIFFTag.TIFF_UNDEFINED: return ((byte[])data)[index] & 0xff; case TIFFTag.TIFF_SBYTE: return ((byte[])data)[index]; case TIFFTag.TIFF_SHORT: return ((char[])data)[index] & 0xffff; case TIFFTag.TIFF_SSHORT: return ((short[])data)[index]; case TIFFTag.TIFF_SLONG: return ((int[])data)[index]; case TIFFTag.TIFF_LONG: case TIFFTag.TIFF_IFD_POINTER: return ((long[])data)[index]; case TIFFTag.TIFF_FLOAT: return ((float[])data)[index]; case TIFFTag.TIFF_DOUBLE: return (float)((double[])data)[index]; case TIFFTag.TIFF_SRATIONAL: int[] ivalue = getAsSRational(index); return (float)((double)ivalue[0]/ivalue[1]); case TIFFTag.TIFF_RATIONAL: long[] lvalue = getAsRational(index); return (float)((double)lvalue[0]/lvalue[1]); case TIFFTag.TIFF_ASCII: String s = ((String[])data)[index]; return (float)Double.parseDouble(s); default: throw new ClassCastException(); // should never happen } } /** * Returns data in any format as a double. * *

TIFFTag.TIFF_BYTE and TIFF_UNDEFINED data * are treated as unsigned; that is, no sign extension will take * place and the returned value will be in the range [0, 255]. * TIFF_SBYTE data will be returned in the range * [-128, 127]. * *

Data in TIFF_SRATIONAL or * TIFF_RATIONAL format are evaluated by dividing the * numerator into the denominator using double-precision * arithmetic. * *

Data in TIFF_ASCII format will be parsed as by * the Double.parseDouble method. * * @param index The index of the data. * @return The data at the given index as a {@code double}. */ public double getAsDouble(int index) { switch (type) { case TIFFTag.TIFF_BYTE: case TIFFTag.TIFF_UNDEFINED: return ((byte[])data)[index] & 0xff; case TIFFTag.TIFF_SBYTE: return ((byte[])data)[index]; case TIFFTag.TIFF_SHORT: return ((char[])data)[index] & 0xffff; case TIFFTag.TIFF_SSHORT: return ((short[])data)[index]; case TIFFTag.TIFF_SLONG: return ((int[])data)[index]; case TIFFTag.TIFF_LONG: case TIFFTag.TIFF_IFD_POINTER: return ((long[])data)[index]; case TIFFTag.TIFF_FLOAT: return ((float[])data)[index]; case TIFFTag.TIFF_DOUBLE: return ((double[])data)[index]; case TIFFTag.TIFF_SRATIONAL: int[] ivalue = getAsSRational(index); return (double)ivalue[0]/ivalue[1]; case TIFFTag.TIFF_RATIONAL: long[] lvalue = getAsRational(index); return (double)lvalue[0]/lvalue[1]; case TIFFTag.TIFF_ASCII: String s = ((String[])data)[index]; return Double.parseDouble(s); default: throw new ClassCastException(); // should never happen } } /** * Returns a TIFFTag.TIFF_ASCII value as a * String. * * @throws ClassCastException if the field is not of type * TIFF_ASCII. * * @param index The index of the data. * @return The data at the given index as a {@code String}. */ public String getAsString(int index) { return ((String[])data)[index]; } /** * Returns a TIFFTag.TIFF_SRATIONAL data item as a * two-element array of ints. * * @param index The index of the data. * @return The data at the given index as a signed rational. * @throws ClassCastException if the field is not of type * TIFF_SRATIONAL. */ public int[] getAsSRational(int index) { return ((int[][])data)[index]; } /** * Returns a TIFFTag.TIFF_RATIONAL data item as a two-element array * of ints. * * @param index The index of the data. * @return The data at the given index as an unsigned rational. * @throws ClassCastException if the field is not of type * TIFF_RATIONAL. */ public long[] getAsRational(int index) { return ((long[][])data)[index]; } /** * Returns a String containing a human-readable * version of the data item. Data of type * TIFFTag.TIFF_RATIONAL or TIFF_SRATIONAL are * represented as a pair of integers separated by a * '/' character. * * @param index The index of the data. * @return The data at the given index as a {@code String}. * @throws ClassCastException if the field is not of one of the * legal field types. */ public String getValueAsString(int index) { switch (type) { case TIFFTag.TIFF_ASCII: return ((String[])data)[index]; case TIFFTag.TIFF_BYTE: case TIFFTag.TIFF_UNDEFINED: return Integer.toString(((byte[])data)[index] & 0xff); case TIFFTag.TIFF_SBYTE: return Integer.toString(((byte[])data)[index]); case TIFFTag.TIFF_SHORT: return Integer.toString(((char[])data)[index] & 0xffff); case TIFFTag.TIFF_SSHORT: return Integer.toString(((short[])data)[index]); case TIFFTag.TIFF_SLONG: return Integer.toString(((int[])data)[index]); case TIFFTag.TIFF_LONG: case TIFFTag.TIFF_IFD_POINTER: return Long.toString(((long[])data)[index]); case TIFFTag.TIFF_FLOAT: return Float.toString(((float[])data)[index]); case TIFFTag.TIFF_DOUBLE: return Double.toString(((double[])data)[index]); case TIFFTag.TIFF_SRATIONAL: int[] ivalue = getAsSRational(index); String srationalString; if(ivalue[1] != 0 && ivalue[0] % ivalue[1] == 0) { // If the denominator is a non-zero integral divisor // of the numerator then convert the fraction to be // with respect to a unity denominator. srationalString = Integer.toString(ivalue[0] / ivalue[1]) + "/1"; } else { // Use the values directly. srationalString = Integer.toString(ivalue[0]) + "/" + Integer.toString(ivalue[1]); } return srationalString; case TIFFTag.TIFF_RATIONAL: long[] lvalue = getAsRational(index); String rationalString; if(lvalue[1] != 0L && lvalue[0] % lvalue[1] == 0) { // If the denominator is a non-zero integral divisor // of the numerator then convert the fraction to be // with respect to a unity denominator. rationalString = Long.toString(lvalue[0] / lvalue[1]) + "/1"; } else { // Use the values directly. rationalString = Long.toString(lvalue[0]) + "/" + Long.toString(lvalue[1]); } return rationalString; default: throw new ClassCastException(); // should never happen } } /** * Returns whether the field has a TIFFDirectory. * * @return true if and only if getDirectory() returns non-null. */ public boolean hasDirectory() { return getDirectory() != null; } /** * Returns the associated TIFFDirectory, if available. If no * directory is set, then null will be returned. * * @return the TIFFDirectory instance or null. */ public TIFFDirectory getDirectory() { return dir; } /** * Clones the field and all the information contained therein. * * @return A clone of this TIFFField. * @throws CloneNotSupportedException if the instance cannot be cloned. */ @Override public TIFFField clone() throws CloneNotSupportedException { TIFFField field = (TIFFField)super.clone(); Object fieldData; switch (type) { case TIFFTag.TIFF_BYTE: case TIFFTag.TIFF_UNDEFINED: case TIFFTag.TIFF_SBYTE: fieldData = ((byte[])data).clone(); break; case TIFFTag.TIFF_SHORT: fieldData = ((char[])data).clone(); break; case TIFFTag.TIFF_SSHORT: fieldData = ((short[])data).clone(); break; case TIFFTag.TIFF_SLONG: fieldData = ((int[])data).clone(); break; case TIFFTag.TIFF_LONG: case TIFFTag.TIFF_IFD_POINTER: fieldData = ((long[])data).clone(); break; case TIFFTag.TIFF_FLOAT: fieldData = ((float[])data).clone(); break; case TIFFTag.TIFF_DOUBLE: fieldData = ((double[])data).clone(); break; case TIFFTag.TIFF_SRATIONAL: fieldData = ((int[][])data).clone(); break; case TIFFTag.TIFF_RATIONAL: fieldData = ((long[][])data).clone(); break; case TIFFTag.TIFF_ASCII: fieldData = ((String[])data).clone(); break; default: throw new ClassCastException(); // should never happen } field.tag = tag; field.tagNumber = tagNumber; field.type = type; field.count = count; field.data = fieldData; field.dir = dir != null ? dir.clone() : null; return field; } }