1104c45677660586026a7e74ef8c47d396403d50eMichael Jurka/*
2104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Copyright (C) 2012 The Android Open Source Project
3104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
4104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License");
5104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * you may not use this file except in compliance with the License.
6104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * You may obtain a copy of the License at
7104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
8104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *      http://www.apache.org/licenses/LICENSE-2.0
9104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
10104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Unless required by applicable law or agreed to in writing, software
11104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS,
12104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * See the License for the specific language governing permissions and
14104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * limitations under the License.
15104c45677660586026a7e74ef8c47d396403d50eMichael Jurka */
16104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
17104c45677660586026a7e74ef8c47d396403d50eMichael Jurkapackage com.android.gallery3d.exif;
18104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
19104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.util.Log;
20104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
21104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.IOException;
22104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.InputStream;
23104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.ByteOrder;
24104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.charset.Charset;
25104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.Map.Entry;
26104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.TreeMap;
27104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
28104c45677660586026a7e74ef8c47d396403d50eMichael Jurka/**
29104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * This class provides a low-level EXIF parsing API. Given a JPEG format
30104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * InputStream, the caller can request which IFD's to read via
31104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * {@link #parse(InputStream, int)} with given options.
32104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * <p>
33104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Below is an example of getting EXIF data from IFD 0 and EXIF IFD using the
34104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * parser.
35104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
36104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * <pre>
37104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * void parse() {
38104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *     ExifParser parser = ExifParser.parse(mImageInputStream,
39104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *             ExifParser.OPTION_IFD_0 | ExifParser.OPTIONS_IFD_EXIF);
40104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *     int event = parser.next();
41104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *     while (event != ExifParser.EVENT_END) {
42104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *         switch (event) {
43104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *             case ExifParser.EVENT_START_OF_IFD:
44104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *                 break;
45104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *             case ExifParser.EVENT_NEW_TAG:
46104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *                 ExifTag tag = parser.getTag();
47104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *                 if (!tag.hasValue()) {
48104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *                     parser.registerForTagValue(tag);
49104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *                 } else {
50104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *                     processTag(tag);
51104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *                 }
52104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *                 break;
53104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *             case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
54104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *                 tag = parser.getTag();
55104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *                 if (tag.getDataType() != ExifTag.TYPE_UNDEFINED) {
56104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *                     processTag(tag);
57104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *                 }
58104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *                 break;
59104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *         }
60104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *         event = parser.next();
61104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *     }
62104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * }
63104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
64104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * void processTag(ExifTag tag) {
65104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *     // process the tag as you like.
66104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * }
67104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * </pre>
68104c45677660586026a7e74ef8c47d396403d50eMichael Jurka */
69104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaclass ExifParser {
70104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final boolean LOGV = false;
71104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String TAG = "ExifParser";
72104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
73104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * When the parser reaches a new IFD area. Call {@link #getCurrentIfd()} to
74104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * know which IFD we are in.
75104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
76104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final int EVENT_START_OF_IFD = 0;
77104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
78104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * When the parser reaches a new tag. Call {@link #getTag()}to get the
79104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * corresponding tag.
80104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
81104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final int EVENT_NEW_TAG = 1;
82104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
83104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * When the parser reaches the value area of tag that is registered by
84104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * {@link #registerForTagValue(ExifTag)} previously. Call {@link #getTag()}
85104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * to get the corresponding tag.
86104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
87104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final int EVENT_VALUE_OF_REGISTERED_TAG = 2;
88104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
89104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
90104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * When the parser reaches the compressed image area.
91104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
92104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final int EVENT_COMPRESSED_IMAGE = 3;
93104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
94104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * When the parser reaches the uncompressed image strip. Call
95104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * {@link #getStripIndex()} to get the index of the strip.
96104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     *
97104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #getStripIndex()
98104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #getStripCount()
99104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
100104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final int EVENT_UNCOMPRESSED_STRIP = 4;
101104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
102104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * When there is nothing more to parse.
103104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
104104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final int EVENT_END = 5;
105104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
106104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
107104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Option bit to request to parse IFD0.
108104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
109104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final int OPTION_IFD_0 = 1 << 0;
110104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
111104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Option bit to request to parse IFD1.
112104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
113104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final int OPTION_IFD_1 = 1 << 1;
114104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
115104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Option bit to request to parse Exif-IFD.
116104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
117104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final int OPTION_IFD_EXIF = 1 << 2;
118104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
119104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Option bit to request to parse GPS-IFD.
120104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
121104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final int OPTION_IFD_GPS = 1 << 3;
122104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
123104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Option bit to request to parse Interoperability-IFD.
124104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
125104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final int OPTION_IFD_INTEROPERABILITY = 1 << 4;
126104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
127104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Option bit to request to parse thumbnail.
128104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
129104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final int OPTION_THUMBNAIL = 1 << 5;
130104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
131104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected static final int EXIF_HEADER = 0x45786966; // EXIF header "Exif"
132104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected static final short EXIF_HEADER_TAIL = (short) 0x0000; // EXIF header in APP1
133104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
134104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // TIFF header
135104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected static final short LITTLE_ENDIAN_TAG = (short) 0x4949; // "II"
136104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected static final short BIG_ENDIAN_TAG = (short) 0x4d4d; // "MM"
137104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected static final short TIFF_HEADER_TAIL = 0x002A;
138104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
139104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected static final int TAG_SIZE = 12;
140104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected static final int OFFSET_SIZE = 2;
141104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
142104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final Charset US_ASCII = Charset.forName("US-ASCII");
143104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
144104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected static final int DEFAULT_IFD0_OFFSET = 8;
145104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
146104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final CountedDataInputStream mTiffStream;
147104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final int mOptions;
148104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mIfdStartOffset = 0;
149104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mNumOfTagInIfd = 0;
150104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mIfdType;
151104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private ExifTag mTag;
152104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private ImageEvent mImageEvent;
153104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mStripCount;
154104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private ExifTag mStripSizeTag;
155104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private ExifTag mJpegSizeTag;
156104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private boolean mNeedToParseOffsetsInCurrentIfd;
157104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private boolean mContainExifData = false;
158104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mApp1End;
159104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mOffsetToApp1EndFromSOF = 0;
160104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private byte[] mDataAboveIfd0;
161104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mIfd0Position;
162104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mTiffStartPosition;
163104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final ExifInterface mInterface;
164104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
165104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final short TAG_EXIF_IFD = ExifInterface
166104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            .getTrueTagKey(ExifInterface.TAG_EXIF_IFD);
167104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final short TAG_GPS_IFD = ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD);
168104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final short TAG_INTEROPERABILITY_IFD = ExifInterface
169104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            .getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD);
170104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final short TAG_JPEG_INTERCHANGE_FORMAT = ExifInterface
171104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
172104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final short TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = ExifInterface
173104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
174104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final short TAG_STRIP_OFFSETS = ExifInterface
175104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            .getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS);
176104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final short TAG_STRIP_BYTE_COUNTS = ExifInterface
177104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            .getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS);
178104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
179104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final TreeMap<Integer, Object> mCorrespondingEvent = new TreeMap<Integer, Object>();
180104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
181104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private boolean isIfdRequested(int ifdType) {
182104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        switch (ifdType) {
183104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case IfdId.TYPE_IFD_0:
184104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return (mOptions & OPTION_IFD_0) != 0;
185104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case IfdId.TYPE_IFD_1:
186104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return (mOptions & OPTION_IFD_1) != 0;
187104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case IfdId.TYPE_IFD_EXIF:
188104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return (mOptions & OPTION_IFD_EXIF) != 0;
189104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case IfdId.TYPE_IFD_GPS:
190104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return (mOptions & OPTION_IFD_GPS) != 0;
191104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case IfdId.TYPE_IFD_INTEROPERABILITY:
192104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return (mOptions & OPTION_IFD_INTEROPERABILITY) != 0;
193104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
194104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return false;
195104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
196104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
197104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private boolean isThumbnailRequested() {
198104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return (mOptions & OPTION_THUMBNAIL) != 0;
199104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
200104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
201104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private ExifParser(InputStream inputStream, int options, ExifInterface iRef)
202104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throws IOException, ExifInvalidFormatException {
203104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (inputStream == null) {
204104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throw new IOException("Null argument inputStream to ExifParser");
205104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
206104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (LOGV) {
207104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.v(TAG, "Reading exif...");
208104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
209104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mInterface = iRef;
210104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mContainExifData = seekTiffData(inputStream);
211104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTiffStream = new CountedDataInputStream(inputStream);
212104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mOptions = options;
213104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (!mContainExifData) {
214104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
215104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
216104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
217104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        parseTiffHeader();
218104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        long offset = mTiffStream.readUnsignedInt();
219104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (offset > Integer.MAX_VALUE) {
220104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throw new ExifInvalidFormatException("Invalid offset " + offset);
221104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
222104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mIfd0Position = (int) offset;
223104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mIfdType = IfdId.TYPE_IFD_0;
224104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (isIfdRequested(IfdId.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) {
225104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            registerIfd(IfdId.TYPE_IFD_0, offset);
226104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (offset != DEFAULT_IFD0_OFFSET) {
227104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mDataAboveIfd0 = new byte[(int) offset - DEFAULT_IFD0_OFFSET];
228104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                read(mDataAboveIfd0);
229104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
230104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
231104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
232104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
233104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
234104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Parses the the given InputStream with the given options
235104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     *
236104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @exception IOException
237104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @exception ExifInvalidFormatException
238104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
239104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected static ExifParser parse(InputStream inputStream, int options, ExifInterface iRef)
240104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throws IOException, ExifInvalidFormatException {
241104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return new ExifParser(inputStream, options, iRef);
242104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
243104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
244104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
245104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Parses the the given InputStream with default options; that is, every IFD
246104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * and thumbnaill will be parsed.
247104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     *
248104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @exception IOException
249104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @exception ExifInvalidFormatException
250104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #parse(InputStream, int)
251104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
252104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected static ExifParser parse(InputStream inputStream, ExifInterface iRef)
253104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throws IOException, ExifInvalidFormatException {
254104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return new ExifParser(inputStream, OPTION_IFD_0 | OPTION_IFD_1
255104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                | OPTION_IFD_EXIF | OPTION_IFD_GPS | OPTION_IFD_INTEROPERABILITY
256104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                | OPTION_THUMBNAIL, iRef);
257104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
258104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
259104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
260104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Moves the parser forward and returns the next parsing event
261104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     *
262104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @exception IOException
263104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @exception ExifInvalidFormatException
264104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #EVENT_START_OF_IFD
265104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #EVENT_NEW_TAG
266104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #EVENT_VALUE_OF_REGISTERED_TAG
267104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #EVENT_COMPRESSED_IMAGE
268104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #EVENT_UNCOMPRESSED_STRIP
269104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #EVENT_END
270104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
271104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int next() throws IOException, ExifInvalidFormatException {
272104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (!mContainExifData) {
273104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return EVENT_END;
274104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
275104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int offset = mTiffStream.getReadByteCount();
276104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
277104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (offset < endOfTags) {
278104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mTag = readTag();
279104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mTag == null) {
280104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return next();
281104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
282104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mNeedToParseOffsetsInCurrentIfd) {
283104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                checkOffsetOrImageTag(mTag);
284104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
285104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return EVENT_NEW_TAG;
286104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else if (offset == endOfTags) {
287104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // There is a link to ifd1 at the end of ifd0
288104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mIfdType == IfdId.TYPE_IFD_0) {
289104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                long ifdOffset = readUnsignedLong();
290104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested()) {
291104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    if (ifdOffset != 0) {
292104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
293104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    }
294104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
295104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } else {
296104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                int offsetSize = 4;
297104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                // Some camera models use invalid length of the offset
298104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (mCorrespondingEvent.size() > 0) {
299104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    offsetSize = mCorrespondingEvent.firstEntry().getKey() -
300104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            mTiffStream.getReadByteCount();
301104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
302104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (offsetSize < 4) {
303104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    Log.w(TAG, "Invalid size of link to next IFD: " + offsetSize);
304104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                } else {
305104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    long ifdOffset = readUnsignedLong();
306104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    if (ifdOffset != 0) {
307104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        Log.w(TAG, "Invalid link to next IFD: " + ifdOffset);
308104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    }
309104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
310104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
311104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
312104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        while (mCorrespondingEvent.size() != 0) {
313104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
314104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Object event = entry.getValue();
315104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            try {
316104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                skipTo(entry.getKey());
317104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } catch (IOException e) {
318104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                Log.w(TAG, "Failed to skip to data at: " + entry.getKey() +
319104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        " for " + event.getClass().getName() + ", the file may be broken.");
320104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                continue;
321104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
322104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (event instanceof IfdEvent) {
323104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mIfdType = ((IfdEvent) event).ifd;
324104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mNumOfTagInIfd = mTiffStream.readUnsignedShort();
325104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mIfdStartOffset = entry.getKey();
326104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
327104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (mNumOfTagInIfd * TAG_SIZE + mIfdStartOffset + OFFSET_SIZE > mApp1End) {
328104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    Log.w(TAG, "Invalid size of IFD " + mIfdType);
329104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    return EVENT_END;
330104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
331104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
332104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mNeedToParseOffsetsInCurrentIfd = needToParseOffsetsInCurrentIfd();
333104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (((IfdEvent) event).isRequested) {
334104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    return EVENT_START_OF_IFD;
335104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                } else {
336104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    skipRemainingTagsInCurrentIfd();
337104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
338104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } else if (event instanceof ImageEvent) {
339104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mImageEvent = (ImageEvent) event;
340104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return mImageEvent.type;
341104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } else {
342104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                ExifTagEvent tagEvent = (ExifTagEvent) event;
343104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mTag = tagEvent.tag;
344104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (mTag.getDataType() != ExifTag.TYPE_UNDEFINED) {
345104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    readFullTagValue(mTag);
346104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    checkOffsetOrImageTag(mTag);
347104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
348104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (tagEvent.isRequested) {
349104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    return EVENT_VALUE_OF_REGISTERED_TAG;
350104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
351104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
352104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
353104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return EVENT_END;
354104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
355104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
356104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
357104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Skips the tags area of current IFD, if the parser is not in the tag area,
358104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * nothing will happen.
359104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     *
360104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @throws IOException
361104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @throws ExifInvalidFormatException
362104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
363104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected void skipRemainingTagsInCurrentIfd() throws IOException, ExifInvalidFormatException {
364104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
365104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int offset = mTiffStream.getReadByteCount();
366104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (offset > endOfTags) {
367104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
368104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
369104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mNeedToParseOffsetsInCurrentIfd) {
370104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            while (offset < endOfTags) {
371104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mTag = readTag();
372104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                offset += TAG_SIZE;
373104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (mTag == null) {
374104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    continue;
375104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
376104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                checkOffsetOrImageTag(mTag);
377104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
378104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else {
379104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            skipTo(endOfTags);
380104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
381104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        long ifdOffset = readUnsignedLong();
382104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // For ifd0, there is a link to ifd1 in the end of all tags
383104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mIfdType == IfdId.TYPE_IFD_0
384104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                && (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested())) {
385104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (ifdOffset > 0) {
386104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
387104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
388104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
389104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
390104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
391104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private boolean needToParseOffsetsInCurrentIfd() {
392104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        switch (mIfdType) {
393104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case IfdId.TYPE_IFD_0:
394104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return isIfdRequested(IfdId.TYPE_IFD_EXIF) || isIfdRequested(IfdId.TYPE_IFD_GPS)
395104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)
396104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        || isIfdRequested(IfdId.TYPE_IFD_1);
397104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case IfdId.TYPE_IFD_1:
398104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return isThumbnailRequested();
399104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case IfdId.TYPE_IFD_EXIF:
400104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                // The offset to interoperability IFD is located in Exif IFD
401104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY);
402104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            default:
403104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return false;
404104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
405104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
406104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
407104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
408104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * If {@link #next()} return {@link #EVENT_NEW_TAG} or
409104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * {@link #EVENT_VALUE_OF_REGISTERED_TAG}, call this function to get the
410104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * corresponding tag.
411104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * <p>
412104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * For {@link #EVENT_NEW_TAG}, the tag may not contain the value if the size
413104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * of the value is greater than 4 bytes. One should call
414104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * {@link ExifTag#hasValue()} to check if the tag contains value. If there
415104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * is no value,call {@link #registerForTagValue(ExifTag)} to have the parser
416104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area
417104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * pointed by the offset.
418104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * <p>
419104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * When {@link #EVENT_VALUE_OF_REGISTERED_TAG} is emitted, the value of the
420104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * tag will have already been read except for tags of undefined type. For
421104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * tags of undefined type, call one of the read methods to get the value.
422104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     *
423104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #registerForTagValue(ExifTag)
424104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #read(byte[])
425104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #read(byte[], int, int)
426104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #readLong()
427104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #readRational()
428104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #readString(int)
429104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #readString(int, Charset)
430104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
431104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected ExifTag getTag() {
432104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mTag;
433104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
434104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
435104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
436104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Gets number of tags in the current IFD area.
437104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
438104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int getTagCountInCurrentIfd() {
439104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mNumOfTagInIfd;
440104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
441104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
442104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
443104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Gets the ID of current IFD.
444104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     *
445104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see IfdId#TYPE_IFD_0
446104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see IfdId#TYPE_IFD_1
447104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see IfdId#TYPE_IFD_GPS
448104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see IfdId#TYPE_IFD_INTEROPERABILITY
449104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see IfdId#TYPE_IFD_EXIF
450104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
451104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int getCurrentIfd() {
452104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mIfdType;
453104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
454104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
455104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
456104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
457104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * get the index of this strip.
458104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     *
459104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #getStripCount()
460104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
461104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int getStripIndex() {
462104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mImageEvent.stripIndex;
463104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
464104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
465104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
466104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
467104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * get the number of strip data.
468104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     *
469104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #getStripIndex()
470104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
471104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int getStripCount() {
472104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mStripCount;
473104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
474104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
475104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
476104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
477104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * get the strip size.
478104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
479104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int getStripSize() {
480104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mStripSizeTag == null)
481104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return 0;
482104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return (int) mStripSizeTag.getValueAt(0);
483104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
484104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
485104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
486104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * When receiving {@link #EVENT_COMPRESSED_IMAGE}, call this function to get
487104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * the image data size.
488104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
489104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int getCompressedImageSize() {
490104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mJpegSizeTag == null) {
491104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return 0;
492104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
493104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return (int) mJpegSizeTag.getValueAt(0);
494104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
495104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
496104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void skipTo(int offset) throws IOException {
497104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTiffStream.skipTo(offset);
498104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        while (!mCorrespondingEvent.isEmpty() && mCorrespondingEvent.firstKey() < offset) {
499104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mCorrespondingEvent.pollFirstEntry();
500104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
501104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
502104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
503104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
504104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * When getting {@link #EVENT_NEW_TAG} in the tag area of IFD, the tag may
505104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * not contain the value if the size of the value is greater than 4 bytes.
506104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * When the value is not available here, call this method so that the parser
507104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * will emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area
508104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * where the value is located.
509104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     *
510104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * @see #EVENT_VALUE_OF_REGISTERED_TAG
511104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
512104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected void registerForTagValue(ExifTag tag) {
513104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (tag.getOffset() >= mTiffStream.getReadByteCount()) {
514104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, true));
515104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
516104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
517104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
518104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void registerIfd(int ifdType, long offset) {
519104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // Cast unsigned int to int since the offset is always smaller
520104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // than the size of APP1 (65536)
521104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mCorrespondingEvent.put((int) offset, new IfdEvent(ifdType, isIfdRequested(ifdType)));
522104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
523104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
524104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void registerCompressedImage(long offset) {
525104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_COMPRESSED_IMAGE));
526104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
527104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
528104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void registerUncompressedStrip(int stripIndex, long offset) {
529104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_UNCOMPRESSED_STRIP
530104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                , stripIndex));
531104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
532104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
533104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private ExifTag readTag() throws IOException, ExifInvalidFormatException {
534104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        short tagId = mTiffStream.readShort();
535104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        short dataFormat = mTiffStream.readShort();
536104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        long numOfComp = mTiffStream.readUnsignedInt();
537104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (numOfComp > Integer.MAX_VALUE) {
538104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throw new ExifInvalidFormatException(
539104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    "Number of component is larger then Integer.MAX_VALUE");
540104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
541104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // Some invalid image file contains invalid data type. Ignore those tags
542104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (!ExifTag.isValidType(dataFormat)) {
543104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.w(TAG, String.format("Tag %04x: Invalid data type %d", tagId, dataFormat));
544104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mTiffStream.skip(4);
545104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return null;
546104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
547104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // TODO: handle numOfComp overflow
548104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ExifTag tag = new ExifTag(tagId, dataFormat, (int) numOfComp, mIfdType,
549104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                ((int) numOfComp) != ExifTag.SIZE_UNDEFINED);
550104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int dataSize = tag.getDataSize();
551104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (dataSize > 4) {
552104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            long offset = mTiffStream.readUnsignedInt();
553104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (offset > Integer.MAX_VALUE) {
554104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                throw new ExifInvalidFormatException(
555104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        "offset is larger then Integer.MAX_VALUE");
556104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
557104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // Some invalid images put some undefined data before IFD0.
558104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // Read the data here.
559104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if ((offset < mIfd0Position) && (dataFormat == ExifTag.TYPE_UNDEFINED)) {
560104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                byte[] buf = new byte[(int) numOfComp];
561104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                System.arraycopy(mDataAboveIfd0, (int) offset - DEFAULT_IFD0_OFFSET,
562104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        buf, 0, (int) numOfComp);
563104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                tag.setValue(buf);
564104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } else {
565104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                tag.setOffset((int) offset);
566104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
567104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else {
568104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            boolean defCount = tag.hasDefinedCount();
569104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // Set defined count to 0 so we can add \0 to non-terminated strings
570104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            tag.setHasDefinedCount(false);
571104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // Read value
572104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            readFullTagValue(tag);
573104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            tag.setHasDefinedCount(defCount);
574104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mTiffStream.skip(4 - dataSize);
575104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // Set the offset to the position of value.
576104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            tag.setOffset(mTiffStream.getReadByteCount() - 4);
577104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
578104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return tag;
579104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
580104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
581104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
582104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Check the tag, if the tag is one of the offset tag that points to the IFD
583104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * or image the caller is interested in, register the IFD or image.
584104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
585104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void checkOffsetOrImageTag(ExifTag tag) {
586104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // Some invalid formattd image contains tag with 0 size.
587104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (tag.getComponentCount() == 0) {
588104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
589104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
590104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        short tid = tag.getTagId();
591104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int ifd = tag.getIfd();
592104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (tid == TAG_EXIF_IFD && checkAllowed(ifd, ExifInterface.TAG_EXIF_IFD)) {
593104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (isIfdRequested(IfdId.TYPE_IFD_EXIF)
594104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
595104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                registerIfd(IfdId.TYPE_IFD_EXIF, tag.getValueAt(0));
596104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
597104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else if (tid == TAG_GPS_IFD && checkAllowed(ifd, ExifInterface.TAG_GPS_IFD)) {
598104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (isIfdRequested(IfdId.TYPE_IFD_GPS)) {
599104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                registerIfd(IfdId.TYPE_IFD_GPS, tag.getValueAt(0));
600104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
601104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else if (tid == TAG_INTEROPERABILITY_IFD
602104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                && checkAllowed(ifd, ExifInterface.TAG_INTEROPERABILITY_IFD)) {
603104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
604104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                registerIfd(IfdId.TYPE_IFD_INTEROPERABILITY, tag.getValueAt(0));
605104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
606104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT
607104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)) {
608104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (isThumbnailRequested()) {
609104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                registerCompressedImage(tag.getValueAt(0));
610104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
611104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT_LENGTH
612104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)) {
613104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (isThumbnailRequested()) {
614104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mJpegSizeTag = tag;
615104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
616104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else if (tid == TAG_STRIP_OFFSETS && checkAllowed(ifd, ExifInterface.TAG_STRIP_OFFSETS)) {
617104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (isThumbnailRequested()) {
618104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (tag.hasValue()) {
619104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    for (int i = 0; i < tag.getComponentCount(); i++) {
620104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        if (tag.getDataType() == ExifTag.TYPE_UNSIGNED_SHORT) {
621104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            registerUncompressedStrip(i, tag.getValueAt(i));
622104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        } else {
623104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            registerUncompressedStrip(i, tag.getValueAt(i));
624104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        }
625104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    }
626104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                } else {
627104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, false));
628104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
629104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
630104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else if (tid == TAG_STRIP_BYTE_COUNTS
631104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                && checkAllowed(ifd, ExifInterface.TAG_STRIP_BYTE_COUNTS)
632104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                &&isThumbnailRequested() && tag.hasValue()) {
633104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mStripSizeTag = tag;
634104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
635104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
636104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
637104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private boolean checkAllowed(int ifd, int tagId) {
638104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int info = mInterface.getTagInfo().get(tagId);
639104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (info == ExifInterface.DEFINITION_NULL) {
640104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return false;
641104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
642104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return ExifInterface.isIfdAllowed(info, ifd);
643104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
644104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
645104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected void readFullTagValue(ExifTag tag) throws IOException {
646104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // Some invalid images contains tags with wrong size, check it here
647104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        short type = tag.getDataType();
648104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (type == ExifTag.TYPE_ASCII || type == ExifTag.TYPE_UNDEFINED ||
649104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                type == ExifTag.TYPE_UNSIGNED_BYTE) {
650104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int size = tag.getComponentCount();
651104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mCorrespondingEvent.size() > 0) {
652104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (mCorrespondingEvent.firstEntry().getKey() < mTiffStream.getReadByteCount()
653104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        + size) {
654104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    Object event = mCorrespondingEvent.firstEntry().getValue();
655104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    if (event instanceof ImageEvent) {
656104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        // Tag value overlaps thumbnail, ignore thumbnail.
657104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        Log.w(TAG, "Thumbnail overlaps value for tag: \n" + tag.toString());
658104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
659104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        Log.w(TAG, "Invalid thumbnail offset: " + entry.getKey());
660104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    } else {
661104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        // Tag value overlaps another tag, shorten count
662104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        if (event instanceof IfdEvent) {
663104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            Log.w(TAG, "Ifd " + ((IfdEvent) event).ifd
664104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                    + " overlaps value for tag: \n" + tag.toString());
665104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        } else if (event instanceof ExifTagEvent) {
666104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            Log.w(TAG, "Tag value for tag: \n"
667104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                    + ((ExifTagEvent) event).tag.toString()
668104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                    + " overlaps value for tag: \n" + tag.toString());
669104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        }
670104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        size = mCorrespondingEvent.firstEntry().getKey()
671104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                - mTiffStream.getReadByteCount();
672104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        Log.w(TAG, "Invalid size of tag: \n" + tag.toString()
673104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                + " setting count to: " + size);
674104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        tag.forceSetComponentCount(size);
675104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    }
676104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
677104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
678104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
679104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        switch (tag.getDataType()) {
680104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNSIGNED_BYTE:
681104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNDEFINED: {
682104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                byte buf[] = new byte[tag.getComponentCount()];
683104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                read(buf);
684104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                tag.setValue(buf);
685104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
686104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
687104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_ASCII:
688104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                tag.setValue(readString(tag.getComponentCount()));
689104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
690104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNSIGNED_LONG: {
691104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                long value[] = new long[tag.getComponentCount()];
692104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                for (int i = 0, n = value.length; i < n; i++) {
693104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    value[i] = readUnsignedLong();
694104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
695104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                tag.setValue(value);
696104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
697104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
698104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNSIGNED_RATIONAL: {
699104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                Rational value[] = new Rational[tag.getComponentCount()];
700104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                for (int i = 0, n = value.length; i < n; i++) {
701104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    value[i] = readUnsignedRational();
702104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
703104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                tag.setValue(value);
704104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
705104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
706104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNSIGNED_SHORT: {
707104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                int value[] = new int[tag.getComponentCount()];
708104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                for (int i = 0, n = value.length; i < n; i++) {
709104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    value[i] = readUnsignedShort();
710104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
711104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                tag.setValue(value);
712104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
713104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
714104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_LONG: {
715104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                int value[] = new int[tag.getComponentCount()];
716104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                for (int i = 0, n = value.length; i < n; i++) {
717104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    value[i] = readLong();
718104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
719104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                tag.setValue(value);
720104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
721104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
722104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_RATIONAL: {
723104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                Rational value[] = new Rational[tag.getComponentCount()];
724104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                for (int i = 0, n = value.length; i < n; i++) {
725104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    value[i] = readRational();
726104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
727104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                tag.setValue(value);
728104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
729104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
730104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
731104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (LOGV) {
732104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.v(TAG, "\n" + tag.toString());
733104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
734104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
735104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
736104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void parseTiffHeader() throws IOException,
737104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifInvalidFormatException {
738104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        short byteOrder = mTiffStream.readShort();
739104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (LITTLE_ENDIAN_TAG == byteOrder) {
740104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mTiffStream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
741104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else if (BIG_ENDIAN_TAG == byteOrder) {
742104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mTiffStream.setByteOrder(ByteOrder.BIG_ENDIAN);
743104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else {
744104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throw new ExifInvalidFormatException("Invalid TIFF header");
745104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
746104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
747104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mTiffStream.readShort() != TIFF_HEADER_TAIL) {
748104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throw new ExifInvalidFormatException("Invalid TIFF header");
749104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
750104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
751104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
752104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private boolean seekTiffData(InputStream inputStream) throws IOException,
753104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifInvalidFormatException {
754104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        CountedDataInputStream dataStream = new CountedDataInputStream(inputStream);
755104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (dataStream.readShort() != JpegHeader.SOI) {
756104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throw new ExifInvalidFormatException("Invalid JPEG format");
757104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
758104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
759104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        short marker = dataStream.readShort();
760104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        while (marker != JpegHeader.EOI
761104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                && !JpegHeader.isSofMarker(marker)) {
762104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int length = dataStream.readUnsignedShort();
763104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // Some invalid formatted image contains multiple APP1,
764104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // try to find the one with Exif data.
765104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (marker == JpegHeader.APP1) {
766104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                int header = 0;
767104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                short headerTail = 0;
768104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (length >= 8) {
769104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    header = dataStream.readInt();
770104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    headerTail = dataStream.readShort();
771104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    length -= 6;
772104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    if (header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) {
773104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        mTiffStartPosition = dataStream.getReadByteCount();
774104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        mApp1End = length;
775104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        mOffsetToApp1EndFromSOF = mTiffStartPosition + mApp1End;
776104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        return true;
777104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    }
778104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
779104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
780104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (length < 2 || (length - 2) != dataStream.skip(length - 2)) {
781104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                Log.w(TAG, "Invalid JPEG format.");
782104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return false;
783104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
784104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            marker = dataStream.readShort();
785104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
786104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return false;
787104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
788104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
789104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int getOffsetToExifEndFromSOF() {
790104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mOffsetToApp1EndFromSOF;
791104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
792104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
793104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int getTiffStartPosition() {
794104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mTiffStartPosition;
795104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
796104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
797104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
798104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Reads bytes from the InputStream.
799104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
800104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int read(byte[] buffer, int offset, int length) throws IOException {
801104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mTiffStream.read(buffer, offset, length);
802104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
803104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
804104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
805104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Equivalent to read(buffer, 0, buffer.length).
806104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
807104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int read(byte[] buffer) throws IOException {
808104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mTiffStream.read(buffer);
809104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
810104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
811104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
812104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Reads a String from the InputStream with US-ASCII charset. The parser
813104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * will read n bytes and convert it to ascii string. This is used for
814104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * reading values of type {@link ExifTag#TYPE_ASCII}.
815104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
816104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected String readString(int n) throws IOException {
817104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return readString(n, US_ASCII);
818104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
819104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
820104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
821104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Reads a String from the InputStream with the given charset. The parser
822104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * will read n bytes and convert it to string. This is used for reading
823104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * values of type {@link ExifTag#TYPE_ASCII}.
824104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
825104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected String readString(int n, Charset charset) throws IOException {
826104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (n > 0) {
827104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return mTiffStream.readString(n, charset);
828104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else {
829104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return "";
830104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
831104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
832104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
833104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
834104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Reads value of type {@link ExifTag#TYPE_UNSIGNED_SHORT} from the
835104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * InputStream.
836104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
837104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int readUnsignedShort() throws IOException {
838104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mTiffStream.readShort() & 0xffff;
839104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
840104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
841104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
842104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Reads value of type {@link ExifTag#TYPE_UNSIGNED_LONG} from the
843104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * InputStream.
844104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
845104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected long readUnsignedLong() throws IOException {
846104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return readLong() & 0xffffffffL;
847104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
848104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
849104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
850104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Reads value of type {@link ExifTag#TYPE_UNSIGNED_RATIONAL} from the
851104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * InputStream.
852104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
853104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected Rational readUnsignedRational() throws IOException {
854104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        long nomi = readUnsignedLong();
855104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        long denomi = readUnsignedLong();
856104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return new Rational(nomi, denomi);
857104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
858104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
859104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
860104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Reads value of type {@link ExifTag#TYPE_LONG} from the InputStream.
861104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
862104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int readLong() throws IOException {
863104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mTiffStream.readInt();
864104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
865104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
866104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
867104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Reads value of type {@link ExifTag#TYPE_RATIONAL} from the InputStream.
868104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
869104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected Rational readRational() throws IOException {
870104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int nomi = readLong();
871104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int denomi = readLong();
872104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return new Rational(nomi, denomi);
873104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
874104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
875104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static class ImageEvent {
876104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int stripIndex;
877104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int type;
878104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
879104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ImageEvent(int type) {
880104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            this.stripIndex = 0;
881104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            this.type = type;
882104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
883104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
884104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ImageEvent(int type, int stripIndex) {
885104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            this.type = type;
886104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            this.stripIndex = stripIndex;
887104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
888104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
889104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
890104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static class IfdEvent {
891104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int ifd;
892104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        boolean isRequested;
893104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
894104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdEvent(int ifd, boolean isInterestedIfd) {
895104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            this.ifd = ifd;
896104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            this.isRequested = isInterestedIfd;
897104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
898104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
899104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
900104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static class ExifTagEvent {
901104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ExifTag tag;
902104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        boolean isRequested;
903104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
904104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ExifTagEvent(ExifTag tag, boolean isRequireByUser) {
905104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            this.tag = tag;
906104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            this.isRequested = isRequireByUser;
907104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
908104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
909104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
910104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
911104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Gets the byte order of the current InputStream.
912104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
913104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected ByteOrder getByteOrder() {
914104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mTiffStream.getByteOrder();
915104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
916104c45677660586026a7e74ef8c47d396403d50eMichael Jurka}
917