KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lowagie > text > pdf > codec > TIFFDirectory


1 /*
2  * Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * -Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * -Redistribution in binary form must reproduct the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of Sun Microsystems, Inc. or the names of contributors may
15  * be used to endorse or promote products derived from this software without
16  * specific prior written permission.
17  *
18  * This software is provided "AS IS," without a warranty of any kind. ALL
19  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
20  * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
21  * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
22  * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
23  * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
24  * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
25  * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
26  * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
27  * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGES.
29  *
30  * You acknowledge that Software is not designed,licensed or intended for use in
31  * the design, construction, operation or maintenance of any nuclear facility.
32  */

33 package com.lowagie.text.pdf.codec;
34 import java.io.EOFException JavaDoc;
35 import java.io.IOException JavaDoc;
36 import java.io.Serializable JavaDoc;
37 import java.util.ArrayList JavaDoc;
38 import java.util.Enumeration JavaDoc;
39 import java.util.Hashtable JavaDoc;
40
41 import com.lowagie.text.pdf.RandomAccessFileOrArray;
42
43 /**
44  * A class representing an Image File Directory (IFD) from a TIFF 6.0
45  * stream. The TIFF file format is described in more detail in the
46  * comments for the TIFFDescriptor class.
47  *
48  * <p> A TIFF IFD consists of a set of TIFFField tags. Methods are
49  * provided to query the set of tags and to obtain the raw field
50  * array. In addition, convenience methods are provided for acquiring
51  * the values of tags that contain a single value that fits into a
52  * byte, int, long, float, or double.
53  *
54  * <p> Every TIFF file is made up of one or more public IFDs that are
55  * joined in a linked list, rooted in the file header. A file may
56  * also contain so-called private IFDs that are referenced from
57  * tag data and do not appear in the main list.
58  *
59  * <p><b> This class is not a committed part of the JAI API. It may
60  * be removed or changed in future releases of JAI.</b>
61  *
62  * @see TIFFField
63  */

64 public class TIFFDirectory extends Object JavaDoc implements Serializable JavaDoc {
65     
66     private static final long serialVersionUID = -168636766193675380L;
67
68     /** A boolean storing the endianness of the stream. */
69     boolean isBigEndian;
70     
71     /** The number of entries in the IFD. */
72     int numEntries;
73     
74     /** An array of TIFFFields. */
75     TIFFField[] fields;
76     
77     /** A Hashtable indexing the fields by tag number. */
78     Hashtable JavaDoc fieldIndex = new Hashtable JavaDoc();
79     
80     /** The offset of this IFD. */
81     long IFDOffset = 8;
82     
83     /** The offset of the next IFD. */
84     long nextIFDOffset = 0;
85     
86     /** The default constructor. */
87     TIFFDirectory() {}
88     
89     private static boolean isValidEndianTag(int endian) {
90         return ((endian == 0x4949) || (endian == 0x4d4d));
91     }
92     
93     /**
94      * Constructs a TIFFDirectory from a SeekableStream.
95      * The directory parameter specifies which directory to read from
96      * the linked list present in the stream; directory 0 is normally
97      * read but it is possible to store multiple images in a single
98      * TIFF file by maintaing multiple directories.
99      *
100      * @param stream a SeekableStream to read from.
101      * @param directory the index of the directory to read.
102      */

103     public TIFFDirectory(RandomAccessFileOrArray stream, int directory)
104     throws IOException JavaDoc {
105         
106         long global_save_offset = stream.getFilePointer();
107         long ifd_offset;
108         
109         // Read the TIFF header
110
stream.seek(0L);
111         int endian = stream.readUnsignedShort();
112         if (!isValidEndianTag(endian)) {
113             throw new
114             IllegalArgumentException JavaDoc("Bad endianness tag (not 0x4949 or 0x4d4d).");
115         }
116         isBigEndian = (endian == 0x4d4d);
117         
118         int magic = readUnsignedShort(stream);
119         if (magic != 42) {
120             throw new
121             IllegalArgumentException JavaDoc("Bad magic number, should be 42.");
122         }
123         
124         // Get the initial ifd offset as an unsigned int (using a long)
125
ifd_offset = readUnsignedInt(stream);
126         
127         for (int i = 0; i < directory; i++) {
128             if (ifd_offset == 0L) {
129                 throw new
130                 IllegalArgumentException JavaDoc("Directory number too large.");
131             }
132             
133             stream.seek(ifd_offset);
134             int entries = readUnsignedShort(stream);
135             stream.skip(12*entries);
136             
137             ifd_offset = readUnsignedInt(stream);
138         }
139         
140         stream.seek(ifd_offset);
141         initialize(stream);
142         stream.seek(global_save_offset);
143     }
144     
145     /**
146      * Constructs a TIFFDirectory by reading a SeekableStream.
147      * The ifd_offset parameter specifies the stream offset from which
148      * to begin reading; this mechanism is sometimes used to store
149      * private IFDs within a TIFF file that are not part of the normal
150      * sequence of IFDs.
151      *
152      * @param stream a SeekableStream to read from.
153      * @param ifd_offset the long byte offset of the directory.
154      * @param directory the index of the directory to read beyond the
155      * one at the current stream offset; zero indicates the IFD
156      * at the current offset.
157      */

158     public TIFFDirectory(RandomAccessFileOrArray stream, long ifd_offset, int directory)
159     throws IOException JavaDoc {
160         
161         long global_save_offset = stream.getFilePointer();
162         stream.seek(0L);
163         int endian = stream.readUnsignedShort();
164         if (!isValidEndianTag(endian)) {
165             throw new
166             IllegalArgumentException JavaDoc("Bad endianness tag (not 0x4949 or 0x4d4d).");
167         }
168         isBigEndian = (endian == 0x4d4d);
169         
170         // Seek to the first IFD.
171
stream.seek(ifd_offset);
172         
173         // Seek to desired IFD if necessary.
174
int dirNum = 0;
175         while(dirNum < directory) {
176             // Get the number of fields in the current IFD.
177
int numEntries = readUnsignedShort(stream);
178             
179             // Skip to the next IFD offset value field.
180
stream.seek(ifd_offset + 12*numEntries);
181             
182             // Read the offset to the next IFD beyond this one.
183
ifd_offset = readUnsignedInt(stream);
184             
185             // Seek to the next IFD.
186
stream.seek(ifd_offset);
187             
188             // Increment the directory.
189
dirNum++;
190         }
191         
192         initialize(stream);
193         stream.seek(global_save_offset);
194     }
195     
196     private static final int[] sizeOfType = {
197         0, // 0 = n/a
198
1, // 1 = byte
199
1, // 2 = ascii
200
2, // 3 = short
201
4, // 4 = long
202
8, // 5 = rational
203
1, // 6 = sbyte
204
1, // 7 = undefined
205
2, // 8 = sshort
206
4, // 9 = slong
207
8, // 10 = srational
208
4, // 11 = float
209
8 // 12 = double
210
};
211     
212     private void initialize(RandomAccessFileOrArray stream) throws IOException JavaDoc {
213         long nextTagOffset = 0L;
214         long maxOffset = stream.length();
215         int i, j;
216         
217         IFDOffset = stream.getFilePointer();
218         
219         numEntries = readUnsignedShort(stream);
220         fields = new TIFFField[numEntries];
221         
222         for (i = 0; (i < numEntries) && (nextTagOffset < maxOffset); i++) {
223             int tag = readUnsignedShort(stream);
224             int type = readUnsignedShort(stream);
225             int count = (int)(readUnsignedInt(stream));
226             boolean processTag = true;
227             
228             // The place to return to to read the next tag
229
nextTagOffset = stream.getFilePointer() + 4;
230             
231             try {
232                 // If the tag data can't fit in 4 bytes, the next 4 bytes
233
// contain the starting offset of the data
234
if (count*sizeOfType[type] > 4) {
235                     long valueOffset = readUnsignedInt(stream);
236                     
237                     // bounds check offset for EOF
238
if (valueOffset < maxOffset) {
239                         stream.seek(valueOffset);
240                     }
241                     else {
242                         // bad offset pointer .. skip tag
243
processTag = false;
244                     }
245                 }
246             } catch (ArrayIndexOutOfBoundsException JavaDoc ae) {
247                 // if the data type is unknown we should skip this TIFF Field
248
processTag = false;
249             }
250             
251             if (processTag) {
252             fieldIndex.put(new Integer JavaDoc(tag), new Integer JavaDoc(i));
253             Object JavaDoc obj = null;
254             
255             switch (type) {
256                 case TIFFField.TIFF_BYTE:
257                 case TIFFField.TIFF_SBYTE:
258                 case TIFFField.TIFF_UNDEFINED:
259                 case TIFFField.TIFF_ASCII:
260                     byte[] bvalues = new byte[count];
261                     stream.readFully(bvalues, 0, count);
262                     
263                     if (type == TIFFField.TIFF_ASCII) {
264                         
265                         // Can be multiple strings
266
int index = 0, prevIndex = 0;
267                         ArrayList JavaDoc v = new ArrayList JavaDoc();
268                         
269                         while (index < count) {
270                             
271                             while ((index < count) && (bvalues[index++] != 0));
272                             
273                             // When we encountered zero, means one string has ended
274
v.add(new String JavaDoc(bvalues, prevIndex,
275                             (index - prevIndex)) );
276                             prevIndex = index;
277                         }
278                         
279                         count = v.size();
280                         String JavaDoc strings[] = new String JavaDoc[count];
281                         for (int c = 0 ; c < count; c++) {
282                             strings[c] = (String JavaDoc)v.get(c);
283                         }
284                         
285                         obj = strings;
286                     } else {
287                         obj = bvalues;
288                     }
289                     
290                     break;
291                     
292                 case TIFFField.TIFF_SHORT:
293                     char[] cvalues = new char[count];
294                     for (j = 0; j < count; j++) {
295                         cvalues[j] = (char)(readUnsignedShort(stream));
296                     }
297                     obj = cvalues;
298                     break;
299                     
300                 case TIFFField.TIFF_LONG:
301                     long[] lvalues = new long[count];
302                     for (j = 0; j < count; j++) {
303                         lvalues[j] = readUnsignedInt(stream);
304                     }
305                     obj = lvalues;
306                     break;
307                     
308                 case TIFFField.TIFF_RATIONAL:
309                     long[][] llvalues = new long[count][2];
310                     for (j = 0; j < count; j++) {
311                         llvalues[j][0] = readUnsignedInt(stream);
312                         llvalues[j][1] = readUnsignedInt(stream);
313                     }
314                     obj = llvalues;
315                     break;
316                     
317                 case TIFFField.TIFF_SSHORT:
318                     short[] svalues = new short[count];
319                     for (j = 0; j < count; j++) {
320                         svalues[j] = readShort(stream);
321                     }
322                     obj = svalues;
323                     break;
324                     
325                 case TIFFField.TIFF_SLONG:
326                     int[] ivalues = new int[count];
327                     for (j = 0; j < count; j++) {
328                         ivalues[j] = readInt(stream);
329                     }
330                     obj = ivalues;
331                     break;
332                     
333                 case TIFFField.TIFF_SRATIONAL:
334                     int[][] iivalues = new int[count][2];
335                     for (j = 0; j < count; j++) {
336                         iivalues[j][0] = readInt(stream);
337                         iivalues[j][1] = readInt(stream);
338                     }
339                     obj = iivalues;
340                     break;
341                     
342                 case TIFFField.TIFF_FLOAT:
343                     float[] fvalues = new float[count];
344                     for (j = 0; j < count; j++) {
345                         fvalues[j] = readFloat(stream);
346                     }
347                     obj = fvalues;
348                     break;
349                     
350                 case TIFFField.TIFF_DOUBLE:
351                     double[] dvalues = new double[count];
352                     for (j = 0; j < count; j++) {
353                         dvalues[j] = readDouble(stream);
354                     }
355                     obj = dvalues;
356                     break;
357                     
358                 default:
359                     break;
360             }
361             
362             fields[i] = new TIFFField(tag, type, count, obj);
363             }
364             
365             stream.seek(nextTagOffset);
366         }
367         
368         // Read the offset of the next IFD.
369
try {
370             nextIFDOffset = readUnsignedInt(stream);
371         }
372         catch (Exception JavaDoc e) {
373             // broken tiffs may not have this pointer
374
nextIFDOffset = 0;
375         }
376     }
377     
378     /** Returns the number of directory entries. */
379     public int getNumEntries() {
380         return numEntries;
381     }
382     
383     /**
384      * Returns the value of a given tag as a TIFFField,
385      * or null if the tag is not present.
386      */

387     public TIFFField getField(int tag) {
388         Integer JavaDoc i = (Integer JavaDoc)fieldIndex.get(new Integer JavaDoc(tag));
389         if (i == null) {
390             return null;
391         } else {
392             return fields[i.intValue()];
393         }
394     }
395     
396     /**
397      * Returns true if a tag appears in the directory.
398      */

399     public boolean isTagPresent(int tag) {
400         return fieldIndex.containsKey(new Integer JavaDoc(tag));
401     }
402     
403     /**
404      * Returns an ordered array of ints indicating the tag
405      * values.
406      */

407     public int[] getTags() {
408         int[] tags = new int[fieldIndex.size()];
409         Enumeration JavaDoc e = fieldIndex.keys();
410         int i = 0;
411         
412         while (e.hasMoreElements()) {
413             tags[i++] = ((Integer JavaDoc)e.nextElement()).intValue();
414         }
415         
416         return tags;
417     }
418     
419     /**
420      * Returns an array of TIFFFields containing all the fields
421      * in this directory.
422      */

423     public TIFFField[] getFields() {
424         return fields;
425     }
426     
427     /**
428      * Returns the value of a particular index of a given tag as a
429      * byte. The caller is responsible for ensuring that the tag is
430      * present and has type TIFFField.TIFF_SBYTE, TIFF_BYTE, or
431      * TIFF_UNDEFINED.
432      */

433     public byte getFieldAsByte(int tag, int index) {
434         Integer JavaDoc i = (Integer JavaDoc)fieldIndex.get(new Integer JavaDoc(tag));
435         byte [] b = (fields[i.intValue()]).getAsBytes();
436         return b[index];
437     }
438     
439     /**
440      * Returns the value of index 0 of a given tag as a
441      * byte. The caller is responsible for ensuring that the tag is
442      * present and has type TIFFField.TIFF_SBYTE, TIFF_BYTE, or
443      * TIFF_UNDEFINED.
444      */

445     public byte getFieldAsByte(int tag) {
446         return getFieldAsByte(tag, 0);
447     }
448     
449     /**
450      * Returns the value of a particular index of a given tag as a
451      * long. The caller is responsible for ensuring that the tag is
452      * present and has type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED,
453      * TIFF_SHORT, TIFF_SSHORT, TIFF_SLONG or TIFF_LONG.
454      */

455     public long getFieldAsLong(int tag, int index) {
456         Integer JavaDoc i = (Integer JavaDoc)fieldIndex.get(new Integer JavaDoc(tag));
457         return fields[i.intValue()].getAsLong(index);
458     }
459     
460     /**
461      * Returns the value of index 0 of a given tag as a
462      * long. The caller is responsible for ensuring that the tag is
463      * present and has type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED,
464      * TIFF_SHORT, TIFF_SSHORT, TIFF_SLONG or TIFF_LONG.
465      */

466     public long getFieldAsLong(int tag) {
467         return getFieldAsLong(tag, 0);
468     }
469     
470     /**
471      * Returns the value of a particular index of a given tag as a
472      * float. The caller is responsible for ensuring that the tag is
473      * present and has numeric type (all but TIFF_UNDEFINED and
474      * TIFF_ASCII).
475      */

476     public float getFieldAsFloat(int tag, int index) {
477         Integer JavaDoc i = (Integer JavaDoc)fieldIndex.get(new Integer JavaDoc(tag));
478         return fields[i.intValue()].getAsFloat(index);
479     }
480     
481     /**
482      * Returns the value of index 0 of a given tag as a float. The
483      * caller is responsible for ensuring that the tag is present and
484      * has numeric type (all but TIFF_UNDEFINED and TIFF_ASCII).
485      */

486     public float getFieldAsFloat(int tag) {
487         return getFieldAsFloat(tag, 0);
488     }
489     
490     /**
491      * Returns the value of a particular index of a given tag as a
492      * double. The caller is responsible for ensuring that the tag is
493      * present and has numeric type (all but TIFF_UNDEFINED and
494      * TIFF_ASCII).
495      */

496     public double getFieldAsDouble(int tag, int index) {
497         Integer JavaDoc i = (Integer JavaDoc)fieldIndex.get(new Integer JavaDoc(tag));
498         return fields[i.intValue()].getAsDouble(index);
499     }
500     
501     /**
502      * Returns the value of index 0 of a given tag as a double. The
503      * caller is responsible for ensuring that the tag is present and
504      * has numeric type (all but TIFF_UNDEFINED and TIFF_ASCII).
505      */

506     public double getFieldAsDouble(int tag) {
507         return getFieldAsDouble(tag, 0);
508     }
509     
510     // Methods to read primitive data types from the stream
511

512     private short readShort(RandomAccessFileOrArray stream)
513     throws IOException JavaDoc {
514         if (isBigEndian) {
515             return stream.readShort();
516         } else {
517             return stream.readShortLE();
518         }
519     }
520     
521     private int readUnsignedShort(RandomAccessFileOrArray stream)
522     throws IOException JavaDoc {
523         if (isBigEndian) {
524             return stream.readUnsignedShort();
525         } else {
526             return stream.readUnsignedShortLE();
527         }
528     }
529     
530     private int readInt(RandomAccessFileOrArray stream)
531     throws IOException JavaDoc {
532         if (isBigEndian) {
533             return stream.readInt();
534         } else {
535             return stream.readIntLE();
536         }
537     }
538     
539     private long readUnsignedInt(RandomAccessFileOrArray stream)
540     throws IOException JavaDoc {
541         if (isBigEndian) {
542             return stream.readUnsignedInt();
543         } else {
544             return stream.readUnsignedIntLE();
545         }
546     }
547     
548     private long readLong(RandomAccessFileOrArray stream)
549     throws IOException JavaDoc {
550         if (isBigEndian) {
551             return stream.readLong();
552         } else {
553             return stream.readLongLE();
554         }
555     }
556     
557     private float readFloat(RandomAccessFileOrArray stream)
558     throws IOException JavaDoc {
559         if (isBigEndian) {
560             return stream.readFloat();
561         } else {
562             return stream.readFloatLE();
563         }
564     }
565     
566     private double readDouble(RandomAccessFileOrArray stream)
567     throws IOException JavaDoc {
568         if (isBigEndian) {
569             return stream.readDouble();
570         } else {
571             return stream.readDoubleLE();
572         }
573     }
574     
575     private static int readUnsignedShort(RandomAccessFileOrArray stream,
576     boolean isBigEndian)
577     throws IOException JavaDoc {
578         if (isBigEndian) {
579             return stream.readUnsignedShort();
580         } else {
581             return stream.readUnsignedShortLE();
582         }
583     }
584     
585     private static long readUnsignedInt(RandomAccessFileOrArray stream,
586     boolean isBigEndian)
587     throws IOException JavaDoc {
588         if (isBigEndian) {
589             return stream.readUnsignedInt();
590         } else {
591             return stream.readUnsignedIntLE();
592         }
593     }
594     
595     // Utilities
596

597     /**
598      * Returns the number of image directories (subimages) stored in a
599      * given TIFF file, represented by a <code>SeekableStream</code>.
600      */

601     public static int getNumDirectories(RandomAccessFileOrArray stream)
602     throws IOException JavaDoc{
603         long pointer = stream.getFilePointer(); // Save stream pointer
604

605         stream.seek(0L);
606         int endian = stream.readUnsignedShort();
607         if (!isValidEndianTag(endian)) {
608             throw new
609             IllegalArgumentException JavaDoc("Bad endianness tag (not 0x4949 or 0x4d4d).");
610         }
611         boolean isBigEndian = (endian == 0x4d4d);
612         int magic = readUnsignedShort(stream, isBigEndian);
613         if (magic != 42) {
614             throw new
615             IllegalArgumentException JavaDoc("Bad magic number, should be 42.");
616         }
617         
618         stream.seek(4L);
619         long offset = readUnsignedInt(stream, isBigEndian);
620         
621         int numDirectories = 0;
622         while (offset != 0L) {
623             ++numDirectories;
624             
625             // EOFException means IFD was probably not properly terminated.
626
try {
627                 stream.seek(offset);
628                 int entries = readUnsignedShort(stream, isBigEndian);
629                 stream.skip(12*entries);
630                 offset = readUnsignedInt(stream, isBigEndian);
631             } catch(EOFException JavaDoc eof) {
632                 numDirectories--;
633                 break;
634             }
635         }
636         
637         stream.seek(pointer); // Reset stream pointer
638
return numDirectories;
639     }
640     
641     /**
642      * Returns a boolean indicating whether the byte order used in the
643      * the TIFF file is big-endian (i.e. whether the byte order is from
644      * the most significant to the least significant)
645      */

646     public boolean isBigEndian() {
647         return isBigEndian;
648     }
649     
650     /**
651      * Returns the offset of the IFD corresponding to this
652      * <code>TIFFDirectory</code>.
653      */

654     public long getIFDOffset() {
655         return IFDOffset;
656     }
657     
658     /**
659      * Returns the offset of the next IFD after the IFD corresponding to this
660      * <code>TIFFDirectory</code>.
661      */

662     public long getNextIFDOffset() {
663         return nextIFDOffset;
664     }
665 }
666
Popular Tags