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