2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com
.android
.gallery3d
.exif
;
19 import android
.util
.Log
;
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
;
29 * This class provides a low-level EXIF parsing API. Given a JPEG format
30 * InputStream, the caller can request which IFD's to read via
31 * {@link #parse(InputStream, int)} with given options.
33 * Below is an example of getting EXIF data from IFD 0 and EXIF IFD using the
38 * ExifParser parser = ExifParser.parse(mImageInputStream,
39 * ExifParser.OPTION_IFD_0 | ExifParser.OPTIONS_IFD_EXIF);
40 * int event = parser.next();
41 * while (event != ExifParser.EVENT_END) {
43 * case ExifParser.EVENT_START_OF_IFD:
45 * case ExifParser.EVENT_NEW_TAG:
46 * ExifTag tag = parser.getTag();
47 * if (!tag.hasValue()) {
48 * parser.registerForTagValue(tag);
53 * case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
54 * tag = parser.getTag();
55 * if (tag.getDataType() != ExifTag.TYPE_UNDEFINED) {
60 * event = parser.next();
64 * void processTag(ExifTag tag) {
65 * // process the tag as you like.
70 private static final boolean LOGV
= false;
71 private static final String TAG
= "ExifParser";
73 * When the parser reaches a new IFD area. Call {@link #getCurrentIfd()} to
74 * know which IFD we are in.
76 public static final int EVENT_START_OF_IFD
= 0;
78 * When the parser reaches a new tag. Call {@link #getTag()}to get the
81 public static final int EVENT_NEW_TAG
= 1;
83 * When the parser reaches the value area of tag that is registered by
84 * {@link #registerForTagValue(ExifTag)} previously. Call {@link #getTag()}
85 * to get the corresponding tag.
87 public static final int EVENT_VALUE_OF_REGISTERED_TAG
= 2;
90 * When the parser reaches the compressed image area.
92 public static final int EVENT_COMPRESSED_IMAGE
= 3;
94 * When the parser reaches the uncompressed image strip. Call
95 * {@link #getStripIndex()} to get the index of the strip.
97 * @see #getStripIndex()
98 * @see #getStripCount()
100 public static final int EVENT_UNCOMPRESSED_STRIP
= 4;
102 * When there is nothing more to parse.
104 public static final int EVENT_END
= 5;
107 * Option bit to request to parse IFD0.
109 public static final int OPTION_IFD_0
= 1 << 0;
111 * Option bit to request to parse IFD1.
113 public static final int OPTION_IFD_1
= 1 << 1;
115 * Option bit to request to parse Exif-IFD.
117 public static final int OPTION_IFD_EXIF
= 1 << 2;
119 * Option bit to request to parse GPS-IFD.
121 public static final int OPTION_IFD_GPS
= 1 << 3;
123 * Option bit to request to parse Interoperability-IFD.
125 public static final int OPTION_IFD_INTEROPERABILITY
= 1 << 4;
127 * Option bit to request to parse thumbnail.
129 public static final int OPTION_THUMBNAIL
= 1 << 5;
131 protected static final int EXIF_HEADER
= 0x45786966; // EXIF header "Exif"
132 protected static final short EXIF_HEADER_TAIL
= (short) 0x0000; // EXIF header in APP1
135 protected static final short LITTLE_ENDIAN_TAG
= (short) 0x4949; // "II"
136 protected static final short BIG_ENDIAN_TAG
= (short) 0x4d4d; // "MM"
137 protected static final short TIFF_HEADER_TAIL
= 0x002A;
139 protected static final int TAG_SIZE
= 12;
140 protected static final int OFFSET_SIZE
= 2;
142 private static final Charset US_ASCII
= Charset
.forName("US-ASCII");
144 protected static final int DEFAULT_IFD0_OFFSET
= 8;
146 private final CountedDataInputStream mTiffStream
;
147 private final int mOptions
;
148 private int mIfdStartOffset
= 0;
149 private int mNumOfTagInIfd
= 0;
150 private int mIfdType
;
151 private ExifTag mTag
;
152 private ImageEvent mImageEvent
;
153 private int mStripCount
;
154 private ExifTag mStripSizeTag
;
155 private ExifTag mJpegSizeTag
;
156 private boolean mNeedToParseOffsetsInCurrentIfd
;
157 private boolean mContainExifData
= false;
158 private int mApp1End
;
159 private int mOffsetToApp1EndFromSOF
= 0;
160 private byte[] mDataAboveIfd0
;
161 private int mIfd0Position
;
162 private int mTiffStartPosition
;
163 private final ExifInterface mInterface
;
165 private static final short TAG_EXIF_IFD
= ExifInterface
166 .getTrueTagKey(ExifInterface
.TAG_EXIF_IFD
);
167 private static final short TAG_GPS_IFD
= ExifInterface
.getTrueTagKey(ExifInterface
.TAG_GPS_IFD
);
168 private static final short TAG_INTEROPERABILITY_IFD
= ExifInterface
169 .getTrueTagKey(ExifInterface
.TAG_INTEROPERABILITY_IFD
);
170 private static final short TAG_JPEG_INTERCHANGE_FORMAT
= ExifInterface
171 .getTrueTagKey(ExifInterface
.TAG_JPEG_INTERCHANGE_FORMAT
);
172 private static final short TAG_JPEG_INTERCHANGE_FORMAT_LENGTH
= ExifInterface
173 .getTrueTagKey(ExifInterface
.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH
);
174 private static final short TAG_STRIP_OFFSETS
= ExifInterface
175 .getTrueTagKey(ExifInterface
.TAG_STRIP_OFFSETS
);
176 private static final short TAG_STRIP_BYTE_COUNTS
= ExifInterface
177 .getTrueTagKey(ExifInterface
.TAG_STRIP_BYTE_COUNTS
);
179 private final TreeMap
<Integer
, Object
> mCorrespondingEvent
= new TreeMap
<Integer
, Object
>();
181 private boolean isIfdRequested(int ifdType
) {
183 case IfdId
.TYPE_IFD_0
:
184 return (mOptions
& OPTION_IFD_0
) != 0;
185 case IfdId
.TYPE_IFD_1
:
186 return (mOptions
& OPTION_IFD_1
) != 0;
187 case IfdId
.TYPE_IFD_EXIF
:
188 return (mOptions
& OPTION_IFD_EXIF
) != 0;
189 case IfdId
.TYPE_IFD_GPS
:
190 return (mOptions
& OPTION_IFD_GPS
) != 0;
191 case IfdId
.TYPE_IFD_INTEROPERABILITY
:
192 return (mOptions
& OPTION_IFD_INTEROPERABILITY
) != 0;
197 private boolean isThumbnailRequested() {
198 return (mOptions
& OPTION_THUMBNAIL
) != 0;
201 private ExifParser(InputStream inputStream
, int options
, ExifInterface iRef
)
202 throws IOException
, ExifInvalidFormatException
{
203 if (inputStream
== null) {
204 throw new IOException("Null argument inputStream to ExifParser");
207 Log
.v(TAG
, "Reading exif...");
210 mContainExifData
= seekTiffData(inputStream
);
211 mTiffStream
= new CountedDataInputStream(inputStream
);
213 if (!mContainExifData
) {
218 long offset
= mTiffStream
.readUnsignedInt();
219 if (offset
> Integer
.MAX_VALUE
) {
220 throw new ExifInvalidFormatException("Invalid offset " + offset
);
222 mIfd0Position
= (int) offset
;
223 mIfdType
= IfdId
.TYPE_IFD_0
;
224 if (isIfdRequested(IfdId
.TYPE_IFD_0
) || needToParseOffsetsInCurrentIfd()) {
225 registerIfd(IfdId
.TYPE_IFD_0
, offset
);
226 if (offset
!= DEFAULT_IFD0_OFFSET
) {
227 mDataAboveIfd0
= new byte[(int) offset
- DEFAULT_IFD0_OFFSET
];
228 read(mDataAboveIfd0
);
234 * Parses the the given InputStream with the given options
236 * @exception IOException
237 * @exception ExifInvalidFormatException
239 protected static ExifParser
parse(InputStream inputStream
, int options
, ExifInterface iRef
)
240 throws IOException
, ExifInvalidFormatException
{
241 return new ExifParser(inputStream
, options
, iRef
);
245 * Parses the the given InputStream with default options; that is, every IFD
246 * and thumbnaill will be parsed.
248 * @exception IOException
249 * @exception ExifInvalidFormatException
250 * @see #parse(InputStream, int)
252 protected static ExifParser
parse(InputStream inputStream
, ExifInterface iRef
)
253 throws IOException
, ExifInvalidFormatException
{
254 return new ExifParser(inputStream
, OPTION_IFD_0
| OPTION_IFD_1
255 | OPTION_IFD_EXIF
| OPTION_IFD_GPS
| OPTION_IFD_INTEROPERABILITY
256 | OPTION_THUMBNAIL
, iRef
);
260 * Moves the parser forward and returns the next parsing event
262 * @exception IOException
263 * @exception ExifInvalidFormatException
264 * @see #EVENT_START_OF_IFD
265 * @see #EVENT_NEW_TAG
266 * @see #EVENT_VALUE_OF_REGISTERED_TAG
267 * @see #EVENT_COMPRESSED_IMAGE
268 * @see #EVENT_UNCOMPRESSED_STRIP
271 protected int next() throws IOException
, ExifInvalidFormatException
{
272 if (!mContainExifData
) {
275 int offset
= mTiffStream
.getReadByteCount();
276 int endOfTags
= mIfdStartOffset
+ OFFSET_SIZE
+ TAG_SIZE
* mNumOfTagInIfd
;
277 if (offset
< endOfTags
) {
282 if (mNeedToParseOffsetsInCurrentIfd
) {
283 checkOffsetOrImageTag(mTag
);
285 return EVENT_NEW_TAG
;
286 } else if (offset
== endOfTags
) {
287 // There is a link to ifd1 at the end of ifd0
288 if (mIfdType
== IfdId
.TYPE_IFD_0
) {
289 long ifdOffset
= readUnsignedLong();
290 if (isIfdRequested(IfdId
.TYPE_IFD_1
) || isThumbnailRequested()) {
291 if (ifdOffset
!= 0) {
292 registerIfd(IfdId
.TYPE_IFD_1
, ifdOffset
);
297 // Some camera models use invalid length of the offset
298 if (mCorrespondingEvent
.size() > 0) {
299 offsetSize
= mCorrespondingEvent
.firstEntry().getKey() -
300 mTiffStream
.getReadByteCount();
302 if (offsetSize
< 4) {
303 Log
.w(TAG
, "Invalid size of link to next IFD: " + offsetSize
);
305 long ifdOffset
= readUnsignedLong();
306 if (ifdOffset
!= 0) {
307 Log
.w(TAG
, "Invalid link to next IFD: " + ifdOffset
);
312 while (mCorrespondingEvent
.size() != 0) {
313 Entry
<Integer
, Object
> entry
= mCorrespondingEvent
.pollFirstEntry();
314 Object event
= entry
.getValue();
316 skipTo(entry
.getKey());
317 } catch (IOException e
) {
318 Log
.w(TAG
, "Failed to skip to data at: " + entry
.getKey() +
319 " for " + event
.getClass().getName() + ", the file may be broken.");
322 if (event
instanceof IfdEvent
) {
323 mIfdType
= ((IfdEvent
) event
).ifd
;
324 mNumOfTagInIfd
= mTiffStream
.readUnsignedShort();
325 mIfdStartOffset
= entry
.getKey();
327 if (mNumOfTagInIfd
* TAG_SIZE
+ mIfdStartOffset
+ OFFSET_SIZE
> mApp1End
) {
328 Log
.w(TAG
, "Invalid size of IFD " + mIfdType
);
332 mNeedToParseOffsetsInCurrentIfd
= needToParseOffsetsInCurrentIfd();
333 if (((IfdEvent
) event
).isRequested
) {
334 return EVENT_START_OF_IFD
;
336 skipRemainingTagsInCurrentIfd();
338 } else if (event
instanceof ImageEvent
) {
339 mImageEvent
= (ImageEvent
) event
;
340 return mImageEvent
.type
;
342 ExifTagEvent tagEvent
= (ExifTagEvent
) event
;
344 if (mTag
.getDataType() != ExifTag
.TYPE_UNDEFINED
) {
345 readFullTagValue(mTag
);
346 checkOffsetOrImageTag(mTag
);
348 if (tagEvent
.isRequested
) {
349 return EVENT_VALUE_OF_REGISTERED_TAG
;
357 * Skips the tags area of current IFD, if the parser is not in the tag area,
358 * nothing will happen.
360 * @throws IOException
361 * @throws ExifInvalidFormatException
363 protected void skipRemainingTagsInCurrentIfd() throws IOException
, ExifInvalidFormatException
{
364 int endOfTags
= mIfdStartOffset
+ OFFSET_SIZE
+ TAG_SIZE
* mNumOfTagInIfd
;
365 int offset
= mTiffStream
.getReadByteCount();
366 if (offset
> endOfTags
) {
369 if (mNeedToParseOffsetsInCurrentIfd
) {
370 while (offset
< endOfTags
) {
376 checkOffsetOrImageTag(mTag
);
381 long ifdOffset
= readUnsignedLong();
382 // For ifd0, there is a link to ifd1 in the end of all tags
383 if (mIfdType
== IfdId
.TYPE_IFD_0
384 && (isIfdRequested(IfdId
.TYPE_IFD_1
) || isThumbnailRequested())) {
386 registerIfd(IfdId
.TYPE_IFD_1
, ifdOffset
);
391 private boolean needToParseOffsetsInCurrentIfd() {
393 case IfdId
.TYPE_IFD_0
:
394 return isIfdRequested(IfdId
.TYPE_IFD_EXIF
) || isIfdRequested(IfdId
.TYPE_IFD_GPS
)
395 || isIfdRequested(IfdId
.TYPE_IFD_INTEROPERABILITY
)
396 || isIfdRequested(IfdId
.TYPE_IFD_1
);
397 case IfdId
.TYPE_IFD_1
:
398 return isThumbnailRequested();
399 case IfdId
.TYPE_IFD_EXIF
:
400 // The offset to interoperability IFD is located in Exif IFD
401 return isIfdRequested(IfdId
.TYPE_IFD_INTEROPERABILITY
);
408 * If {@link #next()} return {@link #EVENT_NEW_TAG} or
409 * {@link #EVENT_VALUE_OF_REGISTERED_TAG}, call this function to get the
412 * For {@link #EVENT_NEW_TAG}, the tag may not contain the value if the size
413 * of the value is greater than 4 bytes. One should call
414 * {@link ExifTag#hasValue()} to check if the tag contains value. If there
415 * is no value,call {@link #registerForTagValue(ExifTag)} to have the parser
416 * emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area
417 * pointed by the offset.
419 * When {@link #EVENT_VALUE_OF_REGISTERED_TAG} is emitted, the value of the
420 * tag will have already been read except for tags of undefined type. For
421 * tags of undefined type, call one of the read methods to get the value.
423 * @see #registerForTagValue(ExifTag)
425 * @see #read(byte[], int, int)
427 * @see #readRational()
428 * @see #readString(int)
429 * @see #readString(int, Charset)
431 protected ExifTag
getTag() {
436 * Gets number of tags in the current IFD area.
438 protected int getTagCountInCurrentIfd() {
439 return mNumOfTagInIfd
;
443 * Gets the ID of current IFD.
445 * @see IfdId#TYPE_IFD_0
446 * @see IfdId#TYPE_IFD_1
447 * @see IfdId#TYPE_IFD_GPS
448 * @see IfdId#TYPE_IFD_INTEROPERABILITY
449 * @see IfdId#TYPE_IFD_EXIF
451 protected int getCurrentIfd() {
456 * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
457 * get the index of this strip.
459 * @see #getStripCount()
461 protected int getStripIndex() {
462 return mImageEvent
.stripIndex
;
466 * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
467 * get the number of strip data.
469 * @see #getStripIndex()
471 protected int getStripCount() {
476 * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
477 * get the strip size.
479 protected int getStripSize() {
480 if (mStripSizeTag
== null)
482 return (int) mStripSizeTag
.getValueAt(0);
486 * When receiving {@link #EVENT_COMPRESSED_IMAGE}, call this function to get
487 * the image data size.
489 protected int getCompressedImageSize() {
490 if (mJpegSizeTag
== null) {
493 return (int) mJpegSizeTag
.getValueAt(0);
496 private void skipTo(int offset
) throws IOException
{
497 mTiffStream
.skipTo(offset
);
498 while (!mCorrespondingEvent
.isEmpty() && mCorrespondingEvent
.firstKey() < offset
) {
499 mCorrespondingEvent
.pollFirstEntry();
504 * When getting {@link #EVENT_NEW_TAG} in the tag area of IFD, the tag may
505 * not contain the value if the size of the value is greater than 4 bytes.
506 * When the value is not available here, call this method so that the parser
507 * will emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area
508 * where the value is located.
510 * @see #EVENT_VALUE_OF_REGISTERED_TAG
512 protected void registerForTagValue(ExifTag tag
) {
513 if (tag
.getOffset() >= mTiffStream
.getReadByteCount()) {
514 mCorrespondingEvent
.put(tag
.getOffset(), new ExifTagEvent(tag
, true));
518 private void registerIfd(int ifdType
, long offset
) {
519 // Cast unsigned int to int since the offset is always smaller
520 // than the size of APP1 (65536)
521 mCorrespondingEvent
.put((int) offset
, new IfdEvent(ifdType
, isIfdRequested(ifdType
)));
524 private void registerCompressedImage(long offset
) {
525 mCorrespondingEvent
.put((int) offset
, new ImageEvent(EVENT_COMPRESSED_IMAGE
));
528 private void registerUncompressedStrip(int stripIndex
, long offset
) {
529 mCorrespondingEvent
.put((int) offset
, new ImageEvent(EVENT_UNCOMPRESSED_STRIP
533 private ExifTag
readTag() throws IOException
, ExifInvalidFormatException
{
534 short tagId
= mTiffStream
.readShort();
535 short dataFormat
= mTiffStream
.readShort();
536 long numOfComp
= mTiffStream
.readUnsignedInt();
537 if (numOfComp
> Integer
.MAX_VALUE
) {
538 throw new ExifInvalidFormatException(
539 "Number of component is larger then Integer.MAX_VALUE");
541 // Some invalid image file contains invalid data type. Ignore those tags
542 if (!ExifTag
.isValidType(dataFormat
)) {
543 Log
.w(TAG
, String
.format("Tag %04x: Invalid data type %d", tagId
, dataFormat
));
547 // TODO: handle numOfComp overflow
548 ExifTag tag
= new ExifTag(tagId
, dataFormat
, (int) numOfComp
, mIfdType
,
549 ((int) numOfComp
) != ExifTag
.SIZE_UNDEFINED
);
550 int dataSize
= tag
.getDataSize();
552 long offset
= mTiffStream
.readUnsignedInt();
553 if (offset
> Integer
.MAX_VALUE
) {
554 throw new ExifInvalidFormatException(
555 "offset is larger then Integer.MAX_VALUE");
557 // Some invalid images put some undefined data before IFD0.
558 // Read the data here.
559 if ((offset
< mIfd0Position
) && (dataFormat
== ExifTag
.TYPE_UNDEFINED
)) {
560 byte[] buf
= new byte[(int) numOfComp
];
561 System
.arraycopy(mDataAboveIfd0
, (int) offset
- DEFAULT_IFD0_OFFSET
,
562 buf
, 0, (int) numOfComp
);
565 tag
.setOffset((int) offset
);
568 boolean defCount
= tag
.hasDefinedCount();
569 // Set defined count to 0 so we can add \0 to non-terminated strings
570 tag
.setHasDefinedCount(false);
572 readFullTagValue(tag
);
573 tag
.setHasDefinedCount(defCount
);
574 mTiffStream
.skip(4 - dataSize
);
575 // Set the offset to the position of value.
576 tag
.setOffset(mTiffStream
.getReadByteCount() - 4);
582 * Check the tag, if the tag is one of the offset tag that points to the IFD
583 * or image the caller is interested in, register the IFD or image.
585 private void checkOffsetOrImageTag(ExifTag tag
) {
586 // Some invalid formattd image contains tag with 0 size.
587 if (tag
.getComponentCount() == 0) {
590 short tid
= tag
.getTagId();
591 int ifd
= tag
.getIfd();
592 if (tid
== TAG_EXIF_IFD
&& checkAllowed(ifd
, ExifInterface
.TAG_EXIF_IFD
)) {
593 if (isIfdRequested(IfdId
.TYPE_IFD_EXIF
)
594 || isIfdRequested(IfdId
.TYPE_IFD_INTEROPERABILITY
)) {
595 registerIfd(IfdId
.TYPE_IFD_EXIF
, tag
.getValueAt(0));
597 } else if (tid
== TAG_GPS_IFD
&& checkAllowed(ifd
, ExifInterface
.TAG_GPS_IFD
)) {
598 if (isIfdRequested(IfdId
.TYPE_IFD_GPS
)) {
599 registerIfd(IfdId
.TYPE_IFD_GPS
, tag
.getValueAt(0));
601 } else if (tid
== TAG_INTEROPERABILITY_IFD
602 && checkAllowed(ifd
, ExifInterface
.TAG_INTEROPERABILITY_IFD
)) {
603 if (isIfdRequested(IfdId
.TYPE_IFD_INTEROPERABILITY
)) {
604 registerIfd(IfdId
.TYPE_IFD_INTEROPERABILITY
, tag
.getValueAt(0));
606 } else if (tid
== TAG_JPEG_INTERCHANGE_FORMAT
607 && checkAllowed(ifd
, ExifInterface
.TAG_JPEG_INTERCHANGE_FORMAT
)) {
608 if (isThumbnailRequested()) {
609 registerCompressedImage(tag
.getValueAt(0));
611 } else if (tid
== TAG_JPEG_INTERCHANGE_FORMAT_LENGTH
612 && checkAllowed(ifd
, ExifInterface
.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH
)) {
613 if (isThumbnailRequested()) {
616 } else if (tid
== TAG_STRIP_OFFSETS
&& checkAllowed(ifd
, ExifInterface
.TAG_STRIP_OFFSETS
)) {
617 if (isThumbnailRequested()) {
618 if (tag
.hasValue()) {
619 for (int i
= 0; i
< tag
.getComponentCount(); i
++) {
620 if (tag
.getDataType() == ExifTag
.TYPE_UNSIGNED_SHORT
) {
621 registerUncompressedStrip(i
, tag
.getValueAt(i
));
623 registerUncompressedStrip(i
, tag
.getValueAt(i
));
627 mCorrespondingEvent
.put(tag
.getOffset(), new ExifTagEvent(tag
, false));
630 } else if (tid
== TAG_STRIP_BYTE_COUNTS
631 && checkAllowed(ifd
, ExifInterface
.TAG_STRIP_BYTE_COUNTS
)
632 &&isThumbnailRequested() && tag
.hasValue()) {
637 private boolean checkAllowed(int ifd
, int tagId
) {
638 int info
= mInterface
.getTagInfo().get(tagId
);
639 if (info
== ExifInterface
.DEFINITION_NULL
) {
642 return ExifInterface
.isIfdAllowed(info
, ifd
);
645 protected void readFullTagValue(ExifTag tag
) throws IOException
{
646 // Some invalid images contains tags with wrong size, check it here
647 short type
= tag
.getDataType();
648 if (type
== ExifTag
.TYPE_ASCII
|| type
== ExifTag
.TYPE_UNDEFINED
||
649 type
== ExifTag
.TYPE_UNSIGNED_BYTE
) {
650 int size
= tag
.getComponentCount();
651 if (mCorrespondingEvent
.size() > 0) {
652 if (mCorrespondingEvent
.firstEntry().getKey() < mTiffStream
.getReadByteCount()
654 Object event
= mCorrespondingEvent
.firstEntry().getValue();
655 if (event
instanceof ImageEvent
) {
656 // Tag value overlaps thumbnail, ignore thumbnail.
657 Log
.w(TAG
, "Thumbnail overlaps value for tag: \n" + tag
.toString());
658 Entry
<Integer
, Object
> entry
= mCorrespondingEvent
.pollFirstEntry();
659 Log
.w(TAG
, "Invalid thumbnail offset: " + entry
.getKey());
661 // Tag value overlaps another tag, shorten count
662 if (event
instanceof IfdEvent
) {
663 Log
.w(TAG
, "Ifd " + ((IfdEvent
) event
).ifd
664 + " overlaps value for tag: \n" + tag
.toString());
665 } else if (event
instanceof ExifTagEvent
) {
666 Log
.w(TAG
, "Tag value for tag: \n"
667 + ((ExifTagEvent
) event
).tag
.toString()
668 + " overlaps value for tag: \n" + tag
.toString());
670 size
= mCorrespondingEvent
.firstEntry().getKey()
671 - mTiffStream
.getReadByteCount();
672 Log
.w(TAG
, "Invalid size of tag: \n" + tag
.toString()
673 + " setting count to: " + size
);
674 tag
.forceSetComponentCount(size
);
679 switch (tag
.getDataType()) {
680 case ExifTag
.TYPE_UNSIGNED_BYTE
:
681 case ExifTag
.TYPE_UNDEFINED
: {
682 byte buf
[] = new byte[tag
.getComponentCount()];
687 case ExifTag
.TYPE_ASCII
:
688 tag
.setValue(readString(tag
.getComponentCount()));
690 case ExifTag
.TYPE_UNSIGNED_LONG
: {
691 long value
[] = new long[tag
.getComponentCount()];
692 for (int i
= 0, n
= value
.length
; i
< n
; i
++) {
693 value
[i
] = readUnsignedLong();
698 case ExifTag
.TYPE_UNSIGNED_RATIONAL
: {
699 Rational value
[] = new Rational
[tag
.getComponentCount()];
700 for (int i
= 0, n
= value
.length
; i
< n
; i
++) {
701 value
[i
] = readUnsignedRational();
706 case ExifTag
.TYPE_UNSIGNED_SHORT
: {
707 int value
[] = new int[tag
.getComponentCount()];
708 for (int i
= 0, n
= value
.length
; i
< n
; i
++) {
709 value
[i
] = readUnsignedShort();
714 case ExifTag
.TYPE_LONG
: {
715 int value
[] = new int[tag
.getComponentCount()];
716 for (int i
= 0, n
= value
.length
; i
< n
; i
++) {
717 value
[i
] = readLong();
722 case ExifTag
.TYPE_RATIONAL
: {
723 Rational value
[] = new Rational
[tag
.getComponentCount()];
724 for (int i
= 0, n
= value
.length
; i
< n
; i
++) {
725 value
[i
] = readRational();
732 Log
.v(TAG
, "\n" + tag
.toString());
736 private void parseTiffHeader() throws IOException
,
737 ExifInvalidFormatException
{
738 short byteOrder
= mTiffStream
.readShort();
739 if (LITTLE_ENDIAN_TAG
== byteOrder
) {
740 mTiffStream
.setByteOrder(ByteOrder
.LITTLE_ENDIAN
);
741 } else if (BIG_ENDIAN_TAG
== byteOrder
) {
742 mTiffStream
.setByteOrder(ByteOrder
.BIG_ENDIAN
);
744 throw new ExifInvalidFormatException("Invalid TIFF header");
747 if (mTiffStream
.readShort() != TIFF_HEADER_TAIL
) {
748 throw new ExifInvalidFormatException("Invalid TIFF header");
752 private boolean seekTiffData(InputStream inputStream
) throws IOException
,
753 ExifInvalidFormatException
{
754 CountedDataInputStream dataStream
= new CountedDataInputStream(inputStream
);
755 if (dataStream
.readShort() != JpegHeader
.SOI
) {
756 throw new ExifInvalidFormatException("Invalid JPEG format");
759 short marker
= dataStream
.readShort();
760 while (marker
!= JpegHeader
.EOI
761 && !JpegHeader
.isSofMarker(marker
)) {
762 int length
= dataStream
.readUnsignedShort();
763 // Some invalid formatted image contains multiple APP1,
764 // try to find the one with Exif data.
765 if (marker
== JpegHeader
.APP1
) {
767 short headerTail
= 0;
769 header
= dataStream
.readInt();
770 headerTail
= dataStream
.readShort();
772 if (header
== EXIF_HEADER
&& headerTail
== EXIF_HEADER_TAIL
) {
773 mTiffStartPosition
= dataStream
.getReadByteCount();
775 mOffsetToApp1EndFromSOF
= mTiffStartPosition
+ mApp1End
;
780 if (length
< 2 || (length
- 2) != dataStream
.skip(length
- 2)) {
781 Log
.w(TAG
, "Invalid JPEG format.");
784 marker
= dataStream
.readShort();
789 protected int getOffsetToExifEndFromSOF() {
790 return mOffsetToApp1EndFromSOF
;
793 protected int getTiffStartPosition() {
794 return mTiffStartPosition
;
798 * Reads bytes from the InputStream.
800 protected int read(byte[] buffer
, int offset
, int length
) throws IOException
{
801 return mTiffStream
.read(buffer
, offset
, length
);
805 * Equivalent to read(buffer, 0, buffer.length).
807 protected int read(byte[] buffer
) throws IOException
{
808 return mTiffStream
.read(buffer
);
812 * Reads a String from the InputStream with US-ASCII charset. The parser
813 * will read n bytes and convert it to ascii string. This is used for
814 * reading values of type {@link ExifTag#TYPE_ASCII}.
816 protected String
readString(int n
) throws IOException
{
817 return readString(n
, US_ASCII
);
821 * Reads a String from the InputStream with the given charset. The parser
822 * will read n bytes and convert it to string. This is used for reading
823 * values of type {@link ExifTag#TYPE_ASCII}.
825 protected String
readString(int n
, Charset charset
) throws IOException
{
827 return mTiffStream
.readString(n
, charset
);
834 * Reads value of type {@link ExifTag#TYPE_UNSIGNED_SHORT} from the
837 protected int readUnsignedShort() throws IOException
{
838 return mTiffStream
.readShort() & 0xffff;
842 * Reads value of type {@link ExifTag#TYPE_UNSIGNED_LONG} from the
845 protected long readUnsignedLong() throws IOException
{
846 return readLong() & 0xffffffffL
;
850 * Reads value of type {@link ExifTag#TYPE_UNSIGNED_RATIONAL} from the
853 protected Rational
readUnsignedRational() throws IOException
{
854 long nomi
= readUnsignedLong();
855 long denomi
= readUnsignedLong();
856 return new Rational(nomi
, denomi
);
860 * Reads value of type {@link ExifTag#TYPE_LONG} from the InputStream.
862 protected int readLong() throws IOException
{
863 return mTiffStream
.readInt();
867 * Reads value of type {@link ExifTag#TYPE_RATIONAL} from the InputStream.
869 protected Rational
readRational() throws IOException
{
870 int nomi
= readLong();
871 int denomi
= readLong();
872 return new Rational(nomi
, denomi
);
875 private static class ImageEvent
{
879 ImageEvent(int type
) {
884 ImageEvent(int type
, int stripIndex
) {
886 this.stripIndex
= stripIndex
;
890 private static class IfdEvent
{
894 IfdEvent(int ifd
, boolean isInterestedIfd
) {
896 this.isRequested
= isInterestedIfd
;
900 private static class ExifTagEvent
{
904 ExifTagEvent(ExifTag tag
, boolean isRequireByUser
) {
906 this.isRequested
= isRequireByUser
;
911 * Gets the byte order of the current InputStream.
913 protected ByteOrder
getByteOrder() {
914 return mTiffStream
.getByteOrder();