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