KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > ext > awt > image > codec > tiff > TIFFDirectory


1 /*
2
3    Copyright 2001,2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.ext.awt.image.codec.tiff;
19
20 import java.io.IOException JavaDoc;
21 import java.io.Serializable JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.Map JavaDoc;
25 import java.util.Vector JavaDoc;
26
27 import org.apache.batik.ext.awt.image.codec.SeekableStream;
28
29 /**
30  * A class representing an Image File Directory (IFD) from a TIFF 6.0
31  * stream. The TIFF file format is described in more detail in the
32  * comments for the TIFFDescriptor class.
33  *
34  * <p> A TIFF IFD consists of a set of TIFFField tags. Methods are
35  * provided to query the set of tags and to obtain the raw field
36  * array. In addition, convenience methods are provided for acquiring
37  * the values of tags that contain a single value that fits into a
38  * byte, int, long, float, or double.
39  *
40  * <p> Every TIFF file is made up of one or more public IFDs that are
41  * joined in a linked list, rooted in the file header. A file may
42  * also contain so-called private IFDs that are referenced from
43  * tag data and do not appear in the main list.
44  *
45  * <p><b> This class is not a committed part of the JAI API. It may
46  * be removed or changed in future releases of JAI.</b>
47  *
48  * @see TIFFField
49  */

50 public class TIFFDirectory extends Object JavaDoc implements Serializable JavaDoc {
51
52     /** A boolean storing the endianness of the stream. */
53     boolean isBigEndian;
54     
55     /** The number of entries in the IFD. */
56     int numEntries;
57
58     /** An array of TIFFFields. */
59     TIFFField[] fields;
60
61     /** A Hashtable indexing the fields by tag number. */
62     Map JavaDoc fieldIndex = new HashMap JavaDoc();
63
64     /** The offset of this IFD. */
65     long IFDOffset = 8;
66
67     /** The offset of the next IFD. */
68     long nextIFDOffset = 0;
69
70     /** The default constructor. */
71     TIFFDirectory() {}
72
73     private static boolean isValidEndianTag(int endian) {
74         return ((endian == 0x4949) || (endian == 0x4d4d));
75     }
76
77     /**
78      * Constructs a TIFFDirectory from a SeekableStream.
79      * The directory parameter specifies which directory to read from
80      * the linked list present in the stream; directory 0 is normally
81      * read but it is possible to store multiple images in a single
82      * TIFF file by maintaing multiple directories.
83      *
84      * @param stream a SeekableStream to read from.
85      * @param directory the index of the directory to read.
86      */

87     public TIFFDirectory(SeekableStream stream, int directory)
88         throws IOException JavaDoc {
89
90         long global_save_offset = stream.getFilePointer();
91         long ifd_offset;
92
93         // Read the TIFF header
94
stream.seek(0L);
95         int endian = stream.readUnsignedShort();
96         if (!isValidEndianTag(endian)) {
97             throw new
98         IllegalArgumentException JavaDoc("TIFFDirectory1");
99         }
100         isBigEndian = (endian == 0x4d4d);
101
102         int magic = readUnsignedShort(stream);
103         if (magic != 42) {
104             throw new
105         IllegalArgumentException JavaDoc("TIFFDirectory2");
106         }
107
108         // Get the initial ifd offset as an unsigned int (using a long)
109
ifd_offset = readUnsignedInt(stream);
110         
111         for (int i = 0; i < directory; i++) {
112             if (ifd_offset == 0L) {
113                 throw new
114            IllegalArgumentException JavaDoc("TIFFDirectory3");
115             }
116             
117             stream.seek(ifd_offset);
118             int entries = readUnsignedShort(stream);
119             stream.skip(12*entries);
120
121             ifd_offset = readUnsignedInt(stream);
122         }
123
124         stream.seek(ifd_offset);
125         initialize(stream);
126         stream.seek(global_save_offset);
127     }
128
129     /**
130      * Constructs a TIFFDirectory by reading a SeekableStream.
131      * The ifd_offset parameter specifies the stream offset from which
132      * to begin reading; this mechanism is sometimes used to store
133      * private IFDs within a TIFF file that are not part of the normal
134      * sequence of IFDs.
135      *
136      * @param stream a SeekableStream to read from.
137      * @param ifd_offset the long byte offset of the directory.
138      * @param directory the index of the directory to read beyond the
139      * one at the current stream offset; zero indicates the IFD
140      * at the current offset.
141      */

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

357     public TIFFField getField(int tag) {
358         Integer JavaDoc i = (Integer JavaDoc)fieldIndex.get(new Integer JavaDoc(tag));
359         if (i == null) {
360             return null;
361         } else {
362             return fields[i.intValue()];
363         }
364     }
365
366     /**
367      * Returns true if a tag appears in the directory.
368      */

369     public boolean isTagPresent(int tag) {
370         return fieldIndex.containsKey(new Integer JavaDoc(tag));
371     }
372
373     /**
374      * Returns an ordered array of ints indicating the tag
375      * values.
376      */

377     public int[] getTags() {
378         int[] tags = new int[fieldIndex.size()];
379         Iterator JavaDoc iter = fieldIndex.keySet().iterator();
380         int i = 0;
381
382         while (iter.hasNext()) {
383             tags[i++] = ((Integer JavaDoc)iter.next()).intValue();
384         }
385
386         return tags;
387     }
388
389     /**
390      * Returns an array of TIFFFields containing all the fields
391      * in this directory.
392      */

393     public TIFFField[] getFields() {
394         return fields;
395     }
396
397     /**
398      * Returns the value of a particular index of a given tag as a
399      * byte. The caller is responsible for ensuring that the tag is
400      * present and has type TIFFField.TIFF_SBYTE, TIFF_BYTE, or
401      * TIFF_UNDEFINED.
402      */

403     public byte getFieldAsByte(int tag, int index) {
404         Integer JavaDoc i = (Integer JavaDoc)fieldIndex.get(new Integer JavaDoc(tag));
405         byte [] b = (fields[i.intValue()]).getAsBytes();
406         return b[index];
407     }
408
409     /**
410      * Returns the value of index 0 of a given tag as a
411      * byte. The caller is responsible for ensuring that the tag is
412      * present and has type TIFFField.TIFF_SBYTE, TIFF_BYTE, or
413      * TIFF_UNDEFINED.
414      */

415     public byte getFieldAsByte(int tag) {
416         return getFieldAsByte(tag, 0);
417     }
418
419     /**
420      * Returns the value of a particular index of a given tag as a
421      * long. The caller is responsible for ensuring that the tag is
422      * present and has type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED,
423      * TIFF_SHORT, TIFF_SSHORT, TIFF_SLONG or TIFF_LONG.
424      */

425     public long getFieldAsLong(int tag, int index) {
426         Integer JavaDoc i = (Integer JavaDoc)fieldIndex.get(new Integer JavaDoc(tag));
427         return (fields[i.intValue()]).getAsLong(index);
428     }
429
430     /**
431      * Returns the value of index 0 of a given tag as a
432      * long. The caller is responsible for ensuring that the tag is
433      * present and has type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED,
434      * TIFF_SHORT, TIFF_SSHORT, TIFF_SLONG or TIFF_LONG.
435      */

436     public long getFieldAsLong(int tag) {
437         return getFieldAsLong(tag, 0);
438     }
439
440     /**
441      * Returns the value of a particular index of a given tag as a
442      * float. The caller is responsible for ensuring that the tag is
443      * present and has numeric type (all but TIFF_UNDEFINED and
444      * TIFF_ASCII).
445      */

446     public float getFieldAsFloat(int tag, int index) {
447         Integer JavaDoc i = (Integer JavaDoc)fieldIndex.get(new Integer JavaDoc(tag));
448         return fields[i.intValue()].getAsFloat(index);
449     }
450
451     /**
452      * Returns the value of index 0 of a given tag as a float. The
453      * caller is responsible for ensuring that the tag is present and
454      * has numeric type (all but TIFF_UNDEFINED and TIFF_ASCII).
455      */

456     public float getFieldAsFloat(int tag) {
457         return getFieldAsFloat(tag, 0);
458     }
459
460     /**
461      * Returns the value of a particular index of a given tag as a
462      * double. The caller is responsible for ensuring that the tag is
463      * present and has numeric type (all but TIFF_UNDEFINED and
464      * TIFF_ASCII).
465      */

466     public double getFieldAsDouble(int tag, int index) {
467         Integer JavaDoc i = (Integer JavaDoc)fieldIndex.get(new Integer JavaDoc(tag));
468         return fields[i.intValue()].getAsDouble(index);
469     }
470
471     /**
472      * Returns the value of index 0 of a given tag as a double. The
473      * caller is responsible for ensuring that the tag is present and
474      * has numeric type (all but TIFF_UNDEFINED and TIFF_ASCII).
475      */

476     public double getFieldAsDouble(int tag) {
477         return getFieldAsDouble(tag, 0);
478     }
479
480     // Methods to read primitive data types from the stream
481

482     private short readShort(SeekableStream stream)
483         throws IOException JavaDoc {
484         if (isBigEndian) {
485             return stream.readShort();
486         } else {
487             return stream.readShortLE();
488         }
489     }
490
491     private int readUnsignedShort(SeekableStream stream)
492         throws IOException JavaDoc {
493         if (isBigEndian) {
494             return stream.readUnsignedShort();
495         } else {
496             return stream.readUnsignedShortLE();
497         }
498     }
499
500     private int readInt(SeekableStream stream)
501         throws IOException JavaDoc {
502         if (isBigEndian) {
503             return stream.readInt();
504         } else {
505             return stream.readIntLE();
506         }
507     }
508
509     private long readUnsignedInt(SeekableStream stream)
510         throws IOException JavaDoc {
511         if (isBigEndian) {
512             return stream.readUnsignedInt();
513         } else {
514             return stream.readUnsignedIntLE();
515         }
516     }
517
518     private long readLong(SeekableStream stream)
519         throws IOException JavaDoc {
520         if (isBigEndian) {
521             return stream.readLong();
522         } else {
523             return stream.readLongLE();
524         }
525     }
526
527     private float readFloat(SeekableStream stream)
528         throws IOException JavaDoc {
529         if (isBigEndian) {
530             return stream.readFloat();
531         } else {
532             return stream.readFloatLE();
533         }
534     }
535
536     private double readDouble(SeekableStream stream)
537         throws IOException JavaDoc {
538         if (isBigEndian) {
539             return stream.readDouble();
540         } else {
541             return stream.readDoubleLE();
542         }
543     }
544
545     private static int readUnsignedShort(SeekableStream stream,
546                                          boolean isBigEndian)
547         throws IOException JavaDoc {
548         if (isBigEndian) {
549             return stream.readUnsignedShort();
550         } else {
551             return stream.readUnsignedShortLE();
552         }
553     }
554
555     private static long readUnsignedInt(SeekableStream stream,
556                                         boolean isBigEndian)
557         throws IOException JavaDoc {
558         if (isBigEndian) {
559             return stream.readUnsignedInt();
560         } else {
561             return stream.readUnsignedIntLE();
562         }
563     }
564
565     // Utilities
566

567     /**
568      * Returns the number of image directories (subimages) stored in a
569      * given TIFF file, represented by a <code>SeekableStream</code>.
570      */

571     public static int getNumDirectories(SeekableStream stream)
572         throws IOException JavaDoc{
573         long pointer = stream.getFilePointer(); // Save stream pointer
574

575         stream.seek(0L);
576         int endian = stream.readUnsignedShort();
577         if (!isValidEndianTag(endian)) {
578             throw new
579         IllegalArgumentException JavaDoc("TIFFDirectory1");
580         }
581         boolean isBigEndian = (endian == 0x4d4d);
582         int magic = readUnsignedShort(stream, isBigEndian);
583         if (magic != 42) {
584             throw new
585         IllegalArgumentException JavaDoc("TIFFDirectory2");
586         }
587         
588         stream.seek(4L);
589         long offset = readUnsignedInt(stream, isBigEndian);
590
591         int numDirectories = 0;
592         while (offset != 0L) {
593             ++numDirectories;
594
595             stream.seek(offset);
596             int entries = readUnsignedShort(stream, isBigEndian);
597             stream.skip(12*entries);
598             offset = readUnsignedInt(stream, isBigEndian);
599         }
600       
601         stream.seek(pointer); // Reset stream pointer
602
return numDirectories;
603     }
604
605     /**
606      * Returns a boolean indicating whether the byte order used in the
607      * the TIFF file is big-endian. That is, whether the byte order is from
608      * the most significant to the least significant.
609      */

610     public boolean isBigEndian() {
611     return isBigEndian;
612     }
613
614     /**
615      * Returns the offset of the IFD corresponding to this
616      * <code>TIFFDirectory</code>.
617      */

618     public long getIFDOffset() {
619         return IFDOffset;
620     }
621
622     /**
623      * Returns the offset of the next IFD after the IFD corresponding to this
624      * <code>TIFFDirectory</code>.
625      */

626     public long getNextIFDOffset() {
627         return nextIFDOffset;
628     }
629 }
630
Popular Tags