ExifInterface.java revision 89889c49482aed4e9dec66b33f733065e3e1580c
1318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber/*
2318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber * Copyright (C) 2007 The Android Open Source Project
3318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber *
4318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber * Licensed under the Apache License, Version 2.0 (the "License");
5318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber * you may not use this file except in compliance with the License.
6318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber * You may obtain a copy of the License at
7318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber *
8318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber *      http://www.apache.org/licenses/LICENSE-2.0
9318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber *
10318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber * Unless required by applicable law or agreed to in writing, software
11318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber * distributed under the License is distributed on an "AS IS" BASIS,
12318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber * See the License for the specific language governing permissions and
14318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber * limitations under the License.
15318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber */
16318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber
170c03d5c7c2fa4d17f7f5159e3fddd2adf6bfc923Andreas Huberpackage android.support.media;
18318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber
19318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huberimport android.content.res.AssetManager;
20318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huberimport android.graphics.Bitmap;
21f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnarimport android.graphics.BitmapFactory;
22f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnarimport android.support.annotation.IntDef;
23255735a38b9d5c3755c7b819bdc8fdaf4357d860Pawin Vongmasaimport android.support.annotation.NonNull;
24255735a38b9d5c3755c7b819bdc8fdaf4357d860Pawin Vongmasaimport android.util.Log;
25255735a38b9d5c3755c7b819bdc8fdaf4357d860Pawin Vongmasaimport android.util.Pair;
266d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang
27318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huberimport java.io.BufferedInputStream;
28255735a38b9d5c3755c7b819bdc8fdaf4357d860Pawin Vongmasaimport java.io.ByteArrayInputStream;
29255735a38b9d5c3755c7b819bdc8fdaf4357d860Pawin Vongmasaimport java.io.Closeable;
30255735a38b9d5c3755c7b819bdc8fdaf4357d860Pawin Vongmasaimport java.io.DataInput;
31255735a38b9d5c3755c7b819bdc8fdaf4357d860Pawin Vongmasaimport java.io.DataInputStream;
32318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huberimport java.io.EOFException;
33318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huberimport java.io.File;
34aef47bf801dcbcb88cec8426c03237c6313da1c2Lajos Molnarimport java.io.FileInputStream;
35f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFaddenimport java.io.FileNotFoundException;
36255735a38b9d5c3755c7b819bdc8fdaf4357d860Pawin Vongmasaimport java.io.FileOutputStream;
37f1d5aa162c02a16b7195a43a9bcea4d592600ac4James Dongimport java.io.FilterOutputStream;
38054219874873b41f1c815552987c10465c34ba2bLajos Molnarimport java.io.IOException;
396cf9a1238986880536de705255f7c2c91c1ba719Chong Zhangimport java.io.InputStream;
402a3847ee1cbdaa8a65eee397a0173bb02211c459Andreas Huberimport java.io.OutputStream;
41f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnarimport java.lang.annotation.Retention;
427e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnarimport java.lang.annotation.RetentionPolicy;
433fd200feb657c157125e45e30c2a7262e3c0244dChong Zhangimport java.nio.ByteBuffer;
442be091cebb9462b63d7160d0fa2cfa8703973a69Pawin Vongmasaimport java.nio.ByteOrder;
45f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnarimport java.nio.charset.Charset;
46f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasaimport java.text.ParsePosition;
47f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasaimport java.text.SimpleDateFormat;
48f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFaddenimport java.util.Arrays;
49f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnarimport java.util.Date;
50f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnarimport java.util.HashMap;
51d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhangimport java.util.HashSet;
52f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnarimport java.util.Map;
53f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnarimport java.util.Set;
54d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhangimport java.util.TimeZone;
55d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhangimport java.util.regex.Matcher;
56f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnarimport java.util.regex.Pattern;
57f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
58f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar/**
59f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar * This is a class for reading and writing Exif tags in a JPEG file or a RAW image file.
60f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar * <p>
61d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang * Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW and RAF.
62f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar * <p>
63d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang * Attribute mutation is supported for JPEG image files.
64f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar */
65f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnarpublic class ExifInterface {
66f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private static final String TAG = "ExifInterface";
67f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private static final boolean DEBUG = false;
68f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
69f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    // The Exif tag names. See Tiff 6.0 Section 3 and Section 8.
70f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is String. */
71d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_ARTIST = "Artist";
72f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is int. */
73f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final String TAG_BITS_PER_SAMPLE = "BitsPerSample";
74f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is int. */
75f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final String TAG_COMPRESSION = "Compression";
76f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is String. */
77f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final String TAG_COPYRIGHT = "Copyright";
78f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is String. */
79f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final String TAG_DATETIME = "DateTime";
80f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is String. */
81f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final String TAG_IMAGE_DESCRIPTION = "ImageDescription";
82f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is int. */
83f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final String TAG_IMAGE_LENGTH = "ImageLength";
84f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is int. */
85f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final String TAG_IMAGE_WIDTH = "ImageWidth";
86f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is int. */
8715ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar    public static final String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
8815ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar    /** Type is int. */
8915ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar    public static final String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
90f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is String. */
9115ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar    public static final String TAG_MAKE = "Make";
92f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is String. */
93f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final String TAG_MODEL = "Model";
94f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is int. */
95f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final String TAG_ORIENTATION = "Orientation";
96f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is int. */
97f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
98f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is int. */
99318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
100318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    /** Type is rational. */
101318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
10271aba39b8767de04e35b3366a6413928f50582f0Lajos Molnar    /** Type is rational. */
103f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    public static final String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
104f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    /** Type is int. */
105318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_RESOLUTION_UNIT = "ResolutionUnit";
106f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    /** Type is int. */
10721b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    public static final String TAG_ROWS_PER_STRIP = "RowsPerStrip";
10821b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    /** Type is int. */
109d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    public static final String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
110d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    /** Type is String. */
111318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_SOFTWARE = "Software";
112318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    /** Type is int. */
11321b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    public static final String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
11421b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    /** Type is int. */
115d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    public static final String TAG_STRIP_OFFSETS = "StripOffsets";
116d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    /** Type is int. */
117d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    public static final String TAG_TRANSFER_FUNCTION = "TransferFunction";
118318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    /** Type is rational. */
119318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_WHITE_POINT = "WhitePoint";
120bff5b5a8bb44754e0b51631527de1c49ab7e5a43Wonsik Kim    /** Type is rational. */
12183750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis    public static final String TAG_X_RESOLUTION = "XResolution";
122d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    /** Type is rational. */
123d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    public static final String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
124d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    /** Type is int. */
125d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    public static final String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
12683750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis    /** Type is int. */
12783750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis    public static final String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
128f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    /** Type is rational. */
129f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    public static final String TAG_Y_RESOLUTION = "YResolution";
130f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    /** Type is rational. */
131f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    public static final String TAG_APERTURE_VALUE = "ApertureValue";
132f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    /** Type is rational. */
133f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    public static final String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
134ef777c71a051f519e0b6998ad663fa5bd291d48aLajos Molnar    /** Type is String. */
135d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    public static final String TAG_CFA_PATTERN = "CFAPattern";
136318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    /** Type is int. */
137318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_COLOR_SPACE = "ColorSpace";
138318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    /** Type is String. */
139ec4ed7d541f48d1d0af8f93cd26ec291ca82061bLajos Molnar    public static final String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
14021b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    /** Type is rational. */
141ec4ed7d541f48d1d0af8f93cd26ec291ca82061bLajos Molnar    public static final String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
142f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    /** Type is int. */
143318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_CONTRAST = "Contrast";
144318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    /** Type is int. */
145ef777c71a051f519e0b6998ad663fa5bd291d48aLajos Molnar    public static final String TAG_CUSTOM_RENDERED = "CustomRendered";
146d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    /** Type is String. */
147318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
148318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    /** Type is String. */
149318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
150318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    /** Type is String. */
151f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    public static final String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
152f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is double. */
153318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
154318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    /** Type is String. */
15521b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    public static final String TAG_EXIF_VERSION = "ExifVersion";
15621b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    /** Type is double. */
15721b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    public static final String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
158ec4ed7d541f48d1d0af8f93cd26ec291ca82061bLajos Molnar    /** Type is rational. */
159ec4ed7d541f48d1d0af8f93cd26ec291ca82061bLajos Molnar    public static final String TAG_EXPOSURE_INDEX = "ExposureIndex";
160ec4ed7d541f48d1d0af8f93cd26ec291ca82061bLajos Molnar    /** Type is int. */
161ec4ed7d541f48d1d0af8f93cd26ec291ca82061bLajos Molnar    public static final String TAG_EXPOSURE_MODE = "ExposureMode";
162ec4ed7d541f48d1d0af8f93cd26ec291ca82061bLajos Molnar    /** Type is int. */
163ec4ed7d541f48d1d0af8f93cd26ec291ca82061bLajos Molnar    public static final String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
164ec4ed7d541f48d1d0af8f93cd26ec291ca82061bLajos Molnar    /** Type is double. */
165ec4ed7d541f48d1d0af8f93cd26ec291ca82061bLajos Molnar    public static final String TAG_EXPOSURE_TIME = "ExposureTime";
166054219874873b41f1c815552987c10465c34ba2bLajos Molnar    /** Type is double. */
167054219874873b41f1c815552987c10465c34ba2bLajos Molnar    public static final String TAG_F_NUMBER = "FNumber";
168054219874873b41f1c815552987c10465c34ba2bLajos Molnar    /** Type is String. */
169d0715867861c216e88a4a7523b6da8a3cb128724Lajos Molnar    public static final String TAG_FILE_SOURCE = "FileSource";
170d0715867861c216e88a4a7523b6da8a3cb128724Lajos Molnar    /** Type is int. */
171d0715867861c216e88a4a7523b6da8a3cb128724Lajos Molnar    public static final String TAG_FLASH = "Flash";
172d0715867861c216e88a4a7523b6da8a3cb128724Lajos Molnar    /** Type is rational. */
1737e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar    public static final String TAG_FLASH_ENERGY = "FlashEnergy";
1747e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar    /** Type is String. */
1757e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar    public static final String TAG_FLASHPIX_VERSION = "FlashpixVersion";
1767e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar    /** Type is rational. */
17741eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim    public static final String TAG_FOCAL_LENGTH = "FocalLength";
17841eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim    /** Type is int. */
17941eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim    public static final String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
18041eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim    /** Type is int. */
181d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    public static final String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
182d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    /** Type is rational. */
183d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    public static final String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
184d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    /** Type is rational. */
185318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
18683750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis    /** Type is int. */
1877e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar    public static final String TAG_GAIN_CONTROL = "GainControl";
188318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    /** Type is int. */
189f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    public static final String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
190d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    /** Type is String. */
191d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    public static final String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
19241eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim    /** Type is int. */
193d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    public static final String TAG_LIGHT_SOURCE = "LightSource";
194318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    /** Type is String. */
195318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_MAKER_NOTE = "MakerNote";
196318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    /** Type is rational. */
197318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
198318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    /** Type is int. */
199318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_METERING_MODE = "MeteringMode";
200318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    /** Type is int. */
201318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_NEW_SUBFILE_TYPE = "NewSubfileType";
202318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    /** Type is String. */
203318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_OECF = "OECF";
204f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is int. */
205f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
206f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is int. */
207f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
208011734f0ce7e8b2e3066f90ef51c323ee7d4dea2Bill Yi    /** Type is String. */
209f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
210f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is int. */
211f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final String TAG_SATURATION = "Saturation";
212f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /** Type is int. */
213d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
214d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
215d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_SCENE_TYPE = "SceneType";
216d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is int. */
217d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_SENSING_METHOD = "SensingMethod";
218d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is int. */
219d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_SHARPNESS = "Sharpness";
220d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is rational. */
221d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
222d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
223d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
224d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
225d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
226d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is int. */
227d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_SUBFILE_TYPE = "SubfileType";
228d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
229d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_SUBSEC_TIME = "SubSecTime";
230d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
231d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
232d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
233d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
234d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is int. */
235d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_SUBJECT_AREA = "SubjectArea";
236d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is double. */
237d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_SUBJECT_DISTANCE = "SubjectDistance";
238d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is int. */
239d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
240d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is int. */
241d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_SUBJECT_LOCATION = "SubjectLocation";
242d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
243d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_USER_COMMENT = "UserComment";
244d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is int. */
245d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_WHITE_BALANCE = "WhiteBalance";
246d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /**
247d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang     * The altitude (in meters) based on the reference in TAG_GPS_ALTITUDE_REF.
248d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang     * Type is rational.
24936f05c7f071e5c877e65943fe181d2b48c7bd6b0Lajos Molnar     */
25036f05c7f071e5c877e65943fe181d2b48c7bd6b0Lajos Molnar    public static final String TAG_GPS_ALTITUDE = "GPSAltitude";
25136f05c7f071e5c877e65943fe181d2b48c7bd6b0Lajos Molnar    /**
25236f05c7f071e5c877e65943fe181d2b48c7bd6b0Lajos Molnar     * 0 if the altitude is above sea level. 1 if the altitude is below sea
25336f05c7f071e5c877e65943fe181d2b48c7bd6b0Lajos Molnar     * level. Type is int.
25436f05c7f071e5c877e65943fe181d2b48c7bd6b0Lajos Molnar     */
25536f05c7f071e5c877e65943fe181d2b48c7bd6b0Lajos Molnar    public static final String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
25636f05c7f071e5c877e65943fe181d2b48c7bd6b0Lajos Molnar    /** Type is String. */
25736f05c7f071e5c877e65943fe181d2b48c7bd6b0Lajos Molnar    public static final String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
258d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is rational. */
259d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_DOP = "GPSDOP";
260d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
261d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_DATESTAMP = "GPSDateStamp";
262d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is rational. */
263d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_DEST_BEARING = "GPSDestBearing";
264d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
265d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
266d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is rational. */
267d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
268d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
269d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
270d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is rational. */
271d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
272d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
273d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
274d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is rational. */
275d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
276d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
277d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
278d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is int. */
279d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
280d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is rational. */
281d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
282d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
283d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
284d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is rational. Format is "num1/denom1,num2/denom2,num3/denom3". */
285d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_LATITUDE = "GPSLatitude";
286d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
287d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
288d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is rational. Format is "num1/denom1,num2/denom2,num3/denom3". */
289d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_LONGITUDE = "GPSLongitude";
290d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
291d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
292d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
293d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_MAP_DATUM = "GPSMapDatum";
294d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
295d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
296d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. Name of GPS processing method used for location finding. */
297d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
298d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
299d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_SATELLITES = "GPSSatellites";
300d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is rational. */
301d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_SPEED = "GPSSpeed";
30236f05c7f071e5c877e65943fe181d2b48c7bd6b0Lajos Molnar    /** Type is String. */
303d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_SPEED_REF = "GPSSpeedRef";
304d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
305d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_STATUS = "GPSStatus";
306d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. Format is "hh:mm:ss". */
307d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
308d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is rational. */
309d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_TRACK = "GPSTrack";
310d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
311d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_TRACK_REF = "GPSTrackRef";
312d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
313d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_GPS_VERSION_ID = "GPSVersionID";
314d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is String. */
315d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
316d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is int. */
317d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
318d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is int. */
319d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
320d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is int. DNG Specification 1.4.0.0. Section 4 */
321d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_DNG_VERSION = "DNGVersion";
322d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is int. DNG Specification 1.4.0.0. Section 4 */
323d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_DEFAULT_CROP_SIZE = "DefaultCropSize";
324d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is undefined. See Olympus MakerNote tags in http://www.exiv2.org/tags-olympus.html. */
325d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_ORF_THUMBNAIL_IMAGE = "ThumbnailImage";
326d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is int. See Olympus Camera Settings tags in http://www.exiv2.org/tags-olympus.html. */
327d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_ORF_PREVIEW_IMAGE_START = "PreviewImageStart";
328d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is int. See Olympus Camera Settings tags in http://www.exiv2.org/tags-olympus.html. */
329d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_ORF_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
330d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /** Type is int. See Olympus Image Processing tags in http://www.exiv2.org/tags-olympus.html. */
331d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_ORF_ASPECT_FRAME = "AspectFrame";
332d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /**
333d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang     * Type is int. See PanasonicRaw tags in
334d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang     * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
335d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang     */
336d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_RW2_SENSOR_BOTTOM_BORDER = "SensorBottomBorder";
337d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /**
338d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang     * Type is int. See PanasonicRaw tags in
339d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang     * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
340d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang     */
341d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final String TAG_RW2_SENSOR_LEFT_BORDER = "SensorLeftBorder";
342d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    /**
343d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang     * Type is int. See PanasonicRaw tags in
344d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang     * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
345d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang     */
346318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final String TAG_RW2_SENSOR_RIGHT_BORDER = "SensorRightBorder";
347e7b894297aebc24939ddfa632ea3dd2d405d9f93Pawin Vongmasa    /**
348318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber     * Type is int. See PanasonicRaw tags in
349318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber     * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
350134ee6a324c35f39e3576172e4eae4c6de6eb9dcAndreas Huber     */
3519113c1e619fd78fe53b548180fdc02300d33303dAndy Hung    public static final String TAG_RW2_SENSOR_TOP_BORDER = "SensorTopBorder";
352d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    /**
353d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar     * Type is int. See PanasonicRaw tags in
35421b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang     * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
3556d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang     */
3563604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang    public static final String TAG_RW2_ISO = "ISO";
3573604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang    /**
3583604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     * Type is undefined. See PanasonicRaw tags in
3593604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
360609b815a3131d22da38b2f452faa9f89daad4039Andy Hung     */
361f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final String TAG_RW2_JPG_FROM_RAW = "JpgFromRaw";
362f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
363f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /**
364f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * Private tags used for pointing the other IFD offsets.
365f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * The types of the following tags are int.
366f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * See JEITA CP-3451C Section 4.6.3: Exif-specific IFD.
367f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * For SubIFD, see Note 1 of Adobe PageMaker® 6.0 TIFF Technical Notes.
368f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     */
369054219874873b41f1c815552987c10465c34ba2bLajos Molnar    private static final String TAG_EXIF_IFD_POINTER = "ExifIFDPointer";
370054219874873b41f1c815552987c10465c34ba2bLajos Molnar    private static final String TAG_GPS_INFO_IFD_POINTER = "GPSInfoIFDPointer";
371d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang    private static final String TAG_INTEROPERABILITY_IFD_POINTER = "InteroperabilityIFDPointer";
372d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang    private static final String TAG_SUB_IFD_POINTER = "SubIFDPointer";
373a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar    // Proprietary pointer tags used for ORF files.
374a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar    // See http://www.exiv2.org/tags-olympus.html
37577d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang    private static final String TAG_ORF_CAMERA_SETTINGS_IFD_POINTER = "CameraSettingsIFDPointer";
37677d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang    private static final String TAG_ORF_IMAGE_PROCESSING_IFD_POINTER = "ImageProcessingIFDPointer";
3778dde7269a5356503d2b283234b6cb46d0c3f214eWei Jia
378d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang    // Private tags used for thumbnail information.
379318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static final String TAG_HAS_THUMBNAIL = "HasThumbnail";
380318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static final String TAG_THUMBNAIL_OFFSET = "ThumbnailOffset";
381318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static final String TAG_THUMBNAIL_LENGTH = "ThumbnailLength";
382f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private static final String TAG_THUMBNAIL_DATA = "ThumbnailData";
383f1d5aa162c02a16b7195a43a9bcea4d592600ac4James Dong    private static final int MAX_THUMBNAIL_SIZE = 512;
384318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber
385318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    // Constants used for the Orientation Exif tag.
386d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final int ORIENTATION_UNDEFINED = 0;
387f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final int ORIENTATION_NORMAL = 1;
388f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public static final int ORIENTATION_FLIP_HORIZONTAL = 2;  // left right reversed mirror
389318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final int ORIENTATION_ROTATE_180 = 3;
390d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final int ORIENTATION_FLIP_VERTICAL = 4;  // upside down mirror
391d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    // flipped about top-left <--> bottom-right axis
392d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public static final int ORIENTATION_TRANSPOSE = 5;
393318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public static final int ORIENTATION_ROTATE_90 = 6;  // rotate 90 cw to right it
394318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    // flipped about top-right <--> bottom-left axis
3956d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang    public static final int ORIENTATION_TRANSVERSE = 7;
3966d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang    public static final int ORIENTATION_ROTATE_270 = 8;  // rotate 270 to right it
3976d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang
398f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden    // Constants used for white balance
399f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden    public static final int WHITEBALANCE_AUTO = 0;
4006d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang    public static final int WHITEBALANCE_MANUAL = 1;
4016d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang
4026d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang    // Maximum size for checking file type signature (see image_type_recognition_lite.cc)
4036d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang    private static final int SIGNATURE_CHECK_SIZE = 5000;
404f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden
405f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden    private static final byte[] JPEG_SIGNATURE = new byte[] {(byte) 0xff, (byte) 0xd8, (byte) 0xff};
406d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    private static final String RAF_SIGNATURE = "FUJIFILMCCD-RAW";
407d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    private static final int RAF_OFFSET_TO_JPEG_IMAGE_OFFSET = 84;
408318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static final int RAF_INFO_SIZE = 160;
409318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static final int RAF_JPEG_LENGTH_VALUE_SIZE = 4;
410318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber
411318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    // See http://fileformats.archiveteam.org/wiki/Olympus_ORF
412318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static final short ORF_SIGNATURE_1 = 0x4f52;
413318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static final short ORF_SIGNATURE_2 = 0x5352;
414d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    // There are two formats for Olympus Makernote Headers. Each has different identifiers and
415f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    // offsets to the actual data.
41643e5eca7048a3b7b3ee0223b7f3cbd837ed10ae5Andreas Huber    // See http://www.exiv2.org/makernote.html#R1
41743e5eca7048a3b7b3ee0223b7f3cbd837ed10ae5Andreas Huber    private static final byte[] ORF_MAKER_NOTE_HEADER_1 = new byte[] {(byte) 0x4f, (byte) 0x4c,
418d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber            (byte) 0x59, (byte) 0x4d, (byte) 0x50, (byte) 0x00}; // "OLYMP\0"
419d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final byte[] ORF_MAKER_NOTE_HEADER_2 = new byte[] {(byte) 0x4f, (byte) 0x4c,
420d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber            (byte) 0x59, (byte) 0x4d, (byte) 0x50, (byte) 0x55, (byte) 0x53, (byte) 0x00,
421d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber            (byte) 0x49, (byte) 0x49}; // "OLYMPUS\0II"
422d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final int ORF_MAKER_NOTE_HEADER_1_SIZE = 8;
423d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final int ORF_MAKER_NOTE_HEADER_2_SIZE = 12;
424134ee6a324c35f39e3576172e4eae4c6de6eb9dcAndreas Huber
425134ee6a324c35f39e3576172e4eae4c6de6eb9dcAndreas Huber    // See http://fileformats.archiveteam.org/wiki/RW2
426134ee6a324c35f39e3576172e4eae4c6de6eb9dcAndreas Huber    private static final short RW2_SIGNATURE = 0x0055;
4273e378967b379afcd559b53d70d00e758f9c2616dWonsik Kim
4283e378967b379afcd559b53d70d00e758f9c2616dWonsik Kim    // See http://fileformats.archiveteam.org/wiki/Pentax_PEF
4293e378967b379afcd559b53d70d00e758f9c2616dWonsik Kim    private static final String PEF_SIGNATURE = "PENTAX";
4303e378967b379afcd559b53d70d00e758f9c2616dWonsik Kim    // See http://www.exiv2.org/makernote.html#R11
4313e378967b379afcd559b53d70d00e758f9c2616dWonsik Kim    private static final int PEF_MAKER_NOTE_SKIP_SIZE = 6;
4323e378967b379afcd559b53d70d00e758f9c2616dWonsik Kim
4333e378967b379afcd559b53d70d00e758f9c2616dWonsik Kim    private static SimpleDateFormat sFormatter;
434134ee6a324c35f39e3576172e4eae4c6de6eb9dcAndreas Huber
435d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    // See Exchangeable image file format for digital still cameras: Exif version 2.2.
436d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    // The following values are for parsing EXIF data area. There are tag groups in EXIF data area.
437d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    // They are called "Image File Directory". They have multiple data formats to cover various
438d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    // image metadata from GPS longitude to camera model name.
439d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber
4403856b090cd04ba5dd4a59a12430ed724d5995909Steve Block    // Types of Exif byte alignments (see JEITA CP-3451C Section 4.5.2)
441d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final short BYTE_ALIGN_II = 0x4949;  // II: Intel order
442d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final short BYTE_ALIGN_MM = 0x4d4d;  // MM: Motorola order
44343e5eca7048a3b7b3ee0223b7f3cbd837ed10ae5Andreas Huber
444d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    // TIFF Header Fixed Constant (see JEITA CP-3451C Section 4.5.2)
445f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private static final byte START_CODE = 0x2a; // 42
446f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private static final int IFD_OFFSET = 8;
44743e5eca7048a3b7b3ee0223b7f3cbd837ed10ae5Andreas Huber
448f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    // Formats for the value in IFD entry (See TIFF 6.0 Section 2, "Image File Directory".)
449f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private static final int IFD_FORMAT_BYTE = 1;
45043e5eca7048a3b7b3ee0223b7f3cbd837ed10ae5Andreas Huber    private static final int IFD_FORMAT_STRING = 2;
45143e5eca7048a3b7b3ee0223b7f3cbd837ed10ae5Andreas Huber    private static final int IFD_FORMAT_USHORT = 3;
45243e5eca7048a3b7b3ee0223b7f3cbd837ed10ae5Andreas Huber    private static final int IFD_FORMAT_ULONG = 4;
45343e5eca7048a3b7b3ee0223b7f3cbd837ed10ae5Andreas Huber    private static final int IFD_FORMAT_URATIONAL = 5;
454d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final int IFD_FORMAT_SBYTE = 6;
455d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final int IFD_FORMAT_UNDEFINED = 7;
456d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final int IFD_FORMAT_SSHORT = 8;
457d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final int IFD_FORMAT_SLONG = 9;
4580d681df3b0ded2c1e335b6b5785439da4ce2c238Andreas Huber    private static final int IFD_FORMAT_SRATIONAL = 10;
4590d681df3b0ded2c1e335b6b5785439da4ce2c238Andreas Huber    private static final int IFD_FORMAT_SINGLE = 11;
4600d681df3b0ded2c1e335b6b5785439da4ce2c238Andreas Huber    private static final int IFD_FORMAT_DOUBLE = 12;
4610d681df3b0ded2c1e335b6b5785439da4ce2c238Andreas Huber    // Format indicating a new IFD entry (See Adobe PageMaker® 6.0 TIFF Technical Notes, "New Tag")
462d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final int IFD_FORMAT_IFD = 13;
463d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    // Names for the data formats for debugging purpose.
464d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final String[] IFD_FORMAT_NAMES = new String[] {
465d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber            "", "BYTE", "STRING", "USHORT", "ULONG", "URATIONAL", "SBYTE", "UNDEFINED", "SSHORT",
466d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber            "SLONG", "SRATIONAL", "SINGLE", "DOUBLE"
4673856b090cd04ba5dd4a59a12430ed724d5995909Steve Block    };
468d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    // Sizes of the components of each IFD value format
469d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final int[] IFD_FORMAT_BYTES_PER_FORMAT = new int[] {
470d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber            0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 1
471d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    };
472d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final byte[] EXIF_ASCII_PREFIX = new byte[] {
47343e5eca7048a3b7b3ee0223b7f3cbd837ed10ae5Andreas Huber            0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0
474d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    };
475f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
476f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /**
47743e5eca7048a3b7b3ee0223b7f3cbd837ed10ae5Andreas Huber     * Constants used for Compression tag.
478f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * For Value 1, 2, 32773, see TIFF 6.0 Spec Section 3: Bilevel Images, Compression
479f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * For Value 6, see TIFF 6.0 Spec Section 22: JPEG Compression, Extensions to Existing Fields
48043e5eca7048a3b7b3ee0223b7f3cbd837ed10ae5Andreas Huber     * For Value 7, 8, 34892, see DNG Specification 1.4.0.0. Section 3, Compression
48143e5eca7048a3b7b3ee0223b7f3cbd837ed10ae5Andreas Huber     */
48243e5eca7048a3b7b3ee0223b7f3cbd837ed10ae5Andreas Huber    private static final int DATA_UNCOMPRESSED = 1;
48343e5eca7048a3b7b3ee0223b7f3cbd837ed10ae5Andreas Huber    private static final int DATA_HUFFMAN_COMPRESSED = 2;
4843856b090cd04ba5dd4a59a12430ed724d5995909Steve Block    private static final int DATA_JPEG = 6;
485d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final int DATA_JPEG_COMPRESSED = 7;
486d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final int DATA_DEFLATE_ZIP = 8;
487d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final int DATA_PACK_BITS_COMPRESSED = 32773;
488d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final int DATA_LOSSY_JPEG = 34892;
489d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber
490d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    /**
491d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber     * Constants used for BitsPerSample tag.
492d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber     * For RGB, see TIFF 6.0 Spec Section 6, Differences from Palette Color Images
493fa70cad40b01627ac1c22e04cdd548ece9c2654fAndreas Huber     * For Greyscale, see TIFF 6.0 Spec Section 4, Differences from Bilevel Images
494d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber     */
495d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final int[] BITS_PER_SAMPLE_RGB = new int[] { 8, 8, 8 };
496d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final int[] BITS_PER_SAMPLE_GREYSCALE_1 = new int[] { 4 };
497f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private static final int[] BITS_PER_SAMPLE_GREYSCALE_2 = new int[] { 8 };
498d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber
499d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    /**
500bdc0f0f0ab2e2c8d1ef43637eafba0b58549f089Lajos Molnar     * Constants used for PhotometricInterpretation tag.
501bdc0f0f0ab2e2c8d1ef43637eafba0b58549f089Lajos Molnar     * For White/Black, see Section 3, Color.
502bdc0f0f0ab2e2c8d1ef43637eafba0b58549f089Lajos Molnar     * See TIFF 6.0 Spec Section 22, Minimum Requirements for TIFF with JPEG Compression.
503d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang     */
504d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    private static final int PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO = 0;
505d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    private static final int PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO = 1;
506addf2cbb120346ae42e78fa739245a353db5edadChong Zhang    private static final int PHOTOMETRIC_INTERPRETATION_RGB = 2;
507f0fb96c352f30b812a4903a1d783a715e1e817bdAndreas Huber    private static final int PHOTOMETRIC_INTERPRETATION_YCBCR = 6;
508318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber
509f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /**
510f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * Constants used for NewSubfileType tag.
511f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * See TIFF 6.0 Spec Section 8
512318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber     * */
5133856b090cd04ba5dd4a59a12430ed724d5995909Steve Block    private static final int ORIGINAL_RESOLUTION_IMAGE = 0;
514318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static final int REDUCED_RESOLUTION_IMAGE = 1;
515d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang
516318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    // A class for indicating EXIF rational type.
517318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static class Rational {
518318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        public final long numerator;
519318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        public final long denominator;
5206d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang
521ad3b7e8d40bf1c97347f1538d30bba78ca371f67Andreas Huber        private Rational(long numerator, long denominator) {
522ad3b7e8d40bf1c97347f1538d30bba78ca371f67Andreas Huber            // Handle erroneous case
523ad3b7e8d40bf1c97347f1538d30bba78ca371f67Andreas Huber            if (denominator == 0) {
524ad3b7e8d40bf1c97347f1538d30bba78ca371f67Andreas Huber                this.numerator = 0;
525ad3b7e8d40bf1c97347f1538d30bba78ca371f67Andreas Huber                this.denominator = 1;
5266d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang                return;
527ad3b7e8d40bf1c97347f1538d30bba78ca371f67Andreas Huber            }
528ad3b7e8d40bf1c97347f1538d30bba78ca371f67Andreas Huber            this.numerator = numerator;
529ad3b7e8d40bf1c97347f1538d30bba78ca371f67Andreas Huber            this.denominator = denominator;
5306d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang        }
5316d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang
532ad3b7e8d40bf1c97347f1538d30bba78ca371f67Andreas Huber        @Override
533e40cda70eec141fa05cbcca1de420fdb22b98be6Andreas Huber        public String toString() {
534e40cda70eec141fa05cbcca1de420fdb22b98be6Andreas Huber            return numerator + "/" + denominator;
535e40cda70eec141fa05cbcca1de420fdb22b98be6Andreas Huber        }
536e40cda70eec141fa05cbcca1de420fdb22b98be6Andreas Huber
537318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        public double calculate() {
538318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            return (double) numerator / denominator;
5396d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang        }
5406d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang    }
5416d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang
5426d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang    // A class for indicating EXIF attribute.
5436d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang    private static class ExifAttribute {
544f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        public final int format;
545f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        public final int numberOfComponents;
546f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        public final byte[] bytes;
547f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
548f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        private ExifAttribute(int format, int numberOfComponents, byte[] bytes) {
549f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            this.format = format;
550f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            this.numberOfComponents = numberOfComponents;
551f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            this.bytes = bytes;
552f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        }
553318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber
554f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        public static ExifAttribute createUShort(int[] values, ByteOrder byteOrder) {
555318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            final ByteBuffer buffer = ByteBuffer.wrap(
556318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                    new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_USHORT] * values.length]);
557318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            buffer.order(byteOrder);
558d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            for (int value : values) {
559d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar                buffer.putShort((short) value);
560d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            }
561d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            return new ExifAttribute(IFD_FORMAT_USHORT, values.length, buffer.array());
562d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar        }
563d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar
564d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar        public static ExifAttribute createUShort(int value, ByteOrder byteOrder) {
565d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            return createUShort(new int[] {value}, byteOrder);
566d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar        }
567d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar
568d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar        public static ExifAttribute createULong(long[] values, ByteOrder byteOrder) {
569d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            final ByteBuffer buffer = ByteBuffer.wrap(
570d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar                    new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_ULONG] * values.length]);
571d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            buffer.order(byteOrder);
5723604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang            for (long value : values) {
5733604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang                buffer.putInt((int) value);
5743604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang            }
5753604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang            return new ExifAttribute(IFD_FORMAT_ULONG, values.length, buffer.array());
5763604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        }
577d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar
5783604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        public static ExifAttribute createULong(long value, ByteOrder byteOrder) {
579d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            return createULong(new long[] {value}, byteOrder);
5803604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        }
581d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar
5823604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        public static ExifAttribute createSLong(int[] values, ByteOrder byteOrder) {
583d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            final ByteBuffer buffer = ByteBuffer.wrap(
584d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar                    new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_SLONG] * values.length]);
585d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            buffer.order(byteOrder);
586d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            for (int value : values) {
587d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar                buffer.putInt(value);
588d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            }
589d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            return new ExifAttribute(IFD_FORMAT_SLONG, values.length, buffer.array());
590d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar        }
591d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar
592d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar        public static ExifAttribute createSLong(int value, ByteOrder byteOrder) {
593d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            return createSLong(new int[] {value}, byteOrder);
594d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar        }
595d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar
596d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar        public static ExifAttribute createByte(String value) {
597d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            // Exception for GPSAltitudeRef tag
598d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            if (value.length() == 1 && value.charAt(0) >= '0' && value.charAt(0) <= '1') {
599318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                final byte[] bytes = new byte[] { (byte) (value.charAt(0) - '0') };
60084333e0475bc911adc16417f4ca327c975cf6c36Andreas Huber                return new ExifAttribute(IFD_FORMAT_BYTE, bytes.length, bytes);
601318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            }
602318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            final byte[] ascii = value.getBytes(ASCII);
603d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            return new ExifAttribute(IFD_FORMAT_BYTE, ascii.length, ascii);
604d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar        }
605d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar
606d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar        public static ExifAttribute createString(String value) {
607d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            final byte[] ascii = (value + '\0').getBytes(ASCII);
608318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            return new ExifAttribute(IFD_FORMAT_STRING, ascii.length, ascii);
609f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        }
610f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
611f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        public static ExifAttribute createURational(Rational[] values, ByteOrder byteOrder) {
612f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            final ByteBuffer buffer = ByteBuffer.wrap(
613f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                    new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_URATIONAL] * values.length]);
614318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            buffer.order(byteOrder);
615318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            for (Rational value : values) {
616318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                buffer.putInt((int) value.numerator);
617318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                buffer.putInt((int) value.denominator);
618f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            }
619318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            return new ExifAttribute(IFD_FORMAT_URATIONAL, values.length, buffer.array());
620f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        }
621f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
622318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        public static ExifAttribute createURational(Rational value, ByteOrder byteOrder) {
6233604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang            return createURational(new Rational[] {value}, byteOrder);
6243604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        }
6253604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang
6263604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        public static ExifAttribute createSRational(Rational[] values, ByteOrder byteOrder) {
627d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            final ByteBuffer buffer = ByteBuffer.wrap(
628d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar                    new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_SRATIONAL] * values.length]);
629d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            buffer.order(byteOrder);
630d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            for (Rational value : values) {
631d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar                buffer.putInt((int) value.numerator);
632318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                buffer.putInt((int) value.denominator);
633318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            }
634f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            return new ExifAttribute(IFD_FORMAT_SRATIONAL, values.length, buffer.array());
635318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        }
636318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber
637318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        public static ExifAttribute createSRational(Rational value, ByteOrder byteOrder) {
638318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            return createSRational(new Rational[] {value}, byteOrder);
63984333e0475bc911adc16417f4ca327c975cf6c36Andreas Huber        }
640318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber
641318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        public static ExifAttribute createDouble(double[] values, ByteOrder byteOrder) {
642d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            final ByteBuffer buffer = ByteBuffer.wrap(
643d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar                    new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_DOUBLE] * values.length]);
644d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            buffer.order(byteOrder);
645d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            for (double value : values) {
646d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar                buffer.putDouble(value);
647318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            }
648f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            return new ExifAttribute(IFD_FORMAT_DOUBLE, values.length, buffer.array());
649f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        }
650f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
651f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        public static ExifAttribute createDouble(double value, ByteOrder byteOrder) {
652f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            return createDouble(new double[] {value}, byteOrder);
653318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        }
654318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber
655318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        @Override
656318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        public String toString() {
657f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            return "(" + IFD_FORMAT_NAMES[format] + ", data length:" + bytes.length + ")";
658318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        }
659f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
660f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        private Object getValue(ByteOrder byteOrder) {
661318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            ByteOrderedDataInputStream inputStream = null;
662d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            try {
663d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar                inputStream = new ByteOrderedDataInputStream(bytes);
664d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar                inputStream.setByteOrder(byteOrder);
665d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar                switch (format) {
666d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar                    case IFD_FORMAT_BYTE:
667318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                    case IFD_FORMAT_SBYTE: {
668318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        // Exception for GPSAltitudeRef tag
669f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                        if (bytes.length == 1 && bytes[0] >= 0 && bytes[0] <= 1) {
670318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                            return new String(new char[] { (char) (bytes[0] + '0') });
671b1d666f5cb555d135eb69e005e88a03330bbb54cJamie Gennis                        }
672b1d666f5cb555d135eb69e005e88a03330bbb54cJamie Gennis                        return new String(bytes, ASCII);
673d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    }
674d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    case IFD_FORMAT_UNDEFINED:
675d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    case IFD_FORMAT_STRING: {
676d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        int index = 0;
677d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        if (numberOfComponents >= EXIF_ASCII_PREFIX.length) {
678d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            boolean same = true;
679d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            for (int i = 0; i < EXIF_ASCII_PREFIX.length; ++i) {
680d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                                if (bytes[i] != EXIF_ASCII_PREFIX[i]) {
681d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                                    same = false;
682ced401e56169d17b9b8d104dd2590690ab7a9e1eRam Mohan                                    break;
683ced401e56169d17b9b8d104dd2590690ab7a9e1eRam Mohan                                }
684ced401e56169d17b9b8d104dd2590690ab7a9e1eRam Mohan                            }
685ced401e56169d17b9b8d104dd2590690ab7a9e1eRam Mohan                            if (same) {
686ced401e56169d17b9b8d104dd2590690ab7a9e1eRam Mohan                                index = EXIF_ASCII_PREFIX.length;
687d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            }
688d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        }
689d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang
690d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        StringBuilder stringBuilder = new StringBuilder();
691d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        while (index < numberOfComponents) {
692d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            int ch = bytes[index];
693d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            if (ch == 0) {
694d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                                break;
695d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            }
696d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            if (ch >= 32) {
697d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                                stringBuilder.append((char) ch);
698d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            } else {
699d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                                stringBuilder.append('?');
700d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            }
701d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            ++index;
702d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        }
703d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        return stringBuilder.toString();
704d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    }
705d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    case IFD_FORMAT_USHORT: {
706d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        final int[] values = new int[numberOfComponents];
707d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        for (int i = 0; i < numberOfComponents; ++i) {
708d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            values[i] = inputStream.readUnsignedShort();
709d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        }
710d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        return values;
711d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    }
712d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    case IFD_FORMAT_ULONG: {
713d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        final long[] values = new long[numberOfComponents];
714d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        for (int i = 0; i < numberOfComponents; ++i) {
715d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            values[i] = inputStream.readUnsignedInt();
716d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        }
717d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        return values;
718d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    }
719d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    case IFD_FORMAT_URATIONAL: {
720d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        final Rational[] values = new Rational[numberOfComponents];
721d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        for (int i = 0; i < numberOfComponents; ++i) {
722d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            final long numerator = inputStream.readUnsignedInt();
723d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            final long denominator = inputStream.readUnsignedInt();
724d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            values[i] = new Rational(numerator, denominator);
725d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        }
726d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        return values;
727d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    }
728d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    case IFD_FORMAT_SSHORT: {
729d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        final int[] values = new int[numberOfComponents];
730d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        for (int i = 0; i < numberOfComponents; ++i) {
731d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            values[i] = inputStream.readShort();
732d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        }
733d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        return values;
734d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    }
735d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    case IFD_FORMAT_SLONG: {
736d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        final int[] values = new int[numberOfComponents];
737d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        for (int i = 0; i < numberOfComponents; ++i) {
738d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            values[i] = inputStream.readInt();
739d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        }
740d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        return values;
741d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    }
742d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    case IFD_FORMAT_SRATIONAL: {
743d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        final Rational[] values = new Rational[numberOfComponents];
744d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        for (int i = 0; i < numberOfComponents; ++i) {
745d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            final long numerator = inputStream.readInt();
746d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            final long denominator = inputStream.readInt();
747d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            values[i] = new Rational(numerator, denominator);
748d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        }
749d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        return values;
750d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    }
751d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    case IFD_FORMAT_SINGLE: {
752d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        final double[] values = new double[numberOfComponents];
753d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        for (int i = 0; i < numberOfComponents; ++i) {
754d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            values[i] = inputStream.readFloat();
755d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        }
756d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        return values;
757d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    }
758d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    case IFD_FORMAT_DOUBLE: {
759d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        final double[] values = new double[numberOfComponents];
760d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        for (int i = 0; i < numberOfComponents; ++i) {
761d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                            values[i] = inputStream.readDouble();
762d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        }
763d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        return values;
764d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    }
765d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    default:
766d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        return null;
767d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                }
768d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            } catch (IOException e) {
769d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                Log.w(TAG, "IOException occurred during reading a value", e);
770d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                return null;
771d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            } finally {
772d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                if (inputStream != null) {
773d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    try {
774d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        inputStream.close();
775d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    } catch (IOException e) {
776d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                        Log.e(TAG, "IOException occurred while closing InputStream", e);
777d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    }
778d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                }
779d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            }
780d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang        }
781d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang
782d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang        public double getDoubleValue(ByteOrder byteOrder) {
783d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            Object value = getValue(byteOrder);
784d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            if (value == null) {
785d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                throw new NumberFormatException("NULL can't be converted to a double value");
786a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar            }
787f80a1f5075a7c6e1982d37c68bfed7c9a611bb20Wei Jia            if (value instanceof String) {
788f80a1f5075a7c6e1982d37c68bfed7c9a611bb20Wei Jia                return Double.parseDouble((String) value);
789f80a1f5075a7c6e1982d37c68bfed7c9a611bb20Wei Jia            }
790f80a1f5075a7c6e1982d37c68bfed7c9a611bb20Wei Jia            if (value instanceof long[]) {
791f80a1f5075a7c6e1982d37c68bfed7c9a611bb20Wei Jia                long[] array = (long[]) value;
792f80a1f5075a7c6e1982d37c68bfed7c9a611bb20Wei Jia                if (array.length == 1) {
793a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar                    return array[0];
794a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar                }
795ce18d7d85a78ac6642624fef1b5831eff4c72d56Jamie Gennis                throw new NumberFormatException("There are more than one component");
796a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar            }
797a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar            if (value instanceof int[]) {
79883750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis                int[] array = (int[]) value;
79983750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis                if (array.length == 1) {
800ce18d7d85a78ac6642624fef1b5831eff4c72d56Jamie Gennis                    return array[0];
80183750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis                }
802a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker                throw new NumberFormatException("There are more than one component");
803a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker            }
804a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker            if (value instanceof double[]) {
805a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker                double[] array = (double[]) value;
806a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker                if (array.length == 1) {
807a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker                    return array[0];
808a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker                }
809a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker                throw new NumberFormatException("There are more than one component");
810a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker            }
811a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker            if (value instanceof Rational[]) {
812a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker                Rational[] array = (Rational[]) value;
813a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker                if (array.length == 1) {
814a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker                    return array[0].calculate();
815a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker                }
816a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker                throw new NumberFormatException("There are more than one component");
817a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker            }
81877d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang            throw new NumberFormatException("Couldn't find a double value");
81977d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang        }
82077d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang
82177d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang        public int getIntValue(ByteOrder byteOrder) {
82277d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang            Object value = getValue(byteOrder);
82377d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang            if (value == null) {
824a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker                throw new NumberFormatException("NULL can't be converted to a integer value");
825a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker            }
826f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            if (value instanceof String) {
827a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker                return Integer.parseInt((String) value);
828a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker            }
829a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker            if (value instanceof long[]) {
83096626b7f9a4e5c9e1e04f7f710383631d1470364Marco Nelissen                long[] array = (long[]) value;
831aef47bf801dcbcb88cec8426c03237c6313da1c2Lajos Molnar                if (array.length == 1) {
832aef47bf801dcbcb88cec8426c03237c6313da1c2Lajos Molnar                    return (int) array[0];
833a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker                }
834a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker                throw new NumberFormatException("There are more than one component");
835aef47bf801dcbcb88cec8426c03237c6313da1c2Lajos Molnar            }
836a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker            if (value instanceof int[]) {
837a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar                int[] array = (int[]) value;
838a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar                if (array.length == 1) {
839a94c49819fd1513f1d60dbfb433017ca64bbd7dbJeff Tinker                    return array[0];
840f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                }
84183750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis                throw new NumberFormatException("There are more than one component");
84283750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis            }
843e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis            throw new NumberFormatException("Couldn't find a integer value");
844e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis        }
845e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis
846e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis        public String getStringValue(ByteOrder byteOrder) {
847e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis            Object value = getValue(byteOrder);
848ce18d7d85a78ac6642624fef1b5831eff4c72d56Jamie Gennis            if (value == null) {
849ce18d7d85a78ac6642624fef1b5831eff4c72d56Jamie Gennis                return null;
850ce18d7d85a78ac6642624fef1b5831eff4c72d56Jamie Gennis            }
851e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis            if (value instanceof String) {
852e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis                return (String) value;
853f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            }
854e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis
855e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis            final StringBuilder stringBuilder = new StringBuilder();
856e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis            if (value instanceof long[]) {
857f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                long[] array = (long[]) value;
858f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                for (int i = 0; i < array.length; ++i) {
859f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                    stringBuilder.append(array[i]);
860e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis                    if (i + 1 != array.length) {
861e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis                        stringBuilder.append(",");
862e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis                    }
863f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                }
864f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                return stringBuilder.toString();
865f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            }
866e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis            if (value instanceof int[]) {
867e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis                int[] array = (int[]) value;
868e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis                for (int i = 0; i < array.length; ++i) {
869e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis                    stringBuilder.append(array[i]);
870e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis                    if (i + 1 != array.length) {
871e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis                        stringBuilder.append(",");
872e2ce6458659c6e1bad420357b61dc10cd8bbe2abJamie Gennis                    }
873f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                }
874054219874873b41f1c815552987c10465c34ba2bLajos Molnar                return stringBuilder.toString();
875d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            }
876d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            if (value instanceof double[]) {
877d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar                double[] array = (double[]) value;
878d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar                for (int i = 0; i < array.length; ++i) {
879054219874873b41f1c815552987c10465c34ba2bLajos Molnar                    stringBuilder.append(array[i]);
8801a19076c36cbe76a537b5742e96747135b4f0d46mspector@google.com                    if (i + 1 != array.length) {
8813e328782f1e1061d08ea0c45b855cc418a2d9ea6Lajos Molnar                        stringBuilder.append(",");
8823e328782f1e1061d08ea0c45b855cc418a2d9ea6Lajos Molnar                    }
8833e328782f1e1061d08ea0c45b855cc418a2d9ea6Lajos Molnar                }
884054219874873b41f1c815552987c10465c34ba2bLajos Molnar                return stringBuilder.toString();
885054219874873b41f1c815552987c10465c34ba2bLajos Molnar            }
886054219874873b41f1c815552987c10465c34ba2bLajos Molnar            if (value instanceof Rational[]) {
887e870772a78ffe08b1c14a791e368f1499f1be0f3James Dong                Rational[] array = (Rational[]) value;
888e870772a78ffe08b1c14a791e368f1499f1be0f3James Dong                for (int i = 0; i < array.length; ++i) {
889e870772a78ffe08b1c14a791e368f1499f1be0f3James Dong                    stringBuilder.append(array[i].numerator);
890e870772a78ffe08b1c14a791e368f1499f1be0f3James Dong                    stringBuilder.append('/');
891054219874873b41f1c815552987c10465c34ba2bLajos Molnar                    stringBuilder.append(array[i].denominator);
892054219874873b41f1c815552987c10465c34ba2bLajos Molnar                    if (i + 1 != array.length) {
893054219874873b41f1c815552987c10465c34ba2bLajos Molnar                        stringBuilder.append(",");
8943e328782f1e1061d08ea0c45b855cc418a2d9ea6Lajos Molnar                    }
895512e979284de984427e5b2f73b9054ae1b5e2b0aLajos Molnar                }
896054219874873b41f1c815552987c10465c34ba2bLajos Molnar                return stringBuilder.toString();
897054219874873b41f1c815552987c10465c34ba2bLajos Molnar            }
898054219874873b41f1c815552987c10465c34ba2bLajos Molnar            return null;
899054219874873b41f1c815552987c10465c34ba2bLajos Molnar        }
900512e979284de984427e5b2f73b9054ae1b5e2b0aLajos Molnar
9013e328782f1e1061d08ea0c45b855cc418a2d9ea6Lajos Molnar        public int size() {
9023e328782f1e1061d08ea0c45b855cc418a2d9ea6Lajos Molnar            return IFD_FORMAT_BYTES_PER_FORMAT[format] * numberOfComponents;
9033e328782f1e1061d08ea0c45b855cc418a2d9ea6Lajos Molnar        }
9043e328782f1e1061d08ea0c45b855cc418a2d9ea6Lajos Molnar    }
905f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
906f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    // A class for indicating EXIF tag.
907f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private static class ExifTag {
908054219874873b41f1c815552987c10465c34ba2bLajos Molnar        public final int number;
909054219874873b41f1c815552987c10465c34ba2bLajos Molnar        public final String name;
9103e328782f1e1061d08ea0c45b855cc418a2d9ea6Lajos Molnar        public final int primaryFormat;
911054219874873b41f1c815552987c10465c34ba2bLajos Molnar        public final int secondaryFormat;
912054219874873b41f1c815552987c10465c34ba2bLajos Molnar
913054219874873b41f1c815552987c10465c34ba2bLajos Molnar        private ExifTag(String name, int number, int format) {
914054219874873b41f1c815552987c10465c34ba2bLajos Molnar            this.name = name;
915054219874873b41f1c815552987c10465c34ba2bLajos Molnar            this.number = number;
916054219874873b41f1c815552987c10465c34ba2bLajos Molnar            this.primaryFormat = format;
9173e328782f1e1061d08ea0c45b855cc418a2d9ea6Lajos Molnar            this.secondaryFormat = -1;
9183e328782f1e1061d08ea0c45b855cc418a2d9ea6Lajos Molnar        }
9193e328782f1e1061d08ea0c45b855cc418a2d9ea6Lajos Molnar
920054219874873b41f1c815552987c10465c34ba2bLajos Molnar        private ExifTag(String name, int number, int primaryFormat, int secondaryFormat) {
921054219874873b41f1c815552987c10465c34ba2bLajos Molnar            this.name = name;
9222c8fec10807dc50c2d65d250e7a9157580f7094cRobert Shih            this.number = number;
9232c8fec10807dc50c2d65d250e7a9157580f7094cRobert Shih            this.primaryFormat = primaryFormat;
9242c8fec10807dc50c2d65d250e7a9157580f7094cRobert Shih            this.secondaryFormat = secondaryFormat;
925f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        }
926e870772a78ffe08b1c14a791e368f1499f1be0f3James Dong    }
927f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
928f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    // Primary image IFD TIFF tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
929f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private static final ExifTag[] IFD_TIFF_TAGS = new ExifTag[] {
930f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            // For below two, see TIFF 6.0 Spec Section 3: Bilevel Images.
931f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_NEW_SUBFILE_TYPE, 254, IFD_FORMAT_ULONG),
932f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_SUBFILE_TYPE, 255, IFD_FORMAT_ULONG),
933f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_IMAGE_WIDTH, 256, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
934054219874873b41f1c815552987c10465c34ba2bLajos Molnar            new ExifTag(TAG_IMAGE_LENGTH, 257, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
935054219874873b41f1c815552987c10465c34ba2bLajos Molnar            new ExifTag(TAG_BITS_PER_SAMPLE, 258, IFD_FORMAT_USHORT),
936f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_COMPRESSION, 259, IFD_FORMAT_USHORT),
937054219874873b41f1c815552987c10465c34ba2bLajos Molnar            new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262, IFD_FORMAT_USHORT),
938054219874873b41f1c815552987c10465c34ba2bLajos Molnar            new ExifTag(TAG_IMAGE_DESCRIPTION, 270, IFD_FORMAT_STRING),
939054219874873b41f1c815552987c10465c34ba2bLajos Molnar            new ExifTag(TAG_MAKE, 271, IFD_FORMAT_STRING),
940054219874873b41f1c815552987c10465c34ba2bLajos Molnar            new ExifTag(TAG_MODEL, 272, IFD_FORMAT_STRING),
941054219874873b41f1c815552987c10465c34ba2bLajos Molnar            new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
942054219874873b41f1c815552987c10465c34ba2bLajos Molnar            new ExifTag(TAG_ORIENTATION, 274, IFD_FORMAT_USHORT),
943054219874873b41f1c815552987c10465c34ba2bLajos Molnar            new ExifTag(TAG_SAMPLES_PER_PIXEL, 277, IFD_FORMAT_USHORT),
9443e328782f1e1061d08ea0c45b855cc418a2d9ea6Lajos Molnar            new ExifTag(TAG_ROWS_PER_STRIP, 278, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
9453e328782f1e1061d08ea0c45b855cc418a2d9ea6Lajos Molnar            new ExifTag(TAG_STRIP_BYTE_COUNTS, 279, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
9463e328782f1e1061d08ea0c45b855cc418a2d9ea6Lajos Molnar            new ExifTag(TAG_X_RESOLUTION, 282, IFD_FORMAT_URATIONAL),
947054219874873b41f1c815552987c10465c34ba2bLajos Molnar            new ExifTag(TAG_Y_RESOLUTION, 283, IFD_FORMAT_URATIONAL),
948054219874873b41f1c815552987c10465c34ba2bLajos Molnar            new ExifTag(TAG_PLANAR_CONFIGURATION, 284, IFD_FORMAT_USHORT),
949054219874873b41f1c815552987c10465c34ba2bLajos Molnar            new ExifTag(TAG_RESOLUTION_UNIT, 296, IFD_FORMAT_USHORT),
950e870772a78ffe08b1c14a791e368f1499f1be0f3James Dong            new ExifTag(TAG_TRANSFER_FUNCTION, 301, IFD_FORMAT_USHORT),
951054219874873b41f1c815552987c10465c34ba2bLajos Molnar            new ExifTag(TAG_SOFTWARE, 305, IFD_FORMAT_STRING),
952f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_DATETIME, 306, IFD_FORMAT_STRING),
953e870772a78ffe08b1c14a791e368f1499f1be0f3James Dong            new ExifTag(TAG_ARTIST, 315, IFD_FORMAT_STRING),
954e870772a78ffe08b1c14a791e368f1499f1be0f3James Dong            new ExifTag(TAG_WHITE_POINT, 318, IFD_FORMAT_URATIONAL),
95556ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319, IFD_FORMAT_URATIONAL),
95656ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            // See Adobe PageMaker® 6.0 TIFF Technical Notes, Note 1.
95756ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG),
95856ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG),
959d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG),
960d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529, IFD_FORMAT_URATIONAL),
961d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT),
962d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT),
963f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL),
964f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING),
96556ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
966d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
967d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            // RW2 file tags
968d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            // See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html)
969d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_RW2_SENSOR_TOP_BORDER, 4, IFD_FORMAT_ULONG),
970d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_RW2_SENSOR_LEFT_BORDER, 5, IFD_FORMAT_ULONG),
971d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_RW2_SENSOR_BOTTOM_BORDER, 6, IFD_FORMAT_ULONG),
97256ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_RW2_SENSOR_RIGHT_BORDER, 7, IFD_FORMAT_ULONG),
97356ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_RW2_ISO, 23, IFD_FORMAT_USHORT),
97456ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_RW2_JPG_FROM_RAW, 46, IFD_FORMAT_UNDEFINED)
97556ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar    };
97656ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar
97756ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar    // Primary image IFD Exif Private tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
978f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private static final ExifTag[] IFD_EXIF_TAGS = new ExifTag[] {
97956ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_EXPOSURE_TIME, 33434, IFD_FORMAT_URATIONAL),
98056ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_F_NUMBER, 33437, IFD_FORMAT_URATIONAL),
98156ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_EXPOSURE_PROGRAM, 34850, IFD_FORMAT_USHORT),
98256ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_SPECTRAL_SENSITIVITY, 34852, IFD_FORMAT_STRING),
983f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_ISO_SPEED_RATINGS, 34855, IFD_FORMAT_USHORT),
98456ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_OECF, 34856, IFD_FORMAT_UNDEFINED),
98556ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_EXIF_VERSION, 36864, IFD_FORMAT_STRING),
98656ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_DATETIME_ORIGINAL, 36867, IFD_FORMAT_STRING),
98756ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_DATETIME_DIGITIZED, 36868, IFD_FORMAT_STRING),
988f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_COMPONENTS_CONFIGURATION, 37121, IFD_FORMAT_UNDEFINED),
989f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_COMPRESSED_BITS_PER_PIXEL, 37122, IFD_FORMAT_URATIONAL),
990f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_SHUTTER_SPEED_VALUE, 37377, IFD_FORMAT_SRATIONAL),
991f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_APERTURE_VALUE, 37378, IFD_FORMAT_URATIONAL),
992f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_BRIGHTNESS_VALUE, 37379, IFD_FORMAT_SRATIONAL),
99356ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_EXPOSURE_BIAS_VALUE, 37380, IFD_FORMAT_SRATIONAL),
99456ce726019f700a95ce5b45beebceadae4836e30Lajos Molnar            new ExifTag(TAG_MAX_APERTURE_VALUE, 37381, IFD_FORMAT_URATIONAL),
9955a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_SUBJECT_DISTANCE, 37382, IFD_FORMAT_URATIONAL),
9965a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_METERING_MODE, 37383, IFD_FORMAT_USHORT),
9975a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_LIGHT_SOURCE, 37384, IFD_FORMAT_USHORT),
9985a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_FLASH, 37385, IFD_FORMAT_USHORT),
999d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            new ExifTag(TAG_FOCAL_LENGTH, 37386, IFD_FORMAT_URATIONAL),
1000d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            new ExifTag(TAG_SUBJECT_AREA, 37396, IFD_FORMAT_USHORT),
1001d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            new ExifTag(TAG_MAKER_NOTE, 37500, IFD_FORMAT_UNDEFINED),
1002d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            new ExifTag(TAG_USER_COMMENT, 37510, IFD_FORMAT_UNDEFINED),
1003f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_SUBSEC_TIME, 37520, IFD_FORMAT_STRING),
1004f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_SUBSEC_TIME_ORIGINAL, 37521, IFD_FORMAT_STRING),
10055a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_SUBSEC_TIME_DIGITIZED, 37522, IFD_FORMAT_STRING),
10065a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_FLASHPIX_VERSION, 40960, IFD_FORMAT_UNDEFINED),
10075a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_COLOR_SPACE, 40961, IFD_FORMAT_USHORT),
10085a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_PIXEL_X_DIMENSION, 40962, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
10095a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_PIXEL_Y_DIMENSION, 40963, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
10105a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_RELATED_SOUND_FILE, 40964, IFD_FORMAT_STRING),
10115a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG),
1012f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_FLASH_ENERGY, 41483, IFD_FORMAT_URATIONAL),
10135a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_SPATIAL_FREQUENCY_RESPONSE, 41484, IFD_FORMAT_UNDEFINED),
10145a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_FOCAL_PLANE_X_RESOLUTION, 41486, IFD_FORMAT_URATIONAL),
10155a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_FOCAL_PLANE_Y_RESOLUTION, 41487, IFD_FORMAT_URATIONAL),
10165a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_FOCAL_PLANE_RESOLUTION_UNIT, 41488, IFD_FORMAT_USHORT),
1017f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_SUBJECT_LOCATION, 41492, IFD_FORMAT_USHORT),
10185a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_EXPOSURE_INDEX, 41493, IFD_FORMAT_URATIONAL),
10195a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_SENSING_METHOD, 41495, IFD_FORMAT_USHORT),
10205a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_FILE_SOURCE, 41728, IFD_FORMAT_UNDEFINED),
10215a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_SCENE_TYPE, 41729, IFD_FORMAT_UNDEFINED),
10225a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_CFA_PATTERN, 41730, IFD_FORMAT_UNDEFINED),
1023f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_CUSTOM_RENDERED, 41985, IFD_FORMAT_USHORT),
1024f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_EXPOSURE_MODE, 41986, IFD_FORMAT_USHORT),
1025f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_WHITE_BALANCE, 41987, IFD_FORMAT_USHORT),
10265a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_DIGITAL_ZOOM_RATIO, 41988, IFD_FORMAT_URATIONAL),
10275a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_FOCAL_LENGTH_IN_35MM_FILM, 41989, IFD_FORMAT_USHORT),
10285a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_SCENE_CAPTURE_TYPE, 41990, IFD_FORMAT_USHORT),
10295a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_GAIN_CONTROL, 41991, IFD_FORMAT_USHORT),
1030f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_CONTRAST, 41992, IFD_FORMAT_USHORT),
1031f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_SATURATION, 41993, IFD_FORMAT_USHORT),
1032f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            new ExifTag(TAG_SHARPNESS, 41994, IFD_FORMAT_USHORT),
10335a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_DEVICE_SETTING_DESCRIPTION, 41995, IFD_FORMAT_UNDEFINED),
10345a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_SUBJECT_DISTANCE_RANGE, 41996, IFD_FORMAT_USHORT),
10355a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_IMAGE_UNIQUE_ID, 42016, IFD_FORMAT_STRING),
10365a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_DNG_VERSION, 50706, IFD_FORMAT_BYTE),
10375a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad            new ExifTag(TAG_DEFAULT_CROP_SIZE, 50720, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG)
1038f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    };
10395a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad
10405a446aafff3020d607ad6fb14cc7ae76dd8f7947Rachad    // Primary image IFD GPS Info tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
1041318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static final ExifTag[] IFD_GPS_TAGS = new ExifTag[] {
1042d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa            new ExifTag(TAG_GPS_VERSION_ID, 0, IFD_FORMAT_BYTE),
1043d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_GPS_LATITUDE_REF, 1, IFD_FORMAT_STRING),
1044d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_GPS_LATITUDE, 2, IFD_FORMAT_URATIONAL),
1045d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_GPS_LONGITUDE_REF, 3, IFD_FORMAT_STRING),
10463fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            new ExifTag(TAG_GPS_LONGITUDE, 4, IFD_FORMAT_URATIONAL),
10473fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            new ExifTag(TAG_GPS_ALTITUDE_REF, 5, IFD_FORMAT_BYTE),
1048d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_GPS_ALTITUDE, 6, IFD_FORMAT_URATIONAL),
1049298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia            new ExifTag(TAG_GPS_TIMESTAMP, 7, IFD_FORMAT_URATIONAL),
1050298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia            new ExifTag(TAG_GPS_SATELLITES, 8, IFD_FORMAT_STRING),
1051298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia            new ExifTag(TAG_GPS_STATUS, 9, IFD_FORMAT_STRING),
1052318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            new ExifTag(TAG_GPS_MEASURE_MODE, 10, IFD_FORMAT_STRING),
105381b554b04c93aebf3e74116330024272770d2967Marco Nelissen            new ExifTag(TAG_GPS_DOP, 11, IFD_FORMAT_URATIONAL),
105481b554b04c93aebf3e74116330024272770d2967Marco Nelissen            new ExifTag(TAG_GPS_SPEED_REF, 12, IFD_FORMAT_STRING),
105581b554b04c93aebf3e74116330024272770d2967Marco Nelissen            new ExifTag(TAG_GPS_SPEED, 13, IFD_FORMAT_URATIONAL),
105681b554b04c93aebf3e74116330024272770d2967Marco Nelissen            new ExifTag(TAG_GPS_TRACK_REF, 14, IFD_FORMAT_STRING),
105781b554b04c93aebf3e74116330024272770d2967Marco Nelissen            new ExifTag(TAG_GPS_TRACK, 15, IFD_FORMAT_URATIONAL),
1058d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_GPS_IMG_DIRECTION_REF, 16, IFD_FORMAT_STRING),
1059d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_GPS_IMG_DIRECTION, 17, IFD_FORMAT_URATIONAL),
1060f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_GPS_MAP_DATUM, 18, IFD_FORMAT_STRING),
1061f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_GPS_DEST_LATITUDE_REF, 19, IFD_FORMAT_STRING),
1062d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_GPS_DEST_LATITUDE, 20, IFD_FORMAT_URATIONAL),
1063f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_GPS_DEST_LONGITUDE_REF, 21, IFD_FORMAT_STRING),
1064f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_GPS_DEST_LONGITUDE, 22, IFD_FORMAT_URATIONAL),
1065d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_GPS_DEST_BEARING_REF, 23, IFD_FORMAT_STRING),
1066f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_GPS_DEST_BEARING, 24, IFD_FORMAT_URATIONAL),
1067f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_GPS_DEST_DISTANCE_REF, 25, IFD_FORMAT_STRING),
1068d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_GPS_DEST_DISTANCE, 26, IFD_FORMAT_URATIONAL),
1069f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_GPS_PROCESSING_METHOD, 27, IFD_FORMAT_UNDEFINED),
1070f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_GPS_AREA_INFORMATION, 28, IFD_FORMAT_UNDEFINED),
1071f851e5a9f968c519f659bf85e3bd7127dc2a2885Pawin Vongmasa            new ExifTag(TAG_GPS_DATESTAMP, 29, IFD_FORMAT_STRING),
1072f851e5a9f968c519f659bf85e3bd7127dc2a2885Pawin Vongmasa            new ExifTag(TAG_GPS_DIFFERENTIAL, 30, IFD_FORMAT_USHORT)
1073f851e5a9f968c519f659bf85e3bd7127dc2a2885Pawin Vongmasa    };
1074f851e5a9f968c519f659bf85e3bd7127dc2a2885Pawin Vongmasa    // Primary image IFD Interoperability tag (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
1075f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    private static final ExifTag[] IFD_INTEROPERABILITY_TAGS = new ExifTag[] {
1076f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_INTEROPERABILITY_INDEX, 1, IFD_FORMAT_STRING)
1077f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    };
1078f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    // IFD Thumbnail tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
1079cc7cc67349b7a3f498882087aa42ffc05a2daf11Lajos Molnar    private static final ExifTag[] IFD_THUMBNAIL_TAGS = new ExifTag[] {
1080318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            // For below two, see TIFF 6.0 Spec Section 3: Bilevel Images.
1081d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_NEW_SUBFILE_TYPE, 254, IFD_FORMAT_ULONG),
10823fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            new ExifTag(TAG_SUBFILE_TYPE, 255, IFD_FORMAT_ULONG),
10833fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            new ExifTag(TAG_THUMBNAIL_IMAGE_WIDTH, 256, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
10843fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            new ExifTag(TAG_THUMBNAIL_IMAGE_LENGTH, 257, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
1085f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_BITS_PER_SAMPLE, 258, IFD_FORMAT_USHORT),
1086f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_COMPRESSION, 259, IFD_FORMAT_USHORT),
1087d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262, IFD_FORMAT_USHORT),
108821b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            new ExifTag(TAG_IMAGE_DESCRIPTION, 270, IFD_FORMAT_STRING),
108921b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            new ExifTag(TAG_MAKE, 271, IFD_FORMAT_STRING),
109021b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            new ExifTag(TAG_MODEL, 272, IFD_FORMAT_STRING),
1091d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
109277d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang            new ExifTag(TAG_ORIENTATION, 274, IFD_FORMAT_USHORT),
109377d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang            new ExifTag(TAG_SAMPLES_PER_PIXEL, 277, IFD_FORMAT_USHORT),
109477d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang            new ExifTag(TAG_ROWS_PER_STRIP, 278, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
109577d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang            new ExifTag(TAG_STRIP_BYTE_COUNTS, 279, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
109677d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang            new ExifTag(TAG_X_RESOLUTION, 282, IFD_FORMAT_URATIONAL),
109777d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang            new ExifTag(TAG_Y_RESOLUTION, 283, IFD_FORMAT_URATIONAL),
1098f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_PLANAR_CONFIGURATION, 284, IFD_FORMAT_USHORT),
1099f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_RESOLUTION_UNIT, 296, IFD_FORMAT_USHORT),
1100f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_TRANSFER_FUNCTION, 301, IFD_FORMAT_USHORT),
1101f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_SOFTWARE, 305, IFD_FORMAT_STRING),
1102f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_DATETIME, 306, IFD_FORMAT_STRING),
1103f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_ARTIST, 315, IFD_FORMAT_STRING),
1104f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_WHITE_POINT, 318, IFD_FORMAT_URATIONAL),
1105f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319, IFD_FORMAT_URATIONAL),
1106f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            // See Adobe PageMaker® 6.0 TIFF Technical Notes, Note 1.
1107f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG),
1108f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG),
1109f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG),
1110f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529, IFD_FORMAT_URATIONAL),
1111f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT),
1112f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT),
1113d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL),
1114d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING),
1115d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
1116d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
1117d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_DNG_VERSION, 50706, IFD_FORMAT_BYTE),
1118d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_DEFAULT_CROP_SIZE, 50720, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG)
1119d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang    };
1120d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang
1121d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang    // RAF file tag (See piex.cc line 372)
1122d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang    private static final ExifTag TAG_RAF_IMAGE_SIZE =
1123d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT);
1124d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang
1125d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang    // ORF file tags (See http://www.exiv2.org/tags-olympus.html)
1126f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    private static final ExifTag[] ORF_MAKER_NOTE_TAGS = new ExifTag[] {
1127d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_ORF_THUMBNAIL_IMAGE, 256, IFD_FORMAT_UNDEFINED),
1128d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_ORF_CAMERA_SETTINGS_IFD_POINTER, 8224, IFD_FORMAT_ULONG),
1129d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_ORF_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_ULONG)
1130f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    };
1131d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang    private static final ExifTag[] ORF_CAMERA_SETTINGS_TAGS = new ExifTag[] {
1132d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            new ExifTag(TAG_ORF_PREVIEW_IMAGE_START, 257, IFD_FORMAT_ULONG),
1133ecc97eb44a0675974fcf43b0c68edaaa539d2996Chong Zhang            new ExifTag(TAG_ORF_PREVIEW_IMAGE_LENGTH, 258, IFD_FORMAT_ULONG)
1134ecc97eb44a0675974fcf43b0c68edaaa539d2996Chong Zhang    };
1135ecc97eb44a0675974fcf43b0c68edaaa539d2996Chong Zhang    private static final ExifTag[] ORF_IMAGE_PROCESSING_TAGS = new ExifTag[] {
1136d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            new ExifTag(TAG_ORF_ASPECT_FRAME, 4371, IFD_FORMAT_USHORT)
113721b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    };
113821b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    // PEF file tag (See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Pentax.html)
113921b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    private static final ExifTag[] PEF_TAGS = new ExifTag[] {
114021b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            new ExifTag(TAG_COLOR_SPACE, 55, IFD_FORMAT_USHORT)
1141d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    };
1142ecc97eb44a0675974fcf43b0c68edaaa539d2996Chong Zhang
1143ecc97eb44a0675974fcf43b0c68edaaa539d2996Chong Zhang    // See JEITA CP-3451C Section 4.6.3: Exif-specific IFD.
114421b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    // The following values are used for indicating pointers to the other Image File Directories.
1145d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar
1146f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    // Indices of Exif Ifd tag groups
114721b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    /** @hide */
114821b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    @Retention(RetentionPolicy.SOURCE)
114921b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    @IntDef({IFD_TYPE_PRIMARY, IFD_TYPE_EXIF, IFD_TYPE_GPS, IFD_TYPE_INTEROPERABILITY,
115021b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            IFD_TYPE_THUMBNAIL, IFD_TYPE_PREVIEW, IFD_TYPE_ORF_MAKER_NOTE,
115121b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            IFD_TYPE_ORF_CAMERA_SETTINGS, IFD_TYPE_ORF_IMAGE_PROCESSING, IFD_TYPE_PEF})
115221b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    public @interface IfdType {}
11533fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang
1154f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    private static final int IFD_TYPE_PRIMARY = 0;
115521b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    private static final int IFD_TYPE_EXIF = 1;
1156d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    private static final int IFD_TYPE_GPS = 2;
11573fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang    private static final int IFD_TYPE_INTEROPERABILITY = 3;
115821b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    private static final int IFD_TYPE_THUMBNAIL = 4;
115921b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    private static final int IFD_TYPE_PREVIEW = 5;
116021b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    private static final int IFD_TYPE_ORF_MAKER_NOTE = 6;
116121b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    private static final int IFD_TYPE_ORF_CAMERA_SETTINGS = 7;
116221b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    private static final int IFD_TYPE_ORF_IMAGE_PROCESSING = 8;
116321b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    private static final int IFD_TYPE_PEF = 9;
116421b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang
116521b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    // List of Exif tag groups
116621b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    private static final ExifTag[][] EXIF_TAGS = new ExifTag[][] {
1167318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            IFD_TIFF_TAGS, IFD_EXIF_TAGS, IFD_GPS_TAGS, IFD_INTEROPERABILITY_TAGS,
116821b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            IFD_THUMBNAIL_TAGS, IFD_TIFF_TAGS, ORF_MAKER_NOTE_TAGS, ORF_CAMERA_SETTINGS_TAGS,
1169f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            ORF_IMAGE_PROCESSING_TAGS, PEF_TAGS
117021b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    };
11718a21c0191f974a0b9cbd5818052e2655e0aaa306Pawin Vongmasa    // List of tags for pointing to the other image file directory offset.
11723fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang    private static final ExifTag[] EXIF_POINTER_TAGS = new ExifTag[] {
117321b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG),
1174f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
117521b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
1176318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG),
117721b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            new ExifTag(TAG_ORF_CAMERA_SETTINGS_IFD_POINTER, 8224, IFD_FORMAT_BYTE),
117821b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            new ExifTag(TAG_ORF_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_BYTE)
117921b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    };
118021b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang
118121b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    // Tags for indicating the thumbnail offset and length
118221b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    private static final ExifTag JPEG_INTERCHANGE_FORMAT_TAG =
118321b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG);
118421b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    private static final ExifTag JPEG_INTERCHANGE_FORMAT_LENGTH_TAG =
118521b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG);
1186318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber
118721b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    // Mappings from tag number to tag name and each item represents one IFD tag group.
1188318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static final HashMap[] sExifTagMapsForReading = new HashMap[EXIF_TAGS.length];
1189318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    // Mappings from tag name to tag number and each item represents one IFD tag group.
1190318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static final HashMap[] sExifTagMapsForWriting = new HashMap[EXIF_TAGS.length];
1191318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static final HashSet<String> sTagSetForCompatibility = new HashSet<>(Arrays.asList(
1192318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            TAG_F_NUMBER, TAG_DIGITAL_ZOOM_RATIO, TAG_EXPOSURE_TIME, TAG_SUBJECT_DISTANCE,
1193f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            TAG_GPS_TIMESTAMP));
1194318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    // Mappings from tag number to IFD type for pointer tags.
1195318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static final HashMap sExifPointerTagMap = new HashMap();
119603b268eac37ca2589bfff0bf58daf79d29cc14f4Andreas Huber
119703b268eac37ca2589bfff0bf58daf79d29cc14f4Andreas Huber    // See JPEG File Interchange Format Version 1.02.
1198609b815a3131d22da38b2f452faa9f89daad4039Andy Hung    // The following values are defined for handling JPEG streams. In this implementation, we are
1199318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    // not only getting information from EXIF but also from some JPEG special segments such as
1200d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    // MARKER_COM for user comment and MARKER_SOFx for image width and height.
1201d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber
12026d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang    private static final Charset ASCII = Charset.forName("US-ASCII");
1203f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden    // Identifier for EXIF APP1 segment in JPEG
12046d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang    private static final byte[] IDENTIFIER_EXIF_APP1 = "Exif\0\0".getBytes(ASCII);
1205f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden    // JPEG segment markers, that each marker consumes two bytes beginning with 0xff and ending with
1206f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden    // the indicator. There is no SOF4, SOF8, SOF16 markers in JPEG and SOFx markers indicates start
1207f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    // of frame(baseline DCT) and the image size info exists in its beginning part.
1208f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    private static final byte MARKER = (byte) 0xff;
1209318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static final byte MARKER_SOI = (byte) 0xd8;
1210318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static final byte MARKER_SOF0 = (byte) 0xc0;
1211318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private static final byte MARKER_SOF1 = (byte) 0xc1;
1212c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final byte MARKER_SOF2 = (byte) 0xc2;
1213c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final byte MARKER_SOF3 = (byte) 0xc3;
1214d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa    private static final byte MARKER_SOF5 = (byte) 0xc5;
1215298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia    private static final byte MARKER_SOF6 = (byte) 0xc6;
1216298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia    private static final byte MARKER_SOF7 = (byte) 0xc7;
1217298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia    private static final byte MARKER_SOF9 = (byte) 0xc9;
1218298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia    private static final byte MARKER_SOF10 = (byte) 0xca;
1219c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final byte MARKER_SOF11 = (byte) 0xcb;
1220c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final byte MARKER_SOF13 = (byte) 0xcd;
1221c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final byte MARKER_SOF14 = (byte) 0xce;
1222f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private static final byte MARKER_SOF15 = (byte) 0xcf;
1223c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final byte MARKER_SOS = (byte) 0xda;
1224c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final byte MARKER_APP1 = (byte) 0xe1;
1225f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private static final byte MARKER_COM = (byte) 0xfe;
1226f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private static final byte MARKER_EOI = (byte) 0xd9;
1227f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
1228f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    // Supported Image File Types
1229f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private static final int IMAGE_TYPE_UNKNOWN = 0;
1230c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final int IMAGE_TYPE_ARW = 1;
1231c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final int IMAGE_TYPE_CR2 = 2;
123241eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim    private static final int IMAGE_TYPE_DNG = 3;
1233c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final int IMAGE_TYPE_JPEG = 4;
1234c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final int IMAGE_TYPE_NEF = 5;
1235c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final int IMAGE_TYPE_NRW = 6;
1236c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final int IMAGE_TYPE_ORF = 7;
1237c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final int IMAGE_TYPE_PEF = 8;
1238c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final int IMAGE_TYPE_RAF = 9;
1239c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final int IMAGE_TYPE_RW2 = 10;
1240c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private static final int IMAGE_TYPE_SRW = 11;
1241c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan
1242c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    static {
1243c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan        sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
1244c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan        sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
1245c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan
1246c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan        // Build up the hash tables to look up Exif tags for reading Exif tags.
1247f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
1248c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan            sExifTagMapsForReading[ifdType] = new HashMap();
1249c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan            sExifTagMapsForWriting[ifdType] = new HashMap();
1250c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan            for (ExifTag tag : EXIF_TAGS[ifdType]) {
1251f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                sExifTagMapsForReading[ifdType].put(tag.number, tag);
1252c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan                sExifTagMapsForWriting[ifdType].put(tag.name, tag);
1253c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan            }
1254c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan        }
1255c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan
1256c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan        // Build up the hash table to look up Exif pointer tags.
1257609b815a3131d22da38b2f452faa9f89daad4039Andy Hung        sExifPointerTagMap.put(EXIF_POINTER_TAGS[0].number, IFD_TYPE_PREVIEW); // 330
1258c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan        sExifPointerTagMap.put(EXIF_POINTER_TAGS[1].number, IFD_TYPE_EXIF); // 34665
1259c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan        sExifPointerTagMap.put(EXIF_POINTER_TAGS[2].number, IFD_TYPE_GPS); // 34853
1260f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        sExifPointerTagMap.put(EXIF_POINTER_TAGS[3].number, IFD_TYPE_INTEROPERABILITY); // 40965
1261f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        sExifPointerTagMap.put(EXIF_POINTER_TAGS[4].number, IFD_TYPE_ORF_CAMERA_SETTINGS); // 8224
1262c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan        sExifPointerTagMap.put(EXIF_POINTER_TAGS[5].number, IFD_TYPE_ORF_IMAGE_PROCESSING); // 8256
1263c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    }
1264c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan
1265c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private final String mFilename;
1266c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private final AssetManager.AssetInputStream mAssetInputStream;
1267c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    private int mMimeType;
1268d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang    private final HashMap[] mAttributes = new HashMap[EXIF_TAGS.length];
126983750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis    private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN;
1270d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa    private boolean mHasThumbnail;
1271298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia    // The following values used for indicating a thumbnail position.
1272298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia    private int mThumbnailOffset;
1273298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia    private int mThumbnailLength;
1274298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia    private byte[] mThumbnailBytes;
127583750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis    private int mThumbnailCompression;
12763fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang    private int mExifOffset;
12773fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang    private int mOrfMakerNoteOffset;
12783fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang    private int mOrfThumbnailOffset;
12793fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang    private int mOrfThumbnailLength;
12803fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang    private int mRw2JpgFromRawOffset;
12813fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang    private boolean mIsSupportedFile;
12823fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang
128377d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang    // Pattern to check non zero timestamp
128477d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang    private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
128577d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang    // Pattern to check gps timestamp
128677d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang    private static final Pattern sGpsTimestampPattern =
128777d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang            Pattern.compile("^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$");
128877d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang
128977d00ee0c03ab8043e2f422232b27f5852bb3bb5Dongwon Kang    /**
1290c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan     * Reads Exif tags from the specified image file.
129183750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis     */
1292c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan    public ExifInterface(String filename) throws IOException {
1293c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan        if (filename == null) {
1294c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan            throw new IllegalArgumentException("filename cannot be null");
1295c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan        }
1296c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan        FileInputStream in = null;
1297c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan        mAssetInputStream = null;
1298c5a57efb706ec79032fc09c43b16d11ed0876604Anu Sundararajan        mFilename = filename;
1299ce18d7d85a78ac6642624fef1b5831eff4c72d56Jamie Gennis        try {
1300ce18d7d85a78ac6642624fef1b5831eff4c72d56Jamie Gennis            in = new FileInputStream(filename);
1301ce18d7d85a78ac6642624fef1b5831eff4c72d56Jamie Gennis            loadAttributes(in);
130283750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis        } finally {
1303f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            closeQuietly(in);
130483750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis        }
130583750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis    }
130683750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis
130741eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim    /**
130883750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis     * Reads Exif tags from the specified image input stream. Attribute mutation is not supported
130983750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis     * for input streams. The given input stream will proceed its current position. Developers
131083750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis     * should close the input stream after use. This constructor is not intended to be used with
131183750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis     * an input stream that performs any networking operations.
131283750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis     */
131383750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis    public ExifInterface(InputStream inputStream) throws IOException {
131483750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis        if (inputStream == null) {
131583750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis            throw new IllegalArgumentException("inputStream cannot be null");
131683750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis        }
131783750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis        mFilename = null;
131883750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis        if (inputStream instanceof AssetManager.AssetInputStream) {
131983750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis            mAssetInputStream = (AssetManager.AssetInputStream) inputStream;
132083750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis        } else {
132183750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis            mAssetInputStream = null;
132283750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis        }
132383750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis        loadAttributes(inputStream);
1324f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    }
1325f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
132683750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis    /**
132783750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis     * Returns the EXIF attribute of the specified tag or {@code null} if there is no such tag in
132883750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis     * the image file.
132983750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis     *
133083750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis     * @param tag the name of the tag.
133183750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis     */
1332f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private ExifAttribute getExifAttribute(String tag) {
133383750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis        // Retrieves all tag groups. The value from primary image tag group has a higher priority
133483750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis        // than the value from the thumbnail tag group if there are more than one candidates.
133583750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis        for (int i = 0; i < EXIF_TAGS.length; ++i) {
133683750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis            Object value = mAttributes[i].get(tag);
1337609b815a3131d22da38b2f452faa9f89daad4039Andy Hung            if (value != null) {
133883750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis                return (ExifAttribute) value;
133983750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis            }
1340f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        }
1341f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        return null;
134283750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis    }
134383750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis
134483750eaf5a3f38c243a9e7eb81d4b2421e3a0d88Jamie Gennis    /**
13453fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     * Returns the value of the specified tag or {@code null} if there
13463fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     * is no such tag in the image file.
1347d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa     *
13483fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     * @param tag the name of the tag.
13493fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     */
13503fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang    public String getAttribute(String tag) {
13513fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang        ExifAttribute attribute = getExifAttribute(tag);
1352d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang        if (attribute != null) {
1353d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            if (!sTagSetForCompatibility.contains(tag)) {
13543fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                return attribute.getStringValue(mExifByteOrder);
13553fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            }
13563fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            if (tag.equals(TAG_GPS_TIMESTAMP)) {
1357f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa                // Convert the rational values to the custom formats for backwards compatibility.
13583fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                if (attribute.format != IFD_FORMAT_URATIONAL
13593fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                        && attribute.format != IFD_FORMAT_SRATIONAL) {
13603fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                    Log.w(TAG, "GPS Timestamp format is not rational. format=" + attribute.format);
13613fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                    return null;
13623fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                }
13633fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                Rational[] array = (Rational[]) attribute.getValue(mExifByteOrder);
13643fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                if (array == null || array.length != 3) {
13653fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                    Log.w(TAG, "Invalid GPS Timestamp array. array=" + Arrays.toString(array));
13663fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                    return null;
13673fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                }
1368054219874873b41f1c815552987c10465c34ba2bLajos Molnar                return String.format("%02d:%02d:%02d",
1369f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                        (int) ((float) array[0].numerator / array[0].denominator),
1370d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa                        (int) ((float) array[1].numerator / array[1].denominator),
1371298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                        (int) ((float) array[2].numerator / array[2].denominator));
1372298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia            }
1373298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia            try {
1374298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                return Double.toString(attribute.getDoubleValue(mExifByteOrder));
1375298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia            } catch (NumberFormatException e) {
1376298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                return null;
1377054219874873b41f1c815552987c10465c34ba2bLajos Molnar            }
1378054219874873b41f1c815552987c10465c34ba2bLajos Molnar        }
1379054219874873b41f1c815552987c10465c34ba2bLajos Molnar        return null;
1380d0715867861c216e88a4a7523b6da8a3cb128724Lajos Molnar    }
1381d0715867861c216e88a4a7523b6da8a3cb128724Lajos Molnar
138221b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    /**
1383d0715867861c216e88a4a7523b6da8a3cb128724Lajos Molnar     * Returns the integer value of the specified tag. If there is no such tag
1384e63de4c60116261b90bc93b608ccd4bd182f3484Lajos Molnar     * in the image file or the value cannot be parsed as integer, return
1385e63de4c60116261b90bc93b608ccd4bd182f3484Lajos Molnar     * <var>defaultValue</var>.
1386e63de4c60116261b90bc93b608ccd4bd182f3484Lajos Molnar     *
1387e63de4c60116261b90bc93b608ccd4bd182f3484Lajos Molnar     * @param tag the name of the tag.
1388054219874873b41f1c815552987c10465c34ba2bLajos Molnar     * @param defaultValue the value to return if the tag is not available.
13899847fcefb183e1cb09eb48e17a09577392b0e8f4Lajos Molnar     */
1390e63de4c60116261b90bc93b608ccd4bd182f3484Lajos Molnar    public int getAttributeInt(String tag, int defaultValue) {
1391e63de4c60116261b90bc93b608ccd4bd182f3484Lajos Molnar        ExifAttribute exifAttribute = getExifAttribute(tag);
1392e63de4c60116261b90bc93b608ccd4bd182f3484Lajos Molnar        if (exifAttribute == null) {
1393054219874873b41f1c815552987c10465c34ba2bLajos Molnar            return defaultValue;
1394054219874873b41f1c815552987c10465c34ba2bLajos Molnar        }
1395054219874873b41f1c815552987c10465c34ba2bLajos Molnar
1396054219874873b41f1c815552987c10465c34ba2bLajos Molnar        try {
1397e63de4c60116261b90bc93b608ccd4bd182f3484Lajos Molnar            return exifAttribute.getIntValue(mExifByteOrder);
1398054219874873b41f1c815552987c10465c34ba2bLajos Molnar        } catch (NumberFormatException e) {
1399054219874873b41f1c815552987c10465c34ba2bLajos Molnar            return defaultValue;
1400054219874873b41f1c815552987c10465c34ba2bLajos Molnar        }
1401054219874873b41f1c815552987c10465c34ba2bLajos Molnar    }
1402f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
140349605e8ab171a2b1f474645d632d3982f5f7b8e6Lajos Molnar    /**
140449605e8ab171a2b1f474645d632d3982f5f7b8e6Lajos Molnar     * Returns the double value of the tag that is specified as rational or contains a
1405d0715867861c216e88a4a7523b6da8a3cb128724Lajos Molnar     * double-formatted value. If there is no such tag in the image file or the value cannot be
1406d0715867861c216e88a4a7523b6da8a3cb128724Lajos Molnar     * parsed as double, return <var>defaultValue</var>.
1407d0715867861c216e88a4a7523b6da8a3cb128724Lajos Molnar     *
14083fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     * @param tag the name of the tag.
14093fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     * @param defaultValue the value to return if the tag is not available.
1410d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa     */
14117e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar    public double getAttributeDouble(String tag, double defaultValue) {
14127e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar        ExifAttribute exifAttribute = getExifAttribute(tag);
14137e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar        if (exifAttribute == null) {
14147e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar            return defaultValue;
14157e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar        }
14167e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar
14177e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar        try {
14187e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar            return exifAttribute.getDoubleValue(mExifByteOrder);
14197e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar        } catch (NumberFormatException e) {
14207e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar            return defaultValue;
14217e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar        }
142221b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang    }
14237e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar
14247e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar    /**
14257e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar     * Set the value of the specified tag.
14267e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar     *
14277e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar     * @param tag the name of the tag.
14287e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar     * @param value the value of the tag.
14297e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar     */
14307e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar    public void setAttribute(String tag, String value) {
14317e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar        // Convert the given value to rational values for backwards compatibility.
14327e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar        if (value != null && sTagSetForCompatibility.contains(tag)) {
14337e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar            if (tag.equals(TAG_GPS_TIMESTAMP)) {
14347e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar                Matcher m = sGpsTimestampPattern.matcher(value);
14357e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar                if (!m.find()) {
14367e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar                    Log.w(TAG, "Invalid value for " + tag + " : " + value);
14377e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar                    return;
14387e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar                }
14397e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar                value = Integer.parseInt(m.group(1)) + "/1," + Integer.parseInt(m.group(2)) + "/1,"
14407e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar                        + Integer.parseInt(m.group(3)) + "/1";
14417e0bef8aa6bf9db06079b743794ec2712ad84431Lajos Molnar            } else {
1442addf2cbb120346ae42e78fa739245a353db5edadChong Zhang                try {
1443addf2cbb120346ae42e78fa739245a353db5edadChong Zhang                    double doubleValue = Double.parseDouble(value);
1444addf2cbb120346ae42e78fa739245a353db5edadChong Zhang                    value = (long) (doubleValue * 10000L) + "/10000";
1445addf2cbb120346ae42e78fa739245a353db5edadChong Zhang                } catch (NumberFormatException e) {
1446f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                    Log.w(TAG, "Invalid value for " + tag + " : " + value);
1447f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                    return;
1448d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar                }
1449addf2cbb120346ae42e78fa739245a353db5edadChong Zhang            }
1450d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar        }
1451d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar
1452d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar        for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
1453d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar            if (i == IFD_TYPE_THUMBNAIL && !mHasThumbnail) {
14546d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang                continue;
1455f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden            }
1456f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden            final Object obj = sExifTagMapsForWriting[i].get(tag);
1457f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden            if (obj != null) {
1458addf2cbb120346ae42e78fa739245a353db5edadChong Zhang                if (value == null) {
1459f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                    mAttributes[i].remove(tag);
1460f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                    continue;
1461f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                }
1462f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                final ExifTag exifTag = (ExifTag) obj;
1463f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                Pair<Integer, Integer> guess = guessDataFormat(value);
1464f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                int dataFormat;
1465f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                if (exifTag.primaryFormat == guess.first || exifTag.primaryFormat == guess.second) {
1466f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                    dataFormat = exifTag.primaryFormat;
1467addf2cbb120346ae42e78fa739245a353db5edadChong Zhang                } else if (exifTag.secondaryFormat != -1 && (exifTag.secondaryFormat == guess.first
1468f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                        || exifTag.secondaryFormat == guess.second)) {
1469f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                    dataFormat = exifTag.secondaryFormat;
1470f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                } else if (exifTag.primaryFormat == IFD_FORMAT_BYTE
1471f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                        || exifTag.primaryFormat == IFD_FORMAT_UNDEFINED
1472addf2cbb120346ae42e78fa739245a353db5edadChong Zhang                        || exifTag.primaryFormat == IFD_FORMAT_STRING) {
1473addf2cbb120346ae42e78fa739245a353db5edadChong Zhang                    dataFormat = exifTag.primaryFormat;
1474f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                } else {
1475f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                    Log.w(TAG, "Given tag (" + tag + ") value didn't match with one of expected "
1476f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                            + "formats: " + IFD_FORMAT_NAMES[exifTag.primaryFormat]
1477ba6218eae3dbcf3f962b3561b26374a214dbf5e2Andy McFadden                            + (exifTag.secondaryFormat == -1 ? "" : ", "
1478f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                            + IFD_FORMAT_NAMES[exifTag.secondaryFormat]) + " (guess: "
1479f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                            + IFD_FORMAT_NAMES[guess.first] + (guess.second == -1 ? "" : ", "
1480f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                            + IFD_FORMAT_NAMES[guess.second]) + ")");
1481ba6218eae3dbcf3f962b3561b26374a214dbf5e2Andy McFadden                    continue;
1482ba6218eae3dbcf3f962b3561b26374a214dbf5e2Andy McFadden                }
1483ba6218eae3dbcf3f962b3561b26374a214dbf5e2Andy McFadden                switch (dataFormat) {
1484addf2cbb120346ae42e78fa739245a353db5edadChong Zhang                    case IFD_FORMAT_BYTE: {
1485addf2cbb120346ae42e78fa739245a353db5edadChong Zhang                        mAttributes[i].put(tag, ExifAttribute.createByte(value));
1486addf2cbb120346ae42e78fa739245a353db5edadChong Zhang                        break;
1487addf2cbb120346ae42e78fa739245a353db5edadChong Zhang                    }
1488addf2cbb120346ae42e78fa739245a353db5edadChong Zhang                    case IFD_FORMAT_UNDEFINED:
1489298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                    case IFD_FORMAT_STRING: {
1490298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                        mAttributes[i].put(tag, ExifAttribute.createString(value));
1491298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                        break;
1492addf2cbb120346ae42e78fa739245a353db5edadChong Zhang                    }
1493d291c222357303b9611cab89d0c3b047584ef377Chong Zhang                    case IFD_FORMAT_USHORT: {
1494d291c222357303b9611cab89d0c3b047584ef377Chong Zhang                        final String[] values = value.split(",");
1495d291c222357303b9611cab89d0c3b047584ef377Chong Zhang                        final int[] intArray = new int[values.length];
1496a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar                        for (int j = 0; j < values.length; ++j) {
1497d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa                            intArray[j] = Integer.parseInt(values[j]);
14981b40f2804a27b695e9e53fb1699b64cb0dd387f9Lajos Molnar                        }
1499a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar                        mAttributes[i].put(tag,
1500298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                                ExifAttribute.createUShort(intArray, mExifByteOrder));
1501298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                        break;
1502298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                    }
1503298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                    case IFD_FORMAT_SLONG: {
1504f80a1f5075a7c6e1982d37c68bfed7c9a611bb20Wei Jia                        final String[] values = value.split(",");
1505f80a1f5075a7c6e1982d37c68bfed7c9a611bb20Wei Jia                        final int[] intArray = new int[values.length];
1506f80a1f5075a7c6e1982d37c68bfed7c9a611bb20Wei Jia                        for (int j = 0; j < values.length; ++j) {
1507f80a1f5075a7c6e1982d37c68bfed7c9a611bb20Wei Jia                            intArray[j] = Integer.parseInt(values[j]);
1508f80a1f5075a7c6e1982d37c68bfed7c9a611bb20Wei Jia                        }
1509f80a1f5075a7c6e1982d37c68bfed7c9a611bb20Wei Jia                        mAttributes[i].put(tag,
1510318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                                ExifAttribute.createSLong(intArray, mExifByteOrder));
1511318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        break;
151281b554b04c93aebf3e74116330024272770d2967Marco Nelissen                    }
151381b554b04c93aebf3e74116330024272770d2967Marco Nelissen                    case IFD_FORMAT_ULONG: {
151481b554b04c93aebf3e74116330024272770d2967Marco Nelissen                        final String[] values = value.split(",");
151581b554b04c93aebf3e74116330024272770d2967Marco Nelissen                        final long[] longArray = new long[values.length];
151681b554b04c93aebf3e74116330024272770d2967Marco Nelissen                        for (int j = 0; j < values.length; ++j) {
151721b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang                            longArray[j] = Long.parseLong(values[j]);
1518318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        }
1519318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        mAttributes[i].put(tag,
1520318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                                ExifAttribute.createULong(longArray, mExifByteOrder));
1521318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        break;
1522318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                    }
1523318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                    case IFD_FORMAT_URATIONAL: {
1524318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        final String[] values = value.split(",");
1525f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                        final Rational[] rationalArray = new Rational[values.length];
1526318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        for (int j = 0; j < values.length; ++j) {
1527318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                            final String[] numbers = values[j].split("/");
1528318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                            rationalArray[j] = new Rational((long) Double.parseDouble(numbers[0]),
1529318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                                    (long) Double.parseDouble(numbers[1]));
1530318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        }
1531f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                        mAttributes[i].put(tag,
1532318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                                ExifAttribute.createURational(rationalArray, mExifByteOrder));
1533318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        break;
153403b268eac37ca2589bfff0bf58daf79d29cc14f4Andreas Huber                    }
153503b268eac37ca2589bfff0bf58daf79d29cc14f4Andreas Huber                    case IFD_FORMAT_SRATIONAL: {
1536609b815a3131d22da38b2f452faa9f89daad4039Andy Hung                        final String[] values = value.split(",");
1537a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar                        final Rational[] rationalArray = new Rational[values.length];
1538a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar                        for (int j = 0; j < values.length; ++j) {
15391b40f2804a27b695e9e53fb1699b64cb0dd387f9Lajos Molnar                            final String[] numbers = values[j].split("/");
15401b40f2804a27b695e9e53fb1699b64cb0dd387f9Lajos Molnar                            rationalArray[j] = new Rational((long) Double.parseDouble(numbers[0]),
1541a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar                                    (long) Double.parseDouble(numbers[1]));
1542a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar                        }
1543a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar                        mAttributes[i].put(tag,
1544a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar                                ExifAttribute.createSRational(rationalArray, mExifByteOrder));
1545318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        break;
1546d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber                    }
1547d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber                    case IFD_FORMAT_DOUBLE: {
15486d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang                        final String[] values = value.split(",");
1549f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                        final double[] doubleArray = new double[values.length];
15506d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang                        for (int j = 0; j < values.length; ++j) {
1551f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                            doubleArray[j] = Double.parseDouble(values[j]);
1552a63141af8f036bda0b8f7800107ca8a0e0623135Lajos Molnar                        }
15531b40f2804a27b695e9e53fb1699b64cb0dd387f9Lajos Molnar                        mAttributes[i].put(tag,
15541b40f2804a27b695e9e53fb1699b64cb0dd387f9Lajos Molnar                                ExifAttribute.createDouble(doubleArray, mExifByteOrder));
1555f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                        break;
1556318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                    }
1557318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                    default:
1558318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        Log.w(TAG, "Data format isn't one of expected formats: " + dataFormat);
1559318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        continue;
1560d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa                }
1561318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            }
1562f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        }
1563318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    }
1564d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber
1565d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber    /**
156641eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim     * Update the values of the tags in the tag groups if any value for the tag already was stored.
1567298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia     *
1568298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia     * @param tag the name of the tag.
1569298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia     * @param value the value of the tag in a form of {@link ExifAttribute}.
1570298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia     * @return Returns {@code true} if updating is placed.
1571318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber     */
1572318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private boolean updateAttribute(String tag, ExifAttribute value) {
1573318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        boolean updated = false;
1574f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
1575318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            if (mAttributes[i].containsKey(tag)) {
1576318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                mAttributes[i].put(tag, value);
1577318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                updated = true;
1578609b815a3131d22da38b2f452faa9f89daad4039Andy Hung            }
1579318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        }
1580318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        return updated;
1581318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    }
1582318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber
15833fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang    /**
1584d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa     * Remove any values of the specified tag.
1585318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber     *
1586318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber     * @param tag the name of the tag.
158741eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim     */
1588298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia    private void removeAttribute(String tag) {
1589298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia        for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
1590298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia            mAttributes[i].remove(tag);
1591298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia        }
15923fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang    }
15933fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang
15943fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang    /**
15953fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     * This function decides which parser to read the image data according to the given input stream
15963fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     * type and the content of the input stream. In each case, it reads the first three bytes to
15973fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     * determine whether the image data format is JPEG or not.
15983fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     */
15993fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang    private void loadAttributes(@NonNull InputStream in) throws IOException {
16003fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang        try {
16013fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            // Initialize mAttributes.
16023fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            for (int i = 0; i < EXIF_TAGS.length; ++i) {
16033fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                mAttributes[i] = new HashMap();
16043fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            }
16053fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang
1606318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            // Check file type
1607318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            in = new BufferedInputStream(in, SIGNATURE_CHECK_SIZE);
1608318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            mMimeType = getMimeType((BufferedInputStream) in);
1609318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber
161015ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            // Create byte-ordered input stream
161115ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            ByteOrderedDataInputStream inputStream = new ByteOrderedDataInputStream(in);
161215ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar
161315ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            switch (mMimeType) {
161415ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                case IMAGE_TYPE_JPEG: {
161515ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                    getJpegAttributes(inputStream, 0, IFD_TYPE_PRIMARY); // 0 is offset
161615ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                    break;
1617f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                }
1618f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                case IMAGE_TYPE_RAF: {
1619f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                    getRafAttributes(inputStream);
162015ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                    break;
1621f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                }
1622318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                case IMAGE_TYPE_ORF: {
1623f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                    getOrfAttributes(inputStream);
1624f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                    break;
162515ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                }
1626f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                case IMAGE_TYPE_RW2: {
1627f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                    getRw2Attributes(inputStream);
1628f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                    break;
1629318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                }
1630318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                case IMAGE_TYPE_ARW:
1631318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                case IMAGE_TYPE_CR2:
1632318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                case IMAGE_TYPE_DNG:
16333fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                case IMAGE_TYPE_NEF:
16343fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                case IMAGE_TYPE_NRW:
1635d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                case IMAGE_TYPE_PEF:
1636d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                case IMAGE_TYPE_SRW:
1637d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                case IMAGE_TYPE_UNKNOWN: {
1638d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    getRawAttributes(inputStream);
1639d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                    break;
164049b2b4d30a0f74314630a5ea5f0e59697d90443eChong Zhang                }
164149b2b4d30a0f74314630a5ea5f0e59697d90443eChong Zhang                default: {
16423fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                    break;
1643d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang                }
1644d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            }
16453fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            // Set thumbnail image offset and length
16463fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            setThumbnailData(inputStream);
1647d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            mIsSupportedFile = true;
1648d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang        } catch (IOException e) {
16493fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            // Ignore exceptions in order to keep the compatibility with the old versions of
1650d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            // ExifInterface.
1651d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            mIsSupportedFile = false;
1652d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            if (DEBUG) {
16533fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                Log.w(TAG, "Invalid image: ExifInterface got an unsupported image format file"
16543fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                        + "(ExifInterface supports JPEG and some RAW image formats only) "
16553fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                        + "or a corrupted JPEG file to ExifInterface.", e);
16563fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            }
16573fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang        } finally {
1658d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang            addDefaultValuesForCompatibility();
1659d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa
1660318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            if (DEBUG) {
166115ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                printAttributes();
1662318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            }
1663d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar        }
16646d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang    }
1665d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar
1666d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    // Prints out attributes for debugging.
1667d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar    private void printAttributes() {
1668d32d4030c3778b7947c08e9be7d38c229d8d555dLajos Molnar        for (int i = 0; i < mAttributes.length; ++i) {
166941eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim            Log.d(TAG, "The size of tag group[" + i + "]: " + mAttributes[i].size());
1670298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia            for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
1671298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                final ExifAttribute tagValue = (ExifAttribute) entry.getValue();
1672298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                Log.d(TAG, "tagName: " + entry.getKey() + ", tagType: " + tagValue.toString()
1673298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                        + ", tagValue: '" + tagValue.getStringValue(mExifByteOrder) + "'");
1674318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            }
1675318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        }
1676534bb6132a6a664f90b42b3ef81298b42efb3dc2Lajos Molnar    }
1677534bb6132a6a664f90b42b3ef81298b42efb3dc2Lajos Molnar
1678534bb6132a6a664f90b42b3ef81298b42efb3dc2Lajos Molnar    /**
1679534bb6132a6a664f90b42b3ef81298b42efb3dc2Lajos Molnar     * Save the tag data into the original image file. This is expensive because it involves
1680534bb6132a6a664f90b42b3ef81298b42efb3dc2Lajos Molnar     * copying all the data from one file to another and deleting the old file and renaming the
16815fb8b2987ab96ad65dc4b046616607ece16d6fb3Lajos Molnar     * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write
1682054219874873b41f1c815552987c10465c34ba2bLajos Molnar     * and make a single call rather than multiple calls for each attribute.
16835fb8b2987ab96ad65dc4b046616607ece16d6fb3Lajos Molnar     * <p>
16845fb8b2987ab96ad65dc4b046616607ece16d6fb3Lajos Molnar     * This method is only supported for JPEG files.
16855fb8b2987ab96ad65dc4b046616607ece16d6fb3Lajos Molnar     * </p>
16865fb8b2987ab96ad65dc4b046616607ece16d6fb3Lajos Molnar     */
1687ec4ed7d541f48d1d0af8f93cd26ec291ca82061bLajos Molnar    public void saveAttributes() throws IOException {
16885fb8b2987ab96ad65dc4b046616607ece16d6fb3Lajos Molnar        if (!mIsSupportedFile || mMimeType != IMAGE_TYPE_JPEG) {
16895fb8b2987ab96ad65dc4b046616607ece16d6fb3Lajos Molnar            throw new IOException("ExifInterface only supports saving attributes on JPEG formats.");
16905fb8b2987ab96ad65dc4b046616607ece16d6fb3Lajos Molnar        }
16915fb8b2987ab96ad65dc4b046616607ece16d6fb3Lajos Molnar        if (mFilename == null) {
16925fb8b2987ab96ad65dc4b046616607ece16d6fb3Lajos Molnar            throw new IOException(
16935fb8b2987ab96ad65dc4b046616607ece16d6fb3Lajos Molnar                    "ExifInterface does not support saving attributes for the current input.");
16945fb8b2987ab96ad65dc4b046616607ece16d6fb3Lajos Molnar        }
16955fb8b2987ab96ad65dc4b046616607ece16d6fb3Lajos Molnar
1696ef777c71a051f519e0b6998ad663fa5bd291d48aLajos Molnar        // Keep the thumbnail in memory
1697054219874873b41f1c815552987c10465c34ba2bLajos Molnar        mThumbnailBytes = getThumbnail();
1698318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber
169915ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        File tempFile = new File(mFilename + ".tmp");
1700f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        File originalFile = new File(mFilename);
1701f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        if (!originalFile.renameTo(tempFile)) {
1702f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            throw new IOException("Could not rename to " + tempFile.getAbsolutePath());
1703f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        }
1704f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
1705f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        FileInputStream in = null;
1706f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        FileOutputStream out = null;
1707f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        try {
1708f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            // Save the new file.
1709f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            in = new FileInputStream(tempFile);
1710f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            out = new FileOutputStream(mFilename);
1711f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            saveJpegAttributes(in, out);
1712f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        } finally {
1713f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            closeQuietly(in);
1714f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            closeQuietly(out);
1715f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            tempFile.delete();
1716f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        }
1717f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
1718f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        // Discard the thumbnail in memory
1719f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        mThumbnailBytes = null;
1720f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    }
1721f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
1722f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /**
1723f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * Returns true if the image file has a thumbnail.
1724f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     */
1725f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    public boolean hasThumbnail() {
172615ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        return mHasThumbnail;
172715ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar    }
172815ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar
172915ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar    /**
173015ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar     * Returns the JPEG compressed thumbnail inside the image file, or {@code null} if there is no
173115ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar     * JPEG compressed thumbnail.
173215ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar     * The returned data can be decoded using
173315ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar     * {@link android.graphics.BitmapFactory#decodeByteArray(byte[],int,int)}
173415ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar     */
173515ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar    public byte[] getThumbnail() {
173615ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) {
173715ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            return getThumbnailBytes();
173815ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        }
173915ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        return null;
174015ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar    }
174115ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar
174215ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar    /**
174315ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar     * Returns the thumbnail bytes inside the image file, regardless of the compression type of the
174415ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar     * thumbnail image.
174515ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar     */
174615ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar    public byte[] getThumbnailBytes() {
174715ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        if (!mHasThumbnail) {
174815ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            return null;
174915ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        }
175015ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        if (mThumbnailBytes != null) {
175115ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            return mThumbnailBytes;
175215ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        }
175315ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar
175415ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        // Read the thumbnail.
175515ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        InputStream in = null;
175615ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        try {
175715ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            if (mAssetInputStream != null) {
175815ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                in = mAssetInputStream;
175915ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                if (in.markSupported()) {
176015ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                    in.reset();
176115ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                } else {
176215ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                    Log.d(TAG, "Cannot read thumbnail from inputstream without mark/reset support");
176315ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                    return null;
176415ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                }
176515ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            } else if (mFilename != null) {
176615ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                in = new FileInputStream(mFilename);
176715ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            }
176815ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            if (in == null) {
1769f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                // Should not be reached this.
177015ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                throw new FileNotFoundException();
177115ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            }
1772f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            if (in.skip(mThumbnailOffset) != mThumbnailOffset) {
1773f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                throw new IOException("Corrupted image");
1774f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            }
177515ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            byte[] buffer = new byte[mThumbnailLength];
177615ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            if (in.read(buffer) != mThumbnailLength) {
177715ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                throw new IOException("Corrupted image");
177815ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            }
177915ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            mThumbnailBytes = buffer;
178015ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            return buffer;
178115ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        } catch (IOException e) {
1782f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            // Couldn't get a thumbnail image.
1783f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            Log.d(TAG, "Encountered exception while getting thumbnail", e);
1784f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        } finally {
1785f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            closeQuietly(in);
1786f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        }
1787f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        return null;
1788f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    }
1789f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
1790f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /**
179115ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar     * Creates and returns a Bitmap object of the thumbnail image based on the byte array and the
1792f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * thumbnail compression value, or {@code null} if the compression type is unsupported.
1793f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     */
1794318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public Bitmap getThumbnailBitmap() {
179515ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        if (!mHasThumbnail) {
1796f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            return null;
1797f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        } else if (mThumbnailBytes == null) {
1798f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            mThumbnailBytes = getThumbnailBytes();
1799f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        }
1800f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
1801f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) {
1802f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            return BitmapFactory.decodeByteArray(mThumbnailBytes, 0, mThumbnailLength);
1803f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        } else if (mThumbnailCompression == DATA_UNCOMPRESSED) {
1804f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            int[] rgbValues = new int[mThumbnailBytes.length / 3];
1805318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            byte alpha = (byte) 0xff000000;
1806318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            for (int i = 0; i < rgbValues.length; i++) {
1807318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                rgbValues[i] = alpha + (mThumbnailBytes[3 * i] << 16)
1808318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        + (mThumbnailBytes[3 * i + 1] << 8) + mThumbnailBytes[3 * i + 2];
1809f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden            }
1810d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang
1811d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa            ExifAttribute imageLengthAttribute =
18123604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang                    (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_LENGTH);
18136cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang            ExifAttribute imageWidthAttribute =
1814298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                    (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_WIDTH);
1815298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia            if (imageLengthAttribute != null && imageWidthAttribute != null) {
1816298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                int imageLength = imageLengthAttribute.getIntValue(mExifByteOrder);
1817298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                int imageWidth = imageWidthAttribute.getIntValue(mExifByteOrder);
1818298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                return Bitmap.createBitmap(
1819e63de4c60116261b90bc93b608ccd4bd182f3484Lajos Molnar                        rgbValues, imageWidth, imageLength, Bitmap.Config.ARGB_8888);
182021b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            }
1821054219874873b41f1c815552987c10465c34ba2bLajos Molnar        }
182215ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        return null;
182315ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar    }
1824054219874873b41f1c815552987c10465c34ba2bLajos Molnar
1825054219874873b41f1c815552987c10465c34ba2bLajos Molnar    /**
1826f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden     * Returns true if thumbnail image is JPEG Compressed, or false if either thumbnail image does
18273604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     * not exist or thumbnail image is uncompressed.
18286d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang     */
1829054219874873b41f1c815552987c10465c34ba2bLajos Molnar    public boolean isThumbnailCompressed() {
1830e63de4c60116261b90bc93b608ccd4bd182f3484Lajos Molnar        return mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED;
1831e63de4c60116261b90bc93b608ccd4bd182f3484Lajos Molnar    }
1832e63de4c60116261b90bc93b608ccd4bd182f3484Lajos Molnar
1833e63de4c60116261b90bc93b608ccd4bd182f3484Lajos Molnar    /**
1834e63de4c60116261b90bc93b608ccd4bd182f3484Lajos Molnar     * Returns the offset and length of thumbnail inside the image file, or
1835e63de4c60116261b90bc93b608ccd4bd182f3484Lajos Molnar     * {@code null} if there is no thumbnail.
1836e63de4c60116261b90bc93b608ccd4bd182f3484Lajos Molnar     *
18373604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     * @return two-element array, the offset in the first value, and length in
18383604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     *         the second, or {@code null} if no thumbnail was found.
18393604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     */
18403604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang    public long[] getThumbnailRange() {
18413604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        if (!mHasThumbnail) {
18423604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang            return null;
18433604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        }
18443604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang
18453604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        long[] range = new long[2];
18463604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        range[0] = mThumbnailOffset;
18473604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        range[1] = mThumbnailLength;
18483604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang
18493604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        return range;
18503604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang    }
18513604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang
18523604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang    /**
18533604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     * Stores the latitude and longitude value in a float array. The first element is the latitude,
18543604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     * and the second element is the longitude. Returns false if the Exif tags are not available.
18553604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     *
18563604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     * @deprecated Use {@link #getLatLong()} instead.
18573604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     */
18583604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang    @Deprecated
18593604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang    public boolean getLatLong(float output[]) {
18603604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        double[] latLong = getLatLong();
18613604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        if (latLong == null) {
18623604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang            return false;
18633604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        }
18643604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang
18653604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        output[0] = (float) latLong[0];
18663604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        output[1] = (float) latLong[1];
18673604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        return true;
18683604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang    }
18693604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang
18703604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang    /**
18713604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     * Gets the latitude and longitude values.
18723604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     * <p>
18733604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     * If there are valid latitude and longitude values in the image, this method returns a double
18743604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     * array where the first element is the latitude and the second element is the longitude.
18753604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     * Otherwise, it returns null.
18763604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang     */
18773604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang    public double[] getLatLong() {
18783604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        String latValue = getAttribute(TAG_GPS_LATITUDE);
18793604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        String latRef = getAttribute(TAG_GPS_LATITUDE_REF);
1880f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden        String lngValue = getAttribute(TAG_GPS_LONGITUDE);
1881f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden        String lngRef = getAttribute(TAG_GPS_LONGITUDE_REF);
1882d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang
1883d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa        if (latValue != null && latRef != null && lngValue != null && lngRef != null) {
18843fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            try {
18853fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                double latitude = convertRationalLatLonToDouble(latValue, latRef);
18863fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                double longitude = convertRationalLatLonToDouble(lngValue, lngRef);
18873fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                return new double[] {latitude, longitude};
18883fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            } catch (IllegalArgumentException e) {
18893fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                Log.w(TAG, "Latitude/longitude values are not parseable. " +
18903fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                        String.format("latValue=%s, latRef=%s, lngValue=%s, lngRef=%s",
18913fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang                                latValue, latRef, lngValue, lngRef));
18923fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang            }
18933fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang        }
1894d02c08671d666e12a94bcadd943787a9ab2ceea8Chong Zhang        return null;
18953fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang    }
18963fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang
18973fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang    /**
18983fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     * Sets the latitude and longitude values.
18993fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     *
19003fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     * @param latitude the decimal value of latitude. Must be a valid double value between -90.0 and
19013fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     *                 90.0.
19023fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     * @param longitude the decimal value of longitude. Must be a valid double value between -180.0
19033fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     *                  and 180.0.
19043fd200feb657c157125e45e30c2a7262e3c0244dChong Zhang     * @throws IllegalArgumentException If {@code latitude} or {@code longitude} is outside the
19056d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang     *                                  specified range.
1906331207d51a620bf018081950da4b20133014657fChong Zhang     */
1907318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public void setLatLong(double latitude, double longitude) {
19083604cb1a5548694393c7b7a87191eb517bebaa47Chong Zhang        if (latitude < -90.0 || latitude > 90.0 || Double.isNaN(latitude)) {
19096d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang            throw new IllegalArgumentException("Latitude value " + latitude + " is not valid.");
1910f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        }
1911f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        if (longitude < -180.0 || longitude > 180.0 || Double.isNaN(longitude)) {
19126d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang            throw new IllegalArgumentException("Longitude value " + longitude + " is not valid.");
19136d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang        }
19146d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang        setAttribute(TAG_GPS_LATITUDE_REF, latitude >= 0 ? "N" : "S");
19156d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang        setAttribute(TAG_GPS_LATITUDE, convertDecimalDegree(Math.abs(latitude)));
19166d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang        setAttribute(TAG_GPS_LONGITUDE_REF, longitude >= 0 ? "E" : "W");
19176d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang        setAttribute(TAG_GPS_LONGITUDE, convertDecimalDegree(Math.abs(longitude)));
19186d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang    }
19196d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang
19206d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang    /**
19216d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang     * Return the altitude in meters. If the exif tag does not exist, return
19226d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang     * <var>defaultValue</var>.
19236d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang     *
19246d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang     * @param defaultValue the value to return if the tag is not available.
19256d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang     */
19266d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang    public double getAltitude(double defaultValue) {
19276d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang        double altitude = getAttributeDouble(TAG_GPS_ALTITUDE, -1);
1928dd81af7ef969981748f35ec839869d34ed0cc768Lajos Molnar        int ref = getAttributeInt(TAG_GPS_ALTITUDE_REF, -1);
1929dd81af7ef969981748f35ec839869d34ed0cc768Lajos Molnar
1930dd81af7ef969981748f35ec839869d34ed0cc768Lajos Molnar        if (altitude >= 0 && ref >= 0) {
19316d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang            return (altitude * ((ref == 1) ? -1 : 1));
19326d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang        } else {
19336d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang            return defaultValue;
1934e40cda70eec141fa05cbcca1de420fdb22b98be6Andreas Huber        }
19356d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang    }
19366d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang
1937e40cda70eec141fa05cbcca1de420fdb22b98be6Andreas Huber    /**
19386d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang     * Returns number of milliseconds since Jan. 1, 1970, midnight local time.
1939e40cda70eec141fa05cbcca1de420fdb22b98be6Andreas Huber     * Returns -1 if the date time information if not available.
1940e40cda70eec141fa05cbcca1de420fdb22b98be6Andreas Huber     * @hide
1941d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang     */
1942d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    public long getDateTime() {
194321b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang        String dateTimeString = getAttribute(TAG_DATETIME);
194421b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang        if (dateTimeString == null
194521b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang                || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1;
194621b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang
194721b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang        ParsePosition pos = new ParsePosition(0);
194821b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang        try {
194921b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            // The exif field is in local time. Parsing it as if it is UTC will yield time
195021b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            // since 1/1/1970 local time
195121b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            Date datetime = sFormatter.parse(dateTimeString, pos);
195221b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            if (datetime == null) return -1;
195321b46588d022d0c22d7c7c08e919d7a9c5cd76e3Chong Zhang            long msecs = datetime.getTime();
1954d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang
1955d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang            String subSecs = getAttribute(TAG_SUBSEC_TIME);
195626a48f304a8754d655e554178ffb6d7ba4c5aac3Lajos Molnar            if (subSecs != null) {
1957318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                try {
1958318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                    long sub = Long.parseLong(subSecs);
195941eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim                    while (sub > 1000) {
1960298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                        sub /= 10;
1961298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                    }
196241eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim                    msecs += sub;
1963298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                } catch (NumberFormatException e) {
1964318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                    // Ignored
1965f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                }
1966f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            }
1967f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            return msecs;
1968f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        } catch (IllegalArgumentException e) {
1969f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            return -1;
197015ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        }
197115ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar    }
1972f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
1973f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /**
1974f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * Returns number of milliseconds since Jan. 1, 1970, midnight UTC.
1975f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * Returns -1 if the date time information if not available.
1976318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber     * @hide
1977318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber     */
1978318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    public long getGpsDateTime() {
1979ec4ed7d541f48d1d0af8f93cd26ec291ca82061bLajos Molnar        String date = getAttribute(TAG_GPS_DATESTAMP);
1980ec4ed7d541f48d1d0af8f93cd26ec291ca82061bLajos Molnar        String time = getAttribute(TAG_GPS_TIMESTAMP);
1981ec4ed7d541f48d1d0af8f93cd26ec291ca82061bLajos Molnar        if (date == null || time == null
1982ec4ed7d541f48d1d0af8f93cd26ec291ca82061bLajos Molnar                || (!sNonZeroTimePattern.matcher(date).matches()
1983ec4ed7d541f48d1d0af8f93cd26ec291ca82061bLajos Molnar                && !sNonZeroTimePattern.matcher(time).matches())) {
1984ef777c71a051f519e0b6998ad663fa5bd291d48aLajos Molnar            return -1;
19851b4ca5cebd7f42a8f8842e45bfabe19001e9a435Andreas Huber        }
19866d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang
19876d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang        String dateTimeString = date + ' ' + time;
198894ee4b708acfa941581160b267afb79192b1d816Chong Zhang
1989f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        ParsePosition pos = new ParsePosition(0);
199041eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim        try {
199141eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim            Date datetime = sFormatter.parse(dateTimeString, pos);
199241eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim            if (datetime == null) return -1;
199341eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim            return datetime.getTime();
1994f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        } catch (IllegalArgumentException e) {
1995f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            return -1;
1996f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        }
1997f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    }
1998f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
1999f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private static double convertRationalLatLonToDouble(String rationalString, String ref) {
200015ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar        try {
2001f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            String [] parts = rationalString.split(",");
2002f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
20036d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang            String [] pair;
20046d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang            pair = parts[0].split("/");
200594ee4b708acfa941581160b267afb79192b1d816Chong Zhang            double degrees = Double.parseDouble(pair[0].trim())
20066d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang                    / Double.parseDouble(pair[1].trim());
20071b4ca5cebd7f42a8f8842e45bfabe19001e9a435Andreas Huber
20081b4ca5cebd7f42a8f8842e45bfabe19001e9a435Andreas Huber            pair = parts[1].split("/");
20091b4ca5cebd7f42a8f8842e45bfabe19001e9a435Andreas Huber            double minutes = Double.parseDouble(pair[0].trim())
20106d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang                    / Double.parseDouble(pair[1].trim());
20116d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang
201226a48f304a8754d655e554178ffb6d7ba4c5aac3Lajos Molnar            pair = parts[2].split("/");
201326a48f304a8754d655e554178ffb6d7ba4c5aac3Lajos Molnar            double seconds = Double.parseDouble(pair[0].trim())
20146cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang                    / Double.parseDouble(pair[1].trim());
20156cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang
20166cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang            double result = degrees + (minutes / 60.0) + (seconds / 3600.0);
201726a48f304a8754d655e554178ffb6d7ba4c5aac3Lajos Molnar            if ((ref.equals("S") || ref.equals("W"))) {
201826a48f304a8754d655e554178ffb6d7ba4c5aac3Lajos Molnar                return -result;
201926a48f304a8754d655e554178ffb6d7ba4c5aac3Lajos Molnar            } else if (ref.equals("N") || ref.equals("E")) {
202026a48f304a8754d655e554178ffb6d7ba4c5aac3Lajos Molnar                return result;
202126a48f304a8754d655e554178ffb6d7ba4c5aac3Lajos Molnar            } else {
20226cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang                // Not valid
20236cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang                throw new IllegalArgumentException();
20246cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang            }
20256cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
20266cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang            // Not valid
20276cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang            throw new IllegalArgumentException();
20286cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        }
20296cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang    }
20306cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang
20316cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang    private String convertDecimalDegree(double decimalDegree) {
20326cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        long degrees = (long) decimalDegree;
20336cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        long minutes = (long) ((decimalDegree - degrees) * 60.0);
20346cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        long seconds = Math.round((decimalDegree - degrees - minutes / 60.0) * 3600.0 * 1e7);
20356cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        return degrees + "/1," + minutes + "/1," + seconds + "/10000000";
20366cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang    }
20376cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang
20386cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang    // Checks the type of image file
20396cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang    private int getMimeType(BufferedInputStream in) throws IOException {
20406cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        in.mark(SIGNATURE_CHECK_SIZE);
20416cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        byte[] signatureCheckBytes = new byte[SIGNATURE_CHECK_SIZE];
20426cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        if (in.read(signatureCheckBytes) != SIGNATURE_CHECK_SIZE) {
20436cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang            throw new EOFException();
20446cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        }
20456cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        in.reset();
20466cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        if (isJpegFormat(signatureCheckBytes)) {
20476cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang            return IMAGE_TYPE_JPEG;
20486cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        } else if (isRafFormat(signatureCheckBytes)) {
20496cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang            return IMAGE_TYPE_RAF;
20506cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        } else if (isOrfFormat(signatureCheckBytes)) {
20516cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang            return IMAGE_TYPE_ORF;
20526cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        } else if (isRw2Format(signatureCheckBytes)) {
20536cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang            return IMAGE_TYPE_RW2;
20546cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        }
20556cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        // Certain file formats (PEF) are identified in readImageFileDirectory()
20566cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        return IMAGE_TYPE_UNKNOWN;
20576cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang    }
20586cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang
20596cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang    /**
20606cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang     * This method looks at the first 3 bytes to determine if this file is a JPEG file.
20616cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang     * See http://www.media.mit.edu/pia/Research/deepview/exif.html, "JPEG format and Marker"
20626cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang     */
20636cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang    private static boolean isJpegFormat(byte[] signatureCheckBytes) throws IOException {
20646cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        for (int i = 0; i < JPEG_SIGNATURE.length; i++) {
20656cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang            if (signatureCheckBytes[i] != JPEG_SIGNATURE[i]) {
20666cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang                return false;
20676cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang            }
20686cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        }
20696cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        return true;
20706cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang    }
20716cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang
20726cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang    /**
20736cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang     * This method looks at the first 15 bytes to determine if this file is a RAF file.
20746cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang     * There is no official specification for RAF files from Fuji, but there is an online archive of
20756cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang     * image file specifications:
20766cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang     * http://fileformats.archiveteam.org/wiki/Fujifilm_RAF
20776cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang     */
20786cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang    private boolean isRafFormat(byte[] signatureCheckBytes) throws IOException {
20796cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        byte[] rafSignatureBytes = RAF_SIGNATURE.getBytes(Charset.defaultCharset());
20806cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        for (int i = 0; i < rafSignatureBytes.length; i++) {
20816cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang            if (signatureCheckBytes[i] != rafSignatureBytes[i]) {
20826cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang                return false;
20836cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang            }
20846cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        }
20856cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang        return true;
20866cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang    }
20876cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang
20886cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang    /**
20896cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang     * ORF has a similar structure to TIFF but it contains a different signature at the TIFF Header.
20906cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang     * This method looks at the 2 bytes following the Byte Order bytes to determine if this file is
20916cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang     * an ORF file.
20926cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang     * There is no official specification for ORF files from Olympus, but there is an online archive
20936cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang     * of image file specifications:
20946cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang     * http://fileformats.archiveteam.org/wiki/Olympus_ORF
20956cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang     */
20966cf9a1238986880536de705255f7c2c91c1ba719Chong Zhang    private boolean isOrfFormat(byte[] signatureCheckBytes) throws IOException {
209726a48f304a8754d655e554178ffb6d7ba4c5aac3Lajos Molnar        ByteOrderedDataInputStream signatureInputStream =
209826a48f304a8754d655e554178ffb6d7ba4c5aac3Lajos Molnar                new ByteOrderedDataInputStream(signatureCheckBytes);
209926a48f304a8754d655e554178ffb6d7ba4c5aac3Lajos Molnar        // Read byte order
210026a48f304a8754d655e554178ffb6d7ba4c5aac3Lajos Molnar        mExifByteOrder = readByteOrder(signatureInputStream);
210126a48f304a8754d655e554178ffb6d7ba4c5aac3Lajos Molnar        // Set byte order
210226a48f304a8754d655e554178ffb6d7ba4c5aac3Lajos Molnar        signatureInputStream.setByteOrder(mExifByteOrder);
21031b4ca5cebd7f42a8f8842e45bfabe19001e9a435Andreas Huber
2104318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        short orfSignature = signatureInputStream.readShort();
2105318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        signatureInputStream.close();
2106984a54322f7c70bc75e862d91bdd975814872affLajos Molnar        return orfSignature == ORF_SIGNATURE_1 || orfSignature == ORF_SIGNATURE_2;
2107984a54322f7c70bc75e862d91bdd975814872affLajos Molnar    }
2108984a54322f7c70bc75e862d91bdd975814872affLajos Molnar
2109318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    /**
2110318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber     * RW2 is TIFF-based, but stores 0x55 signature byte instead of 0x42 at the header
2111d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang     * See http://lclevy.free.fr/raw/
211229357bc2c0dd7c43ad3bd0c8e3efa4e6fd9bfd47Steve Block     */
2113318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private boolean isRw2Format(byte[] signatureCheckBytes) throws IOException {
2114318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        ByteOrderedDataInputStream signatureInputStream =
2115d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                new ByteOrderedDataInputStream(signatureCheckBytes);
2116318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        // Read byte order
2117318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        mExifByteOrder = readByteOrder(signatureInputStream);
2118f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden        // Set byte order
2119f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden        signatureInputStream.setByteOrder(mExifByteOrder);
2120f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden
2121f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden        short signatureByte = signatureInputStream.readShort();
2122f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        signatureInputStream.close();
2123f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        return signatureByte == RW2_SIGNATURE;
2124f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    }
2125f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
2126f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    /**
2127f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * Loads EXIF attributes from a JPEG input stream.
2128f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     *
2129f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * @param in The input stream that starts with the JPEG data.
2130f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * @param jpegOffset The offset value in input stream for JPEG data.
2131f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * @param imageType The image type from which to retrieve metadata. Use IFD_TYPE_PRIMARY for
2132f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     *                   primary image, IFD_TYPE_PREVIEW for preview image, and
2133f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     *                   IFD_TYPE_THUMBNAIL for thumbnail image.
2134f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * @throws IOException If the data contains invalid JPEG markers, offsets, or length values.
2135f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     */
2136f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar    private void getJpegAttributes(ByteOrderedDataInputStream in, int jpegOffset, int imageType)
2137f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            throws IOException {
2138f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        // See JPEG File Interchange Format Specification, "JFIF Specification"
2139f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        if (DEBUG) {
2140f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            Log.d(TAG, "getJpegAttributes starting with: " + in);
2141f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        }
2142f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
2143f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        // JPEG uses Big Endian by default. See https://people.cs.umass.edu/~verts/cs32/endian.html
2144f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        in.setByteOrder(ByteOrder.BIG_ENDIAN);
2145f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
2146f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        // Skip to JPEG data
2147f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        in.seek(jpegOffset);
2148f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        int bytesRead = jpegOffset;
2149f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar
2150f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        byte marker;
2151f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        if ((marker = in.readByte()) != MARKER) {
2152f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
2153f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        }
2154f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        ++bytesRead;
2155f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        if (in.readByte() != MARKER_SOI) {
2156f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
2157f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        }
2158f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        ++bytesRead;
21596d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang        while (true) {
2160f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden            marker = in.readByte();
2161e40cda70eec141fa05cbcca1de420fdb22b98be6Andreas Huber            if (marker != MARKER) {
2162e40cda70eec141fa05cbcca1de420fdb22b98be6Andreas Huber                throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff));
2163e40cda70eec141fa05cbcca1de420fdb22b98be6Andreas Huber            }
2164e40cda70eec141fa05cbcca1de420fdb22b98be6Andreas Huber            ++bytesRead;
21656d332d2cdf6e62c2c20ebff220868fe9e3ed7f44Chong Zhang            marker = in.readByte();
2166f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden            if (DEBUG) {
21676392ae13cf6e15c706fce45396ec1e79af6171a9Lajos Molnar                Log.d(TAG, "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff));
21686392ae13cf6e15c706fce45396ec1e79af6171a9Lajos Molnar            }
21696392ae13cf6e15c706fce45396ec1e79af6171a9Lajos Molnar            ++bytesRead;
21706392ae13cf6e15c706fce45396ec1e79af6171a9Lajos Molnar
21716392ae13cf6e15c706fce45396ec1e79af6171a9Lajos Molnar            // EOI indicates the end of an image and in case of SOS, JPEG image stream starts and
21726392ae13cf6e15c706fce45396ec1e79af6171a9Lajos Molnar            // the image data will terminate right after.
21736392ae13cf6e15c706fce45396ec1e79af6171a9Lajos Molnar            if (marker == MARKER_EOI || marker == MARKER_SOS) {
2174f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden                break;
2175f779bb50d9746d9526541c3e6dcdf619cac941b7Andy McFadden            }
2176318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            int length = in.readUnsignedShort() - 2;
2177318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            bytesRead += 2;
217884333e0475bc911adc16417f4ca327c975cf6c36Andreas Huber            if (DEBUG) {
2179318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                Log.d(TAG, "JPEG segment: " + Integer.toHexString(marker & 0xff) + " (length: "
2180318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        + (length + 2) + ")");
2181318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            }
2182318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber            if (length < 0) {
2183318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                throw new IOException("Invalid length");
2184298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia            }
2185298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia            switch (marker) {
2186298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                case MARKER_APP1: {
2187298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                    if (DEBUG) {
2188318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        Log.d(TAG, "MARKER_APP1");
2189134ee6a324c35f39e3576172e4eae4c6de6eb9dcAndreas Huber                    }
2190134ee6a324c35f39e3576172e4eae4c6de6eb9dcAndreas Huber                    if (length < 6) {
2191134ee6a324c35f39e3576172e4eae4c6de6eb9dcAndreas Huber                        // Skip if it's not an EXIF APP1 segment.
2192d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                        break;
2193d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    }
2194d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    byte[] identifier = new byte[6];
2195d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    if (in.read(identifier) != 6) {
2196d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                        throw new IOException("Invalid exif");
2197d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    }
2198d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    bytesRead += 6;
2199d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    length -= 6;
2200d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
2201d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                        // Skip if it's not an EXIF APP1 segment.
2202d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                        break;
2203d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    }
2204d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    if (length <= 0) {
2205d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                        throw new IOException("Invalid exif");
2206d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    }
2207d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    if (DEBUG) {
2208d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                        Log.d(TAG, "readExifSegment with a byte array (length: " + length + ")");
220936f05c7f071e5c877e65943fe181d2b48c7bd6b0Lajos Molnar                    }
221036f05c7f071e5c877e65943fe181d2b48c7bd6b0Lajos Molnar                    // Save offset values for createJpegThumbnailBitmap() function
2211d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    mExifOffset = bytesRead;
2212d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang
2213d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    byte[] bytes = new byte[length];
2214d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    if (in.read(bytes) != length) {
2215d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                        throw new IOException("Invalid exif");
2216d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    }
2217d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    bytesRead += length;
2218d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    length = 0;
2219d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang
2220d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    readExifSegment(bytes, imageType);
2221d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                    break;
2222d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                }
2223d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang
2224d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                case MARKER_COM: {
2225318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                    byte[] bytes = new byte[length];
2226318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                    if (in.read(bytes) != length) {
2227318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        throw new IOException("Invalid exif");
2228318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                    }
222984333e0475bc911adc16417f4ca327c975cf6c36Andreas Huber                    length = 0;
2230318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                    if (getAttribute(TAG_USER_COMMENT) == null) {
2231318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        mAttributes[IFD_TYPE_EXIF].put(TAG_USER_COMMENT, ExifAttribute.createString(
2232298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                                new String(bytes, ASCII)));
2233298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                    }
2234298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                    break;
2235298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                }
2236318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber
2237134ee6a324c35f39e3576172e4eae4c6de6eb9dcAndreas Huber                case MARKER_SOF0:
2238134ee6a324c35f39e3576172e4eae4c6de6eb9dcAndreas Huber                case MARKER_SOF1:
2239134ee6a324c35f39e3576172e4eae4c6de6eb9dcAndreas Huber                case MARKER_SOF2:
224015ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar                case MARKER_SOF3:
2241d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                case MARKER_SOF5:
2242d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                case MARKER_SOF6:
2243d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                case MARKER_SOF7:
2244d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                case MARKER_SOF9:
2245d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                case MARKER_SOF10:
2246d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                case MARKER_SOF11:
2247d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                case MARKER_SOF13:
2248d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                case MARKER_SOF14:
2249318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                case MARKER_SOF15: {
2250318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                    if (in.skipBytes(1) != 1) {
2251318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                        throw new IOException("Invalid SOFx");
2252318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                    }
225384333e0475bc911adc16417f4ca327c975cf6c36Andreas Huber                    mAttributes[imageType].put(TAG_IMAGE_LENGTH, ExifAttribute.createULong(
2254318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                            in.readUnsignedShort(), mExifByteOrder));
2255318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber                    mAttributes[imageType].put(TAG_IMAGE_WIDTH, ExifAttribute.createULong(
2256298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                            in.readUnsignedShort(), mExifByteOrder));
2257298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                    length -= 5;
2258298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                    break;
2259298f3c866c5cdb987d15e1e4fc12692fc971ad63Wei Jia                }
2260318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber
2261134ee6a324c35f39e3576172e4eae4c6de6eb9dcAndreas Huber                default: {
2262134ee6a324c35f39e3576172e4eae4c6de6eb9dcAndreas Huber                    break;
2263134ee6a324c35f39e3576172e4eae4c6de6eb9dcAndreas Huber                }
226415ab4996019387f27a48b81cb4774c21502bc0e5Lajos Molnar            }
2265d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang            if (length < 0) {
2266d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                throw new IOException("Invalid length");
2267d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang            }
2268d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang            if (in.skipBytes(length) != length) {
2269d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang                throw new IOException("Invalid JPEG segment");
2270d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang            }
2271d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang            bytesRead += length;
2272d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang        }
2273d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang        // Restore original byte order
2274d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang        in.setByteOrder(mExifByteOrder);
2275d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang    }
2276d59b97223424a3974d2ac31cff998d02eecf2eedChong Zhang
2277318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber    private void getRawAttributes(ByteOrderedDataInputStream in) throws IOException {
2278318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber        // Parse TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
2279d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa        parseTiffHeaders(in, in.available());
2280d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber
2281d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber        // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6.
2282d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber        readImageFileDirectory(in, IFD_TYPE_PRIMARY);
2283d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber
2284f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        // Update ImageLength/Width tags for all image data.
2285f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        updateImageSizeValues(in, IFD_TYPE_PRIMARY);
2286f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        updateImageSizeValues(in, IFD_TYPE_PREVIEW);
2287f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar        updateImageSizeValues(in, IFD_TYPE_THUMBNAIL);
2288d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber
2289d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber        // Check if each image data is in valid position.
2290d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber        validateImages(in);
2291d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa
2292d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber        if (mMimeType == IMAGE_TYPE_PEF) {
2293d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber            // PEF files contain a MakerNote data, which contains the data for ColorSpace tag.
2294f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            // See http://lclevy.free.fr/raw/ and piex.cc PefGetPreviewData()
2295d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber            ExifAttribute makerNoteAttribute =
2296f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                    (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE);
2297f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar            if (makerNoteAttribute != null) {
2298f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                // Create an ordered DataInputStream for MakerNote
2299f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                ByteOrderedDataInputStream makerNoteDataInputStream =
2300f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                        new ByteOrderedDataInputStream(makerNoteAttribute.bytes);
2301d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber                makerNoteDataInputStream.setByteOrder(mExifByteOrder);
2302d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber
2303d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber                // Seek to MakerNote data
2304f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar                makerNoteDataInputStream.seek(PEF_MAKER_NOTE_SKIP_SIZE);
2305d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber
2306d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber                // Read IFD data from MakerNote
2307d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber                readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_PEF);
2308d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber
2309d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber                // Update ColorSpace tag
2310b92add32c22656dedfb82d26ccc168c43c92b8ebChad Brubaker                ExifAttribute colorSpaceAttribute =
2311b92add32c22656dedfb82d26ccc168c43c92b8ebChad Brubaker                        (ExifAttribute) mAttributes[IFD_TYPE_PEF].get(TAG_COLOR_SPACE);
2312d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber                if (colorSpaceAttribute != null) {
2313d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber                    mAttributes[IFD_TYPE_EXIF].put(TAG_COLOR_SPACE, colorSpaceAttribute);
2314d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber                }
2315d6703ab22c85b43fdb2deb78a37e51465c902a5fAndreas Huber            }
2316d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa        }
2317609b815a3131d22da38b2f452faa9f89daad4039Andy Hung    }
2318609b815a3131d22da38b2f452faa9f89daad4039Andy Hung
2319609b815a3131d22da38b2f452faa9f89daad4039Andy Hung    /**
2320609b815a3131d22da38b2f452faa9f89daad4039Andy Hung     * RAF files contains a JPEG and a CFA data.
2321d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa     * The JPEG contains two images, a preview and a thumbnail, while the CFA contains a RAW image.
2322609b815a3131d22da38b2f452faa9f89daad4039Andy Hung     * This method looks at the first 160 bytes of a RAF file to retrieve the offset and length
2323609b815a3131d22da38b2f452faa9f89daad4039Andy Hung     * values for the JPEG and CFA data.
2324f296e2b262d2a8f7c570eaed454a28cca99eb976Lajos Molnar     * Using that data, it parses the JPEG data to retrieve the preview and thumbnail image data,
2325609b815a3131d22da38b2f452faa9f89daad4039Andy Hung     * then parses the CFA metadata to retrieve the primary image length/width values.
2326d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa     * For data format details, see http://fileformats.archiveteam.org/wiki/Fujifilm_RAF
2327609b815a3131d22da38b2f452faa9f89daad4039Andy Hung     */
2328609b815a3131d22da38b2f452faa9f89daad4039Andy Hung    private void getRafAttributes(ByteOrderedDataInputStream in) throws IOException {
2329609b815a3131d22da38b2f452faa9f89daad4039Andy Hung        // Retrieve offset & length values
2330609b815a3131d22da38b2f452faa9f89daad4039Andy Hung        in.skipBytes(RAF_OFFSET_TO_JPEG_IMAGE_OFFSET);
2331609b815a3131d22da38b2f452faa9f89daad4039Andy Hung        byte[] jpegOffsetBytes = new byte[4];
2332609b815a3131d22da38b2f452faa9f89daad4039Andy Hung        byte[] cfaHeaderOffsetBytes = new byte[4];
233341eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim        in.read(jpegOffsetBytes);
2334d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa        // Skip JPEG length value since it is not needed
2335609b815a3131d22da38b2f452faa9f89daad4039Andy Hung        in.skipBytes(RAF_JPEG_LENGTH_VALUE_SIZE);
2336609b815a3131d22da38b2f452faa9f89daad4039Andy Hung        in.read(cfaHeaderOffsetBytes);
2337609b815a3131d22da38b2f452faa9f89daad4039Andy Hung        int rafJpegOffset = ByteBuffer.wrap(jpegOffsetBytes).getInt();
2338609b815a3131d22da38b2f452faa9f89daad4039Andy Hung        int rafCfaHeaderOffset = ByteBuffer.wrap(cfaHeaderOffsetBytes).getInt();
23395419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang
23405419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang        // Retrieve JPEG image metadata
23415419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang        getJpegAttributes(in, rafJpegOffset, IFD_TYPE_PREVIEW);
23425419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang
23435419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang        // Skip to CFA header offset.
234441eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim        in.seek(rafCfaHeaderOffset);
234541eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim
234641eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim        // Retrieve primary image length/width values, if TAG_RAF_IMAGE_SIZE exists
234741eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim        in.setByteOrder(ByteOrder.BIG_ENDIAN);
234841eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim        int numberOfDirectoryEntry = in.readInt();
234941eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim        if (DEBUG) {
235041eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim            Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry);
235141eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim        }
235241eca4f0ec697529fe8a47f34f43f5ba98a50162Wonsik Kim        // CFA stores some metadata about the RAW image. Since CFA uses proprietary tags, can only
2353609b815a3131d22da38b2f452faa9f89daad4039Andy Hung        // find and retrieve image size information tags, while skipping others.
2354609b815a3131d22da38b2f452faa9f89daad4039Andy Hung        // See piex.cc RafGetDimension()
2355d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa        for (int i = 0; i < numberOfDirectoryEntry; ++i) {
2356609b815a3131d22da38b2f452faa9f89daad4039Andy Hung            int tagNumber = in.readUnsignedShort();
2357609b815a3131d22da38b2f452faa9f89daad4039Andy Hung            int numberOfBytes = in.readUnsignedShort();
2358609b815a3131d22da38b2f452faa9f89daad4039Andy Hung            if (tagNumber == TAG_RAF_IMAGE_SIZE.number) {
2359609b815a3131d22da38b2f452faa9f89daad4039Andy Hung                int imageLength = in.readShort();
23605419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang                int imageWidth = in.readShort();
23615419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang                ExifAttribute imageLengthAttribute =
23625419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang                        ExifAttribute.createUShort(imageLength, mExifByteOrder);
23635419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang                ExifAttribute imageWidthAttribute =
23645419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang                        ExifAttribute.createUShort(imageWidth, mExifByteOrder);
23655419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang                mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, imageLengthAttribute);
2366609b815a3131d22da38b2f452faa9f89daad4039Andy Hung                mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, imageWidthAttribute);
2367609b815a3131d22da38b2f452faa9f89daad4039Andy Hung                if (DEBUG) {
2368d3ed3883c2d7bf3fb871be512055ed72cea964daPawin Vongmasa                    Log.d(TAG, "Updated to length: " + imageLength + ", width: " + imageWidth);
2369609b815a3131d22da38b2f452faa9f89daad4039Andy Hung                }
2370609b815a3131d22da38b2f452faa9f89daad4039Andy Hung                return;
2371609b815a3131d22da38b2f452faa9f89daad4039Andy Hung            }
2372609b815a3131d22da38b2f452faa9f89daad4039Andy Hung            in.skipBytes(numberOfBytes);
23735419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang        }
23745419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang    }
23755419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang
23765419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang    /**
23775419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang     * ORF files contains a primary image data and a MakerNote data that contains preview/thumbnail
23785419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang     * images. Both data takes the form of IFDs and can therefore be read with the
23795419242328f33f4d126a22ef6296c99353f4dfb4Chong Zhang     * readImageFileDirectory() method.
2380609b815a3131d22da38b2f452faa9f89daad4039Andy Hung     * This method reads all the necessary data and updates the primary/preview/thumbnail image
2381609b815a3131d22da38b2f452faa9f89daad4039Andy Hung     * information according to the GetOlympusPreviewImage() method in piex.cc.
2382318ad9c1d9d6515026dfc2c021359d27decaa7a1Andreas Huber     * For data format details, see the following:
2383     * http://fileformats.archiveteam.org/wiki/Olympus_ORF
2384     * https://libopenraw.freedesktop.org/wiki/Olympus_ORF
2385     */
2386    private void getOrfAttributes(ByteOrderedDataInputStream in) throws IOException {
2387        // Retrieve primary image data
2388        // Other Exif data will be located in the Makernote.
2389        getRawAttributes(in);
2390
2391        // Additionally retrieve preview/thumbnail information from MakerNote tag, which contains
2392        // proprietary tags and therefore does not have offical documentation
2393        // See GetOlympusPreviewImage() in piex.cc & http://www.exiv2.org/tags-olympus.html
2394        ExifAttribute makerNoteAttribute =
2395                (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE);
2396        if (makerNoteAttribute != null) {
2397            // Create an ordered DataInputStream for MakerNote
2398            ByteOrderedDataInputStream makerNoteDataInputStream =
2399                    new ByteOrderedDataInputStream(makerNoteAttribute.bytes);
2400            makerNoteDataInputStream.setByteOrder(mExifByteOrder);
2401
2402            // There are two types of headers for Olympus MakerNotes
2403            // See http://www.exiv2.org/makernote.html#R1
2404            byte[] makerNoteHeader1Bytes = new byte[ORF_MAKER_NOTE_HEADER_1.length];
2405            makerNoteDataInputStream.readFully(makerNoteHeader1Bytes);
2406            makerNoteDataInputStream.seek(0);
2407            byte[] makerNoteHeader2Bytes = new byte[ORF_MAKER_NOTE_HEADER_2.length];
2408            makerNoteDataInputStream.readFully(makerNoteHeader2Bytes);
2409            // Skip the corresponding amount of bytes for each header type
2410            if (Arrays.equals(makerNoteHeader1Bytes, ORF_MAKER_NOTE_HEADER_1)) {
2411                makerNoteDataInputStream.seek(ORF_MAKER_NOTE_HEADER_1_SIZE);
2412            } else if (Arrays.equals(makerNoteHeader2Bytes, ORF_MAKER_NOTE_HEADER_2)) {
2413                makerNoteDataInputStream.seek(ORF_MAKER_NOTE_HEADER_2_SIZE);
2414            }
2415
2416            // Read IFD data from MakerNote
2417            readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_ORF_MAKER_NOTE);
2418
2419            // Retrieve & update preview image offset & length values
2420            ExifAttribute imageLengthAttribute = (ExifAttribute)
2421                    mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_START);
2422            ExifAttribute bitsPerSampleAttribute = (ExifAttribute)
2423                    mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_LENGTH);
2424
2425            if (imageLengthAttribute != null && bitsPerSampleAttribute != null) {
2426                mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT,
2427                        imageLengthAttribute);
2428                mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
2429                        bitsPerSampleAttribute);
2430            }
2431
2432            // TODO: Check this behavior in other ORF files
2433            // Retrieve primary image length & width values
2434            // See piex.cc GetOlympusPreviewImage()
2435            ExifAttribute aspectFrameAttribute = (ExifAttribute)
2436                    mAttributes[IFD_TYPE_ORF_IMAGE_PROCESSING].get(TAG_ORF_ASPECT_FRAME);
2437            if (aspectFrameAttribute != null) {
2438                int[] aspectFrameValues = (int[]) aspectFrameAttribute.getValue(mExifByteOrder);
2439                if (aspectFrameValues == null || aspectFrameValues.length != 4) {
2440                    Log.w(TAG, "Invalid aspect frame values. frame="
2441                            + Arrays.toString(aspectFrameValues));
2442                    return;
2443                }
2444                if (aspectFrameValues[2] > aspectFrameValues[0] &&
2445                        aspectFrameValues[3] > aspectFrameValues[1]) {
2446                    int primaryImageWidth = aspectFrameValues[2] - aspectFrameValues[0] + 1;
2447                    int primaryImageLength = aspectFrameValues[3] - aspectFrameValues[1] + 1;
2448                    // Swap width & length values
2449                    if (primaryImageWidth < primaryImageLength) {
2450                        primaryImageWidth += primaryImageLength;
2451                        primaryImageLength = primaryImageWidth - primaryImageLength;
2452                        primaryImageWidth -= primaryImageLength;
2453                    }
2454                    ExifAttribute primaryImageWidthAttribute =
2455                            ExifAttribute.createUShort(primaryImageWidth, mExifByteOrder);
2456                    ExifAttribute primaryImageLengthAttribute =
2457                            ExifAttribute.createUShort(primaryImageLength, mExifByteOrder);
2458
2459                    mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, primaryImageWidthAttribute);
2460                    mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, primaryImageLengthAttribute);
2461                }
2462            }
2463        }
2464    }
2465
2466    // RW2 contains the primary image data in IFD0 and the preview and/or thumbnail image data in
2467    // the JpgFromRaw tag
2468    // See https://libopenraw.freedesktop.org/wiki/Panasonic_RAW/ and piex.cc Rw2GetPreviewData()
2469    private void getRw2Attributes(ByteOrderedDataInputStream in) throws IOException {
2470        // Retrieve primary image data
2471        getRawAttributes(in);
2472
2473        // Retrieve preview and/or thumbnail image data
2474        ExifAttribute jpgFromRawAttribute =
2475                (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].get(TAG_RW2_JPG_FROM_RAW);
2476        if (jpgFromRawAttribute != null) {
2477            getJpegAttributes(in, mRw2JpgFromRawOffset, IFD_TYPE_PREVIEW);
2478        }
2479
2480        // Set ISO tag value if necessary
2481        ExifAttribute rw2IsoAttribute =
2482                (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].get(TAG_RW2_ISO);
2483        ExifAttribute exifIsoAttribute =
2484                (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_ISO_SPEED_RATINGS);
2485        if (rw2IsoAttribute != null && exifIsoAttribute == null) {
2486            // Place this attribute only if it doesn't exist
2487            mAttributes[IFD_TYPE_EXIF].put(TAG_ISO_SPEED_RATINGS, rw2IsoAttribute);
2488        }
2489    }
2490
2491    // Stores a new JPEG image with EXIF attributes into a given output stream.
2492    private void saveJpegAttributes(InputStream inputStream, OutputStream outputStream)
2493            throws IOException {
2494        // See JPEG File Interchange Format Specification, "JFIF Specification"
2495        if (DEBUG) {
2496            Log.d(TAG, "saveJpegAttributes starting with (inputStream: " + inputStream
2497                    + ", outputStream: " + outputStream + ")");
2498        }
2499        DataInputStream dataInputStream = new DataInputStream(inputStream);
2500        ByteOrderedDataOutputStream dataOutputStream =
2501                new ByteOrderedDataOutputStream(outputStream, ByteOrder.BIG_ENDIAN);
2502        if (dataInputStream.readByte() != MARKER) {
2503            throw new IOException("Invalid marker");
2504        }
2505        dataOutputStream.writeByte(MARKER);
2506        if (dataInputStream.readByte() != MARKER_SOI) {
2507            throw new IOException("Invalid marker");
2508        }
2509        dataOutputStream.writeByte(MARKER_SOI);
2510
2511        // Write EXIF APP1 segment
2512        dataOutputStream.writeByte(MARKER);
2513        dataOutputStream.writeByte(MARKER_APP1);
2514        writeExifSegment(dataOutputStream, 6);
2515
2516        byte[] bytes = new byte[4096];
2517
2518        while (true) {
2519            byte marker = dataInputStream.readByte();
2520            if (marker != MARKER) {
2521                throw new IOException("Invalid marker");
2522            }
2523            marker = dataInputStream.readByte();
2524            switch (marker) {
2525                case MARKER_APP1: {
2526                    int length = dataInputStream.readUnsignedShort() - 2;
2527                    if (length < 0) {
2528                        throw new IOException("Invalid length");
2529                    }
2530                    byte[] identifier = new byte[6];
2531                    if (length >= 6) {
2532                        if (dataInputStream.read(identifier) != 6) {
2533                            throw new IOException("Invalid exif");
2534                        }
2535                        if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
2536                            // Skip the original EXIF APP1 segment.
2537                            if (dataInputStream.skipBytes(length - 6) != length - 6) {
2538                                throw new IOException("Invalid length");
2539                            }
2540                            break;
2541                        }
2542                    }
2543                    // Copy non-EXIF APP1 segment.
2544                    dataOutputStream.writeByte(MARKER);
2545                    dataOutputStream.writeByte(marker);
2546                    dataOutputStream.writeUnsignedShort(length + 2);
2547                    if (length >= 6) {
2548                        length -= 6;
2549                        dataOutputStream.write(identifier);
2550                    }
2551                    int read;
2552                    while (length > 0 && (read = dataInputStream.read(
2553                            bytes, 0, Math.min(length, bytes.length))) >= 0) {
2554                        dataOutputStream.write(bytes, 0, read);
2555                        length -= read;
2556                    }
2557                    break;
2558                }
2559                case MARKER_EOI:
2560                case MARKER_SOS: {
2561                    dataOutputStream.writeByte(MARKER);
2562                    dataOutputStream.writeByte(marker);
2563                    // Copy all the remaining data
2564                    copy(dataInputStream, dataOutputStream);
2565                    return;
2566                }
2567                default: {
2568                    // Copy JPEG segment
2569                    dataOutputStream.writeByte(MARKER);
2570                    dataOutputStream.writeByte(marker);
2571                    int length = dataInputStream.readUnsignedShort();
2572                    dataOutputStream.writeUnsignedShort(length);
2573                    length -= 2;
2574                    if (length < 0) {
2575                        throw new IOException("Invalid length");
2576                    }
2577                    int read;
2578                    while (length > 0 && (read = dataInputStream.read(
2579                            bytes, 0, Math.min(length, bytes.length))) >= 0) {
2580                        dataOutputStream.write(bytes, 0, read);
2581                        length -= read;
2582                    }
2583                    break;
2584                }
2585            }
2586        }
2587    }
2588
2589    // Reads the given EXIF byte area and save its tag data into attributes.
2590    private void readExifSegment(byte[] exifBytes, int imageType) throws IOException {
2591        ByteOrderedDataInputStream dataInputStream =
2592                new ByteOrderedDataInputStream(exifBytes);
2593
2594        // Parse TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
2595        parseTiffHeaders(dataInputStream, exifBytes.length);
2596
2597        // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6.
2598        readImageFileDirectory(dataInputStream, imageType);
2599    }
2600
2601    private void addDefaultValuesForCompatibility() {
2602        // The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag.
2603        String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL);
2604        if (valueOfDateTimeOriginal != null) {
2605            mAttributes[IFD_TYPE_PRIMARY].put(TAG_DATETIME,
2606                    ExifAttribute.createString(valueOfDateTimeOriginal));
2607        }
2608
2609        // Add the default value.
2610        if (getAttribute(TAG_IMAGE_WIDTH) == null) {
2611            mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH,
2612                    ExifAttribute.createULong(0, mExifByteOrder));
2613        }
2614        if (getAttribute(TAG_IMAGE_LENGTH) == null) {
2615            mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH,
2616                    ExifAttribute.createULong(0, mExifByteOrder));
2617        }
2618        if (getAttribute(TAG_ORIENTATION) == null) {
2619            mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION,
2620                    ExifAttribute.createULong(0, mExifByteOrder));
2621        }
2622        if (getAttribute(TAG_LIGHT_SOURCE) == null) {
2623            mAttributes[IFD_TYPE_EXIF].put(TAG_LIGHT_SOURCE,
2624                    ExifAttribute.createULong(0, mExifByteOrder));
2625        }
2626    }
2627
2628    private ByteOrder readByteOrder(ByteOrderedDataInputStream dataInputStream)
2629            throws IOException {
2630        // Read byte order.
2631        short byteOrder = dataInputStream.readShort();
2632        switch (byteOrder) {
2633            case BYTE_ALIGN_II:
2634                if (DEBUG) {
2635                    Log.d(TAG, "readExifSegment: Byte Align II");
2636                }
2637                return ByteOrder.LITTLE_ENDIAN;
2638            case BYTE_ALIGN_MM:
2639                if (DEBUG) {
2640                    Log.d(TAG, "readExifSegment: Byte Align MM");
2641                }
2642                return ByteOrder.BIG_ENDIAN;
2643            default:
2644                throw new IOException("Invalid byte order: " + Integer.toHexString(byteOrder));
2645        }
2646    }
2647
2648    private void parseTiffHeaders(ByteOrderedDataInputStream dataInputStream,
2649            int exifBytesLength) throws IOException {
2650        // Read byte order
2651        mExifByteOrder = readByteOrder(dataInputStream);
2652        // Set byte order
2653        dataInputStream.setByteOrder(mExifByteOrder);
2654
2655        // Check start code
2656        int startCode = dataInputStream.readUnsignedShort();
2657        if (mMimeType != IMAGE_TYPE_ORF && mMimeType != IMAGE_TYPE_RW2 && startCode != START_CODE) {
2658            throw new IOException("Invalid start code: " + Integer.toHexString(startCode));
2659        }
2660
2661        // Read and skip to first ifd offset
2662        int firstIfdOffset = dataInputStream.readInt();
2663        if (firstIfdOffset < 8 || firstIfdOffset >= exifBytesLength) {
2664            throw new IOException("Invalid first Ifd offset: " + firstIfdOffset);
2665        }
2666        firstIfdOffset -= 8;
2667        if (firstIfdOffset > 0) {
2668            if (dataInputStream.skipBytes(firstIfdOffset) != firstIfdOffset) {
2669                throw new IOException("Couldn't jump to first Ifd: " + firstIfdOffset);
2670            }
2671        }
2672    }
2673
2674    // Reads image file directory, which is a tag group in EXIF.
2675    private void readImageFileDirectory(ByteOrderedDataInputStream dataInputStream,
2676            @IfdType int ifdType) throws IOException {
2677        if (dataInputStream.mPosition + 2 > dataInputStream.mLength) {
2678            // Return if there is no data from the offset.
2679            return;
2680        }
2681        // See TIFF 6.0 Section 2: TIFF Structure, Figure 1.
2682        short numberOfDirectoryEntry = dataInputStream.readShort();
2683        if (dataInputStream.mPosition + 12 * numberOfDirectoryEntry > dataInputStream.mLength) {
2684            // Return if the size of entries is too big.
2685            return;
2686        }
2687
2688        if (DEBUG) {
2689            Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry);
2690        }
2691
2692        // See TIFF 6.0 Section 2: TIFF Structure, "Image File Directory".
2693        for (short i = 0; i < numberOfDirectoryEntry; ++i) {
2694            int tagNumber = dataInputStream.readUnsignedShort();
2695            int dataFormat = dataInputStream.readUnsignedShort();
2696            int numberOfComponents = dataInputStream.readInt();
2697            // Next four bytes is for data offset or value.
2698            long nextEntryOffset = dataInputStream.peek() + 4;
2699
2700            // Look up a corresponding tag from tag number
2701            ExifTag tag = (ExifTag) sExifTagMapsForReading[ifdType].get(tagNumber);
2702
2703            if (DEBUG) {
2704                Log.d(TAG, String.format("ifdType: %d, tagNumber: %d, tagName: %s, dataFormat: %d, "
2705                        + "numberOfComponents: %d", ifdType, tagNumber,
2706                        tag != null ? tag.name : null, dataFormat, numberOfComponents));
2707            }
2708
2709            long byteCount = 0;
2710            boolean valid = false;
2711            if (tag == null) {
2712                Log.w(TAG, "Skip the tag entry since tag number is not defined: " + tagNumber);
2713            } else if (dataFormat <= 0 || dataFormat >= IFD_FORMAT_BYTES_PER_FORMAT.length) {
2714                Log.w(TAG, "Skip the tag entry since data format is invalid: " + dataFormat);
2715            } else {
2716                byteCount = (long) numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat];
2717                if (byteCount < 0 || byteCount > Integer.MAX_VALUE) {
2718                    Log.w(TAG, "Skip the tag entry since the number of components is invalid: "
2719                            + numberOfComponents);
2720                } else {
2721                    valid = true;
2722                }
2723            }
2724            if (!valid) {
2725                dataInputStream.seek(nextEntryOffset);
2726                continue;
2727            }
2728
2729            // Read a value from data field or seek to the value offset which is stored in data
2730            // field if the size of the entry value is bigger than 4.
2731            if (byteCount > 4) {
2732                int offset = dataInputStream.readInt();
2733                if (DEBUG) {
2734                    Log.d(TAG, "seek to data offset: " + offset);
2735                }
2736                if (mMimeType == IMAGE_TYPE_ORF) {
2737                    if (TAG_MAKER_NOTE.equals(tag.name)) {
2738                        // Save offset value for reading thumbnail
2739                        mOrfMakerNoteOffset = offset;
2740                    } else if (ifdType == IFD_TYPE_ORF_MAKER_NOTE
2741                            && TAG_ORF_THUMBNAIL_IMAGE.equals(tag.name)) {
2742                        // Retrieve & update values for thumbnail offset and length values for ORF
2743                        mOrfThumbnailOffset = offset;
2744                        mOrfThumbnailLength = numberOfComponents;
2745
2746                        ExifAttribute compressionAttribute =
2747                                ExifAttribute.createUShort(DATA_JPEG, mExifByteOrder);
2748                        ExifAttribute jpegInterchangeFormatAttribute =
2749                                ExifAttribute.createULong(mOrfThumbnailOffset, mExifByteOrder);
2750                        ExifAttribute jpegInterchangeFormatLengthAttribute =
2751                                ExifAttribute.createULong(mOrfThumbnailLength, mExifByteOrder);
2752
2753                        mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_COMPRESSION, compressionAttribute);
2754                        mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT,
2755                                jpegInterchangeFormatAttribute);
2756                        mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
2757                                jpegInterchangeFormatLengthAttribute);
2758                    }
2759                } else if (mMimeType == IMAGE_TYPE_RW2) {
2760                    if (TAG_RW2_JPG_FROM_RAW.equals(tag.name)) {
2761                        mRw2JpgFromRawOffset = offset;
2762                    }
2763                }
2764                if (offset + byteCount <= dataInputStream.mLength) {
2765                    dataInputStream.seek(offset);
2766                } else {
2767                    // Skip if invalid data offset.
2768                    Log.w(TAG, "Skip the tag entry since data offset is invalid: " + offset);
2769                    dataInputStream.seek(nextEntryOffset);
2770                    continue;
2771                }
2772            }
2773
2774            // Recursively parse IFD when a IFD pointer tag appears.
2775            Object nextIfdType = sExifPointerTagMap.get(tagNumber);
2776            if (DEBUG) {
2777                Log.d(TAG, "nextIfdType: " + nextIfdType + " byteCount: " + byteCount);
2778            }
2779
2780            if (nextIfdType != null) {
2781                long offset = -1L;
2782                // Get offset from data field
2783                switch (dataFormat) {
2784                    case IFD_FORMAT_USHORT: {
2785                        offset = dataInputStream.readUnsignedShort();
2786                        break;
2787                    }
2788                    case IFD_FORMAT_SSHORT: {
2789                        offset = dataInputStream.readShort();
2790                        break;
2791                    }
2792                    case IFD_FORMAT_ULONG: {
2793                        offset = dataInputStream.readUnsignedInt();
2794                        break;
2795                    }
2796                    case IFD_FORMAT_SLONG:
2797                    case IFD_FORMAT_IFD: {
2798                        offset = dataInputStream.readInt();
2799                        break;
2800                    }
2801                    default: {
2802                        // Nothing to do
2803                        break;
2804                    }
2805                }
2806                if (DEBUG) {
2807                    Log.d(TAG, String.format("Offset: %d, tagName: %s", offset, tag.name));
2808                }
2809                if (offset > 0L && offset < dataInputStream.mLength) {
2810                    dataInputStream.seek(offset);
2811                    readImageFileDirectory(dataInputStream, (int) nextIfdType);
2812                } else {
2813                    Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset);
2814                }
2815
2816                dataInputStream.seek(nextEntryOffset);
2817                continue;
2818            }
2819
2820            byte[] bytes = new byte[(int) byteCount];
2821            dataInputStream.readFully(bytes);
2822            ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, bytes);
2823            mAttributes[ifdType].put(tag.name, attribute);
2824
2825            // DNG files have a DNG Version tag specifying the version of specifications that the
2826            // image file is following.
2827            // See http://fileformats.archiveteam.org/wiki/DNG
2828            if (TAG_DNG_VERSION.equals(tag.name)) {
2829                mMimeType = IMAGE_TYPE_DNG;
2830            }
2831
2832            // PEF files have a Make or Model tag that begins with "PENTAX" or a compression tag
2833            // that is 65535.
2834            // See http://fileformats.archiveteam.org/wiki/Pentax_PEF
2835            if (((TAG_MAKE.equals(tag.name) || TAG_MODEL.equals(tag.name))
2836                    && attribute.getStringValue(mExifByteOrder).contains(PEF_SIGNATURE))
2837                    || (TAG_COMPRESSION.equals(tag.name)
2838                    && attribute.getIntValue(mExifByteOrder) == 65535)) {
2839                mMimeType = IMAGE_TYPE_PEF;
2840            }
2841
2842            // Seek to next tag offset
2843            if (dataInputStream.peek() != nextEntryOffset) {
2844                dataInputStream.seek(nextEntryOffset);
2845            }
2846        }
2847
2848        if (dataInputStream.peek() + 4 <= dataInputStream.mLength) {
2849            int nextIfdOffset = dataInputStream.readInt();
2850            if (DEBUG) {
2851                Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset));
2852            }
2853            // The next IFD offset needs to be bigger than 8
2854            // since the first IFD offset is at least 8.
2855            if (nextIfdOffset > 8 && nextIfdOffset < dataInputStream.mLength) {
2856                dataInputStream.seek(nextIfdOffset);
2857                if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
2858                    // Do not overwrite thumbnail IFD data if it alreay exists.
2859                    readImageFileDirectory(dataInputStream, IFD_TYPE_THUMBNAIL);
2860                } else if (mAttributes[IFD_TYPE_PREVIEW].isEmpty()) {
2861                    readImageFileDirectory(dataInputStream, IFD_TYPE_PREVIEW);
2862                }
2863            }
2864        }
2865    }
2866
2867    /**
2868     * JPEG compressed images do not contain IMAGE_LENGTH & IMAGE_WIDTH tags.
2869     * This value uses JpegInterchangeFormat(JPEG data offset) value, and calls getJpegAttributes()
2870     * to locate SOF(Start of Frame) marker and update the image length & width values.
2871     * See JEITA CP-3451C Table 5 and Section 4.8.1. B.
2872     */
2873    private void retrieveJpegImageSize(ByteOrderedDataInputStream in, int imageType)
2874            throws IOException {
2875        // Check if image already has IMAGE_LENGTH & IMAGE_WIDTH values
2876        ExifAttribute imageLengthAttribute =
2877                (ExifAttribute) mAttributes[imageType].get(TAG_IMAGE_LENGTH);
2878        ExifAttribute imageWidthAttribute =
2879                (ExifAttribute) mAttributes[imageType].get(TAG_IMAGE_WIDTH);
2880
2881        if (imageLengthAttribute == null || imageWidthAttribute == null) {
2882            // Find if offset for JPEG data exists
2883            ExifAttribute jpegInterchangeFormatAttribute =
2884                    (ExifAttribute) mAttributes[imageType].get(TAG_JPEG_INTERCHANGE_FORMAT);
2885            if (jpegInterchangeFormatAttribute != null) {
2886                int jpegInterchangeFormat =
2887                        jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder);
2888
2889                // Searches for SOF marker in JPEG data and updates IMAGE_LENGTH & IMAGE_WIDTH tags
2890                getJpegAttributes(in, jpegInterchangeFormat, imageType);
2891            }
2892        }
2893    }
2894
2895    // Sets thumbnail offset & length attributes based on JpegInterchangeFormat or StripOffsets tags
2896    private void setThumbnailData(ByteOrderedDataInputStream in) throws IOException {
2897        HashMap thumbnailData = mAttributes[IFD_TYPE_THUMBNAIL];
2898
2899        ExifAttribute compressionAttribute =
2900                (ExifAttribute) thumbnailData.get(TAG_COMPRESSION);
2901        if (compressionAttribute != null) {
2902            mThumbnailCompression = compressionAttribute.getIntValue(mExifByteOrder);
2903            switch (mThumbnailCompression) {
2904                case DATA_JPEG: {
2905                    handleThumbnailFromJfif(in, thumbnailData);
2906                    break;
2907                }
2908                case DATA_UNCOMPRESSED:
2909                case DATA_JPEG_COMPRESSED: {
2910                    if (isSupportedDataType(thumbnailData)) {
2911                        handleThumbnailFromStrips(in, thumbnailData);
2912                    }
2913                    break;
2914                }
2915            }
2916        } else {
2917            // Thumbnail data may not contain Compression tag value
2918            mThumbnailCompression = DATA_JPEG;
2919            handleThumbnailFromJfif(in, thumbnailData);
2920        }
2921    }
2922
2923    // Check JpegInterchangeFormat(JFIF) tags to retrieve thumbnail offset & length values
2924    // and reads the corresponding bytes if stream does not support seek function
2925    private void handleThumbnailFromJfif(ByteOrderedDataInputStream in, HashMap thumbnailData)
2926            throws IOException {
2927        ExifAttribute jpegInterchangeFormatAttribute =
2928                (ExifAttribute) thumbnailData.get(TAG_JPEG_INTERCHANGE_FORMAT);
2929        ExifAttribute jpegInterchangeFormatLengthAttribute =
2930                (ExifAttribute) thumbnailData.get(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
2931        if (jpegInterchangeFormatAttribute != null
2932                && jpegInterchangeFormatLengthAttribute != null) {
2933            int thumbnailOffset = jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder);
2934            int thumbnailLength = jpegInterchangeFormatLengthAttribute.getIntValue(mExifByteOrder);
2935
2936            // The following code limits the size of thumbnail size not to overflow EXIF data area.
2937            thumbnailLength = Math.min(thumbnailLength, in.available() - thumbnailOffset);
2938            if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF
2939                    || mMimeType == IMAGE_TYPE_RW2) {
2940                thumbnailOffset += mExifOffset;
2941            } else if (mMimeType == IMAGE_TYPE_ORF) {
2942                // Update offset value since RAF files have IFD data preceding MakerNote data.
2943                thumbnailOffset += mOrfMakerNoteOffset;
2944            }
2945            if (DEBUG) {
2946                Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset
2947                        + ", length: " + thumbnailLength);
2948            }
2949            if (thumbnailOffset > 0 && thumbnailLength > 0) {
2950                mHasThumbnail = true;
2951                mThumbnailOffset = thumbnailOffset;
2952                mThumbnailLength = thumbnailLength;
2953                if (mFilename == null && mAssetInputStream == null) {
2954                    // Save the thumbnail in memory if the input doesn't support reading again.
2955                    byte[] thumbnailBytes = new byte[thumbnailLength];
2956                    in.seek(thumbnailOffset);
2957                    in.readFully(thumbnailBytes);
2958                    mThumbnailBytes = thumbnailBytes;
2959                }
2960            }
2961        }
2962    }
2963
2964    // Check StripOffsets & StripByteCounts tags to retrieve thumbnail offset & length values
2965    private void handleThumbnailFromStrips(ByteOrderedDataInputStream in, HashMap thumbnailData)
2966            throws IOException {
2967        ExifAttribute stripOffsetsAttribute =
2968                (ExifAttribute) thumbnailData.get(TAG_STRIP_OFFSETS);
2969        ExifAttribute stripByteCountsAttribute =
2970                (ExifAttribute) thumbnailData.get(TAG_STRIP_BYTE_COUNTS);
2971
2972        if (stripOffsetsAttribute != null && stripByteCountsAttribute != null) {
2973            long[] stripOffsets =
2974                    convertToLongArray(stripOffsetsAttribute.getValue(mExifByteOrder));
2975            long[] stripByteCounts =
2976                    convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder));
2977
2978            if (stripOffsets == null) {
2979                Log.w(TAG, "stripOffsets should not be null.");
2980                return;
2981            }
2982            if (stripByteCounts == null) {
2983                Log.w(TAG, "stripByteCounts should not be null.");
2984                return;
2985            }
2986
2987            long totalStripByteCount = 0;
2988            for (long byteCount : stripByteCounts) {
2989                totalStripByteCount += byteCount;
2990            }
2991
2992            // Set thumbnail byte array data for non-consecutive strip bytes
2993            byte[] totalStripBytes = new byte[(int) totalStripByteCount];
2994
2995            int bytesRead = 0;
2996            int bytesAdded = 0;
2997            for (int i = 0; i < stripOffsets.length; i++) {
2998                int stripOffset = (int) stripOffsets[i];
2999                int stripByteCount = (int) stripByteCounts[i];
3000
3001                // Skip to offset
3002                int skipBytes = stripOffset - bytesRead;
3003                if (skipBytes < 0) {
3004                    Log.d(TAG, "Invalid strip offset value");
3005                }
3006                in.seek(skipBytes);
3007                bytesRead += skipBytes;
3008
3009                // Read strip bytes
3010                byte[] stripBytes = new byte[stripByteCount];
3011                in.read(stripBytes);
3012                bytesRead += stripByteCount;
3013
3014                // Add bytes to array
3015                System.arraycopy(stripBytes, 0, totalStripBytes, bytesAdded,
3016                        stripBytes.length);
3017                bytesAdded += stripBytes.length;
3018            }
3019
3020            mHasThumbnail = true;
3021            mThumbnailBytes = totalStripBytes;
3022            mThumbnailLength = totalStripBytes.length;
3023        }
3024    }
3025
3026    // Check if thumbnail data type is currently supported or not
3027    private boolean isSupportedDataType(HashMap thumbnailData) throws IOException {
3028        ExifAttribute bitsPerSampleAttribute =
3029                (ExifAttribute) thumbnailData.get(TAG_BITS_PER_SAMPLE);
3030        if (bitsPerSampleAttribute != null) {
3031            int[] bitsPerSampleValue = (int[]) bitsPerSampleAttribute.getValue(mExifByteOrder);
3032
3033            if (Arrays.equals(BITS_PER_SAMPLE_RGB, bitsPerSampleValue)) {
3034                return true;
3035            }
3036
3037            // See DNG Specification 1.4.0.0. Section 3, Compression.
3038            if (mMimeType == IMAGE_TYPE_DNG) {
3039                ExifAttribute photometricInterpretationAttribute =
3040                        (ExifAttribute) thumbnailData.get(TAG_PHOTOMETRIC_INTERPRETATION);
3041                if (photometricInterpretationAttribute != null) {
3042                    int photometricInterpretationValue
3043                            = photometricInterpretationAttribute.getIntValue(mExifByteOrder);
3044                    if ((photometricInterpretationValue == PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO
3045                            && Arrays.equals(bitsPerSampleValue, BITS_PER_SAMPLE_GREYSCALE_2))
3046                            || ((photometricInterpretationValue == PHOTOMETRIC_INTERPRETATION_YCBCR)
3047                            && (Arrays.equals(bitsPerSampleValue, BITS_PER_SAMPLE_RGB)))) {
3048                        return true;
3049                    } else {
3050                        // TODO: Add support for lossless Huffman JPEG data
3051                    }
3052                }
3053            }
3054        }
3055        if (DEBUG) {
3056            Log.d(TAG, "Unsupported data type value");
3057        }
3058        return false;
3059    }
3060
3061    // Returns true if the image length and width values are <= 512.
3062    // See Section 4.8 of http://standardsproposals.bsigroup.com/Home/getPDF/567
3063    private boolean isThumbnail(HashMap map) throws IOException {
3064        ExifAttribute imageLengthAttribute = (ExifAttribute) map.get(TAG_IMAGE_LENGTH);
3065        ExifAttribute imageWidthAttribute = (ExifAttribute) map.get(TAG_IMAGE_WIDTH);
3066
3067        if (imageLengthAttribute != null && imageWidthAttribute != null) {
3068            int imageLengthValue = imageLengthAttribute.getIntValue(mExifByteOrder);
3069            int imageWidthValue = imageWidthAttribute.getIntValue(mExifByteOrder);
3070            if (imageLengthValue <= MAX_THUMBNAIL_SIZE && imageWidthValue <= MAX_THUMBNAIL_SIZE) {
3071                return true;
3072            }
3073        }
3074        return false;
3075    }
3076
3077    // Validate primary, preview, thumbnail image data by comparing image size
3078    private void validateImages(InputStream in) throws IOException {
3079        // Swap images based on size (primary > preview > thumbnail)
3080        swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_PREVIEW);
3081        swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_THUMBNAIL);
3082        swapBasedOnImageSize(IFD_TYPE_PREVIEW, IFD_TYPE_THUMBNAIL);
3083
3084        // Check if image has PixelXDimension/PixelYDimension tags, which contain valid image
3085        // sizes, excluding padding at the right end or bottom end of the image to make sure that
3086        // the values are multiples of 64. See JEITA CP-3451C Table 5 and Section 4.8.1. B.
3087        ExifAttribute pixelXDimAttribute =
3088                (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_X_DIMENSION);
3089        ExifAttribute pixelYDimAttribute =
3090                (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_Y_DIMENSION);
3091        if (pixelXDimAttribute != null && pixelYDimAttribute != null) {
3092            mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, pixelXDimAttribute);
3093            mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, pixelYDimAttribute);
3094        }
3095
3096        // Check whether thumbnail image exists and whether preview image satisfies the thumbnail
3097        // image requirements
3098        if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
3099            if (isThumbnail(mAttributes[IFD_TYPE_PREVIEW])) {
3100                mAttributes[IFD_TYPE_THUMBNAIL] = mAttributes[IFD_TYPE_PREVIEW];
3101                mAttributes[IFD_TYPE_PREVIEW] = new HashMap();
3102            }
3103        }
3104
3105        // Check if the thumbnail image satisfies the thumbnail size requirements
3106        if (!isThumbnail(mAttributes[IFD_TYPE_THUMBNAIL])) {
3107            Log.d(TAG, "No image meets the size requirements of a thumbnail image.");
3108        }
3109    }
3110
3111    /**
3112     * If image is uncompressed, ImageWidth/Length tags are used to store size info.
3113     * However, uncompressed images often store extra pixels around the edges of the final image,
3114     * which results in larger values for TAG_IMAGE_WIDTH and TAG_IMAGE_LENGTH tags.
3115     * This method corrects those tag values by checking first the values of TAG_DEFAULT_CROP_SIZE
3116     * See DNG Specification 1.4.0.0. Section 4. (DefaultCropSize)
3117     *
3118     * If image is a RW2 file, valid image sizes are stored in SensorBorder tags.
3119     * See tiff_parser.cc GetFullDimension32()
3120     * */
3121    private void updateImageSizeValues(ByteOrderedDataInputStream in, int imageType)
3122            throws IOException {
3123        // Uncompressed image valid image size values
3124        ExifAttribute defaultCropSizeAttribute =
3125                (ExifAttribute) mAttributes[imageType].get(TAG_DEFAULT_CROP_SIZE);
3126        // RW2 image valid image size values
3127        ExifAttribute topBorderAttribute =
3128                (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_TOP_BORDER);
3129        ExifAttribute leftBorderAttribute =
3130                (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_LEFT_BORDER);
3131        ExifAttribute bottomBorderAttribute =
3132                (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_BOTTOM_BORDER);
3133        ExifAttribute rightBorderAttribute =
3134                (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_RIGHT_BORDER);
3135
3136        if (defaultCropSizeAttribute != null) {
3137            // Update for uncompressed image
3138            ExifAttribute defaultCropSizeXAttribute, defaultCropSizeYAttribute;
3139            if (defaultCropSizeAttribute.format == IFD_FORMAT_URATIONAL) {
3140                Rational[] defaultCropSizeValue =
3141                        (Rational[]) defaultCropSizeAttribute.getValue(mExifByteOrder);
3142                if (defaultCropSizeValue == null || defaultCropSizeValue.length != 2) {
3143                    Log.w(TAG, "Invalid crop size values. cropSize="
3144                            + Arrays.toString(defaultCropSizeValue));
3145                    return;
3146                }
3147                defaultCropSizeXAttribute =
3148                        ExifAttribute.createURational(defaultCropSizeValue[0], mExifByteOrder);
3149                defaultCropSizeYAttribute =
3150                        ExifAttribute.createURational(defaultCropSizeValue[1], mExifByteOrder);
3151            } else {
3152                int[] defaultCropSizeValue =
3153                        (int[]) defaultCropSizeAttribute.getValue(mExifByteOrder);
3154                if (defaultCropSizeValue == null || defaultCropSizeValue.length != 2) {
3155                    Log.w(TAG, "Invalid crop size values. cropSize="
3156                            + Arrays.toString(defaultCropSizeValue));
3157                    return;
3158                }
3159                defaultCropSizeXAttribute =
3160                        ExifAttribute.createUShort(defaultCropSizeValue[0], mExifByteOrder);
3161                defaultCropSizeYAttribute =
3162                        ExifAttribute.createUShort(defaultCropSizeValue[1], mExifByteOrder);
3163            }
3164            mAttributes[imageType].put(TAG_IMAGE_WIDTH, defaultCropSizeXAttribute);
3165            mAttributes[imageType].put(TAG_IMAGE_LENGTH, defaultCropSizeYAttribute);
3166        } else if (topBorderAttribute != null && leftBorderAttribute != null &&
3167                bottomBorderAttribute != null && rightBorderAttribute != null) {
3168            // Update for RW2 image
3169            int topBorderValue = topBorderAttribute.getIntValue(mExifByteOrder);
3170            int bottomBorderValue = bottomBorderAttribute.getIntValue(mExifByteOrder);
3171            int rightBorderValue = rightBorderAttribute.getIntValue(mExifByteOrder);
3172            int leftBorderValue = leftBorderAttribute.getIntValue(mExifByteOrder);
3173            if (bottomBorderValue > topBorderValue && rightBorderValue > leftBorderValue) {
3174                int length = bottomBorderValue - topBorderValue;
3175                int width = rightBorderValue - leftBorderValue;
3176                ExifAttribute imageLengthAttribute =
3177                        ExifAttribute.createUShort(length, mExifByteOrder);
3178                ExifAttribute imageWidthAttribute =
3179                        ExifAttribute.createUShort(width, mExifByteOrder);
3180                mAttributes[imageType].put(TAG_IMAGE_LENGTH, imageLengthAttribute);
3181                mAttributes[imageType].put(TAG_IMAGE_WIDTH, imageWidthAttribute);
3182            }
3183        } else {
3184            retrieveJpegImageSize(in, imageType);
3185        }
3186    }
3187
3188    // Writes an Exif segment into the given output stream.
3189    private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream,
3190            int exifOffsetFromBeginning) throws IOException {
3191        // The following variables are for calculating each IFD tag group size in bytes.
3192        int[] ifdOffsets = new int[EXIF_TAGS.length];
3193        int[] ifdDataSizes = new int[EXIF_TAGS.length];
3194
3195        // Remove IFD pointer tags (we'll re-add it later.)
3196        for (ExifTag tag : EXIF_POINTER_TAGS) {
3197            removeAttribute(tag.name);
3198        }
3199        // Remove old thumbnail data
3200        removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
3201        removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
3202
3203        // Remove null value tags.
3204        for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
3205            for (Object obj : mAttributes[ifdType].entrySet().toArray()) {
3206                final Map.Entry entry = (Map.Entry) obj;
3207                if (entry.getValue() == null) {
3208                    mAttributes[ifdType].remove(entry.getKey());
3209                }
3210            }
3211        }
3212
3213        // Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD
3214        // offset when there is one or more tags in the thumbnail IFD.
3215        if (!mAttributes[IFD_TYPE_EXIF].isEmpty()) {
3216            mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[1].name,
3217                    ExifAttribute.createULong(0, mExifByteOrder));
3218        }
3219        if (!mAttributes[IFD_TYPE_GPS].isEmpty()) {
3220            mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[2].name,
3221                    ExifAttribute.createULong(0, mExifByteOrder));
3222        }
3223        if (!mAttributes[IFD_TYPE_INTEROPERABILITY].isEmpty()) {
3224            mAttributes[IFD_TYPE_EXIF].put(EXIF_POINTER_TAGS[3].name,
3225                    ExifAttribute.createULong(0, mExifByteOrder));
3226        }
3227        if (mHasThumbnail) {
3228            mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
3229                    ExifAttribute.createULong(0, mExifByteOrder));
3230            mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
3231                    ExifAttribute.createULong(mThumbnailLength, mExifByteOrder));
3232        }
3233
3234        // Calculate IFD group data area sizes. IFD group data area is assigned to save the entry
3235        // value which has a bigger size than 4 bytes.
3236        for (int i = 0; i < EXIF_TAGS.length; ++i) {
3237            int sum = 0;
3238            for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
3239                final ExifAttribute exifAttribute = (ExifAttribute) entry.getValue();
3240                final int size = exifAttribute.size();
3241                if (size > 4) {
3242                    sum += size;
3243                }
3244            }
3245            ifdDataSizes[i] += sum;
3246        }
3247
3248        // Calculate IFD offsets.
3249        int position = 8;
3250        for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
3251            if (!mAttributes[ifdType].isEmpty()) {
3252                ifdOffsets[ifdType] = position;
3253                position += 2 + mAttributes[ifdType].size() * 12 + 4 + ifdDataSizes[ifdType];
3254            }
3255        }
3256        if (mHasThumbnail) {
3257            int thumbnailOffset = position;
3258            mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
3259                    ExifAttribute.createULong(thumbnailOffset, mExifByteOrder));
3260            mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset;
3261            position += mThumbnailLength;
3262        }
3263
3264        // Calculate the total size
3265        int totalSize = position + 8;  // eight bytes is for header part.
3266        if (DEBUG) {
3267            Log.d(TAG, "totalSize length: " + totalSize);
3268            for (int i = 0; i < EXIF_TAGS.length; ++i) {
3269                Log.d(TAG, String.format("index: %d, offsets: %d, tag count: %d, data sizes: %d",
3270                        i, ifdOffsets[i], mAttributes[i].size(), ifdDataSizes[i]));
3271            }
3272        }
3273
3274        // Update IFD pointer tags with the calculated offsets.
3275        if (!mAttributes[IFD_TYPE_EXIF].isEmpty()) {
3276            mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[1].name,
3277                    ExifAttribute.createULong(ifdOffsets[IFD_TYPE_EXIF], mExifByteOrder));
3278        }
3279        if (!mAttributes[IFD_TYPE_GPS].isEmpty()) {
3280            mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[2].name,
3281                    ExifAttribute.createULong(ifdOffsets[IFD_TYPE_GPS], mExifByteOrder));
3282        }
3283        if (!mAttributes[IFD_TYPE_INTEROPERABILITY].isEmpty()) {
3284            mAttributes[IFD_TYPE_EXIF].put(EXIF_POINTER_TAGS[3].name, ExifAttribute.createULong(
3285                    ifdOffsets[IFD_TYPE_INTEROPERABILITY], mExifByteOrder));
3286        }
3287
3288        // Write TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
3289        dataOutputStream.writeUnsignedShort(totalSize);
3290        dataOutputStream.write(IDENTIFIER_EXIF_APP1);
3291        dataOutputStream.writeShort(mExifByteOrder == ByteOrder.BIG_ENDIAN
3292                ? BYTE_ALIGN_MM : BYTE_ALIGN_II);
3293        dataOutputStream.setByteOrder(mExifByteOrder);
3294        dataOutputStream.writeUnsignedShort(START_CODE);
3295        dataOutputStream.writeUnsignedInt(IFD_OFFSET);
3296
3297        // Write IFD groups. See JEITA CP-3451C Section 4.5.8. Figure 9.
3298        for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
3299            if (!mAttributes[ifdType].isEmpty()) {
3300                // See JEITA CP-3451C Section 4.6.2: IFD structure.
3301                // Write entry count
3302                dataOutputStream.writeUnsignedShort(mAttributes[ifdType].size());
3303
3304                // Write entry info
3305                int dataOffset = ifdOffsets[ifdType] + 2 + mAttributes[ifdType].size() * 12 + 4;
3306                for (Map.Entry entry : (Set<Map.Entry>) mAttributes[ifdType].entrySet()) {
3307                    // Convert tag name to tag number.
3308                    final ExifTag tag =
3309                            (ExifTag) sExifTagMapsForWriting[ifdType].get(entry.getKey());
3310                    final int tagNumber = tag.number;
3311                    final ExifAttribute attribute = (ExifAttribute) entry.getValue();
3312                    final int size = attribute.size();
3313
3314                    dataOutputStream.writeUnsignedShort(tagNumber);
3315                    dataOutputStream.writeUnsignedShort(attribute.format);
3316                    dataOutputStream.writeInt(attribute.numberOfComponents);
3317                    if (size > 4) {
3318                        dataOutputStream.writeUnsignedInt(dataOffset);
3319                        dataOffset += size;
3320                    } else {
3321                        dataOutputStream.write(attribute.bytes);
3322                        // Fill zero up to 4 bytes
3323                        if (size < 4) {
3324                            for (int i = size; i < 4; ++i) {
3325                                dataOutputStream.writeByte(0);
3326                            }
3327                        }
3328                    }
3329                }
3330
3331                // Write the next offset. It writes the offset of thumbnail IFD if there is one or
3332                // more tags in the thumbnail IFD when the current IFD is the primary image TIFF
3333                // IFD; Otherwise 0.
3334                if (ifdType == 0 && !mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
3335                    dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_TYPE_THUMBNAIL]);
3336                } else {
3337                    dataOutputStream.writeUnsignedInt(0);
3338                }
3339
3340                // Write values of data field exceeding 4 bytes after the next offset.
3341                for (Map.Entry entry : (Set<Map.Entry>) mAttributes[ifdType].entrySet()) {
3342                    ExifAttribute attribute = (ExifAttribute) entry.getValue();
3343
3344                    if (attribute.bytes.length > 4) {
3345                        dataOutputStream.write(attribute.bytes, 0, attribute.bytes.length);
3346                    }
3347                }
3348            }
3349        }
3350
3351        // Write thumbnail
3352        if (mHasThumbnail) {
3353            dataOutputStream.write(getThumbnailBytes());
3354        }
3355
3356        // Reset the byte order to big endian in order to write remaining parts of the JPEG file.
3357        dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
3358
3359        return totalSize;
3360    }
3361
3362    /**
3363     * Determines the data format of EXIF entry value.
3364     *
3365     * @param entryValue The value to be determined.
3366     * @return Returns two data formats gussed as a pair in integer. If there is no two candidate
3367               data formats for the given entry value, returns {@code -1} in the second of the pair.
3368     */
3369    private static Pair<Integer, Integer> guessDataFormat(String entryValue) {
3370        // See TIFF 6.0 Section 2, "Image File Directory".
3371        // Take the first component if there are more than one component.
3372        if (entryValue.contains(",")) {
3373            String[] entryValues = entryValue.split(",");
3374            Pair<Integer, Integer> dataFormat = guessDataFormat(entryValues[0]);
3375            if (dataFormat.first == IFD_FORMAT_STRING) {
3376                return dataFormat;
3377            }
3378            for (int i = 1; i < entryValues.length; ++i) {
3379                final Pair<Integer, Integer> guessDataFormat = guessDataFormat(entryValues[i]);
3380                int first = -1, second = -1;
3381                if (guessDataFormat.first.equals(dataFormat.first)
3382                        || guessDataFormat.second.equals(dataFormat.first)) {
3383                    first = dataFormat.first;
3384                }
3385                if (dataFormat.second != -1 && (guessDataFormat.first.equals(dataFormat.second)
3386                        || guessDataFormat.second.equals(dataFormat.second))) {
3387                    second = dataFormat.second;
3388                }
3389                if (first == -1 && second == -1) {
3390                    return new Pair<>(IFD_FORMAT_STRING, -1);
3391                }
3392                if (first == -1) {
3393                    dataFormat = new Pair<>(second, -1);
3394                    continue;
3395                }
3396                if (second == -1) {
3397                    dataFormat = new Pair<>(first, -1);
3398                    continue;
3399                }
3400            }
3401            return dataFormat;
3402        }
3403
3404        if (entryValue.contains("/")) {
3405            String[] rationalNumber = entryValue.split("/");
3406            if (rationalNumber.length == 2) {
3407                try {
3408                    long numerator = (long) Double.parseDouble(rationalNumber[0]);
3409                    long denominator = (long) Double.parseDouble(rationalNumber[1]);
3410                    if (numerator < 0L || denominator < 0L) {
3411                        return new Pair<>(IFD_FORMAT_SRATIONAL, -1);
3412                    }
3413                    if (numerator > Integer.MAX_VALUE || denominator > Integer.MAX_VALUE) {
3414                        return new Pair<>(IFD_FORMAT_URATIONAL, -1);
3415                    }
3416                    return new Pair<>(IFD_FORMAT_SRATIONAL, IFD_FORMAT_URATIONAL);
3417                } catch (NumberFormatException e)  {
3418                    // Ignored
3419                }
3420            }
3421            return new Pair<>(IFD_FORMAT_STRING, -1);
3422        }
3423        try {
3424            Long longValue = Long.parseLong(entryValue);
3425            if (longValue >= 0 && longValue <= 65535) {
3426                return new Pair<>(IFD_FORMAT_USHORT, IFD_FORMAT_ULONG);
3427            }
3428            if (longValue < 0) {
3429                return new Pair<>(IFD_FORMAT_SLONG, -1);
3430            }
3431            return new Pair<>(IFD_FORMAT_ULONG, -1);
3432        } catch (NumberFormatException e) {
3433            // Ignored
3434        }
3435        try {
3436            Double.parseDouble(entryValue);
3437            return new Pair<>(IFD_FORMAT_DOUBLE, -1);
3438        } catch (NumberFormatException e) {
3439            // Ignored
3440        }
3441        return new Pair<>(IFD_FORMAT_STRING, -1);
3442    }
3443
3444    // An input stream to parse EXIF data area, which can be written in either little or big endian
3445    // order.
3446    private static class ByteOrderedDataInputStream extends InputStream implements DataInput {
3447        private static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN;
3448        private static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN;
3449
3450        private DataInputStream mDataInputStream;
3451        private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN;
3452        private final int mLength;
3453        private int mPosition;
3454
3455        public ByteOrderedDataInputStream(InputStream in) throws IOException {
3456            mDataInputStream = new DataInputStream(in);
3457            mLength = mDataInputStream.available();
3458            mPosition = 0;
3459            mDataInputStream.mark(mLength);
3460        }
3461
3462        public ByteOrderedDataInputStream(byte[] bytes) throws IOException {
3463            this(new ByteArrayInputStream(bytes));
3464        }
3465
3466        public void setByteOrder(ByteOrder byteOrder) {
3467            mByteOrder = byteOrder;
3468        }
3469
3470        public void seek(long byteCount) throws IOException {
3471            if (mPosition > byteCount) {
3472                mPosition = 0;
3473                mDataInputStream.reset();
3474                mDataInputStream.mark(mLength);
3475            } else {
3476                byteCount -= mPosition;
3477            }
3478
3479            if (skipBytes((int) byteCount) != (int) byteCount) {
3480                throw new IOException("Couldn't seek up to the byteCount");
3481            }
3482        }
3483
3484        public int peek() {
3485            return mPosition;
3486        }
3487
3488        @Override
3489        public int available() throws IOException {
3490            return mDataInputStream.available();
3491        }
3492
3493        @Override
3494        public int read() throws IOException {
3495            ++mPosition;
3496            return mDataInputStream.read();
3497        }
3498
3499        @Override
3500        public int read(byte[] b, int off, int len) throws IOException {
3501            int bytesRead = mDataInputStream.read(b, off, len);
3502            mPosition += bytesRead;
3503            return bytesRead;
3504        }
3505
3506        @Override
3507        public int readUnsignedByte() throws IOException {
3508            ++mPosition;
3509            return mDataInputStream.readUnsignedByte();
3510        }
3511
3512        @Override
3513        public String readLine() throws IOException {
3514            Log.d(TAG, "Currently unsupported");
3515            return null;
3516        }
3517
3518        @Override
3519        public boolean readBoolean() throws IOException {
3520            ++mPosition;
3521            return mDataInputStream.readBoolean();
3522        }
3523
3524        @Override
3525        public char readChar() throws IOException {
3526            mPosition += 2;
3527            return mDataInputStream.readChar();
3528        }
3529
3530        @Override
3531        public String readUTF() throws IOException {
3532            mPosition += 2;
3533            return mDataInputStream.readUTF();
3534        }
3535
3536        @Override
3537        public void readFully(byte[] buffer, int offset, int length) throws IOException {
3538            mPosition += length;
3539            if (mPosition > mLength) {
3540                throw new EOFException();
3541            }
3542            if (mDataInputStream.read(buffer, offset, length) != length) {
3543                throw new IOException("Couldn't read up to the length of buffer");
3544            }
3545        }
3546
3547        @Override
3548        public void readFully(byte[] buffer) throws IOException {
3549            mPosition += buffer.length;
3550            if (mPosition > mLength) {
3551                throw new EOFException();
3552            }
3553            if (mDataInputStream.read(buffer, 0, buffer.length) != buffer.length) {
3554                throw new IOException("Couldn't read up to the length of buffer");
3555            }
3556        }
3557
3558        @Override
3559        public byte readByte() throws IOException {
3560            ++mPosition;
3561            if (mPosition > mLength) {
3562                throw new EOFException();
3563            }
3564            int ch = mDataInputStream.read();
3565            if (ch < 0) {
3566                throw new EOFException();
3567            }
3568            return (byte) ch;
3569        }
3570
3571        @Override
3572        public short readShort() throws IOException {
3573            mPosition += 2;
3574            if (mPosition > mLength) {
3575                throw new EOFException();
3576            }
3577            int ch1 = mDataInputStream.read();
3578            int ch2 = mDataInputStream.read();
3579            if ((ch1 | ch2) < 0) {
3580                throw new EOFException();
3581            }
3582            if (mByteOrder == LITTLE_ENDIAN) {
3583                return (short) ((ch2 << 8) + (ch1));
3584            } else if (mByteOrder == BIG_ENDIAN) {
3585                return (short) ((ch1 << 8) + (ch2));
3586            }
3587            throw new IOException("Invalid byte order: " + mByteOrder);
3588        }
3589
3590        @Override
3591        public int readInt() throws IOException {
3592            mPosition += 4;
3593            if (mPosition > mLength) {
3594                throw new EOFException();
3595            }
3596            int ch1 = mDataInputStream.read();
3597            int ch2 = mDataInputStream.read();
3598            int ch3 = mDataInputStream.read();
3599            int ch4 = mDataInputStream.read();
3600            if ((ch1 | ch2 | ch3 | ch4) < 0) {
3601                throw new EOFException();
3602            }
3603            if (mByteOrder == LITTLE_ENDIAN) {
3604                return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1);
3605            } else if (mByteOrder == BIG_ENDIAN) {
3606                return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4);
3607            }
3608            throw new IOException("Invalid byte order: " + mByteOrder);
3609        }
3610
3611        @Override
3612        public int skipBytes(int byteCount) throws IOException {
3613            int totalSkip = Math.min(byteCount, mLength - mPosition);
3614            int skipped = 0;
3615            while (skipped < totalSkip) {
3616                skipped += mDataInputStream.skipBytes(totalSkip - skipped);
3617            }
3618            mPosition += skipped;
3619            return skipped;
3620        }
3621
3622        public int readUnsignedShort() throws IOException {
3623            mPosition += 2;
3624            if (mPosition > mLength) {
3625                throw new EOFException();
3626            }
3627            int ch1 = mDataInputStream.read();
3628            int ch2 = mDataInputStream.read();
3629            if ((ch1 | ch2) < 0) {
3630                throw new EOFException();
3631            }
3632            if (mByteOrder == LITTLE_ENDIAN) {
3633                return ((ch2 << 8) + (ch1));
3634            } else if (mByteOrder == BIG_ENDIAN) {
3635                return ((ch1 << 8) + (ch2));
3636            }
3637            throw new IOException("Invalid byte order: " + mByteOrder);
3638        }
3639
3640        public long readUnsignedInt() throws IOException {
3641            return readInt() & 0xffffffffL;
3642        }
3643
3644        @Override
3645        public long readLong() throws IOException {
3646            mPosition += 8;
3647            if (mPosition > mLength) {
3648                throw new EOFException();
3649            }
3650            int ch1 = mDataInputStream.read();
3651            int ch2 = mDataInputStream.read();
3652            int ch3 = mDataInputStream.read();
3653            int ch4 = mDataInputStream.read();
3654            int ch5 = mDataInputStream.read();
3655            int ch6 = mDataInputStream.read();
3656            int ch7 = mDataInputStream.read();
3657            int ch8 = mDataInputStream.read();
3658            if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) {
3659                throw new EOFException();
3660            }
3661            if (mByteOrder == LITTLE_ENDIAN) {
3662                return (((long) ch8 << 56) + ((long) ch7 << 48) + ((long) ch6 << 40)
3663                        + ((long) ch5 << 32) + ((long) ch4 << 24) + ((long) ch3 << 16)
3664                        + ((long) ch2 << 8) + (long) ch1);
3665            } else if (mByteOrder == BIG_ENDIAN) {
3666                return (((long) ch1 << 56) + ((long) ch2 << 48) + ((long) ch3 << 40)
3667                        + ((long) ch4 << 32) + ((long) ch5 << 24) + ((long) ch6 << 16)
3668                        + ((long) ch7 << 8) + (long) ch8);
3669            }
3670            throw new IOException("Invalid byte order: " + mByteOrder);
3671        }
3672
3673        @Override
3674        public float readFloat() throws IOException {
3675            return Float.intBitsToFloat(readInt());
3676        }
3677
3678        @Override
3679        public double readDouble() throws IOException {
3680            return Double.longBitsToDouble(readLong());
3681        }
3682    }
3683
3684    // An output stream to write EXIF data area, which can be written in either little or big endian
3685    // order.
3686    private static class ByteOrderedDataOutputStream extends FilterOutputStream {
3687        private final OutputStream mOutputStream;
3688        private ByteOrder mByteOrder;
3689
3690        public ByteOrderedDataOutputStream(OutputStream out, ByteOrder byteOrder) {
3691            super(out);
3692            mOutputStream = out;
3693            mByteOrder = byteOrder;
3694        }
3695
3696        public void setByteOrder(ByteOrder byteOrder) {
3697            mByteOrder = byteOrder;
3698        }
3699
3700        public void write(byte[] bytes) throws IOException {
3701            mOutputStream.write(bytes);
3702        }
3703
3704        public void write(byte[] bytes, int offset, int length) throws IOException {
3705            mOutputStream.write(bytes, offset, length);
3706        }
3707
3708        public void writeByte(int val) throws IOException {
3709            mOutputStream.write(val);
3710        }
3711
3712        public void writeShort(short val) throws IOException {
3713            if (mByteOrder == ByteOrder.LITTLE_ENDIAN) {
3714                mOutputStream.write((val >>> 0) & 0xFF);
3715                mOutputStream.write((val >>> 8) & 0xFF);
3716            } else if (mByteOrder == ByteOrder.BIG_ENDIAN) {
3717                mOutputStream.write((val >>> 8) & 0xFF);
3718                mOutputStream.write((val >>> 0) & 0xFF);
3719            }
3720        }
3721
3722        public void writeInt(int val) throws IOException {
3723            if (mByteOrder == ByteOrder.LITTLE_ENDIAN) {
3724                mOutputStream.write((val >>> 0) & 0xFF);
3725                mOutputStream.write((val >>> 8) & 0xFF);
3726                mOutputStream.write((val >>> 16) & 0xFF);
3727                mOutputStream.write((val >>> 24) & 0xFF);
3728            } else if (mByteOrder == ByteOrder.BIG_ENDIAN) {
3729                mOutputStream.write((val >>> 24) & 0xFF);
3730                mOutputStream.write((val >>> 16) & 0xFF);
3731                mOutputStream.write((val >>> 8) & 0xFF);
3732                mOutputStream.write((val >>> 0) & 0xFF);
3733            }
3734        }
3735
3736        public void writeUnsignedShort(int val) throws IOException {
3737            writeShort((short) val);
3738        }
3739
3740        public void writeUnsignedInt(long val) throws IOException {
3741            writeInt((int) val);
3742        }
3743    }
3744
3745    // Swaps image data based on image size
3746    private void swapBasedOnImageSize(@IfdType int firstIfdType, @IfdType int secondIfdType)
3747            throws IOException {
3748        if (mAttributes[firstIfdType].isEmpty() || mAttributes[secondIfdType].isEmpty()) {
3749            if (DEBUG) {
3750                Log.d(TAG, "Cannot perform swap since only one image data exists");
3751            }
3752            return;
3753        }
3754
3755        ExifAttribute firstImageLengthAttribute =
3756                (ExifAttribute) mAttributes[firstIfdType].get(TAG_IMAGE_LENGTH);
3757        ExifAttribute firstImageWidthAttribute =
3758                (ExifAttribute) mAttributes[firstIfdType].get(TAG_IMAGE_WIDTH);
3759        ExifAttribute secondImageLengthAttribute =
3760                (ExifAttribute) mAttributes[secondIfdType].get(TAG_IMAGE_LENGTH);
3761        ExifAttribute secondImageWidthAttribute =
3762                (ExifAttribute) mAttributes[secondIfdType].get(TAG_IMAGE_WIDTH);
3763
3764        if (firstImageLengthAttribute == null || firstImageWidthAttribute == null) {
3765            if (DEBUG) {
3766                Log.d(TAG, "First image does not contain valid size information");
3767            }
3768        } else if (secondImageLengthAttribute == null || secondImageWidthAttribute == null) {
3769            if (DEBUG) {
3770                Log.d(TAG, "Second image does not contain valid size information");
3771            }
3772        } else {
3773            int firstImageLengthValue = firstImageLengthAttribute.getIntValue(mExifByteOrder);
3774            int firstImageWidthValue = firstImageWidthAttribute.getIntValue(mExifByteOrder);
3775            int secondImageLengthValue = secondImageLengthAttribute.getIntValue(mExifByteOrder);
3776            int secondImageWidthValue = secondImageWidthAttribute.getIntValue(mExifByteOrder);
3777
3778            if (firstImageLengthValue < secondImageLengthValue &&
3779                    firstImageWidthValue < secondImageWidthValue) {
3780                HashMap tempMap = mAttributes[firstIfdType];
3781                mAttributes[firstIfdType] = mAttributes[secondIfdType];
3782                mAttributes[secondIfdType] = tempMap;
3783            }
3784        }
3785    }
3786
3787    /**
3788     * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null.
3789     */
3790    private static void closeQuietly(Closeable closeable) {
3791        if (closeable != null) {
3792            try {
3793                closeable.close();
3794            } catch (RuntimeException rethrown) {
3795                throw rethrown;
3796            } catch (Exception ignored) {
3797            }
3798        }
3799    }
3800
3801    /**
3802     * Copies all of the bytes from {@code in} to {@code out}. Neither stream is closed.
3803     * Returns the total number of bytes transferred.
3804     */
3805    private static int copy(InputStream in, OutputStream out) throws IOException {
3806        int total = 0;
3807        byte[] buffer = new byte[8192];
3808        int c;
3809        while ((c = in.read(buffer)) != -1) {
3810            total += c;
3811            out.write(buffer, 0, c);
3812        }
3813        return total;
3814    }
3815
3816    /**
3817     * Convert given int[] to long[]. If long[] is given, just return it.
3818     * Return null for other types of input.
3819     */
3820    private static long[] convertToLongArray(Object inputObj) {
3821        if (inputObj instanceof int[]) {
3822            int[] input = (int[]) inputObj;
3823            long[] result = new long[input.length];
3824            for (int i = 0; i < input.length; i++) {
3825                result[i] = input[i];
3826            }
3827            return result;
3828        } else if (inputObj instanceof long[]) {
3829            return (long[]) inputObj;
3830        }
3831        return null;
3832    }
3833}
3834