+/*
+ * Copyright 2002-2015 Drew Noakes
+ *
+ * More information about this project is available at:
+ *
+ * https://drewnoakes.com/code/exif/
+ * https://github.com/drewnoakes/metadata-extractor
+ */
+package tim.prune.jpeg.drew;
+
+import tim.prune.jpeg.JpegData;
+
+/**
+ * Implementation of TiffHandler used for handling TIFF tags according to the Exif standard.
+ *
+ * @author Drew Noakes https://drewnoakes.com
+ */
+public class ExifTiffHandler
+{
+ private JpegData _jpegData = null;
+ private long _thumbnailOffset = -1L, _thumbnailLength = -1L;
+
+ /** This tag is a pointer to the Exif SubIFD. */
+ final int DIR_EXIF_SUB_IFD_OFFSET = 0x8769;
+ /** This tag is a pointer to the Exif GPS IFD. */
+ final int DIR_GPS_INFO_OFFSET = 0x8825;
+
+ private static final int TAG_GPS_LATITUDE_REF = 0x0001;
+ private static final int TAG_GPS_LATITUDE = 0x0002;
+ private static final int TAG_GPS_LONGITUDE_REF = 0x0003;
+ private static final int TAG_GPS_LONGITUDE = 0x0004;
+ private static final int TAG_GPS_ALTITUDE = 0x0006;
+ private static final int TAG_GPS_BEARING = 0x0011;
+
+ private static final int TAG_ORIENTATION = 0x0112;
+ private static final int TAG_THUMBNAIL_OFFSET = 0x0201;
+ private static final int TAG_THUMBNAIL_LENGTH = 0x0202;
+
+ private static final int TAG_SUB_ORITIME = 0x9003;
+ private static final int TAG_SUB_DIGITIME = 0x9004;
+
+
+ /**
+ * Constructor
+ * @param jpegData data object to populate with received results
+ */
+ public ExifTiffHandler(JpegData jpegData)
+ {
+ _jpegData = jpegData;
+ _thumbnailOffset = _thumbnailLength = -1L;
+ }
+
+ public boolean isTagIfdPointer(int tagType)
+ {
+ if (tagType == DIR_EXIF_SUB_IFD_OFFSET) {
+ return true;
+ } else if (tagType == DIR_GPS_INFO_OFFSET) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public void completed(final ByteArrayReader reader, final int tiffHeaderOffset)
+ {
+ // after the extraction process, if we have the correct tags, we may be able to store thumbnail information
+ if (_thumbnailOffset >= 0L && _thumbnailLength > 0L)
+ {
+ try {
+ byte[] thumbData = reader.getBytes(tiffHeaderOffset + (int) _thumbnailOffset, (int) _thumbnailLength);
+ if (thumbData != null)
+ {
+ byte[] thumbCopy = new byte[thumbData.length];
+ System.arraycopy(thumbData, 0, thumbCopy, 0, thumbData.length);
+ _jpegData.setThumbnailImage(thumbCopy);
+ }
+ } catch (ExifException ex) {}
+ }
+ }
+
+ public void setRationalArray(int tagId, Rational[] array)
+ {
+ switch (tagId)
+ {
+ case TAG_GPS_LATITUDE:
+ _jpegData.setLatitude(new double[] {array[0].doubleValue(), array[1].doubleValue(),
+ array[2].convertToPositiveValue()});
+ break;
+ case TAG_GPS_LONGITUDE:
+ _jpegData.setLongitude(new double[] {array[0].doubleValue(), array[1].doubleValue(),
+ array[2].convertToPositiveValue()});
+ break;
+ }
+ }
+
+ public void setRational(int tagId, Rational rational)
+ {
+ switch (tagId)
+ {
+ case TAG_GPS_ALTITUDE:
+ _jpegData.setAltitude(rational.intValue());
+ return;
+ case TAG_GPS_BEARING:
+ _jpegData.setBearing(rational.doubleValue());
+ return;
+ }
+ // maybe it was an integer passed as a rational?
+ if (rational.getDenominator() == 1L) {
+ setIntegerValue(tagId, rational.intValue());
+ }
+ }
+
+ public void setString(int tagId, String string)
+ {
+ switch (tagId)
+ {
+ case TAG_SUB_ORITIME:
+ _jpegData.setOriginalTimestamp(string);
+ break;
+ case TAG_SUB_DIGITIME:
+ _jpegData.setDigitizedTimestamp(string);
+ break;
+ case TAG_GPS_LATITUDE_REF:
+ _jpegData.setLatitudeRef(string);
+ break;
+ case TAG_GPS_LONGITUDE_REF:
+ _jpegData.setLongitudeRef(string);
+ break;
+ }
+ }
+
+ public void setIntegerValue(int tagId, int intVal)
+ {
+ switch (tagId)
+ {
+ case TAG_ORIENTATION:
+ _jpegData.setOrientationCode(intVal);
+ break;
+ case TAG_THUMBNAIL_OFFSET:
+ _thumbnailOffset = intVal;
+ break;
+ case TAG_THUMBNAIL_LENGTH:
+ _thumbnailLength = intVal;
+ break;
+ }
+ }
+
+
+ /**
+ * Decide, based on the directory id and the tag id, if we want to parse and process it
+ * @param inDirectoryId
+ * @param childTagId
+ * @return true if the tag should be parsed
+ */
+ public boolean isInterestingTag(int inDirectoryId, int childTagId)
+ {
+ switch (inDirectoryId)
+ {
+ case DIR_GPS_INFO_OFFSET:
+ return true;
+ case DIR_EXIF_SUB_IFD_OFFSET:
+ return childTagId == TAG_SUB_ORITIME
+ || childTagId == TAG_SUB_DIGITIME;
+ case 0:
+ return childTagId == TAG_THUMBNAIL_OFFSET
+ || childTagId == TAG_THUMBNAIL_LENGTH
+ || childTagId == TAG_ORIENTATION;
+ }
+ return false;
+ }
+}