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 */ 25package com.sun.imageio.plugins.tiff; 26 27import java.io.EOFException; 28import java.io.IOException; 29import java.nio.charset.StandardCharsets; 30import java.util.ArrayList; 31import java.util.Arrays; 32import java.util.Iterator; 33import java.util.List; 34import java.util.Set; 35import javax.imageio.IIOException; 36import javax.imageio.stream.ImageInputStream; 37import javax.imageio.stream.ImageOutputStream; 38import javax.imageio.plugins.tiff.BaselineTIFFTagSet; 39import javax.imageio.plugins.tiff.TIFFDirectory; 40import javax.imageio.plugins.tiff.TIFFField; 41import javax.imageio.plugins.tiff.TIFFTag; 42import javax.imageio.plugins.tiff.TIFFTagSet; 43 44public 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 // Retrieve the value of a baseline field as a long. 418 // 419 private long getFieldAsLong(int tagNumber) { 420 TIFFField f = getTIFFField(tagNumber); 421 return f == null ? -1 : f.getAsLong(0); 422 } 423 424 // 425 // Retrieve the value of a baseline field as an int. 426 // 427 private int getFieldAsInt(int tagNumber) { 428 TIFFField f = getTIFFField(tagNumber); 429 return f == null ? -1 : f.getAsInt(0); 430 } 431 432 // 433 // Calculate the number of bytes in each strip or tile. This method 434 // is to be used if and only if no fields exist which provide this 435 // information. The parameter must be empty and if the method succeeds 436 // will contain a single element. 437 // 438 private boolean calculateByteCounts(int expectedSize, 439 List<TIFFField> byteCounts) { 440 if (!byteCounts.isEmpty()) { 441 throw new IllegalArgumentException("byteCounts is not empty"); 442 } 443 444 // must be interleaved 445 if (getFieldAsInt(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION) == 446 BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) { 447 return false; 448 } 449 450 // must be uncompressed 451 if (getFieldAsInt(BaselineTIFFTagSet.TAG_COMPRESSION) != 452 BaselineTIFFTagSet.COMPRESSION_NONE) { 453 return false; 454 } 455 456 // must have image dimensions 457 long w = getFieldAsLong(BaselineTIFFTagSet.TAG_IMAGE_WIDTH); 458 if (w < 0) { 459 return false; 460 } 461 long h = getFieldAsLong(BaselineTIFFTagSet.TAG_IMAGE_LENGTH); 462 if (h < 0) { 463 return false; 464 } 465 466 long tw = getFieldAsLong(BaselineTIFFTagSet.TAG_TILE_WIDTH); 467 if (tw < 0) { 468 tw = w; 469 } 470 long th = getFieldAsLong(BaselineTIFFTagSet.TAG_TILE_LENGTH); 471 if (th < 0) { 472 th = getFieldAsLong(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP); 473 if (th < 0) { 474 th = h; 475 } 476 } 477 478 int[] bitsPerSample = null; 479 TIFFField f = getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE); 480 if (f != null) { 481 bitsPerSample = f.getAsInts(); 482 } else { 483 int samplesPerPixel = 484 getFieldAsInt(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL); 485 if (samplesPerPixel < 0) { 486 samplesPerPixel = 1; 487 } 488 bitsPerSample = new int[samplesPerPixel]; 489 Arrays.fill(bitsPerSample, 8); 490 } 491 492 int bitsPerPixel = 0; 493 for (int bps : bitsPerSample) { 494 bitsPerPixel += bps; 495 } 496 497 int bytesPerRow = (int)(tw*bitsPerPixel + 7)/8; 498 int bytesPerPacket = (int)th*bytesPerRow; 499 500 long nx = (w + tw - 1)/tw; 501 long ny = (h + th - 1)/th; 502 503 if (nx*ny != expectedSize) { 504 return false; 505 } 506 507 boolean isTiled = 508 getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS) != null; 509 510 int tagNumber; 511 if (isTiled) { 512 tagNumber = BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS; 513 } else { 514 tagNumber = BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS; 515 } 516 517 TIFFTag t = BaselineTIFFTagSet.getInstance().getTag(tagNumber); 518 f = getTIFFField(tagNumber); 519 if (f != null) { 520 removeTIFFField(tagNumber); 521 } 522 523 int numPackets = (int)(nx*ny); 524 long[] packetByteCounts = new long[numPackets]; 525 Arrays.fill(packetByteCounts, bytesPerPacket); 526 527 // if the strip or tile width does not exceed the image width and the 528 // image height is not a multiple of the strip or tile height, then 529 // truncate the estimate of the byte count of the last strip to avoid 530 // reading past the end of the data 531 if (tw <= w && h % th != 0) { 532 int numRowsInLastStrip = (int)(h - (ny - 1)*th); 533 packetByteCounts[numPackets - 1] = numRowsInLastStrip*bytesPerRow; 534 } 535 536 f = new TIFFField(t, TIFFTag.TIFF_LONG, numPackets, packetByteCounts); 537 addTIFFField(f); 538 byteCounts.add(f); 539 540 return true; 541 } 542 543 // 544 // Verify that data pointed to outside of the IFD itself are within the 545 // stream. To be called after all fields have been read and populated. 546 // 547 private void checkFieldOffsets(long streamLength) throws IIOException { 548 if (streamLength < 0) { 549 return; 550 } 551 552 // StripOffsets 553 List<TIFFField> offsets = new ArrayList<>(); 554 TIFFField f = getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS); 555 int count = 0; 556 if (f != null) { 557 count = f.getCount(); 558 offsets.add(f); 559 } 560 561 // TileOffsets 562 f = getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS); 563 if (f != null) { 564 int sz = offsets.size(); 565 int newCount = f.getCount(); 566 if (sz > 0 && newCount != count) { 567 throw new IIOException 568 ("StripOffsets count != TileOffsets count"); 569 } 570 571 if (sz == 0) { 572 count = newCount; 573 } 574 offsets.add(f); 575 } 576 577 List<TIFFField> byteCounts = new ArrayList<>(); 578 if (offsets.size() > 0) { 579 // StripByteCounts 580 f = getTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS); 581 if (f != null) { 582 if (f.getCount() != count) { 583 throw new IIOException 584 ("StripByteCounts count != number of offsets"); 585 } 586 byteCounts.add(f); 587 } 588 589 // TileByteCounts 590 f = getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS); 591 if (f != null) { 592 if (f.getCount() != count) { 593 throw new IIOException 594 ("TileByteCounts count != number of offsets"); 595 } 596 byteCounts.add(f); 597 } 598 599 if (byteCounts.size() > 0) { 600 for (TIFFField offset : offsets) { 601 for (TIFFField byteCount : byteCounts) { 602 for (int i = 0; i < count; i++) { 603 long dataOffset = offset.getAsLong(i); 604 long dataByteCount = byteCount.getAsLong(i); 605 if (dataOffset + dataByteCount > streamLength) { 606 throw new IIOException 607 ("Data segment out of stream"); 608 } 609 } 610 } 611 } 612 } 613 } 614 615 // JPEGInterchangeFormat and JPEGInterchangeFormatLength 616 TIFFField jpegOffset = 617 getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT); 618 if (jpegOffset != null) { 619 TIFFField jpegLength = 620 getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 621 if (jpegLength != null) { 622 if (jpegOffset.getAsLong(0) + jpegLength.getAsLong(0) 623 > streamLength) { 624 throw new IIOException 625 ("JPEGInterchangeFormat data out of stream"); 626 } 627 } 628 } 629 630 // Ensure there is at least a data pointer for JPEG interchange format or 631 // both data offsets and byte counts for other compression types. 632 if (jpegOffset == null 633 && (offsets.size() == 0 || byteCounts.size() == 0)) { 634 boolean throwException = true; 635 if (offsets.size() != 0 && byteCounts.size() == 0) { 636 // Attempt to calculate missing byte counts 637 int expectedSize = offsets.get(0).getCount(); 638 throwException = 639 !calculateByteCounts(expectedSize, byteCounts); 640 } 641 if (throwException) { 642 throw new IIOException 643 ("Insufficient data offsets or byte counts"); 644 } 645 } 646 647 // JPEGQTables - one 64-byte table for each offset. 648 f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_Q_TABLES); 649 if (f != null) { 650 long[] tableOffsets = f.getAsLongs(); 651 for (long off : tableOffsets) { 652 if (off + 64 > streamLength) { 653 throw new IIOException("JPEGQTables data out of stream"); 654 } 655 } 656 } 657 658 // JPEGDCTables 659 f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_DC_TABLES); 660 if (f != null) { 661 long[] tableOffsets = f.getAsLongs(); 662 for (long off : tableOffsets) { 663 if (off + 16 > streamLength) { 664 throw new IIOException("JPEGDCTables data out of stream"); 665 } 666 } 667 } 668 669 // JPEGACTables 670 f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_AC_TABLES); 671 if (f != null) { 672 long[] tableOffsets = f.getAsLongs(); 673 for (long off : tableOffsets) { 674 if (off + 16 > streamLength) { 675 throw new IIOException("JPEGACTables data out of stream"); 676 } 677 } 678 } 679 } 680 681 // Stream position initially at beginning, left at end 682 // if readUnknownTags is false, do not load fields for which 683 // a tag cannot be found in an allowed TagSet. 684 public void initialize(ImageInputStream stream, boolean isPrimaryIFD, 685 boolean ignoreMetadata, boolean readUnknownTags) throws IOException { 686 687 removeTIFFFields(); 688 689 long streamLength = stream.length(); 690 boolean haveStreamLength = streamLength != -1; 691 692 List<TIFFTagSet> tagSetList = getTagSetList(); 693 694 // Configure essential tag variables if this is the primary IFD and 695 // either all metadata are being ignored, or metadata are not being 696 // ignored but both unknown tags are being ignored and the tag set 697 // list does not contain the baseline tags. 698 boolean ensureEssentialTags = false; 699 TIFFTagSet baselineTagSet = null; 700 if (isPrimaryIFD && 701 (ignoreMetadata || 702 (!readUnknownTags && 703 !tagSetList.contains(BaselineTIFFTagSet.getInstance())))) { 704 ensureEssentialTags = true; 705 initializeEssentialTags(); 706 baselineTagSet = BaselineTIFFTagSet.getInstance(); 707 } 708 709 List<Object> entries = new ArrayList<>(); 710 Object[] entryData = new Object[1]; // allocate once for later reuse. 711 712 // Read the IFD entries, loading the field values which are no more than 713 // four bytes long, and storing the 4-byte offsets for the others. 714 int numEntries = stream.readUnsignedShort(); 715 for (int i = 0; i < numEntries; i++) { 716 // Read tag number, value type, and value count. 717 int tagNumber = stream.readUnsignedShort(); 718 int type = stream.readUnsignedShort(); 719 int sizeOfType; 720 try { 721 sizeOfType = TIFFTag.getSizeOfType(type); 722 } catch (IllegalArgumentException ignored) { 723 // Continue with the next IFD entry. 724 stream.skipBytes(4); 725 continue; 726 } 727 long longCount = stream.readUnsignedInt(); 728 729 // Get the associated TIFFTag. 730 TIFFTag tag = getTag(tagNumber, tagSetList); 731 732 if (tag == null && ensureEssentialTags 733 && essentialTags.contains(tagNumber)) { 734 tag = baselineTagSet.getTag(tagNumber); 735 } 736 737 // Ignore non-essential fields, unknown fields unless forcibly 738 // being read, fields with unknown type, and fields 739 // with count out of int range. 740 if((ignoreMetadata && 741 (!ensureEssentialTags || !essentialTags.contains(tagNumber))) 742 || (tag == null && !readUnknownTags) 743 || (tag != null && !tag.isDataTypeOK(type)) 744 || longCount > Integer.MAX_VALUE) { 745 // Skip the value/offset so as to leave the stream 746 // position at the start of the next IFD entry. 747 stream.skipBytes(4); 748 749 // Continue with the next IFD entry. 750 continue; 751 } 752 753 int count = (int)longCount; 754 755 if (tag == null) { 756 tag = new TIFFTag(TIFFTag.UNKNOWN_TAG_NAME, tagNumber, 757 1 << type, count); 758 } else { 759 int expectedCount = tag.getCount(); 760 if (expectedCount > 0) { 761 // If the tag count is positive then the tag defines a 762 // specific, fixed count that the field must match. 763 if (count != expectedCount) { 764 throw new IIOException("Unexpected count " 765 + count + " for " + tag.getName() + " field"); 766 } 767 } else if (type == TIFFTag.TIFF_ASCII) { 768 // Clamp the size of ASCII fields of unspecified length 769 // to a maximum value. 770 int asciiSize = TIFFTag.getSizeOfType(TIFFTag.TIFF_ASCII); 771 if (count*asciiSize > MAX_ASCII_SIZE) { 772 count = (int)(MAX_ASCII_SIZE/asciiSize); 773 } 774 } 775 } 776 777 long longSize = longCount*sizeOfType; 778 if (longSize > Integer.MAX_VALUE) { 779 // Continue with the next IFD entry. 780 stream.skipBytes(4); 781 continue; 782 } 783 int size = (int)longSize; 784 785 if (size > 4 || tag.isIFDPointer()) { 786 // The IFD entry value is a pointer to the actual field value. 787 long offset = stream.readUnsignedInt(); 788 789 // Check whether the the field value is within the stream. 790 if (haveStreamLength && offset + size > streamLength) { 791 continue; 792 } 793 794 // Add a TIFFIFDEntry as a placeholder. This avoids a mark, 795 // seek to the data, and a reset. 796 entries.add(new TIFFIFDEntry(tag, type, count, offset)); 797 } else { 798 // The IFD entry value is the actual field value of no more than 799 // four bytes. 800 Object obj = null; 801 try { 802 // Read the field value and update the count. 803 count = readFieldValue(stream, type, count, entryData); 804 obj = entryData[0]; 805 } catch (EOFException eofe) { 806 // The TIFF 6.0 fields have tag numbers less than or equal 807 // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright). 808 // If there is an error reading a baseline tag, then re-throw 809 // the exception and fail; otherwise continue with the next 810 // field. 811 if (BaselineTIFFTagSet.getInstance().getTag(tagNumber) == null) { 812 throw eofe; 813 } 814 } 815 816 // If the field value is smaller than four bytes then skip 817 // the remaining, unused bytes. 818 if (size < 4) { 819 stream.skipBytes(4 - size); 820 } 821 822 // Add the populated TIFFField to the list of entries. 823 entries.add(new TIFFField(tag, type, count, obj)); 824 } 825 } 826 827 // After reading the IFD entries the stream is positioned at an unsigned 828 // four byte integer containing either the offset of the next IFD or 829 // zero if this is the last IFD. 830 long nextIFDOffset = stream.getStreamPosition(); 831 832 Object[] fieldData = new Object[1]; 833 for (Object entry : entries) { 834 if (entry instanceof TIFFField) { 835 // Add the populated field directly. 836 addTIFFField((TIFFField)entry); 837 } else { 838 TIFFIFDEntry e = (TIFFIFDEntry)entry; 839 TIFFTag tag = e.tag; 840 int tagNumber = tag.getNumber(); 841 int type = e.type; 842 int count = e.count; 843 844 stream.seek(e.offset); 845 846 if (tag.isIFDPointer()) { 847 List<TIFFTagSet> tagSets = new ArrayList<TIFFTagSet>(1); 848 tagSets.add(tag.getTagSet()); 849 TIFFIFD subIFD = new TIFFIFD(tagSets); 850 851 subIFD.initialize(stream, false, ignoreMetadata, 852 readUnknownTags); 853 TIFFField f = new TIFFField(tag, type, e.offset, subIFD); 854 addTIFFField(f); 855 } else { 856 if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS 857 || tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS 858 || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) { 859 this.stripOrTileByteCountsPosition 860 = stream.getStreamPosition(); 861 } else if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_OFFSETS 862 || tagNumber == BaselineTIFFTagSet.TAG_TILE_OFFSETS 863 || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) { 864 this.stripOrTileOffsetsPosition 865 = stream.getStreamPosition(); 866 } 867 868 Object obj = null; 869 try { 870 count = readFieldValue(stream, type, count, fieldData); 871 obj = fieldData[0]; 872 } catch (EOFException eofe) { 873 // The TIFF 6.0 fields have tag numbers less than or equal 874 // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright). 875 // If there is an error reading a baseline tag, then re-throw 876 // the exception and fail; otherwise continue with the next 877 // field. 878 if (BaselineTIFFTagSet.getInstance().getTag(tagNumber) == null) { 879 throw eofe; 880 } 881 } 882 883 if (obj == null) { 884 continue; 885 } 886 887 TIFFField f = new TIFFField(tag, type, count, obj); 888 addTIFFField(f); 889 } 890 } 891 } 892 893 if(isPrimaryIFD && haveStreamLength) { 894 checkFieldOffsets(streamLength); 895 } 896 897 stream.seek(nextIFDOffset); 898 this.lastPosition = stream.getStreamPosition(); 899 } 900 901 public void writeToStream(ImageOutputStream stream) 902 throws IOException { 903 904 int numFields = getNumTIFFFields(); 905 stream.writeShort(numFields); 906 907 long nextSpace = stream.getStreamPosition() + 12*numFields + 4; 908 909 Iterator<TIFFField> iter = iterator(); 910 while (iter.hasNext()) { 911 TIFFField f = iter.next(); 912 913 TIFFTag tag = f.getTag(); 914 915 int type = f.getType(); 916 int count = f.getCount(); 917 918 // Deal with unknown tags 919 if (type == 0) { 920 type = TIFFTag.TIFF_UNDEFINED; 921 } 922 int size = count*TIFFTag.getSizeOfType(type); 923 924 if (type == TIFFTag.TIFF_ASCII) { 925 int chars = 0; 926 for (int i = 0; i < count; i++) { 927 chars += f.getAsString(i).length() + 1; 928 } 929 count = chars; 930 size = count; 931 } 932 933 int tagNumber = f.getTagNumber(); 934 stream.writeShort(tagNumber); 935 stream.writeShort(type); 936 stream.writeInt(count); 937 938 // Write a dummy value to fill space 939 stream.writeInt(0); 940 stream.mark(); // Mark beginning of next field 941 stream.skipBytes(-4); 942 943 long pos; 944 945 if (size > 4 || tag.isIFDPointer()) { 946 // Ensure IFD or value is written on a word boundary 947 nextSpace = (nextSpace + 3) & ~0x3; 948 949 stream.writeInt((int)nextSpace); 950 stream.seek(nextSpace); 951 pos = nextSpace; 952 953 if (tag.isIFDPointer() && f.hasDirectory()) { 954 TIFFIFD subIFD = getDirectoryAsIFD(f.getDirectory()); 955 subIFD.writeToStream(stream); 956 nextSpace = subIFD.lastPosition; 957 } else { 958 writeTIFFFieldToStream(f, stream); 959 nextSpace = stream.getStreamPosition(); 960 } 961 } else { 962 pos = stream.getStreamPosition(); 963 writeTIFFFieldToStream(f, stream); 964 } 965 966 // If we are writing the data for the 967 // StripByteCounts, TileByteCounts, StripOffsets, 968 // TileOffsets, JPEGInterchangeFormat, or 969 // JPEGInterchangeFormatLength fields, record the current stream 970 // position for backpatching 971 if (tagNumber == 972 BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS || 973 tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS || 974 tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) { 975 this.stripOrTileByteCountsPosition = pos; 976 } else if (tagNumber == 977 BaselineTIFFTagSet.TAG_STRIP_OFFSETS || 978 tagNumber == 979 BaselineTIFFTagSet.TAG_TILE_OFFSETS || 980 tagNumber == 981 BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) { 982 this.stripOrTileOffsetsPosition = pos; 983 } 984 985 stream.reset(); // Go to marked position of next field 986 } 987 988 this.lastPosition = nextSpace; 989 } 990 991 public long getStripOrTileByteCountsPosition() { 992 return stripOrTileByteCountsPosition; 993 } 994 995 public long getStripOrTileOffsetsPosition() { 996 return stripOrTileOffsetsPosition; 997 } 998 999 public long getLastPosition() { 1000 return lastPosition; 1001 } 1002 1003 void setPositions(long stripOrTileOffsetsPosition, 1004 long stripOrTileByteCountsPosition, 1005 long lastPosition) { 1006 this.stripOrTileOffsetsPosition = stripOrTileOffsetsPosition; 1007 this.stripOrTileByteCountsPosition = stripOrTileByteCountsPosition; 1008 this.lastPosition = lastPosition; 1009 } 1010 1011 /** 1012 * Returns a {@code TIFFIFD} wherein all fields from the 1013 * {@code BaselineTIFFTagSet} are copied by value and all other 1014 * fields copied by reference. 1015 */ 1016 public TIFFIFD getShallowClone() { 1017 // Get the baseline TagSet. 1018 TIFFTagSet baselineTagSet = BaselineTIFFTagSet.getInstance(); 1019 1020 // If the baseline TagSet is not included just return. 1021 List<TIFFTagSet> tagSetList = getTagSetList(); 1022 if(!tagSetList.contains(baselineTagSet)) { 1023 return this; 1024 } 1025 1026 // Create a new object. 1027 TIFFIFD shallowClone = new TIFFIFD(tagSetList, getParentTag()); 1028 1029 // Get the tag numbers in the baseline set. 1030 Set<Integer> baselineTagNumbers = baselineTagSet.getTagNumbers(); 1031 1032 // Iterate over the fields in this IFD. 1033 Iterator<TIFFField> fields = iterator(); 1034 while(fields.hasNext()) { 1035 // Get the next field. 1036 TIFFField field = fields.next(); 1037 1038 // Get its tag number. 1039 Integer tagNumber = Integer.valueOf(field.getTagNumber()); 1040 1041 // Branch based on membership in baseline set. 1042 TIFFField fieldClone; 1043 if(baselineTagNumbers.contains(tagNumber)) { 1044 // Copy by value. 1045 Object fieldData = field.getData(); 1046 1047 int fieldType = field.getType(); 1048 1049 try { 1050 switch (fieldType) { 1051 case TIFFTag.TIFF_BYTE: 1052 case TIFFTag.TIFF_SBYTE: 1053 case TIFFTag.TIFF_UNDEFINED: 1054 fieldData = ((byte[])fieldData).clone(); 1055 break; 1056 case TIFFTag.TIFF_ASCII: 1057 fieldData = ((String[])fieldData).clone(); 1058 break; 1059 case TIFFTag.TIFF_SHORT: 1060 fieldData = ((char[])fieldData).clone(); 1061 break; 1062 case TIFFTag.TIFF_LONG: 1063 case TIFFTag.TIFF_IFD_POINTER: 1064 fieldData = ((long[])fieldData).clone(); 1065 break; 1066 case TIFFTag.TIFF_RATIONAL: 1067 fieldData = ((long[][])fieldData).clone(); 1068 break; 1069 case TIFFTag.TIFF_SSHORT: 1070 fieldData = ((short[])fieldData).clone(); 1071 break; 1072 case TIFFTag.TIFF_SLONG: 1073 fieldData = ((int[])fieldData).clone(); 1074 break; 1075 case TIFFTag.TIFF_SRATIONAL: 1076 fieldData = ((int[][])fieldData).clone(); 1077 break; 1078 case TIFFTag.TIFF_FLOAT: 1079 fieldData = ((float[])fieldData).clone(); 1080 break; 1081 case TIFFTag.TIFF_DOUBLE: 1082 fieldData = ((double[])fieldData).clone(); 1083 break; 1084 default: 1085 // Shouldn't happen but do nothing ... 1086 } 1087 } catch(Exception e) { 1088 // Ignore it and copy by reference ... 1089 } 1090 1091 fieldClone = new TIFFField(field.getTag(), fieldType, 1092 field.getCount(), fieldData); 1093 } else { 1094 // Copy by reference. 1095 fieldClone = field; 1096 } 1097 1098 // Add the field to the clone. 1099 shallowClone.addTIFFField(fieldClone); 1100 } 1101 1102 // Set positions. 1103 shallowClone.setPositions(stripOrTileOffsetsPosition, 1104 stripOrTileByteCountsPosition, 1105 lastPosition); 1106 1107 return shallowClone; 1108 } 1109} 1110