001/* 002 * Copyright 2003-2008 by Paulo Soares. 003 * 004 * This code was originally released in 2001 by SUN (see class 005 * com.sun.media.imageio.plugins.tiff.TIFFDirectory.java) 006 * using the BSD license in a specific wording. In a mail dating from 007 * January 23, 2008, Brian Burkhalter (@sun.com) gave us permission 008 * to use the code under the following version of the BSD license: 009 * 010 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. 011 * 012 * Redistribution and use in source and binary forms, with or without 013 * modification, are permitted provided that the following conditions 014 * are met: 015 * 016 * - Redistribution of source code must retain the above copyright 017 * notice, this list of conditions and the following disclaimer. 018 * 019 * - Redistribution in binary form must reproduce the above copyright 020 * notice, this list of conditions and the following disclaimer in 021 * the documentation and/or other materials provided with the 022 * distribution. 023 * 024 * Neither the name of Sun Microsystems, Inc. or the names of 025 * contributors may be used to endorse or promote products derived 026 * from this software without specific prior written permission. 027 * 028 * This software is provided "AS IS," without a warranty of any 029 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 030 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 031 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY 032 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 033 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 034 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS 035 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 036 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, 037 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND 038 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR 039 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE 040 * POSSIBILITY OF SUCH DAMAGES. 041 * 042 * You acknowledge that this software is not designed or intended for 043 * use in the design, construction, operation or maintenance of any 044 * nuclear facility. 045 */ 046package com.itextpdf.text.pdf.codec; 047import java.io.EOFException; 048import java.io.IOException; 049import java.io.Serializable; 050import java.util.ArrayList; 051import java.util.Enumeration; 052import java.util.Hashtable; 053 054import com.itextpdf.text.error_messages.MessageLocalization; 055import com.itextpdf.text.pdf.RandomAccessFileOrArray; 056 057/** 058 * A class representing an Image File Directory (IFD) from a TIFF 6.0 059 * stream. The TIFF file format is described in more detail in the 060 * comments for the TIFFDescriptor class. 061 * 062 * <p> A TIFF IFD consists of a set of TIFFField tags. Methods are 063 * provided to query the set of tags and to obtain the raw field 064 * array. In addition, convenience methods are provided for acquiring 065 * the values of tags that contain a single value that fits into a 066 * byte, int, long, float, or double. 067 * 068 * <p> Every TIFF file is made up of one or more public IFDs that are 069 * joined in a linked list, rooted in the file header. A file may 070 * also contain so-called private IFDs that are referenced from 071 * tag data and do not appear in the main list. 072 * 073 * <p><b> This class is not a committed part of the JAI API. It may 074 * be removed or changed in future releases of JAI.</b> 075 * 076 * @see TIFFField 077 */ 078public class TIFFDirectory extends Object implements Serializable { 079 080 private static final long serialVersionUID = -168636766193675380L; 081 082 /** A boolean storing the endianness of the stream. */ 083 boolean isBigEndian; 084 085 /** The number of entries in the IFD. */ 086 int numEntries; 087 088 /** An array of TIFFFields. */ 089 TIFFField[] fields; 090 091 /** A Hashtable indexing the fields by tag number. */ 092 Hashtable<Integer, Integer> fieldIndex = new Hashtable<Integer, Integer>(); 093 094 /** The offset of this IFD. */ 095 long IFDOffset = 8; 096 097 /** The offset of the next IFD. */ 098 long nextIFDOffset = 0; 099 100 /** The default constructor. */ 101 TIFFDirectory() {} 102 103 private static boolean isValidEndianTag(int endian) { 104 return endian == 0x4949 || endian == 0x4d4d; 105 } 106 107 /** 108 * Constructs a TIFFDirectory from a SeekableStream. 109 * The directory parameter specifies which directory to read from 110 * the linked list present in the stream; directory 0 is normally 111 * read but it is possible to store multiple images in a single 112 * TIFF file by maintaining multiple directories. 113 * 114 * @param stream a SeekableStream to read from. 115 * @param directory the index of the directory to read. 116 */ 117 public TIFFDirectory(RandomAccessFileOrArray stream, int directory) 118 throws IOException { 119 120 long global_save_offset = stream.getFilePointer(); 121 long ifd_offset; 122 123 // Read the TIFF header 124 stream.seek(0L); 125 int endian = stream.readUnsignedShort(); 126 if (!isValidEndianTag(endian)) { 127 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("bad.endianness.tag.not.0x4949.or.0x4d4d")); 128 } 129 isBigEndian = endian == 0x4d4d; 130 131 int magic = readUnsignedShort(stream); 132 if (magic != 42) { 133 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("bad.magic.number.should.be.42")); 134 } 135 136 // Get the initial ifd offset as an unsigned int (using a long) 137 ifd_offset = readUnsignedInt(stream); 138 139 for (int i = 0; i < directory; i++) { 140 if (ifd_offset == 0L) { 141 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("directory.number.too.large")); 142 } 143 144 stream.seek(ifd_offset); 145 int entries = readUnsignedShort(stream); 146 stream.skip(12*entries); 147 148 ifd_offset = readUnsignedInt(stream); 149 } 150 151 stream.seek(ifd_offset); 152 initialize(stream); 153 stream.seek(global_save_offset); 154 } 155 156 /** 157 * Constructs a TIFFDirectory by reading a SeekableStream. 158 * The ifd_offset parameter specifies the stream offset from which 159 * to begin reading; this mechanism is sometimes used to store 160 * private IFDs within a TIFF file that are not part of the normal 161 * sequence of IFDs. 162 * 163 * @param stream a SeekableStream to read from. 164 * @param ifd_offset the long byte offset of the directory. 165 * @param directory the index of the directory to read beyond the 166 * one at the current stream offset; zero indicates the IFD 167 * at the current offset. 168 */ 169 public TIFFDirectory(RandomAccessFileOrArray stream, long ifd_offset, int directory) 170 throws IOException { 171 172 long global_save_offset = stream.getFilePointer(); 173 stream.seek(0L); 174 int endian = stream.readUnsignedShort(); 175 if (!isValidEndianTag(endian)) { 176 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("bad.endianness.tag.not.0x4949.or.0x4d4d")); 177 } 178 isBigEndian = endian == 0x4d4d; 179 180 // Seek to the first IFD. 181 stream.seek(ifd_offset); 182 183 // Seek to desired IFD if necessary. 184 int dirNum = 0; 185 while(dirNum < directory) { 186 // Get the number of fields in the current IFD. 187 int numEntries = readUnsignedShort(stream); 188 189 // Skip to the next IFD offset value field. 190 stream.seek(ifd_offset + 12*numEntries); 191 192 // Read the offset to the next IFD beyond this one. 193 ifd_offset = readUnsignedInt(stream); 194 195 // Seek to the next IFD. 196 stream.seek(ifd_offset); 197 198 // Increment the directory. 199 dirNum++; 200 } 201 202 initialize(stream); 203 stream.seek(global_save_offset); 204 } 205 206 private static final int[] sizeOfType = { 207 0, // 0 = n/a 208 1, // 1 = byte 209 1, // 2 = ascii 210 2, // 3 = short 211 4, // 4 = long 212 8, // 5 = rational 213 1, // 6 = sbyte 214 1, // 7 = undefined 215 2, // 8 = sshort 216 4, // 9 = slong 217 8, // 10 = srational 218 4, // 11 = float 219 8 // 12 = double 220 }; 221 222 private void initialize(RandomAccessFileOrArray stream) throws IOException { 223 long nextTagOffset = 0L; 224 long maxOffset = stream.length(); 225 int i, j; 226 227 IFDOffset = stream.getFilePointer(); 228 229 numEntries = readUnsignedShort(stream); 230 fields = new TIFFField[numEntries]; 231 232 for (i = 0; i < numEntries && nextTagOffset < maxOffset; i++) { 233 int tag = readUnsignedShort(stream); 234 int type = readUnsignedShort(stream); 235 int count = (int)readUnsignedInt(stream); 236 boolean processTag = true; 237 238 // The place to return to to read the next tag 239 nextTagOffset = stream.getFilePointer() + 4; 240 241 try { 242 // If the tag data can't fit in 4 bytes, the next 4 bytes 243 // contain the starting offset of the data 244 if (count*sizeOfType[type] > 4) { 245 long valueOffset = readUnsignedInt(stream); 246 247 // bounds check offset for EOF 248 if (valueOffset < maxOffset) { 249 stream.seek(valueOffset); 250 } 251 else { 252 // bad offset pointer .. skip tag 253 processTag = false; 254 } 255 } 256 } catch (ArrayIndexOutOfBoundsException ae) { 257 // if the data type is unknown we should skip this TIFF Field 258 processTag = false; 259 } 260 261 if (processTag) { 262 fieldIndex.put(Integer.valueOf(tag), Integer.valueOf(i)); 263 Object obj = null; 264 265 switch (type) { 266 case TIFFField.TIFF_BYTE: 267 case TIFFField.TIFF_SBYTE: 268 case TIFFField.TIFF_UNDEFINED: 269 case TIFFField.TIFF_ASCII: 270 byte[] bvalues = new byte[count]; 271 stream.readFully(bvalues, 0, count); 272 273 if (type == TIFFField.TIFF_ASCII) { 274 275 // Can be multiple strings 276 int index = 0, prevIndex = 0; 277 ArrayList<String> v = new ArrayList<String>(); 278 279 while (index < count) { 280 281 while (index < count && bvalues[index++] != 0); 282 283 // When we encountered zero, means one string has ended 284 v.add(new String(bvalues, prevIndex, 285 (index - prevIndex)) ); 286 prevIndex = index; 287 } 288 289 count = v.size(); 290 String strings[] = new String[count]; 291 for (int c = 0 ; c < count; c++) { 292 strings[c] = v.get(c); 293 } 294 295 obj = strings; 296 } else { 297 obj = bvalues; 298 } 299 300 break; 301 302 case TIFFField.TIFF_SHORT: 303 char[] cvalues = new char[count]; 304 for (j = 0; j < count; j++) { 305 cvalues[j] = (char)readUnsignedShort(stream); 306 } 307 obj = cvalues; 308 break; 309 310 case TIFFField.TIFF_LONG: 311 long[] lvalues = new long[count]; 312 for (j = 0; j < count; j++) { 313 lvalues[j] = readUnsignedInt(stream); 314 } 315 obj = lvalues; 316 break; 317 318 case TIFFField.TIFF_RATIONAL: 319 long[][] llvalues = new long[count][2]; 320 for (j = 0; j < count; j++) { 321 llvalues[j][0] = readUnsignedInt(stream); 322 llvalues[j][1] = readUnsignedInt(stream); 323 } 324 obj = llvalues; 325 break; 326 327 case TIFFField.TIFF_SSHORT: 328 short[] svalues = new short[count]; 329 for (j = 0; j < count; j++) { 330 svalues[j] = readShort(stream); 331 } 332 obj = svalues; 333 break; 334 335 case TIFFField.TIFF_SLONG: 336 int[] ivalues = new int[count]; 337 for (j = 0; j < count; j++) { 338 ivalues[j] = readInt(stream); 339 } 340 obj = ivalues; 341 break; 342 343 case TIFFField.TIFF_SRATIONAL: 344 int[][] iivalues = new int[count][2]; 345 for (j = 0; j < count; j++) { 346 iivalues[j][0] = readInt(stream); 347 iivalues[j][1] = readInt(stream); 348 } 349 obj = iivalues; 350 break; 351 352 case TIFFField.TIFF_FLOAT: 353 float[] fvalues = new float[count]; 354 for (j = 0; j < count; j++) { 355 fvalues[j] = readFloat(stream); 356 } 357 obj = fvalues; 358 break; 359 360 case TIFFField.TIFF_DOUBLE: 361 double[] dvalues = new double[count]; 362 for (j = 0; j < count; j++) { 363 dvalues[j] = readDouble(stream); 364 } 365 obj = dvalues; 366 break; 367 368 default: 369 break; 370 } 371 372 fields[i] = new TIFFField(tag, type, count, obj); 373 } 374 375 stream.seek(nextTagOffset); 376 } 377 378 // Read the offset of the next IFD. 379 try { 380 nextIFDOffset = readUnsignedInt(stream); 381 } 382 catch (Exception e) { 383 // broken tiffs may not have this pointer 384 nextIFDOffset = 0; 385 } 386 } 387 388 /** Returns the number of directory entries. */ 389 public int getNumEntries() { 390 return numEntries; 391 } 392 393 /** 394 * Returns the value of a given tag as a TIFFField, 395 * or null if the tag is not present. 396 */ 397 public TIFFField getField(int tag) { 398 Integer i = fieldIndex.get(Integer.valueOf(tag)); 399 if (i == null) { 400 return null; 401 } else { 402 return fields[i.intValue()]; 403 } 404 } 405 406 /** 407 * Returns true if a tag appears in the directory. 408 */ 409 public boolean isTagPresent(int tag) { 410 return fieldIndex.containsKey(Integer.valueOf(tag)); 411 } 412 413 /** 414 * Returns an ordered array of ints indicating the tag 415 * values. 416 */ 417 public int[] getTags() { 418 int[] tags = new int[fieldIndex.size()]; 419 Enumeration<Integer> e = fieldIndex.keys(); 420 int i = 0; 421 422 while (e.hasMoreElements()) { 423 tags[i++] = e.nextElement().intValue(); 424 } 425 426 return tags; 427 } 428 429 /** 430 * Returns an array of TIFFFields containing all the fields 431 * in this directory. 432 */ 433 public TIFFField[] getFields() { 434 return fields; 435 } 436 437 /** 438 * Returns the value of a particular index of a given tag as a 439 * byte. The caller is responsible for ensuring that the tag is 440 * present and has type TIFFField.TIFF_SBYTE, TIFF_BYTE, or 441 * TIFF_UNDEFINED. 442 */ 443 public byte getFieldAsByte(int tag, int index) { 444 Integer i = fieldIndex.get(Integer.valueOf(tag)); 445 byte [] b = fields[i.intValue()].getAsBytes(); 446 return b[index]; 447 } 448 449 /** 450 * Returns the value of index 0 of a given tag as a 451 * byte. The caller is responsible for ensuring that the tag is 452 * present and has type TIFFField.TIFF_SBYTE, TIFF_BYTE, or 453 * TIFF_UNDEFINED. 454 */ 455 public byte getFieldAsByte(int tag) { 456 return getFieldAsByte(tag, 0); 457 } 458 459 /** 460 * Returns the value of a particular index of a given tag as a 461 * long. The caller is responsible for ensuring that the tag is 462 * present and has type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, 463 * TIFF_SHORT, TIFF_SSHORT, TIFF_SLONG or TIFF_LONG. 464 */ 465 public long getFieldAsLong(int tag, int index) { 466 Integer i = fieldIndex.get(Integer.valueOf(tag)); 467 return fields[i.intValue()].getAsLong(index); 468 } 469 470 /** 471 * Returns the value of index 0 of a given tag as a 472 * long. The caller is responsible for ensuring that the tag is 473 * present and has type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, 474 * TIFF_SHORT, TIFF_SSHORT, TIFF_SLONG or TIFF_LONG. 475 */ 476 public long getFieldAsLong(int tag) { 477 return getFieldAsLong(tag, 0); 478 } 479 480 /** 481 * Returns the value of a particular index of a given tag as a 482 * float. The caller is responsible for ensuring that the tag is 483 * present and has numeric type (all but TIFF_UNDEFINED and 484 * TIFF_ASCII). 485 */ 486 public float getFieldAsFloat(int tag, int index) { 487 Integer i = fieldIndex.get(Integer.valueOf(tag)); 488 return fields[i.intValue()].getAsFloat(index); 489 } 490 491 /** 492 * Returns the value of index 0 of a given tag as a float. The 493 * caller is responsible for ensuring that the tag is present and 494 * has numeric type (all but TIFF_UNDEFINED and TIFF_ASCII). 495 */ 496 public float getFieldAsFloat(int tag) { 497 return getFieldAsFloat(tag, 0); 498 } 499 500 /** 501 * Returns the value of a particular index of a given tag as a 502 * double. The caller is responsible for ensuring that the tag is 503 * present and has numeric type (all but TIFF_UNDEFINED and 504 * TIFF_ASCII). 505 */ 506 public double getFieldAsDouble(int tag, int index) { 507 Integer i = fieldIndex.get(Integer.valueOf(tag)); 508 return fields[i.intValue()].getAsDouble(index); 509 } 510 511 /** 512 * Returns the value of index 0 of a given tag as a double. The 513 * caller is responsible for ensuring that the tag is present and 514 * has numeric type (all but TIFF_UNDEFINED and TIFF_ASCII). 515 */ 516 public double getFieldAsDouble(int tag) { 517 return getFieldAsDouble(tag, 0); 518 } 519 520 // Methods to read primitive data types from the stream 521 522 private short readShort(RandomAccessFileOrArray stream) 523 throws IOException { 524 if (isBigEndian) { 525 return stream.readShort(); 526 } else { 527 return stream.readShortLE(); 528 } 529 } 530 531 private int readUnsignedShort(RandomAccessFileOrArray stream) 532 throws IOException { 533 if (isBigEndian) { 534 return stream.readUnsignedShort(); 535 } else { 536 return stream.readUnsignedShortLE(); 537 } 538 } 539 540 private int readInt(RandomAccessFileOrArray stream) 541 throws IOException { 542 if (isBigEndian) { 543 return stream.readInt(); 544 } else { 545 return stream.readIntLE(); 546 } 547 } 548 549 private long readUnsignedInt(RandomAccessFileOrArray stream) 550 throws IOException { 551 if (isBigEndian) { 552 return stream.readUnsignedInt(); 553 } else { 554 return stream.readUnsignedIntLE(); 555 } 556 } 557 558 private long readLong(RandomAccessFileOrArray stream) 559 throws IOException { 560 if (isBigEndian) { 561 return stream.readLong(); 562 } else { 563 return stream.readLongLE(); 564 } 565 } 566 567 private float readFloat(RandomAccessFileOrArray stream) 568 throws IOException { 569 if (isBigEndian) { 570 return stream.readFloat(); 571 } else { 572 return stream.readFloatLE(); 573 } 574 } 575 576 private double readDouble(RandomAccessFileOrArray stream) 577 throws IOException { 578 if (isBigEndian) { 579 return stream.readDouble(); 580 } else { 581 return stream.readDoubleLE(); 582 } 583 } 584 585 private static int readUnsignedShort(RandomAccessFileOrArray stream, 586 boolean isBigEndian) 587 throws IOException { 588 if (isBigEndian) { 589 return stream.readUnsignedShort(); 590 } else { 591 return stream.readUnsignedShortLE(); 592 } 593 } 594 595 private static long readUnsignedInt(RandomAccessFileOrArray stream, 596 boolean isBigEndian) 597 throws IOException { 598 if (isBigEndian) { 599 return stream.readUnsignedInt(); 600 } else { 601 return stream.readUnsignedIntLE(); 602 } 603 } 604 605 // Utilities 606 607 /** 608 * Returns the number of image directories (subimages) stored in a 609 * given TIFF file, represented by a <code>SeekableStream</code>. 610 */ 611 public static int getNumDirectories(RandomAccessFileOrArray stream) 612 throws IOException{ 613 long pointer = stream.getFilePointer(); // Save stream pointer 614 615 stream.seek(0L); 616 int endian = stream.readUnsignedShort(); 617 if (!isValidEndianTag(endian)) { 618 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("bad.endianness.tag.not.0x4949.or.0x4d4d")); 619 } 620 boolean isBigEndian = endian == 0x4d4d; 621 int magic = readUnsignedShort(stream, isBigEndian); 622 if (magic != 42) { 623 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("bad.magic.number.should.be.42")); 624 } 625 626 stream.seek(4L); 627 long offset = readUnsignedInt(stream, isBigEndian); 628 629 int numDirectories = 0; 630 while (offset != 0L) { 631 ++numDirectories; 632 633 // EOFException means IFD was probably not properly terminated. 634 try { 635 stream.seek(offset); 636 int entries = readUnsignedShort(stream, isBigEndian); 637 stream.skip(12*entries); 638 offset = readUnsignedInt(stream, isBigEndian); 639 } catch(EOFException eof) { 640 //numDirectories--; 641 break; 642 } 643 } 644 645 stream.seek(pointer); // Reset stream pointer 646 return numDirectories; 647 } 648 649 /** 650 * Returns a boolean indicating whether the byte order used in the 651 * the TIFF file is big-endian (i.e. whether the byte order is from 652 * the most significant to the least significant) 653 */ 654 public boolean isBigEndian() { 655 return isBigEndian; 656 } 657 658 /** 659 * Returns the offset of the IFD corresponding to this 660 * <code>TIFFDirectory</code>. 661 */ 662 public long getIFDOffset() { 663 return IFDOffset; 664 } 665 666 /** 667 * Returns the offset of the next IFD after the IFD corresponding to this 668 * <code>TIFFDirectory</code>. 669 */ 670 public long getNextIFDOffset() { 671 return nextIFDOffset; 672 } 673}