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 ignoreUnknownFields is true, 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 ignoreUnknownFields) throws IOException { 548 549 removeTIFFFields(); 550 551 long streamLength = stream.length(); 552 boolean haveStreamLength = streamLength != -1; 553 554 List<TIFFTagSet> tagSetList = getTagSetList(); 555 556 boolean ensureEssentialTags = false; 557 TIFFTagSet baselineTagSet = null; 558 if (isPrimaryIFD && ignoreUnknownFields 559 && !tagSetList.contains(BaselineTIFFTagSet.getInstance())) { 560 ensureEssentialTags = true; 561 initializeEssentialTags(); 562 baselineTagSet = BaselineTIFFTagSet.getInstance(); 563 } 564 565 List<Object> entries = new ArrayList<>(); 566 Object[] entryData = new Object[1]; // allocate once for later reuse. 567 568 // Read the IFD entries, loading the field values which are no more than 569 // four bytes long, and storing the 4-byte offsets for the others. 570 int numEntries = stream.readUnsignedShort(); 571 for (int i = 0; i < numEntries; i++) { 572 // Read tag number, value type, and value count. 573 int tagNumber = stream.readUnsignedShort(); 574 int type = stream.readUnsignedShort(); 575 int sizeOfType; 576 try { 577 sizeOfType = TIFFTag.getSizeOfType(type); 578 } catch (IllegalArgumentException ignored) { 579 // Continue with the next IFD entry. 580 stream.skipBytes(4); 581 continue; 582 } 583 long longCount = stream.readUnsignedInt(); 584 585 // Get the associated TIFFTag. 586 TIFFTag tag = getTag(tagNumber, tagSetList); 587 588 if (tag == null && ensureEssentialTags 589 && essentialTags.contains(tagNumber)) { 590 tag = baselineTagSet.getTag(tagNumber); 591 } 592 593 // Ignore unknown fields, fields with unknown type, and fields 594 // with count out of int range. 595 if((tag == null && ignoreUnknownFields) 596 || (tag != null && !tag.isDataTypeOK(type)) 597 || longCount > Integer.MAX_VALUE) { 598 // Skip the value/offset so as to leave the stream 599 // position at the start of the next IFD entry. 600 stream.skipBytes(4); 601 602 // Continue with the next IFD entry. 603 continue; 604 } 605 606 int count = (int)longCount; 607 608 if (tag == null) { 609 tag = new TIFFTag(TIFFTag.UNKNOWN_TAG_NAME, tagNumber, 610 1 << type, count); 611 } else { 612 int expectedCount = tag.getCount(); 613 if (expectedCount > 0) { 614 // If the tag count is positive then the tag defines a 615 // specific, fixed count that the field must match. 616 if (count != expectedCount) { 617 throw new IIOException("Unexpected count " 618 + count + " for " + tag.getName() + " field"); 619 } 620 } else if (type == TIFFTag.TIFF_ASCII) { 621 // Clamp the size of ASCII fields of unspecified length 622 // to a maximum value. 623 int asciiSize = TIFFTag.getSizeOfType(TIFFTag.TIFF_ASCII); 624 if (count*asciiSize > MAX_ASCII_SIZE) { 625 count = (int)(MAX_ASCII_SIZE/asciiSize); 626 } 627 } 628 } 629 630 long longSize = longCount*sizeOfType; 631 if (longSize > Integer.MAX_VALUE) { 632 // Continue with the next IFD entry. 633 stream.skipBytes(4); 634 continue; 635 } 636 int size = (int)longSize; 637 638 if (size > 4 || tag.isIFDPointer()) { 639 // The IFD entry value is a pointer to the actual field value. 640 long offset = stream.readUnsignedInt(); 641 642 // Check whether the the field value is within the stream. 643 if (haveStreamLength && offset + size > streamLength) { 644 continue; 645 } 646 647 // Add a TIFFIFDEntry as a placeholder. This avoids a mark, 648 // seek to the data, and a reset. 649 entries.add(new TIFFIFDEntry(tag, type, count, offset)); 650 } else { 651 // The IFD entry value is the actual field value of no more than 652 // four bytes. 653 Object obj = null; 654 try { 655 // Read the field value and update the count. 656 count = readFieldValue(stream, type, count, entryData); 657 obj = entryData[0]; 658 } catch (EOFException eofe) { 659 // The TIFF 6.0 fields have tag numbers less than or equal 660 // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright). 661 // If there is an error reading a baseline tag, then re-throw 662 // the exception and fail; otherwise continue with the next 663 // field. 664 if (BaselineTIFFTagSet.getInstance().getTag(tagNumber) == null) { 665 throw eofe; 666 } 667 } 668 669 // If the field value is smaller than four bytes then skip 670 // the remaining, unused bytes. 671 if (size < 4) { 672 stream.skipBytes(4 - size); 673 } 674 675 // Add the populated TIFFField to the list of entries. 676 entries.add(new TIFFField(tag, type, count, obj)); 677 } 678 } 679 680 // After reading the IFD entries the stream is positioned at an unsigned 681 // four byte integer containing either the offset of the next IFD or 682 // zero if this is the last IFD. 683 long nextIFDOffset = stream.getStreamPosition(); 684 685 Object[] fieldData = new Object[1]; 686 for (Object entry : entries) { 687 if (entry instanceof TIFFField) { 688 // Add the populated field directly. 689 addTIFFField((TIFFField)entry); 690 } else { 691 TIFFIFDEntry e = (TIFFIFDEntry)entry; 692 TIFFTag tag = e.tag; 693 int tagNumber = tag.getNumber(); 694 int type = e.type; 695 int count = e.count; 696 697 stream.seek(e.offset); 698 699 if (tag.isIFDPointer()) { 700 List<TIFFTagSet> tagSets = new ArrayList<TIFFTagSet>(1); 701 tagSets.add(tag.getTagSet()); 702 TIFFIFD subIFD = new TIFFIFD(tagSets); 703 704 subIFD.initialize(stream, false, ignoreUnknownFields); 705 TIFFField f = new TIFFField(tag, type, e.offset, subIFD); 706 addTIFFField(f); 707 } else { 708 if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS 709 || tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS 710 || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) { 711 this.stripOrTileByteCountsPosition 712 = stream.getStreamPosition(); 713 } else if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_OFFSETS 714 || tagNumber == BaselineTIFFTagSet.TAG_TILE_OFFSETS 715 || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) { 716 this.stripOrTileOffsetsPosition 717 = stream.getStreamPosition(); 718 } 719 720 Object obj = null; 721 try { 722 count = readFieldValue(stream, type, count, fieldData); 723 obj = fieldData[0]; 724 } catch (EOFException eofe) { 725 // The TIFF 6.0 fields have tag numbers less than or equal 726 // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright). 727 // If there is an error reading a baseline tag, then re-throw 728 // the exception and fail; otherwise continue with the next 729 // field. 730 if (BaselineTIFFTagSet.getInstance().getTag(tagNumber) == null) { 731 throw eofe; 732 } 733 } 734 735 if (obj == null) { 736 continue; 737 } 738 739 TIFFField f = new TIFFField(tag, type, count, obj); 740 addTIFFField(f); 741 } 742 } 743 } 744 745 if(isPrimaryIFD && haveStreamLength) { 746 checkFieldOffsets(streamLength); 747 } 748 749 stream.seek(nextIFDOffset); 750 this.lastPosition = stream.getStreamPosition(); 751 } 752 753 public void writeToStream(ImageOutputStream stream) 754 throws IOException { 755 756 int numFields = getNumTIFFFields(); 757 stream.writeShort(numFields); 758 759 long nextSpace = stream.getStreamPosition() + 12*numFields + 4; 760 761 Iterator<TIFFField> iter = iterator(); 762 while (iter.hasNext()) { 763 TIFFField f = iter.next(); 764 765 TIFFTag tag = f.getTag(); 766 767 int type = f.getType(); 768 int count = f.getCount(); 769 770 // Deal with unknown tags 771 if (type == 0) { 772 type = TIFFTag.TIFF_UNDEFINED; 773 } 774 int size = count*TIFFTag.getSizeOfType(type); 775 776 if (type == TIFFTag.TIFF_ASCII) { 777 int chars = 0; 778 for (int i = 0; i < count; i++) { 779 chars += f.getAsString(i).length() + 1; 780 } 781 count = chars; 782 size = count; 783 } 784 785 int tagNumber = f.getTagNumber(); 786 stream.writeShort(tagNumber); 787 stream.writeShort(type); 788 stream.writeInt(count); 789 790 // Write a dummy value to fill space 791 stream.writeInt(0); 792 stream.mark(); // Mark beginning of next field 793 stream.skipBytes(-4); 794 795 long pos; 796 797 if (size > 4 || tag.isIFDPointer()) { 798 // Ensure IFD or value is written on a word boundary 799 nextSpace = (nextSpace + 3) & ~0x3; 800 801 stream.writeInt((int)nextSpace); 802 stream.seek(nextSpace); 803 pos = nextSpace; 804 805 if (tag.isIFDPointer() && f.hasDirectory()) { 806 TIFFIFD subIFD = getDirectoryAsIFD(f.getDirectory()); 807 subIFD.writeToStream(stream); 808 nextSpace = subIFD.lastPosition; 809 } else { 810 writeTIFFFieldToStream(f, stream); 811 nextSpace = stream.getStreamPosition(); 812 } 813 } else { 814 pos = stream.getStreamPosition(); 815 writeTIFFFieldToStream(f, stream); 816 } 817 818 // If we are writing the data for the 819 // StripByteCounts, TileByteCounts, StripOffsets, 820 // TileOffsets, JPEGInterchangeFormat, or 821 // JPEGInterchangeFormatLength fields, record the current stream 822 // position for backpatching 823 if (tagNumber == 824 BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS || 825 tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS || 826 tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) { 827 this.stripOrTileByteCountsPosition = pos; 828 } else if (tagNumber == 829 BaselineTIFFTagSet.TAG_STRIP_OFFSETS || 830 tagNumber == 831 BaselineTIFFTagSet.TAG_TILE_OFFSETS || 832 tagNumber == 833 BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) { 834 this.stripOrTileOffsetsPosition = pos; 835 } 836 837 stream.reset(); // Go to marked position of next field 838 } 839 840 this.lastPosition = nextSpace; 841 } 842 843 public long getStripOrTileByteCountsPosition() { 844 return stripOrTileByteCountsPosition; 845 } 846 847 public long getStripOrTileOffsetsPosition() { 848 return stripOrTileOffsetsPosition; 849 } 850 851 public long getLastPosition() { 852 return lastPosition; 853 } 854 855 void setPositions(long stripOrTileOffsetsPosition, 856 long stripOrTileByteCountsPosition, 857 long lastPosition) { 858 this.stripOrTileOffsetsPosition = stripOrTileOffsetsPosition; 859 this.stripOrTileByteCountsPosition = stripOrTileByteCountsPosition; 860 this.lastPosition = lastPosition; 861 } 862 863 /** 864 * Returns a {@code TIFFIFD} wherein all fields from the 865 * {@code BaselineTIFFTagSet} are copied by value and all other 866 * fields copied by reference. 867 */ 868 public TIFFIFD getShallowClone() { 869 // Get the baseline TagSet. 870 TIFFTagSet baselineTagSet = BaselineTIFFTagSet.getInstance(); 871 872 // If the baseline TagSet is not included just return. 873 List<TIFFTagSet> tagSetList = getTagSetList(); 874 if(!tagSetList.contains(baselineTagSet)) { 875 return this; 876 } 877 878 // Create a new object. 879 TIFFIFD shallowClone = new TIFFIFD(tagSetList, getParentTag()); 880 881 // Get the tag numbers in the baseline set. 882 Set<Integer> baselineTagNumbers = baselineTagSet.getTagNumbers(); 883 884 // Iterate over the fields in this IFD. 885 Iterator<TIFFField> fields = iterator(); 886 while(fields.hasNext()) { 887 // Get the next field. 888 TIFFField field = fields.next(); 889 890 // Get its tag number. 891 Integer tagNumber = Integer.valueOf(field.getTagNumber()); 892 893 // Branch based on membership in baseline set. 894 TIFFField fieldClone; 895 if(baselineTagNumbers.contains(tagNumber)) { 896 // Copy by value. 897 Object fieldData = field.getData(); 898 899 int fieldType = field.getType(); 900 901 try { 902 switch (fieldType) { 903 case TIFFTag.TIFF_BYTE: 904 case TIFFTag.TIFF_SBYTE: 905 case TIFFTag.TIFF_UNDEFINED: 906 fieldData = ((byte[])fieldData).clone(); 907 break; 908 case TIFFTag.TIFF_ASCII: 909 fieldData = ((String[])fieldData).clone(); 910 break; 911 case TIFFTag.TIFF_SHORT: 912 fieldData = ((char[])fieldData).clone(); 913 break; 914 case TIFFTag.TIFF_LONG: 915 case TIFFTag.TIFF_IFD_POINTER: 916 fieldData = ((long[])fieldData).clone(); 917 break; 918 case TIFFTag.TIFF_RATIONAL: 919 fieldData = ((long[][])fieldData).clone(); 920 break; 921 case TIFFTag.TIFF_SSHORT: 922 fieldData = ((short[])fieldData).clone(); 923 break; 924 case TIFFTag.TIFF_SLONG: 925 fieldData = ((int[])fieldData).clone(); 926 break; 927 case TIFFTag.TIFF_SRATIONAL: 928 fieldData = ((int[][])fieldData).clone(); 929 break; 930 case TIFFTag.TIFF_FLOAT: 931 fieldData = ((float[])fieldData).clone(); 932 break; 933 case TIFFTag.TIFF_DOUBLE: 934 fieldData = ((double[])fieldData).clone(); 935 break; 936 default: 937 // Shouldn't happen but do nothing ... 938 } 939 } catch(Exception e) { 940 // Ignore it and copy by reference ... 941 } 942 943 fieldClone = new TIFFField(field.getTag(), fieldType, 944 field.getCount(), fieldData); 945 } else { 946 // Copy by reference. 947 fieldClone = field; 948 } 949 950 // Add the field to the clone. 951 shallowClone.addTIFFField(fieldClone); 952 } 953 954 // Set positions. 955 shallowClone.setPositions(stripOrTileOffsetsPosition, 956 stripOrTileByteCountsPosition, 957 lastPosition); 958 959 return shallowClone; 960 } 961 }