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