1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.imaging.formats.tiff;
18
19 import java.awt.image.BufferedImage;
20 import java.io.IOException;
21 import java.nio.ByteOrder;
22 import java.util.ArrayList;
23 import java.util.List;
24
25 import org.apache.commons.imaging.ImagingException;
26 import org.apache.commons.imaging.common.GenericImageMetadata;
27 import org.apache.commons.imaging.common.RationalNumber;
28 import org.apache.commons.imaging.formats.tiff.constants.GpsTagConstants;
29 import org.apache.commons.imaging.formats.tiff.constants.TiffDirectoryConstants;
30 import org.apache.commons.imaging.formats.tiff.constants.TiffDirectoryType;
31 import org.apache.commons.imaging.formats.tiff.fieldtypes.AbstractFieldType;
32 import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo;
33 import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoAscii;
34 import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoByte;
35 import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoDoubles;
36 import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoFloats;
37 import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoGpsText;
38 import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoLongs;
39 import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoRationals;
40 import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSBytes;
41 import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSLongs;
42 import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSRationals;
43 import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSShorts;
44 import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoShorts;
45 import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoXpString;
46 import org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory;
47 import org.apache.commons.imaging.formats.tiff.write.TiffOutputField;
48 import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet;
49
50 public class TiffImageMetadata extends GenericImageMetadata {
51 public static class Directory extends GenericImageMetadata implements ImageMetadataItem {
52
53
54 public final int type;
55
56 private final TiffDirectory directory;
57 private final ByteOrder byteOrder;
58
59 public Directory(final ByteOrder byteOrder, final TiffDirectory directory) {
60 this.type = directory.type;
61 this.directory = directory;
62 this.byteOrder = byteOrder;
63 }
64
65 public void add(final TiffField entry) {
66 add(new TiffMetadataItem(entry));
67 }
68
69 public TiffField findField(final TagInfo tagInfo) throws ImagingException {
70 return directory.findField(tagInfo);
71 }
72
73 public List<TiffField> getAllFields() {
74 return directory.getDirectoryEntries();
75 }
76
77 public JpegImageData getJpegImageData() {
78 return directory.getJpegImageData();
79 }
80
81 public TiffOutputDirectory getOutputDirectory(final ByteOrder byteOrder) throws ImagingException {
82 try {
83 final TiffOutputDirectory dstDir = new TiffOutputDirectory(type, byteOrder);
84
85 final List<? extends ImageMetadataItem> entries = getItems();
86 for (final ImageMetadataItem entry : entries) {
87 final TiffMetadataItem item = (TiffMetadataItem) entry;
88 final TiffField srcField = item.getTiffField();
89
90 if (null != dstDir.findField(srcField.getTag())) {
91
92 continue;
93 }
94 if (srcField.getTagInfo().isOffset()) {
95
96 continue;
97 }
98
99 final TagInfo tagInfo = srcField.getTagInfo();
100 final AbstractFieldType abstractFieldType = srcField.getFieldType();
101
102
103
104
105 final Object value = srcField.getValue();
106
107
108
109 final byte[] bytes = tagInfo.encodeValue(abstractFieldType, value, byteOrder);
110
111
112
113
114
115
116
117 final int count = bytes.length / abstractFieldType.getSize();
118 final TiffOutputField dstField = new TiffOutputField(srcField.getTag(), tagInfo, abstractFieldType, count, bytes);
119 dstField.setSortHint(srcField.getSortHint());
120 dstDir.add(dstField);
121 }
122
123 dstDir.setTiffImageData(getTiffImageData());
124 dstDir.setJpegImageData(getJpegImageData());
125
126 return dstDir;
127 } catch (final ImagingException e) {
128 throw new ImagingException(e.getMessage(), e);
129 }
130 }
131
132 public BufferedImage getThumbnail() throws ImagingException, IOException {
133 return directory.getTiffImage(byteOrder);
134 }
135
136 public AbstractTiffImageData getTiffImageData() {
137 return directory.getTiffImageData();
138 }
139
140 @Override
141 public String toString(final String prefix) {
142 return (prefix != null ? prefix : "") + directory.description() + ": "
143 + (getTiffImageData() != null ? " (tiffImageData)" : "")
144 + (getJpegImageData() != null ? " (jpegImageData)" : "")
145 + "\n" + super.toString(prefix) + "\n";
146 }
147
148 }
149
150 public static class GpsInfo {
151 public final String latitudeRef;
152 public final String longitudeRef;
153
154 public final RationalNumber latitudeDegrees;
155 public final RationalNumber latitudeMinutes;
156 public final RationalNumber latitudeSeconds;
157 public final RationalNumber longitudeDegrees;
158 public final RationalNumber longitudeMinutes;
159 public final RationalNumber longitudeSeconds;
160
161 public GpsInfo(final String latitudeRef, final String longitudeRef, final RationalNumber latitudeDegrees, final RationalNumber latitudeMinutes,
162 final RationalNumber latitudeSeconds, final RationalNumber longitudeDegrees, final RationalNumber longitudeMinutes,
163 final RationalNumber longitudeSeconds) {
164 this.latitudeRef = latitudeRef;
165 this.longitudeRef = longitudeRef;
166 this.latitudeDegrees = latitudeDegrees;
167 this.latitudeMinutes = latitudeMinutes;
168 this.latitudeSeconds = latitudeSeconds;
169 this.longitudeDegrees = longitudeDegrees;
170 this.longitudeMinutes = longitudeMinutes;
171 this.longitudeSeconds = longitudeSeconds;
172 }
173
174 public double getLatitudeAsDegreesNorth() throws ImagingException {
175 final double result = latitudeDegrees.doubleValue() + latitudeMinutes.doubleValue() / 60.0 + latitudeSeconds.doubleValue() / 3600.0;
176
177 if (latitudeRef.trim().equalsIgnoreCase("n")) {
178 return result;
179 }
180 if (latitudeRef.trim().equalsIgnoreCase("s")) {
181 return -result;
182 }
183 throw new ImagingException("Unknown latitude ref: \"" + latitudeRef + "\"");
184 }
185
186 public double getLongitudeAsDegreesEast() throws ImagingException {
187 final double result = longitudeDegrees.doubleValue() + longitudeMinutes.doubleValue() / 60.0 + longitudeSeconds.doubleValue() / 3600.0;
188
189 if (longitudeRef.trim().equalsIgnoreCase("e")) {
190 return result;
191 }
192 if (longitudeRef.trim().equalsIgnoreCase("w")) {
193 return -result;
194 }
195 throw new ImagingException("Unknown longitude ref: \"" + longitudeRef + "\"");
196 }
197
198 @Override
199 public String toString() {
200
201
202
203
204
205 return "[GPS. Latitude: " + latitudeDegrees.toDisplayString() + " degrees, " + latitudeMinutes.toDisplayString() + " minutes, "
206 + latitudeSeconds.toDisplayString() + " seconds " + latitudeRef + ", Longitude: " + longitudeDegrees.toDisplayString() + " degrees, "
207 + longitudeMinutes.toDisplayString() + " minutes, " + longitudeSeconds.toDisplayString() + " seconds " + longitudeRef + ']';
208 }
209
210 }
211
212 public static class TiffMetadataItem extends GenericImageMetadataItem {
213 private final TiffField entry;
214
215 public TiffMetadataItem(final TiffField entry) {
216
217 super(entry.getTagName(), entry.getValueDescription());
218 this.entry = entry;
219 }
220
221 public TiffField getTiffField() {
222 return entry;
223 }
224
225 }
226
227 public final TiffContents contents;
228
229 public TiffImageMetadata(final TiffContents contents) {
230 this.contents = contents;
231 }
232
233 public TiffDirectory findDirectory(final int directoryType) {
234 final List<? extends ImageMetadataItem> directories = getDirectories();
235 for (final ImageMetadataItem directory1 : directories) {
236 final Directory directory = (Directory) directory1;
237 if (directory.type == directoryType) {
238 return directory.directory;
239 }
240 }
241 return null;
242 }
243
244 public TiffField findField(final TagInfo tagInfo) throws ImagingException {
245 return findField(tagInfo, false);
246 }
247
248 public TiffField findField(final TagInfo tagInfo, final boolean exactDirectoryMatch) throws ImagingException {
249
250 final Integer tagCount = TiffTags.getTagCount(tagInfo.tag);
251 final int tagsMatching = tagCount == null ? 0 : tagCount;
252
253 final List<? extends ImageMetadataItem> directories = getDirectories();
254 if (exactDirectoryMatch || tagInfo.directoryType != TiffDirectoryType.EXIF_DIRECTORY_UNKNOWN) {
255 for (final ImageMetadataItem directory1 : directories) {
256 final Directory directory = (Directory) directory1;
257 if (directory.type == tagInfo.directoryType.directoryType) {
258 final TiffField field = directory.findField(tagInfo);
259 if (field != null) {
260 return field;
261 }
262 }
263 }
264 if (exactDirectoryMatch || tagsMatching > 1) {
265 return null;
266 }
267 for (final ImageMetadataItem directory1 : directories) {
268 final Directory directory = (Directory) directory1;
269 if (tagInfo.directoryType.isImageDirectory() && directory.type >= 0 || !tagInfo.directoryType.isImageDirectory() && directory.type < 0) {
270 final TiffField field = directory.findField(tagInfo);
271 if (field != null) {
272 return field;
273 }
274 }
275 }
276 }
277
278 for (final ImageMetadataItem directory1 : directories) {
279 final Directory directory = (Directory) directory1;
280 final TiffField field = directory.findField(tagInfo);
281 if (field != null) {
282 return field;
283 }
284 }
285
286 return null;
287 }
288
289 public List<TiffField> getAllFields() {
290 final List<TiffField> result = new ArrayList<>();
291 final List<? extends ImageMetadataItem> directories = getDirectories();
292 for (final ImageMetadataItem directory1 : directories) {
293 final Directory directory = (Directory) directory1;
294 result.addAll(directory.getAllFields());
295 }
296 return result;
297 }
298
299 public List<? extends ImageMetadataItem> getDirectories() {
300 return super.getItems();
301 }
302
303 public Object getFieldValue(final TagInfo tag) throws ImagingException {
304 final TiffField field = findField(tag);
305 if (field == null) {
306 return null;
307 }
308 return field.getValue();
309 }
310
311 public String[] getFieldValue(final TagInfoAscii tag) throws ImagingException {
312 final TiffField field = findField(tag);
313 if (field == null) {
314 return null;
315 }
316 if (!tag.dataTypes.contains(field.getFieldType())) {
317 return null;
318 }
319 final byte[] bytes = field.getByteArrayValue();
320 return tag.getValue(field.getByteOrder(), bytes);
321 }
322
323 public byte[] getFieldValue(final TagInfoByte tag) throws ImagingException {
324 final TiffField field = findField(tag);
325 if (field == null) {
326 return null;
327 }
328 if (!tag.dataTypes.contains(field.getFieldType())) {
329 return null;
330 }
331 return field.getByteArrayValue();
332 }
333
334 public double[] getFieldValue(final TagInfoDoubles tag) throws ImagingException {
335 final TiffField field = findField(tag);
336 if (field == null) {
337 return null;
338 }
339 if (!tag.dataTypes.contains(field.getFieldType())) {
340 return null;
341 }
342 final byte[] bytes = field.getByteArrayValue();
343 return tag.getValue(field.getByteOrder(), bytes);
344 }
345
346 public float[] getFieldValue(final TagInfoFloats tag) throws ImagingException {
347 final TiffField field = findField(tag);
348 if (field == null) {
349 return null;
350 }
351 if (!tag.dataTypes.contains(field.getFieldType())) {
352 return null;
353 }
354 final byte[] bytes = field.getByteArrayValue();
355 return tag.getValue(field.getByteOrder(), bytes);
356 }
357
358 public String getFieldValue(final TagInfoGpsText tag) throws ImagingException {
359 final TiffField field = findField(tag);
360 if (field == null) {
361 return null;
362 }
363 return tag.getValue(field);
364 }
365
366 public int[] getFieldValue(final TagInfoLongs tag) throws ImagingException {
367 final TiffField field = findField(tag);
368 if (field == null) {
369 return null;
370 }
371 if (!tag.dataTypes.contains(field.getFieldType())) {
372 return null;
373 }
374 final byte[] bytes = field.getByteArrayValue();
375 return tag.getValue(field.getByteOrder(), bytes);
376 }
377
378 public RationalNumber[] getFieldValue(final TagInfoRationals tag) throws ImagingException {
379 final TiffField field = findField(tag);
380 if (field == null) {
381 return null;
382 }
383 if (!tag.dataTypes.contains(field.getFieldType())) {
384 return null;
385 }
386 final byte[] bytes = field.getByteArrayValue();
387 return tag.getValue(field.getByteOrder(), bytes);
388 }
389
390 public byte[] getFieldValue(final TagInfoSBytes tag) throws ImagingException {
391 final TiffField field = findField(tag);
392 if (field == null) {
393 return null;
394 }
395 if (!tag.dataTypes.contains(field.getFieldType())) {
396 return null;
397 }
398 return field.getByteArrayValue();
399 }
400
401 public short[] getFieldValue(final TagInfoShorts tag) throws ImagingException {
402 final TiffField field = findField(tag);
403 if (field == null) {
404 return null;
405 }
406 if (!tag.dataTypes.contains(field.getFieldType())) {
407 return null;
408 }
409 final byte[] bytes = field.getByteArrayValue();
410 return tag.getValue(field.getByteOrder(), bytes);
411 }
412
413 public int[] getFieldValue(final TagInfoSLongs tag) throws ImagingException {
414 final TiffField field = findField(tag);
415 if (field == null) {
416 return null;
417 }
418 if (!tag.dataTypes.contains(field.getFieldType())) {
419 return null;
420 }
421 final byte[] bytes = field.getByteArrayValue();
422 return tag.getValue(field.getByteOrder(), bytes);
423 }
424
425 public RationalNumber[] getFieldValue(final TagInfoSRationals tag) throws ImagingException {
426 final TiffField field = findField(tag);
427 if (field == null) {
428 return null;
429 }
430 if (!tag.dataTypes.contains(field.getFieldType())) {
431 return null;
432 }
433 final byte[] bytes = field.getByteArrayValue();
434 return tag.getValue(field.getByteOrder(), bytes);
435 }
436
437 public short[] getFieldValue(final TagInfoSShorts tag) throws ImagingException {
438 final TiffField field = findField(tag);
439 if (field == null) {
440 return null;
441 }
442 if (!tag.dataTypes.contains(field.getFieldType())) {
443 return null;
444 }
445 final byte[] bytes = field.getByteArrayValue();
446 return tag.getValue(field.getByteOrder(), bytes);
447 }
448
449 public String getFieldValue(final TagInfoXpString tag) throws ImagingException {
450 final TiffField field = findField(tag);
451 if (field == null) {
452 return null;
453 }
454 return tag.getValue(field);
455 }
456
457 public GpsInfo getGpsInfo() throws ImagingException {
458 final TiffDirectory gpsDirectory = findDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_GPS);
459 if (null == gpsDirectory) {
460 return null;
461 }
462
463
464 final TiffField latitudeRefField = gpsDirectory.findField(GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF);
465 final TiffField latitudeField = gpsDirectory.findField(GpsTagConstants.GPS_TAG_GPS_LATITUDE);
466 final TiffField longitudeRefField = gpsDirectory.findField(GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF);
467 final TiffField longitudeField = gpsDirectory.findField(GpsTagConstants.GPS_TAG_GPS_LONGITUDE);
468
469 if (latitudeRefField == null || latitudeField == null || longitudeRefField == null || longitudeField == null) {
470 return null;
471 }
472
473
474 final String latitudeRef = latitudeRefField.getStringValue();
475 final RationalNumber[] latitude = (RationalNumber[]) latitudeField.getValue();
476 final String longitudeRef = longitudeRefField.getStringValue();
477 final RationalNumber[] longitude = (RationalNumber[]) longitudeField.getValue();
478
479 if (latitude.length != 3 || longitude.length != 3) {
480 throw new ImagingException("Expected three values for latitude and longitude.");
481 }
482
483 final RationalNumber latitudeDegrees = latitude[0];
484 final RationalNumber latitudeMinutes = latitude[1];
485 final RationalNumber latitudeSeconds = latitude[2];
486
487 final RationalNumber longitudeDegrees = longitude[0];
488 final RationalNumber longitudeMinutes = longitude[1];
489 final RationalNumber longitudeSeconds = longitude[2];
490
491 return new GpsInfo(latitudeRef, longitudeRef, latitudeDegrees, latitudeMinutes, latitudeSeconds, longitudeDegrees, longitudeMinutes, longitudeSeconds);
492 }
493
494 @Override
495 public List<? extends ImageMetadataItem> getItems() {
496 final List<ImageMetadataItem> result = new ArrayList<>();
497
498 final List<? extends ImageMetadataItem> items = super.getItems();
499 for (final ImageMetadataItem item : items) {
500 final Directory dir = (Directory) item;
501 result.addAll(dir.getItems());
502 }
503
504 return result;
505 }
506
507 public TiffOutputSet getOutputSet() throws ImagingException {
508 final ByteOrder byteOrder = contents.header.byteOrder;
509 final TiffOutputSet result = new TiffOutputSet(byteOrder);
510
511 final List<? extends ImageMetadataItem> srcDirs = getDirectories();
512 for (final ImageMetadataItem srcDir1 : srcDirs) {
513 final Directory srcDir = (Directory) srcDir1;
514
515 if (null != result.findDirectory(srcDir.type)) {
516
517
518
519 continue;
520 }
521
522 final TiffOutputDirectory outputDirectory = srcDir.getOutputDirectory(byteOrder);
523 result.addDirectory(outputDirectory);
524 }
525
526 return result;
527 }
528
529 }