
edu.harvard.hul.ois.jhove.module.tiff.IFD Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jhove-modules Show documentation
Show all versions of jhove-modules Show documentation
The JHOVE HUL validation modules.
The newest version!
/********************************************************************** * Jhove - JSTOR/Harvard Object Validation Environment * Copyright 2004 by JSTOR and the President and Fellows of Harvard College **********************************************************************/ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; import java.io.*; import java.text.*; import java.util.*; /** * Encapsulation of a TIFF image file directory (IFD). */ public abstract class IFD { /****************************************************************** * DEBUGGING FIELDS. * All debugging fields should be set to false for release code. ******************************************************************/ /* Set to true to allow out-of-sequence tags. */ private static final boolean debug_allowoutofsequence = false; /****************************************************************** * PUBLIC CLASS FIELDS. ******************************************************************/ /** Standard TIFF IFD. */ public static final int TIFF = 0; /** Exif IFD. */ public static final int EXIF = 1; /** Exif Interoperability IFD. */ public static final int INTEROPERABILITY = 2; /** GPSInfo IFD. */ public static final int GPSINFO = 3; /** Global parameters IFD. */ public static final int GLOBALPARAMETERS = 4; /** Undefined value for integer tags. */ public static final int NULL = -1; /****************************************************************** * PRIVATE CLASS FIELDS. ******************************************************************/ /* TIFF data types. */ /** TIFF BYTE (unsigned 8-bit) type. */ public static final int BYTE = 1; /** TIFF ASCII type. */ public static final int ASCII = 2; /** TIFF SHORT (unsigned 16-bit) type. */ public static final int SHORT = 3; /** TIFF LONG (unsigned 32-bit) type. */ public static final int LONG = 4; /** TIFF RATIONAL (two LONGs) type. */ public static final int RATIONAL = 5; /** TIFF SBYTE (signed 8-bit) type. */ public static final int SBYTE = 6; /** TIFF UNDEFINED (unsigned 8-bit) type. */ public static final int UNDEFINED = 7; /** TIFF SSHORT (signed 16-bit) type. */ public static final int SSHORT = 8; /** TIFF SLONG (signed 32-bit) type. */ public static final int SLONG = 9; /** TIFF SRATIONAL (two SLONGs) type. */ public static final int SRATIONAL = 10; /** TIFF FLOAT (32-bit IEEE floating point) type. */ public static final int FLOAT = 11; /** TIFF DOUBLE (64-bit IEEE floating point) type. */ public static final int DOUBLE = 12; /** TIFF IFD (LONG) type. */ public static final int IFD = 13; /** TIFF type labels. */ public static final String TYPE [] = { "", "BYTE", "ASCII", "SHORT", "LONG", "RATIONAL", "SBYTE", "UNDEFINED", "SSHORT", "SLONG", "SRATIONAL", "FLOAT", "DOUBLE", "IFD" }; /****************************************************************** * PRIVATE INSTANCE FIELDS. ******************************************************************/ /** True if big-endian file. */ protected boolean _bigEndian; /** List of errors. */ private List
is true, returns * an INTEGER property, and_errors; /** True if this is the first IFD. */ private boolean _first; /** True if the is the "thumbnail" IFD. */ private boolean _thumbnail; /** Format for converting float to string. */ private NumberFormat _format; /** Representation information. */ protected RepInfo _info; /** Offset of next IFD. */ protected long _next; /** IFD offset. */ protected long _offset; /** Open random access TIFF file. */ private RandomAccessFile _raf; /** TIFF version. */ protected int _version; /****************************************************************** * CLASS CONSTRUCTOR. ******************************************************************/ /** Instantiate an IFD
object. * @param offset IFD offset * @param info Representation information * @param raf TIFF file * @param bigEndian True if big-endian file */ public IFD(long offset, RepInfo info, RandomAccessFile raf, boolean bigEndian) { _offset = offset; _info = info; _raf = raf; _bigEndian = bigEndian; _first = false; _thumbnail = false; _next = 0L; _version = 4; _errors = new LinkedList(); _format = NumberFormat.getInstance(); _format.setGroupingUsed(false); _format.setMinimumFractionDigits(0); } /****************************************************************** * PUBLIC INSTANCE METHODS. ******************************************************************/ /** Get any errors discovered during parsing. */ public List getErrors() { return _errors; } /** Get the offset of the next IFD. */ public long getNext() { return _next; } /** Get the IFD offset. */ public long getOffset() { return _offset; } /** Get the IFD properties. */ public abstract Property getProperty(boolean rawOutput) throws TiffException; /** Get the TIFF version. */ public int getVersion() { return _version; } /** Return true if this is the first IFD. */ public boolean isFirst() { return _first; } /** Return true if this is the thumbnail IFD. */ public boolean isThumbnail() { return _thumbnail; } /** Lookup IFD tag. */ public abstract void lookupTag(int tag, int type, long count, long value) throws TiffException; /** Parse the IFD. Errors are not suppressed, and odd byte offsets for * tags not allowed. * * @return The offset of the next IFD */ public long parse() throws TiffException { return parse(false, false); } /** Parse the IFD. * @param byteOffsetIsValid If true, allow offsets on odd byte boundaries * @param suppressErrors If true, return IFD even with errors * @return The offset of the next IFD */ public long parse(boolean byteOffsetIsValid, boolean suppressErrors) throws TiffException { try { return parse(byteOffsetIsValid); } catch (TiffException e) { // If we got a TiffException and we're suppressing errors, // cover over the exception and issue an info message; // but we can't follow the IFD chain further. if (suppressErrors) { _info.setMessage (new InfoMessage(e.getMessage(), e.getOffset())); return 0; } throw e; } } /** Parse the IFD. Errors are not suppressed. * * @param byteOffsetIsValid If true, allow offsets on odd byte boundaries * @return The offset of the next IFD */ public long parse(boolean byteOffsetIsValid) throws TiffException { /* Start at the IFD offset, read the number of entries, then * read the entire IFD. */ long offset = _offset; _next = 0L; byte [] buffer; int nFields = 0; try { _raf.seek(offset); nFields = ModuleBase.readUnsignedShort(_raf, _bigEndian); offset += 2; int len = 12*nFields; buffer = new byte[len]; _raf.read(buffer, 0, len); /* Read the offset of the next IFD (or 0 if none). */ offset += len; _next = ModuleBase.readUnsignedInt(_raf, _bigEndian); } catch (Exception e) { throw new TiffException(MessageConstants.ERR_TIFF_PREM_EOF, offset); } DataInputStream ifdStream = new DataInputStream(new ByteArrayInputStream(buffer)); try { int prevTag = 0; for (int i=0; i IFD) { _info.setMessage(new ErrorMessage(MessageConstants.ERR_UNK_DATA_TYPE, MessageConstants.ERR_UNK_DATA_TYPE_SUB_1 + type + MessageConstants.ERR_UNK_DATA_TYPE_SUB_2 + tag, _offset + 4 + 12*i)); } else { /* Type gives indication of the TIFF version. */ if (SBYTE <= type && type <= IFD) { _version = 6; } long count = ModuleBase.readUnsignedInt(ifdStream, _bigEndian, null); long value = ModuleBase.readUnsignedInt(ifdStream, _bigEndian, null); if (calcValueSize(type, count) > 4) { /* Value is the word-aligned offset of the actual * value. */ if ((value & 1) != 0) { final String message = MessageConstants.INF_VALOFF_NOT_WORD_ALIGN + value; if (byteOffsetIsValid) { _info.setMessage(new InfoMessage(message, _offset + 10 + 12*i)); } else { throw new TiffException(message,_offset + 10 + 12*i); } } } else { /* Value is the actual value; pass the offset of * the value. */ value = _offset + 10 + 12*i; } lookupTag(tag, type, count, value); } } } catch (IOException e) { throw new TiffException(MessageConstants.ERR_IO_READ, _offset + 2); } postParseInitialization(); return _next; } /** Sets flag indicating whether this is the first IFD. */ public void setFirst(boolean first) { _first = first; } /** Sets flag indicating whether this is the "thumbnail" IFD. * The second IFD in the top-level chain is assumed to be * the Thumbnail IFD. */ public void setThumbnail(boolean thumbnail) { _thumbnail = thumbnail; } /** * Returns a Property representing a bitmask. * If rawOutput
is true, returns a LIST * property whose elements are STRING properties. The * string values of these STRING properties are the * elements oflabels
whose indices * correspond to 1 bits in the bitmask, counting * the low-order bit as bit 0. * ifrawOutput
is false, returns a LONG * property whose numeric value isvalue
. */ protected Property addBitmaskProperty(String name, long value, String [] labels, boolean rawOutput) { Property prop = null; if (!rawOutput) { Listlist = new LinkedList (); try { for (int i=0; i rawOutput labels
and *index
are unused. Otherwise, * returns a STRING property, with the * string being the element oflabels
* whose index isvalue
. */ protected Property addIntegerProperty(String name, int value, String [] labels, boolean rawOutput) { Property prop = null; if (!rawOutput) { try { prop = new Property(name, PropertyType.STRING, labels[value]); } catch (ArrayIndexOutOfBoundsException aioobe) { _errors.add(name + MessageConstants.ERR_VAL_OUT_OF_RANGE + value); } } if (prop == null) { prop = new Property(name, PropertyType.INTEGER, new Integer(value)); } return prop; } /** * Returns an Property representing an integer value. * IfrawOutput
is true, returns * an INTEGER property, andlabels
and *index
are unused. Otherwise, * returns a STRING property, with the * string being the element oflabels
* whose index is the index of *value
inindex
. */ protected Property addIntegerProperty(String name, int value, String [] labels, int [] index, boolean rawOutput) { Property prop = null; if (!rawOutput) { int n = -1; for (int i = 0; i < index.length; i++) { if (value == index[i]) { n = i; break; } } if (n > -1) { prop = new Property(name, PropertyType.STRING, labels[n]); } else { _errors.add(name + MessageConstants.ERR_VAL_OUT_OF_RANGE + value); } } if (prop == null) { prop = new Property(name, PropertyType.INTEGER, new Integer(value)); } return prop; } /** * Returns an ARRAY Property representing an integer array. * IfrawOutput
is true, the elements of the property array * are INTEGER properties, andlabels
is unused. Otherwise, * the elements of the array are STRING properties, with the * elements ofvalue
used as indices into *labels
. */ protected Property addIntegerArrayProperty(String name, int [] value, String [] labels, boolean rawOutput) { Property prop = null; if (!rawOutput) { String [] s = new String[value.length]; for (int i=0; i127) { sb.append(byteToHex(c)); } else { sb.append((char) c); } } return sb.toString(); } /** Reads an array of strings from the TIFF file. * * @param count Number of strings to read * @param value Offset from which to read * */ protected String [] readASCIIArray(long count, long value) throws IOException { _raf.seek(value); int nstrs = 0; List list = new LinkedList (); byte[] buf = new byte[(int) count]; _raf.read(buf); StringBuffer strbuf = new StringBuffer(); for (int i=0; i 127) { strbuf.append(byteToHex((byte) b)); } else { strbuf.append((char) b); } } } /* We can't use ArrayList.toArray because that returns an Object[], not a String[] ... sigh. */ String [] strs = new String[nstrs]; ListIterator iter = list.listIterator(); for (int i=0; i true if file is big-endian, * false
if little-endian. */ public boolean isBigEndian() { return _bigEndian; } /****************************************************************** * PRIVATE CLASS METHODS. ******************************************************************/ /** * Check the tag entry count. * @param tag Tag entry value * @param count Tag entry count * @param minCount Tag count */ protected static void checkCount(int tag, long count, int minCount) throws TiffException { if (count < minCount) { throw new TiffException(MessageConstants.ERR_TAG_COUNT_MISMATCH + tag + MessageConstants.MISMATCH_SUB_1 + minCount + MessageConstants.MISMATCH_SUB_2 + count); } } /** * Check the tag entry type. * @param tag Tag entry value * @param type Tag entry type * @param expected Tag type */ protected static void checkType(int tag, int type, int expected) throws TiffException { /* Readers are supposed to accept BYTE, SHORT or LONG for any * unsigned integer field. */ if (type == BYTE || type == SHORT || type == LONG || type == IFD) { if (expected == BYTE || expected == SHORT || expected == LONG || expected == IFD) { return; // it's OK } } if (type != expected) { throw new TiffException(MessageConstants.ERR_TAG_TYPE_MISMATCH + tag + MessageConstants.MISMATCH_SUB_1 + expected + MessageConstants.MISMATCH_SUB_2 + type); } } /** * Check the tag entry type. * @param tag Tag entry value * @param type Tag entry type * @param type1 Tag type * @param type2 Alternate tag type */ protected static void checkType(int tag, int type, int type1, int type2) throws TiffException { if (type != type1 && type != type2) { throw new TiffException(MessageConstants.ERR_TAG_TYPE_MISMATCH + tag + MessageConstants.MISMATCH_SUB_1 + type1 + MessageConstants.MISMATCH_SUB_3 + type2 + MessageConstants.MISMATCH_SUB_2 + type); } } protected static Rational average(Rational r1, Rational r2) { long d1 = r1.getDenominator(); long d2 = r2.getDenominator(); Rational f1 = new Rational(r1.getNumerator()*d2, r1.getDenominator()*d2); Rational f2 = new Rational(r2.getNumerator()*d1, r2.getDenominator()*d1); return new Rational((f1.getNumerator() + f2.getNumerator())/2, f1.getDenominator()); } /****************************************************************** * PRIVATE INSTANCE METHODS. ******************************************************************/ /** Represent a byte value as %XX */ private String byteToHex(byte c) { int[] nibbles = new int[2]; nibbles[0] = ((int) c & 0XF0) >> 4; nibbles[1] = (int) c & 0X0F; StringBuffer retval = new StringBuffer("%"); for (int i = 0; i <= 1; i++) { int b = nibbles[i]; if (b >= 10) { b += (int) 'A' - 10; } else { b += (int) '0'; } retval.append((char) b); } return retval.toString(); } }
© 2015 - 2025 Weber Informatics LLC | Privacy Policy