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 }