TMI-136: Better TIFF (IFD) format write support.

This commit is contained in:
Harald Kuhr 2015-08-12 10:23:15 +02:00
parent 998851c9fb
commit 517fc770bd
3 changed files with 73 additions and 8 deletions

View File

@ -38,13 +38,14 @@ import com.twelvemonkeys.imageio.metadata.AbstractEntry;
* @version $Id: EXIFEntry.java,v 1.0 Nov 13, 2009 5:47:35 PM haraldk Exp$ * @version $Id: EXIFEntry.java,v 1.0 Nov 13, 2009 5:47:35 PM haraldk Exp$
*/ */
final class EXIFEntry extends AbstractEntry { final class EXIFEntry extends AbstractEntry {
// TODO: Expose as TIFFEntry
final private short type; final private short type;
EXIFEntry(final int identifier, final Object value, final short type) { EXIFEntry(final int identifier, final Object value, final short type) {
super(identifier, value); 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)); throw new IllegalArgumentException(String.format("Illegal TIFF type: %s", type));
} }
// TODO: Validate that type is applicable to value? // TODO: Validate that type is applicable to value?
@ -114,6 +115,8 @@ final class EXIFEntry extends AbstractEntry {
return "PlanarConfiguration"; return "PlanarConfiguration";
case TIFF.TAG_RESOLUTION_UNIT: case TIFF.TAG_RESOLUTION_UNIT:
return "ResolutionUnit"; return "ResolutionUnit";
case TIFF.TAG_PAGE_NAME:
return "PageName";
case TIFF.TAG_PAGE_NUMBER: case TIFF.TAG_PAGE_NUMBER:
return "PageNumber"; return "PageNumber";
case TIFF.TAG_SOFTWARE: case TIFF.TAG_SOFTWARE:
@ -228,6 +231,8 @@ final class EXIFEntry extends AbstractEntry {
return "DateTimeDigitized"; return "DateTimeDigitized";
case EXIF.TAG_IMAGE_NUMBER: case EXIF.TAG_IMAGE_NUMBER:
return "ImageNumber"; return "ImageNumber";
case EXIF.TAG_MAKER_NOTE:
return "MakerNote";
case EXIF.TAG_USER_COMMENT: case EXIF.TAG_USER_COMMENT:
return "UserComment"; return "UserComment";

View File

@ -40,6 +40,7 @@ import java.io.IOException;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
/** /**
@ -248,9 +249,12 @@ public final class EXIFWriter extends MetadataWriter {
switch (type) { switch (type) {
case TIFF.TYPE_UNDEFINED: case TIFF.TYPE_UNDEFINED:
case TIFF.TYPE_BYTE: case TIFF.TYPE_BYTE:
case TIFF.TYPE_SBYTE:
stream.write((byte[]) value); stream.write((byte[]) value);
break; break;
case TIFF.TYPE_SHORT: case TIFF.TYPE_SHORT:
case TIFF.TYPE_SSHORT:
short[] shorts; short[] shorts;
if (value instanceof short[]) { if (value instanceof short[]) {
@ -279,7 +283,9 @@ public final class EXIFWriter extends MetadataWriter {
stream.writeShorts(shorts, 0, shorts.length); stream.writeShorts(shorts, 0, shorts.length);
break; break;
case TIFF.TYPE_LONG: case TIFF.TYPE_LONG:
case TIFF.TYPE_SLONG:
int[] ints; int[] ints;
if (value instanceof int[]) { if (value instanceof int[]) {
@ -298,17 +304,45 @@ public final class EXIFWriter extends MetadataWriter {
} }
stream.writeInts(ints, 0, ints.length); stream.writeInts(ints, 0, ints.length);
break; break;
case TIFF.TYPE_RATIONAL: case TIFF.TYPE_RATIONAL:
case TIFF.TYPE_SRATIONAL:
Rational[] rationals = (Rational[]) value; Rational[] rationals = (Rational[]) value;
for (Rational rational : rationals) { for (Rational rational : rationals) {
stream.writeInt((int) rational.numerator()); stream.writeInt((int) rational.numerator());
stream.writeInt((int) rational.denominator()); stream.writeInt((int) rational.denominator());
} }
// TODO: More types break;
case TIFF.TYPE_FLOAT:
float[] floats;
if (value instanceof float[]) {
floats = (float[]) value;
}
else {
throw new IllegalArgumentException("Unsupported type for TIFF FLOAT: " + value.getClass());
}
stream.writeFloats(floats, 0, floats.length);
break;
case TIFF.TYPE_DOUBLE:
double[] doubles;
if (value instanceof double[]) {
doubles = (double[]) value;
}
else {
throw new IllegalArgumentException("Unsupported type for TIFF FLOAT: " + value.getClass());
}
stream.writeDoubles(doubles, 0, doubles.length);
break;
default: default:
throw new IllegalArgumentException("Unsupported TIFF type: " + type); throw new IllegalArgumentException("Unsupported TIFF type: " + type);
@ -319,27 +353,36 @@ public final class EXIFWriter extends MetadataWriter {
// } // }
else { else {
switch (type) { switch (type) {
case TIFF.TYPE_UNDEFINED:
case TIFF.TYPE_BYTE: case TIFF.TYPE_BYTE:
stream.writeByte((Integer) value); case TIFF.TYPE_SBYTE:
case TIFF.TYPE_UNDEFINED:
stream.writeByte(((Number) value).intValue());
break; break;
case TIFF.TYPE_ASCII: case TIFF.TYPE_ASCII:
byte[] bytes = ((String) value).getBytes(Charset.forName("UTF-8")); byte[] bytes = ((String) value).getBytes(StandardCharsets.UTF_8);
stream.write(bytes); stream.write(bytes);
stream.write(0); stream.write(0);
break; break;
case TIFF.TYPE_SHORT: case TIFF.TYPE_SHORT:
stream.writeShort((Integer) value); case TIFF.TYPE_SSHORT:
stream.writeShort(((Number) value).intValue());
break; break;
case TIFF.TYPE_LONG: case TIFF.TYPE_LONG:
case TIFF.TYPE_SLONG:
stream.writeInt(((Number) value).intValue()); stream.writeInt(((Number) value).intValue());
break; break;
case TIFF.TYPE_RATIONAL: case TIFF.TYPE_RATIONAL:
case TIFF.TYPE_SRATIONAL:
Rational rational = (Rational) value; Rational rational = (Rational) value;
stream.writeInt((int) rational.numerator()); stream.writeInt((int) rational.numerator());
stream.writeInt((int) rational.denominator()); stream.writeInt((int) rational.denominator());
break; break;
// TODO: More types case TIFF.TYPE_FLOAT:
stream.writeFloat(((Number) value).floatValue());
break;
case TIFF.TYPE_DOUBLE:
stream.writeDouble(((Number) value).doubleValue());
break;
default: default:
throw new IllegalArgumentException("Unsupported TIFF type: " + type); throw new IllegalArgumentException("Unsupported TIFF type: " + type);
@ -356,11 +399,26 @@ public final class EXIFWriter extends MetadataWriter {
} }
private short getType(final Entry entry) { private short getType(final Entry entry) {
// TODO: What a MESS! Rewrite and expose EXIFEntry as TIFFEntry or so...
// For internal entries use type directly
if (entry instanceof EXIFEntry) { if (entry instanceof EXIFEntry) {
EXIFEntry exifEntry = (EXIFEntry) entry; EXIFEntry exifEntry = (EXIFEntry) entry;
return exifEntry.getType(); return exifEntry.getType();
} }
// For other entries, use name if it matches
String typeName = entry.getTypeName();
if (typeName != null) {
for (int i = 1; i < TIFF.TYPE_NAMES.length; i++) {
if (typeName.equals(TIFF.TYPE_NAMES[i])) {
return (short) i;
}
}
}
// Otherwise, fall back to the native Java type
Object value = Validate.notNull(entry.getValue()); Object value = Validate.notNull(entry.getValue());
boolean array = value.getClass().isArray(); boolean array = value.getClass().isArray();

View File

@ -95,6 +95,7 @@ public interface TIFF {
null, null, null, null,
"LONG8", "SLONG8", "IFD8" "LONG8", "SLONG8", "IFD8"
}; };
/** Length of the corresponding type, in bytes. */
int[] TYPE_LENGTHS = { int[] TYPE_LENGTHS = {
-1, -1,
1, 1, 2, 4, 8, 1, 1, 2, 4, 8,
@ -165,6 +166,7 @@ public interface TIFF {
int TAG_IMAGE_DESCRIPTION = 270; int TAG_IMAGE_DESCRIPTION = 270;
int TAG_MAKE = 271; int TAG_MAKE = 271;
int TAG_MODEL = 272; int TAG_MODEL = 272;
int TAG_PAGE_NAME = 285;
int TAG_PAGE_NUMBER = 297; int TAG_PAGE_NUMBER = 297;
int TAG_SOFTWARE = 305; int TAG_SOFTWARE = 305;
int TAG_ARTIST = 315; int TAG_ARTIST = 315;