1e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor/*
2e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * Copyright (C) 2012 The Android Open Source Project
3e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *
4e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * Licensed under the Apache License, Version 2.0 (the "License");
5e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * you may not use this file except in compliance with the License.
6e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * You may obtain a copy of the License at
7e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *
8e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *      http://www.apache.org/licenses/LICENSE-2.0
9e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *
10e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * Unless required by applicable law or agreed to in writing, software
11e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * distributed under the License is distributed on an "AS IS" BASIS,
12e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * See the License for the specific language governing permissions and
14e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * limitations under the License.
15e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor */
16e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
17e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorpackage com.android.mms.exif;
18e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
19e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorimport android.util.Log;
20e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
21e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorimport java.io.IOException;
22e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorimport java.io.InputStream;
23e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorimport java.nio.ByteOrder;
24e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorimport java.nio.charset.Charset;
25e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorimport java.util.Map.Entry;
26e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorimport java.util.TreeMap;
27e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
28e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor/**
29e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * This class provides a low-level EXIF parsing API. Given a JPEG format
30e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * InputStream, the caller can request which IFD's to read via
31e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * {@link #parse(InputStream, int)} with given options.
32e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * <p>
33e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * Below is an example of getting EXIF data from IFD 0 and EXIF IFD using the
34e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * parser.
35e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *
36e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * <pre>
37e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * void parse() {
38e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *     ExifParser parser = ExifParser.parse(mImageInputStream,
39e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *             ExifParser.OPTION_IFD_0 | ExifParser.OPTIONS_IFD_EXIF);
40e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *     int event = parser.next();
41e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *     while (event != ExifParser.EVENT_END) {
42e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *         switch (event) {
43e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *             case ExifParser.EVENT_START_OF_IFD:
44e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *                 break;
45e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *             case ExifParser.EVENT_NEW_TAG:
46e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *                 ExifTag tag = parser.getTag();
47e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *                 if (!tag.hasValue()) {
48e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *                     parser.registerForTagValue(tag);
49e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *                 } else {
50e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *                     processTag(tag);
51e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *                 }
52e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *                 break;
53e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *             case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
54e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *                 tag = parser.getTag();
55e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *                 if (tag.getDataType() != ExifTag.TYPE_UNDEFINED) {
56e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *                     processTag(tag);
57e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *                 }
58e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *                 break;
59e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *         }
60e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *         event = parser.next();
61e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *     }
62e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * }
63e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *
64e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * void processTag(ExifTag tag) {
65e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *     // process the tag as you like.
66e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * }
67e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * </pre>
68e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor */
69e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorpublic class ExifParser {
70e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final boolean LOGV = false;
71e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final String TAG = "ExifParser";
72e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
73e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * When the parser reaches a new IFD area. Call {@link #getCurrentIfd()} to
74e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * know which IFD we are in.
75e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
76e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    public static final int EVENT_START_OF_IFD = 0;
77e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
78e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * When the parser reaches a new tag. Call {@link #getTag()}to get the
79e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * corresponding tag.
80e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
81e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    public static final int EVENT_NEW_TAG = 1;
82e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
83e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * When the parser reaches the value area of tag that is registered by
84e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * {@link #registerForTagValue(ExifTag)} previously. Call {@link #getTag()}
85e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * to get the corresponding tag.
86e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
87e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    public static final int EVENT_VALUE_OF_REGISTERED_TAG = 2;
88e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
89e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
90e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * When the parser reaches the compressed image area.
91e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
92e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    public static final int EVENT_COMPRESSED_IMAGE = 3;
93e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
94e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * When the parser reaches the uncompressed image strip. Call
95e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * {@link #getStripIndex()} to get the index of the strip.
96e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     *
97e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #getStripIndex()
98e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #getStripCount()
99e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
100e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    public static final int EVENT_UNCOMPRESSED_STRIP = 4;
101e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
102e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * When there is nothing more to parse.
103e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
104e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    public static final int EVENT_END = 5;
105e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
106e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
107e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Option bit to request to parse IFD0.
108e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
109e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    public static final int OPTION_IFD_0 = 1 << 0;
110e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
111e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Option bit to request to parse IFD1.
112e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
113e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    public static final int OPTION_IFD_1 = 1 << 1;
114e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
115e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Option bit to request to parse Exif-IFD.
116e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
117e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    public static final int OPTION_IFD_EXIF = 1 << 2;
118e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
119e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Option bit to request to parse GPS-IFD.
120e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
121e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    public static final int OPTION_IFD_GPS = 1 << 3;
122e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
123e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Option bit to request to parse Interoperability-IFD.
124e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
125e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    public static final int OPTION_IFD_INTEROPERABILITY = 1 << 4;
126e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
127e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Option bit to request to parse thumbnail.
128e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
129e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    public static final int OPTION_THUMBNAIL = 1 << 5;
130e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
131e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected static final int EXIF_HEADER = 0x45786966; // EXIF header "Exif"
132e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected static final short EXIF_HEADER_TAIL = (short) 0x0000; // EXIF header in APP1
133e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
134e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    // TIFF header
135e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected static final short LITTLE_ENDIAN_TAG = (short) 0x4949; // "II"
136e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected static final short BIG_ENDIAN_TAG = (short) 0x4d4d; // "MM"
137e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected static final short TIFF_HEADER_TAIL = 0x002A;
138e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
139e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected static final int TAG_SIZE = 12;
140e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected static final int OFFSET_SIZE = 2;
141e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
142e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final Charset US_ASCII = Charset.forName("US-ASCII");
143e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
144e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected static final int DEFAULT_IFD0_OFFSET = 8;
145e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
146e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private final CountedDataInputStream mTiffStream;
147e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private final int mOptions;
148e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private int mIfdStartOffset = 0;
149e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private int mNumOfTagInIfd = 0;
150e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private int mIfdType;
151e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private ExifTag mTag;
152e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private ImageEvent mImageEvent;
153e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private int mStripCount;
154e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private ExifTag mStripSizeTag;
155e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private ExifTag mJpegSizeTag;
156e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private boolean mNeedToParseOffsetsInCurrentIfd;
157e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private boolean mContainExifData = false;
158e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private int mApp1End;
159e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private int mOffsetToApp1EndFromSOF = 0;
160e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private byte[] mDataAboveIfd0;
161e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private int mIfd0Position;
162e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private int mTiffStartPosition;
163e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private final ExifInterface mInterface;
164e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
165e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final short TAG_EXIF_IFD = ExifInterface
166e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            .getTrueTagKey(ExifInterface.TAG_EXIF_IFD);
167e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final short TAG_GPS_IFD = ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD);
168e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final short TAG_INTEROPERABILITY_IFD = ExifInterface
169e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            .getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD);
170e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final short TAG_JPEG_INTERCHANGE_FORMAT = ExifInterface
171e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
172e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final short TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = ExifInterface
173e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
174e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final short TAG_STRIP_OFFSETS = ExifInterface
175e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            .getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS);
176e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final short TAG_STRIP_BYTE_COUNTS = ExifInterface
177e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            .getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS);
178e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
179e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private final TreeMap<Integer, Object> mCorrespondingEvent = new TreeMap<Integer, Object>();
180e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
181e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private boolean isIfdRequested(int ifdType) {
182e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        switch (ifdType) {
183e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case IfdId.TYPE_IFD_0:
184e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                return (mOptions & OPTION_IFD_0) != 0;
185e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case IfdId.TYPE_IFD_1:
186e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                return (mOptions & OPTION_IFD_1) != 0;
187e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case IfdId.TYPE_IFD_EXIF:
188e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                return (mOptions & OPTION_IFD_EXIF) != 0;
189e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case IfdId.TYPE_IFD_GPS:
190e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                return (mOptions & OPTION_IFD_GPS) != 0;
191e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case IfdId.TYPE_IFD_INTEROPERABILITY:
192e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                return (mOptions & OPTION_IFD_INTEROPERABILITY) != 0;
193e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
194e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return false;
195e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
196e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
197e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private boolean isThumbnailRequested() {
198e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return (mOptions & OPTION_THUMBNAIL) != 0;
199e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
200e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
201e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private ExifParser(InputStream inputStream, int options, ExifInterface iRef)
202e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            throws IOException, ExifInvalidFormatException {
203e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (inputStream == null) {
204e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            throw new IOException("Null argument inputStream to ExifParser");
205e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
206e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (LOGV) {
207e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            Log.v(TAG, "Reading exif...");
208e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
209e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        mInterface = iRef;
210e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        mContainExifData = seekTiffData(inputStream);
211e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        mTiffStream = new CountedDataInputStream(inputStream);
212e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        mOptions = options;
213e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (!mContainExifData) {
214e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            return;
215e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
216e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
217e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        parseTiffHeader();
218e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        long offset = mTiffStream.readUnsignedInt();
219e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (offset > Integer.MAX_VALUE) {
220e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            throw new ExifInvalidFormatException("Invalid offset " + offset);
221e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
222e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        mIfd0Position = (int) offset;
223e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        mIfdType = IfdId.TYPE_IFD_0;
224e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (isIfdRequested(IfdId.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) {
225e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            registerIfd(IfdId.TYPE_IFD_0, offset);
226e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (offset != DEFAULT_IFD0_OFFSET) {
227e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                mDataAboveIfd0 = new byte[(int) offset - DEFAULT_IFD0_OFFSET];
228e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                read(mDataAboveIfd0);
229e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
230e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
231e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
232e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
233e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
234e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Parses the the given InputStream with the given options
235e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     *
236e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @exception IOException
237e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @exception ExifInvalidFormatException
238e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
239e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected static ExifParser parse(InputStream inputStream, int options, ExifInterface iRef)
240e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            throws IOException, ExifInvalidFormatException {
241e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return new ExifParser(inputStream, options, iRef);
242e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
243e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
244e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
245e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Parses the the given InputStream with default options; that is, every IFD
246e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * and thumbnaill will be parsed.
247e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     *
248e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @exception IOException
249e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @exception ExifInvalidFormatException
250e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #parse(InputStream, int)
251e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
252e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected static ExifParser parse(InputStream inputStream, ExifInterface iRef)
253e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            throws IOException, ExifInvalidFormatException {
254e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return new ExifParser(inputStream, OPTION_IFD_0 | OPTION_IFD_1
255e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                | OPTION_IFD_EXIF | OPTION_IFD_GPS | OPTION_IFD_INTEROPERABILITY
256e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                | OPTION_THUMBNAIL, iRef);
257e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
258e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
259e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
260e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Moves the parser forward and returns the next parsing event
261e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     *
262e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @exception IOException
263e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @exception ExifInvalidFormatException
264e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #EVENT_START_OF_IFD
265e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #EVENT_NEW_TAG
266e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #EVENT_VALUE_OF_REGISTERED_TAG
267e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #EVENT_COMPRESSED_IMAGE
268e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #EVENT_UNCOMPRESSED_STRIP
269e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #EVENT_END
270e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
271e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected int next() throws IOException, ExifInvalidFormatException {
272e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (!mContainExifData) {
273e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            return EVENT_END;
274e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
275e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        int offset = mTiffStream.getReadByteCount();
276e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
277e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (offset < endOfTags) {
278e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            mTag = readTag();
279e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (mTag == null) {
280e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                return next();
281e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
282e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (mNeedToParseOffsetsInCurrentIfd) {
283e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                checkOffsetOrImageTag(mTag);
284e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
285e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            return EVENT_NEW_TAG;
286e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else if (offset == endOfTags) {
287e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            // There is a link to ifd1 at the end of ifd0
288e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (mIfdType == IfdId.TYPE_IFD_0) {
289e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                long ifdOffset = readUnsignedLong();
290e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                if (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested()) {
291e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    if (ifdOffset != 0) {
292e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
293e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    }
294e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
295e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            } else {
296e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                int offsetSize = 4;
297e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                // Some camera models use invalid length of the offset
298e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                if (mCorrespondingEvent.size() > 0) {
299e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    offsetSize = mCorrespondingEvent.firstEntry().getKey() -
300e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                            mTiffStream.getReadByteCount();
301e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
302e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                if (offsetSize < 4) {
303e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    Log.w(TAG, "Invalid size of link to next IFD: " + offsetSize);
304e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                } else {
305e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    long ifdOffset = readUnsignedLong();
306e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    if (ifdOffset != 0) {
307e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        Log.w(TAG, "Invalid link to next IFD: " + ifdOffset);
308e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    }
309e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
310e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
311e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
312e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        while (mCorrespondingEvent.size() != 0) {
313e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
314e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            Object event = entry.getValue();
315e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            try {
316e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                skipTo(entry.getKey());
317e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            } catch (IOException e) {
318e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                Log.w(TAG, "Failed to skip to data at: " + entry.getKey() +
319e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        " for " + event.getClass().getName() + ", the file may be broken.");
320e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                continue;
321e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
322e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (event instanceof IfdEvent) {
323e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                mIfdType = ((IfdEvent) event).ifd;
324e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                mNumOfTagInIfd = mTiffStream.readUnsignedShort();
325e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                mIfdStartOffset = entry.getKey();
326e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
327e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                if (mNumOfTagInIfd * TAG_SIZE + mIfdStartOffset + OFFSET_SIZE > mApp1End) {
328e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    Log.w(TAG, "Invalid size of IFD " + mIfdType);
329e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    return EVENT_END;
330e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
331e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
332e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                mNeedToParseOffsetsInCurrentIfd = needToParseOffsetsInCurrentIfd();
333e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                if (((IfdEvent) event).isRequested) {
334e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    return EVENT_START_OF_IFD;
335e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                } else {
336e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    skipRemainingTagsInCurrentIfd();
337e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
338e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            } else if (event instanceof ImageEvent) {
339e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                mImageEvent = (ImageEvent) event;
340e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                return mImageEvent.type;
341e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            } else {
342e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                ExifTagEvent tagEvent = (ExifTagEvent) event;
343e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                mTag = tagEvent.tag;
344e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                if (mTag.getDataType() != ExifTag.TYPE_UNDEFINED) {
345e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    readFullTagValue(mTag);
346e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    checkOffsetOrImageTag(mTag);
347e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
348e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                if (tagEvent.isRequested) {
349e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    return EVENT_VALUE_OF_REGISTERED_TAG;
350e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
351e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
352e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
353e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return EVENT_END;
354e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
355e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
356e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
357e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Skips the tags area of current IFD, if the parser is not in the tag area,
358e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * nothing will happen.
359e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     *
360e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @throws IOException
361e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @throws ExifInvalidFormatException
362e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
363e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected void skipRemainingTagsInCurrentIfd() throws IOException, ExifInvalidFormatException {
364e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
365e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        int offset = mTiffStream.getReadByteCount();
366e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (offset > endOfTags) {
367e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            return;
368e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
369e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (mNeedToParseOffsetsInCurrentIfd) {
370e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            while (offset < endOfTags) {
371e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                mTag = readTag();
372e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                offset += TAG_SIZE;
373e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                if (mTag == null) {
374e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    continue;
375e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
376e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                checkOffsetOrImageTag(mTag);
377e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
378e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else {
379e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            skipTo(endOfTags);
380e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
381e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        long ifdOffset = readUnsignedLong();
382e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        // For ifd0, there is a link to ifd1 in the end of all tags
383e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (mIfdType == IfdId.TYPE_IFD_0
384e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                && (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested())) {
385e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (ifdOffset > 0) {
386e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
387e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
388e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
389e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
390e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
391e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private boolean needToParseOffsetsInCurrentIfd() {
392e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        switch (mIfdType) {
393e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case IfdId.TYPE_IFD_0:
394e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                return isIfdRequested(IfdId.TYPE_IFD_EXIF) || isIfdRequested(IfdId.TYPE_IFD_GPS)
395e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)
396e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        || isIfdRequested(IfdId.TYPE_IFD_1);
397e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case IfdId.TYPE_IFD_1:
398e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                return isThumbnailRequested();
399e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case IfdId.TYPE_IFD_EXIF:
400e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                // The offset to interoperability IFD is located in Exif IFD
401e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                return isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY);
402e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            default:
403e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                return false;
404e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
405e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
406e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
407e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
408e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * If {@link #next()} return {@link #EVENT_NEW_TAG} or
409e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * {@link #EVENT_VALUE_OF_REGISTERED_TAG}, call this function to get the
410e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * corresponding tag.
411e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * <p>
412e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * For {@link #EVENT_NEW_TAG}, the tag may not contain the value if the size
413e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * of the value is greater than 4 bytes. One should call
414e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * {@link ExifTag#hasValue()} to check if the tag contains value. If there
415e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * is no value,call {@link #registerForTagValue(ExifTag)} to have the parser
416e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area
417e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * pointed by the offset.
418e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * <p>
419e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * When {@link #EVENT_VALUE_OF_REGISTERED_TAG} is emitted, the value of the
420e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * tag will have already been read except for tags of undefined type. For
421e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * tags of undefined type, call one of the read methods to get the value.
422e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     *
423e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #registerForTagValue(ExifTag)
424e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #read(byte[])
425e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #read(byte[], int, int)
426e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #readLong()
427e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #readRational()
428e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #readString(int)
429e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #readString(int, Charset)
430e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
431e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected ExifTag getTag() {
432e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return mTag;
433e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
434e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
435e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
436e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Gets number of tags in the current IFD area.
437e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
438e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected int getTagCountInCurrentIfd() {
439e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return mNumOfTagInIfd;
440e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
441e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
442e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
443e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Gets the ID of current IFD.
444e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     *
445e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see IfdId#TYPE_IFD_0
446e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see IfdId#TYPE_IFD_1
447e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see IfdId#TYPE_IFD_GPS
448e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see IfdId#TYPE_IFD_INTEROPERABILITY
449e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see IfdId#TYPE_IFD_EXIF
450e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
451e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected int getCurrentIfd() {
452e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return mIfdType;
453e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
454e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
455e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
456e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
457e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * get the index of this strip.
458e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     *
459e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #getStripCount()
460e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
461e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected int getStripIndex() {
462e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return mImageEvent.stripIndex;
463e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
464e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
465e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
466e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
467e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * get the number of strip data.
468e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     *
469e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #getStripIndex()
470e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
471e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected int getStripCount() {
472e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return mStripCount;
473e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
474e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
475e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
476e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
477e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * get the strip size.
478e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
479e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected int getStripSize() {
480e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (mStripSizeTag == null)
481e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            return 0;
482e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return (int) mStripSizeTag.getValueAt(0);
483e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
484e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
485e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
486e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * When receiving {@link #EVENT_COMPRESSED_IMAGE}, call this function to get
487e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * the image data size.
488e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
489e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected int getCompressedImageSize() {
490e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (mJpegSizeTag == null) {
491e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            return 0;
492e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
493e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return (int) mJpegSizeTag.getValueAt(0);
494e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
495e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
496e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private void skipTo(int offset) throws IOException {
497e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        mTiffStream.skipTo(offset);
498e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        while (!mCorrespondingEvent.isEmpty() && mCorrespondingEvent.firstKey() < offset) {
499e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            mCorrespondingEvent.pollFirstEntry();
500e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
501e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
502e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
503e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
504e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * When getting {@link #EVENT_NEW_TAG} in the tag area of IFD, the tag may
505e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * not contain the value if the size of the value is greater than 4 bytes.
506e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * When the value is not available here, call this method so that the parser
507e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * will emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area
508e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * where the value is located.
509e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     *
510e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * @see #EVENT_VALUE_OF_REGISTERED_TAG
511e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
512e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected void registerForTagValue(ExifTag tag) {
513e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (tag.getOffset() >= mTiffStream.getReadByteCount()) {
514e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, true));
515e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
516e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
517e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
518e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private void registerIfd(int ifdType, long offset) {
519e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        // Cast unsigned int to int since the offset is always smaller
520e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        // than the size of APP1 (65536)
521e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        mCorrespondingEvent.put((int) offset, new IfdEvent(ifdType, isIfdRequested(ifdType)));
522e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
523e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
524e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private void registerCompressedImage(long offset) {
525e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_COMPRESSED_IMAGE));
526e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
527e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
528e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private void registerUncompressedStrip(int stripIndex, long offset) {
529e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_UNCOMPRESSED_STRIP
530e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                , stripIndex));
531e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
532e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
533e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private ExifTag readTag() throws IOException, ExifInvalidFormatException {
534e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        short tagId = mTiffStream.readShort();
535e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        short dataFormat = mTiffStream.readShort();
536e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        long numOfComp = mTiffStream.readUnsignedInt();
537e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (numOfComp > Integer.MAX_VALUE) {
538e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            throw new ExifInvalidFormatException(
539e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    "Number of component is larger then Integer.MAX_VALUE");
540e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
541e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        // Some invalid image file contains invalid data type. Ignore those tags
542e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (!ExifTag.isValidType(dataFormat)) {
543e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            Log.w(TAG, String.format("Tag %04x: Invalid data type %d", tagId, dataFormat));
544e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            mTiffStream.skip(4);
545e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            return null;
546e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
547e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        // TODO: handle numOfComp overflow
548e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        ExifTag tag = new ExifTag(tagId, dataFormat, (int) numOfComp, mIfdType,
549e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                ((int) numOfComp) != ExifTag.SIZE_UNDEFINED);
550e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        int dataSize = tag.getDataSize();
551e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (dataSize > 4) {
552e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            long offset = mTiffStream.readUnsignedInt();
553e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (offset > Integer.MAX_VALUE) {
554e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                throw new ExifInvalidFormatException(
555e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        "offset is larger then Integer.MAX_VALUE");
556e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
557e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            // Some invalid images put some undefined data before IFD0.
558e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            // Read the data here.
559e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if ((offset < mIfd0Position) && (dataFormat == ExifTag.TYPE_UNDEFINED)) {
560e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                byte[] buf = new byte[(int) numOfComp];
561e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                System.arraycopy(mDataAboveIfd0, (int) offset - DEFAULT_IFD0_OFFSET,
562e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        buf, 0, (int) numOfComp);
563e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                tag.setValue(buf);
564e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            } else {
565e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                tag.setOffset((int) offset);
566e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
567e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else {
568e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            boolean defCount = tag.hasDefinedCount();
569e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            // Set defined count to 0 so we can add \0 to non-terminated strings
570e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            tag.setHasDefinedCount(false);
571e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            // Read value
572e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            readFullTagValue(tag);
573e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            tag.setHasDefinedCount(defCount);
574e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            mTiffStream.skip(4 - dataSize);
575e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            // Set the offset to the position of value.
576e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            tag.setOffset(mTiffStream.getReadByteCount() - 4);
577e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
578e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return tag;
579e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
580e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
581e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
582e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Check the tag, if the tag is one of the offset tag that points to the IFD
583e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * or image the caller is interested in, register the IFD or image.
584e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
585e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private void checkOffsetOrImageTag(ExifTag tag) {
586e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        // Some invalid formattd image contains tag with 0 size.
587e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (tag.getComponentCount() == 0) {
588e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            return;
589e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
590e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        short tid = tag.getTagId();
591e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        int ifd = tag.getIfd();
592e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (tid == TAG_EXIF_IFD && checkAllowed(ifd, ExifInterface.TAG_EXIF_IFD)) {
593e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (isIfdRequested(IfdId.TYPE_IFD_EXIF)
594e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
595e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                registerIfd(IfdId.TYPE_IFD_EXIF, tag.getValueAt(0));
596e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
597e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else if (tid == TAG_GPS_IFD && checkAllowed(ifd, ExifInterface.TAG_GPS_IFD)) {
598e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (isIfdRequested(IfdId.TYPE_IFD_GPS)) {
599e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                registerIfd(IfdId.TYPE_IFD_GPS, tag.getValueAt(0));
600e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
601e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else if (tid == TAG_INTEROPERABILITY_IFD
602e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                && checkAllowed(ifd, ExifInterface.TAG_INTEROPERABILITY_IFD)) {
603e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
604e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                registerIfd(IfdId.TYPE_IFD_INTEROPERABILITY, tag.getValueAt(0));
605e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
606e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT
607e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)) {
608e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (isThumbnailRequested()) {
609e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                registerCompressedImage(tag.getValueAt(0));
610e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
611e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT_LENGTH
612e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)) {
613e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (isThumbnailRequested()) {
614e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                mJpegSizeTag = tag;
615e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
616e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else if (tid == TAG_STRIP_OFFSETS && checkAllowed(ifd, ExifInterface.TAG_STRIP_OFFSETS)) {
617e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (isThumbnailRequested()) {
618e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                if (tag.hasValue()) {
619e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    for (int i = 0; i < tag.getComponentCount(); i++) {
620e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        if (tag.getDataType() == ExifTag.TYPE_UNSIGNED_SHORT) {
621e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                            registerUncompressedStrip(i, tag.getValueAt(i));
622e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        } else {
623e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                            registerUncompressedStrip(i, tag.getValueAt(i));
624e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        }
625e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    }
626e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                } else {
627e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, false));
628e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
629e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
630e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else if (tid == TAG_STRIP_BYTE_COUNTS
631e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                && checkAllowed(ifd, ExifInterface.TAG_STRIP_BYTE_COUNTS)
632e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                &&isThumbnailRequested() && tag.hasValue()) {
633e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            mStripSizeTag = tag;
634e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
635e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
636e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
637e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private boolean checkAllowed(int ifd, int tagId) {
638e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        int info = mInterface.getTagInfo().get(tagId);
639e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (info == ExifInterface.DEFINITION_NULL) {
640e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            return false;
641e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
642e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return ExifInterface.isIfdAllowed(info, ifd);
643e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
644e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
645e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected void readFullTagValue(ExifTag tag) throws IOException {
646e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        // Some invalid images contains tags with wrong size, check it here
647e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        short type = tag.getDataType();
648e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (type == ExifTag.TYPE_ASCII || type == ExifTag.TYPE_UNDEFINED ||
649e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                type == ExifTag.TYPE_UNSIGNED_BYTE) {
650e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            int size = tag.getComponentCount();
651e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (mCorrespondingEvent.size() > 0) {
652e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                if (mCorrespondingEvent.firstEntry().getKey() < mTiffStream.getReadByteCount()
653e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        + size) {
654e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    Object event = mCorrespondingEvent.firstEntry().getValue();
655e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    if (event instanceof ImageEvent) {
656e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        // Tag value overlaps thumbnail, ignore thumbnail.
657e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        Log.w(TAG, "Thumbnail overlaps value for tag: \n" + tag.toString());
658e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
659e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        Log.w(TAG, "Invalid thumbnail offset: " + entry.getKey());
660e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    } else {
661e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        // Tag value overlaps another tag, shorten count
662e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        if (event instanceof IfdEvent) {
663e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                            Log.w(TAG, "Ifd " + ((IfdEvent) event).ifd
664e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                                    + " overlaps value for tag: \n" + tag.toString());
665e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        } else if (event instanceof ExifTagEvent) {
666e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                            Log.w(TAG, "Tag value for tag: \n"
667e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                                    + ((ExifTagEvent) event).tag.toString()
668e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                                    + " overlaps value for tag: \n" + tag.toString());
669e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        }
670e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        size = mCorrespondingEvent.firstEntry().getKey()
671e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                                - mTiffStream.getReadByteCount();
672e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        Log.w(TAG, "Invalid size of tag: \n" + tag.toString()
673e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                                + " setting count to: " + size);
674e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        tag.forceSetComponentCount(size);
675e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    }
676e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
677e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
678e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
679e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        switch (tag.getDataType()) {
680e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case ExifTag.TYPE_UNSIGNED_BYTE:
681e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case ExifTag.TYPE_UNDEFINED: {
682e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                byte buf[] = new byte[tag.getComponentCount()];
683e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                read(buf);
684e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                tag.setValue(buf);
685e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
686e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                break;
687e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case ExifTag.TYPE_ASCII:
688e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                tag.setValue(readString(tag.getComponentCount()));
689e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                break;
690e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case ExifTag.TYPE_UNSIGNED_LONG: {
691e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                long value[] = new long[tag.getComponentCount()];
692e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                for (int i = 0, n = value.length; i < n; i++) {
693e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    value[i] = readUnsignedLong();
694e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
695e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                tag.setValue(value);
696e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
697e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                break;
698e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case ExifTag.TYPE_UNSIGNED_RATIONAL: {
699e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                Rational value[] = new Rational[tag.getComponentCount()];
700e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                for (int i = 0, n = value.length; i < n; i++) {
701e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    value[i] = readUnsignedRational();
702e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
703e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                tag.setValue(value);
704e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
705e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                break;
706e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case ExifTag.TYPE_UNSIGNED_SHORT: {
707e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                int value[] = new int[tag.getComponentCount()];
708e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                for (int i = 0, n = value.length; i < n; i++) {
709e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    value[i] = readUnsignedShort();
710e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
711e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                tag.setValue(value);
712e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
713e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                break;
714e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case ExifTag.TYPE_LONG: {
715e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                int value[] = new int[tag.getComponentCount()];
716e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                for (int i = 0, n = value.length; i < n; i++) {
717e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    value[i] = readLong();
718e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
719e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                tag.setValue(value);
720e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
721e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                break;
722e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case ExifTag.TYPE_RATIONAL: {
723e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                Rational value[] = new Rational[tag.getComponentCount()];
724e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                for (int i = 0, n = value.length; i < n; i++) {
725e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    value[i] = readRational();
726e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
727e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                tag.setValue(value);
728e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
729e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                break;
730e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
731e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (LOGV) {
732e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            Log.v(TAG, "\n" + tag.toString());
733e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
734e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
735e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
736e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private void parseTiffHeader() throws IOException,
737e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ExifInvalidFormatException {
738e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        short byteOrder = mTiffStream.readShort();
739e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (LITTLE_ENDIAN_TAG == byteOrder) {
740e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            mTiffStream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
741e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else if (BIG_ENDIAN_TAG == byteOrder) {
742e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            mTiffStream.setByteOrder(ByteOrder.BIG_ENDIAN);
743e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else {
744e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            throw new ExifInvalidFormatException("Invalid TIFF header");
745e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
746e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
747e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (mTiffStream.readShort() != TIFF_HEADER_TAIL) {
748e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            throw new ExifInvalidFormatException("Invalid TIFF header");
749e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
750e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
751e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
752e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private boolean seekTiffData(InputStream inputStream) throws IOException,
753e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ExifInvalidFormatException {
754e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        CountedDataInputStream dataStream = new CountedDataInputStream(inputStream);
755e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (dataStream.readShort() != JpegHeader.SOI) {
756e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            throw new ExifInvalidFormatException("Invalid JPEG format");
757e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
758e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
759e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        short marker = dataStream.readShort();
760e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        while (marker != JpegHeader.EOI
761e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                && !JpegHeader.isSofMarker(marker)) {
762e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            int length = dataStream.readUnsignedShort();
763e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            // Some invalid formatted image contains multiple APP1,
764e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            // try to find the one with Exif data.
765e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (marker == JpegHeader.APP1) {
766e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                int header = 0;
767e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                short headerTail = 0;
768e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                if (length >= 8) {
769e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    header = dataStream.readInt();
770e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    headerTail = dataStream.readShort();
771e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    length -= 6;
772e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    if (header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) {
773e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        mTiffStartPosition = dataStream.getReadByteCount();
774e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        mApp1End = length;
775e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        mOffsetToApp1EndFromSOF = mTiffStartPosition + mApp1End;
776e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        return true;
777e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    }
778e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
779e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
780e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (length < 2 || (length - 2) != dataStream.skip(length - 2)) {
781e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                Log.w(TAG, "Invalid JPEG format.");
782e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                return false;
783e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
784e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            marker = dataStream.readShort();
785e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
786e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return false;
787e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
788e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
789e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected int getOffsetToExifEndFromSOF() {
790e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return mOffsetToApp1EndFromSOF;
791e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
792e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
793e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected int getTiffStartPosition() {
794e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return mTiffStartPosition;
795e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
796e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
797e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
798e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Reads bytes from the InputStream.
799e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
800e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected int read(byte[] buffer, int offset, int length) throws IOException {
801e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return mTiffStream.read(buffer, offset, length);
802e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
803e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
804e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
805e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Equivalent to read(buffer, 0, buffer.length).
806e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
807e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected int read(byte[] buffer) throws IOException {
808e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return mTiffStream.read(buffer);
809e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
810e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
811e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
812e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Reads a String from the InputStream with US-ASCII charset. The parser
813e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * will read n bytes and convert it to ascii string. This is used for
814e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * reading values of type {@link ExifTag#TYPE_ASCII}.
815e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
816e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected String readString(int n) throws IOException {
817e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return readString(n, US_ASCII);
818e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
819e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
820e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
821e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Reads a String from the InputStream with the given charset. The parser
822e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * will read n bytes and convert it to string. This is used for reading
823e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * values of type {@link ExifTag#TYPE_ASCII}.
824e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
825e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected String readString(int n, Charset charset) throws IOException {
826e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (n > 0) {
827e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            return mTiffStream.readString(n, charset);
828e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else {
829e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            return "";
830e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
831e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
832e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
833e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
834e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Reads value of type {@link ExifTag#TYPE_UNSIGNED_SHORT} from the
835e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * InputStream.
836e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
837e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected int readUnsignedShort() throws IOException {
838e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return mTiffStream.readShort() & 0xffff;
839e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
840e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
841e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
842e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Reads value of type {@link ExifTag#TYPE_UNSIGNED_LONG} from the
843e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * InputStream.
844e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
845e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected long readUnsignedLong() throws IOException {
846e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return readLong() & 0xffffffffL;
847e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
848e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
849e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
850e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Reads value of type {@link ExifTag#TYPE_UNSIGNED_RATIONAL} from the
851e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * InputStream.
852e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
853e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected Rational readUnsignedRational() throws IOException {
854e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        long nomi = readUnsignedLong();
855e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        long denomi = readUnsignedLong();
856e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return new Rational(nomi, denomi);
857e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
858e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
859e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
860e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Reads value of type {@link ExifTag#TYPE_LONG} from the InputStream.
861e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
862e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected int readLong() throws IOException {
863e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return mTiffStream.readInt();
864e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
865e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
866e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
867e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Reads value of type {@link ExifTag#TYPE_RATIONAL} from the InputStream.
868e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
869e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected Rational readRational() throws IOException {
870e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        int nomi = readLong();
871e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        int denomi = readLong();
872e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return new Rational(nomi, denomi);
873e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
874e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
875e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static class ImageEvent {
876e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        int stripIndex;
877e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        int type;
878e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
879e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        ImageEvent(int type) {
880e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            this.stripIndex = 0;
881e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            this.type = type;
882e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
883e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
884e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        ImageEvent(int type, int stripIndex) {
885e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            this.type = type;
886e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            this.stripIndex = stripIndex;
887e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
888e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
889e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
890e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static class IfdEvent {
891e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        int ifd;
892e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        boolean isRequested;
893e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
894e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        IfdEvent(int ifd, boolean isInterestedIfd) {
895e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            this.ifd = ifd;
896e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            this.isRequested = isInterestedIfd;
897e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
898e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
899e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
900e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static class ExifTagEvent {
901e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        ExifTag tag;
902e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        boolean isRequested;
903e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
904e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        ExifTagEvent(ExifTag tag, boolean isRequireByUser) {
905e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            this.tag = tag;
906e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            this.isRequested = isRequireByUser;
907e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
908e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
909e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
910e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
911e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Gets the byte order of the current InputStream.
912e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
913e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected ByteOrder getByteOrder() {
914e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return mTiffStream.getByteOrder();
915e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
916e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor}
917