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