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