1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.support.media; 18 19import android.content.res.AssetManager; 20import android.graphics.Bitmap; 21import android.graphics.BitmapFactory; 22import android.support.annotation.IntDef; 23import android.support.annotation.NonNull; 24import android.util.Log; 25import android.util.Pair; 26 27import java.io.BufferedInputStream; 28import java.io.ByteArrayInputStream; 29import java.io.Closeable; 30import java.io.DataInput; 31import java.io.DataInputStream; 32import java.io.EOFException; 33import java.io.File; 34import java.io.FileInputStream; 35import java.io.FileNotFoundException; 36import java.io.FileOutputStream; 37import java.io.FilterOutputStream; 38import java.io.IOException; 39import java.io.InputStream; 40import java.io.OutputStream; 41import java.lang.annotation.Retention; 42import java.lang.annotation.RetentionPolicy; 43import java.nio.ByteBuffer; 44import java.nio.ByteOrder; 45import java.nio.charset.Charset; 46import java.text.ParsePosition; 47import java.text.SimpleDateFormat; 48import java.util.Arrays; 49import java.util.Date; 50import java.util.HashMap; 51import java.util.HashSet; 52import java.util.Map; 53import java.util.TimeZone; 54import java.util.regex.Matcher; 55import java.util.regex.Pattern; 56 57/** 58 * This is a class for reading and writing Exif tags in a JPEG file or a RAW image file. 59 * <p> 60 * Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW and RAF. 61 * <p> 62 * Attribute mutation is supported for JPEG image files. 63 */ 64public class ExifInterface { 65 private static final String TAG = "ExifInterface"; 66 private static final boolean DEBUG = false; 67 68 // The Exif tag names. See Tiff 6.0 Section 3 and Section 8. 69 /** Type is String. */ 70 public static final String TAG_ARTIST = "Artist"; 71 /** Type is int. */ 72 public static final String TAG_BITS_PER_SAMPLE = "BitsPerSample"; 73 /** Type is int. */ 74 public static final String TAG_COMPRESSION = "Compression"; 75 /** Type is String. */ 76 public static final String TAG_COPYRIGHT = "Copyright"; 77 /** Type is String. */ 78 public static final String TAG_DATETIME = "DateTime"; 79 /** Type is String. */ 80 public static final String TAG_IMAGE_DESCRIPTION = "ImageDescription"; 81 /** Type is int. */ 82 public static final String TAG_IMAGE_LENGTH = "ImageLength"; 83 /** Type is int. */ 84 public static final String TAG_IMAGE_WIDTH = "ImageWidth"; 85 /** Type is int. */ 86 public static final String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat"; 87 /** Type is int. */ 88 public static final String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength"; 89 /** Type is String. */ 90 public static final String TAG_MAKE = "Make"; 91 /** Type is String. */ 92 public static final String TAG_MODEL = "Model"; 93 /** Type is int. */ 94 public static final String TAG_ORIENTATION = "Orientation"; 95 /** Type is int. */ 96 public static final String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation"; 97 /** Type is int. */ 98 public static final String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration"; 99 /** Type is rational. */ 100 public static final String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities"; 101 /** Type is rational. */ 102 public static final String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite"; 103 /** Type is int. */ 104 public static final String TAG_RESOLUTION_UNIT = "ResolutionUnit"; 105 /** Type is int. */ 106 public static final String TAG_ROWS_PER_STRIP = "RowsPerStrip"; 107 /** Type is int. */ 108 public static final String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel"; 109 /** Type is String. */ 110 public static final String TAG_SOFTWARE = "Software"; 111 /** Type is int. */ 112 public static final String TAG_STRIP_BYTE_COUNTS = "StripByteCounts"; 113 /** Type is int. */ 114 public static final String TAG_STRIP_OFFSETS = "StripOffsets"; 115 /** Type is int. */ 116 public static final String TAG_TRANSFER_FUNCTION = "TransferFunction"; 117 /** Type is rational. */ 118 public static final String TAG_WHITE_POINT = "WhitePoint"; 119 /** Type is rational. */ 120 public static final String TAG_X_RESOLUTION = "XResolution"; 121 /** Type is rational. */ 122 public static final String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients"; 123 /** Type is int. */ 124 public static final String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning"; 125 /** Type is int. */ 126 public static final String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling"; 127 /** Type is rational. */ 128 public static final String TAG_Y_RESOLUTION = "YResolution"; 129 /** Type is rational. */ 130 public static final String TAG_APERTURE_VALUE = "ApertureValue"; 131 /** Type is rational. */ 132 public static final String TAG_BRIGHTNESS_VALUE = "BrightnessValue"; 133 /** Type is String. */ 134 public static final String TAG_CFA_PATTERN = "CFAPattern"; 135 /** Type is int. */ 136 public static final String TAG_COLOR_SPACE = "ColorSpace"; 137 /** Type is String. */ 138 public static final String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration"; 139 /** Type is rational. */ 140 public static final String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel"; 141 /** Type is int. */ 142 public static final String TAG_CONTRAST = "Contrast"; 143 /** Type is int. */ 144 public static final String TAG_CUSTOM_RENDERED = "CustomRendered"; 145 /** Type is String. */ 146 public static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized"; 147 /** Type is String. */ 148 public static final String TAG_DATETIME_ORIGINAL = "DateTimeOriginal"; 149 /** Type is String. */ 150 public static final String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription"; 151 /** Type is double. */ 152 public static final String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio"; 153 /** Type is String. */ 154 public static final String TAG_EXIF_VERSION = "ExifVersion"; 155 /** Type is double. */ 156 public static final String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue"; 157 /** Type is rational. */ 158 public static final String TAG_EXPOSURE_INDEX = "ExposureIndex"; 159 /** Type is int. */ 160 public static final String TAG_EXPOSURE_MODE = "ExposureMode"; 161 /** Type is int. */ 162 public static final String TAG_EXPOSURE_PROGRAM = "ExposureProgram"; 163 /** Type is double. */ 164 public static final String TAG_EXPOSURE_TIME = "ExposureTime"; 165 /** Type is double. */ 166 public static final String TAG_F_NUMBER = "FNumber"; 167 /** Type is String. */ 168 public static final String TAG_FILE_SOURCE = "FileSource"; 169 /** Type is int. */ 170 public static final String TAG_FLASH = "Flash"; 171 /** Type is rational. */ 172 public static final String TAG_FLASH_ENERGY = "FlashEnergy"; 173 /** Type is String. */ 174 public static final String TAG_FLASHPIX_VERSION = "FlashpixVersion"; 175 /** Type is rational. */ 176 public static final String TAG_FOCAL_LENGTH = "FocalLength"; 177 /** Type is int. */ 178 public static final String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm"; 179 /** Type is int. */ 180 public static final String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit"; 181 /** Type is rational. */ 182 public static final String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution"; 183 /** Type is rational. */ 184 public static final String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution"; 185 /** Type is int. */ 186 public static final String TAG_GAIN_CONTROL = "GainControl"; 187 /** Type is int. */ 188 public static final String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings"; 189 /** Type is String. */ 190 public static final String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID"; 191 /** Type is int. */ 192 public static final String TAG_LIGHT_SOURCE = "LightSource"; 193 /** Type is String. */ 194 public static final String TAG_MAKER_NOTE = "MakerNote"; 195 /** Type is rational. */ 196 public static final String TAG_MAX_APERTURE_VALUE = "MaxApertureValue"; 197 /** Type is int. */ 198 public static final String TAG_METERING_MODE = "MeteringMode"; 199 /** Type is int. */ 200 public static final String TAG_NEW_SUBFILE_TYPE = "NewSubfileType"; 201 /** Type is String. */ 202 public static final String TAG_OECF = "OECF"; 203 /** Type is int. */ 204 public static final String TAG_PIXEL_X_DIMENSION = "PixelXDimension"; 205 /** Type is int. */ 206 public static final String TAG_PIXEL_Y_DIMENSION = "PixelYDimension"; 207 /** Type is String. */ 208 public static final String TAG_RELATED_SOUND_FILE = "RelatedSoundFile"; 209 /** Type is int. */ 210 public static final String TAG_SATURATION = "Saturation"; 211 /** Type is int. */ 212 public static final String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType"; 213 /** Type is String. */ 214 public static final String TAG_SCENE_TYPE = "SceneType"; 215 /** Type is int. */ 216 public static final String TAG_SENSING_METHOD = "SensingMethod"; 217 /** Type is int. */ 218 public static final String TAG_SHARPNESS = "Sharpness"; 219 /** Type is rational. */ 220 public static final String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue"; 221 /** Type is String. */ 222 public static final String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse"; 223 /** Type is String. */ 224 public static final String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity"; 225 /** Type is int. */ 226 public static final String TAG_SUBFILE_TYPE = "SubfileType"; 227 /** Type is String. */ 228 public static final String TAG_SUBSEC_TIME = "SubSecTime"; 229 /** Type is String. */ 230 public static final String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized"; 231 /** Type is String. */ 232 public static final String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal"; 233 /** Type is int. */ 234 public static final String TAG_SUBJECT_AREA = "SubjectArea"; 235 /** Type is double. */ 236 public static final String TAG_SUBJECT_DISTANCE = "SubjectDistance"; 237 /** Type is int. */ 238 public static final String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange"; 239 /** Type is int. */ 240 public static final String TAG_SUBJECT_LOCATION = "SubjectLocation"; 241 /** Type is String. */ 242 public static final String TAG_USER_COMMENT = "UserComment"; 243 /** Type is int. */ 244 public static final String TAG_WHITE_BALANCE = "WhiteBalance"; 245 /** 246 * The altitude (in meters) based on the reference in TAG_GPS_ALTITUDE_REF. 247 * Type is rational. 248 */ 249 public static final String TAG_GPS_ALTITUDE = "GPSAltitude"; 250 /** 251 * 0 if the altitude is above sea level. 1 if the altitude is below sea 252 * level. Type is int. 253 */ 254 public static final String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef"; 255 /** Type is String. */ 256 public static final String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation"; 257 /** Type is rational. */ 258 public static final String TAG_GPS_DOP = "GPSDOP"; 259 /** Type is String. */ 260 public static final String TAG_GPS_DATESTAMP = "GPSDateStamp"; 261 /** Type is rational. */ 262 public static final String TAG_GPS_DEST_BEARING = "GPSDestBearing"; 263 /** Type is String. */ 264 public static final String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef"; 265 /** Type is rational. */ 266 public static final String TAG_GPS_DEST_DISTANCE = "GPSDestDistance"; 267 /** Type is String. */ 268 public static final String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef"; 269 /** Type is rational. */ 270 public static final String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude"; 271 /** Type is String. */ 272 public static final String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef"; 273 /** Type is rational. */ 274 public static final String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude"; 275 /** Type is String. */ 276 public static final String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef"; 277 /** Type is int. */ 278 public static final String TAG_GPS_DIFFERENTIAL = "GPSDifferential"; 279 /** Type is rational. */ 280 public static final String TAG_GPS_IMG_DIRECTION = "GPSImgDirection"; 281 /** Type is String. */ 282 public static final String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef"; 283 /** Type is rational. Format is "num1/denom1,num2/denom2,num3/denom3". */ 284 public static final String TAG_GPS_LATITUDE = "GPSLatitude"; 285 /** Type is String. */ 286 public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef"; 287 /** Type is rational. Format is "num1/denom1,num2/denom2,num3/denom3". */ 288 public static final String TAG_GPS_LONGITUDE = "GPSLongitude"; 289 /** Type is String. */ 290 public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef"; 291 /** Type is String. */ 292 public static final String TAG_GPS_MAP_DATUM = "GPSMapDatum"; 293 /** Type is String. */ 294 public static final String TAG_GPS_MEASURE_MODE = "GPSMeasureMode"; 295 /** Type is String. Name of GPS processing method used for location finding. */ 296 public static final String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod"; 297 /** Type is String. */ 298 public static final String TAG_GPS_SATELLITES = "GPSSatellites"; 299 /** Type is rational. */ 300 public static final String TAG_GPS_SPEED = "GPSSpeed"; 301 /** Type is String. */ 302 public static final String TAG_GPS_SPEED_REF = "GPSSpeedRef"; 303 /** Type is String. */ 304 public static final String TAG_GPS_STATUS = "GPSStatus"; 305 /** Type is String. Format is "hh:mm:ss". */ 306 public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp"; 307 /** Type is rational. */ 308 public static final String TAG_GPS_TRACK = "GPSTrack"; 309 /** Type is String. */ 310 public static final String TAG_GPS_TRACK_REF = "GPSTrackRef"; 311 /** Type is String. */ 312 public static final String TAG_GPS_VERSION_ID = "GPSVersionID"; 313 /** Type is String. */ 314 public static final String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex"; 315 /** Type is int. */ 316 public static final String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength"; 317 /** Type is int. */ 318 public static final String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth"; 319 /** Type is int. DNG Specification 1.4.0.0. Section 4 */ 320 public static final String TAG_DNG_VERSION = "DNGVersion"; 321 /** Type is int. DNG Specification 1.4.0.0. Section 4 */ 322 public static final String TAG_DEFAULT_CROP_SIZE = "DefaultCropSize"; 323 /** Type is undefined. See Olympus MakerNote tags in http://www.exiv2.org/tags-olympus.html. */ 324 public static final String TAG_ORF_THUMBNAIL_IMAGE = "ThumbnailImage"; 325 /** Type is int. See Olympus Camera Settings tags in http://www.exiv2.org/tags-olympus.html. */ 326 public static final String TAG_ORF_PREVIEW_IMAGE_START = "PreviewImageStart"; 327 /** Type is int. See Olympus Camera Settings tags in http://www.exiv2.org/tags-olympus.html. */ 328 public static final String TAG_ORF_PREVIEW_IMAGE_LENGTH = "PreviewImageLength"; 329 /** Type is int. See Olympus Image Processing tags in http://www.exiv2.org/tags-olympus.html. */ 330 public static final String TAG_ORF_ASPECT_FRAME = "AspectFrame"; 331 /** 332 * Type is int. See PanasonicRaw tags in 333 * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html 334 */ 335 public static final String TAG_RW2_SENSOR_BOTTOM_BORDER = "SensorBottomBorder"; 336 /** 337 * Type is int. See PanasonicRaw tags in 338 * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html 339 */ 340 public static final String TAG_RW2_SENSOR_LEFT_BORDER = "SensorLeftBorder"; 341 /** 342 * Type is int. See PanasonicRaw tags in 343 * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html 344 */ 345 public static final String TAG_RW2_SENSOR_RIGHT_BORDER = "SensorRightBorder"; 346 /** 347 * Type is int. See PanasonicRaw tags in 348 * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html 349 */ 350 public static final String TAG_RW2_SENSOR_TOP_BORDER = "SensorTopBorder"; 351 /** 352 * Type is int. See PanasonicRaw tags in 353 * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html 354 */ 355 public static final String TAG_RW2_ISO = "ISO"; 356 /** 357 * Type is undefined. See PanasonicRaw tags in 358 * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html 359 */ 360 public static final String TAG_RW2_JPG_FROM_RAW = "JpgFromRaw"; 361 362 /** 363 * Private tags used for pointing the other IFD offsets. 364 * The types of the following tags are int. 365 * See JEITA CP-3451C Section 4.6.3: Exif-specific IFD. 366 * For SubIFD, see Note 1 of Adobe PageMaker® 6.0 TIFF Technical Notes. 367 */ 368 private static final String TAG_EXIF_IFD_POINTER = "ExifIFDPointer"; 369 private static final String TAG_GPS_INFO_IFD_POINTER = "GPSInfoIFDPointer"; 370 private static final String TAG_INTEROPERABILITY_IFD_POINTER = "InteroperabilityIFDPointer"; 371 private static final String TAG_SUB_IFD_POINTER = "SubIFDPointer"; 372 // Proprietary pointer tags used for ORF files. 373 // See http://www.exiv2.org/tags-olympus.html 374 private static final String TAG_ORF_CAMERA_SETTINGS_IFD_POINTER = "CameraSettingsIFDPointer"; 375 private static final String TAG_ORF_IMAGE_PROCESSING_IFD_POINTER = "ImageProcessingIFDPointer"; 376 377 // Private tags used for thumbnail information. 378 private static final String TAG_HAS_THUMBNAIL = "HasThumbnail"; 379 private static final String TAG_THUMBNAIL_OFFSET = "ThumbnailOffset"; 380 private static final String TAG_THUMBNAIL_LENGTH = "ThumbnailLength"; 381 private static final String TAG_THUMBNAIL_DATA = "ThumbnailData"; 382 private static final int MAX_THUMBNAIL_SIZE = 512; 383 384 // Constants used for the Orientation Exif tag. 385 public static final int ORIENTATION_UNDEFINED = 0; 386 public static final int ORIENTATION_NORMAL = 1; 387 public static final int ORIENTATION_FLIP_HORIZONTAL = 2; // left right reversed mirror 388 public static final int ORIENTATION_ROTATE_180 = 3; 389 public static final int ORIENTATION_FLIP_VERTICAL = 4; // upside down mirror 390 // flipped about top-left <--> bottom-right axis 391 public static final int ORIENTATION_TRANSPOSE = 5; 392 public static final int ORIENTATION_ROTATE_90 = 6; // rotate 90 cw to right it 393 // flipped about top-right <--> bottom-left axis 394 public static final int ORIENTATION_TRANSVERSE = 7; 395 public static final int ORIENTATION_ROTATE_270 = 8; // rotate 270 to right it 396 397 // Constants used for white balance 398 public static final int WHITEBALANCE_AUTO = 0; 399 public static final int WHITEBALANCE_MANUAL = 1; 400 401 // Maximum size for checking file type signature (see image_type_recognition_lite.cc) 402 private static final int SIGNATURE_CHECK_SIZE = 5000; 403 404 private static final byte[] JPEG_SIGNATURE = new byte[] {(byte) 0xff, (byte) 0xd8, (byte) 0xff}; 405 private static final String RAF_SIGNATURE = "FUJIFILMCCD-RAW"; 406 private static final int RAF_OFFSET_TO_JPEG_IMAGE_OFFSET = 84; 407 private static final int RAF_INFO_SIZE = 160; 408 private static final int RAF_JPEG_LENGTH_VALUE_SIZE = 4; 409 410 // See http://fileformats.archiveteam.org/wiki/Olympus_ORF 411 private static final short ORF_SIGNATURE_1 = 0x4f52; 412 private static final short ORF_SIGNATURE_2 = 0x5352; 413 // There are two formats for Olympus Makernote Headers. Each has different identifiers and 414 // offsets to the actual data. 415 // See http://www.exiv2.org/makernote.html#R1 416 private static final byte[] ORF_MAKER_NOTE_HEADER_1 = new byte[] {(byte) 0x4f, (byte) 0x4c, 417 (byte) 0x59, (byte) 0x4d, (byte) 0x50, (byte) 0x00}; // "OLYMP\0" 418 private static final byte[] ORF_MAKER_NOTE_HEADER_2 = new byte[] {(byte) 0x4f, (byte) 0x4c, 419 (byte) 0x59, (byte) 0x4d, (byte) 0x50, (byte) 0x55, (byte) 0x53, (byte) 0x00, 420 (byte) 0x49, (byte) 0x49}; // "OLYMPUS\0II" 421 private static final int ORF_MAKER_NOTE_HEADER_1_SIZE = 8; 422 private static final int ORF_MAKER_NOTE_HEADER_2_SIZE = 12; 423 424 // See http://fileformats.archiveteam.org/wiki/RW2 425 private static final short RW2_SIGNATURE = 0x0055; 426 427 // See http://fileformats.archiveteam.org/wiki/Pentax_PEF 428 private static final String PEF_SIGNATURE = "PENTAX"; 429 // See http://www.exiv2.org/makernote.html#R11 430 private static final int PEF_MAKER_NOTE_SKIP_SIZE = 6; 431 432 private static SimpleDateFormat sFormatter; 433 434 // See Exchangeable image file format for digital still cameras: Exif version 2.2. 435 // The following values are for parsing EXIF data area. There are tag groups in EXIF data area. 436 // They are called "Image File Directory". They have multiple data formats to cover various 437 // image metadata from GPS longitude to camera model name. 438 439 // Types of Exif byte alignments (see JEITA CP-3451C Section 4.5.2) 440 private static final short BYTE_ALIGN_II = 0x4949; // II: Intel order 441 private static final short BYTE_ALIGN_MM = 0x4d4d; // MM: Motorola order 442 443 // TIFF Header Fixed Constant (see JEITA CP-3451C Section 4.5.2) 444 private static final byte START_CODE = 0x2a; // 42 445 private static final int IFD_OFFSET = 8; 446 447 // Formats for the value in IFD entry (See TIFF 6.0 Section 2, "Image File Directory".) 448 private static final int IFD_FORMAT_BYTE = 1; 449 private static final int IFD_FORMAT_STRING = 2; 450 private static final int IFD_FORMAT_USHORT = 3; 451 private static final int IFD_FORMAT_ULONG = 4; 452 private static final int IFD_FORMAT_URATIONAL = 5; 453 private static final int IFD_FORMAT_SBYTE = 6; 454 private static final int IFD_FORMAT_UNDEFINED = 7; 455 private static final int IFD_FORMAT_SSHORT = 8; 456 private static final int IFD_FORMAT_SLONG = 9; 457 private static final int IFD_FORMAT_SRATIONAL = 10; 458 private static final int IFD_FORMAT_SINGLE = 11; 459 private static final int IFD_FORMAT_DOUBLE = 12; 460 // Format indicating a new IFD entry (See Adobe PageMaker® 6.0 TIFF Technical Notes, "New Tag") 461 private static final int IFD_FORMAT_IFD = 13; 462 // Names for the data formats for debugging purpose. 463 private static final String[] IFD_FORMAT_NAMES = new String[] { 464 "", "BYTE", "STRING", "USHORT", "ULONG", "URATIONAL", "SBYTE", "UNDEFINED", "SSHORT", 465 "SLONG", "SRATIONAL", "SINGLE", "DOUBLE" 466 }; 467 // Sizes of the components of each IFD value format 468 private static final int[] IFD_FORMAT_BYTES_PER_FORMAT = new int[] { 469 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 1 470 }; 471 private static final byte[] EXIF_ASCII_PREFIX = new byte[] { 472 0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0 473 }; 474 475 /** 476 * Constants used for Compression tag. 477 * For Value 1, 2, 32773, see TIFF 6.0 Spec Section 3: Bilevel Images, Compression 478 * For Value 6, see TIFF 6.0 Spec Section 22: JPEG Compression, Extensions to Existing Fields 479 * For Value 7, 8, 34892, see DNG Specification 1.4.0.0. Section 3, Compression 480 */ 481 private static final int DATA_UNCOMPRESSED = 1; 482 private static final int DATA_HUFFMAN_COMPRESSED = 2; 483 private static final int DATA_JPEG = 6; 484 private static final int DATA_JPEG_COMPRESSED = 7; 485 private static final int DATA_DEFLATE_ZIP = 8; 486 private static final int DATA_PACK_BITS_COMPRESSED = 32773; 487 private static final int DATA_LOSSY_JPEG = 34892; 488 489 /** 490 * Constants used for BitsPerSample tag. 491 * For RGB, see TIFF 6.0 Spec Section 6, Differences from Palette Color Images 492 * For Greyscale, see TIFF 6.0 Spec Section 4, Differences from Bilevel Images 493 */ 494 private static final int[] BITS_PER_SAMPLE_RGB = new int[] { 8, 8, 8 }; 495 private static final int[] BITS_PER_SAMPLE_GREYSCALE_1 = new int[] { 4 }; 496 private static final int[] BITS_PER_SAMPLE_GREYSCALE_2 = new int[] { 8 }; 497 498 /** 499 * Constants used for PhotometricInterpretation tag. 500 * For White/Black, see Section 3, Color. 501 * See TIFF 6.0 Spec Section 22, Minimum Requirements for TIFF with JPEG Compression. 502 */ 503 private static final int PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO = 0; 504 private static final int PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO = 1; 505 private static final int PHOTOMETRIC_INTERPRETATION_RGB = 2; 506 private static final int PHOTOMETRIC_INTERPRETATION_YCBCR = 6; 507 508 /** 509 * Constants used for NewSubfileType tag. 510 * See TIFF 6.0 Spec Section 8 511 * */ 512 private static final int ORIGINAL_RESOLUTION_IMAGE = 0; 513 private static final int REDUCED_RESOLUTION_IMAGE = 1; 514 515 // A class for indicating EXIF rational type. 516 private static class Rational { 517 public final long numerator; 518 public final long denominator; 519 520 private Rational(long numerator, long denominator) { 521 // Handle erroneous case 522 if (denominator == 0) { 523 this.numerator = 0; 524 this.denominator = 1; 525 return; 526 } 527 this.numerator = numerator; 528 this.denominator = denominator; 529 } 530 531 @Override 532 public String toString() { 533 return numerator + "/" + denominator; 534 } 535 536 public double calculate() { 537 return (double) numerator / denominator; 538 } 539 } 540 541 // A class for indicating EXIF attribute. 542 private static class ExifAttribute { 543 public final int format; 544 public final int numberOfComponents; 545 public final byte[] bytes; 546 547 private ExifAttribute(int format, int numberOfComponents, byte[] bytes) { 548 this.format = format; 549 this.numberOfComponents = numberOfComponents; 550 this.bytes = bytes; 551 } 552 553 public static ExifAttribute createUShort(int[] values, ByteOrder byteOrder) { 554 final ByteBuffer buffer = ByteBuffer.wrap( 555 new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_USHORT] * values.length]); 556 buffer.order(byteOrder); 557 for (int value : values) { 558 buffer.putShort((short) value); 559 } 560 return new ExifAttribute(IFD_FORMAT_USHORT, values.length, buffer.array()); 561 } 562 563 public static ExifAttribute createUShort(int value, ByteOrder byteOrder) { 564 return createUShort(new int[] {value}, byteOrder); 565 } 566 567 public static ExifAttribute createULong(long[] values, ByteOrder byteOrder) { 568 final ByteBuffer buffer = ByteBuffer.wrap( 569 new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_ULONG] * values.length]); 570 buffer.order(byteOrder); 571 for (long value : values) { 572 buffer.putInt((int) value); 573 } 574 return new ExifAttribute(IFD_FORMAT_ULONG, values.length, buffer.array()); 575 } 576 577 public static ExifAttribute createULong(long value, ByteOrder byteOrder) { 578 return createULong(new long[] {value}, byteOrder); 579 } 580 581 public static ExifAttribute createSLong(int[] values, ByteOrder byteOrder) { 582 final ByteBuffer buffer = ByteBuffer.wrap( 583 new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_SLONG] * values.length]); 584 buffer.order(byteOrder); 585 for (int value : values) { 586 buffer.putInt(value); 587 } 588 return new ExifAttribute(IFD_FORMAT_SLONG, values.length, buffer.array()); 589 } 590 591 public static ExifAttribute createSLong(int value, ByteOrder byteOrder) { 592 return createSLong(new int[] {value}, byteOrder); 593 } 594 595 public static ExifAttribute createByte(String value) { 596 // Exception for GPSAltitudeRef tag 597 if (value.length() == 1 && value.charAt(0) >= '0' && value.charAt(0) <= '1') { 598 final byte[] bytes = new byte[] { (byte) (value.charAt(0) - '0') }; 599 return new ExifAttribute(IFD_FORMAT_BYTE, bytes.length, bytes); 600 } 601 final byte[] ascii = value.getBytes(ASCII); 602 return new ExifAttribute(IFD_FORMAT_BYTE, ascii.length, ascii); 603 } 604 605 public static ExifAttribute createString(String value) { 606 final byte[] ascii = (value + '\0').getBytes(ASCII); 607 return new ExifAttribute(IFD_FORMAT_STRING, ascii.length, ascii); 608 } 609 610 public static ExifAttribute createURational(Rational[] values, ByteOrder byteOrder) { 611 final ByteBuffer buffer = ByteBuffer.wrap( 612 new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_URATIONAL] * values.length]); 613 buffer.order(byteOrder); 614 for (Rational value : values) { 615 buffer.putInt((int) value.numerator); 616 buffer.putInt((int) value.denominator); 617 } 618 return new ExifAttribute(IFD_FORMAT_URATIONAL, values.length, buffer.array()); 619 } 620 621 public static ExifAttribute createURational(Rational value, ByteOrder byteOrder) { 622 return createURational(new Rational[] {value}, byteOrder); 623 } 624 625 public static ExifAttribute createSRational(Rational[] values, ByteOrder byteOrder) { 626 final ByteBuffer buffer = ByteBuffer.wrap( 627 new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_SRATIONAL] * values.length]); 628 buffer.order(byteOrder); 629 for (Rational value : values) { 630 buffer.putInt((int) value.numerator); 631 buffer.putInt((int) value.denominator); 632 } 633 return new ExifAttribute(IFD_FORMAT_SRATIONAL, values.length, buffer.array()); 634 } 635 636 public static ExifAttribute createSRational(Rational value, ByteOrder byteOrder) { 637 return createSRational(new Rational[] {value}, byteOrder); 638 } 639 640 public static ExifAttribute createDouble(double[] values, ByteOrder byteOrder) { 641 final ByteBuffer buffer = ByteBuffer.wrap( 642 new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_DOUBLE] * values.length]); 643 buffer.order(byteOrder); 644 for (double value : values) { 645 buffer.putDouble(value); 646 } 647 return new ExifAttribute(IFD_FORMAT_DOUBLE, values.length, buffer.array()); 648 } 649 650 public static ExifAttribute createDouble(double value, ByteOrder byteOrder) { 651 return createDouble(new double[] {value}, byteOrder); 652 } 653 654 @Override 655 public String toString() { 656 return "(" + IFD_FORMAT_NAMES[format] + ", data length:" + bytes.length + ")"; 657 } 658 659 private Object getValue(ByteOrder byteOrder) { 660 ByteOrderedDataInputStream inputStream = null; 661 try { 662 inputStream = new ByteOrderedDataInputStream(bytes); 663 inputStream.setByteOrder(byteOrder); 664 switch (format) { 665 case IFD_FORMAT_BYTE: 666 case IFD_FORMAT_SBYTE: { 667 // Exception for GPSAltitudeRef tag 668 if (bytes.length == 1 && bytes[0] >= 0 && bytes[0] <= 1) { 669 return new String(new char[] { (char) (bytes[0] + '0') }); 670 } 671 return new String(bytes, ASCII); 672 } 673 case IFD_FORMAT_UNDEFINED: 674 case IFD_FORMAT_STRING: { 675 int index = 0; 676 if (numberOfComponents >= EXIF_ASCII_PREFIX.length) { 677 boolean same = true; 678 for (int i = 0; i < EXIF_ASCII_PREFIX.length; ++i) { 679 if (bytes[i] != EXIF_ASCII_PREFIX[i]) { 680 same = false; 681 break; 682 } 683 } 684 if (same) { 685 index = EXIF_ASCII_PREFIX.length; 686 } 687 } 688 689 StringBuilder stringBuilder = new StringBuilder(); 690 while (index < numberOfComponents) { 691 int ch = bytes[index]; 692 if (ch == 0) { 693 break; 694 } 695 if (ch >= 32) { 696 stringBuilder.append((char) ch); 697 } else { 698 stringBuilder.append('?'); 699 } 700 ++index; 701 } 702 return stringBuilder.toString(); 703 } 704 case IFD_FORMAT_USHORT: { 705 final int[] values = new int[numberOfComponents]; 706 for (int i = 0; i < numberOfComponents; ++i) { 707 values[i] = inputStream.readUnsignedShort(); 708 } 709 return values; 710 } 711 case IFD_FORMAT_ULONG: { 712 final long[] values = new long[numberOfComponents]; 713 for (int i = 0; i < numberOfComponents; ++i) { 714 values[i] = inputStream.readUnsignedInt(); 715 } 716 return values; 717 } 718 case IFD_FORMAT_URATIONAL: { 719 final Rational[] values = new Rational[numberOfComponents]; 720 for (int i = 0; i < numberOfComponents; ++i) { 721 final long numerator = inputStream.readUnsignedInt(); 722 final long denominator = inputStream.readUnsignedInt(); 723 values[i] = new Rational(numerator, denominator); 724 } 725 return values; 726 } 727 case IFD_FORMAT_SSHORT: { 728 final int[] values = new int[numberOfComponents]; 729 for (int i = 0; i < numberOfComponents; ++i) { 730 values[i] = inputStream.readShort(); 731 } 732 return values; 733 } 734 case IFD_FORMAT_SLONG: { 735 final int[] values = new int[numberOfComponents]; 736 for (int i = 0; i < numberOfComponents; ++i) { 737 values[i] = inputStream.readInt(); 738 } 739 return values; 740 } 741 case IFD_FORMAT_SRATIONAL: { 742 final Rational[] values = new Rational[numberOfComponents]; 743 for (int i = 0; i < numberOfComponents; ++i) { 744 final long numerator = inputStream.readInt(); 745 final long denominator = inputStream.readInt(); 746 values[i] = new Rational(numerator, denominator); 747 } 748 return values; 749 } 750 case IFD_FORMAT_SINGLE: { 751 final double[] values = new double[numberOfComponents]; 752 for (int i = 0; i < numberOfComponents; ++i) { 753 values[i] = inputStream.readFloat(); 754 } 755 return values; 756 } 757 case IFD_FORMAT_DOUBLE: { 758 final double[] values = new double[numberOfComponents]; 759 for (int i = 0; i < numberOfComponents; ++i) { 760 values[i] = inputStream.readDouble(); 761 } 762 return values; 763 } 764 default: 765 return null; 766 } 767 } catch (IOException e) { 768 Log.w(TAG, "IOException occurred during reading a value", e); 769 return null; 770 } finally { 771 if (inputStream != null) { 772 try { 773 inputStream.close(); 774 } catch (IOException e) { 775 Log.e(TAG, "IOException occurred while closing InputStream", e); 776 } 777 } 778 } 779 } 780 781 public double getDoubleValue(ByteOrder byteOrder) { 782 Object value = getValue(byteOrder); 783 if (value == null) { 784 throw new NumberFormatException("NULL can't be converted to a double value"); 785 } 786 if (value instanceof String) { 787 return Double.parseDouble((String) value); 788 } 789 if (value instanceof long[]) { 790 long[] array = (long[]) value; 791 if (array.length == 1) { 792 return array[0]; 793 } 794 throw new NumberFormatException("There are more than one component"); 795 } 796 if (value instanceof int[]) { 797 int[] array = (int[]) value; 798 if (array.length == 1) { 799 return array[0]; 800 } 801 throw new NumberFormatException("There are more than one component"); 802 } 803 if (value instanceof double[]) { 804 double[] array = (double[]) value; 805 if (array.length == 1) { 806 return array[0]; 807 } 808 throw new NumberFormatException("There are more than one component"); 809 } 810 if (value instanceof Rational[]) { 811 Rational[] array = (Rational[]) value; 812 if (array.length == 1) { 813 return array[0].calculate(); 814 } 815 throw new NumberFormatException("There are more than one component"); 816 } 817 throw new NumberFormatException("Couldn't find a double value"); 818 } 819 820 public int getIntValue(ByteOrder byteOrder) { 821 Object value = getValue(byteOrder); 822 if (value == null) { 823 throw new NumberFormatException("NULL can't be converted to a integer value"); 824 } 825 if (value instanceof String) { 826 return Integer.parseInt((String) value); 827 } 828 if (value instanceof long[]) { 829 long[] array = (long[]) value; 830 if (array.length == 1) { 831 return (int) array[0]; 832 } 833 throw new NumberFormatException("There are more than one component"); 834 } 835 if (value instanceof int[]) { 836 int[] array = (int[]) value; 837 if (array.length == 1) { 838 return array[0]; 839 } 840 throw new NumberFormatException("There are more than one component"); 841 } 842 throw new NumberFormatException("Couldn't find a integer value"); 843 } 844 845 public String getStringValue(ByteOrder byteOrder) { 846 Object value = getValue(byteOrder); 847 if (value == null) { 848 return null; 849 } 850 if (value instanceof String) { 851 return (String) value; 852 } 853 854 final StringBuilder stringBuilder = new StringBuilder(); 855 if (value instanceof long[]) { 856 long[] array = (long[]) value; 857 for (int i = 0; i < array.length; ++i) { 858 stringBuilder.append(array[i]); 859 if (i + 1 != array.length) { 860 stringBuilder.append(","); 861 } 862 } 863 return stringBuilder.toString(); 864 } 865 if (value instanceof int[]) { 866 int[] array = (int[]) value; 867 for (int i = 0; i < array.length; ++i) { 868 stringBuilder.append(array[i]); 869 if (i + 1 != array.length) { 870 stringBuilder.append(","); 871 } 872 } 873 return stringBuilder.toString(); 874 } 875 if (value instanceof double[]) { 876 double[] array = (double[]) value; 877 for (int i = 0; i < array.length; ++i) { 878 stringBuilder.append(array[i]); 879 if (i + 1 != array.length) { 880 stringBuilder.append(","); 881 } 882 } 883 return stringBuilder.toString(); 884 } 885 if (value instanceof Rational[]) { 886 Rational[] array = (Rational[]) value; 887 for (int i = 0; i < array.length; ++i) { 888 stringBuilder.append(array[i].numerator); 889 stringBuilder.append('/'); 890 stringBuilder.append(array[i].denominator); 891 if (i + 1 != array.length) { 892 stringBuilder.append(","); 893 } 894 } 895 return stringBuilder.toString(); 896 } 897 return null; 898 } 899 900 public int size() { 901 return IFD_FORMAT_BYTES_PER_FORMAT[format] * numberOfComponents; 902 } 903 } 904 905 // A class for indicating EXIF tag. 906 private static class ExifTag { 907 public final int number; 908 public final String name; 909 public final int primaryFormat; 910 public final int secondaryFormat; 911 912 private ExifTag(String name, int number, int format) { 913 this.name = name; 914 this.number = number; 915 this.primaryFormat = format; 916 this.secondaryFormat = -1; 917 } 918 919 private ExifTag(String name, int number, int primaryFormat, int secondaryFormat) { 920 this.name = name; 921 this.number = number; 922 this.primaryFormat = primaryFormat; 923 this.secondaryFormat = secondaryFormat; 924 } 925 } 926 927 // Primary image IFD TIFF tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels) 928 private static final ExifTag[] IFD_TIFF_TAGS = new ExifTag[] { 929 // For below two, see TIFF 6.0 Spec Section 3: Bilevel Images. 930 new ExifTag(TAG_NEW_SUBFILE_TYPE, 254, IFD_FORMAT_ULONG), 931 new ExifTag(TAG_SUBFILE_TYPE, 255, IFD_FORMAT_ULONG), 932 new ExifTag(TAG_IMAGE_WIDTH, 256, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG), 933 new ExifTag(TAG_IMAGE_LENGTH, 257, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG), 934 new ExifTag(TAG_BITS_PER_SAMPLE, 258, IFD_FORMAT_USHORT), 935 new ExifTag(TAG_COMPRESSION, 259, IFD_FORMAT_USHORT), 936 new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262, IFD_FORMAT_USHORT), 937 new ExifTag(TAG_IMAGE_DESCRIPTION, 270, IFD_FORMAT_STRING), 938 new ExifTag(TAG_MAKE, 271, IFD_FORMAT_STRING), 939 new ExifTag(TAG_MODEL, 272, IFD_FORMAT_STRING), 940 new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG), 941 new ExifTag(TAG_ORIENTATION, 274, IFD_FORMAT_USHORT), 942 new ExifTag(TAG_SAMPLES_PER_PIXEL, 277, IFD_FORMAT_USHORT), 943 new ExifTag(TAG_ROWS_PER_STRIP, 278, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG), 944 new ExifTag(TAG_STRIP_BYTE_COUNTS, 279, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG), 945 new ExifTag(TAG_X_RESOLUTION, 282, IFD_FORMAT_URATIONAL), 946 new ExifTag(TAG_Y_RESOLUTION, 283, IFD_FORMAT_URATIONAL), 947 new ExifTag(TAG_PLANAR_CONFIGURATION, 284, IFD_FORMAT_USHORT), 948 new ExifTag(TAG_RESOLUTION_UNIT, 296, IFD_FORMAT_USHORT), 949 new ExifTag(TAG_TRANSFER_FUNCTION, 301, IFD_FORMAT_USHORT), 950 new ExifTag(TAG_SOFTWARE, 305, IFD_FORMAT_STRING), 951 new ExifTag(TAG_DATETIME, 306, IFD_FORMAT_STRING), 952 new ExifTag(TAG_ARTIST, 315, IFD_FORMAT_STRING), 953 new ExifTag(TAG_WHITE_POINT, 318, IFD_FORMAT_URATIONAL), 954 new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319, IFD_FORMAT_URATIONAL), 955 // See Adobe PageMaker® 6.0 TIFF Technical Notes, Note 1. 956 new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG), 957 new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG), 958 new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG), 959 new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529, IFD_FORMAT_URATIONAL), 960 new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT), 961 new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT), 962 new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL), 963 new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING), 964 new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG), 965 new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG), 966 // RW2 file tags 967 // See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html) 968 new ExifTag(TAG_RW2_SENSOR_TOP_BORDER, 4, IFD_FORMAT_ULONG), 969 new ExifTag(TAG_RW2_SENSOR_LEFT_BORDER, 5, IFD_FORMAT_ULONG), 970 new ExifTag(TAG_RW2_SENSOR_BOTTOM_BORDER, 6, IFD_FORMAT_ULONG), 971 new ExifTag(TAG_RW2_SENSOR_RIGHT_BORDER, 7, IFD_FORMAT_ULONG), 972 new ExifTag(TAG_RW2_ISO, 23, IFD_FORMAT_USHORT), 973 new ExifTag(TAG_RW2_JPG_FROM_RAW, 46, IFD_FORMAT_UNDEFINED) 974 }; 975 976 // Primary image IFD Exif Private tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels) 977 private static final ExifTag[] IFD_EXIF_TAGS = new ExifTag[] { 978 new ExifTag(TAG_EXPOSURE_TIME, 33434, IFD_FORMAT_URATIONAL), 979 new ExifTag(TAG_F_NUMBER, 33437, IFD_FORMAT_URATIONAL), 980 new ExifTag(TAG_EXPOSURE_PROGRAM, 34850, IFD_FORMAT_USHORT), 981 new ExifTag(TAG_SPECTRAL_SENSITIVITY, 34852, IFD_FORMAT_STRING), 982 new ExifTag(TAG_ISO_SPEED_RATINGS, 34855, IFD_FORMAT_USHORT), 983 new ExifTag(TAG_OECF, 34856, IFD_FORMAT_UNDEFINED), 984 new ExifTag(TAG_EXIF_VERSION, 36864, IFD_FORMAT_STRING), 985 new ExifTag(TAG_DATETIME_ORIGINAL, 36867, IFD_FORMAT_STRING), 986 new ExifTag(TAG_DATETIME_DIGITIZED, 36868, IFD_FORMAT_STRING), 987 new ExifTag(TAG_COMPONENTS_CONFIGURATION, 37121, IFD_FORMAT_UNDEFINED), 988 new ExifTag(TAG_COMPRESSED_BITS_PER_PIXEL, 37122, IFD_FORMAT_URATIONAL), 989 new ExifTag(TAG_SHUTTER_SPEED_VALUE, 37377, IFD_FORMAT_SRATIONAL), 990 new ExifTag(TAG_APERTURE_VALUE, 37378, IFD_FORMAT_URATIONAL), 991 new ExifTag(TAG_BRIGHTNESS_VALUE, 37379, IFD_FORMAT_SRATIONAL), 992 new ExifTag(TAG_EXPOSURE_BIAS_VALUE, 37380, IFD_FORMAT_SRATIONAL), 993 new ExifTag(TAG_MAX_APERTURE_VALUE, 37381, IFD_FORMAT_URATIONAL), 994 new ExifTag(TAG_SUBJECT_DISTANCE, 37382, IFD_FORMAT_URATIONAL), 995 new ExifTag(TAG_METERING_MODE, 37383, IFD_FORMAT_USHORT), 996 new ExifTag(TAG_LIGHT_SOURCE, 37384, IFD_FORMAT_USHORT), 997 new ExifTag(TAG_FLASH, 37385, IFD_FORMAT_USHORT), 998 new ExifTag(TAG_FOCAL_LENGTH, 37386, IFD_FORMAT_URATIONAL), 999 new ExifTag(TAG_SUBJECT_AREA, 37396, IFD_FORMAT_USHORT), 1000 new ExifTag(TAG_MAKER_NOTE, 37500, IFD_FORMAT_UNDEFINED), 1001 new ExifTag(TAG_USER_COMMENT, 37510, IFD_FORMAT_UNDEFINED), 1002 new ExifTag(TAG_SUBSEC_TIME, 37520, IFD_FORMAT_STRING), 1003 new ExifTag(TAG_SUBSEC_TIME_ORIGINAL, 37521, IFD_FORMAT_STRING), 1004 new ExifTag(TAG_SUBSEC_TIME_DIGITIZED, 37522, IFD_FORMAT_STRING), 1005 new ExifTag(TAG_FLASHPIX_VERSION, 40960, IFD_FORMAT_UNDEFINED), 1006 new ExifTag(TAG_COLOR_SPACE, 40961, IFD_FORMAT_USHORT), 1007 new ExifTag(TAG_PIXEL_X_DIMENSION, 40962, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG), 1008 new ExifTag(TAG_PIXEL_Y_DIMENSION, 40963, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG), 1009 new ExifTag(TAG_RELATED_SOUND_FILE, 40964, IFD_FORMAT_STRING), 1010 new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG), 1011 new ExifTag(TAG_FLASH_ENERGY, 41483, IFD_FORMAT_URATIONAL), 1012 new ExifTag(TAG_SPATIAL_FREQUENCY_RESPONSE, 41484, IFD_FORMAT_UNDEFINED), 1013 new ExifTag(TAG_FOCAL_PLANE_X_RESOLUTION, 41486, IFD_FORMAT_URATIONAL), 1014 new ExifTag(TAG_FOCAL_PLANE_Y_RESOLUTION, 41487, IFD_FORMAT_URATIONAL), 1015 new ExifTag(TAG_FOCAL_PLANE_RESOLUTION_UNIT, 41488, IFD_FORMAT_USHORT), 1016 new ExifTag(TAG_SUBJECT_LOCATION, 41492, IFD_FORMAT_USHORT), 1017 new ExifTag(TAG_EXPOSURE_INDEX, 41493, IFD_FORMAT_URATIONAL), 1018 new ExifTag(TAG_SENSING_METHOD, 41495, IFD_FORMAT_USHORT), 1019 new ExifTag(TAG_FILE_SOURCE, 41728, IFD_FORMAT_UNDEFINED), 1020 new ExifTag(TAG_SCENE_TYPE, 41729, IFD_FORMAT_UNDEFINED), 1021 new ExifTag(TAG_CFA_PATTERN, 41730, IFD_FORMAT_UNDEFINED), 1022 new ExifTag(TAG_CUSTOM_RENDERED, 41985, IFD_FORMAT_USHORT), 1023 new ExifTag(TAG_EXPOSURE_MODE, 41986, IFD_FORMAT_USHORT), 1024 new ExifTag(TAG_WHITE_BALANCE, 41987, IFD_FORMAT_USHORT), 1025 new ExifTag(TAG_DIGITAL_ZOOM_RATIO, 41988, IFD_FORMAT_URATIONAL), 1026 new ExifTag(TAG_FOCAL_LENGTH_IN_35MM_FILM, 41989, IFD_FORMAT_USHORT), 1027 new ExifTag(TAG_SCENE_CAPTURE_TYPE, 41990, IFD_FORMAT_USHORT), 1028 new ExifTag(TAG_GAIN_CONTROL, 41991, IFD_FORMAT_USHORT), 1029 new ExifTag(TAG_CONTRAST, 41992, IFD_FORMAT_USHORT), 1030 new ExifTag(TAG_SATURATION, 41993, IFD_FORMAT_USHORT), 1031 new ExifTag(TAG_SHARPNESS, 41994, IFD_FORMAT_USHORT), 1032 new ExifTag(TAG_DEVICE_SETTING_DESCRIPTION, 41995, IFD_FORMAT_UNDEFINED), 1033 new ExifTag(TAG_SUBJECT_DISTANCE_RANGE, 41996, IFD_FORMAT_USHORT), 1034 new ExifTag(TAG_IMAGE_UNIQUE_ID, 42016, IFD_FORMAT_STRING), 1035 new ExifTag(TAG_DNG_VERSION, 50706, IFD_FORMAT_BYTE), 1036 new ExifTag(TAG_DEFAULT_CROP_SIZE, 50720, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG) 1037 }; 1038 1039 // Primary image IFD GPS Info tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels) 1040 private static final ExifTag[] IFD_GPS_TAGS = new ExifTag[] { 1041 new ExifTag(TAG_GPS_VERSION_ID, 0, IFD_FORMAT_BYTE), 1042 new ExifTag(TAG_GPS_LATITUDE_REF, 1, IFD_FORMAT_STRING), 1043 new ExifTag(TAG_GPS_LATITUDE, 2, IFD_FORMAT_URATIONAL), 1044 new ExifTag(TAG_GPS_LONGITUDE_REF, 3, IFD_FORMAT_STRING), 1045 new ExifTag(TAG_GPS_LONGITUDE, 4, IFD_FORMAT_URATIONAL), 1046 new ExifTag(TAG_GPS_ALTITUDE_REF, 5, IFD_FORMAT_BYTE), 1047 new ExifTag(TAG_GPS_ALTITUDE, 6, IFD_FORMAT_URATIONAL), 1048 new ExifTag(TAG_GPS_TIMESTAMP, 7, IFD_FORMAT_URATIONAL), 1049 new ExifTag(TAG_GPS_SATELLITES, 8, IFD_FORMAT_STRING), 1050 new ExifTag(TAG_GPS_STATUS, 9, IFD_FORMAT_STRING), 1051 new ExifTag(TAG_GPS_MEASURE_MODE, 10, IFD_FORMAT_STRING), 1052 new ExifTag(TAG_GPS_DOP, 11, IFD_FORMAT_URATIONAL), 1053 new ExifTag(TAG_GPS_SPEED_REF, 12, IFD_FORMAT_STRING), 1054 new ExifTag(TAG_GPS_SPEED, 13, IFD_FORMAT_URATIONAL), 1055 new ExifTag(TAG_GPS_TRACK_REF, 14, IFD_FORMAT_STRING), 1056 new ExifTag(TAG_GPS_TRACK, 15, IFD_FORMAT_URATIONAL), 1057 new ExifTag(TAG_GPS_IMG_DIRECTION_REF, 16, IFD_FORMAT_STRING), 1058 new ExifTag(TAG_GPS_IMG_DIRECTION, 17, IFD_FORMAT_URATIONAL), 1059 new ExifTag(TAG_GPS_MAP_DATUM, 18, IFD_FORMAT_STRING), 1060 new ExifTag(TAG_GPS_DEST_LATITUDE_REF, 19, IFD_FORMAT_STRING), 1061 new ExifTag(TAG_GPS_DEST_LATITUDE, 20, IFD_FORMAT_URATIONAL), 1062 new ExifTag(TAG_GPS_DEST_LONGITUDE_REF, 21, IFD_FORMAT_STRING), 1063 new ExifTag(TAG_GPS_DEST_LONGITUDE, 22, IFD_FORMAT_URATIONAL), 1064 new ExifTag(TAG_GPS_DEST_BEARING_REF, 23, IFD_FORMAT_STRING), 1065 new ExifTag(TAG_GPS_DEST_BEARING, 24, IFD_FORMAT_URATIONAL), 1066 new ExifTag(TAG_GPS_DEST_DISTANCE_REF, 25, IFD_FORMAT_STRING), 1067 new ExifTag(TAG_GPS_DEST_DISTANCE, 26, IFD_FORMAT_URATIONAL), 1068 new ExifTag(TAG_GPS_PROCESSING_METHOD, 27, IFD_FORMAT_UNDEFINED), 1069 new ExifTag(TAG_GPS_AREA_INFORMATION, 28, IFD_FORMAT_UNDEFINED), 1070 new ExifTag(TAG_GPS_DATESTAMP, 29, IFD_FORMAT_STRING), 1071 new ExifTag(TAG_GPS_DIFFERENTIAL, 30, IFD_FORMAT_USHORT) 1072 }; 1073 // Primary image IFD Interoperability tag (See JEITA CP-3451C Section 4.6.8 Tag Support Levels) 1074 private static final ExifTag[] IFD_INTEROPERABILITY_TAGS = new ExifTag[] { 1075 new ExifTag(TAG_INTEROPERABILITY_INDEX, 1, IFD_FORMAT_STRING) 1076 }; 1077 // IFD Thumbnail tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels) 1078 private static final ExifTag[] IFD_THUMBNAIL_TAGS = new ExifTag[] { 1079 // For below two, see TIFF 6.0 Spec Section 3: Bilevel Images. 1080 new ExifTag(TAG_NEW_SUBFILE_TYPE, 254, IFD_FORMAT_ULONG), 1081 new ExifTag(TAG_SUBFILE_TYPE, 255, IFD_FORMAT_ULONG), 1082 new ExifTag(TAG_THUMBNAIL_IMAGE_WIDTH, 256, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG), 1083 new ExifTag(TAG_THUMBNAIL_IMAGE_LENGTH, 257, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG), 1084 new ExifTag(TAG_BITS_PER_SAMPLE, 258, IFD_FORMAT_USHORT), 1085 new ExifTag(TAG_COMPRESSION, 259, IFD_FORMAT_USHORT), 1086 new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262, IFD_FORMAT_USHORT), 1087 new ExifTag(TAG_IMAGE_DESCRIPTION, 270, IFD_FORMAT_STRING), 1088 new ExifTag(TAG_MAKE, 271, IFD_FORMAT_STRING), 1089 new ExifTag(TAG_MODEL, 272, IFD_FORMAT_STRING), 1090 new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG), 1091 new ExifTag(TAG_ORIENTATION, 274, IFD_FORMAT_USHORT), 1092 new ExifTag(TAG_SAMPLES_PER_PIXEL, 277, IFD_FORMAT_USHORT), 1093 new ExifTag(TAG_ROWS_PER_STRIP, 278, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG), 1094 new ExifTag(TAG_STRIP_BYTE_COUNTS, 279, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG), 1095 new ExifTag(TAG_X_RESOLUTION, 282, IFD_FORMAT_URATIONAL), 1096 new ExifTag(TAG_Y_RESOLUTION, 283, IFD_FORMAT_URATIONAL), 1097 new ExifTag(TAG_PLANAR_CONFIGURATION, 284, IFD_FORMAT_USHORT), 1098 new ExifTag(TAG_RESOLUTION_UNIT, 296, IFD_FORMAT_USHORT), 1099 new ExifTag(TAG_TRANSFER_FUNCTION, 301, IFD_FORMAT_USHORT), 1100 new ExifTag(TAG_SOFTWARE, 305, IFD_FORMAT_STRING), 1101 new ExifTag(TAG_DATETIME, 306, IFD_FORMAT_STRING), 1102 new ExifTag(TAG_ARTIST, 315, IFD_FORMAT_STRING), 1103 new ExifTag(TAG_WHITE_POINT, 318, IFD_FORMAT_URATIONAL), 1104 new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319, IFD_FORMAT_URATIONAL), 1105 // See Adobe PageMaker® 6.0 TIFF Technical Notes, Note 1. 1106 new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG), 1107 new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG), 1108 new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG), 1109 new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529, IFD_FORMAT_URATIONAL), 1110 new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT), 1111 new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT), 1112 new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL), 1113 new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING), 1114 new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG), 1115 new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG), 1116 new ExifTag(TAG_DNG_VERSION, 50706, IFD_FORMAT_BYTE), 1117 new ExifTag(TAG_DEFAULT_CROP_SIZE, 50720, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG) 1118 }; 1119 1120 // RAF file tag (See piex.cc line 372) 1121 private static final ExifTag TAG_RAF_IMAGE_SIZE = 1122 new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT); 1123 1124 // ORF file tags (See http://www.exiv2.org/tags-olympus.html) 1125 private static final ExifTag[] ORF_MAKER_NOTE_TAGS = new ExifTag[] { 1126 new ExifTag(TAG_ORF_THUMBNAIL_IMAGE, 256, IFD_FORMAT_UNDEFINED), 1127 new ExifTag(TAG_ORF_CAMERA_SETTINGS_IFD_POINTER, 8224, IFD_FORMAT_ULONG), 1128 new ExifTag(TAG_ORF_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_ULONG) 1129 }; 1130 private static final ExifTag[] ORF_CAMERA_SETTINGS_TAGS = new ExifTag[] { 1131 new ExifTag(TAG_ORF_PREVIEW_IMAGE_START, 257, IFD_FORMAT_ULONG), 1132 new ExifTag(TAG_ORF_PREVIEW_IMAGE_LENGTH, 258, IFD_FORMAT_ULONG) 1133 }; 1134 private static final ExifTag[] ORF_IMAGE_PROCESSING_TAGS = new ExifTag[] { 1135 new ExifTag(TAG_ORF_ASPECT_FRAME, 4371, IFD_FORMAT_USHORT) 1136 }; 1137 // PEF file tag (See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Pentax.html) 1138 private static final ExifTag[] PEF_TAGS = new ExifTag[] { 1139 new ExifTag(TAG_COLOR_SPACE, 55, IFD_FORMAT_USHORT) 1140 }; 1141 1142 // See JEITA CP-3451C Section 4.6.3: Exif-specific IFD. 1143 // The following values are used for indicating pointers to the other Image File Directories. 1144 1145 // Indices of Exif Ifd tag groups 1146 /** @hide */ 1147 @Retention(RetentionPolicy.SOURCE) 1148 @IntDef({IFD_TYPE_PRIMARY, IFD_TYPE_EXIF, IFD_TYPE_GPS, IFD_TYPE_INTEROPERABILITY, 1149 IFD_TYPE_THUMBNAIL, IFD_TYPE_PREVIEW, IFD_TYPE_ORF_MAKER_NOTE, 1150 IFD_TYPE_ORF_CAMERA_SETTINGS, IFD_TYPE_ORF_IMAGE_PROCESSING, IFD_TYPE_PEF}) 1151 public @interface IfdType {} 1152 1153 private static final int IFD_TYPE_PRIMARY = 0; 1154 private static final int IFD_TYPE_EXIF = 1; 1155 private static final int IFD_TYPE_GPS = 2; 1156 private static final int IFD_TYPE_INTEROPERABILITY = 3; 1157 private static final int IFD_TYPE_THUMBNAIL = 4; 1158 private static final int IFD_TYPE_PREVIEW = 5; 1159 private static final int IFD_TYPE_ORF_MAKER_NOTE = 6; 1160 private static final int IFD_TYPE_ORF_CAMERA_SETTINGS = 7; 1161 private static final int IFD_TYPE_ORF_IMAGE_PROCESSING = 8; 1162 private static final int IFD_TYPE_PEF = 9; 1163 1164 // List of Exif tag groups 1165 private static final ExifTag[][] EXIF_TAGS = new ExifTag[][] { 1166 IFD_TIFF_TAGS, IFD_EXIF_TAGS, IFD_GPS_TAGS, IFD_INTEROPERABILITY_TAGS, 1167 IFD_THUMBNAIL_TAGS, IFD_TIFF_TAGS, ORF_MAKER_NOTE_TAGS, ORF_CAMERA_SETTINGS_TAGS, 1168 ORF_IMAGE_PROCESSING_TAGS, PEF_TAGS 1169 }; 1170 // List of tags for pointing to the other image file directory offset. 1171 private static final ExifTag[] EXIF_POINTER_TAGS = new ExifTag[] { 1172 new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG), 1173 new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG), 1174 new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG), 1175 new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG), 1176 new ExifTag(TAG_ORF_CAMERA_SETTINGS_IFD_POINTER, 8224, IFD_FORMAT_BYTE), 1177 new ExifTag(TAG_ORF_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_BYTE) 1178 }; 1179 1180 // Tags for indicating the thumbnail offset and length 1181 private static final ExifTag JPEG_INTERCHANGE_FORMAT_TAG = 1182 new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG); 1183 private static final ExifTag JPEG_INTERCHANGE_FORMAT_LENGTH_TAG = 1184 new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG); 1185 1186 // Mappings from tag number to tag name and each item represents one IFD tag group. 1187 @SuppressWarnings("unchecked") 1188 private static final HashMap<Integer, ExifTag>[] sExifTagMapsForReading = 1189 new HashMap[EXIF_TAGS.length]; 1190 // Mappings from tag name to tag number and each item represents one IFD tag group. 1191 @SuppressWarnings("unchecked") 1192 private static final HashMap<String, ExifTag>[] sExifTagMapsForWriting = 1193 new HashMap[EXIF_TAGS.length]; 1194 private static final HashSet<String> sTagSetForCompatibility = new HashSet<>(Arrays.asList( 1195 TAG_F_NUMBER, TAG_DIGITAL_ZOOM_RATIO, TAG_EXPOSURE_TIME, TAG_SUBJECT_DISTANCE, 1196 TAG_GPS_TIMESTAMP)); 1197 // Mappings from tag number to IFD type for pointer tags. 1198 @SuppressWarnings("unchecked") 1199 private static final HashMap<Integer, Integer> sExifPointerTagMap = new HashMap(); 1200 1201 // See JPEG File Interchange Format Version 1.02. 1202 // The following values are defined for handling JPEG streams. In this implementation, we are 1203 // not only getting information from EXIF but also from some JPEG special segments such as 1204 // MARKER_COM for user comment and MARKER_SOFx for image width and height. 1205 1206 private static final Charset ASCII = Charset.forName("US-ASCII"); 1207 // Identifier for EXIF APP1 segment in JPEG 1208 private static final byte[] IDENTIFIER_EXIF_APP1 = "Exif\0\0".getBytes(ASCII); 1209 // JPEG segment markers, that each marker consumes two bytes beginning with 0xff and ending with 1210 // the indicator. There is no SOF4, SOF8, SOF16 markers in JPEG and SOFx markers indicates start 1211 // of frame(baseline DCT) and the image size info exists in its beginning part. 1212 private static final byte MARKER = (byte) 0xff; 1213 private static final byte MARKER_SOI = (byte) 0xd8; 1214 private static final byte MARKER_SOF0 = (byte) 0xc0; 1215 private static final byte MARKER_SOF1 = (byte) 0xc1; 1216 private static final byte MARKER_SOF2 = (byte) 0xc2; 1217 private static final byte MARKER_SOF3 = (byte) 0xc3; 1218 private static final byte MARKER_SOF5 = (byte) 0xc5; 1219 private static final byte MARKER_SOF6 = (byte) 0xc6; 1220 private static final byte MARKER_SOF7 = (byte) 0xc7; 1221 private static final byte MARKER_SOF9 = (byte) 0xc9; 1222 private static final byte MARKER_SOF10 = (byte) 0xca; 1223 private static final byte MARKER_SOF11 = (byte) 0xcb; 1224 private static final byte MARKER_SOF13 = (byte) 0xcd; 1225 private static final byte MARKER_SOF14 = (byte) 0xce; 1226 private static final byte MARKER_SOF15 = (byte) 0xcf; 1227 private static final byte MARKER_SOS = (byte) 0xda; 1228 private static final byte MARKER_APP1 = (byte) 0xe1; 1229 private static final byte MARKER_COM = (byte) 0xfe; 1230 private static final byte MARKER_EOI = (byte) 0xd9; 1231 1232 // Supported Image File Types 1233 private static final int IMAGE_TYPE_UNKNOWN = 0; 1234 private static final int IMAGE_TYPE_ARW = 1; 1235 private static final int IMAGE_TYPE_CR2 = 2; 1236 private static final int IMAGE_TYPE_DNG = 3; 1237 private static final int IMAGE_TYPE_JPEG = 4; 1238 private static final int IMAGE_TYPE_NEF = 5; 1239 private static final int IMAGE_TYPE_NRW = 6; 1240 private static final int IMAGE_TYPE_ORF = 7; 1241 private static final int IMAGE_TYPE_PEF = 8; 1242 private static final int IMAGE_TYPE_RAF = 9; 1243 private static final int IMAGE_TYPE_RW2 = 10; 1244 private static final int IMAGE_TYPE_SRW = 11; 1245 1246 static { 1247 sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss"); 1248 sFormatter.setTimeZone(TimeZone.getTimeZone("UTC")); 1249 1250 // Build up the hash tables to look up Exif tags for reading Exif tags. 1251 for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) { 1252 sExifTagMapsForReading[ifdType] = new HashMap<>(); 1253 sExifTagMapsForWriting[ifdType] = new HashMap<>(); 1254 for (ExifTag tag : EXIF_TAGS[ifdType]) { 1255 sExifTagMapsForReading[ifdType].put(tag.number, tag); 1256 sExifTagMapsForWriting[ifdType].put(tag.name, tag); 1257 } 1258 } 1259 1260 // Build up the hash table to look up Exif pointer tags. 1261 sExifPointerTagMap.put(EXIF_POINTER_TAGS[0].number, IFD_TYPE_PREVIEW); // 330 1262 sExifPointerTagMap.put(EXIF_POINTER_TAGS[1].number, IFD_TYPE_EXIF); // 34665 1263 sExifPointerTagMap.put(EXIF_POINTER_TAGS[2].number, IFD_TYPE_GPS); // 34853 1264 sExifPointerTagMap.put(EXIF_POINTER_TAGS[3].number, IFD_TYPE_INTEROPERABILITY); // 40965 1265 sExifPointerTagMap.put(EXIF_POINTER_TAGS[4].number, IFD_TYPE_ORF_CAMERA_SETTINGS); // 8224 1266 sExifPointerTagMap.put(EXIF_POINTER_TAGS[5].number, IFD_TYPE_ORF_IMAGE_PROCESSING); // 8256 1267 } 1268 1269 private final String mFilename; 1270 private final AssetManager.AssetInputStream mAssetInputStream; 1271 private int mMimeType; 1272 @SuppressWarnings("unchecked") 1273 private final HashMap<String, ExifAttribute>[] mAttributes = new HashMap[EXIF_TAGS.length]; 1274 private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN; 1275 private boolean mHasThumbnail; 1276 // The following values used for indicating a thumbnail position. 1277 private int mThumbnailOffset; 1278 private int mThumbnailLength; 1279 private byte[] mThumbnailBytes; 1280 private int mThumbnailCompression; 1281 private int mExifOffset; 1282 private int mOrfMakerNoteOffset; 1283 private int mOrfThumbnailOffset; 1284 private int mOrfThumbnailLength; 1285 private int mRw2JpgFromRawOffset; 1286 private boolean mIsSupportedFile; 1287 1288 // Pattern to check non zero timestamp 1289 private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*"); 1290 // Pattern to check gps timestamp 1291 private static final Pattern sGpsTimestampPattern = 1292 Pattern.compile("^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$"); 1293 1294 /** 1295 * Reads Exif tags from the specified image file. 1296 */ 1297 public ExifInterface(String filename) throws IOException { 1298 if (filename == null) { 1299 throw new IllegalArgumentException("filename cannot be null"); 1300 } 1301 FileInputStream in = null; 1302 mAssetInputStream = null; 1303 mFilename = filename; 1304 try { 1305 in = new FileInputStream(filename); 1306 loadAttributes(in); 1307 } finally { 1308 closeQuietly(in); 1309 } 1310 } 1311 1312 /** 1313 * Reads Exif tags from the specified image input stream. Attribute mutation is not supported 1314 * for input streams. The given input stream will proceed its current position. Developers 1315 * should close the input stream after use. This constructor is not intended to be used with 1316 * an input stream that performs any networking operations. 1317 */ 1318 public ExifInterface(InputStream inputStream) throws IOException { 1319 if (inputStream == null) { 1320 throw new IllegalArgumentException("inputStream cannot be null"); 1321 } 1322 mFilename = null; 1323 if (inputStream instanceof AssetManager.AssetInputStream) { 1324 mAssetInputStream = (AssetManager.AssetInputStream) inputStream; 1325 } else { 1326 mAssetInputStream = null; 1327 } 1328 loadAttributes(inputStream); 1329 } 1330 1331 /** 1332 * Returns the EXIF attribute of the specified tag or {@code null} if there is no such tag in 1333 * the image file. 1334 * 1335 * @param tag the name of the tag. 1336 */ 1337 private ExifAttribute getExifAttribute(String tag) { 1338 // Retrieves all tag groups. The value from primary image tag group has a higher priority 1339 // than the value from the thumbnail tag group if there are more than one candidates. 1340 for (int i = 0; i < EXIF_TAGS.length; ++i) { 1341 ExifAttribute value = mAttributes[i].get(tag); 1342 if (value != null) { 1343 return value; 1344 } 1345 } 1346 return null; 1347 } 1348 1349 /** 1350 * Returns the value of the specified tag or {@code null} if there 1351 * is no such tag in the image file. 1352 * 1353 * @param tag the name of the tag. 1354 */ 1355 public String getAttribute(String tag) { 1356 ExifAttribute attribute = getExifAttribute(tag); 1357 if (attribute != null) { 1358 if (!sTagSetForCompatibility.contains(tag)) { 1359 return attribute.getStringValue(mExifByteOrder); 1360 } 1361 if (tag.equals(TAG_GPS_TIMESTAMP)) { 1362 // Convert the rational values to the custom formats for backwards compatibility. 1363 if (attribute.format != IFD_FORMAT_URATIONAL 1364 && attribute.format != IFD_FORMAT_SRATIONAL) { 1365 Log.w(TAG, "GPS Timestamp format is not rational. format=" + attribute.format); 1366 return null; 1367 } 1368 Rational[] array = (Rational[]) attribute.getValue(mExifByteOrder); 1369 if (array == null || array.length != 3) { 1370 Log.w(TAG, "Invalid GPS Timestamp array. array=" + Arrays.toString(array)); 1371 return null; 1372 } 1373 return String.format("%02d:%02d:%02d", 1374 (int) ((float) array[0].numerator / array[0].denominator), 1375 (int) ((float) array[1].numerator / array[1].denominator), 1376 (int) ((float) array[2].numerator / array[2].denominator)); 1377 } 1378 try { 1379 return Double.toString(attribute.getDoubleValue(mExifByteOrder)); 1380 } catch (NumberFormatException e) { 1381 return null; 1382 } 1383 } 1384 return null; 1385 } 1386 1387 /** 1388 * Returns the integer value of the specified tag. If there is no such tag 1389 * in the image file or the value cannot be parsed as integer, return 1390 * <var>defaultValue</var>. 1391 * 1392 * @param tag the name of the tag. 1393 * @param defaultValue the value to return if the tag is not available. 1394 */ 1395 public int getAttributeInt(String tag, int defaultValue) { 1396 ExifAttribute exifAttribute = getExifAttribute(tag); 1397 if (exifAttribute == null) { 1398 return defaultValue; 1399 } 1400 1401 try { 1402 return exifAttribute.getIntValue(mExifByteOrder); 1403 } catch (NumberFormatException e) { 1404 return defaultValue; 1405 } 1406 } 1407 1408 /** 1409 * Returns the double value of the tag that is specified as rational or contains a 1410 * double-formatted value. If there is no such tag in the image file or the value cannot be 1411 * parsed as double, return <var>defaultValue</var>. 1412 * 1413 * @param tag the name of the tag. 1414 * @param defaultValue the value to return if the tag is not available. 1415 */ 1416 public double getAttributeDouble(String tag, double defaultValue) { 1417 ExifAttribute exifAttribute = getExifAttribute(tag); 1418 if (exifAttribute == null) { 1419 return defaultValue; 1420 } 1421 1422 try { 1423 return exifAttribute.getDoubleValue(mExifByteOrder); 1424 } catch (NumberFormatException e) { 1425 return defaultValue; 1426 } 1427 } 1428 1429 /** 1430 * Set the value of the specified tag. 1431 * 1432 * @param tag the name of the tag. 1433 * @param value the value of the tag. 1434 */ 1435 public void setAttribute(String tag, String value) { 1436 // Convert the given value to rational values for backwards compatibility. 1437 if (value != null && sTagSetForCompatibility.contains(tag)) { 1438 if (tag.equals(TAG_GPS_TIMESTAMP)) { 1439 Matcher m = sGpsTimestampPattern.matcher(value); 1440 if (!m.find()) { 1441 Log.w(TAG, "Invalid value for " + tag + " : " + value); 1442 return; 1443 } 1444 value = Integer.parseInt(m.group(1)) + "/1," + Integer.parseInt(m.group(2)) + "/1," 1445 + Integer.parseInt(m.group(3)) + "/1"; 1446 } else { 1447 try { 1448 double doubleValue = Double.parseDouble(value); 1449 value = (long) (doubleValue * 10000L) + "/10000"; 1450 } catch (NumberFormatException e) { 1451 Log.w(TAG, "Invalid value for " + tag + " : " + value); 1452 return; 1453 } 1454 } 1455 } 1456 1457 for (int i = 0 ; i < EXIF_TAGS.length; ++i) { 1458 if (i == IFD_TYPE_THUMBNAIL && !mHasThumbnail) { 1459 continue; 1460 } 1461 final ExifTag exifTag = sExifTagMapsForWriting[i].get(tag); 1462 if (exifTag != null) { 1463 if (value == null) { 1464 mAttributes[i].remove(tag); 1465 continue; 1466 } 1467 Pair<Integer, Integer> guess = guessDataFormat(value); 1468 int dataFormat; 1469 if (exifTag.primaryFormat == guess.first || exifTag.primaryFormat == guess.second) { 1470 dataFormat = exifTag.primaryFormat; 1471 } else if (exifTag.secondaryFormat != -1 && (exifTag.secondaryFormat == guess.first 1472 || exifTag.secondaryFormat == guess.second)) { 1473 dataFormat = exifTag.secondaryFormat; 1474 } else if (exifTag.primaryFormat == IFD_FORMAT_BYTE 1475 || exifTag.primaryFormat == IFD_FORMAT_UNDEFINED 1476 || exifTag.primaryFormat == IFD_FORMAT_STRING) { 1477 dataFormat = exifTag.primaryFormat; 1478 } else { 1479 Log.w(TAG, "Given tag (" + tag + ") value didn't match with one of expected " 1480 + "formats: " + IFD_FORMAT_NAMES[exifTag.primaryFormat] 1481 + (exifTag.secondaryFormat == -1 ? "" : ", " 1482 + IFD_FORMAT_NAMES[exifTag.secondaryFormat]) + " (guess: " 1483 + IFD_FORMAT_NAMES[guess.first] + (guess.second == -1 ? "" : ", " 1484 + IFD_FORMAT_NAMES[guess.second]) + ")"); 1485 continue; 1486 } 1487 switch (dataFormat) { 1488 case IFD_FORMAT_BYTE: { 1489 mAttributes[i].put(tag, ExifAttribute.createByte(value)); 1490 break; 1491 } 1492 case IFD_FORMAT_UNDEFINED: 1493 case IFD_FORMAT_STRING: { 1494 mAttributes[i].put(tag, ExifAttribute.createString(value)); 1495 break; 1496 } 1497 case IFD_FORMAT_USHORT: { 1498 final String[] values = value.split(","); 1499 final int[] intArray = new int[values.length]; 1500 for (int j = 0; j < values.length; ++j) { 1501 intArray[j] = Integer.parseInt(values[j]); 1502 } 1503 mAttributes[i].put(tag, 1504 ExifAttribute.createUShort(intArray, mExifByteOrder)); 1505 break; 1506 } 1507 case IFD_FORMAT_SLONG: { 1508 final String[] values = value.split(","); 1509 final int[] intArray = new int[values.length]; 1510 for (int j = 0; j < values.length; ++j) { 1511 intArray[j] = Integer.parseInt(values[j]); 1512 } 1513 mAttributes[i].put(tag, 1514 ExifAttribute.createSLong(intArray, mExifByteOrder)); 1515 break; 1516 } 1517 case IFD_FORMAT_ULONG: { 1518 final String[] values = value.split(","); 1519 final long[] longArray = new long[values.length]; 1520 for (int j = 0; j < values.length; ++j) { 1521 longArray[j] = Long.parseLong(values[j]); 1522 } 1523 mAttributes[i].put(tag, 1524 ExifAttribute.createULong(longArray, mExifByteOrder)); 1525 break; 1526 } 1527 case IFD_FORMAT_URATIONAL: { 1528 final String[] values = value.split(","); 1529 final Rational[] rationalArray = new Rational[values.length]; 1530 for (int j = 0; j < values.length; ++j) { 1531 final String[] numbers = values[j].split("/"); 1532 rationalArray[j] = new Rational((long) Double.parseDouble(numbers[0]), 1533 (long) Double.parseDouble(numbers[1])); 1534 } 1535 mAttributes[i].put(tag, 1536 ExifAttribute.createURational(rationalArray, mExifByteOrder)); 1537 break; 1538 } 1539 case IFD_FORMAT_SRATIONAL: { 1540 final String[] values = value.split(","); 1541 final Rational[] rationalArray = new Rational[values.length]; 1542 for (int j = 0; j < values.length; ++j) { 1543 final String[] numbers = values[j].split("/"); 1544 rationalArray[j] = new Rational((long) Double.parseDouble(numbers[0]), 1545 (long) Double.parseDouble(numbers[1])); 1546 } 1547 mAttributes[i].put(tag, 1548 ExifAttribute.createSRational(rationalArray, mExifByteOrder)); 1549 break; 1550 } 1551 case IFD_FORMAT_DOUBLE: { 1552 final String[] values = value.split(","); 1553 final double[] doubleArray = new double[values.length]; 1554 for (int j = 0; j < values.length; ++j) { 1555 doubleArray[j] = Double.parseDouble(values[j]); 1556 } 1557 mAttributes[i].put(tag, 1558 ExifAttribute.createDouble(doubleArray, mExifByteOrder)); 1559 break; 1560 } 1561 default: 1562 Log.w(TAG, "Data format isn't one of expected formats: " + dataFormat); 1563 continue; 1564 } 1565 } 1566 } 1567 } 1568 1569 /** 1570 * Update the values of the tags in the tag groups if any value for the tag already was stored. 1571 * 1572 * @param tag the name of the tag. 1573 * @param value the value of the tag in a form of {@link ExifAttribute}. 1574 * @return Returns {@code true} if updating is placed. 1575 */ 1576 private boolean updateAttribute(String tag, ExifAttribute value) { 1577 boolean updated = false; 1578 for (int i = 0 ; i < EXIF_TAGS.length; ++i) { 1579 if (mAttributes[i].containsKey(tag)) { 1580 mAttributes[i].put(tag, value); 1581 updated = true; 1582 } 1583 } 1584 return updated; 1585 } 1586 1587 /** 1588 * Remove any values of the specified tag. 1589 * 1590 * @param tag the name of the tag. 1591 */ 1592 private void removeAttribute(String tag) { 1593 for (int i = 0 ; i < EXIF_TAGS.length; ++i) { 1594 mAttributes[i].remove(tag); 1595 } 1596 } 1597 1598 /** 1599 * This function decides which parser to read the image data according to the given input stream 1600 * type and the content of the input stream. In each case, it reads the first three bytes to 1601 * determine whether the image data format is JPEG or not. 1602 */ 1603 private void loadAttributes(@NonNull InputStream in) throws IOException { 1604 try { 1605 // Initialize mAttributes. 1606 for (int i = 0; i < EXIF_TAGS.length; ++i) { 1607 mAttributes[i] = new HashMap<>(); 1608 } 1609 1610 // Check file type 1611 in = new BufferedInputStream(in, SIGNATURE_CHECK_SIZE); 1612 mMimeType = getMimeType((BufferedInputStream) in); 1613 1614 // Create byte-ordered input stream 1615 ByteOrderedDataInputStream inputStream = new ByteOrderedDataInputStream(in); 1616 1617 switch (mMimeType) { 1618 case IMAGE_TYPE_JPEG: { 1619 getJpegAttributes(inputStream, 0, IFD_TYPE_PRIMARY); // 0 is offset 1620 break; 1621 } 1622 case IMAGE_TYPE_RAF: { 1623 getRafAttributes(inputStream); 1624 break; 1625 } 1626 case IMAGE_TYPE_ORF: { 1627 getOrfAttributes(inputStream); 1628 break; 1629 } 1630 case IMAGE_TYPE_RW2: { 1631 getRw2Attributes(inputStream); 1632 break; 1633 } 1634 case IMAGE_TYPE_ARW: 1635 case IMAGE_TYPE_CR2: 1636 case IMAGE_TYPE_DNG: 1637 case IMAGE_TYPE_NEF: 1638 case IMAGE_TYPE_NRW: 1639 case IMAGE_TYPE_PEF: 1640 case IMAGE_TYPE_SRW: 1641 case IMAGE_TYPE_UNKNOWN: { 1642 getRawAttributes(inputStream); 1643 break; 1644 } 1645 default: { 1646 break; 1647 } 1648 } 1649 // Set thumbnail image offset and length 1650 setThumbnailData(inputStream); 1651 mIsSupportedFile = true; 1652 } catch (IOException e) { 1653 // Ignore exceptions in order to keep the compatibility with the old versions of 1654 // ExifInterface. 1655 mIsSupportedFile = false; 1656 if (DEBUG) { 1657 Log.w(TAG, "Invalid image: ExifInterface got an unsupported image format file" 1658 + "(ExifInterface supports JPEG and some RAW image formats only) " 1659 + "or a corrupted JPEG file to ExifInterface.", e); 1660 } 1661 } finally { 1662 addDefaultValuesForCompatibility(); 1663 1664 if (DEBUG) { 1665 printAttributes(); 1666 } 1667 } 1668 } 1669 1670 // Prints out attributes for debugging. 1671 private void printAttributes() { 1672 for (int i = 0; i < mAttributes.length; ++i) { 1673 Log.d(TAG, "The size of tag group[" + i + "]: " + mAttributes[i].size()); 1674 for (Map.Entry<String, ExifAttribute> entry : mAttributes[i].entrySet()) { 1675 final ExifAttribute tagValue = entry.getValue(); 1676 Log.d(TAG, "tagName: " + entry.getKey() + ", tagType: " + tagValue.toString() 1677 + ", tagValue: '" + tagValue.getStringValue(mExifByteOrder) + "'"); 1678 } 1679 } 1680 } 1681 1682 /** 1683 * Save the tag data into the original image file. This is expensive because it involves 1684 * copying all the data from one file to another and deleting the old file and renaming the 1685 * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write 1686 * and make a single call rather than multiple calls for each attribute. 1687 * <p> 1688 * This method is only supported for JPEG files. 1689 * </p> 1690 */ 1691 public void saveAttributes() throws IOException { 1692 if (!mIsSupportedFile || mMimeType != IMAGE_TYPE_JPEG) { 1693 throw new IOException("ExifInterface only supports saving attributes on JPEG formats."); 1694 } 1695 if (mFilename == null) { 1696 throw new IOException( 1697 "ExifInterface does not support saving attributes for the current input."); 1698 } 1699 1700 // Keep the thumbnail in memory 1701 mThumbnailBytes = getThumbnail(); 1702 1703 File tempFile = new File(mFilename + ".tmp"); 1704 File originalFile = new File(mFilename); 1705 if (!originalFile.renameTo(tempFile)) { 1706 throw new IOException("Could not rename to " + tempFile.getAbsolutePath()); 1707 } 1708 1709 FileInputStream in = null; 1710 FileOutputStream out = null; 1711 try { 1712 // Save the new file. 1713 in = new FileInputStream(tempFile); 1714 out = new FileOutputStream(mFilename); 1715 saveJpegAttributes(in, out); 1716 } finally { 1717 closeQuietly(in); 1718 closeQuietly(out); 1719 tempFile.delete(); 1720 } 1721 1722 // Discard the thumbnail in memory 1723 mThumbnailBytes = null; 1724 } 1725 1726 /** 1727 * Returns true if the image file has a thumbnail. 1728 */ 1729 public boolean hasThumbnail() { 1730 return mHasThumbnail; 1731 } 1732 1733 /** 1734 * Returns the JPEG compressed thumbnail inside the image file, or {@code null} if there is no 1735 * JPEG compressed thumbnail. 1736 * The returned data can be decoded using 1737 * {@link android.graphics.BitmapFactory#decodeByteArray(byte[],int,int)} 1738 */ 1739 public byte[] getThumbnail() { 1740 if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) { 1741 return getThumbnailBytes(); 1742 } 1743 return null; 1744 } 1745 1746 /** 1747 * Returns the thumbnail bytes inside the image file, regardless of the compression type of the 1748 * thumbnail image. 1749 */ 1750 public byte[] getThumbnailBytes() { 1751 if (!mHasThumbnail) { 1752 return null; 1753 } 1754 if (mThumbnailBytes != null) { 1755 return mThumbnailBytes; 1756 } 1757 1758 // Read the thumbnail. 1759 InputStream in = null; 1760 try { 1761 if (mAssetInputStream != null) { 1762 in = mAssetInputStream; 1763 if (in.markSupported()) { 1764 in.reset(); 1765 } else { 1766 Log.d(TAG, "Cannot read thumbnail from inputstream without mark/reset support"); 1767 return null; 1768 } 1769 } else if (mFilename != null) { 1770 in = new FileInputStream(mFilename); 1771 } 1772 if (in == null) { 1773 // Should not be reached this. 1774 throw new FileNotFoundException(); 1775 } 1776 if (in.skip(mThumbnailOffset) != mThumbnailOffset) { 1777 throw new IOException("Corrupted image"); 1778 } 1779 byte[] buffer = new byte[mThumbnailLength]; 1780 if (in.read(buffer) != mThumbnailLength) { 1781 throw new IOException("Corrupted image"); 1782 } 1783 mThumbnailBytes = buffer; 1784 return buffer; 1785 } catch (IOException e) { 1786 // Couldn't get a thumbnail image. 1787 Log.d(TAG, "Encountered exception while getting thumbnail", e); 1788 } finally { 1789 closeQuietly(in); 1790 } 1791 return null; 1792 } 1793 1794 /** 1795 * Creates and returns a Bitmap object of the thumbnail image based on the byte array and the 1796 * thumbnail compression value, or {@code null} if the compression type is unsupported. 1797 */ 1798 public Bitmap getThumbnailBitmap() { 1799 if (!mHasThumbnail) { 1800 return null; 1801 } else if (mThumbnailBytes == null) { 1802 mThumbnailBytes = getThumbnailBytes(); 1803 } 1804 1805 if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) { 1806 return BitmapFactory.decodeByteArray(mThumbnailBytes, 0, mThumbnailLength); 1807 } else if (mThumbnailCompression == DATA_UNCOMPRESSED) { 1808 int[] rgbValues = new int[mThumbnailBytes.length / 3]; 1809 byte alpha = (byte) 0xff000000; 1810 for (int i = 0; i < rgbValues.length; i++) { 1811 rgbValues[i] = alpha + (mThumbnailBytes[3 * i] << 16) 1812 + (mThumbnailBytes[3 * i + 1] << 8) + mThumbnailBytes[3 * i + 2]; 1813 } 1814 1815 ExifAttribute imageLengthAttribute = 1816 (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_LENGTH); 1817 ExifAttribute imageWidthAttribute = 1818 (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_WIDTH); 1819 if (imageLengthAttribute != null && imageWidthAttribute != null) { 1820 int imageLength = imageLengthAttribute.getIntValue(mExifByteOrder); 1821 int imageWidth = imageWidthAttribute.getIntValue(mExifByteOrder); 1822 return Bitmap.createBitmap( 1823 rgbValues, imageWidth, imageLength, Bitmap.Config.ARGB_8888); 1824 } 1825 } 1826 return null; 1827 } 1828 1829 /** 1830 * Returns true if thumbnail image is JPEG Compressed, or false if either thumbnail image does 1831 * not exist or thumbnail image is uncompressed. 1832 */ 1833 public boolean isThumbnailCompressed() { 1834 return mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED; 1835 } 1836 1837 /** 1838 * Returns the offset and length of thumbnail inside the image file, or 1839 * {@code null} if there is no thumbnail. 1840 * 1841 * @return two-element array, the offset in the first value, and length in 1842 * the second, or {@code null} if no thumbnail was found. 1843 */ 1844 public long[] getThumbnailRange() { 1845 if (!mHasThumbnail) { 1846 return null; 1847 } 1848 1849 long[] range = new long[2]; 1850 range[0] = mThumbnailOffset; 1851 range[1] = mThumbnailLength; 1852 1853 return range; 1854 } 1855 1856 /** 1857 * Stores the latitude and longitude value in a float array. The first element is the latitude, 1858 * and the second element is the longitude. Returns false if the Exif tags are not available. 1859 * 1860 * @deprecated Use {@link #getLatLong()} instead. 1861 */ 1862 @Deprecated 1863 public boolean getLatLong(float output[]) { 1864 double[] latLong = getLatLong(); 1865 if (latLong == null) { 1866 return false; 1867 } 1868 1869 output[0] = (float) latLong[0]; 1870 output[1] = (float) latLong[1]; 1871 return true; 1872 } 1873 1874 /** 1875 * Gets the latitude and longitude values. 1876 * <p> 1877 * If there are valid latitude and longitude values in the image, this method returns a double 1878 * array where the first element is the latitude and the second element is the longitude. 1879 * Otherwise, it returns null. 1880 */ 1881 public double[] getLatLong() { 1882 String latValue = getAttribute(TAG_GPS_LATITUDE); 1883 String latRef = getAttribute(TAG_GPS_LATITUDE_REF); 1884 String lngValue = getAttribute(TAG_GPS_LONGITUDE); 1885 String lngRef = getAttribute(TAG_GPS_LONGITUDE_REF); 1886 1887 if (latValue != null && latRef != null && lngValue != null && lngRef != null) { 1888 try { 1889 double latitude = convertRationalLatLonToDouble(latValue, latRef); 1890 double longitude = convertRationalLatLonToDouble(lngValue, lngRef); 1891 return new double[] {latitude, longitude}; 1892 } catch (IllegalArgumentException e) { 1893 Log.w(TAG, "Latitude/longitude values are not parseable. " + 1894 String.format("latValue=%s, latRef=%s, lngValue=%s, lngRef=%s", 1895 latValue, latRef, lngValue, lngRef)); 1896 } 1897 } 1898 return null; 1899 } 1900 1901 /** 1902 * Sets the latitude and longitude values. 1903 * 1904 * @param latitude the decimal value of latitude. Must be a valid double value between -90.0 and 1905 * 90.0. 1906 * @param longitude the decimal value of longitude. Must be a valid double value between -180.0 1907 * and 180.0. 1908 * @throws IllegalArgumentException If {@code latitude} or {@code longitude} is outside the 1909 * specified range. 1910 */ 1911 public void setLatLong(double latitude, double longitude) { 1912 if (latitude < -90.0 || latitude > 90.0 || Double.isNaN(latitude)) { 1913 throw new IllegalArgumentException("Latitude value " + latitude + " is not valid."); 1914 } 1915 if (longitude < -180.0 || longitude > 180.0 || Double.isNaN(longitude)) { 1916 throw new IllegalArgumentException("Longitude value " + longitude + " is not valid."); 1917 } 1918 setAttribute(TAG_GPS_LATITUDE_REF, latitude >= 0 ? "N" : "S"); 1919 setAttribute(TAG_GPS_LATITUDE, convertDecimalDegree(Math.abs(latitude))); 1920 setAttribute(TAG_GPS_LONGITUDE_REF, longitude >= 0 ? "E" : "W"); 1921 setAttribute(TAG_GPS_LONGITUDE, convertDecimalDegree(Math.abs(longitude))); 1922 } 1923 1924 /** 1925 * Return the altitude in meters. If the exif tag does not exist, return 1926 * <var>defaultValue</var>. 1927 * 1928 * @param defaultValue the value to return if the tag is not available. 1929 */ 1930 public double getAltitude(double defaultValue) { 1931 double altitude = getAttributeDouble(TAG_GPS_ALTITUDE, -1); 1932 int ref = getAttributeInt(TAG_GPS_ALTITUDE_REF, -1); 1933 1934 if (altitude >= 0 && ref >= 0) { 1935 return (altitude * ((ref == 1) ? -1 : 1)); 1936 } else { 1937 return defaultValue; 1938 } 1939 } 1940 1941 /** 1942 * Returns number of milliseconds since Jan. 1, 1970, midnight local time. 1943 * Returns -1 if the date time information if not available. 1944 * @hide 1945 */ 1946 public long getDateTime() { 1947 String dateTimeString = getAttribute(TAG_DATETIME); 1948 if (dateTimeString == null 1949 || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1; 1950 1951 ParsePosition pos = new ParsePosition(0); 1952 try { 1953 // The exif field is in local time. Parsing it as if it is UTC will yield time 1954 // since 1/1/1970 local time 1955 Date datetime = sFormatter.parse(dateTimeString, pos); 1956 if (datetime == null) return -1; 1957 long msecs = datetime.getTime(); 1958 1959 String subSecs = getAttribute(TAG_SUBSEC_TIME); 1960 if (subSecs != null) { 1961 try { 1962 long sub = Long.parseLong(subSecs); 1963 while (sub > 1000) { 1964 sub /= 10; 1965 } 1966 msecs += sub; 1967 } catch (NumberFormatException e) { 1968 // Ignored 1969 } 1970 } 1971 return msecs; 1972 } catch (IllegalArgumentException e) { 1973 return -1; 1974 } 1975 } 1976 1977 /** 1978 * Returns number of milliseconds since Jan. 1, 1970, midnight UTC. 1979 * Returns -1 if the date time information if not available. 1980 * @hide 1981 */ 1982 public long getGpsDateTime() { 1983 String date = getAttribute(TAG_GPS_DATESTAMP); 1984 String time = getAttribute(TAG_GPS_TIMESTAMP); 1985 if (date == null || time == null 1986 || (!sNonZeroTimePattern.matcher(date).matches() 1987 && !sNonZeroTimePattern.matcher(time).matches())) { 1988 return -1; 1989 } 1990 1991 String dateTimeString = date + ' ' + time; 1992 1993 ParsePosition pos = new ParsePosition(0); 1994 try { 1995 Date datetime = sFormatter.parse(dateTimeString, pos); 1996 if (datetime == null) return -1; 1997 return datetime.getTime(); 1998 } catch (IllegalArgumentException e) { 1999 return -1; 2000 } 2001 } 2002 2003 private static double convertRationalLatLonToDouble(String rationalString, String ref) { 2004 try { 2005 String [] parts = rationalString.split(","); 2006 2007 String [] pair; 2008 pair = parts[0].split("/"); 2009 double degrees = Double.parseDouble(pair[0].trim()) 2010 / Double.parseDouble(pair[1].trim()); 2011 2012 pair = parts[1].split("/"); 2013 double minutes = Double.parseDouble(pair[0].trim()) 2014 / Double.parseDouble(pair[1].trim()); 2015 2016 pair = parts[2].split("/"); 2017 double seconds = Double.parseDouble(pair[0].trim()) 2018 / Double.parseDouble(pair[1].trim()); 2019 2020 double result = degrees + (minutes / 60.0) + (seconds / 3600.0); 2021 if ((ref.equals("S") || ref.equals("W"))) { 2022 return -result; 2023 } else if (ref.equals("N") || ref.equals("E")) { 2024 return result; 2025 } else { 2026 // Not valid 2027 throw new IllegalArgumentException(); 2028 } 2029 } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { 2030 // Not valid 2031 throw new IllegalArgumentException(); 2032 } 2033 } 2034 2035 private String convertDecimalDegree(double decimalDegree) { 2036 long degrees = (long) decimalDegree; 2037 long minutes = (long) ((decimalDegree - degrees) * 60.0); 2038 long seconds = Math.round((decimalDegree - degrees - minutes / 60.0) * 3600.0 * 1e7); 2039 return degrees + "/1," + minutes + "/1," + seconds + "/10000000"; 2040 } 2041 2042 // Checks the type of image file 2043 private int getMimeType(BufferedInputStream in) throws IOException { 2044 in.mark(SIGNATURE_CHECK_SIZE); 2045 byte[] signatureCheckBytes = new byte[SIGNATURE_CHECK_SIZE]; 2046 if (in.read(signatureCheckBytes) != SIGNATURE_CHECK_SIZE) { 2047 throw new EOFException(); 2048 } 2049 in.reset(); 2050 if (isJpegFormat(signatureCheckBytes)) { 2051 return IMAGE_TYPE_JPEG; 2052 } else if (isRafFormat(signatureCheckBytes)) { 2053 return IMAGE_TYPE_RAF; 2054 } else if (isOrfFormat(signatureCheckBytes)) { 2055 return IMAGE_TYPE_ORF; 2056 } else if (isRw2Format(signatureCheckBytes)) { 2057 return IMAGE_TYPE_RW2; 2058 } 2059 // Certain file formats (PEF) are identified in readImageFileDirectory() 2060 return IMAGE_TYPE_UNKNOWN; 2061 } 2062 2063 /** 2064 * This method looks at the first 3 bytes to determine if this file is a JPEG file. 2065 * See http://www.media.mit.edu/pia/Research/deepview/exif.html, "JPEG format and Marker" 2066 */ 2067 private static boolean isJpegFormat(byte[] signatureCheckBytes) throws IOException { 2068 for (int i = 0; i < JPEG_SIGNATURE.length; i++) { 2069 if (signatureCheckBytes[i] != JPEG_SIGNATURE[i]) { 2070 return false; 2071 } 2072 } 2073 return true; 2074 } 2075 2076 /** 2077 * This method looks at the first 15 bytes to determine if this file is a RAF file. 2078 * There is no official specification for RAF files from Fuji, but there is an online archive of 2079 * image file specifications: 2080 * http://fileformats.archiveteam.org/wiki/Fujifilm_RAF 2081 */ 2082 private boolean isRafFormat(byte[] signatureCheckBytes) throws IOException { 2083 byte[] rafSignatureBytes = RAF_SIGNATURE.getBytes(Charset.defaultCharset()); 2084 for (int i = 0; i < rafSignatureBytes.length; i++) { 2085 if (signatureCheckBytes[i] != rafSignatureBytes[i]) { 2086 return false; 2087 } 2088 } 2089 return true; 2090 } 2091 2092 /** 2093 * ORF has a similar structure to TIFF but it contains a different signature at the TIFF Header. 2094 * This method looks at the 2 bytes following the Byte Order bytes to determine if this file is 2095 * an ORF file. 2096 * There is no official specification for ORF files from Olympus, but there is an online archive 2097 * of image file specifications: 2098 * http://fileformats.archiveteam.org/wiki/Olympus_ORF 2099 */ 2100 private boolean isOrfFormat(byte[] signatureCheckBytes) throws IOException { 2101 ByteOrderedDataInputStream signatureInputStream = 2102 new ByteOrderedDataInputStream(signatureCheckBytes); 2103 // Read byte order 2104 mExifByteOrder = readByteOrder(signatureInputStream); 2105 // Set byte order 2106 signatureInputStream.setByteOrder(mExifByteOrder); 2107 2108 short orfSignature = signatureInputStream.readShort(); 2109 signatureInputStream.close(); 2110 return orfSignature == ORF_SIGNATURE_1 || orfSignature == ORF_SIGNATURE_2; 2111 } 2112 2113 /** 2114 * RW2 is TIFF-based, but stores 0x55 signature byte instead of 0x42 at the header 2115 * See http://lclevy.free.fr/raw/ 2116 */ 2117 private boolean isRw2Format(byte[] signatureCheckBytes) throws IOException { 2118 ByteOrderedDataInputStream signatureInputStream = 2119 new ByteOrderedDataInputStream(signatureCheckBytes); 2120 // Read byte order 2121 mExifByteOrder = readByteOrder(signatureInputStream); 2122 // Set byte order 2123 signatureInputStream.setByteOrder(mExifByteOrder); 2124 2125 short signatureByte = signatureInputStream.readShort(); 2126 signatureInputStream.close(); 2127 return signatureByte == RW2_SIGNATURE; 2128 } 2129 2130 /** 2131 * Loads EXIF attributes from a JPEG input stream. 2132 * 2133 * @param in The input stream that starts with the JPEG data. 2134 * @param jpegOffset The offset value in input stream for JPEG data. 2135 * @param imageType The image type from which to retrieve metadata. Use IFD_TYPE_PRIMARY for 2136 * primary image, IFD_TYPE_PREVIEW for preview image, and 2137 * IFD_TYPE_THUMBNAIL for thumbnail image. 2138 * @throws IOException If the data contains invalid JPEG markers, offsets, or length values. 2139 */ 2140 private void getJpegAttributes(ByteOrderedDataInputStream in, int jpegOffset, int imageType) 2141 throws IOException { 2142 // See JPEG File Interchange Format Specification, "JFIF Specification" 2143 if (DEBUG) { 2144 Log.d(TAG, "getJpegAttributes starting with: " + in); 2145 } 2146 2147 // JPEG uses Big Endian by default. See https://people.cs.umass.edu/~verts/cs32/endian.html 2148 in.setByteOrder(ByteOrder.BIG_ENDIAN); 2149 2150 // Skip to JPEG data 2151 in.seek(jpegOffset); 2152 int bytesRead = jpegOffset; 2153 2154 byte marker; 2155 if ((marker = in.readByte()) != MARKER) { 2156 throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff)); 2157 } 2158 ++bytesRead; 2159 if (in.readByte() != MARKER_SOI) { 2160 throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff)); 2161 } 2162 ++bytesRead; 2163 while (true) { 2164 marker = in.readByte(); 2165 if (marker != MARKER) { 2166 throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff)); 2167 } 2168 ++bytesRead; 2169 marker = in.readByte(); 2170 if (DEBUG) { 2171 Log.d(TAG, "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff)); 2172 } 2173 ++bytesRead; 2174 2175 // EOI indicates the end of an image and in case of SOS, JPEG image stream starts and 2176 // the image data will terminate right after. 2177 if (marker == MARKER_EOI || marker == MARKER_SOS) { 2178 break; 2179 } 2180 int length = in.readUnsignedShort() - 2; 2181 bytesRead += 2; 2182 if (DEBUG) { 2183 Log.d(TAG, "JPEG segment: " + Integer.toHexString(marker & 0xff) + " (length: " 2184 + (length + 2) + ")"); 2185 } 2186 if (length < 0) { 2187 throw new IOException("Invalid length"); 2188 } 2189 switch (marker) { 2190 case MARKER_APP1: { 2191 if (DEBUG) { 2192 Log.d(TAG, "MARKER_APP1"); 2193 } 2194 if (length < 6) { 2195 // Skip if it's not an EXIF APP1 segment. 2196 break; 2197 } 2198 byte[] identifier = new byte[6]; 2199 if (in.read(identifier) != 6) { 2200 throw new IOException("Invalid exif"); 2201 } 2202 bytesRead += 6; 2203 length -= 6; 2204 if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { 2205 // Skip if it's not an EXIF APP1 segment. 2206 break; 2207 } 2208 if (length <= 0) { 2209 throw new IOException("Invalid exif"); 2210 } 2211 if (DEBUG) { 2212 Log.d(TAG, "readExifSegment with a byte array (length: " + length + ")"); 2213 } 2214 // Save offset values for createJpegThumbnailBitmap() function 2215 mExifOffset = bytesRead; 2216 2217 byte[] bytes = new byte[length]; 2218 if (in.read(bytes) != length) { 2219 throw new IOException("Invalid exif"); 2220 } 2221 bytesRead += length; 2222 length = 0; 2223 2224 readExifSegment(bytes, imageType); 2225 break; 2226 } 2227 2228 case MARKER_COM: { 2229 byte[] bytes = new byte[length]; 2230 if (in.read(bytes) != length) { 2231 throw new IOException("Invalid exif"); 2232 } 2233 length = 0; 2234 if (getAttribute(TAG_USER_COMMENT) == null) { 2235 mAttributes[IFD_TYPE_EXIF].put(TAG_USER_COMMENT, ExifAttribute.createString( 2236 new String(bytes, ASCII))); 2237 } 2238 break; 2239 } 2240 2241 case MARKER_SOF0: 2242 case MARKER_SOF1: 2243 case MARKER_SOF2: 2244 case MARKER_SOF3: 2245 case MARKER_SOF5: 2246 case MARKER_SOF6: 2247 case MARKER_SOF7: 2248 case MARKER_SOF9: 2249 case MARKER_SOF10: 2250 case MARKER_SOF11: 2251 case MARKER_SOF13: 2252 case MARKER_SOF14: 2253 case MARKER_SOF15: { 2254 if (in.skipBytes(1) != 1) { 2255 throw new IOException("Invalid SOFx"); 2256 } 2257 mAttributes[imageType].put(TAG_IMAGE_LENGTH, ExifAttribute.createULong( 2258 in.readUnsignedShort(), mExifByteOrder)); 2259 mAttributes[imageType].put(TAG_IMAGE_WIDTH, ExifAttribute.createULong( 2260 in.readUnsignedShort(), mExifByteOrder)); 2261 length -= 5; 2262 break; 2263 } 2264 2265 default: { 2266 break; 2267 } 2268 } 2269 if (length < 0) { 2270 throw new IOException("Invalid length"); 2271 } 2272 if (in.skipBytes(length) != length) { 2273 throw new IOException("Invalid JPEG segment"); 2274 } 2275 bytesRead += length; 2276 } 2277 // Restore original byte order 2278 in.setByteOrder(mExifByteOrder); 2279 } 2280 2281 private void getRawAttributes(ByteOrderedDataInputStream in) throws IOException { 2282 // Parse TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1. 2283 parseTiffHeaders(in, in.available()); 2284 2285 // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6. 2286 readImageFileDirectory(in, IFD_TYPE_PRIMARY); 2287 2288 // Update ImageLength/Width tags for all image data. 2289 updateImageSizeValues(in, IFD_TYPE_PRIMARY); 2290 updateImageSizeValues(in, IFD_TYPE_PREVIEW); 2291 updateImageSizeValues(in, IFD_TYPE_THUMBNAIL); 2292 2293 // Check if each image data is in valid position. 2294 validateImages(in); 2295 2296 if (mMimeType == IMAGE_TYPE_PEF) { 2297 // PEF files contain a MakerNote data, which contains the data for ColorSpace tag. 2298 // See http://lclevy.free.fr/raw/ and piex.cc PefGetPreviewData() 2299 ExifAttribute makerNoteAttribute = 2300 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE); 2301 if (makerNoteAttribute != null) { 2302 // Create an ordered DataInputStream for MakerNote 2303 ByteOrderedDataInputStream makerNoteDataInputStream = 2304 new ByteOrderedDataInputStream(makerNoteAttribute.bytes); 2305 makerNoteDataInputStream.setByteOrder(mExifByteOrder); 2306 2307 // Seek to MakerNote data 2308 makerNoteDataInputStream.seek(PEF_MAKER_NOTE_SKIP_SIZE); 2309 2310 // Read IFD data from MakerNote 2311 readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_PEF); 2312 2313 // Update ColorSpace tag 2314 ExifAttribute colorSpaceAttribute = 2315 (ExifAttribute) mAttributes[IFD_TYPE_PEF].get(TAG_COLOR_SPACE); 2316 if (colorSpaceAttribute != null) { 2317 mAttributes[IFD_TYPE_EXIF].put(TAG_COLOR_SPACE, colorSpaceAttribute); 2318 } 2319 } 2320 } 2321 } 2322 2323 /** 2324 * RAF files contains a JPEG and a CFA data. 2325 * The JPEG contains two images, a preview and a thumbnail, while the CFA contains a RAW image. 2326 * This method looks at the first 160 bytes of a RAF file to retrieve the offset and length 2327 * values for the JPEG and CFA data. 2328 * Using that data, it parses the JPEG data to retrieve the preview and thumbnail image data, 2329 * then parses the CFA metadata to retrieve the primary image length/width values. 2330 * For data format details, see http://fileformats.archiveteam.org/wiki/Fujifilm_RAF 2331 */ 2332 private void getRafAttributes(ByteOrderedDataInputStream in) throws IOException { 2333 // Retrieve offset & length values 2334 in.skipBytes(RAF_OFFSET_TO_JPEG_IMAGE_OFFSET); 2335 byte[] jpegOffsetBytes = new byte[4]; 2336 byte[] cfaHeaderOffsetBytes = new byte[4]; 2337 in.read(jpegOffsetBytes); 2338 // Skip JPEG length value since it is not needed 2339 in.skipBytes(RAF_JPEG_LENGTH_VALUE_SIZE); 2340 in.read(cfaHeaderOffsetBytes); 2341 int rafJpegOffset = ByteBuffer.wrap(jpegOffsetBytes).getInt(); 2342 int rafCfaHeaderOffset = ByteBuffer.wrap(cfaHeaderOffsetBytes).getInt(); 2343 2344 // Retrieve JPEG image metadata 2345 getJpegAttributes(in, rafJpegOffset, IFD_TYPE_PREVIEW); 2346 2347 // Skip to CFA header offset. 2348 in.seek(rafCfaHeaderOffset); 2349 2350 // Retrieve primary image length/width values, if TAG_RAF_IMAGE_SIZE exists 2351 in.setByteOrder(ByteOrder.BIG_ENDIAN); 2352 int numberOfDirectoryEntry = in.readInt(); 2353 if (DEBUG) { 2354 Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry); 2355 } 2356 // CFA stores some metadata about the RAW image. Since CFA uses proprietary tags, can only 2357 // find and retrieve image size information tags, while skipping others. 2358 // See piex.cc RafGetDimension() 2359 for (int i = 0; i < numberOfDirectoryEntry; ++i) { 2360 int tagNumber = in.readUnsignedShort(); 2361 int numberOfBytes = in.readUnsignedShort(); 2362 if (tagNumber == TAG_RAF_IMAGE_SIZE.number) { 2363 int imageLength = in.readShort(); 2364 int imageWidth = in.readShort(); 2365 ExifAttribute imageLengthAttribute = 2366 ExifAttribute.createUShort(imageLength, mExifByteOrder); 2367 ExifAttribute imageWidthAttribute = 2368 ExifAttribute.createUShort(imageWidth, mExifByteOrder); 2369 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, imageLengthAttribute); 2370 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, imageWidthAttribute); 2371 if (DEBUG) { 2372 Log.d(TAG, "Updated to length: " + imageLength + ", width: " + imageWidth); 2373 } 2374 return; 2375 } 2376 in.skipBytes(numberOfBytes); 2377 } 2378 } 2379 2380 /** 2381 * ORF files contains a primary image data and a MakerNote data that contains preview/thumbnail 2382 * images. Both data takes the form of IFDs and can therefore be read with the 2383 * readImageFileDirectory() method. 2384 * This method reads all the necessary data and updates the primary/preview/thumbnail image 2385 * information according to the GetOlympusPreviewImage() method in piex.cc. 2386 * For data format details, see the following: 2387 * http://fileformats.archiveteam.org/wiki/Olympus_ORF 2388 * https://libopenraw.freedesktop.org/wiki/Olympus_ORF 2389 */ 2390 private void getOrfAttributes(ByteOrderedDataInputStream in) throws IOException { 2391 // Retrieve primary image data 2392 // Other Exif data will be located in the Makernote. 2393 getRawAttributes(in); 2394 2395 // Additionally retrieve preview/thumbnail information from MakerNote tag, which contains 2396 // proprietary tags and therefore does not have offical documentation 2397 // See GetOlympusPreviewImage() in piex.cc & http://www.exiv2.org/tags-olympus.html 2398 ExifAttribute makerNoteAttribute = 2399 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE); 2400 if (makerNoteAttribute != null) { 2401 // Create an ordered DataInputStream for MakerNote 2402 ByteOrderedDataInputStream makerNoteDataInputStream = 2403 new ByteOrderedDataInputStream(makerNoteAttribute.bytes); 2404 makerNoteDataInputStream.setByteOrder(mExifByteOrder); 2405 2406 // There are two types of headers for Olympus MakerNotes 2407 // See http://www.exiv2.org/makernote.html#R1 2408 byte[] makerNoteHeader1Bytes = new byte[ORF_MAKER_NOTE_HEADER_1.length]; 2409 makerNoteDataInputStream.readFully(makerNoteHeader1Bytes); 2410 makerNoteDataInputStream.seek(0); 2411 byte[] makerNoteHeader2Bytes = new byte[ORF_MAKER_NOTE_HEADER_2.length]; 2412 makerNoteDataInputStream.readFully(makerNoteHeader2Bytes); 2413 // Skip the corresponding amount of bytes for each header type 2414 if (Arrays.equals(makerNoteHeader1Bytes, ORF_MAKER_NOTE_HEADER_1)) { 2415 makerNoteDataInputStream.seek(ORF_MAKER_NOTE_HEADER_1_SIZE); 2416 } else if (Arrays.equals(makerNoteHeader2Bytes, ORF_MAKER_NOTE_HEADER_2)) { 2417 makerNoteDataInputStream.seek(ORF_MAKER_NOTE_HEADER_2_SIZE); 2418 } 2419 2420 // Read IFD data from MakerNote 2421 readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_ORF_MAKER_NOTE); 2422 2423 // Retrieve & update preview image offset & length values 2424 ExifAttribute imageLengthAttribute = (ExifAttribute) 2425 mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_START); 2426 ExifAttribute bitsPerSampleAttribute = (ExifAttribute) 2427 mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_LENGTH); 2428 2429 if (imageLengthAttribute != null && bitsPerSampleAttribute != null) { 2430 mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT, 2431 imageLengthAttribute); 2432 mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 2433 bitsPerSampleAttribute); 2434 } 2435 2436 // TODO: Check this behavior in other ORF files 2437 // Retrieve primary image length & width values 2438 // See piex.cc GetOlympusPreviewImage() 2439 ExifAttribute aspectFrameAttribute = (ExifAttribute) 2440 mAttributes[IFD_TYPE_ORF_IMAGE_PROCESSING].get(TAG_ORF_ASPECT_FRAME); 2441 if (aspectFrameAttribute != null) { 2442 int[] aspectFrameValues = (int[]) aspectFrameAttribute.getValue(mExifByteOrder); 2443 if (aspectFrameValues == null || aspectFrameValues.length != 4) { 2444 Log.w(TAG, "Invalid aspect frame values. frame=" 2445 + Arrays.toString(aspectFrameValues)); 2446 return; 2447 } 2448 if (aspectFrameValues[2] > aspectFrameValues[0] && 2449 aspectFrameValues[3] > aspectFrameValues[1]) { 2450 int primaryImageWidth = aspectFrameValues[2] - aspectFrameValues[0] + 1; 2451 int primaryImageLength = aspectFrameValues[3] - aspectFrameValues[1] + 1; 2452 // Swap width & length values 2453 if (primaryImageWidth < primaryImageLength) { 2454 primaryImageWidth += primaryImageLength; 2455 primaryImageLength = primaryImageWidth - primaryImageLength; 2456 primaryImageWidth -= primaryImageLength; 2457 } 2458 ExifAttribute primaryImageWidthAttribute = 2459 ExifAttribute.createUShort(primaryImageWidth, mExifByteOrder); 2460 ExifAttribute primaryImageLengthAttribute = 2461 ExifAttribute.createUShort(primaryImageLength, mExifByteOrder); 2462 2463 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, primaryImageWidthAttribute); 2464 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, primaryImageLengthAttribute); 2465 } 2466 } 2467 } 2468 } 2469 2470 // RW2 contains the primary image data in IFD0 and the preview and/or thumbnail image data in 2471 // the JpgFromRaw tag 2472 // See https://libopenraw.freedesktop.org/wiki/Panasonic_RAW/ and piex.cc Rw2GetPreviewData() 2473 private void getRw2Attributes(ByteOrderedDataInputStream in) throws IOException { 2474 // Retrieve primary image data 2475 getRawAttributes(in); 2476 2477 // Retrieve preview and/or thumbnail image data 2478 ExifAttribute jpgFromRawAttribute = 2479 (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].get(TAG_RW2_JPG_FROM_RAW); 2480 if (jpgFromRawAttribute != null) { 2481 getJpegAttributes(in, mRw2JpgFromRawOffset, IFD_TYPE_PREVIEW); 2482 } 2483 2484 // Set ISO tag value if necessary 2485 ExifAttribute rw2IsoAttribute = 2486 (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].get(TAG_RW2_ISO); 2487 ExifAttribute exifIsoAttribute = 2488 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_ISO_SPEED_RATINGS); 2489 if (rw2IsoAttribute != null && exifIsoAttribute == null) { 2490 // Place this attribute only if it doesn't exist 2491 mAttributes[IFD_TYPE_EXIF].put(TAG_ISO_SPEED_RATINGS, rw2IsoAttribute); 2492 } 2493 } 2494 2495 // Stores a new JPEG image with EXIF attributes into a given output stream. 2496 private void saveJpegAttributes(InputStream inputStream, OutputStream outputStream) 2497 throws IOException { 2498 // See JPEG File Interchange Format Specification, "JFIF Specification" 2499 if (DEBUG) { 2500 Log.d(TAG, "saveJpegAttributes starting with (inputStream: " + inputStream 2501 + ", outputStream: " + outputStream + ")"); 2502 } 2503 DataInputStream dataInputStream = new DataInputStream(inputStream); 2504 ByteOrderedDataOutputStream dataOutputStream = 2505 new ByteOrderedDataOutputStream(outputStream, ByteOrder.BIG_ENDIAN); 2506 if (dataInputStream.readByte() != MARKER) { 2507 throw new IOException("Invalid marker"); 2508 } 2509 dataOutputStream.writeByte(MARKER); 2510 if (dataInputStream.readByte() != MARKER_SOI) { 2511 throw new IOException("Invalid marker"); 2512 } 2513 dataOutputStream.writeByte(MARKER_SOI); 2514 2515 // Write EXIF APP1 segment 2516 dataOutputStream.writeByte(MARKER); 2517 dataOutputStream.writeByte(MARKER_APP1); 2518 writeExifSegment(dataOutputStream, 6); 2519 2520 byte[] bytes = new byte[4096]; 2521 2522 while (true) { 2523 byte marker = dataInputStream.readByte(); 2524 if (marker != MARKER) { 2525 throw new IOException("Invalid marker"); 2526 } 2527 marker = dataInputStream.readByte(); 2528 switch (marker) { 2529 case MARKER_APP1: { 2530 int length = dataInputStream.readUnsignedShort() - 2; 2531 if (length < 0) { 2532 throw new IOException("Invalid length"); 2533 } 2534 byte[] identifier = new byte[6]; 2535 if (length >= 6) { 2536 if (dataInputStream.read(identifier) != 6) { 2537 throw new IOException("Invalid exif"); 2538 } 2539 if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { 2540 // Skip the original EXIF APP1 segment. 2541 if (dataInputStream.skipBytes(length - 6) != length - 6) { 2542 throw new IOException("Invalid length"); 2543 } 2544 break; 2545 } 2546 } 2547 // Copy non-EXIF APP1 segment. 2548 dataOutputStream.writeByte(MARKER); 2549 dataOutputStream.writeByte(marker); 2550 dataOutputStream.writeUnsignedShort(length + 2); 2551 if (length >= 6) { 2552 length -= 6; 2553 dataOutputStream.write(identifier); 2554 } 2555 int read; 2556 while (length > 0 && (read = dataInputStream.read( 2557 bytes, 0, Math.min(length, bytes.length))) >= 0) { 2558 dataOutputStream.write(bytes, 0, read); 2559 length -= read; 2560 } 2561 break; 2562 } 2563 case MARKER_EOI: 2564 case MARKER_SOS: { 2565 dataOutputStream.writeByte(MARKER); 2566 dataOutputStream.writeByte(marker); 2567 // Copy all the remaining data 2568 copy(dataInputStream, dataOutputStream); 2569 return; 2570 } 2571 default: { 2572 // Copy JPEG segment 2573 dataOutputStream.writeByte(MARKER); 2574 dataOutputStream.writeByte(marker); 2575 int length = dataInputStream.readUnsignedShort(); 2576 dataOutputStream.writeUnsignedShort(length); 2577 length -= 2; 2578 if (length < 0) { 2579 throw new IOException("Invalid length"); 2580 } 2581 int read; 2582 while (length > 0 && (read = dataInputStream.read( 2583 bytes, 0, Math.min(length, bytes.length))) >= 0) { 2584 dataOutputStream.write(bytes, 0, read); 2585 length -= read; 2586 } 2587 break; 2588 } 2589 } 2590 } 2591 } 2592 2593 // Reads the given EXIF byte area and save its tag data into attributes. 2594 private void readExifSegment(byte[] exifBytes, int imageType) throws IOException { 2595 ByteOrderedDataInputStream dataInputStream = 2596 new ByteOrderedDataInputStream(exifBytes); 2597 2598 // Parse TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1. 2599 parseTiffHeaders(dataInputStream, exifBytes.length); 2600 2601 // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6. 2602 readImageFileDirectory(dataInputStream, imageType); 2603 } 2604 2605 private void addDefaultValuesForCompatibility() { 2606 // The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag. 2607 String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL); 2608 if (valueOfDateTimeOriginal != null) { 2609 mAttributes[IFD_TYPE_PRIMARY].put(TAG_DATETIME, 2610 ExifAttribute.createString(valueOfDateTimeOriginal)); 2611 } 2612 2613 // Add the default value. 2614 if (getAttribute(TAG_IMAGE_WIDTH) == null) { 2615 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, 2616 ExifAttribute.createULong(0, mExifByteOrder)); 2617 } 2618 if (getAttribute(TAG_IMAGE_LENGTH) == null) { 2619 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, 2620 ExifAttribute.createULong(0, mExifByteOrder)); 2621 } 2622 if (getAttribute(TAG_ORIENTATION) == null) { 2623 mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION, 2624 ExifAttribute.createULong(0, mExifByteOrder)); 2625 } 2626 if (getAttribute(TAG_LIGHT_SOURCE) == null) { 2627 mAttributes[IFD_TYPE_EXIF].put(TAG_LIGHT_SOURCE, 2628 ExifAttribute.createULong(0, mExifByteOrder)); 2629 } 2630 } 2631 2632 private ByteOrder readByteOrder(ByteOrderedDataInputStream dataInputStream) 2633 throws IOException { 2634 // Read byte order. 2635 short byteOrder = dataInputStream.readShort(); 2636 switch (byteOrder) { 2637 case BYTE_ALIGN_II: 2638 if (DEBUG) { 2639 Log.d(TAG, "readExifSegment: Byte Align II"); 2640 } 2641 return ByteOrder.LITTLE_ENDIAN; 2642 case BYTE_ALIGN_MM: 2643 if (DEBUG) { 2644 Log.d(TAG, "readExifSegment: Byte Align MM"); 2645 } 2646 return ByteOrder.BIG_ENDIAN; 2647 default: 2648 throw new IOException("Invalid byte order: " + Integer.toHexString(byteOrder)); 2649 } 2650 } 2651 2652 private void parseTiffHeaders(ByteOrderedDataInputStream dataInputStream, 2653 int exifBytesLength) throws IOException { 2654 // Read byte order 2655 mExifByteOrder = readByteOrder(dataInputStream); 2656 // Set byte order 2657 dataInputStream.setByteOrder(mExifByteOrder); 2658 2659 // Check start code 2660 int startCode = dataInputStream.readUnsignedShort(); 2661 if (mMimeType != IMAGE_TYPE_ORF && mMimeType != IMAGE_TYPE_RW2 && startCode != START_CODE) { 2662 throw new IOException("Invalid start code: " + Integer.toHexString(startCode)); 2663 } 2664 2665 // Read and skip to first ifd offset 2666 int firstIfdOffset = dataInputStream.readInt(); 2667 if (firstIfdOffset < 8 || firstIfdOffset >= exifBytesLength) { 2668 throw new IOException("Invalid first Ifd offset: " + firstIfdOffset); 2669 } 2670 firstIfdOffset -= 8; 2671 if (firstIfdOffset > 0) { 2672 if (dataInputStream.skipBytes(firstIfdOffset) != firstIfdOffset) { 2673 throw new IOException("Couldn't jump to first Ifd: " + firstIfdOffset); 2674 } 2675 } 2676 } 2677 2678 // Reads image file directory, which is a tag group in EXIF. 2679 private void readImageFileDirectory(ByteOrderedDataInputStream dataInputStream, 2680 @IfdType int ifdType) throws IOException { 2681 if (dataInputStream.mPosition + 2 > dataInputStream.mLength) { 2682 // Return if there is no data from the offset. 2683 return; 2684 } 2685 // See TIFF 6.0 Section 2: TIFF Structure, Figure 1. 2686 short numberOfDirectoryEntry = dataInputStream.readShort(); 2687 if (dataInputStream.mPosition + 12 * numberOfDirectoryEntry > dataInputStream.mLength) { 2688 // Return if the size of entries is too big. 2689 return; 2690 } 2691 2692 if (DEBUG) { 2693 Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry); 2694 } 2695 2696 // See TIFF 6.0 Section 2: TIFF Structure, "Image File Directory". 2697 for (short i = 0; i < numberOfDirectoryEntry; ++i) { 2698 int tagNumber = dataInputStream.readUnsignedShort(); 2699 int dataFormat = dataInputStream.readUnsignedShort(); 2700 int numberOfComponents = dataInputStream.readInt(); 2701 // Next four bytes is for data offset or value. 2702 long nextEntryOffset = dataInputStream.peek() + 4; 2703 2704 // Look up a corresponding tag from tag number 2705 ExifTag tag = (ExifTag) sExifTagMapsForReading[ifdType].get(tagNumber); 2706 2707 if (DEBUG) { 2708 Log.d(TAG, String.format("ifdType: %d, tagNumber: %d, tagName: %s, dataFormat: %d, " 2709 + "numberOfComponents: %d", ifdType, tagNumber, 2710 tag != null ? tag.name : null, dataFormat, numberOfComponents)); 2711 } 2712 2713 long byteCount = 0; 2714 boolean valid = false; 2715 if (tag == null) { 2716 Log.w(TAG, "Skip the tag entry since tag number is not defined: " + tagNumber); 2717 } else if (dataFormat <= 0 || dataFormat >= IFD_FORMAT_BYTES_PER_FORMAT.length) { 2718 Log.w(TAG, "Skip the tag entry since data format is invalid: " + dataFormat); 2719 } else { 2720 byteCount = (long) numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat]; 2721 if (byteCount < 0 || byteCount > Integer.MAX_VALUE) { 2722 Log.w(TAG, "Skip the tag entry since the number of components is invalid: " 2723 + numberOfComponents); 2724 } else { 2725 valid = true; 2726 } 2727 } 2728 if (!valid) { 2729 dataInputStream.seek(nextEntryOffset); 2730 continue; 2731 } 2732 2733 // Read a value from data field or seek to the value offset which is stored in data 2734 // field if the size of the entry value is bigger than 4. 2735 if (byteCount > 4) { 2736 int offset = dataInputStream.readInt(); 2737 if (DEBUG) { 2738 Log.d(TAG, "seek to data offset: " + offset); 2739 } 2740 if (mMimeType == IMAGE_TYPE_ORF) { 2741 if (TAG_MAKER_NOTE.equals(tag.name)) { 2742 // Save offset value for reading thumbnail 2743 mOrfMakerNoteOffset = offset; 2744 } else if (ifdType == IFD_TYPE_ORF_MAKER_NOTE 2745 && TAG_ORF_THUMBNAIL_IMAGE.equals(tag.name)) { 2746 // Retrieve & update values for thumbnail offset and length values for ORF 2747 mOrfThumbnailOffset = offset; 2748 mOrfThumbnailLength = numberOfComponents; 2749 2750 ExifAttribute compressionAttribute = 2751 ExifAttribute.createUShort(DATA_JPEG, mExifByteOrder); 2752 ExifAttribute jpegInterchangeFormatAttribute = 2753 ExifAttribute.createULong(mOrfThumbnailOffset, mExifByteOrder); 2754 ExifAttribute jpegInterchangeFormatLengthAttribute = 2755 ExifAttribute.createULong(mOrfThumbnailLength, mExifByteOrder); 2756 2757 mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_COMPRESSION, compressionAttribute); 2758 mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT, 2759 jpegInterchangeFormatAttribute); 2760 mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 2761 jpegInterchangeFormatLengthAttribute); 2762 } 2763 } else if (mMimeType == IMAGE_TYPE_RW2) { 2764 if (TAG_RW2_JPG_FROM_RAW.equals(tag.name)) { 2765 mRw2JpgFromRawOffset = offset; 2766 } 2767 } 2768 if (offset + byteCount <= dataInputStream.mLength) { 2769 dataInputStream.seek(offset); 2770 } else { 2771 // Skip if invalid data offset. 2772 Log.w(TAG, "Skip the tag entry since data offset is invalid: " + offset); 2773 dataInputStream.seek(nextEntryOffset); 2774 continue; 2775 } 2776 } 2777 2778 // Recursively parse IFD when a IFD pointer tag appears. 2779 Integer nextIfdType = sExifPointerTagMap.get(tagNumber); 2780 if (DEBUG) { 2781 Log.d(TAG, "nextIfdType: " + nextIfdType + " byteCount: " + byteCount); 2782 } 2783 2784 if (nextIfdType != null) { 2785 long offset = -1L; 2786 // Get offset from data field 2787 switch (dataFormat) { 2788 case IFD_FORMAT_USHORT: { 2789 offset = dataInputStream.readUnsignedShort(); 2790 break; 2791 } 2792 case IFD_FORMAT_SSHORT: { 2793 offset = dataInputStream.readShort(); 2794 break; 2795 } 2796 case IFD_FORMAT_ULONG: { 2797 offset = dataInputStream.readUnsignedInt(); 2798 break; 2799 } 2800 case IFD_FORMAT_SLONG: 2801 case IFD_FORMAT_IFD: { 2802 offset = dataInputStream.readInt(); 2803 break; 2804 } 2805 default: { 2806 // Nothing to do 2807 break; 2808 } 2809 } 2810 if (DEBUG) { 2811 Log.d(TAG, String.format("Offset: %d, tagName: %s", offset, tag.name)); 2812 } 2813 if (offset > 0L && offset < dataInputStream.mLength) { 2814 dataInputStream.seek(offset); 2815 readImageFileDirectory(dataInputStream, nextIfdType); 2816 } else { 2817 Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset); 2818 } 2819 2820 dataInputStream.seek(nextEntryOffset); 2821 continue; 2822 } 2823 2824 byte[] bytes = new byte[(int) byteCount]; 2825 dataInputStream.readFully(bytes); 2826 ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, bytes); 2827 mAttributes[ifdType].put(tag.name, attribute); 2828 2829 // DNG files have a DNG Version tag specifying the version of specifications that the 2830 // image file is following. 2831 // See http://fileformats.archiveteam.org/wiki/DNG 2832 if (TAG_DNG_VERSION.equals(tag.name)) { 2833 mMimeType = IMAGE_TYPE_DNG; 2834 } 2835 2836 // PEF files have a Make or Model tag that begins with "PENTAX" or a compression tag 2837 // that is 65535. 2838 // See http://fileformats.archiveteam.org/wiki/Pentax_PEF 2839 if (((TAG_MAKE.equals(tag.name) || TAG_MODEL.equals(tag.name)) 2840 && attribute.getStringValue(mExifByteOrder).contains(PEF_SIGNATURE)) 2841 || (TAG_COMPRESSION.equals(tag.name) 2842 && attribute.getIntValue(mExifByteOrder) == 65535)) { 2843 mMimeType = IMAGE_TYPE_PEF; 2844 } 2845 2846 // Seek to next tag offset 2847 if (dataInputStream.peek() != nextEntryOffset) { 2848 dataInputStream.seek(nextEntryOffset); 2849 } 2850 } 2851 2852 if (dataInputStream.peek() + 4 <= dataInputStream.mLength) { 2853 int nextIfdOffset = dataInputStream.readInt(); 2854 if (DEBUG) { 2855 Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset)); 2856 } 2857 // The next IFD offset needs to be bigger than 8 2858 // since the first IFD offset is at least 8. 2859 if (nextIfdOffset > 8 && nextIfdOffset < dataInputStream.mLength) { 2860 dataInputStream.seek(nextIfdOffset); 2861 if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) { 2862 // Do not overwrite thumbnail IFD data if it alreay exists. 2863 readImageFileDirectory(dataInputStream, IFD_TYPE_THUMBNAIL); 2864 } else if (mAttributes[IFD_TYPE_PREVIEW].isEmpty()) { 2865 readImageFileDirectory(dataInputStream, IFD_TYPE_PREVIEW); 2866 } 2867 } 2868 } 2869 } 2870 2871 /** 2872 * JPEG compressed images do not contain IMAGE_LENGTH & IMAGE_WIDTH tags. 2873 * This value uses JpegInterchangeFormat(JPEG data offset) value, and calls getJpegAttributes() 2874 * to locate SOF(Start of Frame) marker and update the image length & width values. 2875 * See JEITA CP-3451C Table 5 and Section 4.8.1. B. 2876 */ 2877 private void retrieveJpegImageSize(ByteOrderedDataInputStream in, int imageType) 2878 throws IOException { 2879 // Check if image already has IMAGE_LENGTH & IMAGE_WIDTH values 2880 ExifAttribute imageLengthAttribute = 2881 (ExifAttribute) mAttributes[imageType].get(TAG_IMAGE_LENGTH); 2882 ExifAttribute imageWidthAttribute = 2883 (ExifAttribute) mAttributes[imageType].get(TAG_IMAGE_WIDTH); 2884 2885 if (imageLengthAttribute == null || imageWidthAttribute == null) { 2886 // Find if offset for JPEG data exists 2887 ExifAttribute jpegInterchangeFormatAttribute = 2888 (ExifAttribute) mAttributes[imageType].get(TAG_JPEG_INTERCHANGE_FORMAT); 2889 if (jpegInterchangeFormatAttribute != null) { 2890 int jpegInterchangeFormat = 2891 jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder); 2892 2893 // Searches for SOF marker in JPEG data and updates IMAGE_LENGTH & IMAGE_WIDTH tags 2894 getJpegAttributes(in, jpegInterchangeFormat, imageType); 2895 } 2896 } 2897 } 2898 2899 // Sets thumbnail offset & length attributes based on JpegInterchangeFormat or StripOffsets tags 2900 private void setThumbnailData(ByteOrderedDataInputStream in) throws IOException { 2901 HashMap thumbnailData = mAttributes[IFD_TYPE_THUMBNAIL]; 2902 2903 ExifAttribute compressionAttribute = 2904 (ExifAttribute) thumbnailData.get(TAG_COMPRESSION); 2905 if (compressionAttribute != null) { 2906 mThumbnailCompression = compressionAttribute.getIntValue(mExifByteOrder); 2907 switch (mThumbnailCompression) { 2908 case DATA_JPEG: { 2909 handleThumbnailFromJfif(in, thumbnailData); 2910 break; 2911 } 2912 case DATA_UNCOMPRESSED: 2913 case DATA_JPEG_COMPRESSED: { 2914 if (isSupportedDataType(thumbnailData)) { 2915 handleThumbnailFromStrips(in, thumbnailData); 2916 } 2917 break; 2918 } 2919 } 2920 } else { 2921 // Thumbnail data may not contain Compression tag value 2922 mThumbnailCompression = DATA_JPEG; 2923 handleThumbnailFromJfif(in, thumbnailData); 2924 } 2925 } 2926 2927 // Check JpegInterchangeFormat(JFIF) tags to retrieve thumbnail offset & length values 2928 // and reads the corresponding bytes if stream does not support seek function 2929 private void handleThumbnailFromJfif(ByteOrderedDataInputStream in, HashMap thumbnailData) 2930 throws IOException { 2931 ExifAttribute jpegInterchangeFormatAttribute = 2932 (ExifAttribute) thumbnailData.get(TAG_JPEG_INTERCHANGE_FORMAT); 2933 ExifAttribute jpegInterchangeFormatLengthAttribute = 2934 (ExifAttribute) thumbnailData.get(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 2935 if (jpegInterchangeFormatAttribute != null 2936 && jpegInterchangeFormatLengthAttribute != null) { 2937 int thumbnailOffset = jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder); 2938 int thumbnailLength = jpegInterchangeFormatLengthAttribute.getIntValue(mExifByteOrder); 2939 2940 // The following code limits the size of thumbnail size not to overflow EXIF data area. 2941 thumbnailLength = Math.min(thumbnailLength, in.available() - thumbnailOffset); 2942 if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF 2943 || mMimeType == IMAGE_TYPE_RW2) { 2944 thumbnailOffset += mExifOffset; 2945 } else if (mMimeType == IMAGE_TYPE_ORF) { 2946 // Update offset value since RAF files have IFD data preceding MakerNote data. 2947 thumbnailOffset += mOrfMakerNoteOffset; 2948 } 2949 if (DEBUG) { 2950 Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset 2951 + ", length: " + thumbnailLength); 2952 } 2953 if (thumbnailOffset > 0 && thumbnailLength > 0) { 2954 mHasThumbnail = true; 2955 mThumbnailOffset = thumbnailOffset; 2956 mThumbnailLength = thumbnailLength; 2957 if (mFilename == null && mAssetInputStream == null) { 2958 // Save the thumbnail in memory if the input doesn't support reading again. 2959 byte[] thumbnailBytes = new byte[thumbnailLength]; 2960 in.seek(thumbnailOffset); 2961 in.readFully(thumbnailBytes); 2962 mThumbnailBytes = thumbnailBytes; 2963 } 2964 } 2965 } 2966 } 2967 2968 // Check StripOffsets & StripByteCounts tags to retrieve thumbnail offset & length values 2969 private void handleThumbnailFromStrips(ByteOrderedDataInputStream in, HashMap thumbnailData) 2970 throws IOException { 2971 ExifAttribute stripOffsetsAttribute = 2972 (ExifAttribute) thumbnailData.get(TAG_STRIP_OFFSETS); 2973 ExifAttribute stripByteCountsAttribute = 2974 (ExifAttribute) thumbnailData.get(TAG_STRIP_BYTE_COUNTS); 2975 2976 if (stripOffsetsAttribute != null && stripByteCountsAttribute != null) { 2977 long[] stripOffsets = 2978 convertToLongArray(stripOffsetsAttribute.getValue(mExifByteOrder)); 2979 long[] stripByteCounts = 2980 convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder)); 2981 2982 if (stripOffsets == null) { 2983 Log.w(TAG, "stripOffsets should not be null."); 2984 return; 2985 } 2986 if (stripByteCounts == null) { 2987 Log.w(TAG, "stripByteCounts should not be null."); 2988 return; 2989 } 2990 2991 long totalStripByteCount = 0; 2992 for (long byteCount : stripByteCounts) { 2993 totalStripByteCount += byteCount; 2994 } 2995 2996 // Set thumbnail byte array data for non-consecutive strip bytes 2997 byte[] totalStripBytes = new byte[(int) totalStripByteCount]; 2998 2999 int bytesRead = 0; 3000 int bytesAdded = 0; 3001 for (int i = 0; i < stripOffsets.length; i++) { 3002 int stripOffset = (int) stripOffsets[i]; 3003 int stripByteCount = (int) stripByteCounts[i]; 3004 3005 // Skip to offset 3006 int skipBytes = stripOffset - bytesRead; 3007 if (skipBytes < 0) { 3008 Log.d(TAG, "Invalid strip offset value"); 3009 } 3010 in.seek(skipBytes); 3011 bytesRead += skipBytes; 3012 3013 // Read strip bytes 3014 byte[] stripBytes = new byte[stripByteCount]; 3015 in.read(stripBytes); 3016 bytesRead += stripByteCount; 3017 3018 // Add bytes to array 3019 System.arraycopy(stripBytes, 0, totalStripBytes, bytesAdded, 3020 stripBytes.length); 3021 bytesAdded += stripBytes.length; 3022 } 3023 3024 mHasThumbnail = true; 3025 mThumbnailBytes = totalStripBytes; 3026 mThumbnailLength = totalStripBytes.length; 3027 } 3028 } 3029 3030 // Check if thumbnail data type is currently supported or not 3031 private boolean isSupportedDataType(HashMap thumbnailData) throws IOException { 3032 ExifAttribute bitsPerSampleAttribute = 3033 (ExifAttribute) thumbnailData.get(TAG_BITS_PER_SAMPLE); 3034 if (bitsPerSampleAttribute != null) { 3035 int[] bitsPerSampleValue = (int[]) bitsPerSampleAttribute.getValue(mExifByteOrder); 3036 3037 if (Arrays.equals(BITS_PER_SAMPLE_RGB, bitsPerSampleValue)) { 3038 return true; 3039 } 3040 3041 // See DNG Specification 1.4.0.0. Section 3, Compression. 3042 if (mMimeType == IMAGE_TYPE_DNG) { 3043 ExifAttribute photometricInterpretationAttribute = 3044 (ExifAttribute) thumbnailData.get(TAG_PHOTOMETRIC_INTERPRETATION); 3045 if (photometricInterpretationAttribute != null) { 3046 int photometricInterpretationValue 3047 = photometricInterpretationAttribute.getIntValue(mExifByteOrder); 3048 if ((photometricInterpretationValue == PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO 3049 && Arrays.equals(bitsPerSampleValue, BITS_PER_SAMPLE_GREYSCALE_2)) 3050 || ((photometricInterpretationValue == PHOTOMETRIC_INTERPRETATION_YCBCR) 3051 && (Arrays.equals(bitsPerSampleValue, BITS_PER_SAMPLE_RGB)))) { 3052 return true; 3053 } else { 3054 // TODO: Add support for lossless Huffman JPEG data 3055 } 3056 } 3057 } 3058 } 3059 if (DEBUG) { 3060 Log.d(TAG, "Unsupported data type value"); 3061 } 3062 return false; 3063 } 3064 3065 // Returns true if the image length and width values are <= 512. 3066 // See Section 4.8 of http://standardsproposals.bsigroup.com/Home/getPDF/567 3067 private boolean isThumbnail(HashMap map) throws IOException { 3068 ExifAttribute imageLengthAttribute = (ExifAttribute) map.get(TAG_IMAGE_LENGTH); 3069 ExifAttribute imageWidthAttribute = (ExifAttribute) map.get(TAG_IMAGE_WIDTH); 3070 3071 if (imageLengthAttribute != null && imageWidthAttribute != null) { 3072 int imageLengthValue = imageLengthAttribute.getIntValue(mExifByteOrder); 3073 int imageWidthValue = imageWidthAttribute.getIntValue(mExifByteOrder); 3074 if (imageLengthValue <= MAX_THUMBNAIL_SIZE && imageWidthValue <= MAX_THUMBNAIL_SIZE) { 3075 return true; 3076 } 3077 } 3078 return false; 3079 } 3080 3081 // Validate primary, preview, thumbnail image data by comparing image size 3082 private void validateImages(InputStream in) throws IOException { 3083 // Swap images based on size (primary > preview > thumbnail) 3084 swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_PREVIEW); 3085 swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_THUMBNAIL); 3086 swapBasedOnImageSize(IFD_TYPE_PREVIEW, IFD_TYPE_THUMBNAIL); 3087 3088 // Check if image has PixelXDimension/PixelYDimension tags, which contain valid image 3089 // sizes, excluding padding at the right end or bottom end of the image to make sure that 3090 // the values are multiples of 64. See JEITA CP-3451C Table 5 and Section 4.8.1. B. 3091 ExifAttribute pixelXDimAttribute = 3092 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_X_DIMENSION); 3093 ExifAttribute pixelYDimAttribute = 3094 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_Y_DIMENSION); 3095 if (pixelXDimAttribute != null && pixelYDimAttribute != null) { 3096 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, pixelXDimAttribute); 3097 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, pixelYDimAttribute); 3098 } 3099 3100 // Check whether thumbnail image exists and whether preview image satisfies the thumbnail 3101 // image requirements 3102 if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) { 3103 if (isThumbnail(mAttributes[IFD_TYPE_PREVIEW])) { 3104 mAttributes[IFD_TYPE_THUMBNAIL] = mAttributes[IFD_TYPE_PREVIEW]; 3105 mAttributes[IFD_TYPE_PREVIEW] = new HashMap<>(); 3106 } 3107 } 3108 3109 // Check if the thumbnail image satisfies the thumbnail size requirements 3110 if (!isThumbnail(mAttributes[IFD_TYPE_THUMBNAIL])) { 3111 Log.d(TAG, "No image meets the size requirements of a thumbnail image."); 3112 } 3113 } 3114 3115 /** 3116 * If image is uncompressed, ImageWidth/Length tags are used to store size info. 3117 * However, uncompressed images often store extra pixels around the edges of the final image, 3118 * which results in larger values for TAG_IMAGE_WIDTH and TAG_IMAGE_LENGTH tags. 3119 * This method corrects those tag values by checking first the values of TAG_DEFAULT_CROP_SIZE 3120 * See DNG Specification 1.4.0.0. Section 4. (DefaultCropSize) 3121 * 3122 * If image is a RW2 file, valid image sizes are stored in SensorBorder tags. 3123 * See tiff_parser.cc GetFullDimension32() 3124 * */ 3125 private void updateImageSizeValues(ByteOrderedDataInputStream in, int imageType) 3126 throws IOException { 3127 // Uncompressed image valid image size values 3128 ExifAttribute defaultCropSizeAttribute = 3129 (ExifAttribute) mAttributes[imageType].get(TAG_DEFAULT_CROP_SIZE); 3130 // RW2 image valid image size values 3131 ExifAttribute topBorderAttribute = 3132 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_TOP_BORDER); 3133 ExifAttribute leftBorderAttribute = 3134 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_LEFT_BORDER); 3135 ExifAttribute bottomBorderAttribute = 3136 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_BOTTOM_BORDER); 3137 ExifAttribute rightBorderAttribute = 3138 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_RIGHT_BORDER); 3139 3140 if (defaultCropSizeAttribute != null) { 3141 // Update for uncompressed image 3142 ExifAttribute defaultCropSizeXAttribute, defaultCropSizeYAttribute; 3143 if (defaultCropSizeAttribute.format == IFD_FORMAT_URATIONAL) { 3144 Rational[] defaultCropSizeValue = 3145 (Rational[]) defaultCropSizeAttribute.getValue(mExifByteOrder); 3146 if (defaultCropSizeValue == null || defaultCropSizeValue.length != 2) { 3147 Log.w(TAG, "Invalid crop size values. cropSize=" 3148 + Arrays.toString(defaultCropSizeValue)); 3149 return; 3150 } 3151 defaultCropSizeXAttribute = 3152 ExifAttribute.createURational(defaultCropSizeValue[0], mExifByteOrder); 3153 defaultCropSizeYAttribute = 3154 ExifAttribute.createURational(defaultCropSizeValue[1], mExifByteOrder); 3155 } else { 3156 int[] defaultCropSizeValue = 3157 (int[]) defaultCropSizeAttribute.getValue(mExifByteOrder); 3158 if (defaultCropSizeValue == null || defaultCropSizeValue.length != 2) { 3159 Log.w(TAG, "Invalid crop size values. cropSize=" 3160 + Arrays.toString(defaultCropSizeValue)); 3161 return; 3162 } 3163 defaultCropSizeXAttribute = 3164 ExifAttribute.createUShort(defaultCropSizeValue[0], mExifByteOrder); 3165 defaultCropSizeYAttribute = 3166 ExifAttribute.createUShort(defaultCropSizeValue[1], mExifByteOrder); 3167 } 3168 mAttributes[imageType].put(TAG_IMAGE_WIDTH, defaultCropSizeXAttribute); 3169 mAttributes[imageType].put(TAG_IMAGE_LENGTH, defaultCropSizeYAttribute); 3170 } else if (topBorderAttribute != null && leftBorderAttribute != null && 3171 bottomBorderAttribute != null && rightBorderAttribute != null) { 3172 // Update for RW2 image 3173 int topBorderValue = topBorderAttribute.getIntValue(mExifByteOrder); 3174 int bottomBorderValue = bottomBorderAttribute.getIntValue(mExifByteOrder); 3175 int rightBorderValue = rightBorderAttribute.getIntValue(mExifByteOrder); 3176 int leftBorderValue = leftBorderAttribute.getIntValue(mExifByteOrder); 3177 if (bottomBorderValue > topBorderValue && rightBorderValue > leftBorderValue) { 3178 int length = bottomBorderValue - topBorderValue; 3179 int width = rightBorderValue - leftBorderValue; 3180 ExifAttribute imageLengthAttribute = 3181 ExifAttribute.createUShort(length, mExifByteOrder); 3182 ExifAttribute imageWidthAttribute = 3183 ExifAttribute.createUShort(width, mExifByteOrder); 3184 mAttributes[imageType].put(TAG_IMAGE_LENGTH, imageLengthAttribute); 3185 mAttributes[imageType].put(TAG_IMAGE_WIDTH, imageWidthAttribute); 3186 } 3187 } else { 3188 retrieveJpegImageSize(in, imageType); 3189 } 3190 } 3191 3192 // Writes an Exif segment into the given output stream. 3193 private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream, 3194 int exifOffsetFromBeginning) throws IOException { 3195 // The following variables are for calculating each IFD tag group size in bytes. 3196 int[] ifdOffsets = new int[EXIF_TAGS.length]; 3197 int[] ifdDataSizes = new int[EXIF_TAGS.length]; 3198 3199 // Remove IFD pointer tags (we'll re-add it later.) 3200 for (ExifTag tag : EXIF_POINTER_TAGS) { 3201 removeAttribute(tag.name); 3202 } 3203 // Remove old thumbnail data 3204 removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name); 3205 removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name); 3206 3207 // Remove null value tags. 3208 for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) { 3209 for (Object obj : mAttributes[ifdType].entrySet().toArray()) { 3210 final Map.Entry entry = (Map.Entry) obj; 3211 if (entry.getValue() == null) { 3212 mAttributes[ifdType].remove(entry.getKey()); 3213 } 3214 } 3215 } 3216 3217 // Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD 3218 // offset when there is one or more tags in the thumbnail IFD. 3219 if (!mAttributes[IFD_TYPE_EXIF].isEmpty()) { 3220 mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[1].name, 3221 ExifAttribute.createULong(0, mExifByteOrder)); 3222 } 3223 if (!mAttributes[IFD_TYPE_GPS].isEmpty()) { 3224 mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[2].name, 3225 ExifAttribute.createULong(0, mExifByteOrder)); 3226 } 3227 if (!mAttributes[IFD_TYPE_INTEROPERABILITY].isEmpty()) { 3228 mAttributes[IFD_TYPE_EXIF].put(EXIF_POINTER_TAGS[3].name, 3229 ExifAttribute.createULong(0, mExifByteOrder)); 3230 } 3231 if (mHasThumbnail) { 3232 mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name, 3233 ExifAttribute.createULong(0, mExifByteOrder)); 3234 mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name, 3235 ExifAttribute.createULong(mThumbnailLength, mExifByteOrder)); 3236 } 3237 3238 // Calculate IFD group data area sizes. IFD group data area is assigned to save the entry 3239 // value which has a bigger size than 4 bytes. 3240 for (int i = 0; i < EXIF_TAGS.length; ++i) { 3241 int sum = 0; 3242 for (Map.Entry<String, ExifAttribute> entry : mAttributes[i].entrySet()) { 3243 final ExifAttribute exifAttribute = entry.getValue(); 3244 final int size = exifAttribute.size(); 3245 if (size > 4) { 3246 sum += size; 3247 } 3248 } 3249 ifdDataSizes[i] += sum; 3250 } 3251 3252 // Calculate IFD offsets. 3253 int position = 8; 3254 for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) { 3255 if (!mAttributes[ifdType].isEmpty()) { 3256 ifdOffsets[ifdType] = position; 3257 position += 2 + mAttributes[ifdType].size() * 12 + 4 + ifdDataSizes[ifdType]; 3258 } 3259 } 3260 if (mHasThumbnail) { 3261 int thumbnailOffset = position; 3262 mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name, 3263 ExifAttribute.createULong(thumbnailOffset, mExifByteOrder)); 3264 mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset; 3265 position += mThumbnailLength; 3266 } 3267 3268 // Calculate the total size 3269 int totalSize = position + 8; // eight bytes is for header part. 3270 if (DEBUG) { 3271 Log.d(TAG, "totalSize length: " + totalSize); 3272 for (int i = 0; i < EXIF_TAGS.length; ++i) { 3273 Log.d(TAG, String.format("index: %d, offsets: %d, tag count: %d, data sizes: %d", 3274 i, ifdOffsets[i], mAttributes[i].size(), ifdDataSizes[i])); 3275 } 3276 } 3277 3278 // Update IFD pointer tags with the calculated offsets. 3279 if (!mAttributes[IFD_TYPE_EXIF].isEmpty()) { 3280 mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[1].name, 3281 ExifAttribute.createULong(ifdOffsets[IFD_TYPE_EXIF], mExifByteOrder)); 3282 } 3283 if (!mAttributes[IFD_TYPE_GPS].isEmpty()) { 3284 mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[2].name, 3285 ExifAttribute.createULong(ifdOffsets[IFD_TYPE_GPS], mExifByteOrder)); 3286 } 3287 if (!mAttributes[IFD_TYPE_INTEROPERABILITY].isEmpty()) { 3288 mAttributes[IFD_TYPE_EXIF].put(EXIF_POINTER_TAGS[3].name, ExifAttribute.createULong( 3289 ifdOffsets[IFD_TYPE_INTEROPERABILITY], mExifByteOrder)); 3290 } 3291 3292 // Write TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1. 3293 dataOutputStream.writeUnsignedShort(totalSize); 3294 dataOutputStream.write(IDENTIFIER_EXIF_APP1); 3295 dataOutputStream.writeShort(mExifByteOrder == ByteOrder.BIG_ENDIAN 3296 ? BYTE_ALIGN_MM : BYTE_ALIGN_II); 3297 dataOutputStream.setByteOrder(mExifByteOrder); 3298 dataOutputStream.writeUnsignedShort(START_CODE); 3299 dataOutputStream.writeUnsignedInt(IFD_OFFSET); 3300 3301 // Write IFD groups. See JEITA CP-3451C Section 4.5.8. Figure 9. 3302 for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) { 3303 if (!mAttributes[ifdType].isEmpty()) { 3304 // See JEITA CP-3451C Section 4.6.2: IFD structure. 3305 // Write entry count 3306 dataOutputStream.writeUnsignedShort(mAttributes[ifdType].size()); 3307 3308 // Write entry info 3309 int dataOffset = ifdOffsets[ifdType] + 2 + mAttributes[ifdType].size() * 12 + 4; 3310 for (Map.Entry<String, ExifAttribute> entry : mAttributes[ifdType].entrySet()) { 3311 // Convert tag name to tag number. 3312 final ExifTag tag = sExifTagMapsForWriting[ifdType].get(entry.getKey()); 3313 final int tagNumber = tag.number; 3314 final ExifAttribute attribute = entry.getValue(); 3315 final int size = attribute.size(); 3316 3317 dataOutputStream.writeUnsignedShort(tagNumber); 3318 dataOutputStream.writeUnsignedShort(attribute.format); 3319 dataOutputStream.writeInt(attribute.numberOfComponents); 3320 if (size > 4) { 3321 dataOutputStream.writeUnsignedInt(dataOffset); 3322 dataOffset += size; 3323 } else { 3324 dataOutputStream.write(attribute.bytes); 3325 // Fill zero up to 4 bytes 3326 if (size < 4) { 3327 for (int i = size; i < 4; ++i) { 3328 dataOutputStream.writeByte(0); 3329 } 3330 } 3331 } 3332 } 3333 3334 // Write the next offset. It writes the offset of thumbnail IFD if there is one or 3335 // more tags in the thumbnail IFD when the current IFD is the primary image TIFF 3336 // IFD; Otherwise 0. 3337 if (ifdType == 0 && !mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) { 3338 dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_TYPE_THUMBNAIL]); 3339 } else { 3340 dataOutputStream.writeUnsignedInt(0); 3341 } 3342 3343 // Write values of data field exceeding 4 bytes after the next offset. 3344 for (Map.Entry<String, ExifAttribute> entry : mAttributes[ifdType].entrySet()) { 3345 ExifAttribute attribute = entry.getValue(); 3346 3347 if (attribute.bytes.length > 4) { 3348 dataOutputStream.write(attribute.bytes, 0, attribute.bytes.length); 3349 } 3350 } 3351 } 3352 } 3353 3354 // Write thumbnail 3355 if (mHasThumbnail) { 3356 dataOutputStream.write(getThumbnailBytes()); 3357 } 3358 3359 // Reset the byte order to big endian in order to write remaining parts of the JPEG file. 3360 dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN); 3361 3362 return totalSize; 3363 } 3364 3365 /** 3366 * Determines the data format of EXIF entry value. 3367 * 3368 * @param entryValue The value to be determined. 3369 * @return Returns two data formats gussed as a pair in integer. If there is no two candidate 3370 data formats for the given entry value, returns {@code -1} in the second of the pair. 3371 */ 3372 private static Pair<Integer, Integer> guessDataFormat(String entryValue) { 3373 // See TIFF 6.0 Section 2, "Image File Directory". 3374 // Take the first component if there are more than one component. 3375 if (entryValue.contains(",")) { 3376 String[] entryValues = entryValue.split(","); 3377 Pair<Integer, Integer> dataFormat = guessDataFormat(entryValues[0]); 3378 if (dataFormat.first == IFD_FORMAT_STRING) { 3379 return dataFormat; 3380 } 3381 for (int i = 1; i < entryValues.length; ++i) { 3382 final Pair<Integer, Integer> guessDataFormat = guessDataFormat(entryValues[i]); 3383 int first = -1, second = -1; 3384 if (guessDataFormat.first.equals(dataFormat.first) 3385 || guessDataFormat.second.equals(dataFormat.first)) { 3386 first = dataFormat.first; 3387 } 3388 if (dataFormat.second != -1 && (guessDataFormat.first.equals(dataFormat.second) 3389 || guessDataFormat.second.equals(dataFormat.second))) { 3390 second = dataFormat.second; 3391 } 3392 if (first == -1 && second == -1) { 3393 return new Pair<>(IFD_FORMAT_STRING, -1); 3394 } 3395 if (first == -1) { 3396 dataFormat = new Pair<>(second, -1); 3397 continue; 3398 } 3399 if (second == -1) { 3400 dataFormat = new Pair<>(first, -1); 3401 continue; 3402 } 3403 } 3404 return dataFormat; 3405 } 3406 3407 if (entryValue.contains("/")) { 3408 String[] rationalNumber = entryValue.split("/"); 3409 if (rationalNumber.length == 2) { 3410 try { 3411 long numerator = (long) Double.parseDouble(rationalNumber[0]); 3412 long denominator = (long) Double.parseDouble(rationalNumber[1]); 3413 if (numerator < 0L || denominator < 0L) { 3414 return new Pair<>(IFD_FORMAT_SRATIONAL, -1); 3415 } 3416 if (numerator > Integer.MAX_VALUE || denominator > Integer.MAX_VALUE) { 3417 return new Pair<>(IFD_FORMAT_URATIONAL, -1); 3418 } 3419 return new Pair<>(IFD_FORMAT_SRATIONAL, IFD_FORMAT_URATIONAL); 3420 } catch (NumberFormatException e) { 3421 // Ignored 3422 } 3423 } 3424 return new Pair<>(IFD_FORMAT_STRING, -1); 3425 } 3426 try { 3427 Long longValue = Long.parseLong(entryValue); 3428 if (longValue >= 0 && longValue <= 65535) { 3429 return new Pair<>(IFD_FORMAT_USHORT, IFD_FORMAT_ULONG); 3430 } 3431 if (longValue < 0) { 3432 return new Pair<>(IFD_FORMAT_SLONG, -1); 3433 } 3434 return new Pair<>(IFD_FORMAT_ULONG, -1); 3435 } catch (NumberFormatException e) { 3436 // Ignored 3437 } 3438 try { 3439 Double.parseDouble(entryValue); 3440 return new Pair<>(IFD_FORMAT_DOUBLE, -1); 3441 } catch (NumberFormatException e) { 3442 // Ignored 3443 } 3444 return new Pair<>(IFD_FORMAT_STRING, -1); 3445 } 3446 3447 // An input stream to parse EXIF data area, which can be written in either little or big endian 3448 // order. 3449 private static class ByteOrderedDataInputStream extends InputStream implements DataInput { 3450 private static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN; 3451 private static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN; 3452 3453 private DataInputStream mDataInputStream; 3454 private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN; 3455 private final int mLength; 3456 private int mPosition; 3457 3458 public ByteOrderedDataInputStream(InputStream in) throws IOException { 3459 mDataInputStream = new DataInputStream(in); 3460 mLength = mDataInputStream.available(); 3461 mPosition = 0; 3462 mDataInputStream.mark(mLength); 3463 } 3464 3465 public ByteOrderedDataInputStream(byte[] bytes) throws IOException { 3466 this(new ByteArrayInputStream(bytes)); 3467 } 3468 3469 public void setByteOrder(ByteOrder byteOrder) { 3470 mByteOrder = byteOrder; 3471 } 3472 3473 public void seek(long byteCount) throws IOException { 3474 if (mPosition > byteCount) { 3475 mPosition = 0; 3476 mDataInputStream.reset(); 3477 mDataInputStream.mark(mLength); 3478 } else { 3479 byteCount -= mPosition; 3480 } 3481 3482 if (skipBytes((int) byteCount) != (int) byteCount) { 3483 throw new IOException("Couldn't seek up to the byteCount"); 3484 } 3485 } 3486 3487 public int peek() { 3488 return mPosition; 3489 } 3490 3491 @Override 3492 public int available() throws IOException { 3493 return mDataInputStream.available(); 3494 } 3495 3496 @Override 3497 public int read() throws IOException { 3498 ++mPosition; 3499 return mDataInputStream.read(); 3500 } 3501 3502 @Override 3503 public int read(byte[] b, int off, int len) throws IOException { 3504 int bytesRead = mDataInputStream.read(b, off, len); 3505 mPosition += bytesRead; 3506 return bytesRead; 3507 } 3508 3509 @Override 3510 public int readUnsignedByte() throws IOException { 3511 ++mPosition; 3512 return mDataInputStream.readUnsignedByte(); 3513 } 3514 3515 @Override 3516 public String readLine() throws IOException { 3517 Log.d(TAG, "Currently unsupported"); 3518 return null; 3519 } 3520 3521 @Override 3522 public boolean readBoolean() throws IOException { 3523 ++mPosition; 3524 return mDataInputStream.readBoolean(); 3525 } 3526 3527 @Override 3528 public char readChar() throws IOException { 3529 mPosition += 2; 3530 return mDataInputStream.readChar(); 3531 } 3532 3533 @Override 3534 public String readUTF() throws IOException { 3535 mPosition += 2; 3536 return mDataInputStream.readUTF(); 3537 } 3538 3539 @Override 3540 public void readFully(byte[] buffer, int offset, int length) throws IOException { 3541 mPosition += length; 3542 if (mPosition > mLength) { 3543 throw new EOFException(); 3544 } 3545 if (mDataInputStream.read(buffer, offset, length) != length) { 3546 throw new IOException("Couldn't read up to the length of buffer"); 3547 } 3548 } 3549 3550 @Override 3551 public void readFully(byte[] buffer) throws IOException { 3552 mPosition += buffer.length; 3553 if (mPosition > mLength) { 3554 throw new EOFException(); 3555 } 3556 if (mDataInputStream.read(buffer, 0, buffer.length) != buffer.length) { 3557 throw new IOException("Couldn't read up to the length of buffer"); 3558 } 3559 } 3560 3561 @Override 3562 public byte readByte() throws IOException { 3563 ++mPosition; 3564 if (mPosition > mLength) { 3565 throw new EOFException(); 3566 } 3567 int ch = mDataInputStream.read(); 3568 if (ch < 0) { 3569 throw new EOFException(); 3570 } 3571 return (byte) ch; 3572 } 3573 3574 @Override 3575 public short readShort() throws IOException { 3576 mPosition += 2; 3577 if (mPosition > mLength) { 3578 throw new EOFException(); 3579 } 3580 int ch1 = mDataInputStream.read(); 3581 int ch2 = mDataInputStream.read(); 3582 if ((ch1 | ch2) < 0) { 3583 throw new EOFException(); 3584 } 3585 if (mByteOrder == LITTLE_ENDIAN) { 3586 return (short) ((ch2 << 8) + (ch1)); 3587 } else if (mByteOrder == BIG_ENDIAN) { 3588 return (short) ((ch1 << 8) + (ch2)); 3589 } 3590 throw new IOException("Invalid byte order: " + mByteOrder); 3591 } 3592 3593 @Override 3594 public int readInt() throws IOException { 3595 mPosition += 4; 3596 if (mPosition > mLength) { 3597 throw new EOFException(); 3598 } 3599 int ch1 = mDataInputStream.read(); 3600 int ch2 = mDataInputStream.read(); 3601 int ch3 = mDataInputStream.read(); 3602 int ch4 = mDataInputStream.read(); 3603 if ((ch1 | ch2 | ch3 | ch4) < 0) { 3604 throw new EOFException(); 3605 } 3606 if (mByteOrder == LITTLE_ENDIAN) { 3607 return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1); 3608 } else if (mByteOrder == BIG_ENDIAN) { 3609 return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4); 3610 } 3611 throw new IOException("Invalid byte order: " + mByteOrder); 3612 } 3613 3614 @Override 3615 public int skipBytes(int byteCount) throws IOException { 3616 int totalSkip = Math.min(byteCount, mLength - mPosition); 3617 int skipped = 0; 3618 while (skipped < totalSkip) { 3619 skipped += mDataInputStream.skipBytes(totalSkip - skipped); 3620 } 3621 mPosition += skipped; 3622 return skipped; 3623 } 3624 3625 @Override 3626 public int readUnsignedShort() throws IOException { 3627 mPosition += 2; 3628 if (mPosition > mLength) { 3629 throw new EOFException(); 3630 } 3631 int ch1 = mDataInputStream.read(); 3632 int ch2 = mDataInputStream.read(); 3633 if ((ch1 | ch2) < 0) { 3634 throw new EOFException(); 3635 } 3636 if (mByteOrder == LITTLE_ENDIAN) { 3637 return ((ch2 << 8) + (ch1)); 3638 } else if (mByteOrder == BIG_ENDIAN) { 3639 return ((ch1 << 8) + (ch2)); 3640 } 3641 throw new IOException("Invalid byte order: " + mByteOrder); 3642 } 3643 3644 public long readUnsignedInt() throws IOException { 3645 return readInt() & 0xffffffffL; 3646 } 3647 3648 @Override 3649 public long readLong() throws IOException { 3650 mPosition += 8; 3651 if (mPosition > mLength) { 3652 throw new EOFException(); 3653 } 3654 int ch1 = mDataInputStream.read(); 3655 int ch2 = mDataInputStream.read(); 3656 int ch3 = mDataInputStream.read(); 3657 int ch4 = mDataInputStream.read(); 3658 int ch5 = mDataInputStream.read(); 3659 int ch6 = mDataInputStream.read(); 3660 int ch7 = mDataInputStream.read(); 3661 int ch8 = mDataInputStream.read(); 3662 if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) { 3663 throw new EOFException(); 3664 } 3665 if (mByteOrder == LITTLE_ENDIAN) { 3666 return (((long) ch8 << 56) + ((long) ch7 << 48) + ((long) ch6 << 40) 3667 + ((long) ch5 << 32) + ((long) ch4 << 24) + ((long) ch3 << 16) 3668 + ((long) ch2 << 8) + (long) ch1); 3669 } else if (mByteOrder == BIG_ENDIAN) { 3670 return (((long) ch1 << 56) + ((long) ch2 << 48) + ((long) ch3 << 40) 3671 + ((long) ch4 << 32) + ((long) ch5 << 24) + ((long) ch6 << 16) 3672 + ((long) ch7 << 8) + (long) ch8); 3673 } 3674 throw new IOException("Invalid byte order: " + mByteOrder); 3675 } 3676 3677 @Override 3678 public float readFloat() throws IOException { 3679 return Float.intBitsToFloat(readInt()); 3680 } 3681 3682 @Override 3683 public double readDouble() throws IOException { 3684 return Double.longBitsToDouble(readLong()); 3685 } 3686 } 3687 3688 // An output stream to write EXIF data area, which can be written in either little or big endian 3689 // order. 3690 private static class ByteOrderedDataOutputStream extends FilterOutputStream { 3691 private final OutputStream mOutputStream; 3692 private ByteOrder mByteOrder; 3693 3694 public ByteOrderedDataOutputStream(OutputStream out, ByteOrder byteOrder) { 3695 super(out); 3696 mOutputStream = out; 3697 mByteOrder = byteOrder; 3698 } 3699 3700 public void setByteOrder(ByteOrder byteOrder) { 3701 mByteOrder = byteOrder; 3702 } 3703 3704 @Override 3705 public void write(byte[] bytes) throws IOException { 3706 mOutputStream.write(bytes); 3707 } 3708 3709 @Override 3710 public void write(byte[] bytes, int offset, int length) throws IOException { 3711 mOutputStream.write(bytes, offset, length); 3712 } 3713 3714 public void writeByte(int val) throws IOException { 3715 mOutputStream.write(val); 3716 } 3717 3718 public void writeShort(short val) throws IOException { 3719 if (mByteOrder == ByteOrder.LITTLE_ENDIAN) { 3720 mOutputStream.write((val >>> 0) & 0xFF); 3721 mOutputStream.write((val >>> 8) & 0xFF); 3722 } else if (mByteOrder == ByteOrder.BIG_ENDIAN) { 3723 mOutputStream.write((val >>> 8) & 0xFF); 3724 mOutputStream.write((val >>> 0) & 0xFF); 3725 } 3726 } 3727 3728 public void writeInt(int val) throws IOException { 3729 if (mByteOrder == ByteOrder.LITTLE_ENDIAN) { 3730 mOutputStream.write((val >>> 0) & 0xFF); 3731 mOutputStream.write((val >>> 8) & 0xFF); 3732 mOutputStream.write((val >>> 16) & 0xFF); 3733 mOutputStream.write((val >>> 24) & 0xFF); 3734 } else if (mByteOrder == ByteOrder.BIG_ENDIAN) { 3735 mOutputStream.write((val >>> 24) & 0xFF); 3736 mOutputStream.write((val >>> 16) & 0xFF); 3737 mOutputStream.write((val >>> 8) & 0xFF); 3738 mOutputStream.write((val >>> 0) & 0xFF); 3739 } 3740 } 3741 3742 public void writeUnsignedShort(int val) throws IOException { 3743 writeShort((short) val); 3744 } 3745 3746 public void writeUnsignedInt(long val) throws IOException { 3747 writeInt((int) val); 3748 } 3749 } 3750 3751 // Swaps image data based on image size 3752 private void swapBasedOnImageSize(@IfdType int firstIfdType, @IfdType int secondIfdType) 3753 throws IOException { 3754 if (mAttributes[firstIfdType].isEmpty() || mAttributes[secondIfdType].isEmpty()) { 3755 if (DEBUG) { 3756 Log.d(TAG, "Cannot perform swap since only one image data exists"); 3757 } 3758 return; 3759 } 3760 3761 ExifAttribute firstImageLengthAttribute = 3762 (ExifAttribute) mAttributes[firstIfdType].get(TAG_IMAGE_LENGTH); 3763 ExifAttribute firstImageWidthAttribute = 3764 (ExifAttribute) mAttributes[firstIfdType].get(TAG_IMAGE_WIDTH); 3765 ExifAttribute secondImageLengthAttribute = 3766 (ExifAttribute) mAttributes[secondIfdType].get(TAG_IMAGE_LENGTH); 3767 ExifAttribute secondImageWidthAttribute = 3768 (ExifAttribute) mAttributes[secondIfdType].get(TAG_IMAGE_WIDTH); 3769 3770 if (firstImageLengthAttribute == null || firstImageWidthAttribute == null) { 3771 if (DEBUG) { 3772 Log.d(TAG, "First image does not contain valid size information"); 3773 } 3774 } else if (secondImageLengthAttribute == null || secondImageWidthAttribute == null) { 3775 if (DEBUG) { 3776 Log.d(TAG, "Second image does not contain valid size information"); 3777 } 3778 } else { 3779 int firstImageLengthValue = firstImageLengthAttribute.getIntValue(mExifByteOrder); 3780 int firstImageWidthValue = firstImageWidthAttribute.getIntValue(mExifByteOrder); 3781 int secondImageLengthValue = secondImageLengthAttribute.getIntValue(mExifByteOrder); 3782 int secondImageWidthValue = secondImageWidthAttribute.getIntValue(mExifByteOrder); 3783 3784 if (firstImageLengthValue < secondImageLengthValue && 3785 firstImageWidthValue < secondImageWidthValue) { 3786 HashMap<String, ExifAttribute> tempMap = mAttributes[firstIfdType]; 3787 mAttributes[firstIfdType] = mAttributes[secondIfdType]; 3788 mAttributes[secondIfdType] = tempMap; 3789 } 3790 } 3791 } 3792 3793 /** 3794 * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null. 3795 */ 3796 private static void closeQuietly(Closeable closeable) { 3797 if (closeable != null) { 3798 try { 3799 closeable.close(); 3800 } catch (RuntimeException rethrown) { 3801 throw rethrown; 3802 } catch (Exception ignored) { 3803 } 3804 } 3805 } 3806 3807 /** 3808 * Copies all of the bytes from {@code in} to {@code out}. Neither stream is closed. 3809 * Returns the total number of bytes transferred. 3810 */ 3811 private static int copy(InputStream in, OutputStream out) throws IOException { 3812 int total = 0; 3813 byte[] buffer = new byte[8192]; 3814 int c; 3815 while ((c = in.read(buffer)) != -1) { 3816 total += c; 3817 out.write(buffer, 0, c); 3818 } 3819 return total; 3820 } 3821 3822 /** 3823 * Convert given int[] to long[]. If long[] is given, just return it. 3824 * Return null for other types of input. 3825 */ 3826 private static long[] convertToLongArray(Object inputObj) { 3827 if (inputObj instanceof int[]) { 3828 int[] input = (int[]) inputObj; 3829 long[] result = new long[input.length]; 3830 for (int i = 0; i < input.length; i++) { 3831 result[i] = input[i]; 3832 } 3833 return result; 3834 } else if (inputObj instanceof long[]) { 3835 return (long[]) inputObj; 3836 } 3837 return null; 3838 } 3839} 3840