2 * Copyright 2002-2015 Drew Noakes
4 * More information about this project is available at:
6 * https://drewnoakes.com/code/exif/
7 * https://github.com/drewnoakes/metadata-extractor
9 package tim.prune.jpeg.drew;
11 import tim.prune.jpeg.JpegData;
14 * Implementation of TiffHandler used for handling TIFF tags according to the Exif standard.
16 * @author Drew Noakes https://drewnoakes.com
18 public class ExifTiffHandler
20 private JpegData _jpegData = null;
21 private long _thumbnailOffset = -1L, _thumbnailLength = -1L;
23 /** This tag is a pointer to the Exif SubIFD. */
24 final int DIR_EXIF_SUB_IFD_OFFSET = 0x8769;
25 /** This tag is a pointer to the Exif GPS IFD. */
26 final int DIR_GPS_INFO_OFFSET = 0x8825;
28 private static final int TAG_GPS_LATITUDE_REF = 0x0001;
29 private static final int TAG_GPS_LATITUDE = 0x0002;
30 private static final int TAG_GPS_LONGITUDE_REF = 0x0003;
31 private static final int TAG_GPS_LONGITUDE = 0x0004;
32 private static final int TAG_GPS_ALTITUDE = 0x0006;
33 private static final int TAG_GPS_BEARING = 0x0011;
35 private static final int TAG_ORIENTATION = 0x0112;
36 private static final int TAG_THUMBNAIL_OFFSET = 0x0201;
37 private static final int TAG_THUMBNAIL_LENGTH = 0x0202;
39 private static final int TAG_SUB_ORITIME = 0x9003;
40 private static final int TAG_SUB_DIGITIME = 0x9004;
45 * @param jpegData data object to populate with received results
47 public ExifTiffHandler(JpegData jpegData)
50 _thumbnailOffset = _thumbnailLength = -1L;
53 public boolean isTagIfdPointer(int tagType)
55 if (tagType == DIR_EXIF_SUB_IFD_OFFSET) {
57 } else if (tagType == DIR_GPS_INFO_OFFSET) {
64 public void completed(final ByteArrayReader reader, final int tiffHeaderOffset)
66 // after the extraction process, if we have the correct tags, we may be able to store thumbnail information
67 if (_thumbnailOffset >= 0L && _thumbnailLength > 0L)
70 byte[] thumbData = reader.getBytes(tiffHeaderOffset + (int) _thumbnailOffset, (int) _thumbnailLength);
71 if (thumbData != null)
73 byte[] thumbCopy = new byte[thumbData.length];
74 System.arraycopy(thumbData, 0, thumbCopy, 0, thumbData.length);
75 _jpegData.setThumbnailImage(thumbCopy);
77 } catch (ExifException ex) {}
81 public void setRationalArray(int tagId, Rational[] array)
85 case TAG_GPS_LATITUDE:
86 _jpegData.setLatitude(new double[] {array[0].doubleValue(), array[1].doubleValue(),
87 array[2].convertToPositiveValue()});
89 case TAG_GPS_LONGITUDE:
90 _jpegData.setLongitude(new double[] {array[0].doubleValue(), array[1].doubleValue(),
91 array[2].convertToPositiveValue()});
96 public void setRational(int tagId, Rational rational)
100 case TAG_GPS_ALTITUDE:
101 _jpegData.setAltitude(rational.intValue());
103 case TAG_GPS_BEARING:
104 _jpegData.setBearing(rational.doubleValue());
107 // maybe it was an integer passed as a rational?
108 if (rational.getDenominator() == 1L) {
109 setIntegerValue(tagId, rational.intValue());
113 public void setString(int tagId, String string)
117 case TAG_SUB_ORITIME:
118 _jpegData.setOriginalTimestamp(string);
120 case TAG_SUB_DIGITIME:
121 _jpegData.setDigitizedTimestamp(string);
123 case TAG_GPS_LATITUDE_REF:
124 _jpegData.setLatitudeRef(string);
126 case TAG_GPS_LONGITUDE_REF:
127 _jpegData.setLongitudeRef(string);
132 public void setIntegerValue(int tagId, int intVal)
136 case TAG_ORIENTATION:
137 _jpegData.setOrientationCode(intVal);
139 case TAG_THUMBNAIL_OFFSET:
140 _thumbnailOffset = intVal;
142 case TAG_THUMBNAIL_LENGTH:
143 _thumbnailLength = intVal;
150 * Decide, based on the directory id and the tag id, if we want to parse and process it
151 * @param inDirectoryId
153 * @return true if the tag should be parsed
155 public boolean isInterestingTag(int inDirectoryId, int childTagId)
157 switch (inDirectoryId)
159 case DIR_GPS_INFO_OFFSET:
161 case DIR_EXIF_SUB_IFD_OFFSET:
162 return childTagId == TAG_SUB_ORITIME
163 || childTagId == TAG_SUB_DIGITIME;
165 return childTagId == TAG_THUMBNAIL_OFFSET
166 || childTagId == TAG_THUMBNAIL_LENGTH
167 || childTagId == TAG_ORIENTATION;