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