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