001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.imaging.formats.tiff; 018 019import java.awt.image.BufferedImage; 020import java.io.IOException; 021import java.nio.ByteOrder; 022import java.util.ArrayList; 023import java.util.Collections; 024import java.util.List; 025import java.util.Map; 026 027import org.apache.commons.imaging.ImageReadException; 028import org.apache.commons.imaging.common.ByteConversions; 029import org.apache.commons.imaging.common.RationalNumber; 030import org.apache.commons.imaging.formats.tiff.constants.TiffConstants; 031import org.apache.commons.imaging.formats.tiff.constants.TiffDirectoryConstants; 032import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants; 033import org.apache.commons.imaging.formats.tiff.fieldtypes.FieldType; 034import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo; 035import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoAscii; 036import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoByte; 037import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoBytes; 038import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoDouble; 039import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoDoubles; 040import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoFloat; 041import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoFloats; 042import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoGpsText; 043import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoLong; 044import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoLongs; 045import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoRational; 046import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoRationals; 047import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSByte; 048import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSBytes; 049import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSLong; 050import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSLongs; 051import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSRational; 052import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSRationals; 053import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSShort; 054import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSShorts; 055import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoShort; 056import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoShortOrLong; 057import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoShorts; 058import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoXpString; 059 060/** 061 * Provides methods and elements for accessing an Image File Directory (IFD) 062 * from a TIFF file. In the TIFF specification, the IFD is the main container 063 * for individual images or sets of metadata. While not all Directories contain 064 * images, images are always stored in a Directory. 065 */ 066public class TiffDirectory extends TiffElement { 067 public final int type; 068 public final List<TiffField> entries; 069 public final long nextDirectoryOffset; 070 private TiffImageData tiffImageData; 071 private JpegImageData jpegImageData; 072 073 // Preservers the byte order derived from the TIFF file header. 074 // Some of the legacy methods in this class require byte order as an 075 // argument, though that use could be phased out eventually. 076 private final ByteOrder headerByteOrder; 077 078 079 public TiffDirectory( 080 final int type, 081 final List<TiffField> entries, 082 final long offset, 083 final long nextDirectoryOffset, 084 final ByteOrder byteOrder) { 085 super(offset, TiffConstants.TIFF_DIRECTORY_HEADER_LENGTH 086 + entries.size() * TiffConstants.TIFF_ENTRY_LENGTH 087 + TiffConstants.TIFF_DIRECTORY_FOOTER_LENGTH); 088 089 this.type = type; 090 this.entries = Collections.unmodifiableList(entries); 091 this.nextDirectoryOffset = nextDirectoryOffset; 092 this.headerByteOrder = byteOrder; 093 } 094 095 public String description() { 096 return TiffDirectory.description(type); 097 } 098 099 @Override 100 public String getElementDescription() { 101 long entryOffset = offset + TiffConstants.TIFF_DIRECTORY_HEADER_LENGTH; 102 103 final StringBuilder result = new StringBuilder(); 104 for (final TiffField entry : entries) { 105 result.append(String.format("\t[%d]: %s (%d, 0x%x), %s, %d: %s%n", 106 entryOffset, entry.getTagInfo().name, 107 entry.getTag(), entry.getTag(), 108 entry.getFieldType().getName(), entry.getBytesLength(), 109 entry.getValueDescription())); 110 111 entryOffset += TiffConstants.TIFF_ENTRY_LENGTH; 112 } 113 return result.toString(); 114 } 115 116 public static String description(final int type) { 117 switch (type) { 118 case TiffDirectoryConstants.DIRECTORY_TYPE_UNKNOWN: 119 return "Unknown"; 120 case TiffDirectoryConstants.DIRECTORY_TYPE_ROOT: 121 return "Root"; 122 case TiffDirectoryConstants.DIRECTORY_TYPE_SUB: 123 return "Sub"; 124 case TiffDirectoryConstants.DIRECTORY_TYPE_THUMBNAIL: 125 return "Thumbnail"; 126 case TiffDirectoryConstants.DIRECTORY_TYPE_EXIF: 127 return "Exif"; 128 case TiffDirectoryConstants.DIRECTORY_TYPE_GPS: 129 return "Gps"; 130 case TiffDirectoryConstants.DIRECTORY_TYPE_INTEROPERABILITY: 131 return "Interoperability"; 132 default: 133 return "Bad Type"; 134 } 135 } 136 137 138 public List<TiffField> getDirectoryEntries() { 139 return new ArrayList<>(entries); 140 } 141 142 public void dump() { 143 for (final TiffField entry : entries) { 144 entry.dump(); 145 } 146 147 } 148 149 public boolean hasJpegImageData() throws ImageReadException { 150 if (null != findField(TiffTagConstants.TIFF_TAG_JPEG_INTERCHANGE_FORMAT)) { 151 return true; 152 } 153 154 return false; 155 } 156 157 public boolean hasTiffImageData() throws ImageReadException { 158 if (null != findField(TiffTagConstants.TIFF_TAG_TILE_OFFSETS)) { 159 return true; 160 } 161 162 if (null != findField(TiffTagConstants.TIFF_TAG_STRIP_OFFSETS)) { 163 return true; 164 } 165 166 return false; 167 } 168 169 /** 170 * Gets the image associated with the directory, if any. Note that not all 171 * directories contain images. 172 * 173 * @return if successful, a valid BufferedImage instance. 174 * @throws ImageReadException in the event of an invalid or incompatible 175 * data format. 176 * @throws IOException in the event of an I/O error. 177 */ 178 public BufferedImage getTiffImage() throws ImageReadException, IOException { 179 if (null == tiffImageData) { 180 return null; 181 } 182 183 return new TiffImageParser().getBufferedImage(this, headerByteOrder, null); 184 } 185 186 /** 187 * Gets the image associated with the directory, if any. Note that not all 188 * directories contain images. 189 * <p> 190 * The optional parameters map can be used to specify image access or 191 * rendering options such as reading only a part of the overall image (i.e. 192 * reading a sub-image) or applying a custom photometric interpreter. 193 * 194 * @param params a map containing optional parameters to be applied to the 195 * read operation. 196 * @return if successful, a valid BufferedImage instance. 197 * @throws ImageReadException in the event of an invalid or incompatible 198 * data format. 199 * @throws IOException in the event of an I/O error. 200 */ 201 public BufferedImage getTiffImage(final Map<String, Object> params) 202 throws ImageReadException, IOException { 203 if (null == tiffImageData) { 204 return null; 205 } 206 207 return new TiffImageParser().getBufferedImage(this, headerByteOrder, params); 208 } 209 210 /** 211 * Gets the image associated with the directory, if any. Note that not all 212 * directories contain images. 213 * <p> 214 * This method comes from an older version of this class in which byte order 215 * was required from an external source. Developers are encouraged to use 216 * the simpler version of getTiffImage that does not require the byte-order 217 * argument. 218 * 219 * @param byteOrder byte-order obtained from the containing TIFF file 220 * @return if successful, a valid BufferedImage instance. 221 * @throws ImageReadException in the event of an invalid or incompatible 222 * data format. 223 * @throws IOException in the event of an I/O error. 224 */ 225 public BufferedImage getTiffImage(final ByteOrder byteOrder) throws ImageReadException, 226 IOException { 227 final Map<String, Object> params = null; 228 return getTiffImage(byteOrder, params); 229 } 230 231 /** 232 * Gets the image associated with the directory, if any. Note that not all 233 * directories contain images. 234 * <p> 235 * This method comes from an older version of this class in which byte order 236 * was required from an external source. Developers are encouraged to use 237 * the simpler version of getTiffImage that does not require the byte-order 238 * argument. 239 * 240 * @param byteOrder byte-order obtained from the containing TIFF file 241 * @param params a map containing optional parameters to be applied to the 242 * read operation. 243 * @return if successful, a valid BufferedImage instance. 244 * @throws ImageReadException in the event of an invalid or incompatible 245 * data format. 246 * @throws IOException in the event of an I/O error. 247 */ 248 public BufferedImage getTiffImage(final ByteOrder byteOrder, final Map<String, Object> params) 249 throws ImageReadException, IOException { 250 if (null == tiffImageData) { 251 return null; 252 } 253 254 return new TiffImageParser().getBufferedImage(this, byteOrder, params); 255 } 256 257 258 259 public TiffField findField(final TagInfo tag) throws ImageReadException { 260 final boolean failIfMissing = false; 261 return findField(tag, failIfMissing); 262 } 263 264 public TiffField findField(final TagInfo tag, final boolean failIfMissing) 265 throws ImageReadException { 266 if (entries == null) { 267 return null; 268 } 269 270 for (final TiffField field : entries) { 271 if (field.getTag() == tag.tag) { 272 return field; 273 } 274 } 275 276 if (failIfMissing) { 277 throw new ImageReadException("Missing expected field: " 278 + tag.getDescription()); 279 } 280 281 return null; 282 } 283 284 public Object getFieldValue(final TagInfo tag) throws ImageReadException { 285 final TiffField field = findField(tag); 286 if (field == null) { 287 return null; 288 } 289 return field.getValue(); 290 } 291 292 public String getSingleFieldValue(final TagInfoAscii tag) 293 throws ImageReadException { 294 final String[] result = getFieldValue(tag, true); 295 if (result.length != 1) { 296 throw new ImageReadException("Field \"" + tag.name 297 + "\" has incorrect length " + result.length); 298 } 299 return result[0]; 300 } 301 302 public int getSingleFieldValue(final TagInfoShortOrLong tag) throws ImageReadException { 303 final int[] result = getFieldValue(tag, true); 304 if (result.length != 1) { 305 throw new ImageReadException("Field \"" + tag.name 306 + "\" has incorrect length " + result.length); 307 } 308 return result[0]; 309 } 310 311 public byte getFieldValue(final TagInfoByte tag) 312 throws ImageReadException { 313 final TiffField field = findField(tag); 314 if (field == null) { 315 throw new ImageReadException("Required field \"" + tag.name 316 + "\" is missing"); 317 } 318 if (!tag.dataTypes.contains(field.getFieldType())) { 319 throw new ImageReadException("Required field \"" + tag.name 320 + "\" has incorrect type " + field.getFieldType().getName()); 321 } 322 if (field.getCount() != 1) { 323 throw new ImageReadException("Field \"" + tag.name + "\" has wrong count " + field.getCount()); 324 } 325 return field.getByteArrayValue()[0]; 326 } 327 328 public byte[] getFieldValue(final TagInfoBytes tag, final boolean mustExist) 329 throws ImageReadException { 330 final TiffField field = findField(tag); 331 if (field == null) { 332 if (mustExist) { 333 throw new ImageReadException("Required field \"" + tag.name 334 + "\" is missing"); 335 } else { 336 return null; 337 } 338 } 339 if (!tag.dataTypes.contains(field.getFieldType())) { 340 if (mustExist) { 341 throw new ImageReadException("Required field \"" + tag.name 342 + "\" has incorrect type " + field.getFieldType().getName()); 343 } else { 344 return null; 345 } 346 } 347 return field.getByteArrayValue(); 348 } 349 350 public String[] getFieldValue(final TagInfoAscii tag, final boolean mustExist) 351 throws ImageReadException { 352 final TiffField field = findField(tag); 353 if (field == null) { 354 if (mustExist) { 355 throw new ImageReadException("Required field \"" + tag.name 356 + "\" is missing"); 357 } else { 358 return null; 359 } 360 } 361 if (!tag.dataTypes.contains(field.getFieldType())) { 362 if (mustExist) { 363 throw new ImageReadException("Required field \"" + tag.name 364 + "\" has incorrect type " + field.getFieldType().getName()); 365 } else { 366 return null; 367 } 368 } 369 final byte[] bytes = field.getByteArrayValue(); 370 return tag.getValue(field.getByteOrder(), bytes); 371 } 372 373 public short getFieldValue(final TagInfoShort tag) 374 throws ImageReadException { 375 final TiffField field = findField(tag); 376 if (field == null) { 377 throw new ImageReadException("Required field \"" + tag.name 378 + "\" is missing"); 379 } 380 if (!tag.dataTypes.contains(field.getFieldType())) { 381 throw new ImageReadException("Required field \"" + tag.name 382 + "\" has incorrect type " + field.getFieldType().getName()); 383 } 384 if (field.getCount() != 1) { 385 throw new ImageReadException("Field \"" + tag.name + "\" has wrong count " + field.getCount()); 386 } 387 final byte[] bytes = field.getByteArrayValue(); 388 return tag.getValue(field.getByteOrder(), bytes); 389 } 390 391 public short[] getFieldValue(final TagInfoShorts tag, final boolean mustExist) 392 throws ImageReadException { 393 final TiffField field = findField(tag); 394 if (field == null) { 395 if (mustExist) { 396 throw new ImageReadException("Required field \"" + tag.name 397 + "\" is missing"); 398 } else { 399 return null; 400 } 401 } 402 if (!tag.dataTypes.contains(field.getFieldType())) { 403 if (mustExist) { 404 throw new ImageReadException("Required field \"" + tag.name 405 + "\" has incorrect type " + field.getFieldType().getName()); 406 } else { 407 return null; 408 } 409 } 410 final byte[] bytes = field.getByteArrayValue(); 411 return tag.getValue(field.getByteOrder(), bytes); 412 } 413 414 public int getFieldValue(final TagInfoLong tag) 415 throws ImageReadException { 416 final TiffField field = findField(tag); 417 if (field == null) { 418 throw new ImageReadException("Required field \"" + tag.name 419 + "\" is missing"); 420 } 421 if (!tag.dataTypes.contains(field.getFieldType())) { 422 throw new ImageReadException("Required field \"" + tag.name 423 + "\" has incorrect type " + field.getFieldType().getName()); 424 } 425 if (field.getCount() != 1) { 426 throw new ImageReadException("Field \"" + tag.name + "\" has wrong count " + field.getCount()); 427 } 428 final byte[] bytes = field.getByteArrayValue(); 429 return tag.getValue(field.getByteOrder(), bytes); 430 } 431 432 public int[] getFieldValue(final TagInfoLongs tag, final boolean mustExist) 433 throws ImageReadException { 434 final TiffField field = findField(tag); 435 if (field == null) { 436 if (mustExist) { 437 throw new ImageReadException("Required field \"" + tag.name 438 + "\" is missing"); 439 } else { 440 return null; 441 } 442 } 443 if (!tag.dataTypes.contains(field.getFieldType())) { 444 if (mustExist) { 445 throw new ImageReadException("Required field \"" + tag.name 446 + "\" has incorrect type " + field.getFieldType().getName()); 447 } else { 448 return null; 449 } 450 } 451 final byte[] bytes = field.getByteArrayValue(); 452 return tag.getValue(field.getByteOrder(), bytes); 453 } 454 455 public int[] getFieldValue(final TagInfoShortOrLong tag, final boolean mustExist) 456 throws ImageReadException { 457 final TiffField field = findField(tag); 458 if (field == null) { 459 if (mustExist) { 460 throw new ImageReadException("Required field \"" + tag.name 461 + "\" is missing"); 462 } else { 463 return null; 464 } 465 } 466 if (!tag.dataTypes.contains(field.getFieldType())) { 467 if (mustExist) { 468 throw new ImageReadException("Required field \"" + tag.name 469 + "\" has incorrect type " + field.getFieldType().getName()); 470 } else { 471 return null; 472 } 473 } 474 final byte[] bytes = field.getByteArrayValue(); 475 if (field.getFieldType() == FieldType.SHORT) { 476 return ByteConversions.toUInt16s(bytes, field.getByteOrder()); 477 } else { 478 return ByteConversions.toInts(bytes, field.getByteOrder()); 479 } 480 } 481 482 public RationalNumber getFieldValue(final TagInfoRational tag) 483 throws ImageReadException { 484 final TiffField field = findField(tag); 485 if (field == null) { 486 throw new ImageReadException("Required field \"" + tag.name 487 + "\" is missing"); 488 } 489 if (!tag.dataTypes.contains(field.getFieldType())) { 490 throw new ImageReadException("Required field \"" + tag.name 491 + "\" has incorrect type " + field.getFieldType().getName()); 492 } 493 if (field.getCount() != 1) { 494 throw new ImageReadException("Field \"" + tag.name + "\" has wrong count " + field.getCount()); 495 } 496 final byte[] bytes = field.getByteArrayValue(); 497 return tag.getValue(field.getByteOrder(), bytes); 498 } 499 500 public RationalNumber[] getFieldValue(final TagInfoRationals tag, final boolean mustExist) 501 throws ImageReadException { 502 final TiffField field = findField(tag); 503 if (field == null) { 504 if (mustExist) { 505 throw new ImageReadException("Required field \"" + tag.name 506 + "\" is missing"); 507 } else { 508 return null; 509 } 510 } 511 if (!tag.dataTypes.contains(field.getFieldType())) { 512 if (mustExist) { 513 throw new ImageReadException("Required field \"" + tag.name 514 + "\" has incorrect type " + field.getFieldType().getName()); 515 } else { 516 return null; 517 } 518 } 519 final byte[] bytes = field.getByteArrayValue(); 520 return tag.getValue(field.getByteOrder(), bytes); 521 } 522 523 public byte getFieldValue(final TagInfoSByte tag) 524 throws ImageReadException { 525 final TiffField field = findField(tag); 526 if (field == null) { 527 throw new ImageReadException("Required field \"" + tag.name 528 + "\" is missing"); 529 } 530 if (!tag.dataTypes.contains(field.getFieldType())) { 531 throw new ImageReadException("Required field \"" + tag.name 532 + "\" has incorrect type " + field.getFieldType().getName()); 533 } 534 if (field.getCount() != 1) { 535 throw new ImageReadException("Field \"" + tag.name + "\" has wrong count " + field.getCount()); 536 } 537 return field.getByteArrayValue()[0]; 538 } 539 540 public byte[] getFieldValue(final TagInfoSBytes tag, final boolean mustExist) 541 throws ImageReadException { 542 final TiffField field = findField(tag); 543 if (field == null) { 544 if (mustExist) { 545 throw new ImageReadException("Required field \"" + tag.name 546 + "\" is missing"); 547 } else { 548 return null; 549 } 550 } 551 if (!tag.dataTypes.contains(field.getFieldType())) { 552 if (mustExist) { 553 throw new ImageReadException("Required field \"" + tag.name 554 + "\" has incorrect type " + field.getFieldType().getName()); 555 } else { 556 return null; 557 } 558 } 559 return field.getByteArrayValue(); 560 } 561 562 public short getFieldValue(final TagInfoSShort tag) 563 throws ImageReadException { 564 final TiffField field = findField(tag); 565 if (field == null) { 566 throw new ImageReadException("Required field \"" + tag.name 567 + "\" is missing"); 568 } 569 if (!tag.dataTypes.contains(field.getFieldType())) { 570 throw new ImageReadException("Required field \"" + tag.name 571 + "\" has incorrect type " + field.getFieldType().getName()); 572 } 573 if (field.getCount() != 1) { 574 throw new ImageReadException("Field \"" + tag.name + "\" has wrong count " + field.getCount()); 575 } 576 final byte[] bytes = field.getByteArrayValue(); 577 return tag.getValue(field.getByteOrder(), bytes); 578 } 579 580 public short[] getFieldValue(final TagInfoSShorts tag, final boolean mustExist) 581 throws ImageReadException { 582 final TiffField field = findField(tag); 583 if (field == null) { 584 if (mustExist) { 585 throw new ImageReadException("Required field \"" + tag.name 586 + "\" is missing"); 587 } else { 588 return null; 589 } 590 } 591 if (!tag.dataTypes.contains(field.getFieldType())) { 592 if (mustExist) { 593 throw new ImageReadException("Required field \"" + tag.name 594 + "\" has incorrect type " + field.getFieldType().getName()); 595 } else { 596 return null; 597 } 598 } 599 final byte[] bytes = field.getByteArrayValue(); 600 return tag.getValue(field.getByteOrder(), bytes); 601 } 602 603 public int getFieldValue(final TagInfoSLong tag) 604 throws ImageReadException { 605 final TiffField field = findField(tag); 606 if (field == null) { 607 throw new ImageReadException("Required field \"" + tag.name 608 + "\" is missing"); 609 } 610 if (!tag.dataTypes.contains(field.getFieldType())) { 611 throw new ImageReadException("Required field \"" + tag.name 612 + "\" has incorrect type " + field.getFieldType().getName()); 613 } 614 if (field.getCount() != 1) { 615 throw new ImageReadException("Field \"" + tag.name + "\" has wrong count " + field.getCount()); 616 } 617 final byte[] bytes = field.getByteArrayValue(); 618 return tag.getValue(field.getByteOrder(), bytes); 619 } 620 621 public int[] getFieldValue(final TagInfoSLongs tag, final boolean mustExist) 622 throws ImageReadException { 623 final TiffField field = findField(tag); 624 if (field == null) { 625 if (mustExist) { 626 throw new ImageReadException("Required field \"" + tag.name 627 + "\" is missing"); 628 } else { 629 return null; 630 } 631 } 632 if (!tag.dataTypes.contains(field.getFieldType())) { 633 if (mustExist) { 634 throw new ImageReadException("Required field \"" + tag.name 635 + "\" has incorrect type " + field.getFieldType().getName()); 636 } else { 637 return null; 638 } 639 } 640 final byte[] bytes = field.getByteArrayValue(); 641 return tag.getValue(field.getByteOrder(), bytes); 642 } 643 644 public RationalNumber getFieldValue(final TagInfoSRational tag) throws ImageReadException { 645 final TiffField field = findField(tag); 646 if (field == null) { 647 throw new ImageReadException("Required field \"" + tag.name 648 + "\" is missing"); 649 } 650 if (!tag.dataTypes.contains(field.getFieldType())) { 651 throw new ImageReadException("Required field \"" + tag.name 652 + "\" has incorrect type " + field.getFieldType().getName()); 653 } 654 if (field.getCount() != 1) { 655 throw new ImageReadException("Field \"" + tag.name + "\" has wrong count " + field.getCount()); 656 } 657 final byte[] bytes = field.getByteArrayValue(); 658 return tag.getValue(field.getByteOrder(), bytes); 659 } 660 661 public RationalNumber[] getFieldValue(final TagInfoSRationals tag, 662 final boolean mustExist) throws ImageReadException { 663 final TiffField field = findField(tag); 664 if (field == null) { 665 if (mustExist) { 666 throw new ImageReadException("Required field \"" + tag.name 667 + "\" is missing"); 668 } else { 669 return null; 670 } 671 } 672 if (!tag.dataTypes.contains(field.getFieldType())) { 673 if (mustExist) { 674 throw new ImageReadException("Required field \"" + tag.name 675 + "\" has incorrect type " + field.getFieldType().getName()); 676 } else { 677 return null; 678 } 679 } 680 final byte[] bytes = field.getByteArrayValue(); 681 return tag.getValue(field.getByteOrder(), bytes); 682 } 683 684 public float getFieldValue(final TagInfoFloat tag) 685 throws ImageReadException { 686 final TiffField field = findField(tag); 687 if (field == null) { 688 throw new ImageReadException("Required field \"" + tag.name 689 + "\" is missing"); 690 } 691 if (!tag.dataTypes.contains(field.getFieldType())) { 692 throw new ImageReadException("Required field \"" + tag.name 693 + "\" has incorrect type " + field.getFieldType().getName()); 694 } 695 if (field.getCount() != 1) { 696 throw new ImageReadException("Field \"" + tag.name + "\" has wrong count " + field.getCount()); 697 } 698 final byte[] bytes = field.getByteArrayValue(); 699 return tag.getValue(field.getByteOrder(), bytes); 700 } 701 702 public float[] getFieldValue(final TagInfoFloats tag, final boolean mustExist) 703 throws ImageReadException { 704 final TiffField field = findField(tag); 705 if (field == null) { 706 if (mustExist) { 707 throw new ImageReadException("Required field \"" + tag.name 708 + "\" is missing"); 709 } else { 710 return null; 711 } 712 } 713 if (!tag.dataTypes.contains(field.getFieldType())) { 714 if (mustExist) { 715 throw new ImageReadException("Required field \"" + tag.name 716 + "\" has incorrect type " + field.getFieldType().getName()); 717 } else { 718 return null; 719 } 720 } 721 final byte[] bytes = field.getByteArrayValue(); 722 return tag.getValue(field.getByteOrder(), bytes); 723 } 724 725 public double getFieldValue(final TagInfoDouble tag) 726 throws ImageReadException { 727 final TiffField field = findField(tag); 728 if (field == null) { 729 throw new ImageReadException("Required field \"" + tag.name 730 + "\" is missing"); 731 } 732 if (!tag.dataTypes.contains(field.getFieldType())) { 733 throw new ImageReadException("Required field \"" + tag.name 734 + "\" has incorrect type " + field.getFieldType().getName()); 735 } 736 if (field.getCount() != 1) { 737 throw new ImageReadException("Field \"" + tag.name + "\" has wrong count " + field.getCount()); 738 } 739 final byte[] bytes = field.getByteArrayValue(); 740 return tag.getValue(field.getByteOrder(), bytes); 741 } 742 743 public double[] getFieldValue(final TagInfoDoubles tag, final boolean mustExist) 744 throws ImageReadException { 745 final TiffField field = findField(tag); 746 if (field == null) { 747 if (mustExist) { 748 throw new ImageReadException("Required field \"" + tag.name 749 + "\" is missing"); 750 } else { 751 return null; 752 } 753 } 754 if (!tag.dataTypes.contains(field.getFieldType())) { 755 if (mustExist) { 756 throw new ImageReadException("Required field \"" + tag.name 757 + "\" has incorrect type " + field.getFieldType().getName()); 758 } else { 759 return null; 760 } 761 } 762 final byte[] bytes = field.getByteArrayValue(); 763 return tag.getValue(field.getByteOrder(), bytes); 764 } 765 766 public String getFieldValue(final TagInfoGpsText tag, final boolean mustExist) 767 throws ImageReadException { 768 final TiffField field = findField(tag); 769 if (field == null) { 770 if (mustExist) { 771 throw new ImageReadException("Required field \"" + tag.name 772 + "\" is missing"); 773 } else { 774 return null; 775 } 776 } 777 return tag.getValue(field); 778 } 779 780 public String getFieldValue(final TagInfoXpString tag, final boolean mustExist) 781 throws ImageReadException { 782 final TiffField field = findField(tag); 783 if (field == null) { 784 if (mustExist) { 785 throw new ImageReadException("Required field \"" + tag.name 786 + "\" is missing"); 787 } else { 788 return null; 789 } 790 } 791 return tag.getValue(field); 792 } 793 794 public static final class ImageDataElement extends TiffElement { 795 public ImageDataElement(final long offset, final int length) { 796 super(offset, length); 797 } 798 799 @Override 800 public String getElementDescription() { 801 return "ImageDataElement"; 802 } 803 } 804 805 private List<ImageDataElement> getRawImageDataElements( 806 final TiffField offsetsField, final TiffField byteCountsField) 807 throws ImageReadException { 808 final int[] offsets = offsetsField.getIntArrayValue(); 809 final int[] byteCounts = byteCountsField.getIntArrayValue(); 810 811 if (offsets.length != byteCounts.length) { 812 throw new ImageReadException("offsets.length(" + offsets.length 813 + ") != byteCounts.length(" + byteCounts.length + ")"); 814 } 815 816 final List<ImageDataElement> result = new ArrayList<>(offsets.length); 817 for (int i = 0; i < offsets.length; i++) { 818 result.add(new ImageDataElement(offsets[i], byteCounts[i])); 819 } 820 return result; 821 } 822 823 public List<ImageDataElement> getTiffRawImageDataElements() 824 throws ImageReadException { 825 final TiffField tileOffsets = findField(TiffTagConstants.TIFF_TAG_TILE_OFFSETS); 826 final TiffField tileByteCounts = findField(TiffTagConstants.TIFF_TAG_TILE_BYTE_COUNTS); 827 final TiffField stripOffsets = findField(TiffTagConstants.TIFF_TAG_STRIP_OFFSETS); 828 final TiffField stripByteCounts = findField(TiffTagConstants.TIFF_TAG_STRIP_BYTE_COUNTS); 829 830 if ((tileOffsets != null) && (tileByteCounts != null)) { 831 return getRawImageDataElements(tileOffsets, tileByteCounts); 832 } else if ((stripOffsets != null) && (stripByteCounts != null)) { 833 return getRawImageDataElements(stripOffsets, stripByteCounts); 834 } else { 835 throw new ImageReadException("Couldn't find image data."); 836 } 837 } 838 839 public boolean imageDataInStrips() throws ImageReadException { 840 final TiffField tileOffsets = findField(TiffTagConstants.TIFF_TAG_TILE_OFFSETS); 841 final TiffField tileByteCounts = findField(TiffTagConstants.TIFF_TAG_TILE_BYTE_COUNTS); 842 final TiffField stripOffsets = findField(TiffTagConstants.TIFF_TAG_STRIP_OFFSETS); 843 final TiffField stripByteCounts = findField(TiffTagConstants.TIFF_TAG_STRIP_BYTE_COUNTS); 844 845 if ((tileOffsets != null) && (tileByteCounts != null)) { 846 return false; 847 } else if ((stripOffsets != null) && (stripByteCounts != null)) { 848 return true; 849 } else { 850 throw new ImageReadException("Couldn't find image data."); 851 } 852 } 853 854 public ImageDataElement getJpegRawImageDataElement() throws ImageReadException { 855 final TiffField jpegInterchangeFormat = findField(TiffTagConstants.TIFF_TAG_JPEG_INTERCHANGE_FORMAT); 856 final TiffField jpegInterchangeFormatLength = findField(TiffTagConstants.TIFF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 857 858 if (jpegInterchangeFormat != null && jpegInterchangeFormatLength != null) { 859 final int offSet = jpegInterchangeFormat.getIntArrayValue()[0]; 860 final int byteCount = jpegInterchangeFormatLength.getIntArrayValue()[0]; 861 862 return new ImageDataElement(offSet, byteCount); 863 } else { 864 throw new ImageReadException("Couldn't find image data."); 865 } 866 } 867 868 public void setTiffImageData(final TiffImageData rawImageData) { 869 this.tiffImageData = rawImageData; 870 } 871 872 public TiffImageData getTiffImageData() { 873 return tiffImageData; 874 } 875 876 public void setJpegImageData(final JpegImageData value) { 877 this.jpegImageData = value; 878 } 879 880 public JpegImageData getJpegImageData() { 881 return jpegImageData; 882 } 883 884 /** 885 * Reads the floating-point data stored in this TIFF directory, if 886 * available. Note that this method is defined only for TIFF directories 887 * that contain floating-point data. 888 * <p> 889 * TIFF directories that provide floating-point data do not directly specify 890 * images, though it is possible to interpret the data as an image using 891 * this library. TIFF files may contain multiple directories which are 892 * allowed to have different formats. Thus it is possible for a TIFF file to 893 * contain a mix of image and floating-point raster data. 894 * <p> 895 * If desired, sub-image data can be read from the file by using a Java Map 896 * instance to specify the subsection of the image that is required. The 897 * following code illustrates the approach: 898 * <pre> 899 * int x; // coordinate (column) of corner of sub-image 900 * int y; // coordinate (row) of corner of sub-image 901 * int width; // width of sub-image 902 * int height; // height of sub-image 903 * 904 * Map<String, Object>params = new HashMap<>(); 905 * params.put(TiffConstants.PARAM_KEY_SUBIMAGE_X, x); 906 * params.put(TiffConstants.PARAM_KEY_SUBIMAGE_Y, y); 907 * params.put(TiffConstants.PARAM_KEY_SUBIMAGE_WIDTH, width); 908 * params.put(TiffConstants.PARAM_KEY_SUBIMAGE_HEIGHT, height); 909 * TiffRasterData raster = 910 * directory.readFloatingPointRasterData(params); 911 * </pre> 912 913 * @param params an optional parameter map instance 914 * @return a valid instance 915 * @throws ImageReadException in the event of incompatible or malformed data 916 * @throws IOException in the event of an I/O error 917 */ 918 public TiffRasterData getFloatingPointRasterData( 919 final Map<String, Object> params) 920 throws ImageReadException, IOException { 921 922 TiffImageParser parser = new TiffImageParser(); 923 return parser.getFloatingPointRasterData(this, headerByteOrder, params); 924 } 925 926 /** 927 * Indicates whether the directory definition specifies a float-point data 928 * format. 929 * 930 * @return true if the directory contains floating point data; otherwise, 931 * false 932 * @throws ImageReadException in the event of an invalid or malformed 933 * specification. 934 */ 935 public boolean hasTiffFloatingPointRasterData() throws ImageReadException { 936 if (this.hasTiffImageData()) { 937 short[] sSampleFmt = getFieldValue( 938 TiffTagConstants.TIFF_TAG_SAMPLE_FORMAT, false); 939 return sSampleFmt != null && sSampleFmt.length > 0 940 && sSampleFmt[0] == TiffTagConstants.SAMPLE_FORMAT_VALUE_IEEE_FLOATING_POINT; 941 942 } 943 return false; 944 } 945}