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