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