397 // Class to represent an IFD entry where the actual content is at an offset 398 // in the stream somewhere outside the IFD itself. This occurs when the 399 // value cannot be contained within four bytes. Seeking is required to read 400 // such field values. 401 // 402 private static class TIFFIFDEntry { 403 public final TIFFTag tag; 404 public final int type; 405 public final int count; 406 public final long offset; 407 408 TIFFIFDEntry(TIFFTag tag, int type, int count, long offset) { 409 this.tag = tag; 410 this.type = type; 411 this.count = count; 412 this.offset = offset; 413 } 414 } 415 416 // 417 // Verify that data pointed to outside of the IFD itself are within the 418 // stream. To be called after all fields have been read and populated. 419 // 420 private void checkFieldOffsets(long streamLength) throws IIOException { 421 if (streamLength < 0) { 422 return; 423 } 424 425 // StripOffsets 426 List<TIFFField> offsets = new ArrayList<>(); 427 TIFFField f = getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS); 428 int count = 0; 429 if (f != null) { 430 count = f.getCount(); 431 offsets.add(f); 432 } 433 434 // TileOffsets 435 f = getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS); 436 if (f != null) { 485 } 486 } 487 488 // JPEGInterchangeFormat and JPEGInterchangeFormatLength 489 TIFFField jpegOffset = 490 getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT); 491 if (jpegOffset != null) { 492 TIFFField jpegLength = 493 getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 494 if (jpegLength != null) { 495 if (jpegOffset.getAsLong(0) + jpegLength.getAsLong(0) 496 > streamLength) { 497 throw new IIOException 498 ("JPEGInterchangeFormat data out of stream"); 499 } 500 } 501 } 502 503 // Ensure there is at least a data pointer for JPEG interchange format or 504 // both data offsets and byte counts for other compression types. 505 if (jpegOffset == null && (offsets.size() == 0 || byteCounts.size() == 0)) { 506 throw new IIOException("Insufficient data offsets or byte counts"); 507 } 508 509 // JPEGQTables - one 64-byte table for each offset. 510 f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_Q_TABLES); 511 if (f != null) { 512 long[] tableOffsets = f.getAsLongs(); 513 for (long off : tableOffsets) { 514 if (off + 64 > streamLength) { 515 throw new IIOException("JPEGQTables data out of stream"); 516 } 517 } 518 } 519 520 // JPEGDCTables 521 f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_DC_TABLES); 522 if (f != null) { 523 long[] tableOffsets = f.getAsLongs(); 524 for (long off : tableOffsets) { 525 if (off + 16 > streamLength) { 526 throw new IIOException("JPEGDCTables data out of stream"); | 397 // Class to represent an IFD entry where the actual content is at an offset 398 // in the stream somewhere outside the IFD itself. This occurs when the 399 // value cannot be contained within four bytes. Seeking is required to read 400 // such field values. 401 // 402 private static class TIFFIFDEntry { 403 public final TIFFTag tag; 404 public final int type; 405 public final int count; 406 public final long offset; 407 408 TIFFIFDEntry(TIFFTag tag, int type, int count, long offset) { 409 this.tag = tag; 410 this.type = type; 411 this.count = count; 412 this.offset = offset; 413 } 414 } 415 416 // 417 // Retrieve the value of a baseline field as a long. 418 // 419 private long getFieldAsLong(int tagNumber) { 420 TIFFField f = getTIFFField(tagNumber); 421 return f == null ? -1 : f.getAsLong(0); 422 } 423 424 // 425 // Retrieve the value of a baseline field as an int. 426 // 427 private int getFieldAsInt(int tagNumber) { 428 TIFFField f = getTIFFField(tagNumber); 429 return f == null ? -1 : f.getAsInt(0); 430 } 431 432 // 433 // Calculate the number of bytes in each strip or tile. This method 434 // is to be used if and only if no fields exist which provide this 435 // information. The parameter must be empty and if the method succeeds 436 // will contain a single element. 437 // 438 private boolean calculateByteCounts(int expectedSize, 439 List<TIFFField> byteCounts) { 440 if (!byteCounts.isEmpty()) { 441 throw new IllegalArgumentException("byteCounts is not empty"); 442 } 443 444 // must be interleaved 445 if (getFieldAsInt(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION) == 446 BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) { 447 return false; 448 } 449 450 // must be uncompressed 451 if (getFieldAsInt(BaselineTIFFTagSet.TAG_COMPRESSION) != 452 BaselineTIFFTagSet.COMPRESSION_NONE) { 453 return false; 454 } 455 456 // must have image dimensions 457 long w = getFieldAsLong(BaselineTIFFTagSet.TAG_IMAGE_WIDTH); 458 if (w < 0) { 459 return false; 460 } 461 long h = getFieldAsLong(BaselineTIFFTagSet.TAG_IMAGE_LENGTH); 462 if (h < 0) { 463 return false; 464 } 465 466 long tw = getFieldAsLong(BaselineTIFFTagSet.TAG_TILE_WIDTH); 467 if (tw < 0) { 468 tw = w; 469 } 470 long th = getFieldAsLong(BaselineTIFFTagSet.TAG_TILE_LENGTH); 471 if (th < 0) { 472 th = getFieldAsLong(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP); 473 if (th < 0) { 474 th = h; 475 } 476 } 477 478 int[] bitsPerSample = null; 479 TIFFField f = getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE); 480 if (f != null) { 481 bitsPerSample = f.getAsInts(); 482 } else { 483 int samplesPerPixel = 484 getFieldAsInt(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL); 485 if (samplesPerPixel < 0) { 486 samplesPerPixel = 1; 487 } 488 bitsPerSample = new int[samplesPerPixel]; 489 Arrays.fill(bitsPerSample, 8); 490 } 491 492 int bitsPerPixel = 0; 493 for (int bps : bitsPerSample) { 494 bitsPerPixel += bps; 495 } 496 497 int bytesPerRow = (int)(tw*bitsPerPixel + 7)/8; 498 int bytesPerPacket = (int)th*bytesPerRow; 499 500 long nx = (w + tw - 1)/tw; 501 long ny = (h + th - 1)/th; 502 503 if (nx*ny != expectedSize) { 504 return false; 505 } 506 507 boolean isTiled = 508 getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS) != null; 509 510 int tagNumber; 511 if (isTiled) { 512 tagNumber = BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS; 513 } else { 514 tagNumber = BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS; 515 } 516 517 TIFFTag t = BaselineTIFFTagSet.getInstance().getTag(tagNumber); 518 f = getTIFFField(tagNumber); 519 if (f != null) { 520 removeTIFFField(tagNumber); 521 } 522 523 int numPackets = (int)(nx*ny); 524 long[] packetByteCounts = new long[numPackets]; 525 Arrays.fill(packetByteCounts, bytesPerPacket); 526 527 // if the strip or tile width does not exceed the image width and the 528 // image height is not a multiple of the strip or tile height, then 529 // truncate the estimate of the byte count of the last strip to avoid 530 // reading past the end of the data 531 if (tw <= w && h % th != 0) { 532 int numRowsInLastStrip = (int)(h - (ny - 1)*th); 533 packetByteCounts[numPackets - 1] = numRowsInLastStrip*bytesPerRow; 534 } 535 536 f = new TIFFField(t, TIFFTag.TIFF_LONG, numPackets, packetByteCounts); 537 addTIFFField(f); 538 byteCounts.add(f); 539 540 return true; 541 } 542 543 // 544 // Verify that data pointed to outside of the IFD itself are within the 545 // stream. To be called after all fields have been read and populated. 546 // 547 private void checkFieldOffsets(long streamLength) throws IIOException { 548 if (streamLength < 0) { 549 return; 550 } 551 552 // StripOffsets 553 List<TIFFField> offsets = new ArrayList<>(); 554 TIFFField f = getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS); 555 int count = 0; 556 if (f != null) { 557 count = f.getCount(); 558 offsets.add(f); 559 } 560 561 // TileOffsets 562 f = getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS); 563 if (f != null) { 612 } 613 } 614 615 // JPEGInterchangeFormat and JPEGInterchangeFormatLength 616 TIFFField jpegOffset = 617 getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT); 618 if (jpegOffset != null) { 619 TIFFField jpegLength = 620 getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 621 if (jpegLength != null) { 622 if (jpegOffset.getAsLong(0) + jpegLength.getAsLong(0) 623 > streamLength) { 624 throw new IIOException 625 ("JPEGInterchangeFormat data out of stream"); 626 } 627 } 628 } 629 630 // Ensure there is at least a data pointer for JPEG interchange format or 631 // both data offsets and byte counts for other compression types. 632 if (jpegOffset == null 633 && (offsets.size() == 0 || byteCounts.size() == 0)) { 634 boolean throwException = true; 635 if (offsets.size() != 0 && byteCounts.size() == 0) { 636 // Attempt to calculate missing byte counts 637 int expectedSize = offsets.get(0).getCount(); 638 throwException = 639 !calculateByteCounts(expectedSize, byteCounts); 640 } 641 if (throwException) { 642 throw new IIOException 643 ("Insufficient data offsets or byte counts"); 644 } 645 } 646 647 // JPEGQTables - one 64-byte table for each offset. 648 f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_Q_TABLES); 649 if (f != null) { 650 long[] tableOffsets = f.getAsLongs(); 651 for (long off : tableOffsets) { 652 if (off + 64 > streamLength) { 653 throw new IIOException("JPEGQTables data out of stream"); 654 } 655 } 656 } 657 658 // JPEGDCTables 659 f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_DC_TABLES); 660 if (f != null) { 661 long[] tableOffsets = f.getAsLongs(); 662 for (long off : tableOffsets) { 663 if (off + 16 > streamLength) { 664 throw new IIOException("JPEGDCTables data out of stream"); |