TMI-139: Work in progress: TIFF image metadata.

This commit is contained in:
Harald Kuhr
2015-06-22 11:11:37 +02:00
parent 7e65164b87
commit f4cc310096
18 changed files with 1041 additions and 265 deletions

View File

@@ -64,4 +64,8 @@ public interface Entry {
// For arrays only
int valueCount();
// TODO: getValueAsInt, UnsignedInt, Short, UnsignedShort, Byte, UnsignedByte etc
// TODO: getValueAsIntArray, ShortArray, ByteArray, StringArray etc (also for non-arrays, to return a single element array)
}

View File

@@ -43,7 +43,7 @@ final class EXIFEntry extends AbstractEntry {
EXIFEntry(final int identifier, final Object value, final short type) {
super(identifier, value);
if (type < 1 || type > TIFF.TYPE_NAMES.length) {
if (type < 1 || type >= TIFF.TYPE_NAMES.length) {
throw new IllegalArgumentException(String.format("Illegal EXIF type: %s", type));
}
@@ -86,8 +86,16 @@ final class EXIFEntry extends AbstractEntry {
return "Compression";
case TIFF.TAG_PHOTOMETRIC_INTERPRETATION:
return "PhotometricInterpretation";
case TIFF.TAG_FILL_ORDER:
return "FillOrder";
case TIFF.TAG_DOCUMENT_NAME:
return "DocumentName";
case TIFF.TAG_IMAGE_DESCRIPTION:
return "ImageDescription";
case TIFF.TAG_MAKE:
return "Make";
case TIFF.TAG_MODEL:
return "Model";
case TIFF.TAG_STRIP_OFFSETS:
return "StripOffsets";
case TIFF.TAG_ORIENTATION:
@@ -106,14 +114,8 @@ final class EXIFEntry extends AbstractEntry {
return "PlanarConfiguration";
case TIFF.TAG_RESOLUTION_UNIT:
return "ResolutionUnit";
case TIFF.TAG_JPEG_INTERCHANGE_FORMAT:
return "JPEGInterchangeFormat";
case TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH:
return "JPEGInterchangeFormatLength";
case TIFF.TAG_MAKE:
return "Make";
case TIFF.TAG_MODEL:
return "Model";
case TIFF.TAG_PAGE_NUMBER:
return "PageNumber";
case TIFF.TAG_SOFTWARE:
return "Software";
case TIFF.TAG_DATE_TIME:
@@ -140,10 +142,20 @@ final class EXIFEntry extends AbstractEntry {
return "YCbCrPositioning";
case TIFF.TAG_COLOR_MAP:
return "ColorMap";
case TIFF.TAG_INK_SET:
return "InkSet";
case TIFF.TAG_INK_NAMES:
return "InkNames";
case TIFF.TAG_EXTRA_SAMPLES:
return "ExtraSamples";
case TIFF.TAG_SAMPLE_FORMAT:
return "SampleFormat";
case TIFF.TAG_JPEG_TABLES:
return "JPEGTables";
case TIFF.TAG_JPEG_INTERCHANGE_FORMAT:
return "JPEGInterchangeFormat";
case TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH:
return "JPEGInterchangeFormatLength";
case TIFF.TAG_SUB_IFD:
return "SubIFD";
@@ -261,6 +273,6 @@ final class EXIFEntry extends AbstractEntry {
@Override
public String getTypeName() {
return TIFF.TYPE_NAMES[type - 1];
return TIFF.TYPE_NAMES[type];
}
}

View File

@@ -446,8 +446,8 @@ public final class EXIFReader extends MetadataReader {
}
static int getValueLength(final int pType, final int pCount) {
if (pType > 0 && pType <= TIFF.TYPE_LENGTHS.length) {
return TIFF.TYPE_LENGTHS[pType - 1] * pCount;
if (pType > 0 && pType < TIFF.TYPE_LENGTHS.length) {
return TIFF.TYPE_LENGTHS[pType] * pCount;
}
return -1;

View File

@@ -94,11 +94,11 @@ public final class EXIFWriter extends MetadataWriter {
stream.writeShort(42);
}
public long writeIFD(final Collection<Entry> entries, ImageOutputStream stream) throws IOException {
public long writeIFD(final Collection<Entry> entries, final ImageOutputStream stream) throws IOException {
return writeIFD(new IFD(entries), stream, false);
}
private long writeIFD(final Directory original, ImageOutputStream stream, boolean isSubIFD) throws IOException {
private long writeIFD(final Directory original, final ImageOutputStream stream, final boolean isSubIFD) throws IOException {
// TIFF spec says tags should be in increasing order, enforce that when writing
Directory ordered = ensureOrderedDirectory(original);
@@ -183,7 +183,7 @@ public final class EXIFWriter extends MetadataWriter {
private Directory ensureOrderedDirectory(final Directory directory) {
if (!isSorted(directory)) {
List<Entry> entries = new ArrayList<Entry>(directory.size());
List<Entry> entries = new ArrayList<>(directory.size());
for (Entry entry : directory) {
entries.add(entry);
@@ -217,7 +217,7 @@ public final class EXIFWriter extends MetadataWriter {
return true;
}
private long writeValue(Entry entry, long dataOffset, ImageOutputStream stream) throws IOException {
private long writeValue(final Entry entry, final long dataOffset, final ImageOutputStream stream) throws IOException {
short type = getType(entry);
int valueLength = EXIFReader.getValueLength(type, getCount(entry));
@@ -238,14 +238,15 @@ public final class EXIFWriter extends MetadataWriter {
}
}
private int getCount(Entry entry) {
private int getCount(final Entry entry) {
Object value = entry.getValue();
return value instanceof String ? ((String) value).getBytes(Charset.forName("UTF-8")).length + 1 : entry.valueCount();
}
private void writeValueInline(Object value, short type, ImageOutputStream stream) throws IOException {
private void writeValueInline(final Object value, final short type, final ImageOutputStream stream) throws IOException {
if (value.getClass().isArray()) {
switch (type) {
case TIFF.TYPE_UNDEFINED:
case TIFF.TYPE_BYTE:
stream.write((byte[]) value);
break;
@@ -293,7 +294,7 @@ public final class EXIFWriter extends MetadataWriter {
}
}
else {
throw new IllegalArgumentException("Unsupported type for TIFF SHORT: " + value.getClass());
throw new IllegalArgumentException("Unsupported type for TIFF LONG: " + value.getClass());
}
stream.writeInts(ints, 0, ints.length);
@@ -318,6 +319,7 @@ public final class EXIFWriter extends MetadataWriter {
// }
else {
switch (type) {
case TIFF.TYPE_UNDEFINED:
case TIFF.TYPE_BYTE:
stream.writeByte((Integer) value);
break;
@@ -345,7 +347,7 @@ public final class EXIFWriter extends MetadataWriter {
}
}
private void writeValueAt(long dataOffset, Object value, short type, ImageOutputStream stream) throws IOException {
private void writeValueAt(final long dataOffset, final Object value, final short type, final ImageOutputStream stream) throws IOException {
stream.writeInt(assertIntegerOffset(dataOffset));
long position = stream.getStreamPosition();
stream.seek(dataOffset);
@@ -353,7 +355,7 @@ public final class EXIFWriter extends MetadataWriter {
stream.seek(position);
}
private short getType(Entry entry) {
private short getType(final Entry entry) {
if (entry instanceof EXIFEntry) {
EXIFEntry exifEntry = (EXIFEntry) entry;
return exifEntry.getType();

View File

@@ -88,6 +88,7 @@ public interface TIFF {
Should probably all map to Java long (and fail if high bit is set for the unsigned types???)
*/
String[] TYPE_NAMES = {
null,
"BYTE", "ASCII", "SHORT", "LONG", "RATIONAL",
"SBYTE", "UNDEFINED", "SSHORT", "SLONG", "SRATIONAL", "FLOAT", "DOUBLE",
"IFD",
@@ -95,6 +96,7 @@ public interface TIFF {
"LONG8", "SLONG8", "IFD8"
};
int[] TYPE_LENGTHS = {
-1,
1, 1, 2, 4, 8,
1, 1, 2, 4, 8, 4, 8,
4,
@@ -124,6 +126,8 @@ public interface TIFF {
int TAG_YCBCR_POSITIONING = 531;
int TAG_X_RESOLUTION = 282;
int TAG_Y_RESOLUTION = 283;
int TAG_X_POSITION = 286;
int TAG_Y_POSITION = 287;
int TAG_RESOLUTION_UNIT = 296;
/// B. Tags relating to recording offset
@@ -131,6 +135,7 @@ public interface TIFF {
int TAG_STRIP_OFFSETS = 273;
int TAG_ROWS_PER_STRIP = 278;
int TAG_STRIP_BYTE_COUNTS = 279;
int TAG_FREE_OFFSETS = 288; // "Not recommended for general interchange."
// "Old-style" JPEG (still used as EXIF thumbnail)
int TAG_JPEG_INTERCHANGE_FORMAT = 513;
int TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = 514;
@@ -153,6 +158,7 @@ public interface TIFF {
/// D. Other tags
int TAG_DATE_TIME = 306;
int TAG_DOCUMENT_NAME = 269;
int TAG_IMAGE_DESCRIPTION = 270;
int TAG_MAKE = 271;
int TAG_MODEL = 272;

View File

@@ -71,6 +71,7 @@ public final class XMPReader extends MetadataReader {
// TODO: Consider parsing using SAX?
// TODO: Determine encoding and parse using a Reader...
// TODO: Refactor scanner to return inputstream?
// TODO: Be smarter about ASCII-NULL termination/padding (the SAXParser aka Xerces DOMParser doesn't like it)...
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(IIOUtil.createStreamAdapter(input)));