*/\r
private boolean _isMotorolaByteOrder;\r
\r
- /**\r
- * The number of bytes used per format descriptor.\r
- */\r
+ /** Thumbnail offset */\r
+ private int _thumbnailOffset = -1;\r
+ /** Thumbnail length */\r
+ private int _thumbnailLength = -1;\r
+\r
+ /** The number of bytes used per format descriptor */\r
private static final int[] BYTES_PER_FORMAT = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};\r
\r
- /**\r
- * The number of formats known.\r
- */\r
+ /** The number of formats known */\r
private static final int MAX_FORMAT_CODE = 12;\r
\r
// Format types\r
// Note: Cannot use the DataFormat enumeration in the case statement that uses these tags.\r
// Is there a better way?\r
- private static final int FMT_BYTE = 1;\r
+ //private static final int FMT_BYTE = 1;\r
private static final int FMT_STRING = 2;\r
- private static final int FMT_USHORT = 3;\r
- private static final int FMT_ULONG = 4;\r
+ //private static final int FMT_USHORT = 3;\r
+ //private static final int FMT_ULONG = 4;\r
private static final int FMT_URATIONAL = 5;\r
- private static final int FMT_SBYTE = 6;\r
- private static final int FMT_UNDEFINED = 7;\r
- private static final int FMT_SSHORT = 8;\r
- private static final int FMT_SLONG = 9;\r
+ //private static final int FMT_SBYTE = 6;\r
+ //private static final int FMT_UNDEFINED = 7;\r
+ //private static final int FMT_SSHORT = 8;\r
+ //private static final int FMT_SLONG = 9;\r
private static final int FMT_SRATIONAL = 10;\r
- private static final int FMT_SINGLE = 11;\r
- private static final int FMT_DOUBLE = 12;\r
+ //private static final int FMT_SINGLE = 11;\r
+ //private static final int FMT_DOUBLE = 12;\r
\r
public static final int TAG_EXIF_OFFSET = 0x8769;\r
public static final int TAG_INTEROP_OFFSET = 0xA005;\r
public static final int TAG_GPS_TIMESTAMP = 0x0007;\r
/** GPS date (atomic clock) GPSDateStamp 23 1d RATIONAL 3 */\r
public static final int TAG_GPS_DATESTAMP = 0x001d;\r
+ /** Exif timestamp */\r
+ public static final int TAG_DATETIME_ORIGINAL = 0x9003;\r
+ /** Thumbnail offset */\r
+ private static final int TAG_THUMBNAIL_OFFSET = 0x0201;\r
+ /** Thumbnail length */\r
+ private static final int TAG_THUMBNAIL_LENGTH = 0x0202;\r
+\r
\r
/**\r
- * Creates an ExifReader for a Jpeg file.\r
+ * Creates an ExifReader for a Jpeg file\r
* @param inFile File object to attempt to read from\r
- * @throws JpegProcessingException on failure\r
+ * @throws JpegException on failure\r
*/\r
public ExifReader(File inFile) throws JpegException\r
{\r
firstDirectoryOffset = 14;\r
}\r
\r
- HashMap processedDirectoryOffsets = new HashMap();\r
+ HashMap<Integer, String> processedDirectoryOffsets = new HashMap<Integer, String>();\r
\r
// 0th IFD (we merge with Exif IFD)\r
processDirectory(metadata, false, processedDirectoryOffsets, firstDirectoryOffset, TIFF_HEADER_START_OFFSET);\r
* 2 bytes: format code\r
* 4 bytes: component count\r
*/\r
- private void processDirectory(JpegData inMetadata, boolean inIsGPS, HashMap inDirectoryOffsets,\r
+ private void processDirectory(JpegData inMetadata, boolean inIsGPS, HashMap<Integer, String> inDirectoryOffsets,\r
int inDirOffset, int inTiffHeaderOffset)\r
{\r
// check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist\r
- if (inDirectoryOffsets.containsKey(new Integer(inDirOffset)))\r
+ if (inDirectoryOffsets.containsKey(Integer.valueOf(inDirOffset)))\r
return;\r
\r
// remember that we've visited this directory so that we don't visit it again later\r
- inDirectoryOffsets.put(new Integer(inDirOffset), "processed");\r
+ inDirectoryOffsets.put(Integer.valueOf(inDirOffset), "processed");\r
\r
if (inDirOffset >= _data.length || inDirOffset < 0)\r
{\r
// Calculate the value as an offset for cases where the tag represents a directory\r
final int subdirOffset = inTiffHeaderOffset + get32Bits(tagValueOffset);\r
\r
- // TODO: Also look for timestamp(s) in Exif for correlation - which directory?\r
+ // Look in both basic Exif tags (for timestamp, thumbnail) and Gps tags (for lat, long, altitude, timestamp)\r
switch (tagType)\r
{\r
case TAG_EXIF_OFFSET:\r
- // ignore\r
+ processDirectory(inMetadata, false, inDirectoryOffsets, subdirOffset, inTiffHeaderOffset);\r
continue;\r
case TAG_INTEROP_OFFSET:\r
// ignore\r
continue;\r
default:\r
// not a known directory, so must just be a normal tag\r
- // ignore if we're not in gps directory\r
if (inIsGPS)\r
+ {\r
processGpsTag(inMetadata, tagType, tagValueOffset, componentCount, formatCode);\r
+ }\r
+ else\r
+ {\r
+ processExifTag(inMetadata, tagType, tagValueOffset, componentCount, formatCode);\r
+ }\r
break;\r
}\r
}\r
inMetadata.setAltitude(readRational(inTagValueOffset, inFormatCode, inComponentCount));\r
break;\r
case TAG_GPS_TIMESTAMP:\r
- inMetadata.setTimestamp(readRationalArray(inTagValueOffset, inFormatCode, inComponentCount));\r
+ inMetadata.setGpsTimestamp(readRationalArray(inTagValueOffset, inFormatCode, inComponentCount));\r
break;\r
case TAG_GPS_DATESTAMP:\r
- inMetadata.setDatestamp(readRationalArray(inTagValueOffset, inFormatCode, inComponentCount));\r
+ inMetadata.setGpsDatestamp(readRationalArray(inTagValueOffset, inFormatCode, inComponentCount));\r
break;\r
default: // ignore all other tags\r
}\r
}\r
\r
\r
+ /**\r
+ * Process a general Exif tag\r
+ * @param inMetadata metadata holding extracted values\r
+ * @param inTagType tag type (eg latitude)\r
+ * @param inTagValueOffset start offset in data array\r
+ * @param inComponentCount component count for tag\r
+ * @param inFormatCode format code, eg byte\r
+ */\r
+ private void processExifTag(JpegData inMetadata, int inTagType, int inTagValueOffset,\r
+ int inComponentCount, int inFormatCode)\r
+ {\r
+ // Only interested in original timestamp, thumbnail offset and thumbnail length\r
+ if (inTagType == TAG_DATETIME_ORIGINAL)\r
+ {\r
+ inMetadata.setOriginalTimestamp(readString(inTagValueOffset, inFormatCode, inComponentCount));\r
+ }\r
+ else if (inTagType == TAG_THUMBNAIL_OFFSET) {\r
+ _thumbnailOffset = TIFF_HEADER_START_OFFSET + get16Bits(inTagValueOffset);\r
+ extractThumbnail(inMetadata);\r
+ }\r
+ else if (inTagType == TAG_THUMBNAIL_LENGTH) {\r
+ _thumbnailLength = get16Bits(inTagValueOffset);\r
+ extractThumbnail(inMetadata);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Attempt to extract the thumbnail image\r
+ */\r
+ private void extractThumbnail(JpegData inMetadata)\r
+ {\r
+ if (_thumbnailOffset > 0 && _thumbnailLength > 0 && inMetadata.getThumbnailImage() == null)\r
+ {\r
+ byte[] thumbnailBytes = new byte[_thumbnailLength];\r
+ System.arraycopy(_data, _thumbnailOffset, thumbnailBytes, 0, _thumbnailLength);\r
+ inMetadata.setThumbnailImage(thumbnailBytes);\r
+ }\r
+ }\r
+\r
+\r
/**\r
* Calculate the tag value offset\r
* @param inByteCount\r