1/*
2 * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package com.sun.imageio.plugins.tiff;
26
27import java.io.EOFException;
28import java.io.IOException;
29import java.nio.charset.StandardCharsets;
30import java.util.ArrayList;
31import java.util.Arrays;
32import java.util.Iterator;
33import java.util.List;
34import java.util.Set;
35import javax.imageio.IIOException;
36import javax.imageio.stream.ImageInputStream;
37import javax.imageio.stream.ImageOutputStream;
38import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
39import javax.imageio.plugins.tiff.TIFFDirectory;
40import javax.imageio.plugins.tiff.TIFFField;
41import javax.imageio.plugins.tiff.TIFFTag;
42import javax.imageio.plugins.tiff.TIFFTagSet;
43
44public class TIFFIFD extends TIFFDirectory {
45    private static final long MAX_SAMPLES_PER_PIXEL = 0xffff;
46    private static final long MAX_ASCII_SIZE  = 0xffff;
47
48    private long stripOrTileByteCountsPosition = -1;
49    private long stripOrTileOffsetsPosition = -1;
50    private long lastPosition = -1;
51
52    //
53    // A set of tag numbers corresponding to tags essential to decoding
54    // the image and metadata required to interpret its samples.
55    //
56    private static volatile Set<Integer> essentialTags = null;
57
58    private static void initializeEssentialTags() {
59        Set<Integer> tags = essentialTags;
60        if (tags == null) {
61            essentialTags = tags = Set.of(
62                BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE,
63                BaselineTIFFTagSet.TAG_COLOR_MAP,
64                BaselineTIFFTagSet.TAG_COMPRESSION,
65                BaselineTIFFTagSet.TAG_EXTRA_SAMPLES,
66                BaselineTIFFTagSet.TAG_FILL_ORDER,
67                BaselineTIFFTagSet.TAG_ICC_PROFILE,
68                BaselineTIFFTagSet.TAG_IMAGE_LENGTH,
69                BaselineTIFFTagSet.TAG_IMAGE_WIDTH,
70                BaselineTIFFTagSet.TAG_JPEG_AC_TABLES,
71                BaselineTIFFTagSet.TAG_JPEG_DC_TABLES,
72                BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT,
73                BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
74                BaselineTIFFTagSet.TAG_JPEG_PROC,
75                BaselineTIFFTagSet.TAG_JPEG_Q_TABLES,
76                BaselineTIFFTagSet.TAG_JPEG_RESTART_INTERVAL,
77                BaselineTIFFTagSet.TAG_JPEG_TABLES,
78                BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION,
79                BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION,
80                BaselineTIFFTagSet.TAG_PREDICTOR,
81                BaselineTIFFTagSet.TAG_REFERENCE_BLACK_WHITE,
82                BaselineTIFFTagSet.TAG_ROWS_PER_STRIP,
83                BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL,
84                BaselineTIFFTagSet.TAG_SAMPLE_FORMAT,
85                BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS,
86                BaselineTIFFTagSet.TAG_STRIP_OFFSETS,
87                BaselineTIFFTagSet.TAG_T4_OPTIONS,
88                BaselineTIFFTagSet.TAG_T6_OPTIONS,
89                BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS,
90                BaselineTIFFTagSet.TAG_TILE_LENGTH,
91                BaselineTIFFTagSet.TAG_TILE_OFFSETS,
92                BaselineTIFFTagSet.TAG_TILE_WIDTH,
93                BaselineTIFFTagSet.TAG_Y_CB_CR_COEFFICIENTS,
94                BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING
95            );
96        }
97    }
98
99    /**
100     * Converts a {@code TIFFDirectory} to a {@code TIFFIFD}.
101     */
102    public static TIFFIFD getDirectoryAsIFD(TIFFDirectory dir) {
103        if(dir instanceof TIFFIFD) {
104            return (TIFFIFD)dir;
105        }
106
107        TIFFIFD ifd = new TIFFIFD(Arrays.asList(dir.getTagSets()),
108                                  dir.getParentTag());
109        TIFFField[] fields = dir.getTIFFFields();
110        int numFields = fields.length;
111        for(int i = 0; i < numFields; i++) {
112            TIFFField f = fields[i];
113            TIFFTag tag = f.getTag();
114            if(tag.isIFDPointer()) {
115                TIFFDirectory subDir = null;
116                if (f.hasDirectory()) {
117                    subDir = f.getDirectory();
118                } else if (f.getData() instanceof TIFFDirectory) {
119                    subDir = (TIFFDirectory)f.getData();
120                }
121                if (subDir != null) {
122                    TIFFDirectory subIFD = getDirectoryAsIFD(subDir);
123                    f = new TIFFField(tag, f.getType(), (long)f.getCount(),
124                                      subIFD);
125                } else {
126                    f = null;
127                }
128            }
129            if (f != null) {
130                ifd.addTIFFField(f);
131            }
132        }
133
134        return ifd;
135    }
136
137    public static TIFFTag getTag(int tagNumber, List<TIFFTagSet> tagSets) {
138        Iterator<TIFFTagSet> iter = tagSets.iterator();
139        while (iter.hasNext()) {
140            TIFFTagSet tagSet = iter.next();
141            TIFFTag tag = tagSet.getTag(tagNumber);
142            if (tag != null) {
143                return tag;
144            }
145        }
146
147        return null;
148    }
149
150    public static TIFFTag getTag(String tagName, List<TIFFTagSet> tagSets) {
151        Iterator<TIFFTagSet> iter = tagSets.iterator();
152        while (iter.hasNext()) {
153            TIFFTagSet tagSet = iter.next();
154            TIFFTag tag = tagSet.getTag(tagName);
155            if (tag != null) {
156                return tag;
157            }
158        }
159
160        return null;
161    }
162
163    private static void writeTIFFFieldToStream(TIFFField field,
164                                               ImageOutputStream stream)
165        throws IOException {
166        int count = field.getCount();
167        Object data = field.getData();
168
169        switch (field.getType()) {
170        case TIFFTag.TIFF_ASCII:
171            for (int i = 0; i < count; i++) {
172                String s = ((String[])data)[i];
173                int length = s.length();
174                for (int j = 0; j < length; j++) {
175                    stream.writeByte(s.charAt(j) & 0xff);
176                }
177                stream.writeByte(0);
178            }
179            break;
180        case TIFFTag.TIFF_UNDEFINED:
181        case TIFFTag.TIFF_BYTE:
182        case TIFFTag.TIFF_SBYTE:
183            stream.write((byte[])data);
184            break;
185        case TIFFTag.TIFF_SHORT:
186            stream.writeChars((char[])data, 0, ((char[])data).length);
187            break;
188        case TIFFTag.TIFF_SSHORT:
189            stream.writeShorts((short[])data, 0, ((short[])data).length);
190            break;
191        case TIFFTag.TIFF_SLONG:
192            stream.writeInts((int[])data, 0, ((int[])data).length);
193            break;
194        case TIFFTag.TIFF_LONG:
195            for (int i = 0; i < count; i++) {
196                stream.writeInt((int)(((long[])data)[i]));
197            }
198            break;
199        case TIFFTag.TIFF_IFD_POINTER:
200            stream.writeInt(0); // will need to be backpatched
201            break;
202        case TIFFTag.TIFF_FLOAT:
203            stream.writeFloats((float[])data, 0, ((float[])data).length);
204            break;
205        case TIFFTag.TIFF_DOUBLE:
206            stream.writeDoubles((double[])data, 0, ((double[])data).length);
207            break;
208        case TIFFTag.TIFF_SRATIONAL:
209            for (int i = 0; i < count; i++) {
210                stream.writeInt(((int[][])data)[i][0]);
211                stream.writeInt(((int[][])data)[i][1]);
212            }
213            break;
214        case TIFFTag.TIFF_RATIONAL:
215            for (int i = 0; i < count; i++) {
216                long num = ((long[][])data)[i][0];
217                long den = ((long[][])data)[i][1];
218                stream.writeInt((int)num);
219                stream.writeInt((int)den);
220            }
221            break;
222        default:
223            // error
224        }
225    }
226
227    public TIFFIFD(List<TIFFTagSet> tagSets, TIFFTag parentTag) {
228        super(tagSets.toArray(new TIFFTagSet[tagSets.size()]),
229              parentTag);
230    }
231
232    public TIFFIFD(List<TIFFTagSet> tagSets) {
233        this(tagSets, null);
234    }
235
236    public List<TIFFTagSet> getTagSetList() {
237        return Arrays.asList(getTagSets());
238    }
239
240    /**
241     * Returns an {@code Iterator} over the TIFF fields. The
242     * traversal is in the order of increasing tag number.
243     */
244    // Note: the sort is guaranteed for low fields by the use of an
245    // array wherein the index corresponds to the tag number and for
246    // the high fields by the use of a TreeMap with tag number keys.
247    public Iterator<TIFFField> iterator() {
248        return Arrays.asList(getTIFFFields()).iterator();
249    }
250
251    /**
252     * Read the value of a field. The {@code data} parameter should be
253     * an array of length 1 of Object.
254     *
255     * @param stream the input stream
256     * @param type the type as read from the stream
257     * @param count the count read from the stream
258     * @param data a container for the data
259     * @return the updated count
260     * @throws IOException
261     */
262    private static int readFieldValue(ImageInputStream stream,
263        int type, int count, Object[] data) throws IOException {
264        Object obj;
265
266        switch (type) {
267            case TIFFTag.TIFF_BYTE:
268            case TIFFTag.TIFF_SBYTE:
269            case TIFFTag.TIFF_UNDEFINED:
270            case TIFFTag.TIFF_ASCII:
271                byte[] bvalues = new byte[count];
272                stream.readFully(bvalues, 0, count);
273
274                if (type == TIFFTag.TIFF_ASCII) {
275                    // Can be multiple strings
276                    ArrayList<String> v = new ArrayList<>();
277                    boolean inString = false;
278                    int prevIndex = 0;
279                    for (int index = 0; index <= count; index++) {
280                        if (index < count && bvalues[index] != 0) {
281                            if (!inString) {
282                                // start of string
283                                prevIndex = index;
284                                inString = true;
285                            }
286                        } else { // null or special case at end of string
287                            if (inString) {
288                                // end of string
289                                String s = new String(bvalues, prevIndex,
290                                        index - prevIndex,
291                                        StandardCharsets.US_ASCII);
292                                v.add(s);
293                                inString = false;
294                            }
295                        }
296                    }
297
298                    count = v.size();
299                    String[] strings;
300                    if (count != 0) {
301                        strings = new String[count];
302                        for (int c = 0; c < count; c++) {
303                            strings[c] = v.get(c);
304                        }
305                    } else {
306                        // This case has been observed when the value of
307                        // 'count' recorded in the field is non-zero but
308                        // the value portion contains all nulls.
309                        count = 1;
310                        strings = new String[]{""};
311                    }
312
313                    obj = strings;
314                } else {
315                    obj = bvalues;
316                }
317                break;
318
319            case TIFFTag.TIFF_SHORT:
320                char[] cvalues = new char[count];
321                for (int j = 0; j < count; j++) {
322                    cvalues[j] = (char) (stream.readUnsignedShort());
323                }
324                obj = cvalues;
325                break;
326
327            case TIFFTag.TIFF_LONG:
328            case TIFFTag.TIFF_IFD_POINTER:
329                long[] lvalues = new long[count];
330                for (int j = 0; j < count; j++) {
331                    lvalues[j] = stream.readUnsignedInt();
332                }
333                obj = lvalues;
334                break;
335
336            case TIFFTag.TIFF_RATIONAL:
337                long[][] llvalues = new long[count][2];
338                for (int j = 0; j < count; j++) {
339                    llvalues[j][0] = stream.readUnsignedInt();
340                    llvalues[j][1] = stream.readUnsignedInt();
341                }
342                obj = llvalues;
343                break;
344
345            case TIFFTag.TIFF_SSHORT:
346                short[] svalues = new short[count];
347                for (int j = 0; j < count; j++) {
348                    svalues[j] = stream.readShort();
349                }
350                obj = svalues;
351                break;
352
353            case TIFFTag.TIFF_SLONG:
354                int[] ivalues = new int[count];
355                for (int j = 0; j < count; j++) {
356                    ivalues[j] = stream.readInt();
357                }
358                obj = ivalues;
359                break;
360
361            case TIFFTag.TIFF_SRATIONAL:
362                int[][] iivalues = new int[count][2];
363                for (int j = 0; j < count; j++) {
364                    iivalues[j][0] = stream.readInt();
365                    iivalues[j][1] = stream.readInt();
366                }
367                obj = iivalues;
368                break;
369
370            case TIFFTag.TIFF_FLOAT:
371                float[] fvalues = new float[count];
372                for (int j = 0; j < count; j++) {
373                    fvalues[j] = stream.readFloat();
374                }
375                obj = fvalues;
376                break;
377
378            case TIFFTag.TIFF_DOUBLE:
379                double[] dvalues = new double[count];
380                for (int j = 0; j < count; j++) {
381                    dvalues[j] = stream.readDouble();
382                }
383                obj = dvalues;
384                break;
385
386            default:
387                obj = null;
388                break;
389        }
390
391        data[0] = obj;
392
393        return count;
394    }
395
396    //
397    // Class to represent an IFD entry where the actual content is at an offset
398    // in the stream somewhere outside the IFD itself. This occurs when the
399    // value cannot be contained within four bytes. Seeking is required to read
400    // such field values.
401    //
402    private static class TIFFIFDEntry {
403        public final TIFFTag tag;
404        public final int type;
405        public final int count;
406        public final long offset;
407
408        TIFFIFDEntry(TIFFTag tag, int type, int count, long offset) {
409            this.tag = tag;
410            this.type = type;
411            this.count = count;
412            this.offset = offset;
413        }
414    }
415
416    //
417    // Retrieve the value of a baseline field as a long.
418    //
419    private long getFieldAsLong(int tagNumber) {
420        TIFFField f = getTIFFField(tagNumber);
421        return f == null ? -1 : f.getAsLong(0);
422    }
423
424    //
425    // Retrieve the value of a baseline field as an int.
426    //
427    private int getFieldAsInt(int tagNumber) {
428        TIFFField f = getTIFFField(tagNumber);
429        return f == null ? -1 : f.getAsInt(0);
430    }
431
432    //
433    // Calculate the number of bytes in each strip or tile. This method
434    // is to be used if and only if no fields exist which provide this
435    // information. The parameter must be empty and if the method succeeds
436    // will contain a single element.
437    //
438    private boolean calculateByteCounts(int expectedSize,
439        List<TIFFField> byteCounts) {
440        if (!byteCounts.isEmpty()) {
441            throw new IllegalArgumentException("byteCounts is not empty");
442        }
443
444        // must be interleaved
445        if (getFieldAsInt(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION) ==
446            BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) {
447            return false;
448        }
449
450        // must be uncompressed
451        if (getFieldAsInt(BaselineTIFFTagSet.TAG_COMPRESSION) !=
452            BaselineTIFFTagSet.COMPRESSION_NONE) {
453            return false;
454        }
455
456        // must have image dimensions
457        long w = getFieldAsLong(BaselineTIFFTagSet.TAG_IMAGE_WIDTH);
458        if (w < 0) {
459            return false;
460        }
461        long h = getFieldAsLong(BaselineTIFFTagSet.TAG_IMAGE_LENGTH);
462        if (h < 0) {
463            return false;
464        }
465
466        long tw = getFieldAsLong(BaselineTIFFTagSet.TAG_TILE_WIDTH);
467        if (tw < 0) {
468            tw = w;
469        }
470        long th = getFieldAsLong(BaselineTIFFTagSet.TAG_TILE_LENGTH);
471        if (th < 0) {
472            th = getFieldAsLong(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP);
473            if (th < 0) {
474                th = h;
475            }
476        }
477
478        int[] bitsPerSample = null;
479        TIFFField f = getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
480        if (f != null) {
481            bitsPerSample = f.getAsInts();
482        } else {
483            int samplesPerPixel =
484                getFieldAsInt(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL);
485            if (samplesPerPixel < 0) {
486                samplesPerPixel = 1;
487            }
488            bitsPerSample = new int[samplesPerPixel];
489            Arrays.fill(bitsPerSample, 8);
490        }
491
492        int bitsPerPixel = 0;
493        for (int bps : bitsPerSample) {
494            bitsPerPixel += bps;
495        }
496
497        int bytesPerRow = (int)(tw*bitsPerPixel + 7)/8;
498        int bytesPerPacket = (int)th*bytesPerRow;
499
500        long nx = (w + tw - 1)/tw;
501        long ny = (h + th - 1)/th;
502
503        if (nx*ny != expectedSize) {
504            return false;
505        }
506
507        boolean isTiled =
508            getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS) != null;
509
510        int tagNumber;
511        if (isTiled) {
512            tagNumber = BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS;
513        } else {
514            tagNumber = BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS;
515        }
516
517        TIFFTag t = BaselineTIFFTagSet.getInstance().getTag(tagNumber);
518        f = getTIFFField(tagNumber);
519        if (f != null) {
520            removeTIFFField(tagNumber);
521        }
522
523        int numPackets = (int)(nx*ny);
524        long[] packetByteCounts = new long[numPackets];
525        Arrays.fill(packetByteCounts, bytesPerPacket);
526
527        // if the strip or tile width does not exceed the image width and the
528        // image height is not a multiple of the strip or tile height, then
529        // truncate the estimate of the byte count of the last strip to avoid
530        // reading past the end of the data
531        if (tw <= w && h % th != 0) {
532            int numRowsInLastStrip = (int)(h - (ny - 1)*th);
533            packetByteCounts[numPackets - 1] = numRowsInLastStrip*bytesPerRow;
534        }
535
536        f = new TIFFField(t, TIFFTag.TIFF_LONG, numPackets, packetByteCounts);
537        addTIFFField(f);
538        byteCounts.add(f);
539
540        return true;
541    }
542
543    //
544    // Verify that data pointed to outside of the IFD itself are within the
545    // stream. To be called after all fields have been read and populated.
546    //
547    private void checkFieldOffsets(long streamLength) throws IIOException {
548        if (streamLength < 0) {
549            return;
550        }
551
552        // StripOffsets
553        List<TIFFField> offsets = new ArrayList<>();
554        TIFFField f = getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
555        int count = 0;
556        if (f != null) {
557            count = f.getCount();
558            offsets.add(f);
559        }
560
561        // TileOffsets
562        f = getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS);
563        if (f != null) {
564            int sz = offsets.size();
565            int newCount = f.getCount();
566            if (sz > 0 && newCount != count) {
567                throw new IIOException
568                    ("StripOffsets count != TileOffsets count");
569            }
570
571            if (sz == 0) {
572                count = newCount;
573            }
574            offsets.add(f);
575        }
576
577        List<TIFFField> byteCounts = new ArrayList<>();
578        if (offsets.size() > 0) {
579            // StripByteCounts
580            f = getTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS);
581            if (f != null) {
582                if (f.getCount() != count) {
583                    throw new IIOException
584                        ("StripByteCounts count != number of offsets");
585                }
586                byteCounts.add(f);
587            }
588
589            // TileByteCounts
590            f = getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS);
591            if (f != null) {
592                if (f.getCount() != count) {
593                    throw new IIOException
594                        ("TileByteCounts count != number of offsets");
595                }
596                byteCounts.add(f);
597            }
598
599            if (byteCounts.size() > 0) {
600                for (TIFFField offset : offsets) {
601                    for (TIFFField byteCount : byteCounts) {
602                        for (int i = 0; i < count; i++) {
603                            long dataOffset = offset.getAsLong(i);
604                            long dataByteCount = byteCount.getAsLong(i);
605                            if (dataOffset + dataByteCount > streamLength) {
606                                throw new IIOException
607                                    ("Data segment out of stream");
608                            }
609                        }
610                    }
611                }
612            }
613        }
614
615        // JPEGInterchangeFormat and JPEGInterchangeFormatLength
616        TIFFField jpegOffset =
617            getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT);
618        if (jpegOffset != null) {
619            TIFFField jpegLength =
620                getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
621            if (jpegLength != null) {
622                if (jpegOffset.getAsLong(0) + jpegLength.getAsLong(0)
623                    > streamLength) {
624                    throw new IIOException
625                        ("JPEGInterchangeFormat data out of stream");
626                }
627            }
628        }
629
630        // Ensure there is at least a data pointer for JPEG interchange format or
631        // both data offsets and byte counts for other compression types.
632        if (jpegOffset == null
633            && (offsets.size() == 0 || byteCounts.size() == 0)) {
634            boolean throwException = true;
635            if (offsets.size() != 0 && byteCounts.size() == 0) {
636                // Attempt to calculate missing byte counts
637                int expectedSize = offsets.get(0).getCount();
638                throwException =
639                    !calculateByteCounts(expectedSize, byteCounts);
640            }
641            if (throwException) {
642                throw new IIOException
643                    ("Insufficient data offsets or byte counts");
644            }
645        }
646
647        // JPEGQTables - one 64-byte table for each offset.
648        f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_Q_TABLES);
649        if (f != null) {
650            long[] tableOffsets = f.getAsLongs();
651            for (long off : tableOffsets) {
652                if (off + 64 > streamLength) {
653                    throw new IIOException("JPEGQTables data out of stream");
654                }
655            }
656        }
657
658        // JPEGDCTables
659        f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_DC_TABLES);
660        if (f != null) {
661            long[] tableOffsets = f.getAsLongs();
662            for (long off : tableOffsets) {
663                if (off + 16 > streamLength) {
664                    throw new IIOException("JPEGDCTables data out of stream");
665                }
666            }
667        }
668
669        // JPEGACTables
670        f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_AC_TABLES);
671        if (f != null) {
672            long[] tableOffsets = f.getAsLongs();
673            for (long off : tableOffsets) {
674                if (off + 16 > streamLength) {
675                    throw new IIOException("JPEGACTables data out of stream");
676                }
677            }
678        }
679    }
680
681    // Stream position initially at beginning, left at end
682    // if readUnknownTags is false, do not load fields for which
683    // a tag cannot be found in an allowed TagSet.
684    public void initialize(ImageInputStream stream, boolean isPrimaryIFD,
685        boolean ignoreMetadata, boolean readUnknownTags) throws IOException {
686
687        removeTIFFFields();
688
689        long streamLength = stream.length();
690        boolean haveStreamLength = streamLength != -1;
691
692        List<TIFFTagSet> tagSetList = getTagSetList();
693
694        // Configure essential tag variables if this is the primary IFD and
695        // either all metadata are being ignored, or metadata are not being
696        // ignored but both unknown tags are being ignored and the tag set
697        // list does not contain the baseline tags.
698        boolean ensureEssentialTags = false;
699        TIFFTagSet baselineTagSet = null;
700        if (isPrimaryIFD &&
701            (ignoreMetadata ||
702             (!readUnknownTags &&
703              !tagSetList.contains(BaselineTIFFTagSet.getInstance())))) {
704            ensureEssentialTags = true;
705            initializeEssentialTags();
706            baselineTagSet = BaselineTIFFTagSet.getInstance();
707        }
708
709        List<Object> entries = new ArrayList<>();
710        Object[] entryData = new Object[1]; // allocate once for later reuse.
711
712        // Read the IFD entries, loading the field values which are no more than
713        // four bytes long, and storing the 4-byte offsets for the others.
714        int numEntries = stream.readUnsignedShort();
715        for (int i = 0; i < numEntries; i++) {
716            // Read tag number, value type, and value count.
717            int tagNumber = stream.readUnsignedShort();
718            int type = stream.readUnsignedShort();
719            int sizeOfType;
720            try {
721                sizeOfType = TIFFTag.getSizeOfType(type);
722            } catch (IllegalArgumentException ignored) {
723                // Continue with the next IFD entry.
724                stream.skipBytes(4);
725                continue;
726            }
727            long longCount = stream.readUnsignedInt();
728
729            // Get the associated TIFFTag.
730            TIFFTag tag = getTag(tagNumber, tagSetList);
731
732            if (tag == null && ensureEssentialTags
733                && essentialTags.contains(tagNumber)) {
734                tag = baselineTagSet.getTag(tagNumber);
735            }
736
737            // Ignore non-essential fields, unknown fields unless forcibly
738            // being read, fields with unknown type, and fields
739            // with count out of int range.
740            if((ignoreMetadata &&
741                (!ensureEssentialTags || !essentialTags.contains(tagNumber)))
742                || (tag == null && !readUnknownTags)
743                || (tag != null && !tag.isDataTypeOK(type))
744                || longCount > Integer.MAX_VALUE) {
745                // Skip the value/offset so as to leave the stream
746                // position at the start of the next IFD entry.
747                stream.skipBytes(4);
748
749                // Continue with the next IFD entry.
750                continue;
751            }
752
753            int count = (int)longCount;
754
755            if (tag == null) {
756                tag = new TIFFTag(TIFFTag.UNKNOWN_TAG_NAME, tagNumber,
757                    1 << type, count);
758            } else {
759                int expectedCount = tag.getCount();
760                if (expectedCount > 0) {
761                    // If the tag count is positive then the tag defines a
762                    // specific, fixed count that the field must match.
763                    if (count != expectedCount) {
764                        throw new IIOException("Unexpected count "
765                            + count + " for " + tag.getName() + " field");
766                    }
767                } else if (type == TIFFTag.TIFF_ASCII) {
768                    // Clamp the size of ASCII fields of unspecified length
769                    // to a maximum value.
770                    int asciiSize = TIFFTag.getSizeOfType(TIFFTag.TIFF_ASCII);
771                    if (count*asciiSize > MAX_ASCII_SIZE) {
772                        count = (int)(MAX_ASCII_SIZE/asciiSize);
773                    }
774                }
775            }
776
777            long longSize = longCount*sizeOfType;
778            if (longSize > Integer.MAX_VALUE) {
779                // Continue with the next IFD entry.
780                stream.skipBytes(4);
781                continue;
782            }
783            int size = (int)longSize;
784
785            if (size > 4 || tag.isIFDPointer()) {
786                // The IFD entry value is a pointer to the actual field value.
787                long offset = stream.readUnsignedInt();
788
789                // Check whether the the field value is within the stream.
790                if (haveStreamLength && offset + size > streamLength) {
791                    continue;
792                }
793
794                // Add a TIFFIFDEntry as a placeholder. This avoids a mark,
795                // seek to the data, and a reset.
796                entries.add(new TIFFIFDEntry(tag, type, count, offset));
797            } else {
798                // The IFD entry value is the actual field value of no more than
799                // four bytes.
800                Object obj = null;
801                try {
802                    // Read the field value and update the count.
803                    count = readFieldValue(stream, type, count, entryData);
804                    obj = entryData[0];
805                } catch (EOFException eofe) {
806                    // The TIFF 6.0 fields have tag numbers less than or equal
807                    // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright).
808                    // If there is an error reading a baseline tag, then re-throw
809                    // the exception and fail; otherwise continue with the next
810                    // field.
811                    if (BaselineTIFFTagSet.getInstance().getTag(tagNumber) == null) {
812                        throw eofe;
813                    }
814                }
815
816                // If the field value is smaller than four bytes then skip
817                // the remaining, unused bytes.
818                if (size < 4) {
819                    stream.skipBytes(4 - size);
820                }
821
822                // Add the populated TIFFField to the list of entries.
823                entries.add(new TIFFField(tag, type, count, obj));
824            }
825        }
826
827        // After reading the IFD entries the stream is positioned at an unsigned
828        // four byte integer containing either the offset of the next IFD or
829        // zero if this is the last IFD.
830        long nextIFDOffset = stream.getStreamPosition();
831
832        Object[] fieldData = new Object[1];
833        for (Object entry : entries) {
834            if (entry instanceof TIFFField) {
835                // Add the populated field directly.
836                addTIFFField((TIFFField)entry);
837            } else {
838                TIFFIFDEntry e = (TIFFIFDEntry)entry;
839                TIFFTag tag = e.tag;
840                int tagNumber = tag.getNumber();
841                int type = e.type;
842                int count = e.count;
843
844                stream.seek(e.offset);
845
846                if (tag.isIFDPointer()) {
847                    List<TIFFTagSet> tagSets = new ArrayList<TIFFTagSet>(1);
848                    tagSets.add(tag.getTagSet());
849                    TIFFIFD subIFD = new TIFFIFD(tagSets);
850
851                    subIFD.initialize(stream, false, ignoreMetadata,
852                                      readUnknownTags);
853                    TIFFField f = new TIFFField(tag, type, e.offset, subIFD);
854                    addTIFFField(f);
855                } else {
856                    if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS
857                            || tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS
858                            || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) {
859                        this.stripOrTileByteCountsPosition
860                                = stream.getStreamPosition();
861                    } else if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_OFFSETS
862                            || tagNumber == BaselineTIFFTagSet.TAG_TILE_OFFSETS
863                            || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) {
864                        this.stripOrTileOffsetsPosition
865                                = stream.getStreamPosition();
866                    }
867
868                    Object obj = null;
869                    try {
870                        count = readFieldValue(stream, type, count, fieldData);
871                        obj = fieldData[0];
872                    } catch (EOFException eofe) {
873                        // The TIFF 6.0 fields have tag numbers less than or equal
874                        // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright).
875                        // If there is an error reading a baseline tag, then re-throw
876                        // the exception and fail; otherwise continue with the next
877                        // field.
878                        if (BaselineTIFFTagSet.getInstance().getTag(tagNumber) == null) {
879                            throw eofe;
880                        }
881                    }
882
883                    if (obj == null) {
884                        continue;
885                    }
886
887                    TIFFField f = new TIFFField(tag, type, count, obj);
888                    addTIFFField(f);
889                }
890            }
891        }
892
893        if(isPrimaryIFD && haveStreamLength) {
894            checkFieldOffsets(streamLength);
895        }
896
897        stream.seek(nextIFDOffset);
898        this.lastPosition = stream.getStreamPosition();
899    }
900
901    public void writeToStream(ImageOutputStream stream)
902        throws IOException {
903
904        int numFields = getNumTIFFFields();
905        stream.writeShort(numFields);
906
907        long nextSpace = stream.getStreamPosition() + 12*numFields + 4;
908
909        Iterator<TIFFField> iter = iterator();
910        while (iter.hasNext()) {
911            TIFFField f = iter.next();
912
913            TIFFTag tag = f.getTag();
914
915            int type = f.getType();
916            int count = f.getCount();
917
918            // Deal with unknown tags
919            if (type == 0) {
920                type = TIFFTag.TIFF_UNDEFINED;
921            }
922            int size = count*TIFFTag.getSizeOfType(type);
923
924            if (type == TIFFTag.TIFF_ASCII) {
925                int chars = 0;
926                for (int i = 0; i < count; i++) {
927                    chars += f.getAsString(i).length() + 1;
928                }
929                count = chars;
930                size = count;
931            }
932
933            int tagNumber = f.getTagNumber();
934            stream.writeShort(tagNumber);
935            stream.writeShort(type);
936            stream.writeInt(count);
937
938            // Write a dummy value to fill space
939            stream.writeInt(0);
940            stream.mark(); // Mark beginning of next field
941            stream.skipBytes(-4);
942
943            long pos;
944
945            if (size > 4 || tag.isIFDPointer()) {
946                // Ensure IFD or value is written on a word boundary
947                nextSpace = (nextSpace + 3) & ~0x3;
948
949                stream.writeInt((int)nextSpace);
950                stream.seek(nextSpace);
951                pos = nextSpace;
952
953                if (tag.isIFDPointer() && f.hasDirectory()) {
954                    TIFFIFD subIFD = getDirectoryAsIFD(f.getDirectory());
955                    subIFD.writeToStream(stream);
956                    nextSpace = subIFD.lastPosition;
957                } else {
958                    writeTIFFFieldToStream(f, stream);
959                    nextSpace = stream.getStreamPosition();
960                }
961            } else {
962                pos = stream.getStreamPosition();
963                writeTIFFFieldToStream(f, stream);
964            }
965
966            // If we are writing the data for the
967            // StripByteCounts, TileByteCounts, StripOffsets,
968            // TileOffsets, JPEGInterchangeFormat, or
969            // JPEGInterchangeFormatLength fields, record the current stream
970            // position for backpatching
971            if (tagNumber ==
972                BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS ||
973                tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS ||
974                tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) {
975                this.stripOrTileByteCountsPosition = pos;
976            } else if (tagNumber ==
977                       BaselineTIFFTagSet.TAG_STRIP_OFFSETS ||
978                       tagNumber ==
979                       BaselineTIFFTagSet.TAG_TILE_OFFSETS ||
980                       tagNumber ==
981                       BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) {
982                this.stripOrTileOffsetsPosition = pos;
983            }
984
985            stream.reset(); // Go to marked position of next field
986        }
987
988        this.lastPosition = nextSpace;
989    }
990
991    public long getStripOrTileByteCountsPosition() {
992        return stripOrTileByteCountsPosition;
993    }
994
995    public long getStripOrTileOffsetsPosition() {
996        return stripOrTileOffsetsPosition;
997    }
998
999    public long getLastPosition() {
1000        return lastPosition;
1001    }
1002
1003    void setPositions(long stripOrTileOffsetsPosition,
1004                      long stripOrTileByteCountsPosition,
1005                      long lastPosition) {
1006        this.stripOrTileOffsetsPosition = stripOrTileOffsetsPosition;
1007        this.stripOrTileByteCountsPosition = stripOrTileByteCountsPosition;
1008        this.lastPosition = lastPosition;
1009    }
1010
1011    /**
1012     * Returns a {@code TIFFIFD} wherein all fields from the
1013     * {@code BaselineTIFFTagSet} are copied by value and all other
1014     * fields copied by reference.
1015     */
1016    public TIFFIFD getShallowClone() {
1017        // Get the baseline TagSet.
1018        TIFFTagSet baselineTagSet = BaselineTIFFTagSet.getInstance();
1019
1020        // If the baseline TagSet is not included just return.
1021        List<TIFFTagSet> tagSetList = getTagSetList();
1022        if(!tagSetList.contains(baselineTagSet)) {
1023            return this;
1024        }
1025
1026        // Create a new object.
1027        TIFFIFD shallowClone = new TIFFIFD(tagSetList, getParentTag());
1028
1029        // Get the tag numbers in the baseline set.
1030        Set<Integer> baselineTagNumbers = baselineTagSet.getTagNumbers();
1031
1032        // Iterate over the fields in this IFD.
1033        Iterator<TIFFField> fields = iterator();
1034        while(fields.hasNext()) {
1035            // Get the next field.
1036            TIFFField field = fields.next();
1037
1038            // Get its tag number.
1039            Integer tagNumber = Integer.valueOf(field.getTagNumber());
1040
1041            // Branch based on membership in baseline set.
1042            TIFFField fieldClone;
1043            if(baselineTagNumbers.contains(tagNumber)) {
1044                // Copy by value.
1045                Object fieldData = field.getData();
1046
1047                int fieldType = field.getType();
1048
1049                try {
1050                    switch (fieldType) {
1051                    case TIFFTag.TIFF_BYTE:
1052                    case TIFFTag.TIFF_SBYTE:
1053                    case TIFFTag.TIFF_UNDEFINED:
1054                        fieldData = ((byte[])fieldData).clone();
1055                        break;
1056                    case TIFFTag.TIFF_ASCII:
1057                        fieldData = ((String[])fieldData).clone();
1058                        break;
1059                    case TIFFTag.TIFF_SHORT:
1060                        fieldData = ((char[])fieldData).clone();
1061                        break;
1062                    case TIFFTag.TIFF_LONG:
1063                    case TIFFTag.TIFF_IFD_POINTER:
1064                        fieldData = ((long[])fieldData).clone();
1065                        break;
1066                    case TIFFTag.TIFF_RATIONAL:
1067                        fieldData = ((long[][])fieldData).clone();
1068                        break;
1069                    case TIFFTag.TIFF_SSHORT:
1070                        fieldData = ((short[])fieldData).clone();
1071                        break;
1072                    case TIFFTag.TIFF_SLONG:
1073                        fieldData = ((int[])fieldData).clone();
1074                        break;
1075                    case TIFFTag.TIFF_SRATIONAL:
1076                        fieldData = ((int[][])fieldData).clone();
1077                        break;
1078                    case TIFFTag.TIFF_FLOAT:
1079                        fieldData = ((float[])fieldData).clone();
1080                        break;
1081                    case TIFFTag.TIFF_DOUBLE:
1082                        fieldData = ((double[])fieldData).clone();
1083                        break;
1084                    default:
1085                        // Shouldn't happen but do nothing ...
1086                    }
1087                } catch(Exception e) {
1088                    // Ignore it and copy by reference ...
1089                }
1090
1091                fieldClone = new TIFFField(field.getTag(), fieldType,
1092                                           field.getCount(), fieldData);
1093            } else {
1094                // Copy by reference.
1095                fieldClone = field;
1096            }
1097
1098            // Add the field to the clone.
1099            shallowClone.addTIFFField(fieldClone);
1100        }
1101
1102        // Set positions.
1103        shallowClone.setPositions(stripOrTileOffsetsPosition,
1104                                  stripOrTileByteCountsPosition,
1105                                  lastPosition);
1106
1107        return shallowClone;
1108    }
1109}
1110