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