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