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