jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java
changeset 34416 68c0d866db5d
child 34817 9b585ae27455
equal deleted inserted replaced
34415:098d54b4051d 34416:68c0d866db5d
       
     1 /*
       
     2  * Copyright (c) 2005, 2015, 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     public static TIFFTag getTag(int tagNumber, List<TIFFTagSet> tagSets) {
       
    53         Iterator<TIFFTagSet> iter = tagSets.iterator();
       
    54         while (iter.hasNext()) {
       
    55             TIFFTagSet tagSet = iter.next();
       
    56             TIFFTag tag = tagSet.getTag(tagNumber);
       
    57             if (tag != null) {
       
    58                 return tag;
       
    59             }
       
    60         }
       
    61 
       
    62         return null;
       
    63     }
       
    64 
       
    65     public static TIFFTag getTag(String tagName, List<TIFFTagSet> tagSets) {
       
    66         Iterator<TIFFTagSet> iter = tagSets.iterator();
       
    67         while (iter.hasNext()) {
       
    68             TIFFTagSet tagSet = iter.next();
       
    69             TIFFTag tag = tagSet.getTag(tagName);
       
    70             if (tag != null) {
       
    71                 return tag;
       
    72             }
       
    73         }
       
    74 
       
    75         return null;
       
    76     }
       
    77 
       
    78     private static void writeTIFFFieldToStream(TIFFField field,
       
    79                                                ImageOutputStream stream)
       
    80         throws IOException {
       
    81         int count = field.getCount();
       
    82         Object data = field.getData();
       
    83 
       
    84         switch (field.getType()) {
       
    85         case TIFFTag.TIFF_ASCII:
       
    86             for (int i = 0; i < count; i++) {
       
    87                 String s = ((String[])data)[i];
       
    88                 int length = s.length();
       
    89                 for (int j = 0; j < length; j++) {
       
    90                     stream.writeByte(s.charAt(j) & 0xff);
       
    91                 }
       
    92                 stream.writeByte(0);
       
    93             }
       
    94             break;
       
    95         case TIFFTag.TIFF_UNDEFINED:
       
    96         case TIFFTag.TIFF_BYTE:
       
    97         case TIFFTag.TIFF_SBYTE:
       
    98             stream.write((byte[])data);
       
    99             break;
       
   100         case TIFFTag.TIFF_SHORT:
       
   101             stream.writeChars((char[])data, 0, ((char[])data).length);
       
   102             break;
       
   103         case TIFFTag.TIFF_SSHORT:
       
   104             stream.writeShorts((short[])data, 0, ((short[])data).length);
       
   105             break;
       
   106         case TIFFTag.TIFF_SLONG:
       
   107             stream.writeInts((int[])data, 0, ((int[])data).length);
       
   108             break;
       
   109         case TIFFTag.TIFF_LONG:
       
   110             for (int i = 0; i < count; i++) {
       
   111                 stream.writeInt((int)(((long[])data)[i]));
       
   112             }
       
   113             break;
       
   114         case TIFFTag.TIFF_IFD_POINTER:
       
   115             stream.writeInt(0); // will need to be backpatched
       
   116             break;
       
   117         case TIFFTag.TIFF_FLOAT:
       
   118             stream.writeFloats((float[])data, 0, ((float[])data).length);
       
   119             break;
       
   120         case TIFFTag.TIFF_DOUBLE:
       
   121             stream.writeDoubles((double[])data, 0, ((double[])data).length);
       
   122             break;
       
   123         case TIFFTag.TIFF_SRATIONAL:
       
   124             for (int i = 0; i < count; i++) {
       
   125                 stream.writeInt(((int[][])data)[i][0]);
       
   126                 stream.writeInt(((int[][])data)[i][1]);
       
   127             }
       
   128             break;
       
   129         case TIFFTag.TIFF_RATIONAL:
       
   130             for (int i = 0; i < count; i++) {
       
   131                 long num = ((long[][])data)[i][0];
       
   132                 long den = ((long[][])data)[i][1];
       
   133                 stream.writeInt((int)num);
       
   134                 stream.writeInt((int)den);
       
   135             }
       
   136             break;
       
   137         default:
       
   138             // error
       
   139         }
       
   140     }
       
   141 
       
   142     public TIFFIFD(List<TIFFTagSet> tagSets, TIFFTag parentTag) {
       
   143         super(tagSets.toArray(new TIFFTagSet[tagSets.size()]),
       
   144               parentTag);
       
   145     }
       
   146 
       
   147     public TIFFIFD(List<TIFFTagSet> tagSets) {
       
   148         this(tagSets, null);
       
   149     }
       
   150 
       
   151     public List<TIFFTagSet> getTagSetList() {
       
   152         return Arrays.asList(getTagSets());
       
   153     }
       
   154 
       
   155     /**
       
   156      * Returns an <code>Iterator</code> over the TIFF fields. The
       
   157      * traversal is in the order of increasing tag number.
       
   158      */
       
   159     // Note: the sort is guaranteed for low fields by the use of an
       
   160     // array wherein the index corresponds to the tag number and for
       
   161     // the high fields by the use of a TreeMap with tag number keys.
       
   162     public Iterator<TIFFField> iterator() {
       
   163         return Arrays.asList(getTIFFFields()).iterator();
       
   164     }
       
   165 
       
   166     /**
       
   167      * Read the value of a field. The <code>data</code> parameter should be
       
   168      * an array of length 1 of Object.
       
   169      *
       
   170      * @param stream the input stream
       
   171      * @param type the type as read from the stream
       
   172      * @param count the count read from the stream
       
   173      * @param data a container for the data
       
   174      * @return the updated count
       
   175      * @throws IOException
       
   176      */
       
   177     private static int readFieldValue(ImageInputStream stream,
       
   178         int type, int count, Object[] data) throws IOException {
       
   179         Object obj;
       
   180 
       
   181         switch (type) {
       
   182             case TIFFTag.TIFF_BYTE:
       
   183             case TIFFTag.TIFF_SBYTE:
       
   184             case TIFFTag.TIFF_UNDEFINED:
       
   185             case TIFFTag.TIFF_ASCII:
       
   186                 byte[] bvalues = new byte[count];
       
   187                 stream.readFully(bvalues, 0, count);
       
   188 
       
   189                 if (type == TIFFTag.TIFF_ASCII) {
       
   190                     // Can be multiple strings
       
   191                     ArrayList<String> v = new ArrayList<>();
       
   192                     boolean inString = false;
       
   193                     int prevIndex = 0;
       
   194                     for (int index = 0; index <= count; index++) {
       
   195                         if (index < count && bvalues[index] != 0) {
       
   196                             if (!inString) {
       
   197                                 // start of string
       
   198                                 prevIndex = index;
       
   199                                 inString = true;
       
   200                             }
       
   201                         } else { // null or special case at end of string
       
   202                             if (inString) {
       
   203                                 // end of string
       
   204                                 String s = new String(bvalues, prevIndex,
       
   205                                         index - prevIndex,
       
   206                                         StandardCharsets.US_ASCII);
       
   207                                 v.add(s);
       
   208                                 inString = false;
       
   209                             }
       
   210                         }
       
   211                     }
       
   212 
       
   213                     count = v.size();
       
   214                     String[] strings;
       
   215                     if (count != 0) {
       
   216                         strings = new String[count];
       
   217                         for (int c = 0; c < count; c++) {
       
   218                             strings[c] = v.get(c);
       
   219                         }
       
   220                     } else {
       
   221                         // This case has been observed when the value of
       
   222                         // 'count' recorded in the field is non-zero but
       
   223                         // the value portion contains all nulls.
       
   224                         count = 1;
       
   225                         strings = new String[]{""};
       
   226                     }
       
   227 
       
   228                     obj = strings;
       
   229                 } else {
       
   230                     obj = bvalues;
       
   231                 }
       
   232                 break;
       
   233 
       
   234             case TIFFTag.TIFF_SHORT:
       
   235                 char[] cvalues = new char[count];
       
   236                 for (int j = 0; j < count; j++) {
       
   237                     cvalues[j] = (char) (stream.readUnsignedShort());
       
   238                 }
       
   239                 obj = cvalues;
       
   240                 break;
       
   241 
       
   242             case TIFFTag.TIFF_LONG:
       
   243             case TIFFTag.TIFF_IFD_POINTER:
       
   244                 long[] lvalues = new long[count];
       
   245                 for (int j = 0; j < count; j++) {
       
   246                     lvalues[j] = stream.readUnsignedInt();
       
   247                 }
       
   248                 obj = lvalues;
       
   249                 break;
       
   250 
       
   251             case TIFFTag.TIFF_RATIONAL:
       
   252                 long[][] llvalues = new long[count][2];
       
   253                 for (int j = 0; j < count; j++) {
       
   254                     llvalues[j][0] = stream.readUnsignedInt();
       
   255                     llvalues[j][1] = stream.readUnsignedInt();
       
   256                 }
       
   257                 obj = llvalues;
       
   258                 break;
       
   259 
       
   260             case TIFFTag.TIFF_SSHORT:
       
   261                 short[] svalues = new short[count];
       
   262                 for (int j = 0; j < count; j++) {
       
   263                     svalues[j] = stream.readShort();
       
   264                 }
       
   265                 obj = svalues;
       
   266                 break;
       
   267 
       
   268             case TIFFTag.TIFF_SLONG:
       
   269                 int[] ivalues = new int[count];
       
   270                 for (int j = 0; j < count; j++) {
       
   271                     ivalues[j] = stream.readInt();
       
   272                 }
       
   273                 obj = ivalues;
       
   274                 break;
       
   275 
       
   276             case TIFFTag.TIFF_SRATIONAL:
       
   277                 int[][] iivalues = new int[count][2];
       
   278                 for (int j = 0; j < count; j++) {
       
   279                     iivalues[j][0] = stream.readInt();
       
   280                     iivalues[j][1] = stream.readInt();
       
   281                 }
       
   282                 obj = iivalues;
       
   283                 break;
       
   284 
       
   285             case TIFFTag.TIFF_FLOAT:
       
   286                 float[] fvalues = new float[count];
       
   287                 for (int j = 0; j < count; j++) {
       
   288                     fvalues[j] = stream.readFloat();
       
   289                 }
       
   290                 obj = fvalues;
       
   291                 break;
       
   292 
       
   293             case TIFFTag.TIFF_DOUBLE:
       
   294                 double[] dvalues = new double[count];
       
   295                 for (int j = 0; j < count; j++) {
       
   296                     dvalues[j] = stream.readDouble();
       
   297                 }
       
   298                 obj = dvalues;
       
   299                 break;
       
   300 
       
   301             default:
       
   302                 obj = null;
       
   303                 break;
       
   304         }
       
   305 
       
   306         data[0] = obj;
       
   307 
       
   308         return count;
       
   309     }
       
   310 
       
   311     //
       
   312     // Class to represent an IFD entry where the actual content is at an offset
       
   313     // in the stream somewhere outside the IFD itself. This occurs when the
       
   314     // value cannot be contained within four bytes. Seeking is required to read
       
   315     // such field values.
       
   316     //
       
   317     private static class TIFFIFDEntry {
       
   318         public final TIFFTag tag;
       
   319         public final int type;
       
   320         public final int count;
       
   321         public final long offset;
       
   322 
       
   323         TIFFIFDEntry(TIFFTag tag, int type, int count, long offset) {
       
   324             this.tag = tag;
       
   325             this.type = type;
       
   326             this.count = count;
       
   327             this.offset = offset;
       
   328         }
       
   329     }
       
   330 
       
   331     //
       
   332     // Verify that data pointed to outside of the IFD itself are within the
       
   333     // stream. To be called after all fields have been read and populated.
       
   334     //
       
   335     private void checkFieldOffsets(long streamLength) throws IIOException {
       
   336         if (streamLength < 0) {
       
   337             return;
       
   338         }
       
   339 
       
   340         // StripOffsets
       
   341         List<TIFFField> offsets = new ArrayList<>();
       
   342         TIFFField f = getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
       
   343         int count = 0;
       
   344         if (f != null) {
       
   345             count = f.getCount();
       
   346             offsets.add(f);
       
   347         }
       
   348 
       
   349         // TileOffsets
       
   350         f = getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS);
       
   351         if (f != null) {
       
   352             int sz = offsets.size();
       
   353             int newCount = f.getCount();
       
   354             if (sz > 0 && newCount != count) {
       
   355                 throw new IIOException
       
   356                     ("StripOffsets count != TileOffsets count");
       
   357             }
       
   358 
       
   359             if (sz == 0) {
       
   360                 count = newCount;
       
   361             }
       
   362             offsets.add(f);
       
   363         }
       
   364 
       
   365         if (offsets.size() > 0) {
       
   366             // StripByteCounts
       
   367             List<TIFFField> byteCounts = new ArrayList<>();
       
   368             f = getTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS);
       
   369             if (f != null) {
       
   370                 if (f.getCount() != count) {
       
   371                     throw new IIOException
       
   372                         ("StripByteCounts count != number of offsets");
       
   373                 }
       
   374                 byteCounts.add(f);
       
   375             }
       
   376 
       
   377             // TileByteCounts
       
   378             f = getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS);
       
   379             if (f != null) {
       
   380                 if (f.getCount() != count) {
       
   381                     throw new IIOException
       
   382                         ("TileByteCounts count != number of offsets");
       
   383                 }
       
   384                 byteCounts.add(f);
       
   385             }
       
   386 
       
   387             if (byteCounts.size() > 0) {
       
   388                 for (TIFFField offset : offsets) {
       
   389                     for (TIFFField byteCount : byteCounts) {
       
   390                         for (int i = 0; i < count; i++) {
       
   391                             long dataOffset = offset.getAsLong(i);
       
   392                             long dataByteCount = byteCount.getAsLong(i);
       
   393                             if (dataOffset + dataByteCount > streamLength) {
       
   394                                 throw new IIOException
       
   395                                     ("Data segment out of stream");
       
   396                             }
       
   397                         }
       
   398                     }
       
   399                 }
       
   400             }
       
   401         }
       
   402 
       
   403         // JPEGInterchangeFormat and JPEGInterchangeFormatLength
       
   404         TIFFField jpegOffset =
       
   405             getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT);
       
   406         if (jpegOffset != null) {
       
   407             TIFFField jpegLength =
       
   408                 getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
       
   409             if (jpegLength != null) {
       
   410                 if (jpegOffset.getAsLong(0) + jpegLength.getAsLong(0)
       
   411                     > streamLength) {
       
   412                     throw new IIOException
       
   413                         ("JPEGInterchangeFormat data out of stream");
       
   414                 }
       
   415             }
       
   416         }
       
   417 
       
   418         // JPEGQTables - one 64-byte table for each offset.
       
   419         f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_Q_TABLES);
       
   420         if (f != null) {
       
   421             long[] tableOffsets = f.getAsLongs();
       
   422             for (long off : tableOffsets) {
       
   423                 if (off + 64 > streamLength) {
       
   424                     throw new IIOException("JPEGQTables data out of stream");
       
   425                 }
       
   426             }
       
   427         }
       
   428 
       
   429         // JPEGDCTables
       
   430         f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_DC_TABLES);
       
   431         if (f != null) {
       
   432             long[] tableOffsets = f.getAsLongs();
       
   433             for (long off : tableOffsets) {
       
   434                 if (off + 16 > streamLength) {
       
   435                     throw new IIOException("JPEGDCTables data out of stream");
       
   436                 }
       
   437             }
       
   438         }
       
   439 
       
   440         // JPEGACTables
       
   441         f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_AC_TABLES);
       
   442         if (f != null) {
       
   443             long[] tableOffsets = f.getAsLongs();
       
   444             for (long off : tableOffsets) {
       
   445                 if (off + 16 > streamLength) {
       
   446                     throw new IIOException("JPEGACTables data out of stream");
       
   447                 }
       
   448             }
       
   449         }
       
   450     }
       
   451 
       
   452     // Stream position initially at beginning, left at end
       
   453     // if ignoreUnknownFields is true, do not load fields for which
       
   454     // a tag cannot be found in an allowed TagSet.
       
   455     public void initialize(ImageInputStream stream, boolean isPrimaryIFD,
       
   456         boolean ignoreUnknownFields) throws IOException {
       
   457 
       
   458         removeTIFFFields();
       
   459 
       
   460         long streamLength = stream.length();
       
   461         boolean haveStreamLength = streamLength != -1;
       
   462 
       
   463         List<TIFFTagSet> tagSetList = getTagSetList();
       
   464 
       
   465         List<Object> entries = new ArrayList<>();
       
   466         Object[] entryData = new Object[1]; // allocate once for later reuse.
       
   467 
       
   468         // Read the IFD entries, loading the field values which are no more than
       
   469         // four bytes long, and storing the 4-byte offsets for the others.
       
   470         int numEntries = stream.readUnsignedShort();
       
   471         for (int i = 0; i < numEntries; i++) {
       
   472             // Read tag number, value type, and value count.
       
   473             int tagNumber = stream.readUnsignedShort();
       
   474             int type = stream.readUnsignedShort();
       
   475             int count = (int)stream.readUnsignedInt();
       
   476 
       
   477             // Get the associated TIFFTag.
       
   478             TIFFTag tag = getTag(tagNumber, tagSetList);
       
   479 
       
   480             // Ignore unknown fields.
       
   481             if((tag == null && ignoreUnknownFields)
       
   482                 || (tag != null && !tag.isDataTypeOK(type))) {
       
   483                 // Skip the value/offset so as to leave the stream
       
   484                 // position at the start of the next IFD entry.
       
   485                 stream.skipBytes(4);
       
   486 
       
   487                 // Continue with the next IFD entry.
       
   488                 continue;
       
   489             }
       
   490 
       
   491             if (tag == null) {
       
   492                 tag = new TIFFTag(TIFFTag.UNKNOWN_TAG_NAME, tagNumber,
       
   493                     1 << type, count);
       
   494             } else {
       
   495                 int expectedCount = tag.getCount();
       
   496                 if (expectedCount > 0) {
       
   497                     // If the tag count is positive then the tag defines a
       
   498                     // specific, fixed count that the field must match.
       
   499                     if (count != expectedCount) {
       
   500                         throw new IIOException("Unexpected count "
       
   501                             + count + " for " + tag.getName() + " field");
       
   502                     }
       
   503                 } else if (type == TIFFTag.TIFF_ASCII) {
       
   504                     // Clamp the size of ASCII fields of unspecified length
       
   505                     // to a maximum value.
       
   506                     int asciiSize = TIFFTag.getSizeOfType(TIFFTag.TIFF_ASCII);
       
   507                     if (count*asciiSize > MAX_ASCII_SIZE) {
       
   508                         count = (int)(MAX_ASCII_SIZE/asciiSize);
       
   509                     }
       
   510                 }
       
   511             }
       
   512 
       
   513             int size = count*TIFFTag.getSizeOfType(type);
       
   514             if (size > 4 || tag.isIFDPointer()) {
       
   515                 // The IFD entry value is a pointer to the actual field value.
       
   516                 long offset = stream.readUnsignedInt();
       
   517 
       
   518                 // Check whether the the field value is within the stream.
       
   519                 if (haveStreamLength && offset + size > streamLength) {
       
   520                     throw new IIOException("Field data is past end-of-stream");
       
   521                 }
       
   522 
       
   523                 // Add a TIFFIFDEntry as a placeholder. This avoids a mark,
       
   524                 // seek to the data, and a reset.
       
   525                 entries.add(new TIFFIFDEntry(tag, type, count, offset));
       
   526             } else {
       
   527                 // The IFD entry value is the actual field value of no more than
       
   528                 // four bytes.
       
   529                 Object obj = null;
       
   530                 try {
       
   531                     // Read the field value and update the count.
       
   532                     count = readFieldValue(stream, type, count, entryData);
       
   533                     obj = entryData[0];
       
   534                 } catch (EOFException eofe) {
       
   535                     // The TIFF 6.0 fields have tag numbers less than or equal
       
   536                     // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright).
       
   537                     // If there is an error reading a baseline tag, then re-throw
       
   538                     // the exception and fail; otherwise continue with the next
       
   539                     // field.
       
   540                     if (BaselineTIFFTagSet.getInstance().getTag(tagNumber) == null) {
       
   541                         throw eofe;
       
   542                     }
       
   543                 }
       
   544 
       
   545                 // If the field value is smaller than four bytes then skip
       
   546                 // the remaining, unused bytes.
       
   547                 if (size < 4) {
       
   548                     stream.skipBytes(4 - size);
       
   549                 }
       
   550 
       
   551                 // Add the populated TIFFField to the list of entries.
       
   552                 entries.add(new TIFFField(tag, type, count, obj));
       
   553             }
       
   554         }
       
   555 
       
   556         // After reading the IFD entries the stream is positioned at an unsigned
       
   557         // four byte integer containing either the offset of the next IFD or
       
   558         // zero if this is the last IFD.
       
   559         long nextIFDOffset = stream.getStreamPosition();
       
   560 
       
   561         Object[] fieldData = new Object[1];
       
   562         for (Object entry : entries) {
       
   563             if (entry instanceof TIFFField) {
       
   564                 // Add the populated field directly.
       
   565                 addTIFFField((TIFFField)entry);
       
   566             } else {
       
   567                 TIFFIFDEntry e = (TIFFIFDEntry)entry;
       
   568                 TIFFTag tag = e.tag;
       
   569                 int tagNumber = tag.getNumber();
       
   570                 int type = e.type;
       
   571                 int count = e.count;
       
   572 
       
   573                 stream.seek(e.offset);
       
   574 
       
   575                 if (tag.isIFDPointer()) {
       
   576                     List<TIFFTagSet> tagSets = new ArrayList<TIFFTagSet>(1);
       
   577                     tagSets.add(tag.getTagSet());
       
   578                     TIFFIFD subIFD = new TIFFIFD(tagSets);
       
   579 
       
   580                     subIFD.initialize(stream, false, ignoreUnknownFields);
       
   581                     TIFFField f = new TIFFField(tag, type, e.offset, subIFD);
       
   582                     addTIFFField(f);
       
   583                 } else {
       
   584                     if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS
       
   585                             || tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS
       
   586                             || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) {
       
   587                         this.stripOrTileByteCountsPosition
       
   588                                 = stream.getStreamPosition();
       
   589                     } else if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_OFFSETS
       
   590                             || tagNumber == BaselineTIFFTagSet.TAG_TILE_OFFSETS
       
   591                             || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) {
       
   592                         this.stripOrTileOffsetsPosition
       
   593                                 = stream.getStreamPosition();
       
   594                     }
       
   595 
       
   596                     Object obj = null;
       
   597                     try {
       
   598                         count = readFieldValue(stream, type, count, fieldData);
       
   599                         obj = fieldData[0];
       
   600                     } catch (EOFException eofe) {
       
   601                         // The TIFF 6.0 fields have tag numbers less than or equal
       
   602                         // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright).
       
   603                         // If there is an error reading a baseline tag, then re-throw
       
   604                         // the exception and fail; otherwise continue with the next
       
   605                         // field.
       
   606                         if (BaselineTIFFTagSet.getInstance().getTag(tagNumber) == null) {
       
   607                             throw eofe;
       
   608                         }
       
   609                     }
       
   610 
       
   611                     if (obj == null) {
       
   612                         continue;
       
   613                     }
       
   614 
       
   615                     TIFFField f = new TIFFField(tag, type, count, obj);
       
   616                     addTIFFField(f);
       
   617                 }
       
   618             }
       
   619         }
       
   620 
       
   621         if(isPrimaryIFD && haveStreamLength) {
       
   622             checkFieldOffsets(streamLength);
       
   623         }
       
   624 
       
   625         stream.seek(nextIFDOffset);
       
   626         this.lastPosition = stream.getStreamPosition();
       
   627     }
       
   628 
       
   629     public void writeToStream(ImageOutputStream stream)
       
   630         throws IOException {
       
   631 
       
   632         int numFields = getNumTIFFFields();
       
   633         stream.writeShort(numFields);
       
   634 
       
   635         long nextSpace = stream.getStreamPosition() + 12*numFields + 4;
       
   636 
       
   637         Iterator<TIFFField> iter = iterator();
       
   638         while (iter.hasNext()) {
       
   639             TIFFField f = iter.next();
       
   640 
       
   641             TIFFTag tag = f.getTag();
       
   642 
       
   643             int type = f.getType();
       
   644             int count = f.getCount();
       
   645 
       
   646             // Deal with unknown tags
       
   647             if (type == 0) {
       
   648                 type = TIFFTag.TIFF_UNDEFINED;
       
   649             }
       
   650             int size = count*TIFFTag.getSizeOfType(type);
       
   651 
       
   652             if (type == TIFFTag.TIFF_ASCII) {
       
   653                 int chars = 0;
       
   654                 for (int i = 0; i < count; i++) {
       
   655                     chars += f.getAsString(i).length() + 1;
       
   656                 }
       
   657                 count = chars;
       
   658                 size = count;
       
   659             }
       
   660 
       
   661             int tagNumber = f.getTagNumber();
       
   662             stream.writeShort(tagNumber);
       
   663             stream.writeShort(type);
       
   664             stream.writeInt(count);
       
   665 
       
   666             // Write a dummy value to fill space
       
   667             stream.writeInt(0);
       
   668             stream.mark(); // Mark beginning of next field
       
   669             stream.skipBytes(-4);
       
   670 
       
   671             long pos;
       
   672 
       
   673             if (size > 4 || tag.isIFDPointer()) {
       
   674                 // Ensure IFD or value is written on a word boundary
       
   675                 nextSpace = (nextSpace + 3) & ~0x3;
       
   676 
       
   677                 stream.writeInt((int)nextSpace);
       
   678                 stream.seek(nextSpace);
       
   679                 pos = nextSpace;
       
   680 
       
   681                 if (tag.isIFDPointer() && f.hasDirectory()) {
       
   682                     TIFFIFD subIFD = (TIFFIFD)f.getDirectory();
       
   683                     subIFD.writeToStream(stream);
       
   684                     nextSpace = subIFD.lastPosition;
       
   685                 } else {
       
   686                     writeTIFFFieldToStream(f, stream);
       
   687                     nextSpace = stream.getStreamPosition();
       
   688                 }
       
   689             } else {
       
   690                 pos = stream.getStreamPosition();
       
   691                 writeTIFFFieldToStream(f, stream);
       
   692             }
       
   693 
       
   694             // If we are writing the data for the
       
   695             // StripByteCounts, TileByteCounts, StripOffsets,
       
   696             // TileOffsets, JPEGInterchangeFormat, or
       
   697             // JPEGInterchangeFormatLength fields, record the current stream
       
   698             // position for backpatching
       
   699             if (tagNumber ==
       
   700                 BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS ||
       
   701                 tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS ||
       
   702                 tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) {
       
   703                 this.stripOrTileByteCountsPosition = pos;
       
   704             } else if (tagNumber ==
       
   705                        BaselineTIFFTagSet.TAG_STRIP_OFFSETS ||
       
   706                        tagNumber ==
       
   707                        BaselineTIFFTagSet.TAG_TILE_OFFSETS ||
       
   708                        tagNumber ==
       
   709                        BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) {
       
   710                 this.stripOrTileOffsetsPosition = pos;
       
   711             }
       
   712 
       
   713             stream.reset(); // Go to marked position of next field
       
   714         }
       
   715 
       
   716         this.lastPosition = nextSpace;
       
   717     }
       
   718 
       
   719     public long getStripOrTileByteCountsPosition() {
       
   720         return stripOrTileByteCountsPosition;
       
   721     }
       
   722 
       
   723     public long getStripOrTileOffsetsPosition() {
       
   724         return stripOrTileOffsetsPosition;
       
   725     }
       
   726 
       
   727     public long getLastPosition() {
       
   728         return lastPosition;
       
   729     }
       
   730 
       
   731     void setPositions(long stripOrTileOffsetsPosition,
       
   732                       long stripOrTileByteCountsPosition,
       
   733                       long lastPosition) {
       
   734         this.stripOrTileOffsetsPosition = stripOrTileOffsetsPosition;
       
   735         this.stripOrTileByteCountsPosition = stripOrTileByteCountsPosition;
       
   736         this.lastPosition = lastPosition;
       
   737     }
       
   738 
       
   739     /**
       
   740      * Returns a <code>TIFFIFD</code> wherein all fields from the
       
   741      * <code>BaselineTIFFTagSet</code> are copied by value and all other
       
   742      * fields copied by reference.
       
   743      */
       
   744     public TIFFIFD getShallowClone() {
       
   745         // Get the baseline TagSet.
       
   746         TIFFTagSet baselineTagSet = BaselineTIFFTagSet.getInstance();
       
   747 
       
   748         // If the baseline TagSet is not included just return.
       
   749         List<TIFFTagSet> tagSetList = getTagSetList();
       
   750         if(!tagSetList.contains(baselineTagSet)) {
       
   751             return this;
       
   752         }
       
   753 
       
   754         // Create a new object.
       
   755         TIFFIFD shallowClone = new TIFFIFD(tagSetList, getParentTag());
       
   756 
       
   757         // Get the tag numbers in the baseline set.
       
   758         Set<Integer> baselineTagNumbers = baselineTagSet.getTagNumbers();
       
   759 
       
   760         // Iterate over the fields in this IFD.
       
   761         Iterator<TIFFField> fields = iterator();
       
   762         while(fields.hasNext()) {
       
   763             // Get the next field.
       
   764             TIFFField field = fields.next();
       
   765 
       
   766             // Get its tag number.
       
   767             Integer tagNumber = Integer.valueOf(field.getTagNumber());
       
   768 
       
   769             // Branch based on membership in baseline set.
       
   770             TIFFField fieldClone;
       
   771             if(baselineTagNumbers.contains(tagNumber)) {
       
   772                 // Copy by value.
       
   773                 Object fieldData = field.getData();
       
   774 
       
   775                 int fieldType = field.getType();
       
   776 
       
   777                 try {
       
   778                     switch (fieldType) {
       
   779                     case TIFFTag.TIFF_BYTE:
       
   780                     case TIFFTag.TIFF_SBYTE:
       
   781                     case TIFFTag.TIFF_UNDEFINED:
       
   782                         fieldData = ((byte[])fieldData).clone();
       
   783                         break;
       
   784                     case TIFFTag.TIFF_ASCII:
       
   785                         fieldData = ((String[])fieldData).clone();
       
   786                         break;
       
   787                     case TIFFTag.TIFF_SHORT:
       
   788                         fieldData = ((char[])fieldData).clone();
       
   789                         break;
       
   790                     case TIFFTag.TIFF_LONG:
       
   791                     case TIFFTag.TIFF_IFD_POINTER:
       
   792                         fieldData = ((long[])fieldData).clone();
       
   793                         break;
       
   794                     case TIFFTag.TIFF_RATIONAL:
       
   795                         fieldData = ((long[][])fieldData).clone();
       
   796                         break;
       
   797                     case TIFFTag.TIFF_SSHORT:
       
   798                         fieldData = ((short[])fieldData).clone();
       
   799                         break;
       
   800                     case TIFFTag.TIFF_SLONG:
       
   801                         fieldData = ((int[])fieldData).clone();
       
   802                         break;
       
   803                     case TIFFTag.TIFF_SRATIONAL:
       
   804                         fieldData = ((int[][])fieldData).clone();
       
   805                         break;
       
   806                     case TIFFTag.TIFF_FLOAT:
       
   807                         fieldData = ((float[])fieldData).clone();
       
   808                         break;
       
   809                     case TIFFTag.TIFF_DOUBLE:
       
   810                         fieldData = ((double[])fieldData).clone();
       
   811                         break;
       
   812                     default:
       
   813                         // Shouldn't happen but do nothing ...
       
   814                     }
       
   815                 } catch(Exception e) {
       
   816                     // Ignore it and copy by reference ...
       
   817                 }
       
   818 
       
   819                 fieldClone = new TIFFField(field.getTag(), fieldType,
       
   820                                            field.getCount(), fieldData);
       
   821             } else {
       
   822                 // Copy by reference.
       
   823                 fieldClone = field;
       
   824             }
       
   825 
       
   826             // Add the field to the clone.
       
   827             shallowClone.addTIFFField(fieldClone);
       
   828         }
       
   829 
       
   830         // Set positions.
       
   831         shallowClone.setPositions(stripOrTileOffsetsPosition,
       
   832                                   stripOrTileByteCountsPosition,
       
   833                                   lastPosition);
       
   834 
       
   835         return shallowClone;
       
   836     }
       
   837 }