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