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