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