View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.imaging.formats.tiff.write;
18  
19  import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_DIRECTORY_FOOTER_LENGTH;
20  import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_DIRECTORY_HEADER_LENGTH;
21  import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_ENTRY_LENGTH;
22  import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_ENTRY_MAX_VALUE_LENGTH;
23  
24  import java.io.IOException;
25  import java.nio.ByteOrder;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Collections;
29  import java.util.Comparator;
30  import java.util.Iterator;
31  import java.util.List;
32  
33  import org.apache.commons.imaging.ImagingException;
34  import org.apache.commons.imaging.common.Allocator;
35  import org.apache.commons.imaging.common.BinaryOutputStream;
36  import org.apache.commons.imaging.common.RationalNumber;
37  import org.apache.commons.imaging.formats.tiff.AbstractTiffElement;
38  import org.apache.commons.imaging.formats.tiff.AbstractTiffImageData;
39  import org.apache.commons.imaging.formats.tiff.JpegImageData;
40  import org.apache.commons.imaging.formats.tiff.TiffDirectory;
41  import org.apache.commons.imaging.formats.tiff.constants.TiffDirectoryType;
42  import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants;
43  import org.apache.commons.imaging.formats.tiff.fieldtypes.AbstractFieldType;
44  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo;
45  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoAscii;
46  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoAsciiOrByte;
47  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoAsciiOrRational;
48  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoByte;
49  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoByteOrShort;
50  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoBytes;
51  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoDouble;
52  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoDoubles;
53  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoFloat;
54  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoFloats;
55  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoGpsText;
56  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoLong;
57  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoLongs;
58  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoRational;
59  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoRationals;
60  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSByte;
61  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSBytes;
62  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSLong;
63  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSLongs;
64  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSRational;
65  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSRationals;
66  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSShort;
67  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSShorts;
68  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoShort;
69  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoShortOrLong;
70  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoShortOrLongOrRational;
71  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoShortOrRational;
72  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoShorts;
73  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoXpString;
74  
75  public final class TiffOutputDirectory extends AbstractTiffOutputItem implements Iterable<TiffOutputField> {
76      public static final Comparator<TiffOutputDirectory> COMPARATOR = Comparator.comparingInt(TiffOutputDirectory::getType);
77      private final int type;
78      private final List<TiffOutputField> fields = new ArrayList<>();
79      private final ByteOrder byteOrder;
80      private TiffOutputDirectory nextDirectory;
81      private JpegImageData jpegImageData;
82      private AbstractTiffImageData abstractTiffImageData;
83  
84      public TiffOutputDirectory(final int type, final ByteOrder byteOrder) {
85          this.type = type;
86          this.byteOrder = byteOrder;
87      }
88  
89      public void add(final TagInfoAscii tagInfo, final String... values) throws ImagingException {
90          final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
91          if (tagInfo.length > 0 && tagInfo.length != bytes.length) {
92              throw new ImagingException("Tag expects " + tagInfo.length + " byte(s), not " + values.length);
93          }
94          final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.ASCII, bytes.length, bytes);
95          add(tiffOutputField);
96      }
97  
98      public void add(final TagInfoAsciiOrByte tagInfo, final String... values) throws ImagingException {
99          final byte[] bytes = tagInfo.encodeValue(AbstractFieldType.ASCII, values, byteOrder);
100         if (tagInfo.length > 0 && tagInfo.length != bytes.length) {
101             throw new ImagingException("Tag expects " + tagInfo.length + " byte(s), not " + values.length);
102         }
103         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.ASCII, bytes.length, bytes);
104         add(tiffOutputField);
105     }
106 
107     public void add(final TagInfoAsciiOrRational tagInfo, final RationalNumber... values) throws ImagingException {
108         if (tagInfo.length > 0 && tagInfo.length != values.length) {
109             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
110         }
111         final byte[] bytes = tagInfo.encodeValue(AbstractFieldType.RATIONAL, values, byteOrder);
112         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.RATIONAL, bytes.length, bytes);
113         add(tiffOutputField);
114     }
115 
116     public void add(final TagInfoAsciiOrRational tagInfo, final String... values) throws ImagingException {
117         final byte[] bytes = tagInfo.encodeValue(AbstractFieldType.ASCII, values, byteOrder);
118         if (tagInfo.length > 0 && tagInfo.length != bytes.length) {
119             throw new ImagingException("Tag expects " + tagInfo.length + " byte(s), not " + values.length);
120         }
121         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.ASCII, bytes.length, bytes);
122         add(tiffOutputField);
123     }
124 
125     public void add(final TagInfoByte tagInfo, final byte value) throws ImagingException {
126         if (tagInfo.length != 1) {
127             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1");
128         }
129         final byte[] bytes = tagInfo.encodeValue(byteOrder, value);
130         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.BYTE, bytes.length, bytes);
131         add(tiffOutputField);
132     }
133 
134     public void add(final TagInfoByteOrShort tagInfo, final byte... values) throws ImagingException {
135         if (tagInfo.length > 0 && tagInfo.length != values.length) {
136             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
137         }
138         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
139         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.BYTE, values.length, bytes);
140         add(tiffOutputField);
141     }
142 
143     public void add(final TagInfoByteOrShort tagInfo, final short... values) throws ImagingException {
144         if (tagInfo.length > 0 && tagInfo.length != values.length) {
145             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
146         }
147         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
148         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SHORT, values.length, bytes);
149         add(tiffOutputField);
150     }
151 
152     public void add(final TagInfoBytes tagInfo, final byte... values) throws ImagingException {
153         if (tagInfo.length > 0 && tagInfo.length != values.length) {
154             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
155         }
156         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
157         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.BYTE, values.length, bytes);
158         add(tiffOutputField);
159     }
160 
161     public void add(final TagInfoDouble tagInfo, final double value) throws ImagingException {
162         if (tagInfo.length != 1) {
163             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1");
164         }
165         final byte[] bytes = tagInfo.encodeValue(byteOrder, value);
166         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.DOUBLE, 1, bytes);
167         add(tiffOutputField);
168     }
169 
170     public void add(final TagInfoDoubles tagInfo, final double... values) throws ImagingException {
171         if (tagInfo.length > 0 && tagInfo.length != values.length) {
172             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
173         }
174         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
175         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.DOUBLE, values.length, bytes);
176         add(tiffOutputField);
177     }
178 
179     public void add(final TagInfoFloat tagInfo, final float value) throws ImagingException {
180         if (tagInfo.length != 1) {
181             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1");
182         }
183         final byte[] bytes = tagInfo.encodeValue(byteOrder, value);
184         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.FLOAT, 1, bytes);
185         add(tiffOutputField);
186     }
187 
188     public void add(final TagInfoFloats tagInfo, final float... values) throws ImagingException {
189         if (tagInfo.length > 0 && tagInfo.length != values.length) {
190             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
191         }
192         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
193         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.FLOAT, values.length, bytes);
194         add(tiffOutputField);
195     }
196 
197     public void add(final TagInfoGpsText tagInfo, final String value) throws ImagingException {
198         final byte[] bytes = tagInfo.encodeValue(AbstractFieldType.UNDEFINED, value, byteOrder);
199         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, tagInfo.dataTypes.get(0), bytes.length, bytes);
200         add(tiffOutputField);
201     }
202 
203     public void add(final TagInfoLong tagInfo, final int value) throws ImagingException {
204         if (tagInfo.length != 1) {
205             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1");
206         }
207         final byte[] bytes = tagInfo.encodeValue(byteOrder, value);
208         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.LONG, 1, bytes);
209         add(tiffOutputField);
210     }
211 
212     public void add(final TagInfoLongs tagInfo, final int... values) throws ImagingException {
213         if (tagInfo.length > 0 && tagInfo.length != values.length) {
214             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
215         }
216         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
217         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.LONG, values.length, bytes);
218         add(tiffOutputField);
219     }
220 
221     public void add(final TagInfoRational tagInfo, final RationalNumber value) throws ImagingException {
222         if (tagInfo.length != 1) {
223             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1");
224         }
225         final byte[] bytes = tagInfo.encodeValue(byteOrder, value);
226         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.RATIONAL, 1, bytes);
227         add(tiffOutputField);
228     }
229 
230     public void add(final TagInfoRationals tagInfo, final RationalNumber... values) throws ImagingException {
231         if (tagInfo.length > 0 && tagInfo.length != values.length) {
232             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
233         }
234         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
235         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.RATIONAL, values.length, bytes);
236         add(tiffOutputField);
237     }
238 
239     public void add(final TagInfoSByte tagInfo, final byte value) throws ImagingException {
240         if (tagInfo.length != 1) {
241             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1");
242         }
243         final byte[] bytes = tagInfo.encodeValue(byteOrder, value);
244         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SBYTE, 1, bytes);
245         add(tiffOutputField);
246     }
247 
248     public void add(final TagInfoSBytes tagInfo, final byte... values) throws ImagingException {
249         if (tagInfo.length > 0 && tagInfo.length != values.length) {
250             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
251         }
252         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
253         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SBYTE, values.length, bytes);
254         add(tiffOutputField);
255     }
256 
257     public void add(final TagInfoShort tagInfo, final short value) throws ImagingException {
258         if (tagInfo.length != 1) {
259             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1");
260         }
261         final byte[] bytes = tagInfo.encodeValue(byteOrder, value);
262         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SHORT, 1, bytes);
263         add(tiffOutputField);
264     }
265 
266     public void add(final TagInfoShortOrLong tagInfo, final int... values) throws ImagingException {
267         if (tagInfo.length > 0 && tagInfo.length != values.length) {
268             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
269         }
270         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
271         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.LONG, values.length, bytes);
272         add(tiffOutputField);
273     }
274 
275     public void add(final TagInfoShortOrLong tagInfo, final short... values) throws ImagingException {
276         if (tagInfo.length > 0 && tagInfo.length != values.length) {
277             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
278         }
279         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
280         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SHORT, values.length, bytes);
281         add(tiffOutputField);
282     }
283 
284     public void add(final TagInfoShortOrLongOrRational tagInfo, final int... values) throws ImagingException {
285         if (tagInfo.length > 0 && tagInfo.length != values.length) {
286             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
287         }
288         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
289         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.LONG, values.length, bytes);
290         add(tiffOutputField);
291     }
292 
293     public void add(final TagInfoShortOrLongOrRational tagInfo, final RationalNumber... values) throws ImagingException {
294         if (tagInfo.length > 0 && tagInfo.length != values.length) {
295             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
296         }
297         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
298         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.RATIONAL, values.length, bytes);
299         add(tiffOutputField);
300     }
301 
302     public void add(final TagInfoShortOrLongOrRational tagInfo, final short... values) throws ImagingException {
303         if (tagInfo.length > 0 && tagInfo.length != values.length) {
304             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
305         }
306         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
307         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SHORT, values.length, bytes);
308         add(tiffOutputField);
309     }
310 
311     public void add(final TagInfoShortOrRational tagInfo, final RationalNumber... values) throws ImagingException {
312         if (tagInfo.length > 0 && tagInfo.length != values.length) {
313             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
314         }
315         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
316         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.RATIONAL, values.length, bytes);
317         add(tiffOutputField);
318     }
319 
320     public void add(final TagInfoShortOrRational tagInfo, final short... values) throws ImagingException {
321         if (tagInfo.length > 0 && tagInfo.length != values.length) {
322             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
323         }
324         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
325         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SHORT, values.length, bytes);
326         add(tiffOutputField);
327     }
328 
329     public void add(final TagInfoShorts tagInfo, final short... values) throws ImagingException {
330         if (tagInfo.length > 0 && tagInfo.length != values.length) {
331             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
332         }
333         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
334         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SHORT, values.length, bytes);
335         add(tiffOutputField);
336     }
337 
338     public void add(final TagInfoSLong tagInfo, final int value) throws ImagingException {
339         if (tagInfo.length != 1) {
340             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1");
341         }
342         final byte[] bytes = tagInfo.encodeValue(byteOrder, value);
343         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SLONG, 1, bytes);
344         add(tiffOutputField);
345     }
346 
347     public void add(final TagInfoSLongs tagInfo, final int... values) throws ImagingException {
348         if (tagInfo.length > 0 && tagInfo.length != values.length) {
349             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
350         }
351         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
352         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SLONG, values.length, bytes);
353         add(tiffOutputField);
354     }
355 
356     public void add(final TagInfoSRational tagInfo, final RationalNumber value) throws ImagingException {
357         if (tagInfo.length != 1) {
358             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1");
359         }
360         final byte[] bytes = tagInfo.encodeValue(byteOrder, value);
361         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SRATIONAL, 1, bytes);
362         add(tiffOutputField);
363     }
364 
365     public void add(final TagInfoSRationals tagInfo, final RationalNumber... values) throws ImagingException {
366         if (tagInfo.length > 0 && tagInfo.length != values.length) {
367             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
368         }
369         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
370         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SRATIONAL, values.length, bytes);
371         add(tiffOutputField);
372     }
373 
374     public void add(final TagInfoSShort tagInfo, final short value) throws ImagingException {
375         if (tagInfo.length != 1) {
376             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not 1");
377         }
378         final byte[] bytes = tagInfo.encodeValue(byteOrder, value);
379         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SSHORT, 1, bytes);
380         add(tiffOutputField);
381     }
382 
383     public void add(final TagInfoSShorts tagInfo, final short... values) throws ImagingException {
384         if (tagInfo.length > 0 && tagInfo.length != values.length) {
385             throw new ImagingException("Tag expects " + tagInfo.length + " value(s), not " + values.length);
386         }
387         final byte[] bytes = tagInfo.encodeValue(byteOrder, values);
388         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.SSHORT, values.length, bytes);
389         add(tiffOutputField);
390     }
391 
392     public void add(final TagInfoXpString tagInfo, final String value) throws ImagingException {
393         final byte[] bytes = tagInfo.encodeValue(AbstractFieldType.BYTE, value, byteOrder);
394         final TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo, AbstractFieldType.BYTE, bytes.length, bytes);
395         add(tiffOutputField);
396     }
397 
398     public void add(final TiffOutputField field) {
399         fields.add(field);
400     }
401 
402     public String description() {
403         return TiffDirectory.description(getType());
404     }
405 
406     /**
407      * Finds the TiffOutputField for the given tag from this TiffOutputDirectory.
408      *
409      * <p>
410      * If there is no field matching the given tag, null will be returned.
411      * </p>
412      *
413      * @param tag the tag specifying the field
414      * @return the field matching tagInfo or null, if the field isn't present
415      * @see #findField(TagInfo)
416      */
417     public TiffOutputField findField(final int tag) {
418         for (final TiffOutputField field : fields) {
419             if (field.tag == tag) {
420                 return field;
421             }
422         }
423         return null;
424     }
425 
426     /**
427      * Finds the TiffOutputField for the given TagInfo from this TiffOutputDirectory.
428      *
429      * <p>
430      * If there is no field matching the given TagInfo, null will be returned.
431      * </p>
432      *
433      * @param tagInfo the TagInfo specifying the field
434      * @return the field matching tagInfo or null, if the field isn't present
435      * @see #findField(int)
436      */
437     public TiffOutputField findField(final TagInfo tagInfo) {
438         return findField(tagInfo.tag);
439     }
440 
441     public List<TiffOutputField> getFields() {
442         return new ArrayList<>(fields);
443     }
444 
445     @Override
446     public String getItemDescription() {
447         final TiffDirectoryType dirType = TiffDirectoryType.getExifDirectoryType(getType());
448         return "Directory: " + dirType.name + " (" + getType() + ")";
449     }
450 
451     @Override
452     public int getItemLength() {
453         return TIFF_ENTRY_LENGTH * fields.size() + TIFF_DIRECTORY_HEADER_LENGTH + TIFF_DIRECTORY_FOOTER_LENGTH;
454     }
455 
456     protected List<AbstractTiffOutputItem> getOutputItems(final TiffOutputSummary outputSummary) throws ImagingException {
457         // first validate directory fields.
458 
459         removeFieldIfPresent(TiffTagConstants.TIFF_TAG_JPEG_INTERCHANGE_FORMAT);
460         removeFieldIfPresent(TiffTagConstants.TIFF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
461 
462         TiffOutputField jpegOffsetField = null;
463         if (null != jpegImageData) {
464             jpegOffsetField = new TiffOutputField(TiffTagConstants.TIFF_TAG_JPEG_INTERCHANGE_FORMAT, AbstractFieldType.LONG, 1,
465                     new byte[TIFF_ENTRY_MAX_VALUE_LENGTH]);
466             add(jpegOffsetField);
467 
468             final byte[] lengthValue = AbstractFieldType.LONG.writeData(jpegImageData.length, outputSummary.byteOrder);
469 
470             final TiffOutputField jpegLengthField = new TiffOutputField(TiffTagConstants.TIFF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, AbstractFieldType.LONG, 1,
471                     lengthValue);
472             add(jpegLengthField);
473 
474         }
475 
476         removeFieldIfPresent(TiffTagConstants.TIFF_TAG_STRIP_OFFSETS);
477         removeFieldIfPresent(TiffTagConstants.TIFF_TAG_STRIP_BYTE_COUNTS);
478         removeFieldIfPresent(TiffTagConstants.TIFF_TAG_TILE_OFFSETS);
479         removeFieldIfPresent(TiffTagConstants.TIFF_TAG_TILE_BYTE_COUNTS);
480 
481         TiffOutputField imageDataOffsetField;
482         ImageDataOffsets imageDataInfo = null;
483         if (null != abstractTiffImageData) {
484             final boolean stripsNotTiles = abstractTiffImageData.stripsNotTiles();
485 
486             TagInfo offsetTag;
487             TagInfo byteCountsTag;
488             if (stripsNotTiles) {
489                 offsetTag = TiffTagConstants.TIFF_TAG_STRIP_OFFSETS;
490                 byteCountsTag = TiffTagConstants.TIFF_TAG_STRIP_BYTE_COUNTS;
491             } else {
492                 offsetTag = TiffTagConstants.TIFF_TAG_TILE_OFFSETS;
493                 byteCountsTag = TiffTagConstants.TIFF_TAG_TILE_BYTE_COUNTS;
494             }
495 
496             final AbstractTiffElement.DataElement[] imageData = abstractTiffImageData.getImageData();
497 
498             // TiffOutputField imageDataOffsetsField = null;
499 
500             final int[] imageDataOffsets = Allocator.intArray(imageData.length);
501             final int[] imageDataByteCounts = Allocator.intArray(imageData.length);
502             Arrays.setAll(imageDataByteCounts, i -> imageData[i].length);
503 
504             // Append imageData-related fields to first directory
505             imageDataOffsetField = new TiffOutputField(offsetTag, AbstractFieldType.LONG, imageDataOffsets.length,
506                     AbstractFieldType.LONG.writeData(imageDataOffsets, outputSummary.byteOrder));
507             add(imageDataOffsetField);
508 
509             final byte[] data = AbstractFieldType.LONG.writeData(imageDataByteCounts, outputSummary.byteOrder);
510             final TiffOutputField byteCountsField = new TiffOutputField(byteCountsTag, AbstractFieldType.LONG, imageDataByteCounts.length, data);
511             add(byteCountsField);
512 
513             imageDataInfo = new ImageDataOffsets(imageData, imageDataOffsets, imageDataOffsetField);
514         }
515 
516         final List<AbstractTiffOutputItem> result = new ArrayList<>();
517         result.add(this);
518         sortFields();
519 
520         for (final TiffOutputField field : fields) {
521             if (field.isLocalValue()) {
522                 continue;
523             }
524 
525             final AbstractTiffOutputItem item = field.getSeperateValue();
526             result.add(item);
527             // outputSummary.add(item, field);
528         }
529 
530         if (null != imageDataInfo) {
531             Collections.addAll(result, imageDataInfo.outputItems);
532 
533             outputSummary.addTiffImageData(imageDataInfo);
534         }
535 
536         if (null != jpegImageData) {
537             final AbstractTiffOutputItem item = new AbstractTiffOutputItem.Value("JPEG image data", jpegImageData.getData());
538             result.add(item);
539             outputSummary.add(item, jpegOffsetField);
540         }
541 
542         return result;
543     }
544 
545     public JpegImageData getRawJpegImageData() {
546         return jpegImageData;
547     }
548 
549     public AbstractTiffImageData getRawTiffImageData() {
550         return abstractTiffImageData;
551     }
552 
553     public int getType() {
554         return type;
555     }
556 
557     @Override
558     public Iterator<TiffOutputField> iterator() {
559         return fields.iterator();
560     }
561 
562     public void removeField(final int tag) {
563         final List<TiffOutputField> matches = new ArrayList<>();
564         for (final TiffOutputField field : fields) {
565             if (field.tag == tag) {
566                 matches.add(field);
567             }
568         }
569         fields.removeAll(matches);
570     }
571 
572     public void removeField(final TagInfo tagInfo) {
573         removeField(tagInfo.tag);
574     }
575 
576     private void removeFieldIfPresent(final TagInfo tagInfo) {
577         final TiffOutputField field = findField(tagInfo);
578         if (null != field) {
579             fields.remove(field);
580         }
581     }
582 
583     public void setJpegImageData(final JpegImageData rawJpegImageData) {
584         this.jpegImageData = rawJpegImageData;
585     }
586 
587     public void setNextDirectory(final TiffOutputDirectory nextDirectory) {
588         this.nextDirectory = nextDirectory;
589     }
590 
591     public void setTiffImageData(final AbstractTiffImageData rawTiffImageData) {
592         this.abstractTiffImageData = rawTiffImageData;
593     }
594 
595     public void sortFields() {
596         final Comparator<TiffOutputField> comparator = (e1, e2) -> {
597             if (e1.tag != e2.tag) {
598                 return e1.tag - e2.tag;
599             }
600             return e1.getSortHint() - e2.getSortHint();
601         };
602         fields.sort(comparator);
603     }
604 
605     @Override
606     public void writeItem(final BinaryOutputStream bos) throws IOException, ImagingException {
607         // Write Directory Field Count
608         bos.write2Bytes(fields.size()); // DirectoryFieldCount
609 
610         // Write Fields
611         for (final TiffOutputField field : fields) {
612             field.writeField(bos);
613 
614             // Debug.debug("\t" + "writing field (" + field.tag + ", 0x" +
615             // Integer.toHexString(field.tag) + ")", field.tagInfo);
616             // if (field.tagInfo.isOffset())
617             // Debug.debug("\t\tOFFSET!", field.bytes);
618         }
619 
620         long nextDirectoryOffset = 0;
621         if (nextDirectory != null) {
622             nextDirectoryOffset = nextDirectory.getOffset();
623         }
624 
625         // Write nextDirectoryOffset
626         if (nextDirectoryOffset == UNDEFINED_VALUE) {
627             bos.write4Bytes(0);
628         } else {
629             bos.write4Bytes((int) nextDirectoryOffset);
630         }
631     }
632 }