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.write; 018 019import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_DIRECTORY_FOOTER_LENGTH; 020import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_DIRECTORY_HEADER_LENGTH; 021import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_ENTRY_LENGTH; 022import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_ENTRY_MAX_VALUE_LENGTH; 023 024import java.io.IOException; 025import java.nio.ByteOrder; 026import java.util.ArrayList; 027import java.util.Arrays; 028import java.util.Collections; 029import java.util.Comparator; 030import java.util.Iterator; 031import java.util.List; 032 033import org.apache.commons.imaging.ImagingException; 034import org.apache.commons.imaging.common.Allocator; 035import org.apache.commons.imaging.common.BinaryOutputStream; 036import org.apache.commons.imaging.common.RationalNumber; 037import org.apache.commons.imaging.formats.tiff.AbstractTiffElement; 038import org.apache.commons.imaging.formats.tiff.AbstractTiffImageData; 039import org.apache.commons.imaging.formats.tiff.JpegImageData; 040import org.apache.commons.imaging.formats.tiff.TiffDirectory; 041import org.apache.commons.imaging.formats.tiff.constants.TiffDirectoryType; 042import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants; 043import org.apache.commons.imaging.formats.tiff.fieldtypes.AbstractFieldType; 044import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo; 045import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoAscii; 046import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoAsciiOrByte; 047import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoAsciiOrRational; 048import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoByte; 049import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoByteOrShort; 050import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoBytes; 051import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoDouble; 052import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoDoubles; 053import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoFloat; 054import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoFloats; 055import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoGpsText; 056import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoLong; 057import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoLongs; 058import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoRational; 059import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoRationals; 060import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSByte; 061import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSBytes; 062import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSLong; 063import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSLongs; 064import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSRational; 065import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSRationals; 066import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSShort; 067import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSShorts; 068import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoShort; 069import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoShortOrLong; 070import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoShortOrLongOrRational; 071import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoShortOrRational; 072import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoShorts; 073import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoXpString; 074 075public final class TiffOutputDirectory extends AbstractTiffOutputItem implements Iterable<TiffOutputField> { 076 public static final Comparator<TiffOutputDirectory> COMPARATOR = Comparator.comparingInt(TiffOutputDirectory::getType); 077 private final int type; 078 private final List<TiffOutputField> fields = new ArrayList<>(); 079 private final ByteOrder byteOrder; 080 private TiffOutputDirectory nextDirectory; 081 private JpegImageData jpegImageData; 082 private AbstractTiffImageData abstractTiffImageData; 083 084 public TiffOutputDirectory(final int type, final ByteOrder byteOrder) { 085 this.type = type; 086 this.byteOrder = byteOrder; 087 } 088 089 public void add(final TagInfoAscii tagInfo, final String... values) throws ImagingException { 090 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 091 if (tagInfo.length > 0 && tagInfo.length != bytes.length) { 092 throw new ImagingException("Tag expects " + tagInfo.length + " byte(s), not " + values.length); 093 } 094 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.ASCII, bytes.length, bytes); 095 add(tiffOutputField); 096 } 097 098 public void add(final TagInfoAsciiOrByte tagInfo, final String... values) throws ImagingException { 099 final byte[] bytes = tagInfo.encodeValue(AbstractFieldType.ASCII, values, byteOrder); 100 if (tagInfo.length > 0 && tagInfo.length != bytes.length) { 101 throw new ImagingException("Tag expects " + tagInfo.length + " byte(s), not " + values.length); 102 } 103 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.ASCII, bytes.length, bytes); 104 add(tiffOutputField); 105 } 106 107 public void add(final TagInfoAsciiOrRational tagInfo, final RationalNumber... values) throws ImagingException { 108 if (tagInfo.length > 0 && tagInfo.length != values.length) { 109 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 110 } 111 final byte[] bytes = tagInfo.encodeValue(AbstractFieldType.RATIONAL, values, byteOrder); 112 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.RATIONAL, bytes.length, bytes); 113 add(tiffOutputField); 114 } 115 116 public void add(final TagInfoAsciiOrRational tagInfo, final String... values) throws ImagingException { 117 final byte[] bytes = tagInfo.encodeValue(AbstractFieldType.ASCII, values, byteOrder); 118 if (tagInfo.length > 0 && tagInfo.length != bytes.length) { 119 throw new ImagingException("Tag expects " + tagInfo.length + " byte(s), not " + values.length); 120 } 121 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.ASCII, bytes.length, bytes); 122 add(tiffOutputField); 123 } 124 125 public void add(final TagInfoByte tagInfo, final byte value) throws ImagingException { 126 if (tagInfo.length != 1) { 127 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1"); 128 } 129 final byte[] bytes = tagInfo.encodeValue(byteOrder, value); 130 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.BYTE, bytes.length, bytes); 131 add(tiffOutputField); 132 } 133 134 public void add(final TagInfoByteOrShort tagInfo, final byte... values) throws ImagingException { 135 if (tagInfo.length > 0 && tagInfo.length != values.length) { 136 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 137 } 138 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 139 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.BYTE, values.length, bytes); 140 add(tiffOutputField); 141 } 142 143 public void add(final TagInfoByteOrShort tagInfo, final short... values) throws ImagingException { 144 if (tagInfo.length > 0 && tagInfo.length != values.length) { 145 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 146 } 147 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 148 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SHORT, values.length, bytes); 149 add(tiffOutputField); 150 } 151 152 public void add(final TagInfoBytes tagInfo, final byte... values) throws ImagingException { 153 if (tagInfo.length > 0 && tagInfo.length != values.length) { 154 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 155 } 156 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 157 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.BYTE, values.length, bytes); 158 add(tiffOutputField); 159 } 160 161 public void add(final TagInfoDouble tagInfo, final double value) throws ImagingException { 162 if (tagInfo.length != 1) { 163 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1"); 164 } 165 final byte[] bytes = tagInfo.encodeValue(byteOrder, value); 166 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.DOUBLE, 1, bytes); 167 add(tiffOutputField); 168 } 169 170 public void add(final TagInfoDoubles tagInfo, final double... values) throws ImagingException { 171 if (tagInfo.length > 0 && tagInfo.length != values.length) { 172 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 173 } 174 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 175 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.DOUBLE, values.length, bytes); 176 add(tiffOutputField); 177 } 178 179 public void add(final TagInfoFloat tagInfo, final float value) throws ImagingException { 180 if (tagInfo.length != 1) { 181 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1"); 182 } 183 final byte[] bytes = tagInfo.encodeValue(byteOrder, value); 184 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.FLOAT, 1, bytes); 185 add(tiffOutputField); 186 } 187 188 public void add(final TagInfoFloats tagInfo, final float... values) throws ImagingException { 189 if (tagInfo.length > 0 && tagInfo.length != values.length) { 190 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 191 } 192 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 193 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.FLOAT, values.length, bytes); 194 add(tiffOutputField); 195 } 196 197 public void add(final TagInfoGpsText tagInfo, final String value) throws ImagingException { 198 final byte[] bytes = tagInfo.encodeValue(AbstractFieldType.UNDEFINED, value, byteOrder); 199 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, tagInfo.dataTypes.get(0), bytes.length, bytes); 200 add(tiffOutputField); 201 } 202 203 public void add(final TagInfoLong tagInfo, final int value) throws ImagingException { 204 if (tagInfo.length != 1) { 205 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1"); 206 } 207 final byte[] bytes = tagInfo.encodeValue(byteOrder, value); 208 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.LONG, 1, bytes); 209 add(tiffOutputField); 210 } 211 212 public void add(final TagInfoLongs tagInfo, final int... values) throws ImagingException { 213 if (tagInfo.length > 0 && tagInfo.length != values.length) { 214 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 215 } 216 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 217 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.LONG, values.length, bytes); 218 add(tiffOutputField); 219 } 220 221 public void add(final TagInfoRational tagInfo, final RationalNumber value) throws ImagingException { 222 if (tagInfo.length != 1) { 223 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1"); 224 } 225 final byte[] bytes = tagInfo.encodeValue(byteOrder, value); 226 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.RATIONAL, 1, bytes); 227 add(tiffOutputField); 228 } 229 230 public void add(final TagInfoRationals tagInfo, final RationalNumber... values) throws ImagingException { 231 if (tagInfo.length > 0 && tagInfo.length != values.length) { 232 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 233 } 234 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 235 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.RATIONAL, values.length, bytes); 236 add(tiffOutputField); 237 } 238 239 public void add(final TagInfoSByte tagInfo, final byte value) throws ImagingException { 240 if (tagInfo.length != 1) { 241 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1"); 242 } 243 final byte[] bytes = tagInfo.encodeValue(byteOrder, value); 244 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SBYTE, 1, bytes); 245 add(tiffOutputField); 246 } 247 248 public void add(final TagInfoSBytes tagInfo, final byte... values) throws ImagingException { 249 if (tagInfo.length > 0 && tagInfo.length != values.length) { 250 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 251 } 252 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 253 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SBYTE, values.length, bytes); 254 add(tiffOutputField); 255 } 256 257 public void add(final TagInfoShort tagInfo, final short value) throws ImagingException { 258 if (tagInfo.length != 1) { 259 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1"); 260 } 261 final byte[] bytes = tagInfo.encodeValue(byteOrder, value); 262 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SHORT, 1, bytes); 263 add(tiffOutputField); 264 } 265 266 public void add(final TagInfoShortOrLong tagInfo, final int... values) throws ImagingException { 267 if (tagInfo.length > 0 && tagInfo.length != values.length) { 268 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 269 } 270 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 271 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.LONG, values.length, bytes); 272 add(tiffOutputField); 273 } 274 275 public void add(final TagInfoShortOrLong tagInfo, final short... values) throws ImagingException { 276 if (tagInfo.length > 0 && tagInfo.length != values.length) { 277 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 278 } 279 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 280 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SHORT, values.length, bytes); 281 add(tiffOutputField); 282 } 283 284 public void add(final TagInfoShortOrLongOrRational tagInfo, final int... values) throws ImagingException { 285 if (tagInfo.length > 0 && tagInfo.length != values.length) { 286 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 287 } 288 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 289 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.LONG, values.length, bytes); 290 add(tiffOutputField); 291 } 292 293 public void add(final TagInfoShortOrLongOrRational tagInfo, final RationalNumber... values) throws ImagingException { 294 if (tagInfo.length > 0 && tagInfo.length != values.length) { 295 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 296 } 297 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 298 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.RATIONAL, values.length, bytes); 299 add(tiffOutputField); 300 } 301 302 public void add(final TagInfoShortOrLongOrRational tagInfo, final short... values) throws ImagingException { 303 if (tagInfo.length > 0 && tagInfo.length != values.length) { 304 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 305 } 306 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 307 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SHORT, values.length, bytes); 308 add(tiffOutputField); 309 } 310 311 public void add(final TagInfoShortOrRational tagInfo, final RationalNumber... values) throws ImagingException { 312 if (tagInfo.length > 0 && tagInfo.length != values.length) { 313 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 314 } 315 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 316 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.RATIONAL, values.length, bytes); 317 add(tiffOutputField); 318 } 319 320 public void add(final TagInfoShortOrRational tagInfo, final short... values) throws ImagingException { 321 if (tagInfo.length > 0 && tagInfo.length != values.length) { 322 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 323 } 324 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 325 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SHORT, values.length, bytes); 326 add(tiffOutputField); 327 } 328 329 public void add(final TagInfoShorts tagInfo, final short... values) throws ImagingException { 330 if (tagInfo.length > 0 && tagInfo.length != values.length) { 331 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 332 } 333 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 334 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SHORT, values.length, bytes); 335 add(tiffOutputField); 336 } 337 338 public void add(final TagInfoSLong tagInfo, final int value) throws ImagingException { 339 if (tagInfo.length != 1) { 340 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1"); 341 } 342 final byte[] bytes = tagInfo.encodeValue(byteOrder, value); 343 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SLONG, 1, bytes); 344 add(tiffOutputField); 345 } 346 347 public void add(final TagInfoSLongs tagInfo, final int... values) throws ImagingException { 348 if (tagInfo.length > 0 && tagInfo.length != values.length) { 349 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 350 } 351 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 352 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SLONG, values.length, bytes); 353 add(tiffOutputField); 354 } 355 356 public void add(final TagInfoSRational tagInfo, final RationalNumber value) throws ImagingException { 357 if (tagInfo.length != 1) { 358 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1"); 359 } 360 final byte[] bytes = tagInfo.encodeValue(byteOrder, value); 361 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SRATIONAL, 1, bytes); 362 add(tiffOutputField); 363 } 364 365 public void add(final TagInfoSRationals tagInfo, final RationalNumber... values) throws ImagingException { 366 if (tagInfo.length > 0 && tagInfo.length != values.length) { 367 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 368 } 369 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 370 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SRATIONAL, values.length, bytes); 371 add(tiffOutputField); 372 } 373 374 public void add(final TagInfoSShort tagInfo, final short value) throws ImagingException { 375 if (tagInfo.length != 1) { 376 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1"); 377 } 378 final byte[] bytes = tagInfo.encodeValue(byteOrder, value); 379 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SSHORT, 1, bytes); 380 add(tiffOutputField); 381 } 382 383 public void add(final TagInfoSShorts tagInfo, final short... values) throws ImagingException { 384 if (tagInfo.length > 0 && tagInfo.length != values.length) { 385 throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length); 386 } 387 final byte[] bytes = tagInfo.encodeValue(byteOrder, values); 388 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SSHORT, values.length, bytes); 389 add(tiffOutputField); 390 } 391 392 public void add(final TagInfoXpString tagInfo, final String value) throws ImagingException { 393 final byte[] bytes = tagInfo.encodeValue(AbstractFieldType.BYTE, value, byteOrder); 394 final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.BYTE, bytes.length, bytes); 395 add(tiffOutputField); 396 } 397 398 public void add(final TiffOutputField field) { 399 fields.add(field); 400 } 401 402 public String description() { 403 return TiffDirectory.description(getType()); 404 } 405 406 /** 407 * Finds the TiffOutputField for the given tag from this TiffOutputDirectory. 408 * 409 * <p> 410 * If there is no field matching the given tag, null will be returned. 411 * </p> 412 * 413 * @param tag the tag specifying the field 414 * @return the field matching tagInfo or null, if the field isn't present 415 * @see #findField(TagInfo) 416 */ 417 public TiffOutputField findField(final int tag) { 418 for (final TiffOutputField field : fields) { 419 if (field.tag == tag) { 420 return field; 421 } 422 } 423 return null; 424 } 425 426 /** 427 * Finds the TiffOutputField for the given TagInfo from this TiffOutputDirectory. 428 * 429 * <p> 430 * If there is no field matching the given TagInfo, null will be returned. 431 * </p> 432 * 433 * @param tagInfo the TagInfo specifying the field 434 * @return the field matching tagInfo or null, if the field isn't present 435 * @see #findField(int) 436 */ 437 public TiffOutputField findField(final TagInfo tagInfo) { 438 return findField(tagInfo.tag); 439 } 440 441 public List<TiffOutputField> getFields() { 442 return new ArrayList<>(fields); 443 } 444 445 @Override 446 public String getItemDescription() { 447 final TiffDirectoryType dirType = TiffDirectoryType.getExifDirectoryType(getType()); 448 return "Directory: " + dirType.name + " (" + getType() + ")"; 449 } 450 451 @Override 452 public int getItemLength() { 453 return TIFF_ENTRY_LENGTH * fields.size() + TIFF_DIRECTORY_HEADER_LENGTH + TIFF_DIRECTORY_FOOTER_LENGTH; 454 } 455 456 protected List<AbstractTiffOutputItem> getOutputItems(final TiffOutputSummary outputSummary) throws ImagingException { 457 // first validate directory fields. 458 459 removeFieldIfPresent(TiffTagConstants.TIFF_TAG_JPEG_INTERCHANGE_FORMAT); 460 removeFieldIfPresent(TiffTagConstants.TIFF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 461 462 TiffOutputField jpegOffsetField = null; 463 if (null != jpegImageData) { 464 jpegOffsetField = new TiffOutputField(TiffTagConstants.TIFF_TAG_JPEG_INTERCHANGE_FORMAT, AbstractFieldType.LONG, 1, 465 new byte[TIFF_ENTRY_MAX_VALUE_LENGTH]); 466 add(jpegOffsetField); 467 468 final byte[] lengthValue = AbstractFieldType.LONG.writeData(jpegImageData.length, outputSummary.byteOrder); 469 470 final TiffOutputField jpegLengthField = new TiffOutputField(TiffTagConstants.TIFF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, AbstractFieldType.LONG, 1, 471 lengthValue); 472 add(jpegLengthField); 473 474 } 475 476 removeFieldIfPresent(TiffTagConstants.TIFF_TAG_STRIP_OFFSETS); 477 removeFieldIfPresent(TiffTagConstants.TIFF_TAG_STRIP_BYTE_COUNTS); 478 removeFieldIfPresent(TiffTagConstants.TIFF_TAG_TILE_OFFSETS); 479 removeFieldIfPresent(TiffTagConstants.TIFF_TAG_TILE_BYTE_COUNTS); 480 481 TiffOutputField imageDataOffsetField; 482 ImageDataOffsets imageDataInfo = null; 483 if (null != abstractTiffImageData) { 484 final boolean stripsNotTiles = abstractTiffImageData.stripsNotTiles(); 485 486 TagInfo offsetTag; 487 TagInfo byteCountsTag; 488 if (stripsNotTiles) { 489 offsetTag = TiffTagConstants.TIFF_TAG_STRIP_OFFSETS; 490 byteCountsTag = TiffTagConstants.TIFF_TAG_STRIP_BYTE_COUNTS; 491 } else { 492 offsetTag = TiffTagConstants.TIFF_TAG_TILE_OFFSETS; 493 byteCountsTag = TiffTagConstants.TIFF_TAG_TILE_BYTE_COUNTS; 494 } 495 496 final AbstractTiffElement.DataElement[] imageData = abstractTiffImageData.getImageData(); 497 498 // TiffOutputField imageDataOffsetsField = null; 499 500 final int[] imageDataOffsets = Allocator.intArray(imageData.length); 501 final int[] imageDataByteCounts = Allocator.intArray(imageData.length); 502 Arrays.setAll(imageDataByteCounts, i -> imageData[i].length); 503 504 // Append imageData-related fields to first directory 505 imageDataOffsetField = new TiffOutputField(offsetTag, AbstractFieldType.LONG, imageDataOffsets.length, 506 AbstractFieldType.LONG.writeData(imageDataOffsets, outputSummary.byteOrder)); 507 add(imageDataOffsetField); 508 509 final byte[] data = AbstractFieldType.LONG.writeData(imageDataByteCounts, outputSummary.byteOrder); 510 final TiffOutputField byteCountsField = new TiffOutputField(byteCountsTag, AbstractFieldType.LONG, imageDataByteCounts.length, data); 511 add(byteCountsField); 512 513 imageDataInfo = new ImageDataOffsets(imageData, imageDataOffsets, imageDataOffsetField); 514 } 515 516 final List<AbstractTiffOutputItem> result = new ArrayList<>(); 517 result.add(this); 518 sortFields(); 519 520 for (final TiffOutputField field : fields) { 521 if (field.isLocalValue()) { 522 continue; 523 } 524 525 final AbstractTiffOutputItem item = field.getSeperateValue(); 526 result.add(item); 527 // outputSummary.add(item, field); 528 } 529 530 if (null != imageDataInfo) { 531 Collections.addAll(result, imageDataInfo.outputItems); 532 533 outputSummary.addTiffImageData(imageDataInfo); 534 } 535 536 if (null != jpegImageData) { 537 final AbstractTiffOutputItem item = new AbstractTiffOutputItem.Value("JPEG image data", jpegImageData.getData()); 538 result.add(item); 539 outputSummary.add(item, jpegOffsetField); 540 } 541 542 return result; 543 } 544 545 public JpegImageData getRawJpegImageData() { 546 return jpegImageData; 547 } 548 549 public AbstractTiffImageData getRawTiffImageData() { 550 return abstractTiffImageData; 551 } 552 553 public int getType() { 554 return type; 555 } 556 557 @Override 558 public Iterator<TiffOutputField> iterator() { 559 return fields.iterator(); 560 } 561 562 public void removeField(final int tag) { 563 final List<TiffOutputField> matches = new ArrayList<>(); 564 for (final TiffOutputField field : fields) { 565 if (field.tag == tag) { 566 matches.add(field); 567 } 568 } 569 fields.removeAll(matches); 570 } 571 572 public void removeField(final TagInfo tagInfo) { 573 removeField(tagInfo.tag); 574 } 575 576 private void removeFieldIfPresent(final TagInfo tagInfo) { 577 final TiffOutputField field = findField(tagInfo); 578 if (null != field) { 579 fields.remove(field); 580 } 581 } 582 583 public void setJpegImageData(final JpegImageData rawJpegImageData) { 584 this.jpegImageData = rawJpegImageData; 585 } 586 587 public void setNextDirectory(final TiffOutputDirectory nextDirectory) { 588 this.nextDirectory = nextDirectory; 589 } 590 591 public void setTiffImageData(final AbstractTiffImageData rawTiffImageData) { 592 this.abstractTiffImageData = rawTiffImageData; 593 } 594 595 public void sortFields() { 596 final Comparator<TiffOutputField> comparator = (e1, e2) -> { 597 if (e1.tag != e2.tag) { 598 return e1.tag - e2.tag; 599 } 600 return e1.getSortHint() - e2.getSortHint(); 601 }; 602 fields.sort(comparator); 603 } 604 605 @Override 606 public void writeItem(final BinaryOutputStream bos) throws IOException, ImagingException { 607 // Write Directory Field Count 608 bos.write2Bytes(fields.size()); // DirectoryFieldCount 609 610 // Write Fields 611 for (final TiffOutputField field : fields) { 612 field.writeField(bos); 613 614 // Debug.debug("\t" + "writing field (" + field.tag + ", 0x" + 615 // Integer.toHexString(field.tag) + ")", field.tagInfo); 616 // if (field.tagInfo.isOffset()) 617 // Debug.debug("\t\tOFFSET!", field.bytes); 618 } 619 620 long nextDirectoryOffset = 0; 621 if (nextDirectory != null) { 622 nextDirectoryOffset = nextDirectory.getOffset(); 623 } 624 625 // Write nextDirectoryOffset 626 if (nextDirectoryOffset == UNDEFINED_VALUE) { 627 bos.write4Bytes(0); 628 } else { 629 bos.write4Bytes((int) nextDirectoryOffset); 630 } 631 } 632}