1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.messaging.util.exif; 18 19 import android.util.Log; 20 import com.android.messaging.util.LogUtil; 21 22 import java.io.IOException; 23 import java.io.InputStream; 24 import java.nio.ByteOrder; 25 import java.nio.charset.Charset; 26 import java.util.Map.Entry; 27 import java.util.TreeMap; 28 29 /** 30 * This class provides a low-level EXIF parsing API. Given a JPEG format 31 * InputStream, the caller can request which IFD's to read via 32 * {@link #parse(java.io.InputStream, int)} with given options. 33 * <p> 34 * Below is an example of getting EXIF data from IFD 0 and EXIF IFD using the 35 * parser. 36 * 37 * <pre> 38 * void parse() { 39 * ExifParser parser = ExifParser.parse(mImageInputStream, 40 * ExifParser.OPTION_IFD_0 | ExifParser.OPTIONS_IFD_EXIF); 41 * int event = parser.next(); 42 * while (event != ExifParser.EVENT_END) { 43 * switch (event) { 44 * case ExifParser.EVENT_START_OF_IFD: 45 * break; 46 * case ExifParser.EVENT_NEW_TAG: 47 * ExifTag tag = parser.getTag(); 48 * if (!tag.hasValue()) { 49 * parser.registerForTagValue(tag); 50 * } else { 51 * processTag(tag); 52 * } 53 * break; 54 * case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG: 55 * tag = parser.getTag(); 56 * if (tag.getDataType() != ExifTag.TYPE_UNDEFINED) { 57 * processTag(tag); 58 * } 59 * break; 60 * } 61 * event = parser.next(); 62 * } 63 * } 64 * 65 * void processTag(ExifTag tag) { 66 * // process the tag as you like. 67 * } 68 * </pre> 69 */ 70 public class ExifParser { 71 private static final boolean LOGV = false; 72 private static final String TAG = LogUtil.BUGLE_TAG; 73 /** 74 * When the parser reaches a new IFD area. Call {@link #getCurrentIfd()} to 75 * know which IFD we are in. 76 */ 77 public static final int EVENT_START_OF_IFD = 0; 78 /** 79 * When the parser reaches a new tag. Call {@link #getTag()}to get the 80 * corresponding tag. 81 */ 82 public static final int EVENT_NEW_TAG = 1; 83 /** 84 * When the parser reaches the value area of tag that is registered by 85 * {@link #registerForTagValue(ExifTag)} previously. Call {@link #getTag()} 86 * to get the corresponding tag. 87 */ 88 public static final int EVENT_VALUE_OF_REGISTERED_TAG = 2; 89 90 /** 91 * When the parser reaches the compressed image area. 92 */ 93 public static final int EVENT_COMPRESSED_IMAGE = 3; 94 /** 95 * When the parser reaches the uncompressed image strip. Call 96 * {@link #getStripIndex()} to get the index of the strip. 97 * 98 * @see #getStripIndex() 99 * @see #getStripCount() 100 */ 101 public static final int EVENT_UNCOMPRESSED_STRIP = 4; 102 /** 103 * When there is nothing more to parse. 104 */ 105 public static final int EVENT_END = 5; 106 107 /** 108 * Option bit to request to parse IFD0. 109 */ 110 public static final int OPTION_IFD_0 = 1 << 0; 111 /** 112 * Option bit to request to parse IFD1. 113 */ 114 public static final int OPTION_IFD_1 = 1 << 1; 115 /** 116 * Option bit to request to parse Exif-IFD. 117 */ 118 public static final int OPTION_IFD_EXIF = 1 << 2; 119 /** 120 * Option bit to request to parse GPS-IFD. 121 */ 122 public static final int OPTION_IFD_GPS = 1 << 3; 123 /** 124 * Option bit to request to parse Interoperability-IFD. 125 */ 126 public static final int OPTION_IFD_INTEROPERABILITY = 1 << 4; 127 /** 128 * Option bit to request to parse thumbnail. 129 */ 130 public static final int OPTION_THUMBNAIL = 1 << 5; 131 132 protected static final int EXIF_HEADER = 0x45786966; // EXIF header "Exif" 133 protected static final short EXIF_HEADER_TAIL = (short) 0x0000; // EXIF header in APP1 134 135 // TIFF header 136 protected static final short LITTLE_ENDIAN_TAG = (short) 0x4949; // "II" 137 protected static final short BIG_ENDIAN_TAG = (short) 0x4d4d; // "MM" 138 protected static final short TIFF_HEADER_TAIL = 0x002A; 139 140 protected static final int TAG_SIZE = 12; 141 protected static final int OFFSET_SIZE = 2; 142 143 private static final Charset US_ASCII = Charset.forName("US-ASCII"); 144 145 protected static final int DEFAULT_IFD0_OFFSET = 8; 146 147 private final CountedDataInputStream mTiffStream; 148 private final int mOptions; 149 private int mIfdStartOffset = 0; 150 private int mNumOfTagInIfd = 0; 151 private int mIfdType; 152 private ExifTag mTag; 153 private ImageEvent mImageEvent; 154 private int mStripCount; 155 private ExifTag mStripSizeTag; 156 private ExifTag mJpegSizeTag; 157 private boolean mNeedToParseOffsetsInCurrentIfd; 158 private boolean mContainExifData = false; 159 private int mApp1End; 160 private int mOffsetToApp1EndFromSOF = 0; 161 private byte[] mDataAboveIfd0; 162 private int mIfd0Position; 163 private int mTiffStartPosition; 164 private final ExifInterface mInterface; 165 166 private static final short TAG_EXIF_IFD = ExifInterface 167 .getTrueTagKey(ExifInterface.TAG_EXIF_IFD); 168 private static final short TAG_GPS_IFD = ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD); 169 private static final short TAG_INTEROPERABILITY_IFD = ExifInterface 170 .getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD); 171 private static final short TAG_JPEG_INTERCHANGE_FORMAT = ExifInterface 172 .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT); 173 private static final short TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = ExifInterface 174 .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 175 private static final short TAG_STRIP_OFFSETS = ExifInterface 176 .getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS); 177 private static final short TAG_STRIP_BYTE_COUNTS = ExifInterface 178 .getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS); 179 180 private final TreeMap<Integer, Object> mCorrespondingEvent = new TreeMap<Integer, Object>(); 181 isIfdRequested(int ifdType)182 private boolean isIfdRequested(int ifdType) { 183 switch (ifdType) { 184 case IfdId.TYPE_IFD_0: 185 return (mOptions & OPTION_IFD_0) != 0; 186 case IfdId.TYPE_IFD_1: 187 return (mOptions & OPTION_IFD_1) != 0; 188 case IfdId.TYPE_IFD_EXIF: 189 return (mOptions & OPTION_IFD_EXIF) != 0; 190 case IfdId.TYPE_IFD_GPS: 191 return (mOptions & OPTION_IFD_GPS) != 0; 192 case IfdId.TYPE_IFD_INTEROPERABILITY: 193 return (mOptions & OPTION_IFD_INTEROPERABILITY) != 0; 194 } 195 return false; 196 } 197 isThumbnailRequested()198 private boolean isThumbnailRequested() { 199 return (mOptions & OPTION_THUMBNAIL) != 0; 200 } 201 ExifParser(InputStream inputStream, int options, ExifInterface iRef)202 private ExifParser(InputStream inputStream, int options, ExifInterface iRef) 203 throws IOException, ExifInvalidFormatException { 204 if (inputStream == null) { 205 throw new IOException("Null argument inputStream to ExifParser"); 206 } 207 if (LOGV) { 208 Log.v(TAG, "Reading exif..."); 209 } 210 mInterface = iRef; 211 mContainExifData = seekTiffData(inputStream); 212 mTiffStream = new CountedDataInputStream(inputStream); 213 mOptions = options; 214 if (!mContainExifData) { 215 return; 216 } 217 218 parseTiffHeader(); 219 long offset = mTiffStream.readUnsignedInt(); 220 if (offset > Integer.MAX_VALUE) { 221 throw new ExifInvalidFormatException("Invalid offset " + offset); 222 } 223 mIfd0Position = (int) offset; 224 mIfdType = IfdId.TYPE_IFD_0; 225 if (isIfdRequested(IfdId.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) { 226 registerIfd(IfdId.TYPE_IFD_0, offset); 227 if (mIfd0Position > DEFAULT_IFD0_OFFSET) { 228 mDataAboveIfd0 = new byte[mIfd0Position - DEFAULT_IFD0_OFFSET]; 229 read(mDataAboveIfd0); 230 } 231 } 232 } 233 234 /** 235 * Parses the the given InputStream with the given options 236 * 237 * @exception java.io.IOException 238 * @exception ExifInvalidFormatException 239 */ parse(InputStream inputStream, int options, ExifInterface iRef)240 protected static ExifParser parse(InputStream inputStream, int options, ExifInterface iRef) 241 throws IOException, ExifInvalidFormatException { 242 return new ExifParser(inputStream, options, iRef); 243 } 244 245 /** 246 * Parses the the given InputStream with default options; that is, every IFD 247 * and thumbnaill will be parsed. 248 * 249 * @exception java.io.IOException 250 * @exception ExifInvalidFormatException 251 * @see #parse(java.io.InputStream, int) 252 */ parse(InputStream inputStream, ExifInterface iRef)253 protected static ExifParser parse(InputStream inputStream, ExifInterface iRef) 254 throws IOException, ExifInvalidFormatException { 255 return new ExifParser(inputStream, OPTION_IFD_0 | OPTION_IFD_1 256 | OPTION_IFD_EXIF | OPTION_IFD_GPS | OPTION_IFD_INTEROPERABILITY 257 | OPTION_THUMBNAIL, iRef); 258 } 259 260 /** 261 * Moves the parser forward and returns the next parsing event 262 * 263 * @exception java.io.IOException 264 * @exception ExifInvalidFormatException 265 * @see #EVENT_START_OF_IFD 266 * @see #EVENT_NEW_TAG 267 * @see #EVENT_VALUE_OF_REGISTERED_TAG 268 * @see #EVENT_COMPRESSED_IMAGE 269 * @see #EVENT_UNCOMPRESSED_STRIP 270 * @see #EVENT_END 271 */ next()272 protected int next() throws IOException, ExifInvalidFormatException { 273 if (!mContainExifData) { 274 return EVENT_END; 275 } 276 int offset = mTiffStream.getReadByteCount(); 277 int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd; 278 if (offset < endOfTags) { 279 mTag = readTag(); 280 if (mTag == null) { 281 return next(); 282 } 283 if (mNeedToParseOffsetsInCurrentIfd) { 284 checkOffsetOrImageTag(mTag); 285 } 286 return EVENT_NEW_TAG; 287 } else if (offset == endOfTags) { 288 // There is a link to ifd1 at the end of ifd0 289 if (mIfdType == IfdId.TYPE_IFD_0) { 290 long ifdOffset = readUnsignedLong(); 291 if (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested()) { 292 if (ifdOffset != 0) { 293 registerIfd(IfdId.TYPE_IFD_1, ifdOffset); 294 } 295 } 296 } else { 297 int offsetSize = 4; 298 // Some camera models use invalid length of the offset 299 if (mCorrespondingEvent.size() > 0) { 300 offsetSize = mCorrespondingEvent.firstEntry().getKey() - 301 mTiffStream.getReadByteCount(); 302 } 303 if (offsetSize < 4) { 304 Log.w(TAG, "Invalid size of link to next IFD: " + offsetSize); 305 } else { 306 long ifdOffset = readUnsignedLong(); 307 if (ifdOffset != 0) { 308 Log.w(TAG, "Invalid link to next IFD: " + ifdOffset); 309 } 310 } 311 } 312 } 313 while (mCorrespondingEvent.size() != 0) { 314 Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry(); 315 Object event = entry.getValue(); 316 try { 317 skipTo(entry.getKey()); 318 } catch (IOException e) { 319 Log.w(TAG, "Failed to skip to data at: " + entry.getKey() + 320 " for " + event.getClass().getName() + ", the file may be broken."); 321 continue; 322 } 323 if (event instanceof IfdEvent) { 324 mIfdType = ((IfdEvent) event).ifd; 325 mNumOfTagInIfd = mTiffStream.readUnsignedShort(); 326 mIfdStartOffset = entry.getKey(); 327 328 if (mNumOfTagInIfd * TAG_SIZE + mIfdStartOffset + OFFSET_SIZE > mApp1End) { 329 Log.w(TAG, "Invalid size of IFD " + mIfdType); 330 return EVENT_END; 331 } 332 333 mNeedToParseOffsetsInCurrentIfd = needToParseOffsetsInCurrentIfd(); 334 if (((IfdEvent) event).isRequested) { 335 return EVENT_START_OF_IFD; 336 } else { 337 skipRemainingTagsInCurrentIfd(); 338 } 339 } else if (event instanceof ImageEvent) { 340 mImageEvent = (ImageEvent) event; 341 return mImageEvent.type; 342 } else { 343 ExifTagEvent tagEvent = (ExifTagEvent) event; 344 mTag = tagEvent.tag; 345 if (mTag.getDataType() != ExifTag.TYPE_UNDEFINED) { 346 readFullTagValue(mTag); 347 checkOffsetOrImageTag(mTag); 348 } 349 if (tagEvent.isRequested) { 350 return EVENT_VALUE_OF_REGISTERED_TAG; 351 } 352 } 353 } 354 return EVENT_END; 355 } 356 357 /** 358 * Skips the tags area of current IFD, if the parser is not in the tag area, 359 * nothing will happen. 360 * 361 * @throws java.io.IOException 362 * @throws ExifInvalidFormatException 363 */ skipRemainingTagsInCurrentIfd()364 protected void skipRemainingTagsInCurrentIfd() throws IOException, ExifInvalidFormatException { 365 int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd; 366 int offset = mTiffStream.getReadByteCount(); 367 if (offset > endOfTags) { 368 return; 369 } 370 if (mNeedToParseOffsetsInCurrentIfd) { 371 while (offset < endOfTags) { 372 mTag = readTag(); 373 offset += TAG_SIZE; 374 if (mTag == null) { 375 continue; 376 } 377 checkOffsetOrImageTag(mTag); 378 } 379 } else { 380 skipTo(endOfTags); 381 } 382 long ifdOffset = readUnsignedLong(); 383 // For ifd0, there is a link to ifd1 in the end of all tags 384 if (mIfdType == IfdId.TYPE_IFD_0 385 && (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested())) { 386 if (ifdOffset > 0) { 387 registerIfd(IfdId.TYPE_IFD_1, ifdOffset); 388 } 389 } 390 } 391 needToParseOffsetsInCurrentIfd()392 private boolean needToParseOffsetsInCurrentIfd() { 393 switch (mIfdType) { 394 case IfdId.TYPE_IFD_0: 395 return isIfdRequested(IfdId.TYPE_IFD_EXIF) || isIfdRequested(IfdId.TYPE_IFD_GPS) 396 || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY) 397 || isIfdRequested(IfdId.TYPE_IFD_1); 398 case IfdId.TYPE_IFD_1: 399 return isThumbnailRequested(); 400 case IfdId.TYPE_IFD_EXIF: 401 // The offset to interoperability IFD is located in Exif IFD 402 return isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY); 403 default: 404 return false; 405 } 406 } 407 408 /** 409 * If {@link #next()} return {@link #EVENT_NEW_TAG} or 410 * {@link #EVENT_VALUE_OF_REGISTERED_TAG}, call this function to get the 411 * corresponding tag. 412 * <p> 413 * For {@link #EVENT_NEW_TAG}, the tag may not contain the value if the size 414 * of the value is greater than 4 bytes. One should call 415 * {@link ExifTag#hasValue()} to check if the tag contains value. If there 416 * is no value,call {@link #registerForTagValue(ExifTag)} to have the parser 417 * emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area 418 * pointed by the offset. 419 * <p> 420 * When {@link #EVENT_VALUE_OF_REGISTERED_TAG} is emitted, the value of the 421 * tag will have already been read except for tags of undefined type. For 422 * tags of undefined type, call one of the read methods to get the value. 423 * 424 * @see #registerForTagValue(ExifTag) 425 * @see #read(byte[]) 426 * @see #read(byte[], int, int) 427 * @see #readLong() 428 * @see #readRational() 429 * @see #readString(int) 430 * @see #readString(int, java.nio.charset.Charset) 431 */ getTag()432 protected ExifTag getTag() { 433 return mTag; 434 } 435 436 /** 437 * Gets number of tags in the current IFD area. 438 */ getTagCountInCurrentIfd()439 protected int getTagCountInCurrentIfd() { 440 return mNumOfTagInIfd; 441 } 442 443 /** 444 * Gets the ID of current IFD. 445 * 446 * @see IfdId#TYPE_IFD_0 447 * @see IfdId#TYPE_IFD_1 448 * @see IfdId#TYPE_IFD_GPS 449 * @see IfdId#TYPE_IFD_INTEROPERABILITY 450 * @see IfdId#TYPE_IFD_EXIF 451 */ getCurrentIfd()452 protected int getCurrentIfd() { 453 return mIfdType; 454 } 455 456 /** 457 * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to 458 * get the index of this strip. 459 * 460 * @see #getStripCount() 461 */ getStripIndex()462 protected int getStripIndex() { 463 return mImageEvent.stripIndex; 464 } 465 466 /** 467 * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to 468 * get the number of strip data. 469 * 470 * @see #getStripIndex() 471 */ getStripCount()472 protected int getStripCount() { 473 return mStripCount; 474 } 475 476 /** 477 * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to 478 * get the strip size. 479 */ getStripSize()480 protected int getStripSize() { 481 if (mStripSizeTag == null) { 482 return 0; 483 } 484 return (int) mStripSizeTag.getValueAt(0); 485 } 486 487 /** 488 * When receiving {@link #EVENT_COMPRESSED_IMAGE}, call this function to get 489 * the image data size. 490 */ getCompressedImageSize()491 protected int getCompressedImageSize() { 492 if (mJpegSizeTag == null) { 493 return 0; 494 } 495 return (int) mJpegSizeTag.getValueAt(0); 496 } 497 skipTo(int offset)498 private void skipTo(int offset) throws IOException { 499 mTiffStream.skipTo(offset); 500 while (!mCorrespondingEvent.isEmpty() && mCorrespondingEvent.firstKey() < offset) { 501 mCorrespondingEvent.pollFirstEntry(); 502 } 503 } 504 505 /** 506 * When getting {@link #EVENT_NEW_TAG} in the tag area of IFD, the tag may 507 * not contain the value if the size of the value is greater than 4 bytes. 508 * When the value is not available here, call this method so that the parser 509 * will emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area 510 * where the value is located. 511 * 512 * @see #EVENT_VALUE_OF_REGISTERED_TAG 513 */ registerForTagValue(ExifTag tag)514 protected void registerForTagValue(ExifTag tag) { 515 if (tag.getOffset() >= mTiffStream.getReadByteCount()) { 516 mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, true)); 517 } 518 } 519 registerIfd(int ifdType, long offset)520 private void registerIfd(int ifdType, long offset) { 521 // Cast unsigned int to int since the offset is always smaller 522 // than the size of APP1 (65536) 523 mCorrespondingEvent.put((int) offset, new IfdEvent(ifdType, isIfdRequested(ifdType))); 524 } 525 registerCompressedImage(long offset)526 private void registerCompressedImage(long offset) { 527 mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_COMPRESSED_IMAGE)); 528 } 529 registerUncompressedStrip(int stripIndex, long offset)530 private void registerUncompressedStrip(int stripIndex, long offset) { 531 mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_UNCOMPRESSED_STRIP 532 , stripIndex)); 533 } 534 readTag()535 private ExifTag readTag() throws IOException, ExifInvalidFormatException { 536 short tagId = mTiffStream.readShort(); 537 short dataFormat = mTiffStream.readShort(); 538 long numOfComp = mTiffStream.readUnsignedInt(); 539 if (numOfComp > Integer.MAX_VALUE) { 540 throw new ExifInvalidFormatException( 541 "Number of component is larger then Integer.MAX_VALUE"); 542 } 543 // Some invalid image file contains invalid data type. Ignore those tags 544 if (!ExifTag.isValidType(dataFormat)) { 545 Log.w(TAG, String.format("Tag %04x: Invalid data type %d", tagId, dataFormat)); 546 mTiffStream.skip(4); 547 return null; 548 } 549 // TODO: handle numOfComp overflow 550 ExifTag tag = new ExifTag(tagId, dataFormat, (int) numOfComp, mIfdType, 551 ((int) numOfComp) != ExifTag.SIZE_UNDEFINED); 552 int dataSize = tag.getDataSize(); 553 if (dataSize > 4) { 554 long offset = mTiffStream.readUnsignedInt(); 555 if (offset > Integer.MAX_VALUE) { 556 throw new ExifInvalidFormatException( 557 "offset is larger then Integer.MAX_VALUE"); 558 } 559 // Some invalid images put some undefined data before IFD0. 560 // Read the data here. 561 if (mDataAboveIfd0 != null 562 && offset < mIfd0Position 563 && dataFormat == ExifTag.TYPE_UNDEFINED) { 564 byte[] buf = new byte[(int) numOfComp]; 565 System.arraycopy(mDataAboveIfd0, (int) offset - DEFAULT_IFD0_OFFSET, 566 buf, 0, (int) numOfComp); 567 tag.setValue(buf); 568 } else { 569 tag.setOffset((int) offset); 570 } 571 } else { 572 boolean defCount = tag.hasDefinedCount(); 573 // Set defined count to 0 so we can add \0 to non-terminated strings 574 tag.setHasDefinedCount(false); 575 // Read value 576 readFullTagValue(tag); 577 tag.setHasDefinedCount(defCount); 578 mTiffStream.skip(4 - dataSize); 579 // Set the offset to the position of value. 580 tag.setOffset(mTiffStream.getReadByteCount() - 4); 581 } 582 return tag; 583 } 584 585 /** 586 * Check the tag, if the tag is one of the offset tag that points to the IFD 587 * or image the caller is interested in, register the IFD or image. 588 */ checkOffsetOrImageTag(ExifTag tag)589 private void checkOffsetOrImageTag(ExifTag tag) { 590 // Some invalid formattd image contains tag with 0 size. 591 if (tag.getComponentCount() == 0) { 592 return; 593 } 594 short tid = tag.getTagId(); 595 int ifd = tag.getIfd(); 596 if (tid == TAG_EXIF_IFD && checkAllowed(ifd, ExifInterface.TAG_EXIF_IFD)) { 597 if (isIfdRequested(IfdId.TYPE_IFD_EXIF) 598 || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) { 599 registerIfd(IfdId.TYPE_IFD_EXIF, tag.getValueAt(0)); 600 } 601 } else if (tid == TAG_GPS_IFD && checkAllowed(ifd, ExifInterface.TAG_GPS_IFD)) { 602 if (isIfdRequested(IfdId.TYPE_IFD_GPS)) { 603 registerIfd(IfdId.TYPE_IFD_GPS, tag.getValueAt(0)); 604 } 605 } else if (tid == TAG_INTEROPERABILITY_IFD 606 && checkAllowed(ifd, ExifInterface.TAG_INTEROPERABILITY_IFD)) { 607 if (isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) { 608 registerIfd(IfdId.TYPE_IFD_INTEROPERABILITY, tag.getValueAt(0)); 609 } 610 } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT 611 && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)) { 612 if (isThumbnailRequested()) { 613 registerCompressedImage(tag.getValueAt(0)); 614 } 615 } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT_LENGTH 616 && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)) { 617 if (isThumbnailRequested()) { 618 mJpegSizeTag = tag; 619 } 620 } else if (tid == TAG_STRIP_OFFSETS && checkAllowed(ifd, ExifInterface.TAG_STRIP_OFFSETS)) { 621 if (isThumbnailRequested()) { 622 if (tag.hasValue()) { 623 for (int i = 0; i < tag.getComponentCount(); i++) { 624 if (tag.getDataType() == ExifTag.TYPE_UNSIGNED_SHORT) { 625 registerUncompressedStrip(i, tag.getValueAt(i)); 626 } else { 627 registerUncompressedStrip(i, tag.getValueAt(i)); 628 } 629 } 630 } else { 631 mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, false)); 632 } 633 } 634 } else if (tid == TAG_STRIP_BYTE_COUNTS 635 && checkAllowed(ifd, ExifInterface.TAG_STRIP_BYTE_COUNTS) 636 && isThumbnailRequested() && tag.hasValue()) { 637 mStripSizeTag = tag; 638 } 639 } 640 checkAllowed(int ifd, int tagId)641 private boolean checkAllowed(int ifd, int tagId) { 642 int info = mInterface.getTagInfo().get(tagId); 643 if (info == ExifInterface.DEFINITION_NULL) { 644 return false; 645 } 646 return ExifInterface.isIfdAllowed(info, ifd); 647 } 648 readFullTagValue(ExifTag tag)649 protected void readFullTagValue(ExifTag tag) throws IOException { 650 // Some invalid images contains tags with wrong size, check it here 651 short type = tag.getDataType(); 652 if (type == ExifTag.TYPE_ASCII || type == ExifTag.TYPE_UNDEFINED || 653 type == ExifTag.TYPE_UNSIGNED_BYTE) { 654 int size = tag.getComponentCount(); 655 if (mCorrespondingEvent.size() > 0) { 656 if (mCorrespondingEvent.firstEntry().getKey() < mTiffStream.getReadByteCount() 657 + size) { 658 Object event = mCorrespondingEvent.firstEntry().getValue(); 659 if (event instanceof ImageEvent) { 660 // Tag value overlaps thumbnail, ignore thumbnail. 661 Log.w(TAG, "Thumbnail overlaps value for tag: \n" + tag.toString()); 662 Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry(); 663 Log.w(TAG, "Invalid thumbnail offset: " + entry.getKey()); 664 } else { 665 // Tag value overlaps another tag, shorten count 666 if (event instanceof IfdEvent) { 667 Log.w(TAG, "Ifd " + ((IfdEvent) event).ifd 668 + " overlaps value for tag: \n" + tag.toString()); 669 } else if (event instanceof ExifTagEvent) { 670 Log.w(TAG, "Tag value for tag: \n" 671 + ((ExifTagEvent) event).tag.toString() 672 + " overlaps value for tag: \n" + tag.toString()); 673 } 674 size = mCorrespondingEvent.firstEntry().getKey() 675 - mTiffStream.getReadByteCount(); 676 Log.w(TAG, "Invalid size of tag: \n" + tag.toString() 677 + " setting count to: " + size); 678 tag.forceSetComponentCount(size); 679 } 680 } 681 } 682 } 683 switch (tag.getDataType()) { 684 case ExifTag.TYPE_UNSIGNED_BYTE: 685 case ExifTag.TYPE_UNDEFINED: { 686 byte buf[] = new byte[tag.getComponentCount()]; 687 read(buf); 688 tag.setValue(buf); 689 } 690 break; 691 case ExifTag.TYPE_ASCII: 692 tag.setValue(readString(tag.getComponentCount())); 693 break; 694 case ExifTag.TYPE_UNSIGNED_LONG: { 695 long value[] = new long[tag.getComponentCount()]; 696 for (int i = 0, n = value.length; i < n; i++) { 697 value[i] = readUnsignedLong(); 698 } 699 tag.setValue(value); 700 } 701 break; 702 case ExifTag.TYPE_UNSIGNED_RATIONAL: { 703 Rational value[] = new Rational[tag.getComponentCount()]; 704 for (int i = 0, n = value.length; i < n; i++) { 705 value[i] = readUnsignedRational(); 706 } 707 tag.setValue(value); 708 } 709 break; 710 case ExifTag.TYPE_UNSIGNED_SHORT: { 711 int value[] = new int[tag.getComponentCount()]; 712 for (int i = 0, n = value.length; i < n; i++) { 713 value[i] = readUnsignedShort(); 714 } 715 tag.setValue(value); 716 } 717 break; 718 case ExifTag.TYPE_LONG: { 719 int value[] = new int[tag.getComponentCount()]; 720 for (int i = 0, n = value.length; i < n; i++) { 721 value[i] = readLong(); 722 } 723 tag.setValue(value); 724 } 725 break; 726 case ExifTag.TYPE_RATIONAL: { 727 Rational value[] = new Rational[tag.getComponentCount()]; 728 for (int i = 0, n = value.length; i < n; i++) { 729 value[i] = readRational(); 730 } 731 tag.setValue(value); 732 } 733 break; 734 } 735 if (LOGV) { 736 Log.v(TAG, "\n" + tag.toString()); 737 } 738 } 739 parseTiffHeader()740 private void parseTiffHeader() throws IOException, 741 ExifInvalidFormatException { 742 short byteOrder = mTiffStream.readShort(); 743 if (LITTLE_ENDIAN_TAG == byteOrder) { 744 mTiffStream.setByteOrder(ByteOrder.LITTLE_ENDIAN); 745 } else if (BIG_ENDIAN_TAG == byteOrder) { 746 mTiffStream.setByteOrder(ByteOrder.BIG_ENDIAN); 747 } else { 748 throw new ExifInvalidFormatException("Invalid TIFF header"); 749 } 750 751 if (mTiffStream.readShort() != TIFF_HEADER_TAIL) { 752 throw new ExifInvalidFormatException("Invalid TIFF header"); 753 } 754 } 755 seekTiffData(InputStream inputStream)756 private boolean seekTiffData(InputStream inputStream) throws IOException, 757 ExifInvalidFormatException { 758 CountedDataInputStream dataStream = new CountedDataInputStream(inputStream); 759 if (dataStream.readShort() != JpegHeader.SOI) { 760 throw new ExifInvalidFormatException("Invalid JPEG format"); 761 } 762 763 short marker = dataStream.readShort(); 764 while (marker != JpegHeader.EOI 765 && !JpegHeader.isSofMarker(marker)) { 766 int length = dataStream.readUnsignedShort(); 767 // Some invalid formatted image contains multiple APP1, 768 // try to find the one with Exif data. 769 if (marker == JpegHeader.APP1) { 770 int header = 0; 771 short headerTail = 0; 772 if (length >= 8) { 773 header = dataStream.readInt(); 774 headerTail = dataStream.readShort(); 775 length -= 6; 776 if (header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) { 777 mTiffStartPosition = dataStream.getReadByteCount(); 778 mApp1End = length; 779 mOffsetToApp1EndFromSOF = mTiffStartPosition + mApp1End; 780 return true; 781 } 782 } 783 } 784 if (length < 2 || (length - 2) != dataStream.skip(length - 2)) { 785 Log.w(TAG, "Invalid JPEG format."); 786 return false; 787 } 788 marker = dataStream.readShort(); 789 } 790 return false; 791 } 792 getOffsetToExifEndFromSOF()793 protected int getOffsetToExifEndFromSOF() { 794 return mOffsetToApp1EndFromSOF; 795 } 796 getTiffStartPosition()797 protected int getTiffStartPosition() { 798 return mTiffStartPosition; 799 } 800 801 /** 802 * Reads bytes from the InputStream. 803 */ read(byte[] buffer, int offset, int length)804 protected int read(byte[] buffer, int offset, int length) throws IOException { 805 return mTiffStream.read(buffer, offset, length); 806 } 807 808 /** 809 * Equivalent to read(buffer, 0, buffer.length). 810 */ read(byte[] buffer)811 protected int read(byte[] buffer) throws IOException { 812 return mTiffStream.read(buffer); 813 } 814 815 /** 816 * Reads a String from the InputStream with US-ASCII charset. The parser 817 * will read n bytes and convert it to ascii string. This is used for 818 * reading values of type {@link ExifTag#TYPE_ASCII}. 819 */ readString(int n)820 protected String readString(int n) throws IOException { 821 return readString(n, US_ASCII); 822 } 823 824 /** 825 * Reads a String from the InputStream with the given charset. The parser 826 * will read n bytes and convert it to string. This is used for reading 827 * values of type {@link ExifTag#TYPE_ASCII}. 828 */ readString(int n, Charset charset)829 protected String readString(int n, Charset charset) throws IOException { 830 if (n > 0) { 831 return mTiffStream.readString(n, charset); 832 } else { 833 return ""; 834 } 835 } 836 837 /** 838 * Reads value of type {@link ExifTag#TYPE_UNSIGNED_SHORT} from the 839 * InputStream. 840 */ readUnsignedShort()841 protected int readUnsignedShort() throws IOException { 842 return mTiffStream.readShort() & 0xffff; 843 } 844 845 /** 846 * Reads value of type {@link ExifTag#TYPE_UNSIGNED_LONG} from the 847 * InputStream. 848 */ readUnsignedLong()849 protected long readUnsignedLong() throws IOException { 850 return readLong() & 0xffffffffL; 851 } 852 853 /** 854 * Reads value of type {@link ExifTag#TYPE_UNSIGNED_RATIONAL} from the 855 * InputStream. 856 */ readUnsignedRational()857 protected Rational readUnsignedRational() throws IOException { 858 long nomi = readUnsignedLong(); 859 long denomi = readUnsignedLong(); 860 return new Rational(nomi, denomi); 861 } 862 863 /** 864 * Reads value of type {@link ExifTag#TYPE_LONG} from the InputStream. 865 */ readLong()866 protected int readLong() throws IOException { 867 return mTiffStream.readInt(); 868 } 869 870 /** 871 * Reads value of type {@link ExifTag#TYPE_RATIONAL} from the InputStream. 872 */ readRational()873 protected Rational readRational() throws IOException { 874 int nomi = readLong(); 875 int denomi = readLong(); 876 return new Rational(nomi, denomi); 877 } 878 879 private static class ImageEvent { 880 int stripIndex; 881 int type; 882 ImageEvent(int type)883 ImageEvent(int type) { 884 this.stripIndex = 0; 885 this.type = type; 886 } 887 ImageEvent(int type, int stripIndex)888 ImageEvent(int type, int stripIndex) { 889 this.type = type; 890 this.stripIndex = stripIndex; 891 } 892 } 893 894 private static class IfdEvent { 895 int ifd; 896 boolean isRequested; 897 IfdEvent(int ifd, boolean isInterestedIfd)898 IfdEvent(int ifd, boolean isInterestedIfd) { 899 this.ifd = ifd; 900 this.isRequested = isInterestedIfd; 901 } 902 } 903 904 private static class ExifTagEvent { 905 ExifTag tag; 906 boolean isRequested; 907 ExifTagEvent(ExifTag tag, boolean isRequireByUser)908 ExifTagEvent(ExifTag tag, boolean isRequireByUser) { 909 this.tag = tag; 910 this.isRequested = isRequireByUser; 911 } 912 } 913 914 /** 915 * Gets the byte order of the current InputStream. 916 */ getByteOrder()917 protected ByteOrder getByteOrder() { 918 return mTiffStream.getByteOrder(); 919 } 920 } 921