Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/batik/ext/awt/image/codec/tiff/TIFFDirectory.java


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;
21  import java.io.Serializable;
22  import java.util.Iterator;
23  import java.util.HashMap;
24  import java.util.Map;
25  import java.util.Vector;
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 implements Serializable {
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 fieldIndex = new HashMap();
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 {
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("TIFFDirectory1");
99          }
100         isBigEndian = (endian == 0x4d4d);
101 
102         int magic = readUnsignedShort(stream);
103         if (magic != 42) {
104             throw new 
105     IllegalArgumentException("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("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 {
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("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 {
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 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(tag), new Integer(i));
230             Object 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 v = new Vector();
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(bvalues, prevIndex, 
252            (index - prevIndex)) );
253       prevIndex = index;
254         }
255 
256         count = v.size();
257         String strings[] = new String[count];
258         for (int c = 0 ; c < count; c++) {
259       strings[c] = (String)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 i = (Integer)fieldIndex.get(new Integer(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(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 iter = fieldIndex.keySet().iterator();
380         int i = 0;
381 
382         while (iter.hasNext()) {
383             tags[i++] = ((Integer)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 i = (Integer)fieldIndex.get(new Integer(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 i = (Integer)fieldIndex.get(new Integer(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 i = (Integer)fieldIndex.get(new Integer(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 i = (Integer)fieldIndex.get(new Integer(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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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{
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("TIFFDirectory1");
580         }
581         boolean isBigEndian = (endian == 0x4d4d);
582         int magic = readUnsignedShort(stream, isBigEndian);
583         if (magic != 42) {
584             throw new 
585     IllegalArgumentException("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 }