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