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