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