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