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