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