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