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 }