ExifInterface.java revision d0a8a690341ee630162d92f590090a7005afd773
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 * @throws UnsupportedOperationException If this method is called with unsupported files. 1562 */ 1563 public void saveAttributes() throws IOException { 1564 if (!mIsSupportedFile || mIsRaw) { 1565 throw new UnsupportedOperationException( 1566 "ExifInterface only supports saving attributes on JPEG formats."); 1567 } 1568 if (mIsInputStream || (mSeekableFileDescriptor == null && mFilename == null)) { 1569 throw new UnsupportedOperationException( 1570 "ExifInterface does not support saving attributes for the current input."); 1571 } 1572 1573 // Keep the thumbnail in memory 1574 mThumbnailBytes = getThumbnail(); 1575 1576 FileInputStream in = null; 1577 FileOutputStream out = null; 1578 File tempFile = null; 1579 try { 1580 // Move the original file to temporary file. 1581 if (mFilename != null) { 1582 tempFile = new File(mFilename + ".tmp"); 1583 File originalFile = new File(mFilename); 1584 if (!originalFile.renameTo(tempFile)) { 1585 throw new IOException("Could'nt rename to " + tempFile.getAbsolutePath()); 1586 } 1587 } else if (mSeekableFileDescriptor != null) { 1588 tempFile = File.createTempFile("temp", "jpg"); 1589 Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET); 1590 in = new FileInputStream(mSeekableFileDescriptor); 1591 out = new FileOutputStream(tempFile); 1592 Streams.copy(in, out); 1593 } 1594 } catch (ErrnoException e) { 1595 throw e.rethrowAsIOException(); 1596 } finally { 1597 IoUtils.closeQuietly(in); 1598 IoUtils.closeQuietly(out); 1599 } 1600 1601 in = null; 1602 out = null; 1603 try { 1604 // Save the new file. 1605 in = new FileInputStream(tempFile); 1606 if (mFilename != null) { 1607 out = new FileOutputStream(mFilename); 1608 } else if (mSeekableFileDescriptor != null) { 1609 Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET); 1610 out = new FileOutputStream(mSeekableFileDescriptor); 1611 } 1612 saveJpegAttributes(in, out); 1613 } catch (ErrnoException e) { 1614 throw e.rethrowAsIOException(); 1615 } finally { 1616 IoUtils.closeQuietly(in); 1617 IoUtils.closeQuietly(out); 1618 tempFile.delete(); 1619 } 1620 1621 // Discard the thumbnail in memory 1622 mThumbnailBytes = null; 1623 } 1624 1625 /** 1626 * Returns true if the image file has a thumbnail. 1627 */ 1628 public boolean hasThumbnail() { 1629 return mHasThumbnail; 1630 } 1631 1632 /** 1633 * Returns the thumbnail inside the image file, or {@code null} if there is no thumbnail. 1634 * The returned data is in JPEG format and can be decoded using 1635 * {@link android.graphics.BitmapFactory#decodeByteArray(byte[],int,int)} 1636 */ 1637 public byte[] getThumbnail() { 1638 if (!mHasThumbnail) { 1639 return null; 1640 } 1641 if (mThumbnailBytes != null) { 1642 return mThumbnailBytes; 1643 } 1644 1645 // Read the thumbnail. 1646 FileInputStream in = null; 1647 try { 1648 if (mAssetInputStream != null) { 1649 return nativeGetThumbnailFromAsset( 1650 mAssetInputStream.getNativeAsset(), mThumbnailOffset, mThumbnailLength); 1651 } else if (mFilename != null) { 1652 in = new FileInputStream(mFilename); 1653 } else if (mSeekableFileDescriptor != null) { 1654 FileDescriptor fileDescriptor = Os.dup(mSeekableFileDescriptor); 1655 Os.lseek(fileDescriptor, 0, OsConstants.SEEK_SET); 1656 in = new FileInputStream(fileDescriptor); 1657 } 1658 if (in == null) { 1659 // Should not be reached this. 1660 throw new FileNotFoundException(); 1661 } 1662 if (in.skip(mThumbnailOffset) != mThumbnailOffset) { 1663 throw new IOException("Corrupted image"); 1664 } 1665 byte[] buffer = new byte[mThumbnailLength]; 1666 if (in.read(buffer) != mThumbnailLength) { 1667 throw new IOException("Corrupted image"); 1668 } 1669 return buffer; 1670 } catch (IOException | ErrnoException e) { 1671 // Couldn't get a thumbnail image. 1672 } finally { 1673 IoUtils.closeQuietly(in); 1674 } 1675 return null; 1676 } 1677 1678 /** 1679 * Returns the offset and length of thumbnail inside the image file, or 1680 * {@code null} if there is no thumbnail. 1681 * 1682 * @return two-element array, the offset in the first value, and length in 1683 * the second, or {@code null} if no thumbnail was found. 1684 */ 1685 public long[] getThumbnailRange() { 1686 if (!mHasThumbnail) { 1687 return null; 1688 } 1689 1690 long[] range = new long[2]; 1691 range[0] = mThumbnailOffset; 1692 range[1] = mThumbnailLength; 1693 1694 return range; 1695 } 1696 1697 /** 1698 * Stores the latitude and longitude value in a float array. The first element is 1699 * the latitude, and the second element is the longitude. Returns false if the 1700 * Exif tags are not available. 1701 */ 1702 public boolean getLatLong(float output[]) { 1703 String latValue = getAttribute(TAG_GPS_LATITUDE); 1704 String latRef = getAttribute(TAG_GPS_LATITUDE_REF); 1705 String lngValue = getAttribute(TAG_GPS_LONGITUDE); 1706 String lngRef = getAttribute(TAG_GPS_LONGITUDE_REF); 1707 1708 if (latValue != null && latRef != null && lngValue != null && lngRef != null) { 1709 try { 1710 output[0] = convertRationalLatLonToFloat(latValue, latRef); 1711 output[1] = convertRationalLatLonToFloat(lngValue, lngRef); 1712 return true; 1713 } catch (IllegalArgumentException e) { 1714 // if values are not parseable 1715 } 1716 } 1717 1718 return false; 1719 } 1720 1721 /** 1722 * Return the altitude in meters. If the exif tag does not exist, return 1723 * <var>defaultValue</var>. 1724 * 1725 * @param defaultValue the value to return if the tag is not available. 1726 */ 1727 public double getAltitude(double defaultValue) { 1728 double altitude = getAttributeDouble(TAG_GPS_ALTITUDE, -1); 1729 int ref = getAttributeInt(TAG_GPS_ALTITUDE_REF, -1); 1730 1731 if (altitude >= 0 && ref >= 0) { 1732 return (altitude * ((ref == 1) ? -1 : 1)); 1733 } else { 1734 return defaultValue; 1735 } 1736 } 1737 1738 /** 1739 * Returns number of milliseconds since Jan. 1, 1970, midnight local time. 1740 * Returns -1 if the date time information if not available. 1741 * @hide 1742 */ 1743 public long getDateTime() { 1744 String dateTimeString = getAttribute(TAG_DATETIME); 1745 if (dateTimeString == null 1746 || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1; 1747 1748 ParsePosition pos = new ParsePosition(0); 1749 try { 1750 // The exif field is in local time. Parsing it as if it is UTC will yield time 1751 // since 1/1/1970 local time 1752 Date datetime = sFormatter.parse(dateTimeString, pos); 1753 if (datetime == null) return -1; 1754 long msecs = datetime.getTime(); 1755 1756 String subSecs = getAttribute(TAG_SUBSEC_TIME); 1757 if (subSecs != null) { 1758 try { 1759 long sub = Long.valueOf(subSecs); 1760 while (sub > 1000) { 1761 sub /= 10; 1762 } 1763 msecs += sub; 1764 } catch (NumberFormatException e) { 1765 // Ignored 1766 } 1767 } 1768 return msecs; 1769 } catch (IllegalArgumentException e) { 1770 return -1; 1771 } 1772 } 1773 1774 /** 1775 * Returns number of milliseconds since Jan. 1, 1970, midnight UTC. 1776 * Returns -1 if the date time information if not available. 1777 * @hide 1778 */ 1779 public long getGpsDateTime() { 1780 String date = getAttribute(TAG_GPS_DATESTAMP); 1781 String time = getAttribute(TAG_GPS_TIMESTAMP); 1782 if (date == null || time == null 1783 || (!sNonZeroTimePattern.matcher(date).matches() 1784 && !sNonZeroTimePattern.matcher(time).matches())) { 1785 return -1; 1786 } 1787 1788 String dateTimeString = date + ' ' + time; 1789 1790 ParsePosition pos = new ParsePosition(0); 1791 try { 1792 Date datetime = sFormatter.parse(dateTimeString, pos); 1793 if (datetime == null) return -1; 1794 return datetime.getTime(); 1795 } catch (IllegalArgumentException e) { 1796 return -1; 1797 } 1798 } 1799 1800 private static float convertRationalLatLonToFloat(String rationalString, String ref) { 1801 try { 1802 String [] parts = rationalString.split(","); 1803 1804 String [] pair; 1805 pair = parts[0].split("/"); 1806 double degrees = Double.parseDouble(pair[0].trim()) 1807 / Double.parseDouble(pair[1].trim()); 1808 1809 pair = parts[1].split("/"); 1810 double minutes = Double.parseDouble(pair[0].trim()) 1811 / Double.parseDouble(pair[1].trim()); 1812 1813 pair = parts[2].split("/"); 1814 double seconds = Double.parseDouble(pair[0].trim()) 1815 / Double.parseDouble(pair[1].trim()); 1816 1817 double result = degrees + (minutes / 60.0) + (seconds / 3600.0); 1818 if ((ref.equals("S") || ref.equals("W"))) { 1819 return (float) -result; 1820 } 1821 return (float) result; 1822 } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { 1823 // Not valid 1824 throw new IllegalArgumentException(); 1825 } 1826 } 1827 1828 // Loads EXIF attributes from a JPEG input stream. 1829 private void getJpegAttributes(InputStream inputStream) throws IOException { 1830 // See JPEG File Interchange Format Specification page 5. 1831 if (DEBUG) { 1832 Log.d(TAG, "getJpegAttributes starting with: " + inputStream); 1833 } 1834 DataInputStream dataInputStream = new DataInputStream(inputStream); 1835 byte marker; 1836 int bytesRead = 0; 1837 if ((marker = dataInputStream.readByte()) != MARKER) { 1838 throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff)); 1839 } 1840 ++bytesRead; 1841 if (dataInputStream.readByte() != MARKER_SOI) { 1842 throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff)); 1843 } 1844 ++bytesRead; 1845 while (true) { 1846 marker = dataInputStream.readByte(); 1847 if (marker != MARKER) { 1848 throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff)); 1849 } 1850 ++bytesRead; 1851 marker = dataInputStream.readByte(); 1852 if (DEBUG) { 1853 Log.d(TAG, "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff)); 1854 } 1855 ++bytesRead; 1856 1857 // EOI indicates the end of an image and in case of SOS, JPEG image stream starts and 1858 // the image data will terminate right after. 1859 if (marker == MARKER_EOI || marker == MARKER_SOS) { 1860 break; 1861 } 1862 int length = dataInputStream.readUnsignedShort() - 2; 1863 bytesRead += 2; 1864 if (DEBUG) { 1865 Log.d(TAG, "JPEG segment: " + Integer.toHexString(marker & 0xff) + " (length: " 1866 + (length + 2) + ")"); 1867 } 1868 if (length < 0) { 1869 throw new IOException("Invalid length"); 1870 } 1871 switch (marker) { 1872 case MARKER_APP1: { 1873 if (DEBUG) { 1874 Log.d(TAG, "MARKER_APP1"); 1875 } 1876 if (length < 6) { 1877 // Skip if it's not an EXIF APP1 segment. 1878 break; 1879 } 1880 byte[] identifier = new byte[6]; 1881 if (inputStream.read(identifier) != 6) { 1882 throw new IOException("Invalid exif"); 1883 } 1884 bytesRead += 6; 1885 length -= 6; 1886 if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { 1887 // Skip if it's not an EXIF APP1 segment. 1888 break; 1889 } 1890 if (length <= 0) { 1891 throw new IOException("Invalid exif"); 1892 } 1893 if (DEBUG) { 1894 Log.d(TAG, "readExifSegment with a byte array (length: " + length + ")"); 1895 } 1896 byte[] bytes = new byte[length]; 1897 if (dataInputStream.read(bytes) != length) { 1898 throw new IOException("Invalid exif"); 1899 } 1900 readExifSegment(bytes, bytesRead); 1901 bytesRead += length; 1902 length = 0; 1903 break; 1904 } 1905 1906 case MARKER_COM: { 1907 byte[] bytes = new byte[length]; 1908 if (dataInputStream.read(bytes) != length) { 1909 throw new IOException("Invalid exif"); 1910 } 1911 length = 0; 1912 if (getAttribute(TAG_USER_COMMENT) == null) { 1913 mAttributes[IFD_EXIF_HINT].put(TAG_USER_COMMENT, ExifAttribute.createString( 1914 new String(bytes, ASCII))); 1915 } 1916 break; 1917 } 1918 1919 case MARKER_SOF0: 1920 case MARKER_SOF1: 1921 case MARKER_SOF2: 1922 case MARKER_SOF3: 1923 case MARKER_SOF5: 1924 case MARKER_SOF6: 1925 case MARKER_SOF7: 1926 case MARKER_SOF9: 1927 case MARKER_SOF10: 1928 case MARKER_SOF11: 1929 case MARKER_SOF13: 1930 case MARKER_SOF14: 1931 case MARKER_SOF15: { 1932 if (dataInputStream.skipBytes(1) != 1) { 1933 throw new IOException("Invalid SOFx"); 1934 } 1935 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, ExifAttribute.createULong( 1936 dataInputStream.readUnsignedShort(), mExifByteOrder)); 1937 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, ExifAttribute.createULong( 1938 dataInputStream.readUnsignedShort(), mExifByteOrder)); 1939 length -= 5; 1940 break; 1941 } 1942 1943 default: { 1944 break; 1945 } 1946 } 1947 if (length < 0) { 1948 throw new IOException("Invalid length"); 1949 } 1950 if (dataInputStream.skipBytes(length) != length) { 1951 throw new IOException("Invalid JPEG segment"); 1952 } 1953 bytesRead += length; 1954 } 1955 } 1956 1957 // Stores a new JPEG image with EXIF attributes into a given output stream. 1958 private void saveJpegAttributes(InputStream inputStream, OutputStream outputStream) 1959 throws IOException { 1960 // See JPEG File Interchange Format Specification page 5. 1961 if (DEBUG) { 1962 Log.d(TAG, "saveJpegAttributes starting with (inputStream: " + inputStream 1963 + ", outputStream: " + outputStream + ")"); 1964 } 1965 DataInputStream dataInputStream = new DataInputStream(inputStream); 1966 ByteOrderAwarenessDataOutputStream dataOutputStream = 1967 new ByteOrderAwarenessDataOutputStream(outputStream, ByteOrder.BIG_ENDIAN); 1968 if (dataInputStream.readByte() != MARKER) { 1969 throw new IOException("Invalid marker"); 1970 } 1971 dataOutputStream.writeByte(MARKER); 1972 if (dataInputStream.readByte() != MARKER_SOI) { 1973 throw new IOException("Invalid marker"); 1974 } 1975 dataOutputStream.writeByte(MARKER_SOI); 1976 1977 // Write EXIF APP1 segment 1978 dataOutputStream.writeByte(MARKER); 1979 dataOutputStream.writeByte(MARKER_APP1); 1980 writeExifSegment(dataOutputStream, 6); 1981 1982 byte[] bytes = new byte[4096]; 1983 1984 while (true) { 1985 byte marker = dataInputStream.readByte(); 1986 if (marker != MARKER) { 1987 throw new IOException("Invalid marker"); 1988 } 1989 marker = dataInputStream.readByte(); 1990 switch (marker) { 1991 case MARKER_APP1: { 1992 int length = dataInputStream.readUnsignedShort() - 2; 1993 if (length < 0) { 1994 throw new IOException("Invalid length"); 1995 } 1996 byte[] identifier = new byte[6]; 1997 if (length >= 6) { 1998 if (dataInputStream.read(identifier) != 6) { 1999 throw new IOException("Invalid exif"); 2000 } 2001 if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { 2002 // Skip the original EXIF APP1 segment. 2003 if (dataInputStream.skip(length - 6) != length - 6) { 2004 throw new IOException("Invalid length"); 2005 } 2006 break; 2007 } 2008 } 2009 // Copy non-EXIF APP1 segment. 2010 dataOutputStream.writeByte(MARKER); 2011 dataOutputStream.writeByte(marker); 2012 dataOutputStream.writeUnsignedShort(length + 2); 2013 if (length >= 6) { 2014 length -= 6; 2015 dataOutputStream.write(identifier); 2016 } 2017 int read; 2018 while (length > 0 && (read = dataInputStream.read( 2019 bytes, 0, Math.min(length, bytes.length))) >= 0) { 2020 dataOutputStream.write(bytes, 0, read); 2021 length -= read; 2022 } 2023 break; 2024 } 2025 case MARKER_EOI: 2026 case MARKER_SOS: { 2027 dataOutputStream.writeByte(MARKER); 2028 dataOutputStream.writeByte(marker); 2029 // Copy all the remaining data 2030 Streams.copy(dataInputStream, dataOutputStream); 2031 return; 2032 } 2033 default: { 2034 // Copy JPEG segment 2035 dataOutputStream.writeByte(MARKER); 2036 dataOutputStream.writeByte(marker); 2037 int length = dataInputStream.readUnsignedShort(); 2038 dataOutputStream.writeUnsignedShort(length); 2039 length -= 2; 2040 if (length < 0) { 2041 throw new IOException("Invalid length"); 2042 } 2043 int read; 2044 while (length > 0 && (read = dataInputStream.read( 2045 bytes, 0, Math.min(length, bytes.length))) >= 0) { 2046 dataOutputStream.write(bytes, 0, read); 2047 length -= read; 2048 } 2049 break; 2050 } 2051 } 2052 } 2053 } 2054 2055 // Reads the given EXIF byte area and save its tag data into attributes. 2056 private void readExifSegment(byte[] exifBytes, int exifOffsetFromBeginning) throws IOException { 2057 // Parse TIFF Headers. See JEITA CP-3451C Table 1. page 10. 2058 ByteOrderAwarenessDataInputStream dataInputStream = 2059 new ByteOrderAwarenessDataInputStream(exifBytes); 2060 2061 // Read byte align 2062 short byteOrder = dataInputStream.readShort(); 2063 switch (byteOrder) { 2064 case BYTE_ALIGN_II: 2065 if (DEBUG) { 2066 Log.d(TAG, "readExifSegment: Byte Align II"); 2067 } 2068 mExifByteOrder = ByteOrder.LITTLE_ENDIAN; 2069 break; 2070 case BYTE_ALIGN_MM: 2071 if (DEBUG) { 2072 Log.d(TAG, "readExifSegment: Byte Align MM"); 2073 } 2074 mExifByteOrder = ByteOrder.BIG_ENDIAN; 2075 break; 2076 default: 2077 throw new IOException("Invalid byte order: " + Integer.toHexString(byteOrder)); 2078 } 2079 2080 // Set byte order. 2081 dataInputStream.setByteOrder(mExifByteOrder); 2082 2083 int startCode = dataInputStream.readUnsignedShort(); 2084 if (startCode != 0x2a) { 2085 throw new IOException("Invalid exif start: " + Integer.toHexString(startCode)); 2086 } 2087 2088 // Read first ifd offset 2089 long firstIfdOffset = dataInputStream.readUnsignedInt(); 2090 if (firstIfdOffset < 8 || firstIfdOffset >= exifBytes.length) { 2091 throw new IOException("Invalid first Ifd offset: " + firstIfdOffset); 2092 } 2093 firstIfdOffset -= 8; 2094 if (firstIfdOffset > 0) { 2095 if (dataInputStream.skip(firstIfdOffset) != firstIfdOffset) { 2096 throw new IOException("Couldn't jump to first Ifd: " + firstIfdOffset); 2097 } 2098 } 2099 2100 // Read primary image TIFF image file directory. 2101 readImageFileDirectory(dataInputStream, IFD_TIFF_HINT); 2102 2103 // Process thumbnail. 2104 String jpegInterchangeFormatString = getAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name); 2105 String jpegInterchangeFormatLengthString = 2106 getAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name); 2107 if (jpegInterchangeFormatString != null && jpegInterchangeFormatLengthString != null) { 2108 try { 2109 int jpegInterchangeFormat = Integer.parseInt(jpegInterchangeFormatString); 2110 int jpegInterchangeFormatLength = Integer 2111 .parseInt(jpegInterchangeFormatLengthString); 2112 // The following code limits the size of thumbnail size not to overflow EXIF data area. 2113 jpegInterchangeFormatLength = Math.min(jpegInterchangeFormat 2114 + jpegInterchangeFormatLength, exifBytes.length) - jpegInterchangeFormat; 2115 if (jpegInterchangeFormat > 0 && jpegInterchangeFormatLength > 0) { 2116 mHasThumbnail = true; 2117 mThumbnailOffset = exifOffsetFromBeginning + jpegInterchangeFormat; 2118 mThumbnailLength = jpegInterchangeFormatLength; 2119 2120 if (mFilename == null && mAssetInputStream == null 2121 && mSeekableFileDescriptor == null) { 2122 // Save the thumbnail in memory if the input doesn't support reading again. 2123 byte[] thumbnailBytes = new byte[jpegInterchangeFormatLength]; 2124 dataInputStream.seek(jpegInterchangeFormat); 2125 dataInputStream.readFully(thumbnailBytes); 2126 mThumbnailBytes = thumbnailBytes; 2127 2128 if (DEBUG) { 2129 Bitmap bitmap = BitmapFactory.decodeByteArray( 2130 thumbnailBytes, 0, thumbnailBytes.length); 2131 Log.d(TAG, "Thumbnail offset: " + mThumbnailOffset + ", length: " 2132 + mThumbnailLength + ", width: " + bitmap.getWidth() 2133 + ", height: " 2134 + bitmap.getHeight()); 2135 } 2136 } 2137 } 2138 } catch (NumberFormatException e) { 2139 // Ignored the corrupted image. 2140 } 2141 } 2142 } 2143 2144 private void addDefaultValuesForCompatibility() { 2145 // The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag. 2146 String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL); 2147 if (valueOfDateTimeOriginal != null) { 2148 mAttributes[IFD_TIFF_HINT].put(TAG_DATETIME, 2149 ExifAttribute.createString(valueOfDateTimeOriginal)); 2150 } 2151 2152 // Add the default value. 2153 if (getAttribute(TAG_IMAGE_WIDTH) == null) { 2154 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, 2155 ExifAttribute.createULong(0, mExifByteOrder)); 2156 } 2157 if (getAttribute(TAG_IMAGE_LENGTH) == null) { 2158 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, 2159 ExifAttribute.createULong(0, mExifByteOrder)); 2160 } 2161 if (getAttribute(TAG_ORIENTATION) == null) { 2162 mAttributes[IFD_TIFF_HINT].put(TAG_ORIENTATION, 2163 ExifAttribute.createULong(0, mExifByteOrder)); 2164 } 2165 if (getAttribute(TAG_LIGHT_SOURCE) == null) { 2166 mAttributes[IFD_EXIF_HINT].put(TAG_LIGHT_SOURCE, 2167 ExifAttribute.createULong(0, mExifByteOrder)); 2168 } 2169 } 2170 2171 // Reads image file directory, which is a tag group in EXIF. 2172 private void readImageFileDirectory(ByteOrderAwarenessDataInputStream dataInputStream, int hint) 2173 throws IOException { 2174 if (dataInputStream.peek() + 2 > dataInputStream.mLength) { 2175 // Return if there is no data from the offset. 2176 return; 2177 } 2178 // See JEITA CP-3451 Figure 5. page 9. 2179 short numberOfDirectoryEntry = dataInputStream.readShort(); 2180 if (dataInputStream.peek() + 12 * numberOfDirectoryEntry > dataInputStream.mLength) { 2181 // Return if the size of entries is too big. 2182 return; 2183 } 2184 2185 if (DEBUG) { 2186 Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry); 2187 } 2188 2189 for (short i = 0; i < numberOfDirectoryEntry; ++i) { 2190 int tagNumber = dataInputStream.readUnsignedShort(); 2191 int dataFormat = dataInputStream.readUnsignedShort(); 2192 int numberOfComponents = dataInputStream.readInt(); 2193 long nextEntryOffset = dataInputStream.peek() + 4; // next four bytes is for data 2194 // offset or value. 2195 // Look up a corresponding tag from tag number 2196 final ExifTag tag = (ExifTag) sExifTagMapsForReading[hint].get(tagNumber); 2197 2198 if (DEBUG) { 2199 Log.d(TAG, String.format("hint: %d, tagNumber: %d, tagName: %s, dataFormat: %d, " + 2200 "numberOfComponents: %d", hint, tagNumber, tag != null ? tag.name : null, 2201 dataFormat, numberOfComponents)); 2202 } 2203 2204 if (tag == null || dataFormat <= 0 || 2205 dataFormat >= IFD_FORMAT_BYTES_PER_FORMAT.length) { 2206 // Skip if the parsed tag number is not defined or invalid data format. 2207 if (tag == null) { 2208 Log.w(TAG, "Skip the tag entry since tag number is not defined: " + tagNumber); 2209 } else { 2210 Log.w(TAG, "Skip the tag entry since data format is invalid: " + dataFormat); 2211 } 2212 dataInputStream.seek(nextEntryOffset); 2213 continue; 2214 } 2215 2216 // Read a value from data field or seek to the value offset which is stored in data 2217 // field if the size of the entry value is bigger than 4. 2218 int byteCount = numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat]; 2219 if (byteCount > 4) { 2220 long offset = dataInputStream.readUnsignedInt(); 2221 if (DEBUG) { 2222 Log.d(TAG, "seek to data offset: " + offset); 2223 } 2224 if (offset + byteCount <= dataInputStream.mLength) { 2225 dataInputStream.seek(offset); 2226 } else { 2227 // Skip if invalid data offset. 2228 Log.w(TAG, "Skip the tag entry since data offset is invalid: " + offset); 2229 dataInputStream.seek(nextEntryOffset); 2230 continue; 2231 } 2232 } 2233 2234 // Recursively parse IFD when a IFD pointer tag appears. 2235 int innerIfdHint = getIfdHintFromTagNumber(tagNumber); 2236 if (DEBUG) { 2237 Log.d(TAG, "innerIfdHint: " + innerIfdHint + " byteCount: " + byteCount); 2238 } 2239 2240 if (innerIfdHint >= 0) { 2241 long offset = -1L; 2242 // Get offset from data field 2243 switch (dataFormat) { 2244 case IFD_FORMAT_USHORT: { 2245 offset = dataInputStream.readUnsignedShort(); 2246 break; 2247 } 2248 case IFD_FORMAT_SSHORT: { 2249 offset = dataInputStream.readShort(); 2250 break; 2251 } 2252 case IFD_FORMAT_ULONG: { 2253 offset = dataInputStream.readUnsignedInt(); 2254 break; 2255 } 2256 case IFD_FORMAT_SLONG: { 2257 offset = dataInputStream.readInt(); 2258 break; 2259 } 2260 default: { 2261 // Nothing to do 2262 break; 2263 } 2264 } 2265 if (DEBUG) { 2266 Log.d(TAG, String.format("Offset: %d, tagName: %s", offset, tag.name)); 2267 } 2268 if (offset > 0L && offset < dataInputStream.mLength) { 2269 dataInputStream.seek(offset); 2270 readImageFileDirectory(dataInputStream, innerIfdHint); 2271 } else { 2272 Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset); 2273 } 2274 2275 dataInputStream.seek(nextEntryOffset); 2276 continue; 2277 } 2278 2279 byte[] bytes = new byte[numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat]]; 2280 dataInputStream.readFully(bytes); 2281 mAttributes[hint].put( 2282 tag.name, new ExifAttribute(dataFormat, numberOfComponents, bytes)); 2283 if (dataInputStream.peek() != nextEntryOffset) { 2284 dataInputStream.seek(nextEntryOffset); 2285 } 2286 } 2287 2288 if (dataInputStream.peek() + 4 <= dataInputStream.mLength) { 2289 long nextIfdOffset = dataInputStream.readUnsignedInt(); 2290 if (DEBUG) { 2291 Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset)); 2292 } 2293 // The next IFD offset needs to be bigger than 8 2294 // since the first IFD offset is at least 8. 2295 if (nextIfdOffset > 8 && nextIfdOffset < dataInputStream.mLength) { 2296 dataInputStream.seek(nextIfdOffset); 2297 readImageFileDirectory(dataInputStream, IFD_THUMBNAIL_HINT); 2298 } 2299 } 2300 } 2301 2302 // Gets the corresponding IFD group index of the given tag number for writing Exif Tags. 2303 private static int getIfdHintFromTagNumber(int tagNumber) { 2304 for (int i = 0; i < IFD_POINTER_TAG_HINTS.length; ++i) { 2305 if (IFD_POINTER_TAGS[i].number == tagNumber) { 2306 return IFD_POINTER_TAG_HINTS[i]; 2307 } 2308 } 2309 return -1; 2310 } 2311 2312 // Writes an Exif segment into the given output stream. 2313 private int writeExifSegment(ByteOrderAwarenessDataOutputStream dataOutputStream, 2314 int exifOffsetFromBeginning) throws IOException { 2315 // The following variables are for calculating each IFD tag group size in bytes. 2316 int[] ifdOffsets = new int[EXIF_TAGS.length]; 2317 int[] ifdDataSizes = new int[EXIF_TAGS.length]; 2318 2319 // Remove IFD pointer tags (we'll re-add it later.) 2320 for (ExifTag tag : IFD_POINTER_TAGS) { 2321 removeAttribute(tag.name); 2322 } 2323 // Remove old thumbnail data 2324 removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name); 2325 removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name); 2326 2327 // Remove null value tags. 2328 for (int hint = 0; hint < EXIF_TAGS.length; ++hint) { 2329 for (Object obj : mAttributes[hint].entrySet().toArray()) { 2330 final Map.Entry entry = (Map.Entry) obj; 2331 if (entry.getValue() == null) { 2332 mAttributes[hint].remove(entry.getKey()); 2333 } 2334 } 2335 } 2336 2337 // Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD 2338 // offset when there is one or more tags in the thumbnail IFD. 2339 if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) { 2340 mAttributes[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].name, 2341 ExifAttribute.createULong(0, mExifByteOrder)); 2342 } 2343 if (!mAttributes[IFD_EXIF_HINT].isEmpty()) { 2344 mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].name, 2345 ExifAttribute.createULong(0, mExifByteOrder)); 2346 } 2347 if (!mAttributes[IFD_GPS_HINT].isEmpty()) { 2348 mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name, 2349 ExifAttribute.createULong(0, mExifByteOrder)); 2350 } 2351 if (mHasThumbnail) { 2352 mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name, 2353 ExifAttribute.createULong(0, mExifByteOrder)); 2354 mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name, 2355 ExifAttribute.createULong(mThumbnailLength, mExifByteOrder)); 2356 } 2357 2358 // Calculate IFD group data area sizes. IFD group data area is assigned to save the entry 2359 // value which has a bigger size than 4 bytes. 2360 for (int i = 0; i < EXIF_TAGS.length; ++i) { 2361 int sum = 0; 2362 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) { 2363 final ExifAttribute exifAttribute = (ExifAttribute) entry.getValue(); 2364 final int size = exifAttribute.size(); 2365 if (size > 4) { 2366 sum += size; 2367 } 2368 } 2369 ifdDataSizes[i] += sum; 2370 } 2371 2372 // Calculate IFD offsets. 2373 int position = 8; 2374 for (int hint = 0; hint < EXIF_TAGS.length; ++hint) { 2375 if (!mAttributes[hint].isEmpty()) { 2376 ifdOffsets[hint] = position; 2377 position += 2 + mAttributes[hint].size() * 12 + 4 + ifdDataSizes[hint]; 2378 } 2379 } 2380 if (mHasThumbnail) { 2381 int thumbnailOffset = position; 2382 mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name, 2383 ExifAttribute.createULong(thumbnailOffset, mExifByteOrder)); 2384 mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset; 2385 position += mThumbnailLength; 2386 } 2387 2388 // Calculate the total size 2389 int totalSize = position + 8; // eight bytes is for header part. 2390 if (DEBUG) { 2391 Log.d(TAG, "totalSize length: " + totalSize); 2392 for (int i = 0; i < EXIF_TAGS.length; ++i) { 2393 Log.d(TAG, String.format("index: %d, offsets: %d, tag count: %d, data sizes: %d", 2394 i, ifdOffsets[i], mAttributes[i].size(), ifdDataSizes[i])); 2395 } 2396 } 2397 2398 // Update IFD pointer tags with the calculated offsets. 2399 if (!mAttributes[IFD_EXIF_HINT].isEmpty()) { 2400 mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].name, 2401 ExifAttribute.createULong(ifdOffsets[IFD_EXIF_HINT], mExifByteOrder)); 2402 } 2403 if (!mAttributes[IFD_GPS_HINT].isEmpty()) { 2404 mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name, 2405 ExifAttribute.createULong(ifdOffsets[IFD_GPS_HINT], mExifByteOrder)); 2406 } 2407 if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) { 2408 mAttributes[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].name, ExifAttribute.createULong( 2409 ifdOffsets[IFD_INTEROPERABILITY_HINT], mExifByteOrder)); 2410 } 2411 2412 // Write TIFF Headers. See JEITA CP-3451C Table 1. page 10. 2413 dataOutputStream.writeUnsignedShort(totalSize); 2414 dataOutputStream.write(IDENTIFIER_EXIF_APP1); 2415 dataOutputStream.writeShort(mExifByteOrder == ByteOrder.BIG_ENDIAN 2416 ? BYTE_ALIGN_MM : BYTE_ALIGN_II); 2417 dataOutputStream.setByteOrder(mExifByteOrder); 2418 dataOutputStream.writeUnsignedShort(0x2a); 2419 dataOutputStream.writeUnsignedInt(8); 2420 2421 // Write IFD groups. See JEITA CP-3451C Figure 7. page 12. 2422 for (int hint = 0; hint < EXIF_TAGS.length; ++hint) { 2423 if (!mAttributes[hint].isEmpty()) { 2424 // See JEITA CP-3451C 4.6.2 IFD structure. page 13. 2425 // Write entry count 2426 dataOutputStream.writeUnsignedShort(mAttributes[hint].size()); 2427 2428 // Write entry info 2429 int dataOffset = ifdOffsets[hint] + 2 + mAttributes[hint].size() * 12 + 4; 2430 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) { 2431 // Convert tag name to tag number. 2432 final ExifTag tag = (ExifTag) sExifTagMapsForWriting[hint].get(entry.getKey()); 2433 final int tagNumber = tag.number; 2434 final ExifAttribute attribute = (ExifAttribute) entry.getValue(); 2435 final int size = attribute.size(); 2436 2437 dataOutputStream.writeUnsignedShort(tagNumber); 2438 dataOutputStream.writeUnsignedShort(attribute.format); 2439 dataOutputStream.writeInt(attribute.numberOfComponents); 2440 if (size > 4) { 2441 dataOutputStream.writeUnsignedInt(dataOffset); 2442 dataOffset += size; 2443 } else { 2444 dataOutputStream.write(attribute.bytes); 2445 // Fill zero up to 4 bytes 2446 if (size < 4) { 2447 for (int i = size; i < 4; ++i) { 2448 dataOutputStream.writeByte(0); 2449 } 2450 } 2451 } 2452 } 2453 2454 // Write the next offset. It writes the offset of thumbnail IFD if there is one or 2455 // more tags in the thumbnail IFD when the current IFD is the primary image TIFF 2456 // IFD; Otherwise 0. 2457 if (hint == 0 && !mAttributes[IFD_THUMBNAIL_HINT].isEmpty()) { 2458 dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_THUMBNAIL_HINT]); 2459 } else { 2460 dataOutputStream.writeUnsignedInt(0); 2461 } 2462 2463 // Write values of data field exceeding 4 bytes after the next offset. 2464 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) { 2465 ExifAttribute attribute = (ExifAttribute) entry.getValue(); 2466 2467 if (attribute.bytes.length > 4) { 2468 dataOutputStream.write(attribute.bytes, 0, attribute.bytes.length); 2469 } 2470 } 2471 } 2472 } 2473 2474 // Write thumbnail 2475 if (mHasThumbnail) { 2476 dataOutputStream.write(getThumbnail()); 2477 } 2478 2479 // Reset the byte order to big endian in order to write remaining parts of the JPEG file. 2480 dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN); 2481 2482 return totalSize; 2483 } 2484 2485 /** 2486 * Determines the data format of EXIF entry value. 2487 * 2488 * @param entryValue The value to be determined. 2489 * @return Returns two data formats gussed as a pair in integer. If there is no two candidate 2490 data formats for the given entry value, returns {@code -1} in the second of the pair. 2491 */ 2492 private static Pair<Integer, Integer> guessDataFormat(String entryValue) { 2493 // See TIFF 6.0 spec Types. page 15. 2494 // Take the first component if there are more than one component. 2495 if (entryValue.contains(",")) { 2496 String[] entryValues = entryValue.split(","); 2497 Pair<Integer, Integer> dataFormat = guessDataFormat(entryValues[0]); 2498 if (dataFormat.first == IFD_FORMAT_STRING) { 2499 return dataFormat; 2500 } 2501 for (int i = 1; i < entryValues.length; ++i) { 2502 final Pair<Integer, Integer> guessDataFormat = guessDataFormat(entryValues[i]); 2503 int first = -1, second = -1; 2504 if (guessDataFormat.first == dataFormat.first 2505 || guessDataFormat.second == dataFormat.first) { 2506 first = dataFormat.first; 2507 } 2508 if (dataFormat.second != -1 && (guessDataFormat.first == dataFormat.second 2509 || guessDataFormat.second == dataFormat.second)) { 2510 second = dataFormat.second; 2511 } 2512 if (first == -1 && second == -1) { 2513 return new Pair<>(IFD_FORMAT_STRING, -1); 2514 } 2515 if (first == -1) { 2516 dataFormat = new Pair<>(second, -1); 2517 continue; 2518 } 2519 if (second == -1) { 2520 dataFormat = new Pair<>(first, -1); 2521 continue; 2522 } 2523 } 2524 return dataFormat; 2525 } 2526 2527 if (entryValue.contains("/")) { 2528 String[] rationalNumber = entryValue.split("/"); 2529 if (rationalNumber.length == 2) { 2530 try { 2531 long numerator = Long.parseLong(rationalNumber[0]); 2532 long denominator = Long.parseLong(rationalNumber[1]); 2533 if (numerator < 0L || denominator < 0L) { 2534 return new Pair<>(IFD_FORMAT_SRATIONAL, - 1); 2535 } 2536 if (numerator > Integer.MAX_VALUE || denominator > Integer.MAX_VALUE) { 2537 return new Pair<>(IFD_FORMAT_URATIONAL, -1); 2538 } 2539 return new Pair<>(IFD_FORMAT_SRATIONAL, IFD_FORMAT_URATIONAL); 2540 } catch (NumberFormatException e) { 2541 // Ignored 2542 } 2543 } 2544 return new Pair<>(IFD_FORMAT_STRING, -1); 2545 } 2546 try { 2547 Long longValue = Long.parseLong(entryValue); 2548 if (longValue >= 0 && longValue <= 65535) { 2549 return new Pair<>(IFD_FORMAT_USHORT, IFD_FORMAT_ULONG); 2550 } 2551 if (longValue < 0) { 2552 return new Pair<>(IFD_FORMAT_SLONG, -1); 2553 } 2554 return new Pair<>(IFD_FORMAT_ULONG, -1); 2555 } catch (NumberFormatException e) { 2556 // Ignored 2557 } 2558 try { 2559 Double.parseDouble(entryValue); 2560 return new Pair<>(IFD_FORMAT_DOUBLE, -1); 2561 } catch (NumberFormatException e) { 2562 // Ignored 2563 } 2564 return new Pair<>(IFD_FORMAT_STRING, -1); 2565 } 2566 2567 // An input stream to parse EXIF data area, which can be written in either little or big endian 2568 // order. 2569 private static class ByteOrderAwarenessDataInputStream extends ByteArrayInputStream { 2570 private static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN; 2571 private static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN; 2572 2573 private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN; 2574 private final long mLength; 2575 private long mPosition; 2576 2577 public ByteOrderAwarenessDataInputStream(byte[] bytes) { 2578 super(bytes); 2579 mLength = bytes.length; 2580 mPosition = 0L; 2581 } 2582 2583 public void setByteOrder(ByteOrder byteOrder) { 2584 mByteOrder = byteOrder; 2585 } 2586 2587 public void seek(long byteCount) throws IOException { 2588 mPosition = 0L; 2589 reset(); 2590 if (skip(byteCount) != byteCount) { 2591 throw new IOException("Couldn't seek up to the byteCount"); 2592 } 2593 } 2594 2595 public long peek() { 2596 return mPosition; 2597 } 2598 2599 public void readFully(byte[] buffer) throws IOException { 2600 mPosition += buffer.length; 2601 if (mPosition > mLength) { 2602 throw new EOFException(); 2603 } 2604 if (super.read(buffer, 0, buffer.length) != buffer.length) { 2605 throw new IOException("Couldn't read up to the length of buffer"); 2606 } 2607 } 2608 2609 public byte readByte() throws IOException { 2610 ++mPosition; 2611 if (mPosition > mLength) { 2612 throw new EOFException(); 2613 } 2614 int ch = super.read(); 2615 if (ch < 0) { 2616 throw new EOFException(); 2617 } 2618 return (byte) ch; 2619 } 2620 2621 public short readShort() throws IOException { 2622 mPosition += 2; 2623 if (mPosition > mLength) { 2624 throw new EOFException(); 2625 } 2626 int ch1 = super.read(); 2627 int ch2 = super.read(); 2628 if ((ch1 | ch2) < 0) { 2629 throw new EOFException(); 2630 } 2631 if (mByteOrder == LITTLE_ENDIAN) { 2632 return (short) ((ch2 << 8) + (ch1)); 2633 } else if (mByteOrder == BIG_ENDIAN) { 2634 return (short) ((ch1 << 8) + (ch2)); 2635 } 2636 throw new IOException("Invalid byte order: " + mByteOrder); 2637 } 2638 2639 public int readInt() throws IOException { 2640 mPosition += 4; 2641 if (mPosition > mLength) { 2642 throw new EOFException(); 2643 } 2644 int ch1 = super.read(); 2645 int ch2 = super.read(); 2646 int ch3 = super.read(); 2647 int ch4 = super.read(); 2648 if ((ch1 | ch2 | ch3 | ch4) < 0) { 2649 throw new EOFException(); 2650 } 2651 if (mByteOrder == LITTLE_ENDIAN) { 2652 return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1); 2653 } else if (mByteOrder == BIG_ENDIAN) { 2654 return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4); 2655 } 2656 throw new IOException("Invalid byte order: " + mByteOrder); 2657 } 2658 2659 @Override 2660 public long skip(long byteCount) { 2661 long skipped = super.skip(Math.min(byteCount, mLength - mPosition)); 2662 mPosition += skipped; 2663 return skipped; 2664 } 2665 2666 public int readUnsignedShort() throws IOException { 2667 mPosition += 2; 2668 if (mPosition > mLength) { 2669 throw new EOFException(); 2670 } 2671 int ch1 = super.read(); 2672 int ch2 = super.read(); 2673 if ((ch1 | ch2) < 0) { 2674 throw new EOFException(); 2675 } 2676 if (mByteOrder == LITTLE_ENDIAN) { 2677 return ((ch2 << 8) + (ch1)); 2678 } else if (mByteOrder == BIG_ENDIAN) { 2679 return ((ch1 << 8) + (ch2)); 2680 } 2681 throw new IOException("Invalid byte order: " + mByteOrder); 2682 } 2683 2684 public long readUnsignedInt() throws IOException { 2685 return readInt() & 0xffffffffL; 2686 } 2687 2688 public long readLong() throws IOException { 2689 mPosition += 8; 2690 if (mPosition > mLength) { 2691 throw new EOFException(); 2692 } 2693 int ch1 = super.read(); 2694 int ch2 = super.read(); 2695 int ch3 = super.read(); 2696 int ch4 = super.read(); 2697 int ch5 = super.read(); 2698 int ch6 = super.read(); 2699 int ch7 = super.read(); 2700 int ch8 = super.read(); 2701 if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) { 2702 throw new EOFException(); 2703 } 2704 if (mByteOrder == LITTLE_ENDIAN) { 2705 return (((long) ch8 << 56) + ((long) ch7 << 48) + ((long) ch6 << 40) 2706 + ((long) ch5 << 32) + ((long) ch4 << 24) + ((long) ch3 << 16) 2707 + ((long) ch2 << 8) + (long) ch1); 2708 } else if (mByteOrder == BIG_ENDIAN) { 2709 return (((long) ch1 << 56) + ((long) ch2 << 48) + ((long) ch3 << 40) 2710 + ((long) ch4 << 32) + ((long) ch5 << 24) + ((long) ch6 << 16) 2711 + ((long) ch7 << 8) + (long) ch8); 2712 } 2713 throw new IOException("Invalid byte order: " + mByteOrder); 2714 } 2715 2716 public float readFloat() throws IOException { 2717 return Float.intBitsToFloat(readInt()); 2718 } 2719 2720 public double readDouble() throws IOException { 2721 return Double.longBitsToDouble(readLong()); 2722 } 2723 } 2724 2725 // An output stream to write EXIF data area, which can be written in either little or big endian 2726 // order. 2727 private static class ByteOrderAwarenessDataOutputStream extends FilterOutputStream { 2728 private final OutputStream mOutputStream; 2729 private ByteOrder mByteOrder; 2730 2731 public ByteOrderAwarenessDataOutputStream(OutputStream out, ByteOrder byteOrder) { 2732 super(out); 2733 mOutputStream = out; 2734 mByteOrder = byteOrder; 2735 } 2736 2737 public void setByteOrder(ByteOrder byteOrder) { 2738 mByteOrder = byteOrder; 2739 } 2740 2741 public void write(byte[] bytes) throws IOException { 2742 mOutputStream.write(bytes); 2743 } 2744 2745 public void write(byte[] bytes, int offset, int length) throws IOException { 2746 mOutputStream.write(bytes, offset, length); 2747 } 2748 2749 public void writeByte(int val) throws IOException { 2750 mOutputStream.write(val); 2751 } 2752 2753 public void writeShort(short val) throws IOException { 2754 if (mByteOrder == ByteOrder.LITTLE_ENDIAN) { 2755 mOutputStream.write((val >>> 0) & 0xFF); 2756 mOutputStream.write((val >>> 8) & 0xFF); 2757 } else if (mByteOrder == ByteOrder.BIG_ENDIAN) { 2758 mOutputStream.write((val >>> 8) & 0xFF); 2759 mOutputStream.write((val >>> 0) & 0xFF); 2760 } 2761 } 2762 2763 public void writeInt(int val) throws IOException { 2764 if (mByteOrder == ByteOrder.LITTLE_ENDIAN) { 2765 mOutputStream.write((val >>> 0) & 0xFF); 2766 mOutputStream.write((val >>> 8) & 0xFF); 2767 mOutputStream.write((val >>> 16) & 0xFF); 2768 mOutputStream.write((val >>> 24) & 0xFF); 2769 } else if (mByteOrder == ByteOrder.BIG_ENDIAN) { 2770 mOutputStream.write((val >>> 24) & 0xFF); 2771 mOutputStream.write((val >>> 16) & 0xFF); 2772 mOutputStream.write((val >>> 8) & 0xFF); 2773 mOutputStream.write((val >>> 0) & 0xFF); 2774 } 2775 } 2776 2777 public void writeUnsignedShort(int val) throws IOException { 2778 writeShort((short) val); 2779 } 2780 2781 public void writeUnsignedInt(long val) throws IOException { 2782 writeInt((int) val); 2783 } 2784 } 2785 2786 // JNI methods for RAW formats. 2787 private static native void nativeInitRaw(); 2788 private static native byte[] nativeGetThumbnailFromAsset( 2789 long asset, int thumbnailOffset, int thumbnailLength); 2790 private static native HashMap nativeGetRawAttributesFromAsset(long asset); 2791 private static native HashMap nativeGetRawAttributesFromFileDescriptor(FileDescriptor fd); 2792 private static native HashMap nativeGetRawAttributesFromInputStream(InputStream in); 2793} 2794