PageRenderTime 54ms CodeModel.GetById 10ms app.highlight 37ms RepoModel.GetById 1ms app.codeStats 0ms

/packages/WallpaperCropper/src/com/android/gallery3d/exif/ExifParser.java

https://github.com/aizuzi/platform_frameworks_base
Java | 916 lines | 593 code | 64 blank | 259 comment | 169 complexity | 8c2c90db5bb7c6491f6bad65a7b7d123 MD5 | raw file
  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
 17package com.android.gallery3d.exif;
 18
 19import android.util.Log;
 20
 21import java.io.IOException;
 22import java.io.InputStream;
 23import java.nio.ByteOrder;
 24import java.nio.charset.Charset;
 25import java.util.Map.Entry;
 26import java.util.TreeMap;
 27
 28/**
 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.
 32 * <p>
 33 * Below is an example of getting EXIF data from IFD 0 and EXIF IFD using the
 34 * parser.
 35 *
 36 * <pre>
 37 * void parse() {
 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) {
 42 *         switch (event) {
 43 *             case ExifParser.EVENT_START_OF_IFD:
 44 *                 break;
 45 *             case ExifParser.EVENT_NEW_TAG:
 46 *                 ExifTag tag = parser.getTag();
 47 *                 if (!tag.hasValue()) {
 48 *                     parser.registerForTagValue(tag);
 49 *                 } else {
 50 *                     processTag(tag);
 51 *                 }
 52 *                 break;
 53 *             case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
 54 *                 tag = parser.getTag();
 55 *                 if (tag.getDataType() != ExifTag.TYPE_UNDEFINED) {
 56 *                     processTag(tag);
 57 *                 }
 58 *                 break;
 59 *         }
 60 *         event = parser.next();
 61 *     }
 62 * }
 63 *
 64 * void processTag(ExifTag tag) {
 65 *     // process the tag as you like.
 66 * }
 67 * </pre>
 68 */
 69class ExifParser {
 70    private static final boolean LOGV = false;
 71    private static final String TAG = "ExifParser";
 72    /**
 73     * When the parser reaches a new IFD area. Call {@link #getCurrentIfd()} to
 74     * know which IFD we are in.
 75     */
 76    public static final int EVENT_START_OF_IFD = 0;
 77    /**
 78     * When the parser reaches a new tag. Call {@link #getTag()}to get the
 79     * corresponding tag.
 80     */
 81    public static final int EVENT_NEW_TAG = 1;
 82    /**
 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.
 86     */
 87    public static final int EVENT_VALUE_OF_REGISTERED_TAG = 2;
 88
 89    /**
 90     * When the parser reaches the compressed image area.
 91     */
 92    public static final int EVENT_COMPRESSED_IMAGE = 3;
 93    /**
 94     * When the parser reaches the uncompressed image strip. Call
 95     * {@link #getStripIndex()} to get the index of the strip.
 96     *
 97     * @see #getStripIndex()
 98     * @see #getStripCount()
 99     */
100    public static final int EVENT_UNCOMPRESSED_STRIP = 4;
101    /**
102     * When there is nothing more to parse.
103     */
104    public static final int EVENT_END = 5;
105
106    /**
107     * Option bit to request to parse IFD0.
108     */
109    public static final int OPTION_IFD_0 = 1 << 0;
110    /**
111     * Option bit to request to parse IFD1.
112     */
113    public static final int OPTION_IFD_1 = 1 << 1;
114    /**
115     * Option bit to request to parse Exif-IFD.
116     */
117    public static final int OPTION_IFD_EXIF = 1 << 2;
118    /**
119     * Option bit to request to parse GPS-IFD.
120     */
121    public static final int OPTION_IFD_GPS = 1 << 3;
122    /**
123     * Option bit to request to parse Interoperability-IFD.
124     */
125    public static final int OPTION_IFD_INTEROPERABILITY = 1 << 4;
126    /**
127     * Option bit to request to parse thumbnail.
128     */
129    public static final int OPTION_THUMBNAIL = 1 << 5;
130
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
133
134    // TIFF header
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;
138
139    protected static final int TAG_SIZE = 12;
140    protected static final int OFFSET_SIZE = 2;
141
142    private static final Charset US_ASCII = Charset.forName("US-ASCII");
143
144    protected static final int DEFAULT_IFD0_OFFSET = 8;
145
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;
164
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);
178
179    private final TreeMap<Integer, Object> mCorrespondingEvent = new TreeMap<Integer, Object>();
180
181    private boolean isIfdRequested(int ifdType) {
182        switch (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;
193        }
194        return false;
195    }
196
197    private boolean isThumbnailRequested() {
198        return (mOptions & OPTION_THUMBNAIL) != 0;
199    }
200
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");
205        }
206        if (LOGV) {
207            Log.v(TAG, "Reading exif...");
208        }
209        mInterface = iRef;
210        mContainExifData = seekTiffData(inputStream);
211        mTiffStream = new CountedDataInputStream(inputStream);
212        mOptions = options;
213        if (!mContainExifData) {
214            return;
215        }
216
217        parseTiffHeader();
218        long offset = mTiffStream.readUnsignedInt();
219        if (offset > Integer.MAX_VALUE) {
220            throw new ExifInvalidFormatException("Invalid offset " + offset);
221        }
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);
229            }
230        }
231    }
232
233    /**
234     * Parses the the given InputStream with the given options
235     *
236     * @exception IOException
237     * @exception ExifInvalidFormatException
238     */
239    protected static ExifParser parse(InputStream inputStream, int options, ExifInterface iRef)
240            throws IOException, ExifInvalidFormatException {
241        return new ExifParser(inputStream, options, iRef);
242    }
243
244    /**
245     * Parses the the given InputStream with default options; that is, every IFD
246     * and thumbnaill will be parsed.
247     *
248     * @exception IOException
249     * @exception ExifInvalidFormatException
250     * @see #parse(InputStream, int)
251     */
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);
257    }
258
259    /**
260     * Moves the parser forward and returns the next parsing event
261     *
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
269     * @see #EVENT_END
270     */
271    protected int next() throws IOException, ExifInvalidFormatException {
272        if (!mContainExifData) {
273            return EVENT_END;
274        }
275        int offset = mTiffStream.getReadByteCount();
276        int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
277        if (offset < endOfTags) {
278            mTag = readTag();
279            if (mTag == null) {
280                return next();
281            }
282            if (mNeedToParseOffsetsInCurrentIfd) {
283                checkOffsetOrImageTag(mTag);
284            }
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);
293                    }
294                }
295            } else {
296                int offsetSize = 4;
297                // Some camera models use invalid length of the offset
298                if (mCorrespondingEvent.size() > 0) {
299                    offsetSize = mCorrespondingEvent.firstEntry().getKey() -
300                            mTiffStream.getReadByteCount();
301                }
302                if (offsetSize < 4) {
303                    Log.w(TAG, "Invalid size of link to next IFD: " + offsetSize);
304                } else {
305                    long ifdOffset = readUnsignedLong();
306                    if (ifdOffset != 0) {
307                        Log.w(TAG, "Invalid link to next IFD: " + ifdOffset);
308                    }
309                }
310            }
311        }
312        while (mCorrespondingEvent.size() != 0) {
313            Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
314            Object event = entry.getValue();
315            try {
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.");
320                continue;
321            }
322            if (event instanceof IfdEvent) {
323                mIfdType = ((IfdEvent) event).ifd;
324                mNumOfTagInIfd = mTiffStream.readUnsignedShort();
325                mIfdStartOffset = entry.getKey();
326
327                if (mNumOfTagInIfd * TAG_SIZE + mIfdStartOffset + OFFSET_SIZE > mApp1End) {
328                    Log.w(TAG, "Invalid size of IFD " + mIfdType);
329                    return EVENT_END;
330                }
331
332                mNeedToParseOffsetsInCurrentIfd = needToParseOffsetsInCurrentIfd();
333                if (((IfdEvent) event).isRequested) {
334                    return EVENT_START_OF_IFD;
335                } else {
336                    skipRemainingTagsInCurrentIfd();
337                }
338            } else if (event instanceof ImageEvent) {
339                mImageEvent = (ImageEvent) event;
340                return mImageEvent.type;
341            } else {
342                ExifTagEvent tagEvent = (ExifTagEvent) event;
343                mTag = tagEvent.tag;
344                if (mTag.getDataType() != ExifTag.TYPE_UNDEFINED) {
345                    readFullTagValue(mTag);
346                    checkOffsetOrImageTag(mTag);
347                }
348                if (tagEvent.isRequested) {
349                    return EVENT_VALUE_OF_REGISTERED_TAG;
350                }
351            }
352        }
353        return EVENT_END;
354    }
355
356    /**
357     * Skips the tags area of current IFD, if the parser is not in the tag area,
358     * nothing will happen.
359     *
360     * @throws IOException
361     * @throws ExifInvalidFormatException
362     */
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) {
367            return;
368        }
369        if (mNeedToParseOffsetsInCurrentIfd) {
370            while (offset < endOfTags) {
371                mTag = readTag();
372                offset += TAG_SIZE;
373                if (mTag == null) {
374                    continue;
375                }
376                checkOffsetOrImageTag(mTag);
377            }
378        } else {
379            skipTo(endOfTags);
380        }
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())) {
385            if (ifdOffset > 0) {
386                registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
387            }
388        }
389    }
390
391    private boolean needToParseOffsetsInCurrentIfd() {
392        switch (mIfdType) {
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);
402            default:
403                return false;
404        }
405    }
406
407    /**
408     * If {@link #next()} return {@link #EVENT_NEW_TAG} or
409     * {@link #EVENT_VALUE_OF_REGISTERED_TAG}, call this function to get the
410     * corresponding tag.
411     * <p>
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.
418     * <p>
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.
422     *
423     * @see #registerForTagValue(ExifTag)
424     * @see #read(byte[])
425     * @see #read(byte[], int, int)
426     * @see #readLong()
427     * @see #readRational()
428     * @see #readString(int)
429     * @see #readString(int, Charset)
430     */
431    protected ExifTag getTag() {
432        return mTag;
433    }
434
435    /**
436     * Gets number of tags in the current IFD area.
437     */
438    protected int getTagCountInCurrentIfd() {
439        return mNumOfTagInIfd;
440    }
441
442    /**
443     * Gets the ID of current IFD.
444     *
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
450     */
451    protected int getCurrentIfd() {
452        return mIfdType;
453    }
454
455    /**
456     * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
457     * get the index of this strip.
458     *
459     * @see #getStripCount()
460     */
461    protected int getStripIndex() {
462        return mImageEvent.stripIndex;
463    }
464
465    /**
466     * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
467     * get the number of strip data.
468     *
469     * @see #getStripIndex()
470     */
471    protected int getStripCount() {
472        return mStripCount;
473    }
474
475    /**
476     * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
477     * get the strip size.
478     */
479    protected int getStripSize() {
480        if (mStripSizeTag == null)
481            return 0;
482        return (int) mStripSizeTag.getValueAt(0);
483    }
484
485    /**
486     * When receiving {@link #EVENT_COMPRESSED_IMAGE}, call this function to get
487     * the image data size.
488     */
489    protected int getCompressedImageSize() {
490        if (mJpegSizeTag == null) {
491            return 0;
492        }
493        return (int) mJpegSizeTag.getValueAt(0);
494    }
495
496    private void skipTo(int offset) throws IOException {
497        mTiffStream.skipTo(offset);
498        while (!mCorrespondingEvent.isEmpty() && mCorrespondingEvent.firstKey() < offset) {
499            mCorrespondingEvent.pollFirstEntry();
500        }
501    }
502
503    /**
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.
509     *
510     * @see #EVENT_VALUE_OF_REGISTERED_TAG
511     */
512    protected void registerForTagValue(ExifTag tag) {
513        if (tag.getOffset() >= mTiffStream.getReadByteCount()) {
514            mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, true));
515        }
516    }
517
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)));
522    }
523
524    private void registerCompressedImage(long offset) {
525        mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_COMPRESSED_IMAGE));
526    }
527
528    private void registerUncompressedStrip(int stripIndex, long offset) {
529        mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_UNCOMPRESSED_STRIP
530                , stripIndex));
531    }
532
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");
540        }
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));
544            mTiffStream.skip(4);
545            return null;
546        }
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();
551        if (dataSize > 4) {
552            long offset = mTiffStream.readUnsignedInt();
553            if (offset > Integer.MAX_VALUE) {
554                throw new ExifInvalidFormatException(
555                        "offset is larger then Integer.MAX_VALUE");
556            }
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);
563                tag.setValue(buf);
564            } else {
565                tag.setOffset((int) offset);
566            }
567        } else {
568            boolean defCount = tag.hasDefinedCount();
569            // Set defined count to 0 so we can add \0 to non-terminated strings
570            tag.setHasDefinedCount(false);
571            // Read value
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);
577        }
578        return tag;
579    }
580
581    /**
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.
584     */
585    private void checkOffsetOrImageTag(ExifTag tag) {
586        // Some invalid formattd image contains tag with 0 size.
587        if (tag.getComponentCount() == 0) {
588            return;
589        }
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));
596            }
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));
600            }
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));
605            }
606        } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT
607                && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)) {
608            if (isThumbnailRequested()) {
609                registerCompressedImage(tag.getValueAt(0));
610            }
611        } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT_LENGTH
612                && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)) {
613            if (isThumbnailRequested()) {
614                mJpegSizeTag = tag;
615            }
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));
622                        } else {
623                            registerUncompressedStrip(i, tag.getValueAt(i));
624                        }
625                    }
626                } else {
627                    mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, false));
628                }
629            }
630        } else if (tid == TAG_STRIP_BYTE_COUNTS
631                && checkAllowed(ifd, ExifInterface.TAG_STRIP_BYTE_COUNTS)
632                &&isThumbnailRequested() && tag.hasValue()) {
633            mStripSizeTag = tag;
634        }
635    }
636
637    private boolean checkAllowed(int ifd, int tagId) {
638        int info = mInterface.getTagInfo().get(tagId);
639        if (info == ExifInterface.DEFINITION_NULL) {
640            return false;
641        }
642        return ExifInterface.isIfdAllowed(info, ifd);
643    }
644
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()
653                        + size) {
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());
660                    } else {
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());
669                        }
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);
675                    }
676                }
677            }
678        }
679        switch (tag.getDataType()) {
680            case ExifTag.TYPE_UNSIGNED_BYTE:
681            case ExifTag.TYPE_UNDEFINED: {
682                byte buf[] = new byte[tag.getComponentCount()];
683                read(buf);
684                tag.setValue(buf);
685            }
686                break;
687            case ExifTag.TYPE_ASCII:
688                tag.setValue(readString(tag.getComponentCount()));
689                break;
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();
694                }
695                tag.setValue(value);
696            }
697                break;
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();
702                }
703                tag.setValue(value);
704            }
705                break;
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();
710                }
711                tag.setValue(value);
712            }
713                break;
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();
718                }
719                tag.setValue(value);
720            }
721                break;
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();
726                }
727                tag.setValue(value);
728            }
729                break;
730        }
731        if (LOGV) {
732            Log.v(TAG, "\n" + tag.toString());
733        }
734    }
735
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);
743        } else {
744            throw new ExifInvalidFormatException("Invalid TIFF header");
745        }
746
747        if (mTiffStream.readShort() != TIFF_HEADER_TAIL) {
748            throw new ExifInvalidFormatException("Invalid TIFF header");
749        }
750    }
751
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");
757        }
758
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) {
766                int header = 0;
767                short headerTail = 0;
768                if (length >= 8) {
769                    header = dataStream.readInt();
770                    headerTail = dataStream.readShort();
771                    length -= 6;
772                    if (header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) {
773                        mTiffStartPosition = dataStream.getReadByteCount();
774                        mApp1End = length;
775                        mOffsetToApp1EndFromSOF = mTiffStartPosition + mApp1End;
776                        return true;
777                    }
778                }
779            }
780            if (length < 2 || (length - 2) != dataStream.skip(length - 2)) {
781                Log.w(TAG, "Invalid JPEG format.");
782                return false;
783            }
784            marker = dataStream.readShort();
785        }
786        return false;
787    }
788
789    protected int getOffsetToExifEndFromSOF() {
790        return mOffsetToApp1EndFromSOF;
791    }
792
793    protected int getTiffStartPosition() {
794        return mTiffStartPosition;
795    }
796
797    /**
798     * Reads bytes from the InputStream.
799     */
800    protected int read(byte[] buffer, int offset, int length) throws IOException {
801        return mTiffStream.read(buffer, offset, length);
802    }
803
804    /**
805     * Equivalent to read(buffer, 0, buffer.length).
806     */
807    protected int read(byte[] buffer) throws IOException {
808        return mTiffStream.read(buffer);
809    }
810
811    /**
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}.
815     */
816    protected String readString(int n) throws IOException {
817        return readString(n, US_ASCII);
818    }
819
820    /**
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}.
824     */
825    protected String readString(int n, Charset charset) throws IOException {
826        if (n > 0) {
827            return mTiffStream.readString(n, charset);
828        } else {
829            return "";
830        }
831    }
832
833    /**
834     * Reads value of type {@link ExifTag#TYPE_UNSIGNED_SHORT} from the
835     * InputStream.
836     */
837    protected int readUnsignedShort() throws IOException {
838        return mTiffStream.readShort() & 0xffff;
839    }
840
841    /**
842     * Reads value of type {@link ExifTag#TYPE_UNSIGNED_LONG} from the
843     * InputStream.
844     */
845    protected long readUnsignedLong() throws IOException {
846        return readLong() & 0xffffffffL;
847    }
848
849    /**
850     * Reads value of type {@link ExifTag#TYPE_UNSIGNED_RATIONAL} from the
851     * InputStream.
852     */
853    protected Rational readUnsignedRational() throws IOException {
854        long nomi = readUnsignedLong();
855        long denomi = readUnsignedLong();
856        return new Rational(nomi, denomi);
857    }
858
859    /**
860     * Reads value of type {@link ExifTag#TYPE_LONG} from the InputStream.
861     */
862    protected int readLong() throws IOException {
863        return mTiffStream.readInt();
864    }
865
866    /**
867     * Reads value of type {@link ExifTag#TYPE_RATIONAL} from the InputStream.
868     */
869    protected Rational readRational() throws IOException {
870        int nomi = readLong();
871        int denomi = readLong();
872        return new Rational(nomi, denomi);
873    }
874
875    private static class ImageEvent {
876        int stripIndex;
877        int type;
878
879        ImageEvent(int type) {
880            this.stripIndex = 0;
881            this.type = type;
882        }
883
884        ImageEvent(int type, int stripIndex) {
885            this.type = type;
886            this.stripIndex = stripIndex;
887        }
888    }
889
890    private static class IfdEvent {
891        int ifd;
892        boolean isRequested;
893
894        IfdEvent(int ifd, boolean isInterestedIfd) {
895            this.ifd = ifd;
896            this.isRequested = isInterestedIfd;
897        }
898    }
899
900    private static class ExifTagEvent {
901        ExifTag tag;
902        boolean isRequested;
903
904        ExifTagEvent(ExifTag tag, boolean isRequireByUser) {
905            this.tag = tag;
906            this.isRequested = isRequireByUser;
907        }
908    }
909
910    /**
911     * Gets the byte order of the current InputStream.
912     */
913    protected ByteOrder getByteOrder() {
914        return mTiffStream.getByteOrder();
915    }
916}