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

com.aowagie.text.pdf.codec.TIFFDirectory Maven / Gradle / Ivy

/*
 * Copyright 2003-2008 by Paulo Soares.
 *
 * This code was originally released in 2001 by SUN (see class
 * com.sun.media.imageio.plugins.tiff.TIFFDirectory.java)
 * using the BSD license in a specific wording. In a mail dating from
 * January 23, 2008, Brian Burkhalter (@sun.com) gave us permission
 * to use the code under the following version of the BSD license:
 *
 * Copyright (c) 2006 Sun Microsystems, Inc. All  Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistribution of source code must retain the above copyright
 *   notice, this  list of conditions and the following disclaimer.
 *
 * - Redistribution in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any
 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed or intended for
 * use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */
package com.aowagie.text.pdf.codec;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;

import com.aowagie.text.pdf.RandomAccessFileOrArray;

/**
 * A class representing an Image File Directory (IFD) from a TIFF 6.0
 * stream.  The TIFF file format is described in more detail in the
 * comments for the TIFFDescriptor class.
 *
 * 

A TIFF IFD consists of a set of TIFFField tags. Methods are * provided to query the set of tags and to obtain the raw field * array. In addition, convenience methods are provided for acquiring * the values of tags that contain a single value that fits into a * byte, int, long, float, or double. * *

Every TIFF file is made up of one or more public IFDs that are * joined in a linked list, rooted in the file header. A file may * also contain so-called private IFDs that are referenced from * tag data and do not appear in the main list. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. * * @see TIFFField */ class TIFFDirectory extends Object implements Serializable { private static final long serialVersionUID = -168636766193675380L; /** A boolean storing the endianness of the stream. */ private boolean isBigEndian; /** The number of entries in the IFD. */ private int numEntries; /** An array of TIFFFields. */ private TIFFField[] fields; /** A Hashtable indexing the fields by tag number. */ private final Hashtable fieldIndex = new Hashtable(); /** The offset of this IFD. */ private long IFDOffset = 8; /** The offset of the next IFD. */ private long nextIFDOffset = 0; /** The default constructor. */ TIFFDirectory() {} private static boolean isValidEndianTag(final int endian) { return endian == 0x4949 || endian == 0x4d4d; } /** * Constructs a TIFFDirectory from a SeekableStream. * The directory parameter specifies which directory to read from * the linked list present in the stream; directory 0 is normally * read but it is possible to store multiple images in a single * TIFF file by maintaining multiple directories. * * @param stream a SeekableStream to read from. * @param directory the index of the directory to read. */ TIFFDirectory(final RandomAccessFileOrArray stream, final int directory) throws IOException { final long global_save_offset = stream.getFilePointer(); long ifd_offset; // Read the TIFF header stream.seek(0L); final int endian = stream.readUnsignedShort(); if (!isValidEndianTag(endian)) { throw new IllegalArgumentException("Bad endianness tag (not 0x4949 or 0x4d4d)."); } this.isBigEndian = endian == 0x4d4d; final int magic = readUnsignedShort(stream); if (magic != 42) { throw new IllegalArgumentException("Bad magic number, should be 42."); } // Get the initial ifd offset as an unsigned int (using a long) ifd_offset = readUnsignedInt(stream); for (int i = 0; i < directory; i++) { if (ifd_offset == 0L) { throw new IllegalArgumentException("Directory number too large."); } stream.seek(ifd_offset); final int entries = readUnsignedShort(stream); stream.skip(12*entries); ifd_offset = readUnsignedInt(stream); } stream.seek(ifd_offset); initialize(stream); stream.seek(global_save_offset); } private static final int[] sizeOfType = { 0, // 0 = n/a 1, // 1 = byte 1, // 2 = ascii 2, // 3 = short 4, // 4 = long 8, // 5 = rational 1, // 6 = sbyte 1, // 7 = undefined 2, // 8 = sshort 4, // 9 = slong 8, // 10 = srational 4, // 11 = float 8 // 12 = double }; private void initialize(final RandomAccessFileOrArray stream) throws IOException { long nextTagOffset = 0L; final long maxOffset = stream.length(); int i, j; this.IFDOffset = stream.getFilePointer(); this.numEntries = readUnsignedShort(stream); this.fields = new TIFFField[this.numEntries]; for (i = 0; i < this.numEntries && nextTagOffset < maxOffset; i++) { final int tag = readUnsignedShort(stream); final int type = readUnsignedShort(stream); int count = (int)readUnsignedInt(stream); boolean processTag = true; // The place to return to to read the next tag nextTagOffset = stream.getFilePointer() + 4; try { // If the tag data can't fit in 4 bytes, the next 4 bytes // contain the starting offset of the data if (count*sizeOfType[type] > 4) { final long valueOffset = readUnsignedInt(stream); // bounds check offset for EOF if (valueOffset < maxOffset) { stream.seek(valueOffset); } else { // bad offset pointer .. skip tag processTag = false; } } } catch (final ArrayIndexOutOfBoundsException ae) { // if the data type is unknown we should skip this TIFF Field processTag = false; } if (processTag) { this.fieldIndex.put(Integer.valueOf(tag), Integer.valueOf(i)); Object obj = null; switch (type) { case TIFFField.TIFF_BYTE: case TIFFField.TIFF_SBYTE: case TIFFField.TIFF_UNDEFINED: case TIFFField.TIFF_ASCII: final byte[] bvalues = new byte[count]; stream.readFully(bvalues, 0, count); if (type == TIFFField.TIFF_ASCII) { // Can be multiple strings int index = 0, prevIndex = 0; final ArrayList v = new ArrayList(); while (index < count) { while (index < count && bvalues[index++] != 0) { ; } // When we encountered zero, means one string has ended v.add(new String(bvalues, prevIndex, index - prevIndex) ); prevIndex = index; } count = v.size(); final String strings[] = new String[count]; for (int c = 0 ; c < count; c++) { strings[c] = (String)v.get(c); } obj = strings; } else { obj = bvalues; } break; case TIFFField.TIFF_SHORT: final char[] cvalues = new char[count]; for (j = 0; j < count; j++) { cvalues[j] = (char)readUnsignedShort(stream); } obj = cvalues; break; case TIFFField.TIFF_LONG: final long[] lvalues = new long[count]; for (j = 0; j < count; j++) { lvalues[j] = readUnsignedInt(stream); } obj = lvalues; break; case TIFFField.TIFF_RATIONAL: final long[][] llvalues = new long[count][2]; for (j = 0; j < count; j++) { llvalues[j][0] = readUnsignedInt(stream); llvalues[j][1] = readUnsignedInt(stream); } obj = llvalues; break; case TIFFField.TIFF_SSHORT: final short[] svalues = new short[count]; for (j = 0; j < count; j++) { svalues[j] = readShort(stream); } obj = svalues; break; case TIFFField.TIFF_SLONG: final int[] ivalues = new int[count]; for (j = 0; j < count; j++) { ivalues[j] = readInt(stream); } obj = ivalues; break; case TIFFField.TIFF_SRATIONAL: final int[][] iivalues = new int[count][2]; for (j = 0; j < count; j++) { iivalues[j][0] = readInt(stream); iivalues[j][1] = readInt(stream); } obj = iivalues; break; case TIFFField.TIFF_FLOAT: final float[] fvalues = new float[count]; for (j = 0; j < count; j++) { fvalues[j] = readFloat(stream); } obj = fvalues; break; case TIFFField.TIFF_DOUBLE: final double[] dvalues = new double[count]; for (j = 0; j < count; j++) { dvalues[j] = readDouble(stream); } obj = dvalues; break; default: break; } this.fields[i] = new TIFFField(tag, type, count, obj); } stream.seek(nextTagOffset); } // Read the offset of the next IFD. try { this.nextIFDOffset = readUnsignedInt(stream); } catch (final Exception e) { // broken tiffs may not have this pointer this.nextIFDOffset = 0; } } /** Returns the number of directory entries. */ public int getNumEntries() { return this.numEntries; } /** * Returns the value of a given tag as a TIFFField, * or null if the tag is not present. */ TIFFField getField(final int tag) { final Integer i = (Integer)this.fieldIndex.get(Integer.valueOf(tag)); if (i == null) { return null; } else { return this.fields[i.intValue()]; } } /** * Returns true if a tag appears in the directory. */ boolean isTagPresent(final int tag) { return this.fieldIndex.containsKey(Integer.valueOf(tag)); } /** * Returns an ordered array of ints indicating the tag * values. */ public int[] getTags() { final int[] tags = new int[this.fieldIndex.size()]; final Enumeration e = this.fieldIndex.keys(); int i = 0; while (e.hasMoreElements()) { tags[i++] = ((Integer)e.nextElement()).intValue(); } return tags; } /** * Returns an array of TIFFFields containing all the fields * in this directory. */ public TIFFField[] getFields() { return this.fields; } /** * Returns the value of a particular index of a given tag as a * byte. The caller is responsible for ensuring that the tag is * present and has type TIFFField.TIFF_SBYTE, TIFF_BYTE, or * TIFF_UNDEFINED. */ private byte getFieldAsByte(final int tag, final int index) { final Integer i = (Integer)this.fieldIndex.get(Integer.valueOf(tag)); final byte [] b = this.fields[i.intValue()].getAsBytes(); return b[index]; } /** * Returns the value of a particular index of a given tag as a * long. The caller is responsible for ensuring that the tag is * present and has type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, * TIFF_SHORT, TIFF_SSHORT, TIFF_SLONG or TIFF_LONG. */ private long getFieldAsLong(final int tag, final int index) { final Integer i = (Integer)this.fieldIndex.get(Integer.valueOf(tag)); return this.fields[i.intValue()].getAsLong(index); } /** * Returns the value of index 0 of a given tag as a * long. The caller is responsible for ensuring that the tag is * present and has type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, * TIFF_SHORT, TIFF_SSHORT, TIFF_SLONG or TIFF_LONG. */ long getFieldAsLong(final int tag) { return getFieldAsLong(tag, 0); } /** * Returns the value of a particular index of a given tag as a * float. The caller is responsible for ensuring that the tag is * present and has numeric type (all but TIFF_UNDEFINED and * TIFF_ASCII). */ private float getFieldAsFloat(final int tag, final int index) { final Integer i = (Integer)this.fieldIndex.get(Integer.valueOf(tag)); return this.fields[i.intValue()].getAsFloat(index); } /** * Returns the value of a particular index of a given tag as a * double. The caller is responsible for ensuring that the tag is * present and has numeric type (all but TIFF_UNDEFINED and * TIFF_ASCII). */ private double getFieldAsDouble(final int tag, final int index) { final Integer i = (Integer)this.fieldIndex.get(Integer.valueOf(tag)); return this.fields[i.intValue()].getAsDouble(index); } // Methods to read primitive data types from the stream private short readShort(final RandomAccessFileOrArray stream) throws IOException { if (this.isBigEndian) { return stream.readShort(); } else { return stream.readShortLE(); } } private int readUnsignedShort(final RandomAccessFileOrArray stream) throws IOException { if (this.isBigEndian) { return stream.readUnsignedShort(); } else { return stream.readUnsignedShortLE(); } } private int readInt(final RandomAccessFileOrArray stream) throws IOException { if (this.isBigEndian) { return stream.readInt(); } else { return stream.readIntLE(); } } private long readUnsignedInt(final RandomAccessFileOrArray stream) throws IOException { if (this.isBigEndian) { return stream.readUnsignedInt(); } else { return stream.readUnsignedIntLE(); } } private long readLong(final RandomAccessFileOrArray stream) throws IOException { if (this.isBigEndian) { return stream.readLong(); } else { return stream.readLongLE(); } } private float readFloat(final RandomAccessFileOrArray stream) throws IOException { if (this.isBigEndian) { return stream.readFloat(); } else { return stream.readFloatLE(); } } private double readDouble(final RandomAccessFileOrArray stream) throws IOException { if (this.isBigEndian) { return stream.readDouble(); } else { return stream.readDoubleLE(); } } private static int readUnsignedShort(final RandomAccessFileOrArray stream, final boolean isBigEndian) throws IOException { if (isBigEndian) { return stream.readUnsignedShort(); } else { return stream.readUnsignedShortLE(); } } private static long readUnsignedInt(final RandomAccessFileOrArray stream, final boolean isBigEndian) throws IOException { if (isBigEndian) { return stream.readUnsignedInt(); } else { return stream.readUnsignedIntLE(); } } // Utilities /** * Returns a boolean indicating whether the byte order used in the * the TIFF file is big-endian (i.e. whether the byte order is from * the most significant to the least significant) */ public boolean isBigEndian() { return this.isBigEndian; } /** * Returns the offset of the IFD corresponding to this * TIFFDirectory. */ public long getIFDOffset() { return this.IFDOffset; } /** * Returns the offset of the next IFD after the IFD corresponding to this * TIFFDirectory. */ public long getNextIFDOffset() { return this.nextIFDOffset; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy