1 /* 2 * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.imageio.plugins.tiff; 26 27 import java.io.EOFException; 28 import java.io.IOException; 29 import java.nio.charset.StandardCharsets; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.Iterator; 33 import java.util.List; 34 import java.util.Set; 35 import javax.imageio.IIOException; 36 import javax.imageio.stream.ImageInputStream; 37 import javax.imageio.stream.ImageOutputStream; 38 import javax.imageio.plugins.tiff.BaselineTIFFTagSet; 39 import javax.imageio.plugins.tiff.TIFFDirectory; 40 import javax.imageio.plugins.tiff.TIFFField; 41 import javax.imageio.plugins.tiff.TIFFTag; 42 import javax.imageio.plugins.tiff.TIFFTagSet; 43 44 public class TIFFIFD extends TIFFDirectory { 45 private static final long MAX_SAMPLES_PER_PIXEL = 0xffff; 46 private static final long MAX_ASCII_SIZE = 0xffff; 47 48 private long stripOrTileByteCountsPosition = -1; 49 private long stripOrTileOffsetsPosition = -1; 50 private long lastPosition = -1; 51 52 // 53 // A set of tag numbers corresponding to tags essential to decoding 54 // the image and metadata required to interpret its samples. 55 // 56 private static volatile Set<Integer> essentialTags = null; 57 58 private static void initializeEssentialTags() { 59 Set<Integer> tags = essentialTags; 60 if (tags == null) { 61 essentialTags = tags = Set.of( 62 BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE, 63 BaselineTIFFTagSet.TAG_COLOR_MAP, 64 BaselineTIFFTagSet.TAG_COMPRESSION, 65 BaselineTIFFTagSet.TAG_EXTRA_SAMPLES, 66 BaselineTIFFTagSet.TAG_FILL_ORDER, 67 BaselineTIFFTagSet.TAG_ICC_PROFILE, 68 BaselineTIFFTagSet.TAG_IMAGE_LENGTH, 69 BaselineTIFFTagSet.TAG_IMAGE_WIDTH, 70 BaselineTIFFTagSet.TAG_JPEG_AC_TABLES, 71 BaselineTIFFTagSet.TAG_JPEG_DC_TABLES, 72 BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT, 73 BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 74 BaselineTIFFTagSet.TAG_JPEG_PROC, 75 BaselineTIFFTagSet.TAG_JPEG_Q_TABLES, 76 BaselineTIFFTagSet.TAG_JPEG_RESTART_INTERVAL, 77 BaselineTIFFTagSet.TAG_JPEG_TABLES, 78 BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION, 79 BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION, 80 BaselineTIFFTagSet.TAG_PREDICTOR, 81 BaselineTIFFTagSet.TAG_REFERENCE_BLACK_WHITE, 82 BaselineTIFFTagSet.TAG_ROWS_PER_STRIP, 83 BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL, 84 BaselineTIFFTagSet.TAG_SAMPLE_FORMAT, 85 BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS, 86 BaselineTIFFTagSet.TAG_STRIP_OFFSETS, 87 BaselineTIFFTagSet.TAG_T4_OPTIONS, 88 BaselineTIFFTagSet.TAG_T6_OPTIONS, 89 BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS, 90 BaselineTIFFTagSet.TAG_TILE_LENGTH, 91 BaselineTIFFTagSet.TAG_TILE_OFFSETS, 92 BaselineTIFFTagSet.TAG_TILE_WIDTH, 93 BaselineTIFFTagSet.TAG_Y_CB_CR_COEFFICIENTS, 94 BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING 95 ); 96 } 97 } 98 99 /** 100 * Converts a {@code TIFFDirectory} to a {@code TIFFIFD}. 101 */ 102 public static TIFFIFD getDirectoryAsIFD(TIFFDirectory dir) { 103 if(dir instanceof TIFFIFD) { 104 return (TIFFIFD)dir; 105 } 106 107 TIFFIFD ifd = new TIFFIFD(Arrays.asList(dir.getTagSets()), 108 dir.getParentTag()); 109 TIFFField[] fields = dir.getTIFFFields(); 110 int numFields = fields.length; 111 for(int i = 0; i < numFields; i++) { 112 TIFFField f = fields[i]; 113 TIFFTag tag = f.getTag(); 114 if(tag.isIFDPointer()) { 115 TIFFDirectory subDir = null; 116 if (f.hasDirectory()) { 117 subDir = f.getDirectory(); 118 } else if (f.getData() instanceof TIFFDirectory) { 119 subDir = (TIFFDirectory)f.getData(); 120 } 121 if (subDir != null) { 122 TIFFDirectory subIFD = getDirectoryAsIFD(subDir); 123 f = new TIFFField(tag, f.getType(), (long)f.getCount(), 124 subIFD); 125 } else { 126 f = null; 127 } 128 } 129 if (f != null) { 130 ifd.addTIFFField(f); 131 } 132 } 133 134 return ifd; 135 } 136 137 public static TIFFTag getTag(int tagNumber, List<TIFFTagSet> tagSets) { 138 Iterator<TIFFTagSet> iter = tagSets.iterator(); 139 while (iter.hasNext()) { 140 TIFFTagSet tagSet = iter.next(); 141 TIFFTag tag = tagSet.getTag(tagNumber); 142 if (tag != null) { 143 return tag; 144 } 145 } 146 147 return null; 148 } 149 150 public static TIFFTag getTag(String tagName, List<TIFFTagSet> tagSets) { 151 Iterator<TIFFTagSet> iter = tagSets.iterator(); 152 while (iter.hasNext()) { 153 TIFFTagSet tagSet = iter.next(); 154 TIFFTag tag = tagSet.getTag(tagName); 155 if (tag != null) { 156 return tag; 157 } 158 } 159 160 return null; 161 } 162 163 private static void writeTIFFFieldToStream(TIFFField field, 164 ImageOutputStream stream) 165 throws IOException { 166 int count = field.getCount(); 167 Object data = field.getData(); 168 169 switch (field.getType()) { 170 case TIFFTag.TIFF_ASCII: 171 for (int i = 0; i < count; i++) { 172 String s = ((String[])data)[i]; 173 int length = s.length(); 174 for (int j = 0; j < length; j++) { 175 stream.writeByte(s.charAt(j) & 0xff); 176 } 177 stream.writeByte(0); 178 } 179 break; 180 case TIFFTag.TIFF_UNDEFINED: 181 case TIFFTag.TIFF_BYTE: 182 case TIFFTag.TIFF_SBYTE: 183 stream.write((byte[])data); 184 break; 185 case TIFFTag.TIFF_SHORT: 186 stream.writeChars((char[])data, 0, ((char[])data).length); 187 break; 188 case TIFFTag.TIFF_SSHORT: 189 stream.writeShorts((short[])data, 0, ((short[])data).length); 190 break; 191 case TIFFTag.TIFF_SLONG: 192 stream.writeInts((int[])data, 0, ((int[])data).length); 193 break; 194 case TIFFTag.TIFF_LONG: 195 for (int i = 0; i < count; i++) { 196 stream.writeInt((int)(((long[])data)[i])); 197 } 198 break; 199 case TIFFTag.TIFF_IFD_POINTER: 200 stream.writeInt(0); // will need to be backpatched 201 break; 202 case TIFFTag.TIFF_FLOAT: 203 stream.writeFloats((float[])data, 0, ((float[])data).length); 204 break; 205 case TIFFTag.TIFF_DOUBLE: 206 stream.writeDoubles((double[])data, 0, ((double[])data).length); 207 break; 208 case TIFFTag.TIFF_SRATIONAL: 209 for (int i = 0; i < count; i++) { 210 stream.writeInt(((int[][])data)[i][0]); 211 stream.writeInt(((int[][])data)[i][1]); 212 } 213 break; 214 case TIFFTag.TIFF_RATIONAL: 215 for (int i = 0; i < count; i++) { 216 long num = ((long[][])data)[i][0]; 217 long den = ((long[][])data)[i][1]; 218 stream.writeInt((int)num); 219 stream.writeInt((int)den); 220 } 221 break; 222 default: 223 // error 224 } 225 } 226 227 public TIFFIFD(List<TIFFTagSet> tagSets, TIFFTag parentTag) { 228 super(tagSets.toArray(new TIFFTagSet[tagSets.size()]), 229 parentTag); 230 } 231 232 public TIFFIFD(List<TIFFTagSet> tagSets) { 233 this(tagSets, null); 234 } 235 236 public List<TIFFTagSet> getTagSetList() { 237 return Arrays.asList(getTagSets()); 238 } 239 240 /** 241 * Returns an {@code Iterator} over the TIFF fields. The 242 * traversal is in the order of increasing tag number. 243 */ 244 // Note: the sort is guaranteed for low fields by the use of an 245 // array wherein the index corresponds to the tag number and for 246 // the high fields by the use of a TreeMap with tag number keys. 247 public Iterator<TIFFField> iterator() { 248 return Arrays.asList(getTIFFFields()).iterator(); 249 } 250 251 /** 252 * Read the value of a field. The {@code data} parameter should be 253 * an array of length 1 of Object. 254 * 255 * @param stream the input stream 256 * @param type the type as read from the stream 257 * @param count the count read from the stream 258 * @param data a container for the data 259 * @return the updated count 260 * @throws IOException 261 */ 262 private static int readFieldValue(ImageInputStream stream, 263 int type, int count, Object[] data) throws IOException { 264 Object obj; 265 266 switch (type) { 267 case TIFFTag.TIFF_BYTE: 268 case TIFFTag.TIFF_SBYTE: 269 case TIFFTag.TIFF_UNDEFINED: 270 case TIFFTag.TIFF_ASCII: 271 byte[] bvalues = new byte[count]; 272 stream.readFully(bvalues, 0, count); 273 274 if (type == TIFFTag.TIFF_ASCII) { 275 // Can be multiple strings 276 ArrayList<String> v = new ArrayList<>(); 277 boolean inString = false; 278 int prevIndex = 0; 279 for (int index = 0; index <= count; index++) { 280 if (index < count && bvalues[index] != 0) { 281 if (!inString) { 282 // start of string 283 prevIndex = index; 284 inString = true; 285 } 286 } else { // null or special case at end of string 287 if (inString) { 288 // end of string 289 String s = new String(bvalues, prevIndex, 290 index - prevIndex, 291 StandardCharsets.US_ASCII); 292 v.add(s); 293 inString = false; 294 } 295 } 296 } 297 298 count = v.size(); 299 String[] strings; 300 if (count != 0) { 301 strings = new String[count]; 302 for (int c = 0; c < count; c++) { 303 strings[c] = v.get(c); 304 } 305 } else { 306 // This case has been observed when the value of 307 // 'count' recorded in the field is non-zero but 308 // the value portion contains all nulls. 309 count = 1; 310 strings = new String[]{""}; 311 } 312 313 obj = strings; 314 } else { 315 obj = bvalues; 316 } 317 break; 318 319 case TIFFTag.TIFF_SHORT: 320 char[] cvalues = new char[count]; 321 for (int j = 0; j < count; j++) { 322 cvalues[j] = (char) (stream.readUnsignedShort()); 323 } 324 obj = cvalues; 325 break; 326 327 case TIFFTag.TIFF_LONG: 328 case TIFFTag.TIFF_IFD_POINTER: 329 long[] lvalues = new long[count]; 330 for (int j = 0; j < count; j++) { 331 lvalues[j] = stream.readUnsignedInt(); 332 } 333 obj = lvalues; 334 break; 335 336 case TIFFTag.TIFF_RATIONAL: 337 long[][] llvalues = new long[count][2]; 338 for (int j = 0; j < count; j++) { 339 llvalues[j][0] = stream.readUnsignedInt(); 340 llvalues[j][1] = stream.readUnsignedInt(); 341 } 342 obj = llvalues; 343 break; 344 345 case TIFFTag.TIFF_SSHORT: 346 short[] svalues = new short[count]; 347 for (int j = 0; j < count; j++) { 348 svalues[j] = stream.readShort(); 349 } 350 obj = svalues; 351 break; 352 353 case TIFFTag.TIFF_SLONG: 354 int[] ivalues = new int[count]; 355 for (int j = 0; j < count; j++) { 356 ivalues[j] = stream.readInt(); 357 } 358 obj = ivalues; 359 break; 360 361 case TIFFTag.TIFF_SRATIONAL: 362 int[][] iivalues = new int[count][2]; 363 for (int j = 0; j < count; j++) { 364 iivalues[j][0] = stream.readInt(); 365 iivalues[j][1] = stream.readInt(); 366 } 367 obj = iivalues; 368 break; 369 370 case TIFFTag.TIFF_FLOAT: 371 float[] fvalues = new float[count]; 372 for (int j = 0; j < count; j++) { 373 fvalues[j] = stream.readFloat(); 374 } 375 obj = fvalues; 376 break; 377 378 case TIFFTag.TIFF_DOUBLE: 379 double[] dvalues = new double[count]; 380 for (int j = 0; j < count; j++) { 381 dvalues[j] = stream.readDouble(); 382 } 383 obj = dvalues; 384 break; 385 386 default: 387 obj = null; 388 break; 389 } 390 391 data[0] = obj; 392 393 return count; 394 } 395 396 // 397 // Class to represent an IFD entry where the actual content is at an offset 398 // in the stream somewhere outside the IFD itself. This occurs when the 399 // value cannot be contained within four bytes. Seeking is required to read 400 // such field values. 401 // 402 private static class TIFFIFDEntry { 403 public final TIFFTag tag; 404 public final int type; 405 public final int count; 406 public final long offset; 407 408 TIFFIFDEntry(TIFFTag tag, int type, int count, long offset) { 409 this.tag = tag; 410 this.type = type; 411 this.count = count; 412 this.offset = offset; 413 } 414 } 415 416 // 417 // Verify that data pointed to outside of the IFD itself are within the 418 // stream. To be called after all fields have been read and populated. 419 // 420 private void checkFieldOffsets(long streamLength) throws IIOException { 421 if (streamLength < 0) { 422 return; 423 } 424 425 // StripOffsets 426 List<TIFFField> offsets = new ArrayList<>(); 427 TIFFField f = getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS); 428 int count = 0; 429 if (f != null) { 430 count = f.getCount(); 431 offsets.add(f); 432 } 433 434 // TileOffsets 435 f = getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS); 436 if (f != null) { 437 int sz = offsets.size(); 438 int newCount = f.getCount(); 439 if (sz > 0 && newCount != count) { 440 throw new IIOException 441 ("StripOffsets count != TileOffsets count"); 442 } 443 444 if (sz == 0) { 445 count = newCount; 446 } 447 offsets.add(f); 448 } 449 450 List<TIFFField> byteCounts = new ArrayList<>(); 451 if (offsets.size() > 0) { 452 // StripByteCounts 453 f = getTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS); 454 if (f != null) { 455 if (f.getCount() != count) { 456 throw new IIOException 457 ("StripByteCounts count != number of offsets"); 458 } 459 byteCounts.add(f); 460 } 461 462 // TileByteCounts 463 f = getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS); 464 if (f != null) { 465 if (f.getCount() != count) { 466 throw new IIOException 467 ("TileByteCounts count != number of offsets"); 468 } 469 byteCounts.add(f); 470 } 471 472 if (byteCounts.size() > 0) { 473 for (TIFFField offset : offsets) { 474 for (TIFFField byteCount : byteCounts) { 475 for (int i = 0; i < count; i++) { 476 long dataOffset = offset.getAsLong(i); 477 long dataByteCount = byteCount.getAsLong(i); 478 if (dataOffset + dataByteCount > streamLength) { 479 throw new IIOException 480 ("Data segment out of stream"); 481 } 482 } 483 } 484 } 485 } 486 } 487 488 // JPEGInterchangeFormat and JPEGInterchangeFormatLength 489 TIFFField jpegOffset = 490 getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT); 491 if (jpegOffset != null) { 492 TIFFField jpegLength = 493 getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 494 if (jpegLength != null) { 495 if (jpegOffset.getAsLong(0) + jpegLength.getAsLong(0) 496 > streamLength) { 497 throw new IIOException 498 ("JPEGInterchangeFormat data out of stream"); 499 } 500 } 501 } 502 503 // Ensure there is at least a data pointer for JPEG interchange format or 504 // both data offsets and byte counts for other compression types. 505 if (jpegOffset == null && (offsets.size() == 0 || byteCounts.size() == 0)) { 506 throw new IIOException("Insufficient data offsets or byte counts"); 507 } 508 509 // JPEGQTables - one 64-byte table for each offset. 510 f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_Q_TABLES); 511 if (f != null) { 512 long[] tableOffsets = f.getAsLongs(); 513 for (long off : tableOffsets) { 514 if (off + 64 > streamLength) { 515 throw new IIOException("JPEGQTables data out of stream"); 516 } 517 } 518 } 519 520 // JPEGDCTables 521 f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_DC_TABLES); 522 if (f != null) { 523 long[] tableOffsets = f.getAsLongs(); 524 for (long off : tableOffsets) { 525 if (off + 16 > streamLength) { 526 throw new IIOException("JPEGDCTables data out of stream"); 527 } 528 } 529 } 530 531 // JPEGACTables 532 f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_AC_TABLES); 533 if (f != null) { 534 long[] tableOffsets = f.getAsLongs(); 535 for (long off : tableOffsets) { 536 if (off + 16 > streamLength) { 537 throw new IIOException("JPEGACTables data out of stream"); 538 } 539 } 540 } 541 } 542 543 // Stream position initially at beginning, left at end 544 // if readUnknownTags is false, do not load fields for which 545 // a tag cannot be found in an allowed TagSet. 546 public void initialize(ImageInputStream stream, boolean isPrimaryIFD, 547 boolean ignoreMetadata, boolean readUnknownTags) throws IOException { 548 549 removeTIFFFields(); 550 551 long streamLength = stream.length(); 552 boolean haveStreamLength = streamLength != -1; 553 554 List<TIFFTagSet> tagSetList = getTagSetList(); 555 556 // Configure essential tag variables if this is the primary IFD and 557 // either all metadata are being ignored, or metadata are not being 558 // ignored but both unknown tags are being ignored and the tag set 559 // list does not contain the baseline tags. 560 boolean ensureEssentialTags = false; 561 TIFFTagSet baselineTagSet = null; 562 if (isPrimaryIFD && 563 (ignoreMetadata || 564 (!readUnknownTags && 565 !tagSetList.contains(BaselineTIFFTagSet.getInstance())))) { 566 ensureEssentialTags = true; 567 initializeEssentialTags(); 568 baselineTagSet = BaselineTIFFTagSet.getInstance(); 569 } 570 571 List<Object> entries = new ArrayList<>(); 572 Object[] entryData = new Object[1]; // allocate once for later reuse. 573 574 // Read the IFD entries, loading the field values which are no more than 575 // four bytes long, and storing the 4-byte offsets for the others. 576 int numEntries = stream.readUnsignedShort(); 577 for (int i = 0; i < numEntries; i++) { 578 // Read tag number, value type, and value count. 579 int tagNumber = stream.readUnsignedShort(); 580 int type = stream.readUnsignedShort(); 581 int sizeOfType; 582 try { 583 sizeOfType = TIFFTag.getSizeOfType(type); 584 } catch (IllegalArgumentException ignored) { 585 // Continue with the next IFD entry. 586 stream.skipBytes(4); 587 continue; 588 } 589 long longCount = stream.readUnsignedInt(); 590 591 // Get the associated TIFFTag. 592 TIFFTag tag = getTag(tagNumber, tagSetList); 593 594 if (tag == null && ensureEssentialTags 595 && essentialTags.contains(tagNumber)) { 596 tag = baselineTagSet.getTag(tagNumber); 597 } 598 599 // Ignore non-essential fields, unknown fields unless forcibly 600 // being read, fields with unknown type, and fields 601 // with count out of int range. 602 if((ignoreMetadata && 603 (!ensureEssentialTags || !essentialTags.contains(tagNumber))) 604 || (tag == null && !readUnknownTags) 605 || (tag != null && !tag.isDataTypeOK(type)) 606 || longCount > Integer.MAX_VALUE) { 607 // Skip the value/offset so as to leave the stream 608 // position at the start of the next IFD entry. 609 stream.skipBytes(4); 610 611 // Continue with the next IFD entry. 612 continue; 613 } 614 615 int count = (int)longCount; 616 617 if (tag == null) { 618 tag = new TIFFTag(TIFFTag.UNKNOWN_TAG_NAME, tagNumber, 619 1 << type, count); 620 } else { 621 int expectedCount = tag.getCount(); 622 if (expectedCount > 0) { 623 // If the tag count is positive then the tag defines a 624 // specific, fixed count that the field must match. 625 if (count != expectedCount) { 626 throw new IIOException("Unexpected count " 627 + count + " for " + tag.getName() + " field"); 628 } 629 } else if (type == TIFFTag.TIFF_ASCII) { 630 // Clamp the size of ASCII fields of unspecified length 631 // to a maximum value. 632 int asciiSize = TIFFTag.getSizeOfType(TIFFTag.TIFF_ASCII); 633 if (count*asciiSize > MAX_ASCII_SIZE) { 634 count = (int)(MAX_ASCII_SIZE/asciiSize); 635 } 636 } 637 } 638 639 long longSize = longCount*sizeOfType; 640 if (longSize > Integer.MAX_VALUE) { 641 // Continue with the next IFD entry. 642 stream.skipBytes(4); 643 continue; 644 } 645 int size = (int)longSize; 646 647 if (size > 4 || tag.isIFDPointer()) { 648 // The IFD entry value is a pointer to the actual field value. 649 long offset = stream.readUnsignedInt(); 650 651 // Check whether the the field value is within the stream. 652 if (haveStreamLength && offset + size > streamLength) { 653 continue; 654 } 655 656 // Add a TIFFIFDEntry as a placeholder. This avoids a mark, 657 // seek to the data, and a reset. 658 entries.add(new TIFFIFDEntry(tag, type, count, offset)); 659 } else { 660 // The IFD entry value is the actual field value of no more than 661 // four bytes. 662 Object obj = null; 663 try { 664 // Read the field value and update the count. 665 count = readFieldValue(stream, type, count, entryData); 666 obj = entryData[0]; 667 } catch (EOFException eofe) { 668 // The TIFF 6.0 fields have tag numbers less than or equal 669 // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright). 670 // If there is an error reading a baseline tag, then re-throw 671 // the exception and fail; otherwise continue with the next 672 // field. 673 if (BaselineTIFFTagSet.getInstance().getTag(tagNumber) == null) { 674 throw eofe; 675 } 676 } 677 678 // If the field value is smaller than four bytes then skip 679 // the remaining, unused bytes. 680 if (size < 4) { 681 stream.skipBytes(4 - size); 682 } 683 684 // Add the populated TIFFField to the list of entries. 685 entries.add(new TIFFField(tag, type, count, obj)); 686 } 687 } 688 689 // After reading the IFD entries the stream is positioned at an unsigned 690 // four byte integer containing either the offset of the next IFD or 691 // zero if this is the last IFD. 692 long nextIFDOffset = stream.getStreamPosition(); 693 694 Object[] fieldData = new Object[1]; 695 for (Object entry : entries) { 696 if (entry instanceof TIFFField) { 697 // Add the populated field directly. 698 addTIFFField((TIFFField)entry); 699 } else { 700 TIFFIFDEntry e = (TIFFIFDEntry)entry; 701 TIFFTag tag = e.tag; 702 int tagNumber = tag.getNumber(); 703 int type = e.type; 704 int count = e.count; 705 706 stream.seek(e.offset); 707 708 if (tag.isIFDPointer()) { 709 List<TIFFTagSet> tagSets = new ArrayList<TIFFTagSet>(1); 710 tagSets.add(tag.getTagSet()); 711 TIFFIFD subIFD = new TIFFIFD(tagSets); 712 713 subIFD.initialize(stream, false, ignoreMetadata, 714 readUnknownTags); 715 TIFFField f = new TIFFField(tag, type, e.offset, subIFD); 716 addTIFFField(f); 717 } else { 718 if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS 719 || tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS 720 || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) { 721 this.stripOrTileByteCountsPosition 722 = stream.getStreamPosition(); 723 } else if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_OFFSETS 724 || tagNumber == BaselineTIFFTagSet.TAG_TILE_OFFSETS 725 || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) { 726 this.stripOrTileOffsetsPosition 727 = stream.getStreamPosition(); 728 } 729 730 Object obj = null; 731 try { 732 count = readFieldValue(stream, type, count, fieldData); 733 obj = fieldData[0]; 734 } catch (EOFException eofe) { 735 // The TIFF 6.0 fields have tag numbers less than or equal 736 // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright). 737 // If there is an error reading a baseline tag, then re-throw 738 // the exception and fail; otherwise continue with the next 739 // field. 740 if (BaselineTIFFTagSet.getInstance().getTag(tagNumber) == null) { 741 throw eofe; 742 } 743 } 744 745 if (obj == null) { 746 continue; 747 } 748 749 TIFFField f = new TIFFField(tag, type, count, obj); 750 addTIFFField(f); 751 } 752 } 753 } 754 755 if(isPrimaryIFD && haveStreamLength) { 756 checkFieldOffsets(streamLength); 757 } 758 759 stream.seek(nextIFDOffset); 760 this.lastPosition = stream.getStreamPosition(); 761 } 762 763 public void writeToStream(ImageOutputStream stream) 764 throws IOException { 765 766 int numFields = getNumTIFFFields(); 767 stream.writeShort(numFields); 768 769 long nextSpace = stream.getStreamPosition() + 12*numFields + 4; 770 771 Iterator<TIFFField> iter = iterator(); 772 while (iter.hasNext()) { 773 TIFFField f = iter.next(); 774 775 TIFFTag tag = f.getTag(); 776 777 int type = f.getType(); 778 int count = f.getCount(); 779 780 // Deal with unknown tags 781 if (type == 0) { 782 type = TIFFTag.TIFF_UNDEFINED; 783 } 784 int size = count*TIFFTag.getSizeOfType(type); 785 786 if (type == TIFFTag.TIFF_ASCII) { 787 int chars = 0; 788 for (int i = 0; i < count; i++) { 789 chars += f.getAsString(i).length() + 1; 790 } 791 count = chars; 792 size = count; 793 } 794 795 int tagNumber = f.getTagNumber(); 796 stream.writeShort(tagNumber); 797 stream.writeShort(type); 798 stream.writeInt(count); 799 800 // Write a dummy value to fill space 801 stream.writeInt(0); 802 stream.mark(); // Mark beginning of next field 803 stream.skipBytes(-4); 804 805 long pos; 806 807 if (size > 4 || tag.isIFDPointer()) { 808 // Ensure IFD or value is written on a word boundary 809 nextSpace = (nextSpace + 3) & ~0x3; 810 811 stream.writeInt((int)nextSpace); 812 stream.seek(nextSpace); 813 pos = nextSpace; 814 815 if (tag.isIFDPointer() && f.hasDirectory()) { 816 TIFFIFD subIFD = getDirectoryAsIFD(f.getDirectory()); 817 subIFD.writeToStream(stream); 818 nextSpace = subIFD.lastPosition; 819 } else { 820 writeTIFFFieldToStream(f, stream); 821 nextSpace = stream.getStreamPosition(); 822 } 823 } else { 824 pos = stream.getStreamPosition(); 825 writeTIFFFieldToStream(f, stream); 826 } 827 828 // If we are writing the data for the 829 // StripByteCounts, TileByteCounts, StripOffsets, 830 // TileOffsets, JPEGInterchangeFormat, or 831 // JPEGInterchangeFormatLength fields, record the current stream 832 // position for backpatching 833 if (tagNumber == 834 BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS || 835 tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS || 836 tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) { 837 this.stripOrTileByteCountsPosition = pos; 838 } else if (tagNumber == 839 BaselineTIFFTagSet.TAG_STRIP_OFFSETS || 840 tagNumber == 841 BaselineTIFFTagSet.TAG_TILE_OFFSETS || 842 tagNumber == 843 BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) { 844 this.stripOrTileOffsetsPosition = pos; 845 } 846 847 stream.reset(); // Go to marked position of next field 848 } 849 850 this.lastPosition = nextSpace; 851 } 852 853 public long getStripOrTileByteCountsPosition() { 854 return stripOrTileByteCountsPosition; 855 } 856 857 public long getStripOrTileOffsetsPosition() { 858 return stripOrTileOffsetsPosition; 859 } 860 861 public long getLastPosition() { 862 return lastPosition; 863 } 864 865 void setPositions(long stripOrTileOffsetsPosition, 866 long stripOrTileByteCountsPosition, 867 long lastPosition) { 868 this.stripOrTileOffsetsPosition = stripOrTileOffsetsPosition; 869 this.stripOrTileByteCountsPosition = stripOrTileByteCountsPosition; 870 this.lastPosition = lastPosition; 871 } 872 873 /** 874 * Returns a {@code TIFFIFD} wherein all fields from the 875 * {@code BaselineTIFFTagSet} are copied by value and all other 876 * fields copied by reference. 877 */ 878 public TIFFIFD getShallowClone() { 879 // Get the baseline TagSet. 880 TIFFTagSet baselineTagSet = BaselineTIFFTagSet.getInstance(); 881 882 // If the baseline TagSet is not included just return. 883 List<TIFFTagSet> tagSetList = getTagSetList(); 884 if(!tagSetList.contains(baselineTagSet)) { 885 return this; 886 } 887 888 // Create a new object. 889 TIFFIFD shallowClone = new TIFFIFD(tagSetList, getParentTag()); 890 891 // Get the tag numbers in the baseline set. 892 Set<Integer> baselineTagNumbers = baselineTagSet.getTagNumbers(); 893 894 // Iterate over the fields in this IFD. 895 Iterator<TIFFField> fields = iterator(); 896 while(fields.hasNext()) { 897 // Get the next field. 898 TIFFField field = fields.next(); 899 900 // Get its tag number. 901 Integer tagNumber = Integer.valueOf(field.getTagNumber()); 902 903 // Branch based on membership in baseline set. 904 TIFFField fieldClone; 905 if(baselineTagNumbers.contains(tagNumber)) { 906 // Copy by value. 907 Object fieldData = field.getData(); 908 909 int fieldType = field.getType(); 910 911 try { 912 switch (fieldType) { 913 case TIFFTag.TIFF_BYTE: 914 case TIFFTag.TIFF_SBYTE: 915 case TIFFTag.TIFF_UNDEFINED: 916 fieldData = ((byte[])fieldData).clone(); 917 break; 918 case TIFFTag.TIFF_ASCII: 919 fieldData = ((String[])fieldData).clone(); 920 break; 921 case TIFFTag.TIFF_SHORT: 922 fieldData = ((char[])fieldData).clone(); 923 break; 924 case TIFFTag.TIFF_LONG: 925 case TIFFTag.TIFF_IFD_POINTER: 926 fieldData = ((long[])fieldData).clone(); 927 break; 928 case TIFFTag.TIFF_RATIONAL: 929 fieldData = ((long[][])fieldData).clone(); 930 break; 931 case TIFFTag.TIFF_SSHORT: 932 fieldData = ((short[])fieldData).clone(); 933 break; 934 case TIFFTag.TIFF_SLONG: 935 fieldData = ((int[])fieldData).clone(); 936 break; 937 case TIFFTag.TIFF_SRATIONAL: 938 fieldData = ((int[][])fieldData).clone(); 939 break; 940 case TIFFTag.TIFF_FLOAT: 941 fieldData = ((float[])fieldData).clone(); 942 break; 943 case TIFFTag.TIFF_DOUBLE: 944 fieldData = ((double[])fieldData).clone(); 945 break; 946 default: 947 // Shouldn't happen but do nothing ... 948 } 949 } catch(Exception e) { 950 // Ignore it and copy by reference ... 951 } 952 953 fieldClone = new TIFFField(field.getTag(), fieldType, 954 field.getCount(), fieldData); 955 } else { 956 // Copy by reference. 957 fieldClone = field; 958 } 959 960 // Add the field to the clone. 961 shallowClone.addTIFFField(fieldClone); 962 } 963 964 // Set positions. 965 shallowClone.setPositions(stripOrTileOffsetsPosition, 966 stripOrTileByteCountsPosition, 967 lastPosition); 968 969 return shallowClone; 970 } 971 }