1 /*
2  * Copyright (c) 2005, 2020, 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 
initializeEssentialTags()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      */
getDirectoryAsIFD(TIFFDirectory dir)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 
getTag(int tagNumber, List<TIFFTagSet> tagSets)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 
getTag(String tagName, List<TIFFTagSet> tagSets)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 
writeTIFFFieldToStream(TIFFField field, ImageOutputStream stream)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 
TIFFIFD(List<TIFFTagSet> tagSets, TIFFTag parentTag)227     public TIFFIFD(List<TIFFTagSet> tagSets, TIFFTag parentTag) {
228         super(tagSets.toArray(new TIFFTagSet[tagSets.size()]),
229               parentTag);
230     }
231 
TIFFIFD(List<TIFFTagSet> tagSets)232     public TIFFIFD(List<TIFFTagSet> tagSets) {
233         this(tagSets, null);
234     }
235 
getTagSetList()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.
iterator()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      */
readFieldValue(ImageInputStream stream, int type, int count, Object[] data)262     private static int readFieldValue(ImageInputStream stream,
263         int type, int count, Object[] data) throws IOException {
264         Object obj;
265         final int UNIT_SIZE = 1024000;
266 
267         switch (type) {
268             case TIFFTag.TIFF_BYTE:
269             case TIFFTag.TIFF_SBYTE:
270             case TIFFTag.TIFF_UNDEFINED:
271             case TIFFTag.TIFF_ASCII:
272                 if (type == TIFFTag.TIFF_ASCII) {
273                     byte[] bvalues = new byte[count];
274                     stream.readFully(bvalues, 0, count);
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                     if (count < UNIT_SIZE) {
316                         byte[] bvalues = new byte[count];
317                         stream.readFully(bvalues, 0, count);
318                         obj = bvalues;
319                     } else {
320                         int bytesToRead = count;
321                         int bytesRead = 0;
322                         List<byte[]> bufs = new ArrayList<>();
323                         while (bytesToRead != 0) {
324                             int sz = Math.min(bytesToRead, UNIT_SIZE);
325                             byte[] unit = new byte[sz];
326                             stream.readFully(unit, bytesRead, sz);
327                             bufs.add(unit);
328                             bytesRead += sz;
329                             bytesToRead -= sz;
330                         }
331                         byte[] tagData = new byte[bytesRead];
332                         int copiedBytes = 0;
333                         for (byte[] ba : bufs) {
334                             System.arraycopy(ba, 0, tagData, copiedBytes, ba.length);
335                             copiedBytes += ba.length;
336                         }
337                         obj = tagData;
338                     }
339                 }
340                 break;
341 
342             case TIFFTag.TIFF_SHORT:
343                 final int SHORT_TILE_SIZE =
344                     UNIT_SIZE / TIFFTag.getSizeOfType(TIFFTag.TIFF_SHORT);
345                 if (count < SHORT_TILE_SIZE) {
346                     char[] cvalues = new char[count];
347                     for (int j = 0; j < count; j++) {
348                         cvalues[j] = (char) (stream.readUnsignedShort());
349                     }
350                     obj = cvalues;
351                 } else {
352                     int charsToRead = count;
353                     int charsRead = 0;
354                     List<char[]> bufs = new ArrayList<>();
355                     while (charsToRead != 0) {
356                         int sz = Math.min(charsToRead, SHORT_TILE_SIZE);
357                         char[] unit = new char[sz];
358                         for (int i = 0; i < sz ; i++) {
359                             unit[i] = (char) (stream.readUnsignedShort());
360                         }
361                         bufs.add(unit);
362                         charsRead += sz;
363                         charsToRead -= sz;
364                     }
365                     char[] tagData = new char[charsRead];
366                     int copiedChars = 0;
367                     for (char[] ca : bufs) {
368                         System.arraycopy(ca, 0, tagData, copiedChars, ca.length);
369                         copiedChars += ca.length;
370                     }
371                     obj = tagData;
372                 }
373                 break;
374 
375             case TIFFTag.TIFF_LONG:
376             case TIFFTag.TIFF_IFD_POINTER:
377                 final int LONG_TILE_SIZE =
378                     UNIT_SIZE / TIFFTag.getSizeOfType(TIFFTag.TIFF_LONG);
379                 if (count < LONG_TILE_SIZE) {
380                     long[] lvalues = new long[count];
381                     for (int j = 0; j < count; j++) {
382                         lvalues[j] = stream.readUnsignedInt();
383                     }
384                     obj = lvalues;
385                 } else {
386                     int longsToRead = count;
387                     int longsRead = 0;
388                     List<long[]> bufs = new ArrayList<>();
389                     while (longsToRead != 0) {
390                         int sz = Math.min(longsToRead, LONG_TILE_SIZE);
391                         long[] unit = new long[sz];
392                         for (int i = 0; i < sz ; i++) {
393                             unit[i] = stream.readUnsignedInt();
394                         }
395                         bufs.add(unit);
396                         longsRead += sz;
397                         longsToRead -= sz;
398                     }
399                     long[] tagData = new long[longsRead];
400                     int copiedLongs = 0;
401                     for (long[] la : bufs) {
402                         System.arraycopy(la, 0, tagData, copiedLongs, la.length);
403                         copiedLongs += la.length;
404                     }
405                     obj = tagData;
406                 }
407                 break;
408 
409             case TIFFTag.TIFF_RATIONAL:
410                 final int RATIONAL_TILE_SIZE =
411                     UNIT_SIZE / TIFFTag.getSizeOfType(TIFFTag.TIFF_RATIONAL);
412                 if (count < RATIONAL_TILE_SIZE) {
413                     long[][] llvalues = new long[count][2];
414                     for (int j = 0; j < count; j++) {
415                         llvalues[j][0] = stream.readUnsignedInt();
416                         llvalues[j][1] = stream.readUnsignedInt();
417                     }
418                     obj = llvalues;
419                 } else {
420                     int rationalsToRead = count;
421                     int rationalsRead = 0;
422                     List<long[]> bufs = new ArrayList<>();
423                     while (rationalsToRead != 0) {
424                         int sz = Math.min(rationalsToRead, RATIONAL_TILE_SIZE);
425                         long[] unit = new long[sz * 2];
426                         for (int i = 0; i < (sz * 2) ; i++) {
427                             unit[i] = stream.readUnsignedInt();
428                         }
429                         bufs.add(unit);
430                         rationalsRead += sz;
431                         rationalsToRead -= sz;
432                     }
433                     long[][] tagData = new long[rationalsRead][2];
434                     int copiedRationals = 0;
435                     for (long[] la : bufs) {
436                         for (int i = 0; i < la.length; i = i + 2) {
437                             tagData[copiedRationals + i][0] = la[i];
438                             tagData[copiedRationals + i][1] = la[i + 1];
439                         }
440                         copiedRationals += (la.length / 2);
441                     }
442                     obj = tagData;
443                 }
444                 break;
445 
446             case TIFFTag.TIFF_SSHORT:
447                 final int SSHORT_TILE_SIZE =
448                     UNIT_SIZE / TIFFTag.getSizeOfType(TIFFTag.TIFF_SSHORT);
449                 if (count < SSHORT_TILE_SIZE) {
450                     short[] svalues = new short[count];
451                     for (int j = 0; j < count; j++) {
452                         svalues[j] = stream.readShort();
453                     }
454                     obj = svalues;
455                 } else {
456                     int shortsToRead = count;
457                     int shortsRead = 0;
458                     List<short[]> bufs = new ArrayList<>();
459                     while (shortsToRead != 0) {
460                         int sz = Math.min(shortsToRead, SSHORT_TILE_SIZE);
461                         short[] unit = new short[sz];
462                         stream.readFully(unit, shortsRead, sz);
463                         bufs.add(unit);
464                         shortsRead += sz;
465                         shortsToRead -= sz;
466                     }
467                     short[] tagData = new short[shortsRead];
468                     int copiedShorts = 0;
469                     for (short[] sa : bufs) {
470                         System.arraycopy(sa, 0, tagData, copiedShorts, sa.length);
471                         copiedShorts += sa.length;
472                     }
473                     obj = tagData;
474                 }
475                 break;
476 
477             case TIFFTag.TIFF_SLONG:
478                 final int INT_TILE_SIZE =
479                     UNIT_SIZE / TIFFTag.getSizeOfType(TIFFTag.TIFF_SLONG);
480                 if (count < INT_TILE_SIZE) {
481                     int[] ivalues = new int[count];
482                     for (int j = 0; j < count; j++) {
483                         ivalues[j] = stream.readInt();
484                     }
485                     obj = ivalues;
486                 } else {
487                     int intsToRead = count;
488                     int intsRead = 0;
489                     List<int[]> bufs = new ArrayList<>();
490                     while (intsToRead != 0) {
491                         int sz = Math.min(intsToRead, INT_TILE_SIZE);
492                         int[] unit = new int[sz];
493                         stream.readFully(unit, intsToRead, sz);
494                         bufs.add(unit);
495                         intsRead += sz;
496                         intsToRead -= sz;
497                     }
498                     int[] tagData = new int[intsRead];
499                     int copiedInts = 0;
500                     for (int[] ia : bufs) {
501                         System.arraycopy(ia, 0, tagData, copiedInts, ia.length);
502                         copiedInts += ia.length;
503                     }
504                     obj = tagData;
505                 }
506                 break;
507 
508             case TIFFTag.TIFF_SRATIONAL:
509                 final int SRATIONAL_TILE_SIZE =
510                     UNIT_SIZE / TIFFTag.getSizeOfType(TIFFTag.TIFF_SRATIONAL);
511                 if (count < SRATIONAL_TILE_SIZE) {
512                     int[][] iivalues = new int[count][2];
513                     for (int j = 0; j < count; j++) {
514                         iivalues[j][0] = stream.readInt();
515                         iivalues[j][1] = stream.readInt();
516                     }
517                     obj = iivalues;
518                 } else {
519                     int srationalsToRead = count;
520                     int srationalsRead = 0;
521                     List<int[]> bufs = new ArrayList<>();
522                     while (srationalsToRead != 0) {
523                         int sz = Math.min(srationalsToRead, SRATIONAL_TILE_SIZE);
524                         int[] unit = new int[sz * 2];
525                         stream.readFully(unit, (srationalsToRead * 2), (sz * 2));
526                         bufs.add(unit);
527                         srationalsRead += sz;
528                         srationalsToRead -= sz;
529                     }
530                     int[][] tagData = new int[srationalsRead][2];
531                     int copiedSrationals = 0;
532                     for (int[] ia : bufs) {
533                         for (int i = 0; i < ia.length; i = i + 2) {
534                             tagData[copiedSrationals + i][0] = ia[i];
535                             tagData[copiedSrationals + i][1] = ia[i + 1];
536                         }
537                         copiedSrationals += (ia.length / 2);
538                     }
539                     obj = tagData;
540                 }
541                 break;
542 
543             case TIFFTag.TIFF_FLOAT:
544                 final int FLOAT_TILE_SIZE =
545                     UNIT_SIZE / TIFFTag.getSizeOfType(TIFFTag.TIFF_FLOAT);
546                 if (count < FLOAT_TILE_SIZE) {
547                     float[] fvalues = new float[count];
548                     for (int j = 0; j < count; j++) {
549                         fvalues[j] = stream.readFloat();
550                     }
551                     obj = fvalues;
552                 } else {
553                     int floatsToRead = count;
554                     int floatsRead = 0;
555                     List<float[]> bufs = new ArrayList<>();
556                     while (floatsToRead != 0) {
557                         int sz = Math.min(floatsToRead, FLOAT_TILE_SIZE);
558                         float[] unit = new float[sz];
559                         stream.readFully(unit, floatsToRead, sz);
560                         bufs.add(unit);
561                         floatsRead += sz;
562                         floatsToRead -= sz;
563                     }
564                     float[] tagData = new float[floatsRead];
565                     int copiedFloats = 0;
566                     for (float[] fa : bufs) {
567                         System.arraycopy(fa, 0, tagData, copiedFloats, fa.length);
568                         copiedFloats += fa.length;
569                     }
570                     obj = tagData;
571                 }
572                 break;
573 
574             case TIFFTag.TIFF_DOUBLE:
575                 final int DOUBLE_TILE_SIZE =
576                     UNIT_SIZE / TIFFTag.getSizeOfType(TIFFTag.TIFF_DOUBLE);
577                 if (count < DOUBLE_TILE_SIZE) {
578                     double[] dvalues = new double[count];
579                     for (int j = 0; j < count; j++) {
580                         dvalues[j] = stream.readDouble();
581                     }
582                     obj = dvalues;
583                 } else {
584                     int doublesToRead = count;
585                     int doublesRead = 0;
586                     List<double[]> bufs = new ArrayList<>();
587                     while (doublesToRead != 0) {
588                         int sz = Math.min(doublesToRead, DOUBLE_TILE_SIZE);
589                         double[] unit = new double[sz];
590                         stream.readFully(unit, doublesToRead, sz);
591                         bufs.add(unit);
592                         doublesRead += sz;
593                         doublesToRead -= sz;
594                     }
595                     double[] tagData = new double[doublesRead];
596                     int copiedDoubles = 0;
597                     for (double[] da : bufs) {
598                         System.arraycopy(da, 0, tagData, copiedDoubles, da.length);
599                         copiedDoubles += da.length;
600                     }
601                     obj = tagData;
602                 }
603                 break;
604             default:
605                 obj = null;
606                 break;
607         }
608 
609         data[0] = obj;
610 
611         return count;
612     }
613 
614     //
615     // Class to represent an IFD entry where the actual content is at an offset
616     // in the stream somewhere outside the IFD itself. This occurs when the
617     // value cannot be contained within four bytes. Seeking is required to read
618     // such field values.
619     //
620     private static class TIFFIFDEntry {
621         public final TIFFTag tag;
622         public final int type;
623         public final int count;
624         public final long offset;
625 
TIFFIFDEntry(TIFFTag tag, int type, int count, long offset)626         TIFFIFDEntry(TIFFTag tag, int type, int count, long offset) {
627             this.tag = tag;
628             this.type = type;
629             this.count = count;
630             this.offset = offset;
631         }
632     }
633 
634     //
635     // Retrieve the value of a baseline field as a long.
636     //
getFieldAsLong(int tagNumber)637     private long getFieldAsLong(int tagNumber) {
638         TIFFField f = getTIFFField(tagNumber);
639         return f == null ? -1 : f.getAsLong(0);
640     }
641 
642     //
643     // Retrieve the value of a baseline field as an int.
644     //
getFieldAsInt(int tagNumber)645     private int getFieldAsInt(int tagNumber) {
646         TIFFField f = getTIFFField(tagNumber);
647         return f == null ? -1 : f.getAsInt(0);
648     }
649 
650     //
651     // Calculate the number of bytes in each strip or tile. This method
652     // is to be used if and only if no fields exist which provide this
653     // information. The parameter must be empty and if the method succeeds
654     // will contain a single element.
655     //
calculateByteCounts(int expectedSize, List<TIFFField> byteCounts)656     private boolean calculateByteCounts(int expectedSize,
657         List<TIFFField> byteCounts) {
658         if (!byteCounts.isEmpty()) {
659             throw new IllegalArgumentException("byteCounts is not empty");
660         }
661 
662         // must be interleaved
663         if (getFieldAsInt(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION) ==
664             BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) {
665             return false;
666         }
667 
668         // must be uncompressed
669         if (getFieldAsInt(BaselineTIFFTagSet.TAG_COMPRESSION) !=
670             BaselineTIFFTagSet.COMPRESSION_NONE) {
671             return false;
672         }
673 
674         // must have image dimensions
675         long w = getFieldAsLong(BaselineTIFFTagSet.TAG_IMAGE_WIDTH);
676         if (w < 0) {
677             return false;
678         }
679         long h = getFieldAsLong(BaselineTIFFTagSet.TAG_IMAGE_LENGTH);
680         if (h < 0) {
681             return false;
682         }
683 
684         long tw = getFieldAsLong(BaselineTIFFTagSet.TAG_TILE_WIDTH);
685         if (tw < 0) {
686             tw = w;
687         }
688         long th = getFieldAsLong(BaselineTIFFTagSet.TAG_TILE_LENGTH);
689         if (th < 0) {
690             th = getFieldAsLong(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP);
691             if (th < 0) {
692                 th = h;
693             }
694         }
695 
696         int[] bitsPerSample = null;
697         TIFFField f = getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
698         if (f != null) {
699             bitsPerSample = f.getAsInts();
700         } else {
701             int samplesPerPixel =
702                 getFieldAsInt(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL);
703             if (samplesPerPixel < 0) {
704                 samplesPerPixel = 1;
705             }
706             bitsPerSample = new int[samplesPerPixel];
707             Arrays.fill(bitsPerSample, 8);
708         }
709 
710         int bitsPerPixel = 0;
711         for (int bps : bitsPerSample) {
712             bitsPerPixel += bps;
713         }
714 
715         int bytesPerRow = (int)(tw*bitsPerPixel + 7)/8;
716         int bytesPerPacket = (int)th*bytesPerRow;
717 
718         long nx = (w + tw - 1)/tw;
719         long ny = (h + th - 1)/th;
720 
721         if (nx*ny != expectedSize) {
722             return false;
723         }
724 
725         boolean isTiled =
726             getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS) != null;
727 
728         int tagNumber;
729         if (isTiled) {
730             tagNumber = BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS;
731         } else {
732             tagNumber = BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS;
733         }
734 
735         TIFFTag t = BaselineTIFFTagSet.getInstance().getTag(tagNumber);
736         f = getTIFFField(tagNumber);
737         if (f != null) {
738             removeTIFFField(tagNumber);
739         }
740 
741         int numPackets = (int)(nx*ny);
742         long[] packetByteCounts = new long[numPackets];
743         Arrays.fill(packetByteCounts, bytesPerPacket);
744 
745         // if the strip or tile width does not exceed the image width and the
746         // image height is not a multiple of the strip or tile height, then
747         // truncate the estimate of the byte count of the last strip to avoid
748         // reading past the end of the data
749         if (tw <= w && h % th != 0) {
750             int numRowsInLastStrip = (int)(h - (ny - 1)*th);
751             packetByteCounts[numPackets - 1] = numRowsInLastStrip*bytesPerRow;
752         }
753 
754         f = new TIFFField(t, TIFFTag.TIFF_LONG, numPackets, packetByteCounts);
755         addTIFFField(f);
756         byteCounts.add(f);
757 
758         return true;
759     }
760 
761     //
762     // Verify that data pointed to outside of the IFD itself are within the
763     // stream. To be called after all fields have been read and populated.
764     //
checkFieldOffsets(long streamLength)765     private void checkFieldOffsets(long streamLength) throws IIOException {
766         if (streamLength < 0) {
767             return;
768         }
769 
770         // StripOffsets
771         List<TIFFField> offsets = new ArrayList<>();
772         TIFFField f = getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
773         int count = 0;
774         if (f != null) {
775             count = f.getCount();
776             offsets.add(f);
777         }
778 
779         // TileOffsets
780         f = getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS);
781         if (f != null) {
782             int sz = offsets.size();
783             int newCount = f.getCount();
784             if (sz > 0 && newCount != count) {
785                 throw new IIOException
786                     ("StripOffsets count != TileOffsets count");
787             }
788 
789             if (sz == 0) {
790                 count = newCount;
791             }
792             offsets.add(f);
793         }
794 
795         List<TIFFField> byteCounts = new ArrayList<>();
796         if (offsets.size() > 0) {
797             // StripByteCounts
798             f = getTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS);
799             if (f != null) {
800                 if (f.getCount() != count) {
801                     throw new IIOException
802                         ("StripByteCounts count != number of offsets");
803                 }
804                 byteCounts.add(f);
805             }
806 
807             // TileByteCounts
808             f = getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS);
809             if (f != null) {
810                 if (f.getCount() != count) {
811                     throw new IIOException
812                         ("TileByteCounts count != number of offsets");
813                 }
814                 byteCounts.add(f);
815             }
816 
817             if (byteCounts.size() > 0) {
818                 for (TIFFField offset : offsets) {
819                     for (TIFFField byteCount : byteCounts) {
820                         for (int i = 0; i < count; i++) {
821                             long dataOffset = offset.getAsLong(i);
822                             long dataByteCount = byteCount.getAsLong(i);
823                             if (dataOffset + dataByteCount > streamLength) {
824                                 throw new IIOException
825                                     ("Data segment out of stream");
826                             }
827                         }
828                     }
829                 }
830             }
831         }
832 
833         // JPEGInterchangeFormat and JPEGInterchangeFormatLength
834         TIFFField jpegOffset =
835             getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT);
836         if (jpegOffset != null) {
837             TIFFField jpegLength =
838                 getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
839             if (jpegLength != null) {
840                 if (jpegOffset.getAsLong(0) + jpegLength.getAsLong(0)
841                     > streamLength) {
842                     throw new IIOException
843                         ("JPEGInterchangeFormat data out of stream");
844                 }
845             }
846         }
847 
848         // Ensure there is at least a data pointer for JPEG interchange format or
849         // both data offsets and byte counts for other compression types.
850         if (jpegOffset == null
851             && (offsets.size() == 0 || byteCounts.size() == 0)) {
852             boolean throwException = true;
853             if (offsets.size() != 0 && byteCounts.size() == 0) {
854                 // Attempt to calculate missing byte counts
855                 int expectedSize = offsets.get(0).getCount();
856                 throwException =
857                     !calculateByteCounts(expectedSize, byteCounts);
858             }
859             if (throwException) {
860                 throw new IIOException
861                     ("Insufficient data offsets or byte counts");
862             }
863         }
864 
865         // JPEGQTables - one 64-byte table for each offset.
866         f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_Q_TABLES);
867         if (f != null) {
868             long[] tableOffsets = f.getAsLongs();
869             for (long off : tableOffsets) {
870                 if (off + 64 > streamLength) {
871                     throw new IIOException("JPEGQTables data out of stream");
872                 }
873             }
874         }
875 
876         // JPEGDCTables
877         f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_DC_TABLES);
878         if (f != null) {
879             long[] tableOffsets = f.getAsLongs();
880             for (long off : tableOffsets) {
881                 if (off + 16 > streamLength) {
882                     throw new IIOException("JPEGDCTables data out of stream");
883                 }
884             }
885         }
886 
887         // JPEGACTables
888         f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_AC_TABLES);
889         if (f != null) {
890             long[] tableOffsets = f.getAsLongs();
891             for (long off : tableOffsets) {
892                 if (off + 16 > streamLength) {
893                     throw new IIOException("JPEGACTables data out of stream");
894                 }
895             }
896         }
897     }
898 
899     // Stream position initially at beginning, left at end
900     // if readUnknownTags is false, do not load fields for which
901     // a tag cannot be found in an allowed TagSet.
initialize(ImageInputStream stream, boolean isPrimaryIFD, boolean ignoreMetadata, boolean readUnknownTags)902     public void initialize(ImageInputStream stream, boolean isPrimaryIFD,
903         boolean ignoreMetadata, boolean readUnknownTags) throws IOException {
904 
905         removeTIFFFields();
906 
907         long streamLength = stream.length();
908         boolean haveStreamLength = streamLength != -1;
909 
910         List<TIFFTagSet> tagSetList = getTagSetList();
911 
912         // Configure essential tag variables if this is the primary IFD and
913         // either all metadata are being ignored, or metadata are not being
914         // ignored but both unknown tags are being ignored and the tag set
915         // list does not contain the baseline tags.
916         boolean ensureEssentialTags = false;
917         TIFFTagSet baselineTagSet = null;
918         if (isPrimaryIFD &&
919             (ignoreMetadata ||
920              (!readUnknownTags &&
921               !tagSetList.contains(BaselineTIFFTagSet.getInstance())))) {
922             ensureEssentialTags = true;
923             initializeEssentialTags();
924             baselineTagSet = BaselineTIFFTagSet.getInstance();
925         }
926 
927         List<Object> entries = new ArrayList<>();
928         Object[] entryData = new Object[1]; // allocate once for later reuse.
929 
930         // Read the IFD entries, loading the field values which are no more than
931         // four bytes long, and storing the 4-byte offsets for the others.
932         int numEntries = stream.readUnsignedShort();
933         for (int i = 0; i < numEntries; i++) {
934             // Read tag number, value type, and value count.
935             int tagNumber = stream.readUnsignedShort();
936             int type = stream.readUnsignedShort();
937             int sizeOfType;
938             try {
939                 sizeOfType = TIFFTag.getSizeOfType(type);
940             } catch (IllegalArgumentException ignored) {
941                 // Continue with the next IFD entry.
942                 stream.skipBytes(4);
943                 continue;
944             }
945             long longCount = stream.readUnsignedInt();
946 
947             // Get the associated TIFFTag.
948             TIFFTag tag = getTag(tagNumber, tagSetList);
949 
950             if (tag == null && ensureEssentialTags
951                 && essentialTags.contains(tagNumber)) {
952                 tag = baselineTagSet.getTag(tagNumber);
953             }
954 
955             // Ignore non-essential fields, unknown fields unless forcibly
956             // being read, fields with unknown type, and fields
957             // with count out of int range.
958             if((ignoreMetadata &&
959                 (!ensureEssentialTags || !essentialTags.contains(tagNumber)))
960                 || (tag == null && !readUnknownTags)
961                 || (tag != null && !tag.isDataTypeOK(type))
962                 || longCount > Integer.MAX_VALUE) {
963                 // Skip the value/offset so as to leave the stream
964                 // position at the start of the next IFD entry.
965                 stream.skipBytes(4);
966 
967                 // Continue with the next IFD entry.
968                 continue;
969             }
970 
971             int count = (int)longCount;
972 
973             if (tag == null) {
974                 tag = new TIFFTag(TIFFTag.UNKNOWN_TAG_NAME, tagNumber,
975                     1 << type, count);
976             } else {
977                 int expectedCount = tag.getCount();
978                 if (expectedCount > 0) {
979                     // If the tag count is positive then the tag defines a
980                     // specific, fixed count that the field must match.
981                     if (count != expectedCount) {
982                         throw new IIOException("Unexpected count "
983                             + count + " for " + tag.getName() + " field");
984                     }
985                 } else if (type == TIFFTag.TIFF_ASCII) {
986                     // Clamp the size of ASCII fields of unspecified length
987                     // to a maximum value.
988                     int asciiSize = TIFFTag.getSizeOfType(TIFFTag.TIFF_ASCII);
989                     if (count*asciiSize > MAX_ASCII_SIZE) {
990                         count = (int)(MAX_ASCII_SIZE/asciiSize);
991                     }
992                 }
993             }
994 
995             long longSize = longCount*sizeOfType;
996             if (longSize > Integer.MAX_VALUE) {
997                 // Continue with the next IFD entry.
998                 stream.skipBytes(4);
999                 continue;
1000             }
1001             int size = (int)longSize;
1002 
1003             if (size > 4 || tag.isIFDPointer()) {
1004                 // The IFD entry value is a pointer to the actual field value.
1005                 long offset = stream.readUnsignedInt();
1006 
1007                 // Check whether the the field value is within the stream.
1008                 if (haveStreamLength && offset + size > streamLength) {
1009                     continue;
1010                 }
1011 
1012                 // Add a TIFFIFDEntry as a placeholder. This avoids a mark,
1013                 // seek to the data, and a reset.
1014                 entries.add(new TIFFIFDEntry(tag, type, count, offset));
1015             } else {
1016                 // The IFD entry value is the actual field value of no more than
1017                 // four bytes.
1018                 Object obj = null;
1019                 try {
1020                     // Read the field value and update the count.
1021                     count = readFieldValue(stream, type, count, entryData);
1022                     obj = entryData[0];
1023                 } catch (EOFException eofe) {
1024                     // The TIFF 6.0 fields have tag numbers less than or equal
1025                     // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright).
1026                     // If there is an error reading a baseline tag, then re-throw
1027                     // the exception and fail; otherwise continue with the next
1028                     // field.
1029                     if (BaselineTIFFTagSet.getInstance().getTag(tagNumber) == null) {
1030                         throw eofe;
1031                     }
1032                 }
1033 
1034                 // If the field value is smaller than four bytes then skip
1035                 // the remaining, unused bytes.
1036                 if (size < 4) {
1037                     stream.skipBytes(4 - size);
1038                 }
1039 
1040                 // Add the populated TIFFField to the list of entries.
1041                 entries.add(new TIFFField(tag, type, count, obj));
1042             }
1043         }
1044 
1045         // After reading the IFD entries the stream is positioned at an unsigned
1046         // four byte integer containing either the offset of the next IFD or
1047         // zero if this is the last IFD.
1048         long nextIFDOffset = stream.getStreamPosition();
1049 
1050         Object[] fieldData = new Object[1];
1051         for (Object entry : entries) {
1052             if (entry instanceof TIFFField) {
1053                 // Add the populated field directly.
1054                 addTIFFField((TIFFField)entry);
1055             } else {
1056                 TIFFIFDEntry e = (TIFFIFDEntry)entry;
1057                 TIFFTag tag = e.tag;
1058                 int tagNumber = tag.getNumber();
1059                 int type = e.type;
1060                 int count = e.count;
1061 
1062                 stream.seek(e.offset);
1063 
1064                 if (tag.isIFDPointer()) {
1065                     List<TIFFTagSet> tagSets = new ArrayList<TIFFTagSet>(1);
1066                     tagSets.add(tag.getTagSet());
1067                     TIFFIFD subIFD = new TIFFIFD(tagSets);
1068 
1069                     subIFD.initialize(stream, false, ignoreMetadata,
1070                                       readUnknownTags);
1071                     TIFFField f = new TIFFField(tag, type, e.offset, subIFD);
1072                     addTIFFField(f);
1073                 } else {
1074                     if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS
1075                             || tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS
1076                             || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) {
1077                         this.stripOrTileByteCountsPosition
1078                                 = stream.getStreamPosition();
1079                     } else if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_OFFSETS
1080                             || tagNumber == BaselineTIFFTagSet.TAG_TILE_OFFSETS
1081                             || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) {
1082                         this.stripOrTileOffsetsPosition
1083                                 = stream.getStreamPosition();
1084                     }
1085 
1086                     Object obj = null;
1087                     try {
1088                         count = readFieldValue(stream, type, count, fieldData);
1089                         obj = fieldData[0];
1090                     } catch (EOFException eofe) {
1091                         // The TIFF 6.0 fields have tag numbers less than or equal
1092                         // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright).
1093                         // If there is an error reading a baseline tag, then re-throw
1094                         // the exception and fail; otherwise continue with the next
1095                         // field.
1096                         if (BaselineTIFFTagSet.getInstance().getTag(tagNumber) != null) {
1097                             throw eofe;
1098                         }
1099                     }
1100 
1101                     if (obj == null) {
1102                         continue;
1103                     }
1104 
1105                     TIFFField f = new TIFFField(tag, type, count, obj);
1106                     addTIFFField(f);
1107                 }
1108             }
1109         }
1110 
1111         if(isPrimaryIFD && haveStreamLength) {
1112             checkFieldOffsets(streamLength);
1113         }
1114 
1115         stream.seek(nextIFDOffset);
1116         this.lastPosition = stream.getStreamPosition();
1117     }
1118 
writeToStream(ImageOutputStream stream)1119     public void writeToStream(ImageOutputStream stream)
1120         throws IOException {
1121 
1122         int numFields = getNumTIFFFields();
1123         stream.writeShort(numFields);
1124 
1125         long nextSpace = stream.getStreamPosition() + 12*numFields + 4;
1126 
1127         Iterator<TIFFField> iter = iterator();
1128         while (iter.hasNext()) {
1129             TIFFField f = iter.next();
1130 
1131             TIFFTag tag = f.getTag();
1132 
1133             int type = f.getType();
1134             int count = f.getCount();
1135 
1136             // Deal with unknown tags
1137             if (type == 0) {
1138                 type = TIFFTag.TIFF_UNDEFINED;
1139             }
1140             int size = count*TIFFTag.getSizeOfType(type);
1141 
1142             if (type == TIFFTag.TIFF_ASCII) {
1143                 int chars = 0;
1144                 for (int i = 0; i < count; i++) {
1145                     chars += f.getAsString(i).length() + 1;
1146                 }
1147                 count = chars;
1148                 size = count;
1149             }
1150 
1151             int tagNumber = f.getTagNumber();
1152             stream.writeShort(tagNumber);
1153             stream.writeShort(type);
1154             stream.writeInt(count);
1155 
1156             // Write a dummy value to fill space
1157             stream.writeInt(0);
1158             stream.mark(); // Mark beginning of next field
1159             stream.skipBytes(-4);
1160 
1161             long pos;
1162 
1163             if (size > 4 || tag.isIFDPointer()) {
1164                 // Ensure IFD or value is written on a word boundary
1165                 nextSpace = (nextSpace + 3) & ~0x3;
1166 
1167                 stream.writeInt((int)nextSpace);
1168                 stream.seek(nextSpace);
1169                 pos = nextSpace;
1170 
1171                 if (tag.isIFDPointer() && f.hasDirectory()) {
1172                     TIFFIFD subIFD = getDirectoryAsIFD(f.getDirectory());
1173                     subIFD.writeToStream(stream);
1174                     nextSpace = subIFD.lastPosition;
1175                 } else {
1176                     writeTIFFFieldToStream(f, stream);
1177                     nextSpace = stream.getStreamPosition();
1178                 }
1179             } else {
1180                 pos = stream.getStreamPosition();
1181                 writeTIFFFieldToStream(f, stream);
1182             }
1183 
1184             // If we are writing the data for the
1185             // StripByteCounts, TileByteCounts, StripOffsets,
1186             // TileOffsets, JPEGInterchangeFormat, or
1187             // JPEGInterchangeFormatLength fields, record the current stream
1188             // position for backpatching
1189             if (tagNumber ==
1190                 BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS ||
1191                 tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS ||
1192                 tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) {
1193                 this.stripOrTileByteCountsPosition = pos;
1194             } else if (tagNumber ==
1195                        BaselineTIFFTagSet.TAG_STRIP_OFFSETS ||
1196                        tagNumber ==
1197                        BaselineTIFFTagSet.TAG_TILE_OFFSETS ||
1198                        tagNumber ==
1199                        BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) {
1200                 this.stripOrTileOffsetsPosition = pos;
1201             }
1202 
1203             stream.reset(); // Go to marked position of next field
1204         }
1205 
1206         this.lastPosition = nextSpace;
1207     }
1208 
getStripOrTileByteCountsPosition()1209     public long getStripOrTileByteCountsPosition() {
1210         return stripOrTileByteCountsPosition;
1211     }
1212 
getStripOrTileOffsetsPosition()1213     public long getStripOrTileOffsetsPosition() {
1214         return stripOrTileOffsetsPosition;
1215     }
1216 
getLastPosition()1217     public long getLastPosition() {
1218         return lastPosition;
1219     }
1220 
setPositions(long stripOrTileOffsetsPosition, long stripOrTileByteCountsPosition, long lastPosition)1221     void setPositions(long stripOrTileOffsetsPosition,
1222                       long stripOrTileByteCountsPosition,
1223                       long lastPosition) {
1224         this.stripOrTileOffsetsPosition = stripOrTileOffsetsPosition;
1225         this.stripOrTileByteCountsPosition = stripOrTileByteCountsPosition;
1226         this.lastPosition = lastPosition;
1227     }
1228 
1229     /**
1230      * Returns a {@code TIFFIFD} wherein all fields from the
1231      * {@code BaselineTIFFTagSet} are copied by value and all other
1232      * fields copied by reference.
1233      */
getShallowClone()1234     public TIFFIFD getShallowClone() {
1235         // Get the baseline TagSet.
1236         TIFFTagSet baselineTagSet = BaselineTIFFTagSet.getInstance();
1237 
1238         // If the baseline TagSet is not included just return.
1239         List<TIFFTagSet> tagSetList = getTagSetList();
1240         if(!tagSetList.contains(baselineTagSet)) {
1241             return this;
1242         }
1243 
1244         // Create a new object.
1245         TIFFIFD shallowClone = new TIFFIFD(tagSetList, getParentTag());
1246 
1247         // Get the tag numbers in the baseline set.
1248         Set<Integer> baselineTagNumbers = baselineTagSet.getTagNumbers();
1249 
1250         // Iterate over the fields in this IFD.
1251         Iterator<TIFFField> fields = iterator();
1252         while(fields.hasNext()) {
1253             // Get the next field.
1254             TIFFField field = fields.next();
1255 
1256             // Get its tag number.
1257             Integer tagNumber = Integer.valueOf(field.getTagNumber());
1258 
1259             // Branch based on membership in baseline set.
1260             TIFFField fieldClone;
1261             if(baselineTagNumbers.contains(tagNumber)) {
1262                 // Copy by value.
1263                 Object fieldData = field.getData();
1264 
1265                 int fieldType = field.getType();
1266 
1267                 try {
1268                     switch (fieldType) {
1269                     case TIFFTag.TIFF_BYTE:
1270                     case TIFFTag.TIFF_SBYTE:
1271                     case TIFFTag.TIFF_UNDEFINED:
1272                         fieldData = ((byte[])fieldData).clone();
1273                         break;
1274                     case TIFFTag.TIFF_ASCII:
1275                         fieldData = ((String[])fieldData).clone();
1276                         break;
1277                     case TIFFTag.TIFF_SHORT:
1278                         fieldData = ((char[])fieldData).clone();
1279                         break;
1280                     case TIFFTag.TIFF_LONG:
1281                     case TIFFTag.TIFF_IFD_POINTER:
1282                         fieldData = ((long[])fieldData).clone();
1283                         break;
1284                     case TIFFTag.TIFF_RATIONAL:
1285                         fieldData = ((long[][])fieldData).clone();
1286                         break;
1287                     case TIFFTag.TIFF_SSHORT:
1288                         fieldData = ((short[])fieldData).clone();
1289                         break;
1290                     case TIFFTag.TIFF_SLONG:
1291                         fieldData = ((int[])fieldData).clone();
1292                         break;
1293                     case TIFFTag.TIFF_SRATIONAL:
1294                         fieldData = ((int[][])fieldData).clone();
1295                         break;
1296                     case TIFFTag.TIFF_FLOAT:
1297                         fieldData = ((float[])fieldData).clone();
1298                         break;
1299                     case TIFFTag.TIFF_DOUBLE:
1300                         fieldData = ((double[])fieldData).clone();
1301                         break;
1302                     default:
1303                         // Shouldn't happen but do nothing ...
1304                     }
1305                 } catch(Exception e) {
1306                     // Ignore it and copy by reference ...
1307                 }
1308 
1309                 fieldClone = new TIFFField(field.getTag(), fieldType,
1310                                            field.getCount(), fieldData);
1311             } else {
1312                 // Copy by reference.
1313                 fieldClone = field;
1314             }
1315 
1316             // Add the field to the clone.
1317             shallowClone.addTIFFField(fieldClone);
1318         }
1319 
1320         // Set positions.
1321         shallowClone.setPositions(stripOrTileOffsetsPosition,
1322                                   stripOrTileByteCountsPosition,
1323                                   lastPosition);
1324 
1325         return shallowClone;
1326     }
1327 }
1328