TMI-META: Now parsing SubIFDs.

This commit is contained in:
Harald Kuhr 2014-10-15 12:16:06 +02:00
parent 5cb44fa20b
commit ab85ab9bcc
3 changed files with 43 additions and 53 deletions

View File

@ -133,6 +133,11 @@ final class EXIFEntry extends AbstractEntry {
case TIFF.TAG_EXTRA_SAMPLES: case TIFF.TAG_EXTRA_SAMPLES:
return "ExtraSamples"; return "ExtraSamples";
case TIFF.TAG_SUB_IFD:
return "SubIFD";
case TIFF.TAG_SUBFILE_TYPE:
return "SubfileType";
case EXIF.TAG_EXPOSURE_TIME: case EXIF.TAG_EXPOSURE_TIME:
return "ExposureTime"; return "ExposureTime";
case EXIF.TAG_F_NUMBER: case EXIF.TAG_F_NUMBER:

View File

@ -28,7 +28,7 @@
package com.twelvemonkeys.imageio.metadata.exif; package com.twelvemonkeys.imageio.metadata.exif;
import com.twelvemonkeys.imageio.metadata.AbstractCompoundDirectory; import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
import com.twelvemonkeys.imageio.metadata.Directory; import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry; import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.MetadataReader; import com.twelvemonkeys.imageio.metadata.MetadataReader;
@ -52,7 +52,7 @@ import java.util.*;
* @version $Id: EXIFReader.java,v 1.0 Nov 13, 2009 5:42:51 PM haraldk Exp$ * @version $Id: EXIFReader.java,v 1.0 Nov 13, 2009 5:42:51 PM haraldk Exp$
*/ */
public final class EXIFReader extends MetadataReader { public final class EXIFReader extends MetadataReader {
static final Collection<Integer> KNOWN_IFDS = Collections.unmodifiableCollection(Arrays.asList(TIFF.TAG_EXIF_IFD, TIFF.TAG_GPS_IFD, TIFF.TAG_INTEROP_IFD)); static final Collection<Integer> KNOWN_IFDS = Collections.unmodifiableCollection(Arrays.asList(TIFF.TAG_EXIF_IFD, TIFF.TAG_GPS_IFD, TIFF.TAG_INTEROP_IFD, TIFF.TAG_SUB_IFD));
@Override @Override
public Directory read(final ImageInputStream input) throws IOException { public Directory read(final ImageInputStream input) throws IOException {
@ -83,7 +83,7 @@ public final class EXIFReader extends MetadataReader {
return readDirectory(input, directoryOffset); return readDirectory(input, directoryOffset);
} }
private Directory readDirectory(final ImageInputStream pInput, final long pOffset) throws IOException { public Directory readDirectory(final ImageInputStream pInput, final long pOffset) throws IOException {
List<IFD> ifds = new ArrayList<IFD>(); List<IFD> ifds = new ArrayList<IFD>();
List<Entry> entries = new ArrayList<Entry>(); List<Entry> entries = new ArrayList<Entry>();
@ -110,7 +110,7 @@ public final class EXIFReader extends MetadataReader {
// Read linked IFDs // Read linked IFDs
if (nextOffset != 0) { if (nextOffset != 0) {
AbstractCompoundDirectory next = (AbstractCompoundDirectory) readDirectory(pInput, nextOffset); CompoundDirectory next = (CompoundDirectory) readDirectory(pInput, nextOffset);
for (int i = 0; i < next.directoryCount(); i++) { for (int i = 0; i < next.directoryCount(); i++) {
ifds.add((IFD) next.getDirectory(i)); ifds.add((IFD) next.getDirectory(i));
} }
@ -119,7 +119,7 @@ public final class EXIFReader extends MetadataReader {
// TODO: Make what sub-IFDs to parse optional? Or leave this to client code? At least skip the non-TIFF data? // TODO: Make what sub-IFDs to parse optional? Or leave this to client code? At least skip the non-TIFF data?
// TODO: Put it in the constructor? // TODO: Put it in the constructor?
readSubdirectories(pInput, entries, readSubdirectories(pInput, entries,
Arrays.asList(TIFF.TAG_EXIF_IFD, TIFF.TAG_GPS_IFD, TIFF.TAG_INTEROP_IFD Arrays.asList(TIFF.TAG_EXIF_IFD, TIFF.TAG_GPS_IFD, TIFF.TAG_INTEROP_IFD, TIFF.TAG_SUB_IFD
// , TIFF.TAG_IPTC, TIFF.TAG_XMP // , TIFF.TAG_IPTC, TIFF.TAG_XMP
// , TIFF.TAG_ICC_PROFILE // , TIFF.TAG_ICC_PROFILE
// , TIFF.TAG_PHOTOSHOP // , TIFF.TAG_PHOTOSHOP
@ -132,13 +132,9 @@ public final class EXIFReader extends MetadataReader {
return new EXIFDirectory(ifds); return new EXIFDirectory(ifds);
} }
// private Directory readForeignMetadata(final MetadataReader reader, final byte[] bytes) throws IOException {
// return reader.read(ImageIO.createImageInputStream(new ByteArrayInputStream(bytes)));
// }
// TODO: Might be better to leave this for client code, as it's tempting go really overboard and support any possible embedded format.. // TODO: Might be better to leave this for client code, as it's tempting go really overboard and support any possible embedded format..
private void readSubdirectories(ImageInputStream input, List<Entry> entries, List<Integer> subIFDs) throws IOException { private void readSubdirectories(ImageInputStream input, List<Entry> entries, List<Integer> subIFDIds) throws IOException {
if (subIFDs == null || subIFDs.isEmpty()) { if (subIFDIds == null || subIFDIds.isEmpty()) {
return; return;
} }
@ -146,45 +142,29 @@ public final class EXIFReader extends MetadataReader {
EXIFEntry entry = (EXIFEntry) entries.get(i); EXIFEntry entry = (EXIFEntry) entries.get(i);
int tagId = (Integer) entry.getIdentifier(); int tagId = (Integer) entry.getIdentifier();
if (subIFDs.contains(tagId)) { if (subIFDIds.contains(tagId)) {
try { try {
Object directory; if (KNOWN_IFDS.contains(tagId)) {
long[] pointerOffsets = getPointerOffsets(entry);
List<IFD> subIFDs = new ArrayList<IFD>(pointerOffsets.length);
/* for (long pointerOffset : pointerOffsets) {
if (tagId == TIFF.TAG_IPTC) { CompoundDirectory subDirectory = (CompoundDirectory) readDirectory(input, pointerOffset);
directory = readForeignMetadata(new IPTCReader(), (byte[]) entry.getValue());
} for (int j = 0; j < subDirectory.directoryCount(); j++) {
else if (tagId == TIFF.TAG_XMP) { subIFDs.add((IFD) subDirectory.getDirectory(j));
directory = readForeignMetadata(new XMPReader(), (byte[]) entry.getValue()); }
}
else if (tagId == TIFF.TAG_PHOTOSHOP) {
// TODO: This is waaay too fragile.. Might need registry-based meta data parsers?
try {
Class cl = Class.forName("com.twelvemonkeys.imageio.plugins.psd.PSDImageResource");
Method method = cl.getMethod("read", ImageInputStream.class);
method.setAccessible(true);
directory = method.invoke(null, ImageIO.createImageInputStream(new ByteArrayInputStream((byte[]) entry.getValue())));
} }
catch (Exception ignore) {
continue; if (subIFDs.size() == 1) {
// Replace the entry with parsed data
entries.set(i, new EXIFEntry(tagId, subIFDs.get(0), entry.getType()));
}
else {
// Replace the entry with parsed data
entries.set(i, new EXIFEntry(tagId, subIFDs.toArray(new IFD[subIFDs.size()]), entry.getType()));
} }
} }
else if (tagId == TIFF.TAG_ICC_PROFILE) {
directory = ICC_Profile.getInstance((byte[]) entry.getValue());
}
else if (tagId == TIFF.TAG_MODI_OLE_PROPERTY_SET) {
// TODO: Encapsulate in something more useful?
directory = new CompoundDocument(new ByteArrayInputStream((byte[]) entry.getValue())).getRootEntry();
}
else*/ if (KNOWN_IFDS.contains(tagId)) {
directory = ((AbstractCompoundDirectory) readDirectory(input, getPointerOffset(entry))).getDirectory(0);
}
else {
continue;
}
// Replace the entry with parsed data
entries.set(i, new EXIFEntry(tagId, directory, entry.getType()));
} }
catch (IIOException e) { catch (IIOException e) {
// TODO: Issue warning without crashing...? // TODO: Issue warning without crashing...?
@ -194,27 +174,30 @@ public final class EXIFReader extends MetadataReader {
} }
} }
private long getPointerOffset(final Entry entry) throws IIOException { private long[] getPointerOffsets(final Entry entry) throws IIOException {
long offset; long[] offsets;
Object value = entry.getValue(); Object value = entry.getValue();
if (value instanceof Byte) { if (value instanceof Byte) {
offset = (Byte) value & 0xff; offsets = new long[] {(Byte) value & 0xff};
} }
else if (value instanceof Short) { else if (value instanceof Short) {
offset = (Short) value & 0xffff; offsets = new long[] {(Short) value & 0xffff};
} }
else if (value instanceof Integer) { else if (value instanceof Integer) {
offset = (Integer) value & 0xffffffffL; offsets = new long[] {(Integer) value & 0xffffffffL};
} }
else if (value instanceof Long) { else if (value instanceof Long) {
offset = (Long) value; offsets = new long[] {(Long) value};
}
else if (value instanceof long[]) {
offsets = (long[]) value;
} }
else { else {
throw new IIOException(String.format("Unknown pointer type: %s", (value != null ? value.getClass() : null))); throw new IIOException(String.format("Unknown pointer type: %s", (value != null ? value.getClass() : null)));
} }
return offset; return offsets;
} }
private EXIFEntry readEntry(final ImageInputStream pInput) throws IOException { private EXIFEntry readEntry(final ImageInputStream pInput) throws IOException {
@ -322,7 +305,7 @@ public final class EXIFReader extends MetadataReader {
pInput.readFully(bytes); pInput.readFully(bytes);
// NOTE: We don't change (unsigned) BYTE array wider Java type, as most often BYTE array means // NOTE: We don't change (unsigned) BYTE array wider Java type, as most often BYTE array means
// binary data and we want to keep that as a byte array for clients to parse futher // binary data and we want to keep that as a byte array for clients to parse further
return bytes; return bytes;
case TIFF.TYPE_SHORT: case TIFF.TYPE_SHORT:

View File

@ -160,6 +160,8 @@ public interface TIFF {
int TAG_HOST_COMPUTER = 316; int TAG_HOST_COMPUTER = 316;
int TAG_COPYRIGHT = 33432; int TAG_COPYRIGHT = 33432;
int TAG_SUBFILE_TYPE = 254;
int TAG_OLD_SUBFILE_TYPE = 255; // Deprecated NO NOT WRITE!
int TAG_SUB_IFD = 330; int TAG_SUB_IFD = 330;
int TAG_XMP = 700; int TAG_XMP = 700;