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