All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ru.sbtqa.monte.media.tiff.IFDEntry Maven / Gradle / Ivy

/* @(#)IFDEntry.java
 * Copyright © 2009-2010 Werner Randelshofer, Switzerland.
 * You may only use this software in accordance with the license terms.
 */
package ru.sbtqa.monte.media.tiff;

import java.io.IOException;
import static java.lang.Integer.toHexString;
import static java.lang.Long.toHexString;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static ru.sbtqa.monte.media.tiff.IFDDataType.valueOf;

/**
 * Represents a directory entry in a TIFF Image File Directory (IFD).
 * 
 * Each 12-byte IFD entry has the following format:
 * 
 * Bytes 0-1 The Tag that identifies the field.
 * Bytes 2-3 The field Type.
 * Bytes 4-7 The number of values, Count of the indicated Type.
 * Bytes 8-11 The Value Offset, the file offset (in bytes) of the Value for
 * the field. The Value is expected to begin on a word boundary; the
 * corresponding Value Offset will thus be an even number. This file offset may
 * point anywhere in the file, even after the image data.
 * 
 *
 * @author Werner Randelshofer
 * @version 2.1 2010-09-07 Stores ifdOffset.
 * 
2.0 2010-07-24 Reworked. *
1.0 2009-12-26 Created. */ public class IFDEntry { /** * The Tag number that identifies the field. */ private int tagNumber; /** * The field Type. */ private int typeNumber; /** * The number of values, Count of the indicated Type. */ private long count; /** * The Value Offset stores the value or the offset of the value depending on * typeNumber and on count. */ private long valueOffset; /** * The Entry Offset stores the location of the entry in the file. */ private long entryOffset; /** * The IFD Offset stores the location of the IFD in the file. */ private long ifdOffset; /* The entry data. */ private Object data; public IFDEntry(int tagNumber, int typeNumber, long count, long valueOffset, long entryOffset) { this.tagNumber = tagNumber; this.typeNumber = typeNumber; this.count = count; this.valueOffset = valueOffset; this.entryOffset = entryOffset; } public long getCount() { return count; } public int getTagNumber() { return tagNumber; } public int getTypeNumber() { return typeNumber; } /** * The value offset may either contain the data or point to the data * depending on the type and the count. * * @return The value offset. */ public long getValueOffset() { return valueOffset; } /** * The offset to the data. * * @return TODO */ public long getDataOffset() { return isDataInValueOffset() ? entryOffset + 8 : valueOffset + ifdOffset; } public void setIFDOffset(long newValue) { ifdOffset = newValue; } public long getEntryOffset() { return entryOffset; } public long getIFDOffset() { return ifdOffset; } public boolean isDataInValueOffset() { switch (valueOf(typeNumber)) { case ASCII://8-bit byte that contains a 7-bit ASCII code; the last byte //must be NUL (binary zero). return false; case BYTE://8-bit unsigned integer. return count <= 4; case SHORT://16-bit (2-byte) unsigned integer. return count <= 2; case LONG://32-bit (4-byte) unsigned integer. return count <= 1; case RATIONAL://Two LONGs: the first represents the numerator of a fraction; the second, the denominator. return false; case SBYTE: //An 8-bit signed (twos-complement) integer. return count <= 4; case UNDEFINED://An 8-bit byte that may contain anything, depending on the definition of the field. return count <= 4; case SSHORT://A 16-bit (2-byte) signed (twos-complement) integer. return count <= 2; case SLONG://A 32-bit (4-byte) signed (twos-complement) integer. return count <= 1; case SRATIONAL://Two SLONG’s: the first represents the numerator of a fraction, the second the denominator. return false; case FLOAT://Single precision (4-byte) IEEE prettyFormat. return count <= 1; case DOUBLE:// Double precision (8-byte) IEEE prettyFormat. return false; default: return true; } } public long getLength() { switch (valueOf(typeNumber)) { case ASCII://8-bit byte that contains a 7-bit ASCII code; the last byte return count; case BYTE://8-bit unsigned integer. return count; case SHORT://16-bit (2-byte) unsigned integer. return count * 2; case LONG://32-bit (4-byte) unsigned integer. return count * 4; case RATIONAL://Two LONGs: the first represents the numerator of a fraction; the second, the denominator. return count * 8; case SBYTE: //An 8-bit signed (twos-complement) integer. return count; case UNDEFINED://An 8-bit byte that may contain anything, depending on the definition of the field. return count; case SSHORT://A 16-bit (2-byte) signed (twos-complement) integer. return count * 2; case SLONG://A 32-bit (4-byte) signed (twos-complement) integer. return count * 4; case SRATIONAL://Two SLONG’s: the first represents the numerator of a fraction, the second the denominator. return count * 8; case FLOAT://Single precision (4-byte) IEEE prettyFormat. return count * 4; case DOUBLE:// Double precision (8-byte) IEEE prettyFormat. return count * 8; default: return 0; } } /** * Reads value data with ifdDataOffset= * * @param in TODO * @return 0 * @throws java.io.IOException TODO */ public Object readData(TIFFInputStream in) throws IOException { return readData(in, ifdOffset); } /** * Reads value data with the specified ifdDataOffset * * @param in TODO * @param ifdDataOffset TODO * @return . * @throws java.io.IOException TODO */ public Object readData(TIFFInputStream in, long ifdDataOffset) throws IOException { Object d = null; IFDDataType tt = valueOf(typeNumber); if (tt != null) { switch (tt) { case ASCII://8-bit byte that contains a 7-bit ASCII code; the last byte //must be NUL (binary zero). if (count <= 4) { StringBuilder buf = new StringBuilder(); int data = (int) valueOffset; if (in.getByteOrder() == LITTLE_ENDIAN) { for (int i = 0; i < count - 1; i++) { buf.append((char) (data & 0xff)); data >>= 8; } } else { for (int i = 0; i < count - 1; i++) { buf.append((char) (data >>> 24)); data <<= 8; } } return buf.toString(); } else { return in.readASCII(valueOffset + ifdDataOffset, count); } case SHORT://16-bit (2-byte) unsigned integer. if (count == 1) { if (in.getByteOrder() == LITTLE_ENDIAN) { d = (int) (valueOffset & 0xffff); } else { d = (int) ((valueOffset >> 16) & 0xffff); } } else if (count == 2) { d = new int[]{(int) (valueOffset & 0xffff), (int) ((valueOffset & 0xffff0000) >> 16)}; } else { d = in.readSHORT(valueOffset + ifdDataOffset, count); } break; case LONG://32-bit (4-byte) unsigned integer. if (count == 1) { d = valueOffset; } else { d = in.readLONG(valueOffset + ifdDataOffset, count); } break; case RATIONAL://Two LONGs: the first represents the numerator of a fraction; the second, the denominator. if (count == 1) { d = in.readRATIONAL(valueOffset + ifdDataOffset); } else { d = in.readRATIONAL(valueOffset + ifdDataOffset, count); } break; case BYTE://8-bit unsigned integer. if (count == 1) { d = (short) (valueOffset & 0xff); } else if (count == 2) { d = new short[]{(short) ((valueOffset & 0xff00) >> 8), (short) (valueOffset & 0xff)}; } else if (count == 3) { d = new short[]{(short) ((valueOffset & 0xff0000) >> 16), (short) ((valueOffset & 0xff00) >> 8), (short) (valueOffset & 0xff)}; } else if (count == 4) { d = new short[]{(short) ((valueOffset & 0xff000000) >> 24), (short) ((valueOffset & 0xff0000) >> 16), (short) ((valueOffset & 0xff00) >> 8), (short) (valueOffset & 0xff)}; } else { byte[] b = new byte[(int) count]; in.read(valueOffset + ifdDataOffset, b, 0, b.length); short[] s = new short[(int) count]; for (int i = 0; i < b.length; i++) { s[i] = (short) (b[i] & 0xff); } d = s; } break; case SBYTE: //An 8-bit signed (twos-complement) integer. case UNDEFINED://An 8-bit byte that may contain anything, depending on the definition of the field. if (count == 1) { d = (byte) valueOffset; } else if (count == 2) { d = new byte[]{(byte) ((valueOffset & 0xff00) >> 8), (byte) (valueOffset & 0xff)}; } else if (count == 3) { d = new byte[]{(byte) ((valueOffset & 0xff0000) >> 16), (byte) ((valueOffset & 0xff00) >> 8), (byte) (valueOffset & 0xff)}; } else if (count == 4) { d = new byte[]{(byte) ((valueOffset & 0xff000000) >> 24), (byte) ((valueOffset & 0xff0000) >> 16), (byte) ((valueOffset & 0xff00) >> 8), (byte) (valueOffset & 0xff)}; } else { byte[] b = new byte[(int) count]; in.read(valueOffset + ifdDataOffset, b, 0, b.length); d = b; } break; case SSHORT://A 16-bit (2-byte) signed (twos-complement) integer. if (count == 1) { if (in.getByteOrder() == LITTLE_ENDIAN) { d = (short) (valueOffset & 0xffff); } else { d = (short) ((valueOffset >> 16) & 0xffff); } } else if (count == 2) { d = new int[]{(short) (valueOffset & 0xffff), (short) ((valueOffset & 0xffff0000) >> 16)}; } else { d = in.readSSHORT(valueOffset + ifdDataOffset, count); } break; case SLONG://A 32-bit (4-byte) signed (twos-complement) integer. if (count == 1) { d = (int) valueOffset; } else { d = in.readSLONG(valueOffset + ifdDataOffset, count); } break; case SRATIONAL://Two SLONG’s: the first represents the numerator of a fraction, the second the denominator. if (count == 1) { d = in.readSRATIONAL(valueOffset + ifdDataOffset); } else { d = in.readSRATIONAL(valueOffset + ifdDataOffset, count); } break; case FLOAT://Single precision (4-byte) IEEE prettyFormat. case DOUBLE:// Double precision (8-byte) IEEE prettyFormat. default: throw new IOException("Format FLOAT and DOUBLE " + typeNumber + " not implemented"); } } return d; } public void loadData(TIFFInputStream in) throws IOException { data = readData(in); } public Object getData() { return data; } /** * FIXME Output is used by EXIFView * * @return TODO */ @Override public String toString() { return "IFD Entry: tag:0x" + toHexString(tagNumber) + " type:0x" + toHexString(typeNumber) + " count:0x" + toHexString(count) + " valueOffset:0x" + toHexString(valueOffset); } public String toString(Enum tagName) { StringBuilder buf = new StringBuilder(); buf.append("Entry tag:" + tagName + "(" + toHexString(tagNumber) + "), type:" + valueOf(typeNumber) + "(" + typeNumber + "), count:" + count + ", valueOffset:" + valueOffset); if (data != null) { buf.append(", data:"); if (data instanceof byte[]) { byte[] d = (byte[]) data; for (int i = 0; i < d.length; i++) { if (i != 0) { buf.append(','); } buf.append(d[i]); } } else if (data instanceof short[]) { short[] d = (short[]) data; for (int i = 0; i < d.length; i++) { if (i != 0) { buf.append(','); } buf.append(d[i]); } } else if (data instanceof int[]) { int[] d = (int[]) data; for (int i = 0; i < d.length; i++) { if (i != 0) { buf.append(','); } buf.append(d[i]); } } else if (data instanceof long[]) { long[] d = (long[]) data; for (int i = 0; i < d.length; i++) { if (i != 0) { buf.append(','); } buf.append(d[i]); } } else if (data instanceof Object[]) { Object[] d = (Object[]) data; for (int i = 0; i < d.length; i++) { if (i != 0) { buf.append(','); } buf.append(d[i]); } } else { buf.append(data); } } return buf.toString(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy