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