1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.media; 18 19import android.annotation.NonNull; 20import android.content.res.AssetManager; 21import android.graphics.Bitmap; 22import android.graphics.BitmapFactory; 23import android.system.ErrnoException; 24import android.system.Os; 25import android.system.OsConstants; 26import android.util.Log; 27import android.util.Pair; 28 29import java.io.BufferedInputStream; 30import java.io.ByteArrayInputStream; 31import java.io.DataInputStream; 32import java.io.EOFException; 33import java.io.File; 34import java.io.FileDescriptor; 35import java.io.FileInputStream; 36import java.io.FileNotFoundException; 37import java.io.FileOutputStream; 38import java.io.FilterOutputStream; 39import java.io.IOException; 40import java.io.InputStream; 41import java.io.OutputStream; 42import java.nio.ByteBuffer; 43import java.nio.ByteOrder; 44import java.nio.charset.Charset; 45import java.text.ParsePosition; 46import java.text.SimpleDateFormat; 47import java.util.Arrays; 48import java.util.Date; 49import java.util.HashMap; 50import java.util.HashSet; 51import java.util.Map; 52import java.util.Set; 53import java.util.TimeZone; 54import java.util.regex.Matcher; 55import java.util.regex.Pattern; 56 57import libcore.io.IoUtils; 58import libcore.io.Streams; 59 60/** 61 * This is a class for reading and writing Exif tags in a JPEG file or a RAW image file. 62 * <p> 63 * Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF and RAF. 64 * <p> 65 * Attribute mutation is supported for JPEG image files. 66 */ 67public class ExifInterface { 68 private static final String TAG = "ExifInterface"; 69 private static final boolean DEBUG = false; 70 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 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 430 public String toString() { 431 return numerator + "/" + denominator; 432 } 433 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 445 private ExifAttribute(int format, int numberOfComponents, byte[] bytes) { 446 this.format = format; 447 this.numberOfComponents = numberOfComponents; 448 this.bytes = bytes; 449 } 450 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 461 public static ExifAttribute createUShort(int value, ByteOrder byteOrder) { 462 return createUShort(new int[] {value}, byteOrder); 463 } 464 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 475 public static ExifAttribute createULong(long value, ByteOrder byteOrder) { 476 return createULong(new long[] {value}, byteOrder); 477 } 478 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 489 public static ExifAttribute createSLong(int value, ByteOrder byteOrder) { 490 return createSLong(new int[] {value}, byteOrder); 491 } 492 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 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 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 519 public static ExifAttribute createURational(Rational value, ByteOrder byteOrder) { 520 return createURational(new Rational[] {value}, byteOrder); 521 } 522 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 534 public static ExifAttribute createSRational(Rational value, ByteOrder byteOrder) { 535 return createSRational(new Rational[] {value}, byteOrder); 536 } 537 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 548 public static ExifAttribute createDouble(double value, ByteOrder byteOrder) { 549 return createDouble(new double[] {value}, byteOrder); 550 } 551 552 @Override 553 public String toString() { 554 return "(" + IFD_FORMAT_NAMES[format] + ", data length:" + bytes.length + ")"; 555 } 556 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 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 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 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 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 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 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 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 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"); 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]) { 1068 sExifTagMapsForReading[hint].put(tag.number, 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 private boolean mIsSupportedFile; 1087 1088 // Pattern to check non zero timestamp 1089 private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*"); 1090 // Pattern to check gps timestamp 1091 private static final Pattern sGpsTimestampPattern = 1092 Pattern.compile("^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$"); 1093 1094 /** 1095 * Reads Exif tags from the specified image file. 1096 */ 1097 public ExifInterface(String filename) throws IOException { 1098 if (filename == null) { 1099 throw new IllegalArgumentException("filename cannot be null"); 1100 } 1101 FileInputStream in = null; 1102 mAssetInputStream = null; 1103 mFilename = filename; 1104 mIsInputStream = false; 1105 try { 1106 in = new FileInputStream(filename); 1107 if (isSeekableFD(in.getFD())) { 1108 mSeekableFileDescriptor = in.getFD(); 1109 } else { 1110 mSeekableFileDescriptor = null; 1111 } 1112 loadAttributes(in); 1113 } finally { 1114 IoUtils.closeQuietly(in); 1115 } 1116 } 1117 1118 /** 1119 * Reads Exif tags from the specified image file descriptor. Attribute mutation is supported 1120 * for writable and seekable file descriptors only. This constructor will not rewind the offset 1121 * of the given file descriptor. Developers should close the file descriptor after use. 1122 */ 1123 public ExifInterface(FileDescriptor fileDescriptor) throws IOException { 1124 if (fileDescriptor == null) { 1125 throw new IllegalArgumentException("fileDescriptor cannot be null"); 1126 } 1127 mAssetInputStream = null; 1128 mFilename = null; 1129 if (isSeekableFD(fileDescriptor)) { 1130 mSeekableFileDescriptor = fileDescriptor; 1131 // Keep the original file descriptor in order to save attributes when it's seekable. 1132 // Otherwise, just close the given file descriptor after reading it because the save 1133 // feature won't be working. 1134 try { 1135 fileDescriptor = Os.dup(fileDescriptor); 1136 } catch (ErrnoException e) { 1137 throw e.rethrowAsIOException(); 1138 } 1139 } else { 1140 mSeekableFileDescriptor = null; 1141 } 1142 mIsInputStream = false; 1143 FileInputStream in = null; 1144 try { 1145 in = new FileInputStream(fileDescriptor); 1146 loadAttributes(in); 1147 } finally { 1148 IoUtils.closeQuietly(in); 1149 } 1150 } 1151 1152 /** 1153 * Reads Exif tags from the specified image input stream. Attribute mutation is not supported 1154 * for input streams. The given input stream will proceed its current position. Developers 1155 * should close the input stream after use. 1156 */ 1157 public ExifInterface(InputStream inputStream) throws IOException { 1158 if (inputStream == null) { 1159 throw new IllegalArgumentException("inputStream cannot be null"); 1160 } 1161 mFilename = null; 1162 if (inputStream instanceof AssetManager.AssetInputStream) { 1163 mAssetInputStream = (AssetManager.AssetInputStream) inputStream; 1164 mSeekableFileDescriptor = null; 1165 } else if (inputStream instanceof FileInputStream 1166 && isSeekableFD(((FileInputStream) inputStream).getFD())) { 1167 mAssetInputStream = null; 1168 mSeekableFileDescriptor = ((FileInputStream) inputStream).getFD(); 1169 } else { 1170 mAssetInputStream = null; 1171 mSeekableFileDescriptor = null; 1172 } 1173 mIsInputStream = true; 1174 loadAttributes(inputStream); 1175 } 1176 1177 /** 1178 * Returns the EXIF attribute of the specified tag or {@code null} if there is no such tag in 1179 * the image file. 1180 * 1181 * @param tag the name of the tag. 1182 */ 1183 private ExifAttribute getExifAttribute(String tag) { 1184 // Retrieves all tag groups. The value from primary image tag group has a higher priority 1185 // than the value from the thumbnail tag group if there are more than one candidates. 1186 for (int i = 0; i < EXIF_TAGS.length; ++i) { 1187 Object value = mAttributes[i].get(tag); 1188 if (value != null) { 1189 return (ExifAttribute) value; 1190 } 1191 } 1192 return null; 1193 } 1194 1195 /** 1196 * Returns the value of the specified tag or {@code null} if there 1197 * is no such tag in the image file. 1198 * 1199 * @param tag the name of the tag. 1200 */ 1201 public String getAttribute(String tag) { 1202 ExifAttribute attribute = getExifAttribute(tag); 1203 if (attribute != null) { 1204 if (!sTagSetForCompatibility.contains(tag)) { 1205 return attribute.getStringValue(mExifByteOrder); 1206 } 1207 if (tag.equals(TAG_GPS_TIMESTAMP)) { 1208 // Convert the rational values to the custom formats for backwards compatibility. 1209 if (attribute.format != IFD_FORMAT_URATIONAL 1210 && attribute.format != IFD_FORMAT_SRATIONAL) { 1211 return null; 1212 } 1213 Rational[] array = (Rational[]) attribute.getValue(mExifByteOrder); 1214 if (array.length != 3) { 1215 return null; 1216 } 1217 return String.format("%02d:%02d:%02d", 1218 (int) ((float) array[0].numerator / array[0].denominator), 1219 (int) ((float) array[1].numerator / array[1].denominator), 1220 (int) ((float) array[2].numerator / array[2].denominator)); 1221 } 1222 try { 1223 return Double.toString(attribute.getDoubleValue(mExifByteOrder)); 1224 } catch (NumberFormatException e) { 1225 return null; 1226 } 1227 } 1228 return null; 1229 } 1230 1231 /** 1232 * Returns the integer value of the specified tag. If there is no such tag 1233 * in the image file or the value cannot be parsed as integer, return 1234 * <var>defaultValue</var>. 1235 * 1236 * @param tag the name of the tag. 1237 * @param defaultValue the value to return if the tag is not available. 1238 */ 1239 public int getAttributeInt(String tag, int defaultValue) { 1240 ExifAttribute exifAttribute = getExifAttribute(tag); 1241 if (exifAttribute == null) { 1242 return defaultValue; 1243 } 1244 1245 try { 1246 return exifAttribute.getIntValue(mExifByteOrder); 1247 } catch (NumberFormatException e) { 1248 return defaultValue; 1249 } 1250 } 1251 1252 /** 1253 * Returns the double value of the tag that is specified as rational or contains a 1254 * double-formatted value. If there is no such tag in the image file or the value cannot be 1255 * parsed as double, return <var>defaultValue</var>. 1256 * 1257 * @param tag the name of the tag. 1258 * @param defaultValue the value to return if the tag is not available. 1259 */ 1260 public double getAttributeDouble(String tag, double defaultValue) { 1261 ExifAttribute exifAttribute = getExifAttribute(tag); 1262 if (exifAttribute == null) { 1263 return defaultValue; 1264 } 1265 1266 try { 1267 return exifAttribute.getDoubleValue(mExifByteOrder); 1268 } catch (NumberFormatException e) { 1269 return defaultValue; 1270 } 1271 } 1272 1273 /** 1274 * Set the value of the specified tag. 1275 * 1276 * @param tag the name of the tag. 1277 * @param value the value of the tag. 1278 */ 1279 public void setAttribute(String tag, String value) { 1280 // Convert the given value to rational values for backwards compatibility. 1281 if (value != null && sTagSetForCompatibility.contains(tag)) { 1282 if (tag.equals(TAG_GPS_TIMESTAMP)) { 1283 Matcher m = sGpsTimestampPattern.matcher(value); 1284 if (!m.find()) { 1285 Log.w(TAG, "Invalid value for " + tag + " : " + value); 1286 return; 1287 } 1288 value = Integer.parseInt(m.group(1)) + "/1," + Integer.parseInt(m.group(2)) + "/1," 1289 + Integer.parseInt(m.group(3)) + "/1"; 1290 } else { 1291 try { 1292 double doubleValue = Double.parseDouble(value); 1293 value = (long) (doubleValue * 10000L) + "/10000"; 1294 } catch (NumberFormatException e) { 1295 Log.w(TAG, "Invalid value for " + tag + " : " + value); 1296 return; 1297 } 1298 } 1299 } 1300 1301 for (int i = 0 ; i < EXIF_TAGS.length; ++i) { 1302 if (i == IFD_THUMBNAIL_HINT && !mHasThumbnail) { 1303 continue; 1304 } 1305 final Object obj = sExifTagMapsForWriting[i].get(tag); 1306 if (obj != null) { 1307 if (value == null) { 1308 mAttributes[i].remove(tag); 1309 continue; 1310 } 1311 final ExifTag exifTag = (ExifTag) obj; 1312 Pair<Integer, Integer> guess = guessDataFormat(value); 1313 int dataFormat; 1314 if (exifTag.primaryFormat == guess.first || exifTag.primaryFormat == guess.second) { 1315 dataFormat = exifTag.primaryFormat; 1316 } else if (exifTag.secondaryFormat != -1 && (exifTag.secondaryFormat == guess.first 1317 || exifTag.secondaryFormat == guess.second)) { 1318 dataFormat = exifTag.secondaryFormat; 1319 } else if (exifTag.primaryFormat == IFD_FORMAT_BYTE 1320 || exifTag.primaryFormat == IFD_FORMAT_UNDEFINED 1321 || exifTag.primaryFormat == IFD_FORMAT_STRING) { 1322 dataFormat = exifTag.primaryFormat; 1323 } else { 1324 Log.w(TAG, "Given tag (" + tag + ") value didn't match with one of expected " 1325 + "formats: " + IFD_FORMAT_NAMES[exifTag.primaryFormat] 1326 + (exifTag.secondaryFormat == -1 ? "" : ", " 1327 + IFD_FORMAT_NAMES[exifTag.secondaryFormat]) + " (guess: " 1328 + IFD_FORMAT_NAMES[guess.first] + (guess.second == -1 ? "" : ", " 1329 + IFD_FORMAT_NAMES[guess.second]) + ")"); 1330 continue; 1331 } 1332 switch (dataFormat) { 1333 case IFD_FORMAT_BYTE: { 1334 mAttributes[i].put(tag, ExifAttribute.createByte(value)); 1335 break; 1336 } 1337 case IFD_FORMAT_UNDEFINED: 1338 case IFD_FORMAT_STRING: { 1339 mAttributes[i].put(tag, ExifAttribute.createString(value)); 1340 break; 1341 } 1342 case IFD_FORMAT_USHORT: { 1343 final String[] values = value.split(","); 1344 final int[] intArray = new int[values.length]; 1345 for (int j = 0; j < values.length; ++j) { 1346 intArray[j] = Integer.parseInt(values[j]); 1347 } 1348 mAttributes[i].put(tag, 1349 ExifAttribute.createUShort(intArray, mExifByteOrder)); 1350 break; 1351 } 1352 case IFD_FORMAT_SLONG: { 1353 final String[] values = value.split(","); 1354 final int[] intArray = new int[values.length]; 1355 for (int j = 0; j < values.length; ++j) { 1356 intArray[j] = Integer.parseInt(values[j]); 1357 } 1358 mAttributes[i].put(tag, 1359 ExifAttribute.createSLong(intArray, mExifByteOrder)); 1360 break; 1361 } 1362 case IFD_FORMAT_ULONG: { 1363 final String[] values = value.split(","); 1364 final long[] longArray = new long[values.length]; 1365 for (int j = 0; j < values.length; ++j) { 1366 longArray[j] = Long.parseLong(values[j]); 1367 } 1368 mAttributes[i].put(tag, 1369 ExifAttribute.createULong(longArray, mExifByteOrder)); 1370 break; 1371 } 1372 case IFD_FORMAT_URATIONAL: { 1373 final String[] values = value.split(","); 1374 final Rational[] rationalArray = new Rational[values.length]; 1375 for (int j = 0; j < values.length; ++j) { 1376 final String[] numbers = values[j].split("/"); 1377 rationalArray[j] = new Rational(Long.parseLong(numbers[0]), 1378 Long.parseLong(numbers[1])); 1379 } 1380 mAttributes[i].put(tag, 1381 ExifAttribute.createURational(rationalArray, mExifByteOrder)); 1382 break; 1383 } 1384 case IFD_FORMAT_SRATIONAL: { 1385 final String[] values = value.split(","); 1386 final Rational[] rationalArray = new Rational[values.length]; 1387 for (int j = 0; j < values.length; ++j) { 1388 final String[] numbers = values[j].split("/"); 1389 rationalArray[j] = new Rational(Long.parseLong(numbers[0]), 1390 Long.parseLong(numbers[1])); 1391 } 1392 mAttributes[i].put(tag, 1393 ExifAttribute.createSRational(rationalArray, mExifByteOrder)); 1394 break; 1395 } 1396 case IFD_FORMAT_DOUBLE: { 1397 final String[] values = value.split(","); 1398 final double[] doubleArray = new double[values.length]; 1399 for (int j = 0; j < values.length; ++j) { 1400 doubleArray[j] = Double.parseDouble(values[j]); 1401 } 1402 mAttributes[i].put(tag, 1403 ExifAttribute.createDouble(doubleArray, mExifByteOrder)); 1404 break; 1405 } 1406 default: 1407 Log.w(TAG, "Data format isn't one of expected formats: " + dataFormat); 1408 continue; 1409 } 1410 } 1411 } 1412 } 1413 1414 /** 1415 * Update the values of the tags in the tag groups if any value for the tag already was stored. 1416 * 1417 * @param tag the name of the tag. 1418 * @param value the value of the tag in a form of {@link ExifAttribute}. 1419 * @return Returns {@code true} if updating is placed. 1420 */ 1421 private boolean updateAttribute(String tag, ExifAttribute value) { 1422 boolean updated = false; 1423 for (int i = 0 ; i < EXIF_TAGS.length; ++i) { 1424 if (mAttributes[i].containsKey(tag)) { 1425 mAttributes[i].put(tag, value); 1426 updated = true; 1427 } 1428 } 1429 return updated; 1430 } 1431 1432 /** 1433 * Remove any values of the specified tag. 1434 * 1435 * @param tag the name of the tag. 1436 */ 1437 private void removeAttribute(String tag) { 1438 for (int i = 0 ; i < EXIF_TAGS.length; ++i) { 1439 mAttributes[i].remove(tag); 1440 } 1441 } 1442 1443 /** 1444 * This function decides which parser to read the image data according to the given input stream 1445 * type and the content of the input stream. In each case, it reads the first three bytes to 1446 * determine whether the image data format is JPEG or not. 1447 */ 1448 private void loadAttributes(@NonNull InputStream in) throws IOException { 1449 try { 1450 // Initialize mAttributes. 1451 for (int i = 0; i < EXIF_TAGS.length; ++i) { 1452 mAttributes[i] = new HashMap(); 1453 } 1454 1455 // Process RAW input stream 1456 if (mAssetInputStream != null) { 1457 long asset = mAssetInputStream.getNativeAsset(); 1458 if (handleRawResult(nativeGetRawAttributesFromAsset(asset))) { 1459 return; 1460 } 1461 } else if (mSeekableFileDescriptor != null) { 1462 if (handleRawResult(nativeGetRawAttributesFromFileDescriptor( 1463 mSeekableFileDescriptor))) { 1464 return; 1465 } 1466 } else { 1467 in = new BufferedInputStream(in, JPEG_SIGNATURE_SIZE); 1468 if (!isJpegInputStream((BufferedInputStream) in) && handleRawResult( 1469 nativeGetRawAttributesFromInputStream(in))) { 1470 return; 1471 } 1472 } 1473 1474 // Process JPEG input stream 1475 getJpegAttributes(in); 1476 mIsSupportedFile = true; 1477 } catch (IOException e) { 1478 // Ignore exceptions in order to keep the compatibility with the old versions of 1479 // ExifInterface. 1480 mIsSupportedFile = false; 1481 Log.w(TAG, "Invalid image: ExifInterface got an unsupported image format file" 1482 + "(ExifInterface supports JPEG and some RAW image formats only) " 1483 + "or a corrupted JPEG file to ExifInterface.", e); 1484 } finally { 1485 addDefaultValuesForCompatibility(); 1486 1487 if (DEBUG) { 1488 printAttributes(); 1489 } 1490 } 1491 } 1492 1493 private static boolean isJpegInputStream(BufferedInputStream in) throws IOException { 1494 in.mark(JPEG_SIGNATURE_SIZE); 1495 byte[] signatureBytes = new byte[JPEG_SIGNATURE_SIZE]; 1496 if (in.read(signatureBytes) != JPEG_SIGNATURE_SIZE) { 1497 throw new EOFException(); 1498 } 1499 boolean isJpeg = Arrays.equals(JPEG_SIGNATURE, signatureBytes); 1500 in.reset(); 1501 return isJpeg; 1502 } 1503 1504 private boolean handleRawResult(HashMap map) { 1505 if (map == null) { 1506 return false; 1507 } 1508 1509 // Mark for disabling the save feature. 1510 mIsRaw = true; 1511 1512 String value = (String) map.remove(TAG_HAS_THUMBNAIL); 1513 mHasThumbnail = value != null && value.equalsIgnoreCase("true"); 1514 value = (String) map.remove(TAG_THUMBNAIL_OFFSET); 1515 if (value != null) { 1516 mThumbnailOffset = Integer.parseInt(value); 1517 } 1518 value = (String) map.remove(TAG_THUMBNAIL_LENGTH); 1519 if (value != null) { 1520 mThumbnailLength = Integer.parseInt(value); 1521 } 1522 mThumbnailBytes = (byte[]) map.remove(TAG_THUMBNAIL_DATA); 1523 1524 for (Map.Entry entry : (Set<Map.Entry>) map.entrySet()) { 1525 setAttribute((String) entry.getKey(), (String) entry.getValue()); 1526 } 1527 1528 return true; 1529 } 1530 1531 private static boolean isSeekableFD(FileDescriptor fd) throws IOException { 1532 try { 1533 Os.lseek(fd, 0, OsConstants.SEEK_CUR); 1534 return true; 1535 } catch (ErrnoException e) { 1536 return false; 1537 } 1538 } 1539 1540 // Prints out attributes for debugging. 1541 private void printAttributes() { 1542 for (int i = 0; i < mAttributes.length; ++i) { 1543 Log.d(TAG, "The size of tag group[" + i + "]: " + mAttributes[i].size()); 1544 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) { 1545 final ExifAttribute tagValue = (ExifAttribute) entry.getValue(); 1546 Log.d(TAG, "tagName: " + entry.getKey() + ", tagType: " + tagValue.toString() 1547 + ", tagValue: '" + tagValue.getStringValue(mExifByteOrder) + "'"); 1548 } 1549 } 1550 } 1551 1552 /** 1553 * Save the tag data into the original image file. This is expensive because it involves 1554 * copying all the data from one file to another and deleting the old file and renaming the 1555 * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write 1556 * and make a single call rather than multiple calls for each attribute. 1557 * <p> 1558 * This method is only supported for JPEG files. 1559 * </p> 1560 */ 1561 public void saveAttributes() throws IOException { 1562 if (!mIsSupportedFile || mIsRaw) { 1563 throw new IOException("ExifInterface only supports saving attributes on JPEG formats."); 1564 } 1565 if (mIsInputStream || (mSeekableFileDescriptor == null && mFilename == null)) { 1566 throw new IOException( 1567 "ExifInterface does not support saving attributes for the current input."); 1568 } 1569 1570 // Keep the thumbnail in memory 1571 mThumbnailBytes = getThumbnail(); 1572 1573 FileInputStream in = null; 1574 FileOutputStream out = null; 1575 File tempFile = null; 1576 try { 1577 // Move the original file to temporary file. 1578 if (mFilename != null) { 1579 tempFile = new File(mFilename + ".tmp"); 1580 File originalFile = new File(mFilename); 1581 if (!originalFile.renameTo(tempFile)) { 1582 throw new IOException("Could'nt rename to " + tempFile.getAbsolutePath()); 1583 } 1584 } else if (mSeekableFileDescriptor != null) { 1585 tempFile = File.createTempFile("temp", "jpg"); 1586 Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET); 1587 in = new FileInputStream(mSeekableFileDescriptor); 1588 out = new FileOutputStream(tempFile); 1589 Streams.copy(in, out); 1590 } 1591 } catch (ErrnoException e) { 1592 throw e.rethrowAsIOException(); 1593 } finally { 1594 IoUtils.closeQuietly(in); 1595 IoUtils.closeQuietly(out); 1596 } 1597 1598 in = null; 1599 out = null; 1600 try { 1601 // Save the new file. 1602 in = new FileInputStream(tempFile); 1603 if (mFilename != null) { 1604 out = new FileOutputStream(mFilename); 1605 } else if (mSeekableFileDescriptor != null) { 1606 Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET); 1607 out = new FileOutputStream(mSeekableFileDescriptor); 1608 } 1609 saveJpegAttributes(in, out); 1610 } catch (ErrnoException e) { 1611 throw e.rethrowAsIOException(); 1612 } finally { 1613 IoUtils.closeQuietly(in); 1614 IoUtils.closeQuietly(out); 1615 tempFile.delete(); 1616 } 1617 1618 // Discard the thumbnail in memory 1619 mThumbnailBytes = null; 1620 } 1621 1622 /** 1623 * Returns true if the image file has a thumbnail. 1624 */ 1625 public boolean hasThumbnail() { 1626 return mHasThumbnail; 1627 } 1628 1629 /** 1630 * Returns the thumbnail inside the image file, or {@code null} if there is no thumbnail. 1631 * The returned data is in JPEG format and can be decoded using 1632 * {@link android.graphics.BitmapFactory#decodeByteArray(byte[],int,int)} 1633 */ 1634 public byte[] getThumbnail() { 1635 if (!mHasThumbnail) { 1636 return null; 1637 } 1638 if (mThumbnailBytes != null) { 1639 return mThumbnailBytes; 1640 } 1641 1642 // Read the thumbnail. 1643 FileInputStream in = null; 1644 try { 1645 if (mAssetInputStream != null) { 1646 return nativeGetThumbnailFromAsset( 1647 mAssetInputStream.getNativeAsset(), mThumbnailOffset, mThumbnailLength); 1648 } else if (mFilename != null) { 1649 in = new FileInputStream(mFilename); 1650 } else if (mSeekableFileDescriptor != null) { 1651 FileDescriptor fileDescriptor = Os.dup(mSeekableFileDescriptor); 1652 Os.lseek(fileDescriptor, 0, OsConstants.SEEK_SET); 1653 in = new FileInputStream(fileDescriptor); 1654 } 1655 if (in == null) { 1656 // Should not be reached this. 1657 throw new FileNotFoundException(); 1658 } 1659 if (in.skip(mThumbnailOffset) != mThumbnailOffset) { 1660 throw new IOException("Corrupted image"); 1661 } 1662 byte[] buffer = new byte[mThumbnailLength]; 1663 if (in.read(buffer) != mThumbnailLength) { 1664 throw new IOException("Corrupted image"); 1665 } 1666 return buffer; 1667 } catch (IOException | ErrnoException e) { 1668 // Couldn't get a thumbnail image. 1669 } finally { 1670 IoUtils.closeQuietly(in); 1671 } 1672 return null; 1673 } 1674 1675 /** 1676 * Returns the offset and length of thumbnail inside the image file, or 1677 * {@code null} if there is no thumbnail. 1678 * 1679 * @return two-element array, the offset in the first value, and length in 1680 * the second, or {@code null} if no thumbnail was found. 1681 */ 1682 public long[] getThumbnailRange() { 1683 if (!mHasThumbnail) { 1684 return null; 1685 } 1686 1687 long[] range = new long[2]; 1688 range[0] = mThumbnailOffset; 1689 range[1] = mThumbnailLength; 1690 1691 return range; 1692 } 1693 1694 /** 1695 * Stores the latitude and longitude value in a float array. The first element is 1696 * the latitude, and the second element is the longitude. Returns false if the 1697 * Exif tags are not available. 1698 */ 1699 public boolean getLatLong(float output[]) { 1700 String latValue = getAttribute(TAG_GPS_LATITUDE); 1701 String latRef = getAttribute(TAG_GPS_LATITUDE_REF); 1702 String lngValue = getAttribute(TAG_GPS_LONGITUDE); 1703 String lngRef = getAttribute(TAG_GPS_LONGITUDE_REF); 1704 1705 if (latValue != null && latRef != null && lngValue != null && lngRef != null) { 1706 try { 1707 output[0] = convertRationalLatLonToFloat(latValue, latRef); 1708 output[1] = convertRationalLatLonToFloat(lngValue, lngRef); 1709 return true; 1710 } catch (IllegalArgumentException e) { 1711 // if values are not parseable 1712 } 1713 } 1714 1715 return false; 1716 } 1717 1718 /** 1719 * Return the altitude in meters. If the exif tag does not exist, return 1720 * <var>defaultValue</var>. 1721 * 1722 * @param defaultValue the value to return if the tag is not available. 1723 */ 1724 public double getAltitude(double defaultValue) { 1725 double altitude = getAttributeDouble(TAG_GPS_ALTITUDE, -1); 1726 int ref = getAttributeInt(TAG_GPS_ALTITUDE_REF, -1); 1727 1728 if (altitude >= 0 && ref >= 0) { 1729 return (altitude * ((ref == 1) ? -1 : 1)); 1730 } else { 1731 return defaultValue; 1732 } 1733 } 1734 1735 /** 1736 * Returns number of milliseconds since Jan. 1, 1970, midnight local time. 1737 * Returns -1 if the date time information if not available. 1738 * @hide 1739 */ 1740 public long getDateTime() { 1741 String dateTimeString = getAttribute(TAG_DATETIME); 1742 if (dateTimeString == null 1743 || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1; 1744 1745 ParsePosition pos = new ParsePosition(0); 1746 try { 1747 // The exif field is in local time. Parsing it as if it is UTC will yield time 1748 // since 1/1/1970 local time 1749 Date datetime = sFormatter.parse(dateTimeString, pos); 1750 if (datetime == null) return -1; 1751 long msecs = datetime.getTime(); 1752 1753 String subSecs = getAttribute(TAG_SUBSEC_TIME); 1754 if (subSecs != null) { 1755 try { 1756 long sub = Long.valueOf(subSecs); 1757 while (sub > 1000) { 1758 sub /= 10; 1759 } 1760 msecs += sub; 1761 } catch (NumberFormatException e) { 1762 // Ignored 1763 } 1764 } 1765 return msecs; 1766 } catch (IllegalArgumentException e) { 1767 return -1; 1768 } 1769 } 1770 1771 /** 1772 * Returns number of milliseconds since Jan. 1, 1970, midnight UTC. 1773 * Returns -1 if the date time information if not available. 1774 * @hide 1775 */ 1776 public long getGpsDateTime() { 1777 String date = getAttribute(TAG_GPS_DATESTAMP); 1778 String time = getAttribute(TAG_GPS_TIMESTAMP); 1779 if (date == null || time == null 1780 || (!sNonZeroTimePattern.matcher(date).matches() 1781 && !sNonZeroTimePattern.matcher(time).matches())) { 1782 return -1; 1783 } 1784 1785 String dateTimeString = date + ' ' + time; 1786 1787 ParsePosition pos = new ParsePosition(0); 1788 try { 1789 Date datetime = sFormatter.parse(dateTimeString, pos); 1790 if (datetime == null) return -1; 1791 return datetime.getTime(); 1792 } catch (IllegalArgumentException e) { 1793 return -1; 1794 } 1795 } 1796 1797 private static float convertRationalLatLonToFloat(String rationalString, String ref) { 1798 try { 1799 String [] parts = rationalString.split(","); 1800 1801 String [] pair; 1802 pair = parts[0].split("/"); 1803 double degrees = Double.parseDouble(pair[0].trim()) 1804 / Double.parseDouble(pair[1].trim()); 1805 1806 pair = parts[1].split("/"); 1807 double minutes = Double.parseDouble(pair[0].trim()) 1808 / Double.parseDouble(pair[1].trim()); 1809 1810 pair = parts[2].split("/"); 1811 double seconds = Double.parseDouble(pair[0].trim()) 1812 / Double.parseDouble(pair[1].trim()); 1813 1814 double result = degrees + (minutes / 60.0) + (seconds / 3600.0); 1815 if ((ref.equals("S") || ref.equals("W"))) { 1816 return (float) -result; 1817 } 1818 return (float) result; 1819 } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { 1820 // Not valid 1821 throw new IllegalArgumentException(); 1822 } 1823 } 1824 1825 // Loads EXIF attributes from a JPEG input stream. 1826 private void getJpegAttributes(InputStream inputStream) throws IOException { 1827 // See JPEG File Interchange Format Specification page 5. 1828 if (DEBUG) { 1829 Log.d(TAG, "getJpegAttributes starting with: " + inputStream); 1830 } 1831 DataInputStream dataInputStream = new DataInputStream(inputStream); 1832 byte marker; 1833 int bytesRead = 0; 1834 if ((marker = dataInputStream.readByte()) != MARKER) { 1835 throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff)); 1836 } 1837 ++bytesRead; 1838 if (dataInputStream.readByte() != MARKER_SOI) { 1839 throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff)); 1840 } 1841 ++bytesRead; 1842 while (true) { 1843 marker = dataInputStream.readByte(); 1844 if (marker != MARKER) { 1845 throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff)); 1846 } 1847 ++bytesRead; 1848 marker = dataInputStream.readByte(); 1849 if (DEBUG) { 1850 Log.d(TAG, "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff)); 1851 } 1852 ++bytesRead; 1853 1854 // EOI indicates the end of an image and in case of SOS, JPEG image stream starts and 1855 // the image data will terminate right after. 1856 if (marker == MARKER_EOI || marker == MARKER_SOS) { 1857 break; 1858 } 1859 int length = dataInputStream.readUnsignedShort() - 2; 1860 bytesRead += 2; 1861 if (DEBUG) { 1862 Log.d(TAG, "JPEG segment: " + Integer.toHexString(marker & 0xff) + " (length: " 1863 + (length + 2) + ")"); 1864 } 1865 if (length < 0) { 1866 throw new IOException("Invalid length"); 1867 } 1868 switch (marker) { 1869 case MARKER_APP1: { 1870 if (DEBUG) { 1871 Log.d(TAG, "MARKER_APP1"); 1872 } 1873 if (length < 6) { 1874 // Skip if it's not an EXIF APP1 segment. 1875 break; 1876 } 1877 byte[] identifier = new byte[6]; 1878 if (inputStream.read(identifier) != 6) { 1879 throw new IOException("Invalid exif"); 1880 } 1881 bytesRead += 6; 1882 length -= 6; 1883 if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { 1884 // Skip if it's not an EXIF APP1 segment. 1885 break; 1886 } 1887 if (length <= 0) { 1888 throw new IOException("Invalid exif"); 1889 } 1890 if (DEBUG) { 1891 Log.d(TAG, "readExifSegment with a byte array (length: " + length + ")"); 1892 } 1893 byte[] bytes = new byte[length]; 1894 if (dataInputStream.read(bytes) != length) { 1895 throw new IOException("Invalid exif"); 1896 } 1897 readExifSegment(bytes, bytesRead); 1898 bytesRead += length; 1899 length = 0; 1900 break; 1901 } 1902 1903 case MARKER_COM: { 1904 byte[] bytes = new byte[length]; 1905 if (dataInputStream.read(bytes) != length) { 1906 throw new IOException("Invalid exif"); 1907 } 1908 length = 0; 1909 if (getAttribute(TAG_USER_COMMENT) == null) { 1910 mAttributes[IFD_EXIF_HINT].put(TAG_USER_COMMENT, ExifAttribute.createString( 1911 new String(bytes, ASCII))); 1912 } 1913 break; 1914 } 1915 1916 case MARKER_SOF0: 1917 case MARKER_SOF1: 1918 case MARKER_SOF2: 1919 case MARKER_SOF3: 1920 case MARKER_SOF5: 1921 case MARKER_SOF6: 1922 case MARKER_SOF7: 1923 case MARKER_SOF9: 1924 case MARKER_SOF10: 1925 case MARKER_SOF11: 1926 case MARKER_SOF13: 1927 case MARKER_SOF14: 1928 case MARKER_SOF15: { 1929 if (dataInputStream.skipBytes(1) != 1) { 1930 throw new IOException("Invalid SOFx"); 1931 } 1932 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, ExifAttribute.createULong( 1933 dataInputStream.readUnsignedShort(), mExifByteOrder)); 1934 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, ExifAttribute.createULong( 1935 dataInputStream.readUnsignedShort(), mExifByteOrder)); 1936 length -= 5; 1937 break; 1938 } 1939 1940 default: { 1941 break; 1942 } 1943 } 1944 if (length < 0) { 1945 throw new IOException("Invalid length"); 1946 } 1947 if (dataInputStream.skipBytes(length) != length) { 1948 throw new IOException("Invalid JPEG segment"); 1949 } 1950 bytesRead += length; 1951 } 1952 } 1953 1954 // Stores a new JPEG image with EXIF attributes into a given output stream. 1955 private void saveJpegAttributes(InputStream inputStream, OutputStream outputStream) 1956 throws IOException { 1957 // See JPEG File Interchange Format Specification page 5. 1958 if (DEBUG) { 1959 Log.d(TAG, "saveJpegAttributes starting with (inputStream: " + inputStream 1960 + ", outputStream: " + outputStream + ")"); 1961 } 1962 DataInputStream dataInputStream = new DataInputStream(inputStream); 1963 ByteOrderAwarenessDataOutputStream dataOutputStream = 1964 new ByteOrderAwarenessDataOutputStream(outputStream, ByteOrder.BIG_ENDIAN); 1965 if (dataInputStream.readByte() != MARKER) { 1966 throw new IOException("Invalid marker"); 1967 } 1968 dataOutputStream.writeByte(MARKER); 1969 if (dataInputStream.readByte() != MARKER_SOI) { 1970 throw new IOException("Invalid marker"); 1971 } 1972 dataOutputStream.writeByte(MARKER_SOI); 1973 1974 // Write EXIF APP1 segment 1975 dataOutputStream.writeByte(MARKER); 1976 dataOutputStream.writeByte(MARKER_APP1); 1977 writeExifSegment(dataOutputStream, 6); 1978 1979 byte[] bytes = new byte[4096]; 1980 1981 while (true) { 1982 byte marker = dataInputStream.readByte(); 1983 if (marker != MARKER) { 1984 throw new IOException("Invalid marker"); 1985 } 1986 marker = dataInputStream.readByte(); 1987 switch (marker) { 1988 case MARKER_APP1: { 1989 int length = dataInputStream.readUnsignedShort() - 2; 1990 if (length < 0) { 1991 throw new IOException("Invalid length"); 1992 } 1993 byte[] identifier = new byte[6]; 1994 if (length >= 6) { 1995 if (dataInputStream.read(identifier) != 6) { 1996 throw new IOException("Invalid exif"); 1997 } 1998 if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { 1999 // Skip the original EXIF APP1 segment. 2000 if (dataInputStream.skip(length - 6) != length - 6) { 2001 throw new IOException("Invalid length"); 2002 } 2003 break; 2004 } 2005 } 2006 // Copy non-EXIF APP1 segment. 2007 dataOutputStream.writeByte(MARKER); 2008 dataOutputStream.writeByte(marker); 2009 dataOutputStream.writeUnsignedShort(length + 2); 2010 if (length >= 6) { 2011 length -= 6; 2012 dataOutputStream.write(identifier); 2013 } 2014 int read; 2015 while (length > 0 && (read = dataInputStream.read( 2016 bytes, 0, Math.min(length, bytes.length))) >= 0) { 2017 dataOutputStream.write(bytes, 0, read); 2018 length -= read; 2019 } 2020 break; 2021 } 2022 case MARKER_EOI: 2023 case MARKER_SOS: { 2024 dataOutputStream.writeByte(MARKER); 2025 dataOutputStream.writeByte(marker); 2026 // Copy all the remaining data 2027 Streams.copy(dataInputStream, dataOutputStream); 2028 return; 2029 } 2030 default: { 2031 // Copy JPEG segment 2032 dataOutputStream.writeByte(MARKER); 2033 dataOutputStream.writeByte(marker); 2034 int length = dataInputStream.readUnsignedShort(); 2035 dataOutputStream.writeUnsignedShort(length); 2036 length -= 2; 2037 if (length < 0) { 2038 throw new IOException("Invalid length"); 2039 } 2040 int read; 2041 while (length > 0 && (read = dataInputStream.read( 2042 bytes, 0, Math.min(length, bytes.length))) >= 0) { 2043 dataOutputStream.write(bytes, 0, read); 2044 length -= read; 2045 } 2046 break; 2047 } 2048 } 2049 } 2050 } 2051 2052 // Reads the given EXIF byte area and save its tag data into attributes. 2053 private void readExifSegment(byte[] exifBytes, int exifOffsetFromBeginning) throws IOException { 2054 // Parse TIFF Headers. See JEITA CP-3451C Table 1. page 10. 2055 ByteOrderAwarenessDataInputStream dataInputStream = 2056 new ByteOrderAwarenessDataInputStream(exifBytes); 2057 2058 // Read byte align 2059 short byteOrder = dataInputStream.readShort(); 2060 switch (byteOrder) { 2061 case BYTE_ALIGN_II: 2062 if (DEBUG) { 2063 Log.d(TAG, "readExifSegment: Byte Align II"); 2064 } 2065 mExifByteOrder = ByteOrder.LITTLE_ENDIAN; 2066 break; 2067 case BYTE_ALIGN_MM: 2068 if (DEBUG) { 2069 Log.d(TAG, "readExifSegment: Byte Align MM"); 2070 } 2071 mExifByteOrder = ByteOrder.BIG_ENDIAN; 2072 break; 2073 default: 2074 throw new IOException("Invalid byte order: " + Integer.toHexString(byteOrder)); 2075 } 2076 2077 // Set byte order. 2078 dataInputStream.setByteOrder(mExifByteOrder); 2079 2080 int startCode = dataInputStream.readUnsignedShort(); 2081 if (startCode != 0x2a) { 2082 throw new IOException("Invalid exif start: " + Integer.toHexString(startCode)); 2083 } 2084 2085 // Read first ifd offset 2086 long firstIfdOffset = dataInputStream.readUnsignedInt(); 2087 if (firstIfdOffset < 8 || firstIfdOffset >= exifBytes.length) { 2088 throw new IOException("Invalid first Ifd offset: " + firstIfdOffset); 2089 } 2090 firstIfdOffset -= 8; 2091 if (firstIfdOffset > 0) { 2092 if (dataInputStream.skip(firstIfdOffset) != firstIfdOffset) { 2093 throw new IOException("Couldn't jump to first Ifd: " + firstIfdOffset); 2094 } 2095 } 2096 2097 // Read primary image TIFF image file directory. 2098 readImageFileDirectory(dataInputStream, IFD_TIFF_HINT); 2099 2100 // Process thumbnail. 2101 String jpegInterchangeFormatString = getAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name); 2102 String jpegInterchangeFormatLengthString = 2103 getAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name); 2104 if (jpegInterchangeFormatString != null && jpegInterchangeFormatLengthString != null) { 2105 try { 2106 int jpegInterchangeFormat = Integer.parseInt(jpegInterchangeFormatString); 2107 int jpegInterchangeFormatLength = Integer 2108 .parseInt(jpegInterchangeFormatLengthString); 2109 // The following code limits the size of thumbnail size not to overflow EXIF data area. 2110 jpegInterchangeFormatLength = Math.min(jpegInterchangeFormat 2111 + jpegInterchangeFormatLength, exifBytes.length) - jpegInterchangeFormat; 2112 if (jpegInterchangeFormat > 0 && jpegInterchangeFormatLength > 0) { 2113 mHasThumbnail = true; 2114 mThumbnailOffset = exifOffsetFromBeginning + jpegInterchangeFormat; 2115 mThumbnailLength = jpegInterchangeFormatLength; 2116 2117 if (mFilename == null && mAssetInputStream == null 2118 && mSeekableFileDescriptor == null) { 2119 // Save the thumbnail in memory if the input doesn't support reading again. 2120 byte[] thumbnailBytes = new byte[jpegInterchangeFormatLength]; 2121 dataInputStream.seek(jpegInterchangeFormat); 2122 dataInputStream.readFully(thumbnailBytes); 2123 mThumbnailBytes = thumbnailBytes; 2124 2125 if (DEBUG) { 2126 Bitmap bitmap = BitmapFactory.decodeByteArray( 2127 thumbnailBytes, 0, thumbnailBytes.length); 2128 Log.d(TAG, "Thumbnail offset: " + mThumbnailOffset + ", length: " 2129 + mThumbnailLength + ", width: " + bitmap.getWidth() 2130 + ", height: " 2131 + bitmap.getHeight()); 2132 } 2133 } 2134 } 2135 } catch (NumberFormatException e) { 2136 // Ignored the corrupted image. 2137 } 2138 } 2139 } 2140 2141 private void addDefaultValuesForCompatibility() { 2142 // The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag. 2143 String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL); 2144 if (valueOfDateTimeOriginal != null) { 2145 mAttributes[IFD_TIFF_HINT].put(TAG_DATETIME, 2146 ExifAttribute.createString(valueOfDateTimeOriginal)); 2147 } 2148 2149 // Add the default value. 2150 if (getAttribute(TAG_IMAGE_WIDTH) == null) { 2151 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, 2152 ExifAttribute.createULong(0, mExifByteOrder)); 2153 } 2154 if (getAttribute(TAG_IMAGE_LENGTH) == null) { 2155 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, 2156 ExifAttribute.createULong(0, mExifByteOrder)); 2157 } 2158 if (getAttribute(TAG_ORIENTATION) == null) { 2159 mAttributes[IFD_TIFF_HINT].put(TAG_ORIENTATION, 2160 ExifAttribute.createULong(0, mExifByteOrder)); 2161 } 2162 if (getAttribute(TAG_LIGHT_SOURCE) == null) { 2163 mAttributes[IFD_EXIF_HINT].put(TAG_LIGHT_SOURCE, 2164 ExifAttribute.createULong(0, mExifByteOrder)); 2165 } 2166 } 2167 2168 // Reads image file directory, which is a tag group in EXIF. 2169 private void readImageFileDirectory(ByteOrderAwarenessDataInputStream dataInputStream, int hint) 2170 throws IOException { 2171 if (dataInputStream.peek() + 2 > dataInputStream.mLength) { 2172 // Return if there is no data from the offset. 2173 return; 2174 } 2175 // See JEITA CP-3451 Figure 5. page 9. 2176 short numberOfDirectoryEntry = dataInputStream.readShort(); 2177 if (dataInputStream.peek() + 12 * numberOfDirectoryEntry > dataInputStream.mLength) { 2178 // Return if the size of entries is too big. 2179 return; 2180 } 2181 2182 if (DEBUG) { 2183 Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry); 2184 } 2185 2186 for (short i = 0; i < numberOfDirectoryEntry; ++i) { 2187 int tagNumber = dataInputStream.readUnsignedShort(); 2188 int dataFormat = dataInputStream.readUnsignedShort(); 2189 int numberOfComponents = dataInputStream.readInt(); 2190 long nextEntryOffset = dataInputStream.peek() + 4; // next four bytes is for data 2191 // offset or value. 2192 // Look up a corresponding tag from tag number 2193 final ExifTag tag = (ExifTag) sExifTagMapsForReading[hint].get(tagNumber); 2194 2195 if (DEBUG) { 2196 Log.d(TAG, String.format("hint: %d, tagNumber: %d, tagName: %s, dataFormat: %d, " + 2197 "numberOfComponents: %d", hint, tagNumber, tag != null ? tag.name : null, 2198 dataFormat, numberOfComponents)); 2199 } 2200 2201 if (tag == null || dataFormat <= 0 || 2202 dataFormat >= IFD_FORMAT_BYTES_PER_FORMAT.length) { 2203 // Skip if the parsed tag number is not defined or invalid data format. 2204 if (tag == null) { 2205 Log.w(TAG, "Skip the tag entry since tag number is not defined: " + tagNumber); 2206 } else { 2207 Log.w(TAG, "Skip the tag entry since data format is invalid: " + dataFormat); 2208 } 2209 dataInputStream.seek(nextEntryOffset); 2210 continue; 2211 } 2212 2213 // Read a value from data field or seek to the value offset which is stored in data 2214 // field if the size of the entry value is bigger than 4. 2215 int byteCount = numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat]; 2216 if (byteCount > 4) { 2217 long offset = dataInputStream.readUnsignedInt(); 2218 if (DEBUG) { 2219 Log.d(TAG, "seek to data offset: " + offset); 2220 } 2221 if (offset + byteCount <= dataInputStream.mLength) { 2222 dataInputStream.seek(offset); 2223 } else { 2224 // Skip if invalid data offset. 2225 Log.w(TAG, "Skip the tag entry since data offset is invalid: " + offset); 2226 dataInputStream.seek(nextEntryOffset); 2227 continue; 2228 } 2229 } 2230 2231 // Recursively parse IFD when a IFD pointer tag appears. 2232 int innerIfdHint = getIfdHintFromTagNumber(tagNumber); 2233 if (DEBUG) { 2234 Log.d(TAG, "innerIfdHint: " + innerIfdHint + " byteCount: " + byteCount); 2235 } 2236 2237 if (innerIfdHint >= 0) { 2238 long offset = -1L; 2239 // Get offset from data field 2240 switch (dataFormat) { 2241 case IFD_FORMAT_USHORT: { 2242 offset = dataInputStream.readUnsignedShort(); 2243 break; 2244 } 2245 case IFD_FORMAT_SSHORT: { 2246 offset = dataInputStream.readShort(); 2247 break; 2248 } 2249 case IFD_FORMAT_ULONG: { 2250 offset = dataInputStream.readUnsignedInt(); 2251 break; 2252 } 2253 case IFD_FORMAT_SLONG: { 2254 offset = dataInputStream.readInt(); 2255 break; 2256 } 2257 default: { 2258 // Nothing to do 2259 break; 2260 } 2261 } 2262 if (DEBUG) { 2263 Log.d(TAG, String.format("Offset: %d, tagName: %s", offset, tag.name)); 2264 } 2265 if (offset > 0L && offset < dataInputStream.mLength) { 2266 dataInputStream.seek(offset); 2267 readImageFileDirectory(dataInputStream, innerIfdHint); 2268 } else { 2269 Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset); 2270 } 2271 2272 dataInputStream.seek(nextEntryOffset); 2273 continue; 2274 } 2275 2276 byte[] bytes = new byte[numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat]]; 2277 dataInputStream.readFully(bytes); 2278 mAttributes[hint].put( 2279 tag.name, new ExifAttribute(dataFormat, numberOfComponents, bytes)); 2280 if (dataInputStream.peek() != nextEntryOffset) { 2281 dataInputStream.seek(nextEntryOffset); 2282 } 2283 } 2284 2285 if (dataInputStream.peek() + 4 <= dataInputStream.mLength) { 2286 long nextIfdOffset = dataInputStream.readUnsignedInt(); 2287 if (DEBUG) { 2288 Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset)); 2289 } 2290 // The next IFD offset needs to be bigger than 8 2291 // since the first IFD offset is at least 8. 2292 if (nextIfdOffset > 8 && nextIfdOffset < dataInputStream.mLength) { 2293 dataInputStream.seek(nextIfdOffset); 2294 readImageFileDirectory(dataInputStream, IFD_THUMBNAIL_HINT); 2295 } 2296 } 2297 } 2298 2299 // Gets the corresponding IFD group index of the given tag number for writing Exif Tags. 2300 private static int getIfdHintFromTagNumber(int tagNumber) { 2301 for (int i = 0; i < IFD_POINTER_TAG_HINTS.length; ++i) { 2302 if (IFD_POINTER_TAGS[i].number == tagNumber) { 2303 return IFD_POINTER_TAG_HINTS[i]; 2304 } 2305 } 2306 return -1; 2307 } 2308 2309 // Writes an Exif segment into the given output stream. 2310 private int writeExifSegment(ByteOrderAwarenessDataOutputStream dataOutputStream, 2311 int exifOffsetFromBeginning) throws IOException { 2312 // The following variables are for calculating each IFD tag group size in bytes. 2313 int[] ifdOffsets = new int[EXIF_TAGS.length]; 2314 int[] ifdDataSizes = new int[EXIF_TAGS.length]; 2315 2316 // Remove IFD pointer tags (we'll re-add it later.) 2317 for (ExifTag tag : IFD_POINTER_TAGS) { 2318 removeAttribute(tag.name); 2319 } 2320 // Remove old thumbnail data 2321 removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name); 2322 removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name); 2323 2324 // Remove null value tags. 2325 for (int hint = 0; hint < EXIF_TAGS.length; ++hint) { 2326 for (Object obj : mAttributes[hint].entrySet().toArray()) { 2327 final Map.Entry entry = (Map.Entry) obj; 2328 if (entry.getValue() == null) { 2329 mAttributes[hint].remove(entry.getKey()); 2330 } 2331 } 2332 } 2333 2334 // Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD 2335 // offset when there is one or more tags in the thumbnail IFD. 2336 if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) { 2337 mAttributes[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].name, 2338 ExifAttribute.createULong(0, mExifByteOrder)); 2339 } 2340 if (!mAttributes[IFD_EXIF_HINT].isEmpty()) { 2341 mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].name, 2342 ExifAttribute.createULong(0, mExifByteOrder)); 2343 } 2344 if (!mAttributes[IFD_GPS_HINT].isEmpty()) { 2345 mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name, 2346 ExifAttribute.createULong(0, mExifByteOrder)); 2347 } 2348 if (mHasThumbnail) { 2349 mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name, 2350 ExifAttribute.createULong(0, mExifByteOrder)); 2351 mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name, 2352 ExifAttribute.createULong(mThumbnailLength, mExifByteOrder)); 2353 } 2354 2355 // Calculate IFD group data area sizes. IFD group data area is assigned to save the entry 2356 // value which has a bigger size than 4 bytes. 2357 for (int i = 0; i < EXIF_TAGS.length; ++i) { 2358 int sum = 0; 2359 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) { 2360 final ExifAttribute exifAttribute = (ExifAttribute) entry.getValue(); 2361 final int size = exifAttribute.size(); 2362 if (size > 4) { 2363 sum += size; 2364 } 2365 } 2366 ifdDataSizes[i] += sum; 2367 } 2368 2369 // Calculate IFD offsets. 2370 int position = 8; 2371 for (int hint = 0; hint < EXIF_TAGS.length; ++hint) { 2372 if (!mAttributes[hint].isEmpty()) { 2373 ifdOffsets[hint] = position; 2374 position += 2 + mAttributes[hint].size() * 12 + 4 + ifdDataSizes[hint]; 2375 } 2376 } 2377 if (mHasThumbnail) { 2378 int thumbnailOffset = position; 2379 mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name, 2380 ExifAttribute.createULong(thumbnailOffset, mExifByteOrder)); 2381 mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset; 2382 position += mThumbnailLength; 2383 } 2384 2385 // Calculate the total size 2386 int totalSize = position + 8; // eight bytes is for header part. 2387 if (DEBUG) { 2388 Log.d(TAG, "totalSize length: " + totalSize); 2389 for (int i = 0; i < EXIF_TAGS.length; ++i) { 2390 Log.d(TAG, String.format("index: %d, offsets: %d, tag count: %d, data sizes: %d", 2391 i, ifdOffsets[i], mAttributes[i].size(), ifdDataSizes[i])); 2392 } 2393 } 2394 2395 // Update IFD pointer tags with the calculated offsets. 2396 if (!mAttributes[IFD_EXIF_HINT].isEmpty()) { 2397 mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].name, 2398 ExifAttribute.createULong(ifdOffsets[IFD_EXIF_HINT], mExifByteOrder)); 2399 } 2400 if (!mAttributes[IFD_GPS_HINT].isEmpty()) { 2401 mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name, 2402 ExifAttribute.createULong(ifdOffsets[IFD_GPS_HINT], mExifByteOrder)); 2403 } 2404 if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) { 2405 mAttributes[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].name, ExifAttribute.createULong( 2406 ifdOffsets[IFD_INTEROPERABILITY_HINT], mExifByteOrder)); 2407 } 2408 2409 // Write TIFF Headers. See JEITA CP-3451C Table 1. page 10. 2410 dataOutputStream.writeUnsignedShort(totalSize); 2411 dataOutputStream.write(IDENTIFIER_EXIF_APP1); 2412 dataOutputStream.writeShort(mExifByteOrder == ByteOrder.BIG_ENDIAN 2413 ? BYTE_ALIGN_MM : BYTE_ALIGN_II); 2414 dataOutputStream.setByteOrder(mExifByteOrder); 2415 dataOutputStream.writeUnsignedShort(0x2a); 2416 dataOutputStream.writeUnsignedInt(8); 2417 2418 // Write IFD groups. See JEITA CP-3451C Figure 7. page 12. 2419 for (int hint = 0; hint < EXIF_TAGS.length; ++hint) { 2420 if (!mAttributes[hint].isEmpty()) { 2421 // See JEITA CP-3451C 4.6.2 IFD structure. page 13. 2422 // Write entry count 2423 dataOutputStream.writeUnsignedShort(mAttributes[hint].size()); 2424 2425 // Write entry info 2426 int dataOffset = ifdOffsets[hint] + 2 + mAttributes[hint].size() * 12 + 4; 2427 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) { 2428 // Convert tag name to tag number. 2429 final ExifTag tag = (ExifTag) sExifTagMapsForWriting[hint].get(entry.getKey()); 2430 final int tagNumber = tag.number; 2431 final ExifAttribute attribute = (ExifAttribute) entry.getValue(); 2432 final int size = attribute.size(); 2433 2434 dataOutputStream.writeUnsignedShort(tagNumber); 2435 dataOutputStream.writeUnsignedShort(attribute.format); 2436 dataOutputStream.writeInt(attribute.numberOfComponents); 2437 if (size > 4) { 2438 dataOutputStream.writeUnsignedInt(dataOffset); 2439 dataOffset += size; 2440 } else { 2441 dataOutputStream.write(attribute.bytes); 2442 // Fill zero up to 4 bytes 2443 if (size < 4) { 2444 for (int i = size; i < 4; ++i) { 2445 dataOutputStream.writeByte(0); 2446 } 2447 } 2448 } 2449 } 2450 2451 // Write the next offset. It writes the offset of thumbnail IFD if there is one or 2452 // more tags in the thumbnail IFD when the current IFD is the primary image TIFF 2453 // IFD; Otherwise 0. 2454 if (hint == 0 && !mAttributes[IFD_THUMBNAIL_HINT].isEmpty()) { 2455 dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_THUMBNAIL_HINT]); 2456 } else { 2457 dataOutputStream.writeUnsignedInt(0); 2458 } 2459 2460 // Write values of data field exceeding 4 bytes after the next offset. 2461 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) { 2462 ExifAttribute attribute = (ExifAttribute) entry.getValue(); 2463 2464 if (attribute.bytes.length > 4) { 2465 dataOutputStream.write(attribute.bytes, 0, attribute.bytes.length); 2466 } 2467 } 2468 } 2469 } 2470 2471 // Write thumbnail 2472 if (mHasThumbnail) { 2473 dataOutputStream.write(getThumbnail()); 2474 } 2475 2476 // Reset the byte order to big endian in order to write remaining parts of the JPEG file. 2477 dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN); 2478 2479 return totalSize; 2480 } 2481 2482 /** 2483 * Determines the data format of EXIF entry value. 2484 * 2485 * @param entryValue The value to be determined. 2486 * @return Returns two data formats gussed as a pair in integer. If there is no two candidate 2487 data formats for the given entry value, returns {@code -1} in the second of the pair. 2488 */ 2489 private static Pair<Integer, Integer> guessDataFormat(String entryValue) { 2490 // See TIFF 6.0 spec Types. page 15. 2491 // Take the first component if there are more than one component. 2492 if (entryValue.contains(",")) { 2493 String[] entryValues = entryValue.split(","); 2494 Pair<Integer, Integer> dataFormat = guessDataFormat(entryValues[0]); 2495 if (dataFormat.first == IFD_FORMAT_STRING) { 2496 return dataFormat; 2497 } 2498 for (int i = 1; i < entryValues.length; ++i) { 2499 final Pair<Integer, Integer> guessDataFormat = guessDataFormat(entryValues[i]); 2500 int first = -1, second = -1; 2501 if (guessDataFormat.first == dataFormat.first 2502 || guessDataFormat.second == dataFormat.first) { 2503 first = dataFormat.first; 2504 } 2505 if (dataFormat.second != -1 && (guessDataFormat.first == dataFormat.second 2506 || guessDataFormat.second == dataFormat.second)) { 2507 second = dataFormat.second; 2508 } 2509 if (first == -1 && second == -1) { 2510 return new Pair<>(IFD_FORMAT_STRING, -1); 2511 } 2512 if (first == -1) { 2513 dataFormat = new Pair<>(second, -1); 2514 continue; 2515 } 2516 if (second == -1) { 2517 dataFormat = new Pair<>(first, -1); 2518 continue; 2519 } 2520 } 2521 return dataFormat; 2522 } 2523 2524 if (entryValue.contains("/")) { 2525 String[] rationalNumber = entryValue.split("/"); 2526 if (rationalNumber.length == 2) { 2527 try { 2528 long numerator = Long.parseLong(rationalNumber[0]); 2529 long denominator = Long.parseLong(rationalNumber[1]); 2530 if (numerator < 0L || denominator < 0L) { 2531 return new Pair<>(IFD_FORMAT_SRATIONAL, - 1); 2532 } 2533 if (numerator > Integer.MAX_VALUE || denominator > Integer.MAX_VALUE) { 2534 return new Pair<>(IFD_FORMAT_URATIONAL, -1); 2535 } 2536 return new Pair<>(IFD_FORMAT_SRATIONAL, IFD_FORMAT_URATIONAL); 2537 } catch (NumberFormatException e) { 2538 // Ignored 2539 } 2540 } 2541 return new Pair<>(IFD_FORMAT_STRING, -1); 2542 } 2543 try { 2544 Long longValue = Long.parseLong(entryValue); 2545 if (longValue >= 0 && longValue <= 65535) { 2546 return new Pair<>(IFD_FORMAT_USHORT, IFD_FORMAT_ULONG); 2547 } 2548 if (longValue < 0) { 2549 return new Pair<>(IFD_FORMAT_SLONG, -1); 2550 } 2551 return new Pair<>(IFD_FORMAT_ULONG, -1); 2552 } catch (NumberFormatException e) { 2553 // Ignored 2554 } 2555 try { 2556 Double.parseDouble(entryValue); 2557 return new Pair<>(IFD_FORMAT_DOUBLE, -1); 2558 } catch (NumberFormatException e) { 2559 // Ignored 2560 } 2561 return new Pair<>(IFD_FORMAT_STRING, -1); 2562 } 2563 2564 // An input stream to parse EXIF data area, which can be written in either little or big endian 2565 // order. 2566 private static class ByteOrderAwarenessDataInputStream extends ByteArrayInputStream { 2567 private static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN; 2568 private static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN; 2569 2570 private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN; 2571 private final long mLength; 2572 private long mPosition; 2573 2574 public ByteOrderAwarenessDataInputStream(byte[] bytes) { 2575 super(bytes); 2576 mLength = bytes.length; 2577 mPosition = 0L; 2578 } 2579 2580 public void setByteOrder(ByteOrder byteOrder) { 2581 mByteOrder = byteOrder; 2582 } 2583 2584 public void seek(long byteCount) throws IOException { 2585 mPosition = 0L; 2586 reset(); 2587 if (skip(byteCount) != byteCount) { 2588 throw new IOException("Couldn't seek up to the byteCount"); 2589 } 2590 } 2591 2592 public long peek() { 2593 return mPosition; 2594 } 2595 2596 public void readFully(byte[] buffer) throws IOException { 2597 mPosition += buffer.length; 2598 if (mPosition > mLength) { 2599 throw new EOFException(); 2600 } 2601 if (super.read(buffer, 0, buffer.length) != buffer.length) { 2602 throw new IOException("Couldn't read up to the length of buffer"); 2603 } 2604 } 2605 2606 public byte readByte() throws IOException { 2607 ++mPosition; 2608 if (mPosition > mLength) { 2609 throw new EOFException(); 2610 } 2611 int ch = super.read(); 2612 if (ch < 0) { 2613 throw new EOFException(); 2614 } 2615 return (byte) ch; 2616 } 2617 2618 public short readShort() throws IOException { 2619 mPosition += 2; 2620 if (mPosition > mLength) { 2621 throw new EOFException(); 2622 } 2623 int ch1 = super.read(); 2624 int ch2 = super.read(); 2625 if ((ch1 | ch2) < 0) { 2626 throw new EOFException(); 2627 } 2628 if (mByteOrder == LITTLE_ENDIAN) { 2629 return (short) ((ch2 << 8) + (ch1)); 2630 } else if (mByteOrder == BIG_ENDIAN) { 2631 return (short) ((ch1 << 8) + (ch2)); 2632 } 2633 throw new IOException("Invalid byte order: " + mByteOrder); 2634 } 2635 2636 public int readInt() throws IOException { 2637 mPosition += 4; 2638 if (mPosition > mLength) { 2639 throw new EOFException(); 2640 } 2641 int ch1 = super.read(); 2642 int ch2 = super.read(); 2643 int ch3 = super.read(); 2644 int ch4 = super.read(); 2645 if ((ch1 | ch2 | ch3 | ch4) < 0) { 2646 throw new EOFException(); 2647 } 2648 if (mByteOrder == LITTLE_ENDIAN) { 2649 return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1); 2650 } else if (mByteOrder == BIG_ENDIAN) { 2651 return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4); 2652 } 2653 throw new IOException("Invalid byte order: " + mByteOrder); 2654 } 2655 2656 @Override 2657 public long skip(long byteCount) { 2658 long skipped = super.skip(Math.min(byteCount, mLength - mPosition)); 2659 mPosition += skipped; 2660 return skipped; 2661 } 2662 2663 public int readUnsignedShort() throws IOException { 2664 mPosition += 2; 2665 if (mPosition > mLength) { 2666 throw new EOFException(); 2667 } 2668 int ch1 = super.read(); 2669 int ch2 = super.read(); 2670 if ((ch1 | ch2) < 0) { 2671 throw new EOFException(); 2672 } 2673 if (mByteOrder == LITTLE_ENDIAN) { 2674 return ((ch2 << 8) + (ch1)); 2675 } else if (mByteOrder == BIG_ENDIAN) { 2676 return ((ch1 << 8) + (ch2)); 2677 } 2678 throw new IOException("Invalid byte order: " + mByteOrder); 2679 } 2680 2681 public long readUnsignedInt() throws IOException { 2682 return readInt() & 0xffffffffL; 2683 } 2684 2685 public long readLong() throws IOException { 2686 mPosition += 8; 2687 if (mPosition > mLength) { 2688 throw new EOFException(); 2689 } 2690 int ch1 = super.read(); 2691 int ch2 = super.read(); 2692 int ch3 = super.read(); 2693 int ch4 = super.read(); 2694 int ch5 = super.read(); 2695 int ch6 = super.read(); 2696 int ch7 = super.read(); 2697 int ch8 = super.read(); 2698 if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) { 2699 throw new EOFException(); 2700 } 2701 if (mByteOrder == LITTLE_ENDIAN) { 2702 return (((long) ch8 << 56) + ((long) ch7 << 48) + ((long) ch6 << 40) 2703 + ((long) ch5 << 32) + ((long) ch4 << 24) + ((long) ch3 << 16) 2704 + ((long) ch2 << 8) + (long) ch1); 2705 } else if (mByteOrder == BIG_ENDIAN) { 2706 return (((long) ch1 << 56) + ((long) ch2 << 48) + ((long) ch3 << 40) 2707 + ((long) ch4 << 32) + ((long) ch5 << 24) + ((long) ch6 << 16) 2708 + ((long) ch7 << 8) + (long) ch8); 2709 } 2710 throw new IOException("Invalid byte order: " + mByteOrder); 2711 } 2712 2713 public float readFloat() throws IOException { 2714 return Float.intBitsToFloat(readInt()); 2715 } 2716 2717 public double readDouble() throws IOException { 2718 return Double.longBitsToDouble(readLong()); 2719 } 2720 } 2721 2722 // An output stream to write EXIF data area, which can be written in either little or big endian 2723 // order. 2724 private static class ByteOrderAwarenessDataOutputStream extends FilterOutputStream { 2725 private final OutputStream mOutputStream; 2726 private ByteOrder mByteOrder; 2727 2728 public ByteOrderAwarenessDataOutputStream(OutputStream out, ByteOrder byteOrder) { 2729 super(out); 2730 mOutputStream = out; 2731 mByteOrder = byteOrder; 2732 } 2733 2734 public void setByteOrder(ByteOrder byteOrder) { 2735 mByteOrder = byteOrder; 2736 } 2737 2738 public void write(byte[] bytes) throws IOException { 2739 mOutputStream.write(bytes); 2740 } 2741 2742 public void write(byte[] bytes, int offset, int length) throws IOException { 2743 mOutputStream.write(bytes, offset, length); 2744 } 2745 2746 public void writeByte(int val) throws IOException { 2747 mOutputStream.write(val); 2748 } 2749 2750 public void writeShort(short val) throws IOException { 2751 if (mByteOrder == ByteOrder.LITTLE_ENDIAN) { 2752 mOutputStream.write((val >>> 0) & 0xFF); 2753 mOutputStream.write((val >>> 8) & 0xFF); 2754 } else if (mByteOrder == ByteOrder.BIG_ENDIAN) { 2755 mOutputStream.write((val >>> 8) & 0xFF); 2756 mOutputStream.write((val >>> 0) & 0xFF); 2757 } 2758 } 2759 2760 public void writeInt(int val) throws IOException { 2761 if (mByteOrder == ByteOrder.LITTLE_ENDIAN) { 2762 mOutputStream.write((val >>> 0) & 0xFF); 2763 mOutputStream.write((val >>> 8) & 0xFF); 2764 mOutputStream.write((val >>> 16) & 0xFF); 2765 mOutputStream.write((val >>> 24) & 0xFF); 2766 } else if (mByteOrder == ByteOrder.BIG_ENDIAN) { 2767 mOutputStream.write((val >>> 24) & 0xFF); 2768 mOutputStream.write((val >>> 16) & 0xFF); 2769 mOutputStream.write((val >>> 8) & 0xFF); 2770 mOutputStream.write((val >>> 0) & 0xFF); 2771 } 2772 } 2773 2774 public void writeUnsignedShort(int val) throws IOException { 2775 writeShort((short) val); 2776 } 2777 2778 public void writeUnsignedInt(long val) throws IOException { 2779 writeInt((int) val); 2780 } 2781 } 2782 2783 // JNI methods for RAW formats. 2784 private static native void nativeInitRaw(); 2785 private static native byte[] nativeGetThumbnailFromAsset( 2786 long asset, int thumbnailOffset, int thumbnailLength); 2787 private static native HashMap nativeGetRawAttributesFromAsset(long asset); 2788 private static native HashMap nativeGetRawAttributesFromFileDescriptor(FileDescriptor fd); 2789 private static native HashMap nativeGetRawAttributesFromInputStream(InputStream in); 2790} 2791