📄 exifreader.java
字号:
if (!exifDirectory.containsTag(ExifDirectory.TAG_THUMBNAIL_LENGTH) ||
!exifDirectory.containsTag(ExifDirectory.TAG_THUMBNAIL_OFFSET))
return;
try {
int offset = exifDirectory.getInt(ExifDirectory.TAG_THUMBNAIL_OFFSET);
int length = exifDirectory.getInt(ExifDirectory.TAG_THUMBNAIL_LENGTH);
byte[] result = new byte[length];
for (int i = 0; i<result.length; i++) {
result[i] = _data[tiffHeaderOffset + offset + i];
}
exifDirectory.setByteArray(ExifDirectory.TAG_THUMBNAIL_DATA, result);
} catch (Throwable e) {
exifDirectory.addError("Unable to extract thumbnail: " + e.getMessage());
}
}
private boolean setByteOrder(String byteOrderIdentifier)
{
if ("MM".equals(byteOrderIdentifier)) {
_isMotorollaByteOrder = true;
} else if ("II".equals(byteOrderIdentifier)) {
_isMotorollaByteOrder = false;
} else {
return false;
}
return true;
}
/**
* Process one of the nested Tiff IFD directories.
* 2 bytes: number of tags
* for each tag
* 2 bytes: tag type
* 2 bytes: format code
* 4 bytes: component count
*/
private void processDirectory(Directory directory, HashMap processedDirectoryOffsets, int dirStartOffset, int tiffHeaderOffset)
{
// check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist
if (processedDirectoryOffsets.containsKey(new Integer(dirStartOffset)))
return;
// remember that we've visited this directory so that we don't visit it again later
processedDirectoryOffsets.put(new Integer(dirStartOffset), "processed");
if (dirStartOffset>=_data.length || dirStartOffset<0) {
directory.addError("Ignored directory marked to start outside data segement");
return;
}
if (!isDirectoryLengthValid(dirStartOffset, tiffHeaderOffset)) {
directory.addError("Illegally sized directory");
return;
}
// First two bytes in the IFD are the number of tags in this directory
int dirTagCount = get16Bits(dirStartOffset);
// Handle each tag in this directory
for (int tagNumber = 0; tagNumber<dirTagCount; tagNumber++)
{
final int tagOffset = calculateTagOffset(dirStartOffset, tagNumber);
// 2 bytes for the tag type
final int tagType = get16Bits(tagOffset);
// 2 bytes for the format code
final int formatCode = get16Bits(tagOffset + 2);
if (formatCode<1 || formatCode>MAX_FORMAT_CODE) {
directory.addError("Invalid format code: " + formatCode);
continue;
}
// 4 bytes dictate the number of components in this tag's data
final int componentCount = get32Bits(tagOffset + 4);
if (componentCount<0) {
directory.addError("Negative component count in EXIF");
continue;
}
// each component may have more than one byte... calculate the total number of bytes
final int byteCount = componentCount * BYTES_PER_FORMAT[formatCode];
final int tagValueOffset = calculateTagValueOffset(byteCount, tagOffset, tiffHeaderOffset);
if (tagValueOffset<0 || tagValueOffset > _data.length) {
directory.addError("Illegal pointer offset value in EXIF");
continue;
}
// Check that this tag isn't going to allocate outside the bounds of the data array.
// This addresses an uncommon OutOfMemoryError.
if (byteCount < 0 || tagValueOffset + byteCount > _data.length)
{
directory.addError("Illegal number of bytes: " + byteCount);
continue;
}
// Calculate the value as an offset for cases where the tag represents directory
final int subdirOffset = tiffHeaderOffset + get32Bits(tagValueOffset);
switch (tagType) {
case TAG_EXIF_OFFSET:
processDirectory(_metadata.getDirectory(ExifDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
continue;
case TAG_INTEROP_OFFSET:
processDirectory(_metadata.getDirectory(ExifInteropDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
continue;
case TAG_GPS_INFO_OFFSET:
processDirectory(_metadata.getDirectory(GpsDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
continue;
case TAG_MAKER_NOTE:
processMakerNote(tagValueOffset, processedDirectoryOffsets, tiffHeaderOffset);
continue;
default:
processTag(directory, tagType, tagValueOffset, componentCount, formatCode);
break;
}
}
// at the end of each IFD is an optional link to the next IFD
final int finalTagOffset = calculateTagOffset(dirStartOffset, dirTagCount);
int nextDirectoryOffset = get32Bits(finalTagOffset);
if (nextDirectoryOffset!=0) {
nextDirectoryOffset += tiffHeaderOffset;
if (nextDirectoryOffset>=_data.length) {
// Last 4 bytes of IFD reference another IFD with an address that is out of bounds
// Note this could have been caused by jhead 1.3 cropping too much
return;
} else if (nextDirectoryOffset < dirStartOffset) {
// Last 4 bytes of IFD reference another IFD with an address that is before the start of this directory
return;
}
// the next directory is of same type as this one
processDirectory(directory, processedDirectoryOffsets, nextDirectoryOffset, tiffHeaderOffset);
}
}
private void processMakerNote(int subdirOffset, HashMap processedDirectoryOffsets, int tiffHeaderOffset)
{
// Determine the camera model and makernote format
Directory exifDirectory = _metadata.getDirectory(ExifDirectory.class);
if (exifDirectory==null)
return;
String cameraModel = exifDirectory.getString(ExifDirectory.TAG_MAKE);
final String firstTwoChars = new String(_data, subdirOffset, 2);
final String firstThreeChars = new String(_data, subdirOffset, 3);
final String firstFourChars = new String(_data, subdirOffset, 4);
final String firstFiveChars = new String(_data, subdirOffset, 5);
final String firstSixChars = new String(_data, subdirOffset, 6);
final String firstSevenChars = new String(_data, subdirOffset, 7);
final String firstEightChars = new String(_data, subdirOffset, 8);
if ("OLYMP".equals(firstFiveChars) || "EPSON".equals(firstFiveChars) || "AGFA".equals(firstFourChars))
{
// Olympus Makernote
// Epson and Agfa use Olypus maker note standard, see:
// http://www.ozhiker.com/electronics/pjmt/jpeg_info/
processDirectory(_metadata.getDirectory(OlympusMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 8, tiffHeaderOffset);
}
else if (cameraModel!=null && cameraModel.trim().toUpperCase().startsWith("NIKON"))
{
if ("Nikon".equals(firstFiveChars))
{
/* There are two scenarios here:
* Type 1: **
* :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon...........
* :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................
* Type 3: **
* :0000: 4E 69 6B 6F 6E 00 02 00-00 00 4D 4D 00 2A 00 00 Nikon....MM.*...
* :0010: 00 08 00 1E 00 01 00 07-00 00 00 04 30 32 30 30 ............0200
*/
if (_data[subdirOffset+6]==1)
processDirectory(_metadata.getDirectory(NikonType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 8, tiffHeaderOffset);
else if (_data[subdirOffset+6]==2)
processDirectory(_metadata.getDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 18, subdirOffset + 10);
else
exifDirectory.addError("Unsupported makernote data ignored.");
}
else
{
// The IFD begins with the first MakerNote byte (no ASCII name). This occurs with CoolPix 775, E990 and D1 models.
processDirectory(_metadata.getDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
}
}
else if ("SONY CAM".equals(firstEightChars) || "SONY DSC".equals(firstEightChars))
{
processDirectory(_metadata.getDirectory(SonyMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 12, tiffHeaderOffset);
}
else if ("KDK".equals(firstThreeChars))
{
processDirectory(_metadata.getDirectory(KodakMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 20, tiffHeaderOffset);
}
else if ("Canon".equalsIgnoreCase(cameraModel))
{
processDirectory(_metadata.getDirectory(CanonMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
}
else if (cameraModel!=null && cameraModel.toUpperCase().startsWith("CASIO"))
{
if ("QVC\u0000\u0000\u0000".equals(firstSixChars))
processDirectory(_metadata.getDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, tiffHeaderOffset);
else
processDirectory(_metadata.getDirectory(CasioType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
}
else if ("FUJIFILM".equals(firstEightChars) || "Fujifilm".equalsIgnoreCase(cameraModel))
{
// TODO make this field a passed parameter, to avoid threading issues
boolean byteOrderBefore = _isMotorollaByteOrder;
// bug in fujifilm makernote ifd means we temporarily use Intel byte ordering
_isMotorollaByteOrder = false;
// the 4 bytes after "FUJIFILM" in the makernote point to the start of the makernote
// IFD, though the offset is relative to the start of the makernote, not the TIFF
// header (like everywhere else)
int ifdStart = subdirOffset + get32Bits(subdirOffset + 8);
processDirectory(_metadata.getDirectory(FujifilmMakernoteDirectory.class), processedDirectoryOffsets, ifdStart, tiffHeaderOffset);
_isMotorollaByteOrder = byteOrderBefore;
}
else if (cameraModel!=null && cameraModel.toUpperCase().startsWith("MINOLTA"))
{
// Cases seen with the model starting with MINOLTA in capitals seem to have a valid Olympus makernote
// area that commences immediately.
processDirectory(_metadata.getDirectory(OlympusMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
}
else if ("KC".equals(firstTwoChars) || "MINOL".equals(firstFiveChars) || "MLY".equals(firstThreeChars) || "+M+M+M+M".equals(firstEightChars))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -