ExifInterface.java revision 043785660ca6450c96408377fe563f627d036353
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 FileInputStream in = null; 1880 try { 1881 if (mAssetInputStream != null) { 1882 return nativeGetThumbnailFromAsset( 1883 mAssetInputStream.getNativeAsset(), mThumbnailOffset, mThumbnailLength); 1884 } else if (mFilename != null) { 1885 in = new FileInputStream(mFilename); 1886 } else if (mSeekableFileDescriptor != null) { 1887 FileDescriptor fileDescriptor = Os.dup(mSeekableFileDescriptor); 1888 Os.lseek(fileDescriptor, 0, OsConstants.SEEK_SET); 1889 in = new FileInputStream(fileDescriptor); 1890 } 1891 if (in == null) { 1892 // Should not be reached this. 1893 throw new FileNotFoundException(); 1894 } 1895 if (in.skip(mThumbnailOffset) != mThumbnailOffset) { 1896 throw new IOException("Corrupted image"); 1897 } 1898 byte[] buffer = new byte[mThumbnailLength]; 1899 if (in.read(buffer) != mThumbnailLength) { 1900 throw new IOException("Corrupted image"); 1901 } 1902 mThumbnailBytes = buffer; 1903 return buffer; 1904 } catch (IOException | ErrnoException e) { 1905 // Couldn't get a thumbnail image. 1906 } finally { 1907 IoUtils.closeQuietly(in); 1908 } 1909 return null; 1910 } 1911 1912 /** 1913 * Creates and returns a Bitmap object of the thumbnail image based on the byte array and the 1914 * thumbnail compression value, or {@code null} if the compression type is unsupported. 1915 */ 1916 public Bitmap getThumbnailBitmap() { 1917 if (!mHasThumbnail) { 1918 return null; 1919 } else if (mThumbnailBytes == null) { 1920 mThumbnailBytes = getThumbnailBytes(); 1921 } 1922 1923 if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) { 1924 return BitmapFactory.decodeByteArray(mThumbnailBytes, 0, mThumbnailLength); 1925 } else if (mThumbnailCompression == DATA_UNCOMPRESSED) { 1926 int[] rgbValues = new int[mThumbnailBytes.length / 3]; 1927 byte alpha = (byte) 0xff000000; 1928 for (int i = 0; i < rgbValues.length; i++) { 1929 rgbValues[i] = alpha + (mThumbnailBytes[3 * i] << 16) 1930 + (mThumbnailBytes[3 * i + 1] << 8) + mThumbnailBytes[3 * i + 2]; 1931 } 1932 1933 ExifAttribute imageLengthAttribute = 1934 (ExifAttribute) mAttributes[IFD_THUMBNAIL_HINT].get(TAG_IMAGE_LENGTH); 1935 ExifAttribute imageWidthAttribute = 1936 (ExifAttribute) mAttributes[IFD_THUMBNAIL_HINT].get(TAG_IMAGE_WIDTH); 1937 if (imageLengthAttribute != null && imageWidthAttribute != null) { 1938 int imageLength = imageLengthAttribute.getIntValue(mExifByteOrder); 1939 int imageWidth = imageWidthAttribute.getIntValue(mExifByteOrder); 1940 return Bitmap.createBitmap( 1941 rgbValues, imageWidth, imageLength, Bitmap.Config.ARGB_8888); 1942 } 1943 } 1944 return null; 1945 } 1946 1947 /** 1948 * Returns true if thumbnail image is JPEG Compressed, or false if either thumbnail image does 1949 * not exist or thumbnail image is uncompressed. 1950 */ 1951 public boolean isThumbnailCompressed() { 1952 if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) { 1953 return true; 1954 } 1955 return false; 1956 } 1957 1958 /** 1959 * Returns the offset and length of thumbnail inside the image file, or 1960 * {@code null} if there is no thumbnail. 1961 * 1962 * @return two-element array, the offset in the first value, and length in 1963 * the second, or {@code null} if no thumbnail was found. 1964 */ 1965 public long[] getThumbnailRange() { 1966 if (!mHasThumbnail) { 1967 return null; 1968 } 1969 1970 long[] range = new long[2]; 1971 range[0] = mThumbnailOffset; 1972 range[1] = mThumbnailLength; 1973 1974 return range; 1975 } 1976 1977 /** 1978 * Stores the latitude and longitude value in a float array. The first element is 1979 * the latitude, and the second element is the longitude. Returns false if the 1980 * Exif tags are not available. 1981 */ 1982 public boolean getLatLong(float output[]) { 1983 String latValue = getAttribute(TAG_GPS_LATITUDE); 1984 String latRef = getAttribute(TAG_GPS_LATITUDE_REF); 1985 String lngValue = getAttribute(TAG_GPS_LONGITUDE); 1986 String lngRef = getAttribute(TAG_GPS_LONGITUDE_REF); 1987 1988 if (latValue != null && latRef != null && lngValue != null && lngRef != null) { 1989 try { 1990 output[0] = convertRationalLatLonToFloat(latValue, latRef); 1991 output[1] = convertRationalLatLonToFloat(lngValue, lngRef); 1992 return true; 1993 } catch (IllegalArgumentException e) { 1994 // if values are not parseable 1995 } 1996 } 1997 1998 return false; 1999 } 2000 2001 /** 2002 * Return the altitude in meters. If the exif tag does not exist, return 2003 * <var>defaultValue</var>. 2004 * 2005 * @param defaultValue the value to return if the tag is not available. 2006 */ 2007 public double getAltitude(double defaultValue) { 2008 double altitude = getAttributeDouble(TAG_GPS_ALTITUDE, -1); 2009 int ref = getAttributeInt(TAG_GPS_ALTITUDE_REF, -1); 2010 2011 if (altitude >= 0 && ref >= 0) { 2012 return (altitude * ((ref == 1) ? -1 : 1)); 2013 } else { 2014 return defaultValue; 2015 } 2016 } 2017 2018 /** 2019 * Returns number of milliseconds since Jan. 1, 1970, midnight local time. 2020 * Returns -1 if the date time information if not available. 2021 * @hide 2022 */ 2023 public long getDateTime() { 2024 String dateTimeString = getAttribute(TAG_DATETIME); 2025 if (dateTimeString == null 2026 || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1; 2027 2028 ParsePosition pos = new ParsePosition(0); 2029 try { 2030 // The exif field is in local time. Parsing it as if it is UTC will yield time 2031 // since 1/1/1970 local time 2032 Date datetime = sFormatter.parse(dateTimeString, pos); 2033 if (datetime == null) return -1; 2034 long msecs = datetime.getTime(); 2035 2036 String subSecs = getAttribute(TAG_SUBSEC_TIME); 2037 if (subSecs != null) { 2038 try { 2039 long sub = Long.parseLong(subSecs); 2040 while (sub > 1000) { 2041 sub /= 10; 2042 } 2043 msecs += sub; 2044 } catch (NumberFormatException e) { 2045 // Ignored 2046 } 2047 } 2048 return msecs; 2049 } catch (IllegalArgumentException e) { 2050 return -1; 2051 } 2052 } 2053 2054 /** 2055 * Returns number of milliseconds since Jan. 1, 1970, midnight UTC. 2056 * Returns -1 if the date time information if not available. 2057 * @hide 2058 */ 2059 public long getGpsDateTime() { 2060 String date = getAttribute(TAG_GPS_DATESTAMP); 2061 String time = getAttribute(TAG_GPS_TIMESTAMP); 2062 if (date == null || time == null 2063 || (!sNonZeroTimePattern.matcher(date).matches() 2064 && !sNonZeroTimePattern.matcher(time).matches())) { 2065 return -1; 2066 } 2067 2068 String dateTimeString = date + ' ' + time; 2069 2070 ParsePosition pos = new ParsePosition(0); 2071 try { 2072 Date datetime = sFormatter.parse(dateTimeString, pos); 2073 if (datetime == null) return -1; 2074 return datetime.getTime(); 2075 } catch (IllegalArgumentException e) { 2076 return -1; 2077 } 2078 } 2079 2080 private static float convertRationalLatLonToFloat(String rationalString, String ref) { 2081 try { 2082 String [] parts = rationalString.split(","); 2083 2084 String [] pair; 2085 pair = parts[0].split("/"); 2086 double degrees = Double.parseDouble(pair[0].trim()) 2087 / Double.parseDouble(pair[1].trim()); 2088 2089 pair = parts[1].split("/"); 2090 double minutes = Double.parseDouble(pair[0].trim()) 2091 / Double.parseDouble(pair[1].trim()); 2092 2093 pair = parts[2].split("/"); 2094 double seconds = Double.parseDouble(pair[0].trim()) 2095 / Double.parseDouble(pair[1].trim()); 2096 2097 double result = degrees + (minutes / 60.0) + (seconds / 3600.0); 2098 if ((ref.equals("S") || ref.equals("W"))) { 2099 return (float) -result; 2100 } 2101 return (float) result; 2102 } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { 2103 // Not valid 2104 throw new IllegalArgumentException(); 2105 } 2106 } 2107 2108 // Checks the type of image file 2109 private int getMimeType(BufferedInputStream in) throws IOException { 2110 in.mark(SIGNATURE_CHECK_SIZE); 2111 byte[] signatureCheckBytes = new byte[SIGNATURE_CHECK_SIZE]; 2112 if (in.read(signatureCheckBytes) != SIGNATURE_CHECK_SIZE) { 2113 throw new EOFException(); 2114 } 2115 in.reset(); 2116 if (isJpegFormat(signatureCheckBytes)) { 2117 return IMAGE_TYPE_JPEG; 2118 } else if (isRafFormat(signatureCheckBytes)) { 2119 return IMAGE_TYPE_RAF; 2120 } else if (isOrfFormat(signatureCheckBytes)) { 2121 return IMAGE_TYPE_ORF; 2122 } else if (isRw2Format(signatureCheckBytes)) { 2123 return IMAGE_TYPE_RW2; 2124 } 2125 // Certain file formats (PEF) are identified in readImageFileDirectory() 2126 return IMAGE_TYPE_UNKNOWN; 2127 } 2128 2129 /** 2130 * This method looks at the first 3 bytes to determine if this file is a JPEG file. 2131 * See http://www.media.mit.edu/pia/Research/deepview/exif.html, "JPEG format and Marker" 2132 */ 2133 private static boolean isJpegFormat(byte[] signatureCheckBytes) throws IOException { 2134 for (int i = 0; i < JPEG_SIGNATURE.length; i++) { 2135 if (signatureCheckBytes[i] != JPEG_SIGNATURE[i]) { 2136 return false; 2137 } 2138 } 2139 return true; 2140 } 2141 2142 /** 2143 * This method looks at the first 15 bytes to determine if this file is a RAF file. 2144 * There is no official specification for RAF files from Fuji, but there is an online archive of 2145 * image file specifications: 2146 * http://fileformats.archiveteam.org/wiki/Fujifilm_RAF 2147 */ 2148 private boolean isRafFormat(byte[] signatureCheckBytes) throws IOException { 2149 byte[] rafSignatureBytes = RAF_SIGNATURE.getBytes(); 2150 for (int i = 0; i < rafSignatureBytes.length; i++) { 2151 if (signatureCheckBytes[i] != rafSignatureBytes[i]) { 2152 return false; 2153 } 2154 } 2155 return true; 2156 } 2157 2158 /** 2159 * ORF has a similar structure to TIFF but it contains a different signature at the TIFF Header. 2160 * This method looks at the 2 bytes following the Byte Order bytes to determine if this file is 2161 * an ORF file. 2162 * There is no official specification for ORF files from Olympus, but there is an online archive 2163 * of image file specifications: 2164 * http://fileformats.archiveteam.org/wiki/Olympus_ORF 2165 */ 2166 private boolean isOrfFormat(byte[] signatureCheckBytes) throws IOException { 2167 ByteOrderAwarenessDataInputStream signatureInputStream = 2168 new ByteOrderAwarenessDataInputStream(signatureCheckBytes); 2169 // Read byte order 2170 mExifByteOrder = readByteOrder(signatureInputStream); 2171 // Set byte order 2172 signatureInputStream.setByteOrder(mExifByteOrder); 2173 2174 short orfSignature = signatureInputStream.readShort(); 2175 if (orfSignature == ORF_SIGNATURE_1 || orfSignature == ORF_SIGNATURE_2) { 2176 return true; 2177 } 2178 return false; 2179 } 2180 2181 /** 2182 * RW2 is TIFF-based, but stores 0x55 signature byte instead of 0x42 at the header 2183 * See http://lclevy.free.fr/raw/ 2184 */ 2185 private boolean isRw2Format(byte[] signatureCheckBytes) throws IOException { 2186 ByteOrderAwarenessDataInputStream signatureInputStream = 2187 new ByteOrderAwarenessDataInputStream(signatureCheckBytes); 2188 // Read byte order 2189 mExifByteOrder = readByteOrder(signatureInputStream); 2190 // Set byte order 2191 signatureInputStream.setByteOrder(mExifByteOrder); 2192 2193 short signatureByte = signatureInputStream.readShort(); 2194 if (signatureByte == RW2_SIGNATURE) { 2195 return true; 2196 } 2197 return false; 2198 } 2199 2200 /** 2201 * Loads EXIF attributes from a JPEG input stream. 2202 * 2203 * @param inputStream The input stream that starts with the JPEG data. 2204 * @param jpegOffset The offset value in input stream for JPEG data. 2205 * @param imageTypes The image type from which to retrieve metadata. Use IFD_TIFF_HINT for 2206 * primary image, IFD_PREVIEW_HINT for preview image, and 2207 * IFD_THUMBNAIL_HINT for thumbnail image. 2208 * @throws IOException If the data contains invalid JPEG markers, offsets, or length values. 2209 */ 2210 private void getJpegAttributes(InputStream inputStream, int jpegOffset, int imageType) 2211 throws IOException { 2212 // See JPEG File Interchange Format Specification, "JFIF Specification" 2213 if (DEBUG) { 2214 Log.d(TAG, "getJpegAttributes starting with: " + inputStream); 2215 } 2216 2217 DataInputStream dataInputStream = new DataInputStream(inputStream); 2218 // Mark current position to reset after retrieving data 2219 dataInputStream.mark(dataInputStream.available()); 2220 2221 // Skip to JPEG data 2222 if (dataInputStream.skip(jpegOffset) != jpegOffset) { 2223 throw new IOException("Invalid JPEG offset"); 2224 } 2225 int bytesRead = jpegOffset; 2226 2227 byte marker; 2228 if ((marker = dataInputStream.readByte()) != MARKER) { 2229 throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff)); 2230 } 2231 ++bytesRead; 2232 if (dataInputStream.readByte() != MARKER_SOI) { 2233 throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff)); 2234 } 2235 ++bytesRead; 2236 while (true) { 2237 marker = dataInputStream.readByte(); 2238 if (marker != MARKER) { 2239 throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff)); 2240 } 2241 ++bytesRead; 2242 marker = dataInputStream.readByte(); 2243 if (DEBUG) { 2244 Log.d(TAG, "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff)); 2245 } 2246 ++bytesRead; 2247 2248 // EOI indicates the end of an image and in case of SOS, JPEG image stream starts and 2249 // the image data will terminate right after. 2250 if (marker == MARKER_EOI || marker == MARKER_SOS) { 2251 break; 2252 } 2253 int length = dataInputStream.readUnsignedShort() - 2; 2254 bytesRead += 2; 2255 if (DEBUG) { 2256 Log.d(TAG, "JPEG segment: " + Integer.toHexString(marker & 0xff) + " (length: " 2257 + (length + 2) + ")"); 2258 } 2259 if (length < 0) { 2260 throw new IOException("Invalid length"); 2261 } 2262 switch (marker) { 2263 case MARKER_APP1: { 2264 if (DEBUG) { 2265 Log.d(TAG, "MARKER_APP1"); 2266 } 2267 if (length < 6) { 2268 // Skip if it's not an EXIF APP1 segment. 2269 break; 2270 } 2271 byte[] identifier = new byte[6]; 2272 if (inputStream.read(identifier) != 6) { 2273 throw new IOException("Invalid exif"); 2274 } 2275 bytesRead += 6; 2276 length -= 6; 2277 if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { 2278 // Skip if it's not an EXIF APP1 segment. 2279 break; 2280 } 2281 if (length <= 0) { 2282 throw new IOException("Invalid exif"); 2283 } 2284 if (DEBUG) { 2285 Log.d(TAG, "readExifSegment with a byte array (length: " + length + ")"); 2286 } 2287 // Save offset values for createJpegThumbnailBitmap() function 2288 mExifOffset = bytesRead; 2289 2290 byte[] bytes = new byte[length]; 2291 if (dataInputStream.read(bytes) != length) { 2292 throw new IOException("Invalid exif"); 2293 } 2294 bytesRead += length; 2295 length = 0; 2296 2297 readExifSegment(bytes, imageType); 2298 break; 2299 } 2300 2301 case MARKER_COM: { 2302 byte[] bytes = new byte[length]; 2303 if (dataInputStream.read(bytes) != length) { 2304 throw new IOException("Invalid exif"); 2305 } 2306 length = 0; 2307 if (getAttribute(TAG_USER_COMMENT) == null) { 2308 mAttributes[IFD_EXIF_HINT].put(TAG_USER_COMMENT, ExifAttribute.createString( 2309 new String(bytes, ASCII))); 2310 } 2311 break; 2312 } 2313 2314 case MARKER_SOF0: 2315 case MARKER_SOF1: 2316 case MARKER_SOF2: 2317 case MARKER_SOF3: 2318 case MARKER_SOF5: 2319 case MARKER_SOF6: 2320 case MARKER_SOF7: 2321 case MARKER_SOF9: 2322 case MARKER_SOF10: 2323 case MARKER_SOF11: 2324 case MARKER_SOF13: 2325 case MARKER_SOF14: 2326 case MARKER_SOF15: { 2327 if (dataInputStream.skipBytes(1) != 1) { 2328 throw new IOException("Invalid SOFx"); 2329 } 2330 mAttributes[imageType].put(TAG_IMAGE_LENGTH, ExifAttribute.createULong( 2331 dataInputStream.readUnsignedShort(), mExifByteOrder)); 2332 mAttributes[imageType].put(TAG_IMAGE_WIDTH, ExifAttribute.createULong( 2333 dataInputStream.readUnsignedShort(), mExifByteOrder)); 2334 length -= 5; 2335 break; 2336 } 2337 2338 default: { 2339 break; 2340 } 2341 } 2342 if (length < 0) { 2343 throw new IOException("Invalid length"); 2344 } 2345 if (dataInputStream.skipBytes(length) != length) { 2346 throw new IOException("Invalid JPEG segment"); 2347 } 2348 bytesRead += length; 2349 } 2350 // Reset dataInputStream to marked position 2351 dataInputStream.reset(); 2352 } 2353 2354 private void getRawAttributes(InputStream in) throws IOException { 2355 int totalBytes = in.available(); 2356 byte[] exifBytes = new byte[totalBytes]; 2357 in.mark(in.available()); 2358 in.read(exifBytes); 2359 in.reset(); 2360 2361 ByteOrderAwarenessDataInputStream dataInputStream = 2362 new ByteOrderAwarenessDataInputStream(exifBytes); 2363 2364 // Parse TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1. 2365 parseTiffHeaders(dataInputStream, exifBytes.length); 2366 2367 // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6. 2368 readImageFileDirectory(dataInputStream, IFD_TIFF_HINT); 2369 2370 // Update ImageLength/Width tags for all image data. 2371 updateImageSizeValues(in, IFD_TIFF_HINT); 2372 updateImageSizeValues(in, IFD_PREVIEW_HINT); 2373 updateImageSizeValues(in, IFD_THUMBNAIL_HINT); 2374 2375 // Check if each image data is in valid position. 2376 validateImages(in); 2377 2378 if (mMimeType == IMAGE_TYPE_PEF) { 2379 // PEF files contain a MakerNote data, which contains the data for ColorSpace tag. 2380 // See http://lclevy.free.fr/raw/ and piex.cc PefGetPreviewData() 2381 ExifAttribute makerNoteAttribute = 2382 (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_MAKER_NOTE); 2383 if (makerNoteAttribute != null) { 2384 // Create an ordered DataInputStream for MakerNote 2385 ByteOrderAwarenessDataInputStream makerNoteDataInputStream = 2386 new ByteOrderAwarenessDataInputStream(makerNoteAttribute.bytes); 2387 makerNoteDataInputStream.setByteOrder(mExifByteOrder); 2388 2389 // Seek to MakerNote data 2390 makerNoteDataInputStream.seek(PEF_MAKER_NOTE_SKIP_SIZE); 2391 2392 // Read IFD data from MakerNote 2393 readImageFileDirectory(makerNoteDataInputStream, PEF_HINT); 2394 2395 // Update ColorSpace tag 2396 ExifAttribute colorSpaceAttribute = 2397 (ExifAttribute) mAttributes[PEF_HINT].get(TAG_COLOR_SPACE); 2398 if (colorSpaceAttribute != null) { 2399 mAttributes[IFD_EXIF_HINT].put(TAG_COLOR_SPACE, colorSpaceAttribute); 2400 } 2401 } 2402 } 2403 } 2404 2405 /** 2406 * RAF files contains a JPEG and a CFA data. 2407 * The JPEG contains two images, a preview and a thumbnail, while the CFA contains a RAW image. 2408 * This method looks at the first 160 bytes of a RAF file to retrieve the offset and length 2409 * values for the JPEG and CFA data. 2410 * Using that data, it parses the JPEG data to retrieve the preview and thumbnail image data, 2411 * then parses the CFA metadata to retrieve the primary image length/width values. 2412 * For data format details, see http://fileformats.archiveteam.org/wiki/Fujifilm_RAF 2413 */ 2414 private void getRafAttributes(InputStream in) throws IOException { 2415 // Retrieve offset & length values 2416 in.mark(RAF_INFO_SIZE); 2417 in.skip(RAF_OFFSET_TO_JPEG_IMAGE_OFFSET); 2418 byte[] jpegOffsetBytes = new byte[4]; 2419 byte[] cfaHeaderOffsetBytes = new byte[4]; 2420 byte[] cfaHeaderLengthBytes = new byte[4]; 2421 in.read(jpegOffsetBytes); 2422 // Skip JPEG length value since it is not needed 2423 in.skip(RAF_JPEG_LENGTH_VALUE_SIZE); 2424 in.read(cfaHeaderOffsetBytes); 2425 in.read(cfaHeaderLengthBytes); 2426 int rafJpegOffset = ByteBuffer.wrap(jpegOffsetBytes).getInt(); 2427 int rafCfaHeaderOffset = ByteBuffer.wrap(cfaHeaderOffsetBytes).getInt(); 2428 int rafCfaHeaderLength = ByteBuffer.wrap(cfaHeaderLengthBytes).getInt(); 2429 in.reset(); 2430 2431 // Retrieve JPEG image metadata 2432 getJpegAttributes(in, rafJpegOffset, IFD_PREVIEW_HINT); 2433 2434 // Skip to CFA header offset. 2435 // A while loop is used because the skip method may not be able to skip the requested amount 2436 // at once because the size of the buffer may be restricted. 2437 in.mark(rafCfaHeaderOffset + rafCfaHeaderLength); 2438 int totalSkip = rafCfaHeaderOffset; 2439 while (totalSkip > 0) { 2440 long skipped = in.skip(totalSkip); 2441 totalSkip -= skipped; 2442 } 2443 2444 // Retrieve primary image length/width values, if TAG_RAF_IMAGE_SIZE exists 2445 byte[] exifBytes = new byte[rafCfaHeaderLength]; 2446 if (in.read(exifBytes) != rafCfaHeaderLength) { 2447 throw new EOFException(); 2448 } 2449 in.reset(); 2450 ByteOrderAwarenessDataInputStream dataInputStream = 2451 new ByteOrderAwarenessDataInputStream(exifBytes); 2452 int numberOfDirectoryEntry = dataInputStream.readInt(); 2453 if (DEBUG) { 2454 Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry); 2455 } 2456 // CFA stores some metadata about the RAW image. Since CFA uses proprietary tags, can only 2457 // find and retrieve image size information tags, while skipping others. 2458 // See piex.cc RafGetDimension() 2459 for (int i = 0; i < numberOfDirectoryEntry; ++i) { 2460 int tagNumber = dataInputStream.readUnsignedShort(); 2461 int numberOfBytes = dataInputStream.readUnsignedShort(); 2462 if (tagNumber == TAG_RAF_IMAGE_SIZE.number) { 2463 int imageLength = dataInputStream.readShort(); 2464 int imageWidth = dataInputStream.readShort(); 2465 ExifAttribute imageLengthAttribute = 2466 ExifAttribute.createUShort(imageLength, mExifByteOrder); 2467 ExifAttribute imageWidthAttribute = 2468 ExifAttribute.createUShort(imageWidth, mExifByteOrder); 2469 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, imageLengthAttribute); 2470 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, imageWidthAttribute); 2471 if (DEBUG) { 2472 Log.d(TAG, "Updated to length: " + imageLength + ", width: " + imageWidth); 2473 } 2474 return; 2475 } 2476 dataInputStream.skip(numberOfBytes); 2477 } 2478 } 2479 2480 /** 2481 * ORF files contains a primary image data and a MakerNote data that contains preview/thumbnail 2482 * images. Both data takes the form of IFDs and can therefore be read with the 2483 * readImageFileDirectory() method. 2484 * This method reads all the necessary data and updates the primary/preview/thumbnail image 2485 * information according to the GetOlympusPreviewImage() method in piex.cc. 2486 * For data format details, see the following: 2487 * http://fileformats.archiveteam.org/wiki/Olympus_ORF 2488 * https://libopenraw.freedesktop.org/wiki/Olympus_ORF 2489 */ 2490 private void getOrfAttributes(InputStream in) throws IOException { 2491 // Retrieve primary image data 2492 // Other Exif data will be located in the Makernote. 2493 getRawAttributes(in); 2494 2495 // Additionally retrieve preview/thumbnail information from MakerNote tag, which contains 2496 // proprietary tags and therefore does not have offical documentation 2497 // See GetOlympusPreviewImage() in piex.cc & http://www.exiv2.org/tags-olympus.html 2498 ExifAttribute makerNoteAttribute = 2499 (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_MAKER_NOTE); 2500 if (makerNoteAttribute != null) { 2501 // Create an ordered DataInputStream for MakerNote 2502 ByteOrderAwarenessDataInputStream makerNoteDataInputStream = 2503 new ByteOrderAwarenessDataInputStream(makerNoteAttribute.bytes); 2504 makerNoteDataInputStream.setByteOrder(mExifByteOrder); 2505 2506 // There are two types of headers for Olympus MakerNotes 2507 // See http://www.exiv2.org/makernote.html#R1 2508 byte[] makerNoteHeader1Bytes = new byte[ORF_MAKER_NOTE_HEADER_1.length]; 2509 makerNoteDataInputStream.readFully(makerNoteHeader1Bytes); 2510 makerNoteDataInputStream.seek(0); 2511 byte[] makerNoteHeader2Bytes = new byte[ORF_MAKER_NOTE_HEADER_2.length]; 2512 makerNoteDataInputStream.readFully(makerNoteHeader2Bytes); 2513 // Skip the corresponding amount of bytes for each header type 2514 if (Arrays.equals(makerNoteHeader1Bytes, ORF_MAKER_NOTE_HEADER_1)) { 2515 makerNoteDataInputStream.seek(ORF_MAKER_NOTE_HEADER_1_SIZE); 2516 } else if (Arrays.equals(makerNoteHeader2Bytes, ORF_MAKER_NOTE_HEADER_2)) { 2517 makerNoteDataInputStream.seek(ORF_MAKER_NOTE_HEADER_2_SIZE); 2518 } 2519 2520 // Read IFD data from MakerNote 2521 readImageFileDirectory(makerNoteDataInputStream, ORF_MAKER_NOTE_HINT); 2522 2523 // Retrieve & update preview image offset & length values 2524 ExifAttribute imageLengthAttribute = (ExifAttribute) 2525 mAttributes[ORF_CAMERA_SETTINGS_HINT].get(TAG_ORF_PREVIEW_IMAGE_START); 2526 ExifAttribute bitsPerSampleAttribute = (ExifAttribute) 2527 mAttributes[ORF_CAMERA_SETTINGS_HINT].get(TAG_ORF_PREVIEW_IMAGE_LENGTH); 2528 2529 if (imageLengthAttribute != null && bitsPerSampleAttribute != null) { 2530 mAttributes[IFD_PREVIEW_HINT].put(TAG_JPEG_INTERCHANGE_FORMAT, 2531 imageLengthAttribute); 2532 mAttributes[IFD_PREVIEW_HINT].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 2533 bitsPerSampleAttribute); 2534 } 2535 2536 // TODO: Check this behavior in other ORF files 2537 // Retrieve primary image length & width values 2538 // See piex.cc GetOlympusPreviewImage() 2539 ExifAttribute aspectFrameAttribute = (ExifAttribute) 2540 mAttributes[ORF_IMAGE_PROCESSING_HINT].get(TAG_ORF_ASPECT_FRAME); 2541 if (aspectFrameAttribute != null) { 2542 int[] aspectFrameValues = new int[4]; 2543 aspectFrameValues = (int[]) aspectFrameAttribute.getValue(mExifByteOrder); 2544 if (aspectFrameValues[2] > aspectFrameValues[0] && 2545 aspectFrameValues[3] > aspectFrameValues[1]) { 2546 int primaryImageWidth = aspectFrameValues[2] - aspectFrameValues[0] + 1; 2547 int primaryImageLength = aspectFrameValues[3] - aspectFrameValues[1] + 1; 2548 // Swap width & length values 2549 if (primaryImageWidth < primaryImageLength) { 2550 primaryImageWidth += primaryImageLength; 2551 primaryImageLength = primaryImageWidth - primaryImageLength; 2552 primaryImageWidth -= primaryImageLength; 2553 } 2554 ExifAttribute primaryImageWidthAttribute = 2555 ExifAttribute.createUShort(primaryImageWidth, mExifByteOrder); 2556 ExifAttribute primaryImageLengthAttribute = 2557 ExifAttribute.createUShort(primaryImageLength, mExifByteOrder); 2558 2559 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, primaryImageWidthAttribute); 2560 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, primaryImageLengthAttribute); 2561 } 2562 } 2563 } 2564 } 2565 2566 // RW2 contains the primary image data in IFD0 and the preview and/or thumbnail image data in 2567 // the JpgFromRaw tag 2568 // See https://libopenraw.freedesktop.org/wiki/Panasonic_RAW/ and piex.cc Rw2GetPreviewData() 2569 private void getRw2Attributes(InputStream in) throws IOException { 2570 // Retrieve primary image data 2571 getRawAttributes(in); 2572 2573 // Retrieve preview and/or thumbnail image data 2574 ExifAttribute jpgFromRawAttribute = 2575 (ExifAttribute) mAttributes[IFD_TIFF_HINT].get(TAG_RW2_JPG_FROM_RAW); 2576 if (jpgFromRawAttribute != null) { 2577 getJpegAttributes(in, mRw2JpgFromRawOffset, IFD_PREVIEW_HINT); 2578 } 2579 2580 // Set ISO tag value if necessary 2581 ExifAttribute rw2IsoAttribute = 2582 (ExifAttribute) mAttributes[IFD_TIFF_HINT].get(TAG_RW2_ISO); 2583 ExifAttribute exifIsoAttribute = 2584 (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_ISO_SPEED_RATINGS); 2585 if (rw2IsoAttribute != null && exifIsoAttribute == null) { 2586 // Place this attribute only if it doesn't exist 2587 mAttributes[IFD_EXIF_HINT].put(TAG_ISO_SPEED_RATINGS, rw2IsoAttribute); 2588 } 2589 } 2590 2591 // PEF is TIFF-based and contains 3 IFDs. It also contains a MakerNote data, which contains the 2592 // ColorSpace tag data. 2593 // See http://lclevy.free.fr/raw/ and piex.cc PefGetPreviewData() 2594 private void getPefAttributes(InputStream in) throws IOException { 2595 // Retrieve ColorSpace tag 2596 ExifAttribute makerNoteAttribute = 2597 (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_MAKER_NOTE); 2598 if (makerNoteAttribute != null) { 2599 // Create an ordered DataInputStream for MakerNote 2600 ByteOrderAwarenessDataInputStream makerNoteDataInputStream = 2601 new ByteOrderAwarenessDataInputStream(makerNoteAttribute.bytes); 2602 makerNoteDataInputStream.setByteOrder(mExifByteOrder); 2603 2604 // Seek to MakerNote data 2605 makerNoteDataInputStream.seek(PEF_MAKER_NOTE_SKIP_SIZE); 2606 2607 // Read IFD data from MakerNote 2608 readImageFileDirectory(makerNoteDataInputStream, PEF_HINT); 2609 2610 // Update ColorSpace tag 2611 ExifAttribute colorSpaceAttribute = 2612 (ExifAttribute) mAttributes[PEF_HINT].get(TAG_COLOR_SPACE); 2613 if (colorSpaceAttribute != null) { 2614 mAttributes[IFD_EXIF_HINT].put(TAG_COLOR_SPACE, colorSpaceAttribute); 2615 } 2616 } 2617 } 2618 2619 // Stores a new JPEG image with EXIF attributes into a given output stream. 2620 private void saveJpegAttributes(InputStream inputStream, OutputStream outputStream) 2621 throws IOException { 2622 // See JPEG File Interchange Format Specification, "JFIF Specification" 2623 if (DEBUG) { 2624 Log.d(TAG, "saveJpegAttributes starting with (inputStream: " + inputStream 2625 + ", outputStream: " + outputStream + ")"); 2626 } 2627 DataInputStream dataInputStream = new DataInputStream(inputStream); 2628 ByteOrderAwarenessDataOutputStream dataOutputStream = 2629 new ByteOrderAwarenessDataOutputStream(outputStream, ByteOrder.BIG_ENDIAN); 2630 if (dataInputStream.readByte() != MARKER) { 2631 throw new IOException("Invalid marker"); 2632 } 2633 dataOutputStream.writeByte(MARKER); 2634 if (dataInputStream.readByte() != MARKER_SOI) { 2635 throw new IOException("Invalid marker"); 2636 } 2637 dataOutputStream.writeByte(MARKER_SOI); 2638 2639 // Write EXIF APP1 segment 2640 dataOutputStream.writeByte(MARKER); 2641 dataOutputStream.writeByte(MARKER_APP1); 2642 writeExifSegment(dataOutputStream, 6); 2643 2644 byte[] bytes = new byte[4096]; 2645 2646 while (true) { 2647 byte marker = dataInputStream.readByte(); 2648 if (marker != MARKER) { 2649 throw new IOException("Invalid marker"); 2650 } 2651 marker = dataInputStream.readByte(); 2652 switch (marker) { 2653 case MARKER_APP1: { 2654 int length = dataInputStream.readUnsignedShort() - 2; 2655 if (length < 0) { 2656 throw new IOException("Invalid length"); 2657 } 2658 byte[] identifier = new byte[6]; 2659 if (length >= 6) { 2660 if (dataInputStream.read(identifier) != 6) { 2661 throw new IOException("Invalid exif"); 2662 } 2663 if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { 2664 // Skip the original EXIF APP1 segment. 2665 if (dataInputStream.skip(length - 6) != length - 6) { 2666 throw new IOException("Invalid length"); 2667 } 2668 break; 2669 } 2670 } 2671 // Copy non-EXIF APP1 segment. 2672 dataOutputStream.writeByte(MARKER); 2673 dataOutputStream.writeByte(marker); 2674 dataOutputStream.writeUnsignedShort(length + 2); 2675 if (length >= 6) { 2676 length -= 6; 2677 dataOutputStream.write(identifier); 2678 } 2679 int read; 2680 while (length > 0 && (read = dataInputStream.read( 2681 bytes, 0, Math.min(length, bytes.length))) >= 0) { 2682 dataOutputStream.write(bytes, 0, read); 2683 length -= read; 2684 } 2685 break; 2686 } 2687 case MARKER_EOI: 2688 case MARKER_SOS: { 2689 dataOutputStream.writeByte(MARKER); 2690 dataOutputStream.writeByte(marker); 2691 // Copy all the remaining data 2692 Streams.copy(dataInputStream, dataOutputStream); 2693 return; 2694 } 2695 default: { 2696 // Copy JPEG segment 2697 dataOutputStream.writeByte(MARKER); 2698 dataOutputStream.writeByte(marker); 2699 int length = dataInputStream.readUnsignedShort(); 2700 dataOutputStream.writeUnsignedShort(length); 2701 length -= 2; 2702 if (length < 0) { 2703 throw new IOException("Invalid length"); 2704 } 2705 int read; 2706 while (length > 0 && (read = dataInputStream.read( 2707 bytes, 0, Math.min(length, bytes.length))) >= 0) { 2708 dataOutputStream.write(bytes, 0, read); 2709 length -= read; 2710 } 2711 break; 2712 } 2713 } 2714 } 2715 } 2716 2717 // Reads the given EXIF byte area and save its tag data into attributes. 2718 private void readExifSegment(byte[] exifBytes, int imageType) throws IOException { 2719 ByteOrderAwarenessDataInputStream dataInputStream = 2720 new ByteOrderAwarenessDataInputStream(exifBytes); 2721 2722 // Parse TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1. 2723 parseTiffHeaders(dataInputStream, exifBytes.length); 2724 2725 // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6. 2726 readImageFileDirectory(dataInputStream, imageType); 2727 } 2728 2729 private void addDefaultValuesForCompatibility() { 2730 // The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag. 2731 String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL); 2732 if (valueOfDateTimeOriginal != null) { 2733 mAttributes[IFD_TIFF_HINT].put(TAG_DATETIME, 2734 ExifAttribute.createString(valueOfDateTimeOriginal)); 2735 } 2736 2737 // Add the default value. 2738 if (getAttribute(TAG_IMAGE_WIDTH) == null) { 2739 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, 2740 ExifAttribute.createULong(0, mExifByteOrder)); 2741 } 2742 if (getAttribute(TAG_IMAGE_LENGTH) == null) { 2743 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, 2744 ExifAttribute.createULong(0, mExifByteOrder)); 2745 } 2746 if (getAttribute(TAG_ORIENTATION) == null) { 2747 mAttributes[IFD_TIFF_HINT].put(TAG_ORIENTATION, 2748 ExifAttribute.createULong(0, mExifByteOrder)); 2749 } 2750 if (getAttribute(TAG_LIGHT_SOURCE) == null) { 2751 mAttributes[IFD_EXIF_HINT].put(TAG_LIGHT_SOURCE, 2752 ExifAttribute.createULong(0, mExifByteOrder)); 2753 } 2754 } 2755 2756 private ByteOrder readByteOrder(ByteOrderAwarenessDataInputStream dataInputStream) 2757 throws IOException { 2758 // Read byte order. 2759 short byteOrder = dataInputStream.readShort(); 2760 switch (byteOrder) { 2761 case BYTE_ALIGN_II: 2762 if (DEBUG) { 2763 Log.d(TAG, "readExifSegment: Byte Align II"); 2764 } 2765 return ByteOrder.LITTLE_ENDIAN; 2766 case BYTE_ALIGN_MM: 2767 if (DEBUG) { 2768 Log.d(TAG, "readExifSegment: Byte Align MM"); 2769 } 2770 return ByteOrder.BIG_ENDIAN; 2771 default: 2772 throw new IOException("Invalid byte order: " + Integer.toHexString(byteOrder)); 2773 } 2774 } 2775 2776 private void parseTiffHeaders(ByteOrderAwarenessDataInputStream dataInputStream, 2777 int exifBytesLength) throws IOException { 2778 // Read byte order 2779 mExifByteOrder = readByteOrder(dataInputStream); 2780 // Set byte order 2781 dataInputStream.setByteOrder(mExifByteOrder); 2782 2783 // Check start code 2784 int startCode = dataInputStream.readUnsignedShort(); 2785 if (mMimeType != IMAGE_TYPE_ORF && mMimeType != IMAGE_TYPE_RW2 && startCode != START_CODE) { 2786 throw new IOException("Invalid start code: " + Integer.toHexString(startCode)); 2787 } 2788 2789 // Read and skip to first ifd offset 2790 int firstIfdOffset = dataInputStream.readInt(); 2791 if (firstIfdOffset < 8 || firstIfdOffset >= exifBytesLength) { 2792 throw new IOException("Invalid first Ifd offset: " + firstIfdOffset); 2793 } 2794 firstIfdOffset -= 8; 2795 if (firstIfdOffset > 0) { 2796 if (dataInputStream.skip(firstIfdOffset) != firstIfdOffset) { 2797 throw new IOException("Couldn't jump to first Ifd: " + firstIfdOffset); 2798 } 2799 } 2800 } 2801 2802 // Reads image file directory, which is a tag group in EXIF. 2803 private void readImageFileDirectory(ByteOrderAwarenessDataInputStream dataInputStream, int hint) 2804 throws IOException { 2805 if (dataInputStream.peek() + 2 > dataInputStream.mLength) { 2806 // Return if there is no data from the offset. 2807 return; 2808 } 2809 // See TIFF 6.0 Section 2: TIFF Structure, Figure 1. 2810 short numberOfDirectoryEntry = dataInputStream.readShort(); 2811 if (dataInputStream.peek() + 12 * numberOfDirectoryEntry > dataInputStream.mLength) { 2812 // Return if the size of entries is too big. 2813 return; 2814 } 2815 2816 if (DEBUG) { 2817 Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry); 2818 } 2819 2820 // See TIFF 6.0 Section 2: TIFF Structure, "Image File Directory". 2821 for (short i = 0; i < numberOfDirectoryEntry; ++i) { 2822 int tagNumber = dataInputStream.readUnsignedShort(); 2823 int dataFormat = dataInputStream.readUnsignedShort(); 2824 int numberOfComponents = dataInputStream.readInt(); 2825 // Next four bytes is for data offset or value. 2826 long nextEntryOffset = dataInputStream.peek() + 4; 2827 2828 // Look up a corresponding tag from tag number 2829 ExifTag tag = (ExifTag) sExifTagMapsForReading[hint].get(tagNumber); 2830 2831 if (DEBUG) { 2832 Log.d(TAG, String.format("hint: %d, tagNumber: %d, tagName: %s, dataFormat: %d, " + 2833 "numberOfComponents: %d", hint, tagNumber, tag != null ? tag.name : null, 2834 dataFormat, numberOfComponents)); 2835 } 2836 2837 if (tag == null || dataFormat <= 0 || 2838 dataFormat >= IFD_FORMAT_BYTES_PER_FORMAT.length) { 2839 // Skip if the parsed tag number is not defined or invalid data format. 2840 if (tag == null) { 2841 Log.w(TAG, "Skip the tag entry since tag number is not defined: " + tagNumber); 2842 } else { 2843 Log.w(TAG, "Skip the tag entry since data format is invalid: " + dataFormat); 2844 } 2845 dataInputStream.seek(nextEntryOffset); 2846 continue; 2847 } 2848 2849 // Read a value from data field or seek to the value offset which is stored in data 2850 // field if the size of the entry value is bigger than 4. 2851 int byteCount = numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat]; 2852 if (byteCount > 4) { 2853 int offset = dataInputStream.readInt(); 2854 if (DEBUG) { 2855 Log.d(TAG, "seek to data offset: " + offset); 2856 } 2857 if (mMimeType == IMAGE_TYPE_ORF) { 2858 if (tag.name == TAG_MAKER_NOTE) { 2859 // Save offset value for reading thumbnail 2860 mOrfMakerNoteOffset = offset; 2861 } else if (hint == ORF_MAKER_NOTE_HINT && tag.name == TAG_ORF_THUMBNAIL_IMAGE) { 2862 // Retrieve & update values for thumbnail offset and length values for ORF 2863 mOrfThumbnailOffset = offset; 2864 mOrfThumbnailLength = numberOfComponents; 2865 2866 ExifAttribute compressionAttribute = 2867 ExifAttribute.createUShort(DATA_JPEG, mExifByteOrder); 2868 ExifAttribute jpegInterchangeFormatAttribute = 2869 ExifAttribute.createULong(mOrfThumbnailOffset, mExifByteOrder); 2870 ExifAttribute jpegInterchangeFormatLengthAttribute = 2871 ExifAttribute.createULong(mOrfThumbnailLength, mExifByteOrder); 2872 2873 mAttributes[IFD_THUMBNAIL_HINT].put(TAG_COMPRESSION, compressionAttribute); 2874 mAttributes[IFD_THUMBNAIL_HINT].put(TAG_JPEG_INTERCHANGE_FORMAT, 2875 jpegInterchangeFormatAttribute); 2876 mAttributes[IFD_THUMBNAIL_HINT].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 2877 jpegInterchangeFormatLengthAttribute); 2878 } 2879 } else if (mMimeType == IMAGE_TYPE_RW2) { 2880 if (tag.name == TAG_RW2_JPG_FROM_RAW) { 2881 mRw2JpgFromRawOffset = offset; 2882 } 2883 } 2884 if (offset + byteCount <= dataInputStream.mLength) { 2885 dataInputStream.seek(offset); 2886 } else { 2887 // Skip if invalid data offset. 2888 Log.w(TAG, "Skip the tag entry since data offset is invalid: " + offset); 2889 dataInputStream.seek(nextEntryOffset); 2890 continue; 2891 } 2892 } 2893 2894 // Recursively parse IFD when a IFD pointer tag appears. 2895 int innerIfdHint = getIfdHintFromTagNumber(tagNumber); 2896 if (DEBUG) { 2897 Log.d(TAG, "innerIfdHint: " + innerIfdHint + " byteCount: " + byteCount); 2898 } 2899 2900 if (innerIfdHint >= 0) { 2901 long offset = -1L; 2902 // Get offset from data field 2903 switch (dataFormat) { 2904 case IFD_FORMAT_USHORT: { 2905 offset = dataInputStream.readUnsignedShort(); 2906 break; 2907 } 2908 case IFD_FORMAT_SSHORT: { 2909 offset = dataInputStream.readShort(); 2910 break; 2911 } 2912 case IFD_FORMAT_ULONG: { 2913 offset = dataInputStream.readUnsignedInt(); 2914 break; 2915 } 2916 case IFD_FORMAT_SLONG: 2917 case IFD_FORMAT_IFD: { 2918 offset = dataInputStream.readInt(); 2919 break; 2920 } 2921 default: { 2922 // Nothing to do 2923 break; 2924 } 2925 } 2926 if (DEBUG) { 2927 Log.d(TAG, String.format("Offset: %d, tagName: %s", offset, tag.name)); 2928 } 2929 if (offset > 0L && offset < dataInputStream.mLength) { 2930 dataInputStream.seek(offset); 2931 readImageFileDirectory(dataInputStream, innerIfdHint); 2932 } else { 2933 Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset); 2934 } 2935 2936 dataInputStream.seek(nextEntryOffset); 2937 continue; 2938 } 2939 2940 byte[] bytes = new byte[byteCount]; 2941 dataInputStream.readFully(bytes); 2942 ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, bytes); 2943 mAttributes[hint].put(tag.name, attribute); 2944 2945 // PEF files have a Make or Model tag that begins with "PENTAX" or a compression tag 2946 // that is 65535. 2947 // See http://fileformats.archiveteam.org/wiki/Pentax_PEF 2948 if (((tag.name == TAG_MAKE || tag.name == TAG_MODEL) 2949 && attribute.getStringValue(mExifByteOrder).contains(PEF_SIGNATURE)) 2950 || (tag.name == TAG_COMPRESSION 2951 && attribute.getIntValue(mExifByteOrder) == 65535)) { 2952 mMimeType = IMAGE_TYPE_PEF; 2953 } 2954 2955 // Seek to next tag offset 2956 if (dataInputStream.peek() != nextEntryOffset) { 2957 dataInputStream.seek(nextEntryOffset); 2958 } 2959 } 2960 2961 if (dataInputStream.peek() + 4 <= dataInputStream.mLength) { 2962 int nextIfdOffset = dataInputStream.readInt(); 2963 if (DEBUG) { 2964 Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset)); 2965 } 2966 // The next IFD offset needs to be bigger than 8 2967 // since the first IFD offset is at least 8. 2968 if (nextIfdOffset > 8 && nextIfdOffset < dataInputStream.mLength) { 2969 dataInputStream.seek(nextIfdOffset); 2970 if (mAttributes[IFD_THUMBNAIL_HINT].isEmpty()) { 2971 // Do not overwrite thumbnail IFD data if it alreay exists. 2972 readImageFileDirectory(dataInputStream, IFD_THUMBNAIL_HINT); 2973 } else if (mAttributes[IFD_PREVIEW_HINT].isEmpty()) { 2974 readImageFileDirectory(dataInputStream, IFD_PREVIEW_HINT); 2975 } 2976 } 2977 } 2978 } 2979 2980 /** 2981 * JPEG compressed images do not contain IMAGE_LENGTH & IMAGE_WIDTH tags. 2982 * This value uses JpegInterchangeFormat(JPEG data offset) value, and calls getJpegAttributes() 2983 * to locate SOF(Start of Frame) marker and update the image length & width values. 2984 * See JEITA CP-3451C Table 5 and Section 4.8.1. B. 2985 */ 2986 private void retrieveJpegImageSize(InputStream in, int imageType) throws IOException { 2987 // Check if image already has IMAGE_LENGTH & IMAGE_WIDTH values 2988 ExifAttribute imageLengthAttribute = 2989 (ExifAttribute) mAttributes[imageType].get(TAG_IMAGE_LENGTH); 2990 ExifAttribute imageWidthAttribute = 2991 (ExifAttribute) mAttributes[imageType].get(TAG_IMAGE_WIDTH); 2992 2993 if (imageLengthAttribute == null || imageWidthAttribute == null) { 2994 // Find if offset for JPEG data exists 2995 ExifAttribute jpegInterchangeFormatAttribute = 2996 (ExifAttribute) mAttributes[imageType].get(TAG_JPEG_INTERCHANGE_FORMAT); 2997 if (jpegInterchangeFormatAttribute != null) { 2998 int jpegInterchangeFormat = 2999 jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder); 3000 3001 // Searches for SOF marker in JPEG data and updates IMAGE_LENGTH & IMAGE_WIDTH tags 3002 getJpegAttributes(in, jpegInterchangeFormat, imageType); 3003 } 3004 } 3005 } 3006 3007 // Sets thumbnail offset & length attributes based on JpegInterchangeFormat or StripOffsets tags 3008 private void setThumbnailData(InputStream in) throws IOException { 3009 HashMap thumbnailData = mAttributes[IFD_THUMBNAIL_HINT]; 3010 3011 ExifAttribute compressionAttribute = 3012 (ExifAttribute) thumbnailData.get(TAG_COMPRESSION); 3013 if (compressionAttribute != null) { 3014 mThumbnailCompression = compressionAttribute.getIntValue(mExifByteOrder); 3015 switch (mThumbnailCompression) { 3016 case DATA_JPEG: { 3017 handleThumbnailFromJfif(in, thumbnailData); 3018 break; 3019 } 3020 case DATA_UNCOMPRESSED: 3021 case DATA_JPEG_COMPRESSED: { 3022 handleThumbnailFromStrips(in, thumbnailData); 3023 break; 3024 } 3025 } 3026 } else { 3027 // Thumbnail data may not contain Compression tag value 3028 mThumbnailCompression = DATA_JPEG; 3029 handleThumbnailFromJfif(in, thumbnailData); 3030 } 3031 } 3032 3033 // Check JpegInterchangeFormat(JFIF) tags to retrieve thumbnail offset & length values 3034 // and reads the corresponding bytes if stream does not support seek function 3035 private void handleThumbnailFromJfif(InputStream in, HashMap thumbnailData) throws IOException { 3036 ExifAttribute jpegInterchangeFormatAttribute = 3037 (ExifAttribute) thumbnailData.get(TAG_JPEG_INTERCHANGE_FORMAT); 3038 ExifAttribute jpegInterchangeFormatLengthAttribute = 3039 (ExifAttribute) thumbnailData.get(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 3040 if (jpegInterchangeFormatAttribute != null 3041 && jpegInterchangeFormatLengthAttribute != null) { 3042 int thumbnailOffset = jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder); 3043 int thumbnailLength = jpegInterchangeFormatLengthAttribute.getIntValue(mExifByteOrder); 3044 3045 // The following code limits the size of thumbnail size not to overflow EXIF data area. 3046 thumbnailLength = Math.min(thumbnailLength, in.available() - thumbnailOffset); 3047 if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF 3048 || mMimeType == IMAGE_TYPE_RW2) { 3049 thumbnailOffset += mExifOffset; 3050 } else if (mMimeType == IMAGE_TYPE_ORF) { 3051 // Update offset value since RAF files have IFD data preceding MakerNote data. 3052 thumbnailOffset += mOrfMakerNoteOffset; 3053 } 3054 if (DEBUG) { 3055 Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset); 3056 } 3057 if (thumbnailOffset > 0 && thumbnailLength > 0) { 3058 mHasThumbnail = true; 3059 mThumbnailOffset = thumbnailOffset; 3060 mThumbnailLength = thumbnailLength; 3061 if (mFilename == null && mAssetInputStream == null 3062 && mSeekableFileDescriptor == null) { 3063 // Save the thumbnail in memory if the input doesn't support reading again. 3064 byte[] thumbnailBytes = new byte[thumbnailLength]; 3065 in.skip(thumbnailOffset); 3066 in.read(thumbnailBytes); 3067 mThumbnailBytes = thumbnailBytes; 3068 } 3069 } 3070 } 3071 } 3072 3073 // Check StripOffsets & StripByteCounts tags to retrieve thumbnail offset & length values 3074 private void handleThumbnailFromStrips(InputStream in, HashMap thumbnailData) 3075 throws IOException { 3076 ExifAttribute bitsPerSampleAttribute = 3077 (ExifAttribute) thumbnailData.get(TAG_BITS_PER_SAMPLE); 3078 3079 if (bitsPerSampleAttribute != null) { 3080 int[] bitsPerSampleValue = (int[]) bitsPerSampleAttribute.getValue(mExifByteOrder); 3081 3082 if (Arrays.equals(BITS_PER_SAMPLE_RGB, bitsPerSampleValue)) { 3083 ExifAttribute stripOffsetsAttribute = 3084 (ExifAttribute) thumbnailData.get(TAG_STRIP_OFFSETS); 3085 ExifAttribute stripByteCountsAttribute = 3086 (ExifAttribute) thumbnailData.get(TAG_STRIP_BYTE_COUNTS); 3087 3088 if (stripOffsetsAttribute != null && stripByteCountsAttribute != null) { 3089 long[] stripOffsets = 3090 (long[]) stripOffsetsAttribute.getValue(mExifByteOrder); 3091 long[] stripByteCounts = 3092 (long[]) stripByteCountsAttribute.getValue(mExifByteOrder); 3093 3094 // Set thumbnail byte array data for non-consecutive strip bytes 3095 byte[] totalStripBytes = 3096 new byte[(int) Arrays.stream(stripByteCounts).sum()]; 3097 3098 int bytesRead = 0; 3099 int bytesAdded = 0; 3100 for (int i = 0; i < stripOffsets.length; i++) { 3101 int stripOffset = (int) stripOffsets[i]; 3102 int stripByteCount = (int) stripByteCounts[i]; 3103 3104 // Skip to offset 3105 int skipBytes = stripOffset - bytesRead; 3106 if (skipBytes < 0) { 3107 Log.d(TAG, "Invalid strip offset value"); 3108 } 3109 in.skip(skipBytes); 3110 bytesRead += skipBytes; 3111 3112 // Read strip bytes 3113 byte[] stripBytes = new byte[stripByteCount]; 3114 in.read(stripBytes); 3115 bytesRead += stripByteCount; 3116 3117 // Add bytes to array 3118 System.arraycopy(stripBytes, 0, totalStripBytes, bytesAdded, 3119 stripBytes.length); 3120 bytesAdded += stripBytes.length; 3121 } 3122 3123 mHasThumbnail = true; 3124 mThumbnailBytes = totalStripBytes; 3125 } 3126 } 3127 } else { 3128 if (DEBUG) { 3129 Log.d(TAG, "Only Uncompressed RGB data process is supported"); 3130 } 3131 return; 3132 } 3133 } 3134 3135 // Returns true if the image length and width values are <= 512. 3136 // See Section 4.8 of http://standardsproposals.bsigroup.com/Home/getPDF/567 3137 private boolean isThumbnail(HashMap map) throws IOException { 3138 ExifAttribute imageLengthAttribute = (ExifAttribute) map.get(TAG_IMAGE_LENGTH); 3139 ExifAttribute imageWidthAttribute = (ExifAttribute) map.get(TAG_IMAGE_WIDTH); 3140 3141 if (imageLengthAttribute != null && imageWidthAttribute != null) { 3142 int imageLengthValue = imageLengthAttribute.getIntValue(mExifByteOrder); 3143 int imageWidthValue = imageWidthAttribute.getIntValue(mExifByteOrder); 3144 if (imageLengthValue <= MAX_THUMBNAIL_SIZE && imageWidthValue <= MAX_THUMBNAIL_SIZE) { 3145 return true; 3146 } 3147 } 3148 return false; 3149 } 3150 3151 // Validate primary, preview, thumbnail image data by comparing image size 3152 private void validateImages(InputStream in) throws IOException { 3153 // Swap images based on size (primary > preview > thumbnail) 3154 swapBasedOnImageSize(IFD_TIFF_HINT, IFD_PREVIEW_HINT); 3155 swapBasedOnImageSize(IFD_TIFF_HINT, IFD_THUMBNAIL_HINT); 3156 swapBasedOnImageSize(IFD_PREVIEW_HINT, IFD_THUMBNAIL_HINT); 3157 3158 // Check whether thumbnail image exists and whether preview image satisfies the thumbnail 3159 // image requirements 3160 if (mAttributes[IFD_THUMBNAIL_HINT].isEmpty()) { 3161 if (isThumbnail(mAttributes[IFD_PREVIEW_HINT])) { 3162 mAttributes[IFD_THUMBNAIL_HINT] = mAttributes[IFD_PREVIEW_HINT]; 3163 mAttributes[IFD_PREVIEW_HINT] = new HashMap(); 3164 } 3165 } 3166 3167 // Check if the thumbnail image satisfies the thumbnail size requirements 3168 if (!isThumbnail(mAttributes[IFD_THUMBNAIL_HINT])) { 3169 Log.d(TAG, "No image meets the size requirements of a thumbnail image."); 3170 } 3171 } 3172 3173 /** 3174 * If image is uncompressed, ImageWidth/Length tags are used to store size info. 3175 * However, uncompressed images often store extra pixels around the edges of the final image, 3176 * which results in larger values for TAG_IMAGE_WIDTH and TAG_IMAGE_LENGTH tags. 3177 * This method corrects those tag values by checking first the values of TAG_DEFAULT_CROP_SIZE 3178 * See DNG Specification 1.4.0.0. Section 4. (DefaultCropSize) 3179 * 3180 * If image is JPEG compressed, PixelXDimension/PixelYDimension tags are used for size info. 3181 * However, an image may have padding at the right end or bottom end of the image to make sure 3182 * that the values are multiples of 64. If so, the increased value will be saved in the 3183 * SOF(Start of Frame). In order to assure that valid image size values are stored, this method 3184 * checks TAG_PIXEL_X_DIMENSION & TAG_PIXEL_Y_DIMENSION and updates values if necessary. 3185 * See JEITA CP-3451C Table 5 and Section 4.8.1. B. 3186 * 3187 * If image is a RW2 file, valid image sizes are stored in SensorBorder tags. 3188 * See tiff_parser.cc GetFullDimension32() 3189 * */ 3190 private void updateImageSizeValues(InputStream in, int imageType) throws IOException { 3191 // Uncompressed image valid image size values 3192 ExifAttribute defaultCropSizeAttribute = 3193 (ExifAttribute) mAttributes[imageType].get(TAG_DEFAULT_CROP_SIZE); 3194 // RW2 image valid image size values 3195 ExifAttribute topBorderAttribute = 3196 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_TOP_BORDER); 3197 ExifAttribute leftBorderAttribute = 3198 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_LEFT_BORDER); 3199 ExifAttribute bottomBorderAttribute = 3200 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_BOTTOM_BORDER); 3201 ExifAttribute rightBorderAttribute = 3202 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_RIGHT_BORDER); 3203 3204 if (defaultCropSizeAttribute != null) { 3205 // Update for uncompressed image 3206 ExifAttribute defaultCropSizeXAttribute, defaultCropSizeYAttribute; 3207 if (defaultCropSizeAttribute.format == IFD_FORMAT_URATIONAL) { 3208 Rational[] defaultCropSizeValue = 3209 (Rational[]) defaultCropSizeAttribute.getValue(mExifByteOrder); 3210 defaultCropSizeXAttribute = 3211 ExifAttribute.createURational(defaultCropSizeValue[0], mExifByteOrder); 3212 defaultCropSizeYAttribute = 3213 ExifAttribute.createURational(defaultCropSizeValue[1], mExifByteOrder); 3214 } else { 3215 int[] defaultCropSizeValue = 3216 (int[]) defaultCropSizeAttribute.getValue(mExifByteOrder); 3217 defaultCropSizeXAttribute = 3218 ExifAttribute.createUShort(defaultCropSizeValue[0], mExifByteOrder); 3219 defaultCropSizeYAttribute = 3220 ExifAttribute.createUShort(defaultCropSizeValue[1], mExifByteOrder); 3221 } 3222 mAttributes[imageType].put(TAG_IMAGE_WIDTH, defaultCropSizeXAttribute); 3223 mAttributes[imageType].put(TAG_IMAGE_LENGTH, defaultCropSizeYAttribute); 3224 } else if (topBorderAttribute != null && leftBorderAttribute != null && 3225 bottomBorderAttribute != null && rightBorderAttribute != null) { 3226 // Update for RW2 image 3227 int topBorderValue = topBorderAttribute.getIntValue(mExifByteOrder); 3228 int bottomBorderValue = bottomBorderAttribute.getIntValue(mExifByteOrder); 3229 int rightBorderValue = rightBorderAttribute.getIntValue(mExifByteOrder); 3230 int leftBorderValue = leftBorderAttribute.getIntValue(mExifByteOrder); 3231 if (bottomBorderValue > topBorderValue && rightBorderValue > leftBorderValue) { 3232 int length = bottomBorderValue - topBorderValue; 3233 int width = rightBorderValue - leftBorderValue; 3234 ExifAttribute imageLengthAttribute = 3235 ExifAttribute.createUShort(length, mExifByteOrder); 3236 ExifAttribute imageWidthAttribute = 3237 ExifAttribute.createUShort(width, mExifByteOrder); 3238 mAttributes[imageType].put(TAG_IMAGE_LENGTH, imageLengthAttribute); 3239 mAttributes[imageType].put(TAG_IMAGE_WIDTH, imageWidthAttribute); 3240 } 3241 } else { 3242 // Update for JPEG image 3243 if (imageType == IFD_TIFF_HINT) { 3244 ExifAttribute pixelXDimAttribute = 3245 (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_PIXEL_X_DIMENSION); 3246 ExifAttribute pixelYDimAttribute = 3247 (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_PIXEL_Y_DIMENSION); 3248 if (pixelXDimAttribute != null && pixelYDimAttribute != null) { 3249 mAttributes[imageType].put(TAG_IMAGE_WIDTH, pixelXDimAttribute); 3250 mAttributes[imageType].put(TAG_IMAGE_LENGTH, pixelYDimAttribute); 3251 } else { 3252 retrieveJpegImageSize(in, imageType); 3253 } 3254 } else { 3255 retrieveJpegImageSize(in, imageType); 3256 } 3257 } 3258 } 3259 3260 // Gets the corresponding IFD group index of the given tag number for writing Exif Tags. 3261 private static int getIfdHintFromTagNumber(int tagNumber) { 3262 for (int i = 0; i < EXIF_POINTER_TAG_HINTS.length; ++i) { 3263 if (EXIF_POINTER_TAGS[i].number == tagNumber) { 3264 return EXIF_POINTER_TAG_HINTS[i]; 3265 } 3266 } 3267 return -1; 3268 } 3269 3270 // Writes an Exif segment into the given output stream. 3271 private int writeExifSegment(ByteOrderAwarenessDataOutputStream dataOutputStream, 3272 int exifOffsetFromBeginning) throws IOException { 3273 // The following variables are for calculating each IFD tag group size in bytes. 3274 int[] ifdOffsets = new int[EXIF_TAGS.length]; 3275 int[] ifdDataSizes = new int[EXIF_TAGS.length]; 3276 3277 // Remove IFD pointer tags (we'll re-add it later.) 3278 for (ExifTag tag : EXIF_POINTER_TAGS) { 3279 removeAttribute(tag.name); 3280 } 3281 // Remove old thumbnail data 3282 removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name); 3283 removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name); 3284 3285 // Remove null value tags. 3286 for (int hint = 0; hint < EXIF_TAGS.length; ++hint) { 3287 for (Object obj : mAttributes[hint].entrySet().toArray()) { 3288 final Map.Entry entry = (Map.Entry) obj; 3289 if (entry.getValue() == null) { 3290 mAttributes[hint].remove(entry.getKey()); 3291 } 3292 } 3293 } 3294 3295 // Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD 3296 // offset when there is one or more tags in the thumbnail IFD. 3297 if (!mAttributes[IFD_EXIF_HINT].isEmpty()) { 3298 mAttributes[IFD_TIFF_HINT].put(EXIF_POINTER_TAGS[1].name, 3299 ExifAttribute.createULong(0, mExifByteOrder)); 3300 } 3301 if (!mAttributes[IFD_GPS_HINT].isEmpty()) { 3302 mAttributes[IFD_TIFF_HINT].put(EXIF_POINTER_TAGS[2].name, 3303 ExifAttribute.createULong(0, mExifByteOrder)); 3304 } 3305 if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) { 3306 mAttributes[IFD_EXIF_HINT].put(EXIF_POINTER_TAGS[3].name, 3307 ExifAttribute.createULong(0, mExifByteOrder)); 3308 } 3309 if (mHasThumbnail) { 3310 mAttributes[IFD_THUMBNAIL_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name, 3311 ExifAttribute.createULong(0, mExifByteOrder)); 3312 mAttributes[IFD_THUMBNAIL_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name, 3313 ExifAttribute.createULong(mThumbnailLength, mExifByteOrder)); 3314 } 3315 3316 // Calculate IFD group data area sizes. IFD group data area is assigned to save the entry 3317 // value which has a bigger size than 4 bytes. 3318 for (int i = 0; i < EXIF_TAGS.length; ++i) { 3319 int sum = 0; 3320 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) { 3321 final ExifAttribute exifAttribute = (ExifAttribute) ((Map.Entry) entry).getValue(); 3322 final int size = exifAttribute.size(); 3323 if (size > 4) { 3324 sum += size; 3325 } 3326 } 3327 ifdDataSizes[i] += sum; 3328 } 3329 3330 // Calculate IFD offsets. 3331 int position = 8; 3332 for (int hint = 0; hint < EXIF_TAGS.length; ++hint) { 3333 if (!mAttributes[hint].isEmpty()) { 3334 ifdOffsets[hint] = position; 3335 position += 2 + mAttributes[hint].size() * 12 + 4 + ifdDataSizes[hint]; 3336 } 3337 } 3338 if (mHasThumbnail) { 3339 int thumbnailOffset = position; 3340 mAttributes[IFD_THUMBNAIL_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name, 3341 ExifAttribute.createULong(thumbnailOffset, mExifByteOrder)); 3342 mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset; 3343 position += mThumbnailLength; 3344 } 3345 3346 // Calculate the total size 3347 int totalSize = position + 8; // eight bytes is for header part. 3348 if (DEBUG) { 3349 Log.d(TAG, "totalSize length: " + totalSize); 3350 for (int i = 0; i < EXIF_TAGS.length; ++i) { 3351 Log.d(TAG, String.format("index: %d, offsets: %d, tag count: %d, data sizes: %d", 3352 i, ifdOffsets[i], mAttributes[i].size(), ifdDataSizes[i])); 3353 } 3354 } 3355 3356 // Update IFD pointer tags with the calculated offsets. 3357 if (!mAttributes[IFD_EXIF_HINT].isEmpty()) { 3358 mAttributes[IFD_TIFF_HINT].put(EXIF_POINTER_TAGS[1].name, 3359 ExifAttribute.createULong(ifdOffsets[IFD_EXIF_HINT], mExifByteOrder)); 3360 } 3361 if (!mAttributes[IFD_GPS_HINT].isEmpty()) { 3362 mAttributes[IFD_TIFF_HINT].put(EXIF_POINTER_TAGS[2].name, 3363 ExifAttribute.createULong(ifdOffsets[IFD_GPS_HINT], mExifByteOrder)); 3364 } 3365 if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) { 3366 mAttributes[IFD_EXIF_HINT].put(EXIF_POINTER_TAGS[3].name, ExifAttribute.createULong( 3367 ifdOffsets[IFD_INTEROPERABILITY_HINT], mExifByteOrder)); 3368 } 3369 3370 // Write TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1. 3371 dataOutputStream.writeUnsignedShort(totalSize); 3372 dataOutputStream.write(IDENTIFIER_EXIF_APP1); 3373 dataOutputStream.writeShort(mExifByteOrder == ByteOrder.BIG_ENDIAN 3374 ? BYTE_ALIGN_MM : BYTE_ALIGN_II); 3375 dataOutputStream.setByteOrder(mExifByteOrder); 3376 dataOutputStream.writeUnsignedShort(START_CODE); 3377 dataOutputStream.writeUnsignedInt(IFD_OFFSET); 3378 3379 // Write IFD groups. See JEITA CP-3451C Section 4.5.8. Figure 9. 3380 for (int hint = 0; hint < EXIF_TAGS.length; ++hint) { 3381 if (!mAttributes[hint].isEmpty()) { 3382 // See JEITA CP-3451C Section 4.6.2: IFD structure. 3383 // Write entry count 3384 dataOutputStream.writeUnsignedShort(mAttributes[hint].size()); 3385 3386 // Write entry info 3387 int dataOffset = ifdOffsets[hint] + 2 + mAttributes[hint].size() * 12 + 4; 3388 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) { 3389 // Convert tag name to tag number. 3390 final ExifTag tag = (ExifTag) sExifTagMapsForWriting[hint].get(entry.getKey()); 3391 final int tagNumber = tag.number; 3392 final ExifAttribute attribute = (ExifAttribute) entry.getValue(); 3393 final int size = attribute.size(); 3394 3395 dataOutputStream.writeUnsignedShort(tagNumber); 3396 dataOutputStream.writeUnsignedShort(attribute.format); 3397 dataOutputStream.writeInt(attribute.numberOfComponents); 3398 if (size > 4) { 3399 dataOutputStream.writeUnsignedInt(dataOffset); 3400 dataOffset += size; 3401 } else { 3402 dataOutputStream.write(attribute.bytes); 3403 // Fill zero up to 4 bytes 3404 if (size < 4) { 3405 for (int i = size; i < 4; ++i) { 3406 dataOutputStream.writeByte(0); 3407 } 3408 } 3409 } 3410 } 3411 3412 // Write the next offset. It writes the offset of thumbnail IFD if there is one or 3413 // more tags in the thumbnail IFD when the current IFD is the primary image TIFF 3414 // IFD; Otherwise 0. 3415 if (hint == 0 && !mAttributes[IFD_THUMBNAIL_HINT].isEmpty()) { 3416 dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_THUMBNAIL_HINT]); 3417 } else { 3418 dataOutputStream.writeUnsignedInt(0); 3419 } 3420 3421 // Write values of data field exceeding 4 bytes after the next offset. 3422 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) { 3423 ExifAttribute attribute = (ExifAttribute) entry.getValue(); 3424 3425 if (attribute.bytes.length > 4) { 3426 dataOutputStream.write(attribute.bytes, 0, attribute.bytes.length); 3427 } 3428 } 3429 } 3430 } 3431 3432 // Write thumbnail 3433 if (mHasThumbnail) { 3434 dataOutputStream.write(getThumbnail()); 3435 } 3436 3437 // Reset the byte order to big endian in order to write remaining parts of the JPEG file. 3438 dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN); 3439 3440 return totalSize; 3441 } 3442 3443 /** 3444 * Determines the data format of EXIF entry value. 3445 * 3446 * @param entryValue The value to be determined. 3447 * @return Returns two data formats gussed as a pair in integer. If there is no two candidate 3448 data formats for the given entry value, returns {@code -1} in the second of the pair. 3449 */ 3450 private static Pair<Integer, Integer> guessDataFormat(String entryValue) { 3451 // See TIFF 6.0 Section 2, "Image File Directory". 3452 // Take the first component if there are more than one component. 3453 if (entryValue.contains(",")) { 3454 String[] entryValues = entryValue.split(","); 3455 Pair<Integer, Integer> dataFormat = guessDataFormat(entryValues[0]); 3456 if (dataFormat.first == IFD_FORMAT_STRING) { 3457 return dataFormat; 3458 } 3459 for (int i = 1; i < entryValues.length; ++i) { 3460 final Pair<Integer, Integer> guessDataFormat = guessDataFormat(entryValues[i]); 3461 int first = -1, second = -1; 3462 if (guessDataFormat.first == dataFormat.first 3463 || guessDataFormat.second == dataFormat.first) { 3464 first = dataFormat.first; 3465 } 3466 if (dataFormat.second != -1 && (guessDataFormat.first == dataFormat.second 3467 || guessDataFormat.second == dataFormat.second)) { 3468 second = dataFormat.second; 3469 } 3470 if (first == -1 && second == -1) { 3471 return new Pair<>(IFD_FORMAT_STRING, -1); 3472 } 3473 if (first == -1) { 3474 dataFormat = new Pair<>(second, -1); 3475 continue; 3476 } 3477 if (second == -1) { 3478 dataFormat = new Pair<>(first, -1); 3479 continue; 3480 } 3481 } 3482 return dataFormat; 3483 } 3484 3485 if (entryValue.contains("/")) { 3486 String[] rationalNumber = entryValue.split("/"); 3487 if (rationalNumber.length == 2) { 3488 try { 3489 long numerator = Long.parseLong(rationalNumber[0]); 3490 long denominator = Long.parseLong(rationalNumber[1]); 3491 if (numerator < 0L || denominator < 0L) { 3492 return new Pair<>(IFD_FORMAT_SRATIONAL, -1); 3493 } 3494 if (numerator > Integer.MAX_VALUE || denominator > Integer.MAX_VALUE) { 3495 return new Pair<>(IFD_FORMAT_URATIONAL, -1); 3496 } 3497 return new Pair<>(IFD_FORMAT_SRATIONAL, IFD_FORMAT_URATIONAL); 3498 } catch (NumberFormatException e) { 3499 // Ignored 3500 } 3501 } 3502 return new Pair<>(IFD_FORMAT_STRING, -1); 3503 } 3504 try { 3505 Long longValue = Long.parseLong(entryValue); 3506 if (longValue >= 0 && longValue <= 65535) { 3507 return new Pair<>(IFD_FORMAT_USHORT, IFD_FORMAT_ULONG); 3508 } 3509 if (longValue < 0) { 3510 return new Pair<>(IFD_FORMAT_SLONG, -1); 3511 } 3512 return new Pair<>(IFD_FORMAT_ULONG, -1); 3513 } catch (NumberFormatException e) { 3514 // Ignored 3515 } 3516 try { 3517 Double.parseDouble(entryValue); 3518 return new Pair<>(IFD_FORMAT_DOUBLE, -1); 3519 } catch (NumberFormatException e) { 3520 // Ignored 3521 } 3522 return new Pair<>(IFD_FORMAT_STRING, -1); 3523 } 3524 3525 // An input stream to parse EXIF data area, which can be written in either little or big endian 3526 // order. 3527 private static class ByteOrderAwarenessDataInputStream extends ByteArrayInputStream { 3528 private static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN; 3529 private static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN; 3530 3531 private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN; 3532 private final long mLength; 3533 private long mPosition; 3534 3535 public ByteOrderAwarenessDataInputStream(byte[] bytes) { 3536 super(bytes); 3537 mLength = bytes.length; 3538 mPosition = 0L; 3539 } 3540 3541 public void setByteOrder(ByteOrder byteOrder) { 3542 mByteOrder = byteOrder; 3543 } 3544 3545 public void seek(long byteCount) throws IOException { 3546 if (mPosition > byteCount) { 3547 mPosition = 0L; 3548 reset(); 3549 } else { 3550 byteCount -= mPosition; 3551 } 3552 if (skip(byteCount) != byteCount) { 3553 throw new IOException("Couldn't seek up to the byteCount"); 3554 } 3555 } 3556 3557 public long peek() { 3558 return mPosition; 3559 } 3560 3561 public void readFully(byte[] buffer) throws IOException { 3562 mPosition += buffer.length; 3563 if (mPosition > mLength) { 3564 throw new EOFException(); 3565 } 3566 if (super.read(buffer, 0, buffer.length) != buffer.length) { 3567 throw new IOException("Couldn't read up to the length of buffer"); 3568 } 3569 } 3570 3571 public byte readByte() throws IOException { 3572 ++mPosition; 3573 if (mPosition > mLength) { 3574 throw new EOFException(); 3575 } 3576 int ch = super.read(); 3577 if (ch < 0) { 3578 throw new EOFException(); 3579 } 3580 return (byte) ch; 3581 } 3582 3583 public short readShort() throws IOException { 3584 mPosition += 2; 3585 if (mPosition > mLength) { 3586 throw new EOFException(); 3587 } 3588 int ch1 = super.read(); 3589 int ch2 = super.read(); 3590 if ((ch1 | ch2) < 0) { 3591 throw new EOFException(); 3592 } 3593 if (mByteOrder == LITTLE_ENDIAN) { 3594 return (short) ((ch2 << 8) + (ch1)); 3595 } else if (mByteOrder == BIG_ENDIAN) { 3596 return (short) ((ch1 << 8) + (ch2)); 3597 } 3598 throw new IOException("Invalid byte order: " + mByteOrder); 3599 } 3600 3601 public int readInt() throws IOException { 3602 mPosition += 4; 3603 if (mPosition > mLength) { 3604 throw new EOFException(); 3605 } 3606 int ch1 = super.read(); 3607 int ch2 = super.read(); 3608 int ch3 = super.read(); 3609 int ch4 = super.read(); 3610 if ((ch1 | ch2 | ch3 | ch4) < 0) { 3611 throw new EOFException(); 3612 } 3613 if (mByteOrder == LITTLE_ENDIAN) { 3614 return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1); 3615 } else if (mByteOrder == BIG_ENDIAN) { 3616 return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4); 3617 } 3618 throw new IOException("Invalid byte order: " + mByteOrder); 3619 } 3620 3621 @Override 3622 public long skip(long byteCount) { 3623 long skipped = super.skip(Math.min(byteCount, mLength - mPosition)); 3624 mPosition += skipped; 3625 return skipped; 3626 } 3627 3628 public int readUnsignedShort() throws IOException { 3629 mPosition += 2; 3630 if (mPosition > mLength) { 3631 throw new EOFException(); 3632 } 3633 int ch1 = super.read(); 3634 int ch2 = super.read(); 3635 if ((ch1 | ch2) < 0) { 3636 throw new EOFException(); 3637 } 3638 if (mByteOrder == LITTLE_ENDIAN) { 3639 return ((ch2 << 8) + (ch1)); 3640 } else if (mByteOrder == BIG_ENDIAN) { 3641 return ((ch1 << 8) + (ch2)); 3642 } 3643 throw new IOException("Invalid byte order: " + mByteOrder); 3644 } 3645 3646 public long readUnsignedInt() throws IOException { 3647 return readInt() & 0xffffffffL; 3648 } 3649 3650 public long readLong() throws IOException { 3651 mPosition += 8; 3652 if (mPosition > mLength) { 3653 throw new EOFException(); 3654 } 3655 int ch1 = super.read(); 3656 int ch2 = super.read(); 3657 int ch3 = super.read(); 3658 int ch4 = super.read(); 3659 int ch5 = super.read(); 3660 int ch6 = super.read(); 3661 int ch7 = super.read(); 3662 int ch8 = super.read(); 3663 if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) { 3664 throw new EOFException(); 3665 } 3666 if (mByteOrder == LITTLE_ENDIAN) { 3667 return (((long) ch8 << 56) + ((long) ch7 << 48) + ((long) ch6 << 40) 3668 + ((long) ch5 << 32) + ((long) ch4 << 24) + ((long) ch3 << 16) 3669 + ((long) ch2 << 8) + (long) ch1); 3670 } else if (mByteOrder == BIG_ENDIAN) { 3671 return (((long) ch1 << 56) + ((long) ch2 << 48) + ((long) ch3 << 40) 3672 + ((long) ch4 << 32) + ((long) ch5 << 24) + ((long) ch6 << 16) 3673 + ((long) ch7 << 8) + (long) ch8); 3674 } 3675 throw new IOException("Invalid byte order: " + mByteOrder); 3676 } 3677 3678 public float readFloat() throws IOException { 3679 return Float.intBitsToFloat(readInt()); 3680 } 3681 3682 public double readDouble() throws IOException { 3683 return Double.longBitsToDouble(readLong()); 3684 } 3685 } 3686 3687 // An output stream to write EXIF data area, which can be written in either little or big endian 3688 // order. 3689 private static class ByteOrderAwarenessDataOutputStream extends FilterOutputStream { 3690 private final OutputStream mOutputStream; 3691 private ByteOrder mByteOrder; 3692 3693 public ByteOrderAwarenessDataOutputStream(OutputStream out, ByteOrder byteOrder) { 3694 super(out); 3695 mOutputStream = out; 3696 mByteOrder = byteOrder; 3697 } 3698 3699 public void setByteOrder(ByteOrder byteOrder) { 3700 mByteOrder = byteOrder; 3701 } 3702 3703 public void write(byte[] bytes) throws IOException { 3704 mOutputStream.write(bytes); 3705 } 3706 3707 public void write(byte[] bytes, int offset, int length) throws IOException { 3708 mOutputStream.write(bytes, offset, length); 3709 } 3710 3711 public void writeByte(int val) throws IOException { 3712 mOutputStream.write(val); 3713 } 3714 3715 public void writeShort(short val) throws IOException { 3716 if (mByteOrder == ByteOrder.LITTLE_ENDIAN) { 3717 mOutputStream.write((val >>> 0) & 0xFF); 3718 mOutputStream.write((val >>> 8) & 0xFF); 3719 } else if (mByteOrder == ByteOrder.BIG_ENDIAN) { 3720 mOutputStream.write((val >>> 8) & 0xFF); 3721 mOutputStream.write((val >>> 0) & 0xFF); 3722 } 3723 } 3724 3725 public void writeInt(int val) throws IOException { 3726 if (mByteOrder == ByteOrder.LITTLE_ENDIAN) { 3727 mOutputStream.write((val >>> 0) & 0xFF); 3728 mOutputStream.write((val >>> 8) & 0xFF); 3729 mOutputStream.write((val >>> 16) & 0xFF); 3730 mOutputStream.write((val >>> 24) & 0xFF); 3731 } else if (mByteOrder == ByteOrder.BIG_ENDIAN) { 3732 mOutputStream.write((val >>> 24) & 0xFF); 3733 mOutputStream.write((val >>> 16) & 0xFF); 3734 mOutputStream.write((val >>> 8) & 0xFF); 3735 mOutputStream.write((val >>> 0) & 0xFF); 3736 } 3737 } 3738 3739 public void writeUnsignedShort(int val) throws IOException { 3740 writeShort((short) val); 3741 } 3742 3743 public void writeUnsignedInt(long val) throws IOException { 3744 writeInt((int) val); 3745 } 3746 } 3747 3748 // Swaps image data based on image size 3749 private void swapBasedOnImageSize(int firstImageHint, int secondImageHint) 3750 throws IOException { 3751 if (mAttributes[firstImageHint].isEmpty() || mAttributes[secondImageHint].isEmpty()) { 3752 if (DEBUG) { 3753 Log.d(TAG, "Cannot perform swap since only one image data exists"); 3754 } 3755 return; 3756 } 3757 3758 ExifAttribute firstImageLengthAttribute = 3759 (ExifAttribute) mAttributes[firstImageHint].get(TAG_IMAGE_LENGTH); 3760 ExifAttribute firstImageWidthAttribute = 3761 (ExifAttribute) mAttributes[firstImageHint].get(TAG_IMAGE_WIDTH); 3762 ExifAttribute secondImageLengthAttribute = 3763 (ExifAttribute) mAttributes[secondImageHint].get(TAG_IMAGE_LENGTH); 3764 ExifAttribute secondImageWidthAttribute = 3765 (ExifAttribute) mAttributes[secondImageHint].get(TAG_IMAGE_WIDTH); 3766 3767 if (firstImageLengthAttribute == null || firstImageWidthAttribute == null) { 3768 if (DEBUG) { 3769 Log.d(TAG, "First image does not contain valid size information"); 3770 } 3771 } else if (secondImageLengthAttribute == null || secondImageWidthAttribute == null) { 3772 if (DEBUG) { 3773 Log.d(TAG, "Second image does not contain valid size information"); 3774 } 3775 } else { 3776 int firstImageLengthValue = firstImageLengthAttribute.getIntValue(mExifByteOrder); 3777 int firstImageWidthValue = firstImageWidthAttribute.getIntValue(mExifByteOrder); 3778 int secondImageLengthValue = secondImageLengthAttribute.getIntValue(mExifByteOrder); 3779 int secondImageWidthValue = secondImageWidthAttribute.getIntValue(mExifByteOrder); 3780 3781 if (firstImageLengthValue < secondImageLengthValue && 3782 firstImageWidthValue < secondImageWidthValue) { 3783 HashMap tempMap = mAttributes[firstImageHint]; 3784 mAttributes[firstImageHint] = mAttributes[secondImageHint]; 3785 mAttributes[secondImageHint] = tempMap; 3786 } 3787 } 3788 } 3789 3790 // Checks if there is a match 3791 private boolean containsMatch(byte[] mainBytes, byte[] findBytes) { 3792 for (int i = 0; i < mainBytes.length - findBytes.length; i++) { 3793 for (int j = 0; j < findBytes.length; j++) { 3794 if (mainBytes[i + j] != findBytes[j]) { 3795 break; 3796 } 3797 if (j == findBytes.length - 1) { 3798 return true; 3799 } 3800 } 3801 } 3802 return false; 3803 } 3804 3805 // JNI methods for RAW formats. 3806 private static native void nativeInitRaw(); 3807 private static native byte[] nativeGetThumbnailFromAsset( 3808 long asset, int thumbnailOffset, int thumbnailLength); 3809 private static native HashMap nativeGetRawAttributesFromAsset(long asset); 3810 private static native HashMap nativeGetRawAttributesFromFileDescriptor(FileDescriptor fd); 3811 private static native HashMap nativeGetRawAttributesFromInputStream(InputStream in); 3812}