diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/AbstractCompoundDirectory.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/AbstractCompoundDirectory.java
new file mode 100644
index 00000000..6cd35e0b
--- /dev/null
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/AbstractCompoundDirectory.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2012, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.imageio.metadata;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import static com.twelvemonkeys.lang.Validate.noNullElements;
+
+/**
+ * AbstractCompoundDirectory
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: AbstractCompoundDirectory.java,v 1.0 02.01.12 12:43 haraldk Exp$
+ */
+public abstract class AbstractCompoundDirectory extends AbstractDirectory implements CompoundDirectory {
+ private final List directories = new ArrayList();
+
+ protected AbstractCompoundDirectory(final Collection extends Directory> directories) {
+ super(null);
+
+ if (directories != null) {
+ this.directories.addAll(noNullElements(directories));
+ }
+ }
+
+ public Directory getDirectory(int index) {
+ return directories.get(index);
+ }
+
+ public int directoryCount() {
+ return directories.size();
+ }
+
+ @Override
+ public Entry getEntryById(final Object identifier) {
+ for (Directory directory : directories) {
+ Entry entry = directory.getEntryById(identifier);
+
+ if (entry != null) {
+ return entry;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public Entry getEntryByFieldName(final String fieldName) {
+ for (Directory directory : directories) {
+ Entry entry = directory.getEntryByFieldName(fieldName);
+
+ if (entry != null) {
+ return entry;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return new Iterator() {
+ Iterator directoryIterator = directories.iterator();
+ Iterator current;
+
+ public boolean hasNext() {
+ return current != null && current.hasNext() || directoryIterator.hasNext() && (current = directoryIterator.next().iterator()).hasNext();
+ }
+
+ public Entry next() {
+ hasNext();
+
+ return current.next();
+ }
+
+ public void remove() {
+ current.remove();
+ }
+ };
+ }
+
+ // TODO: Define semantics, or leave to subclasses?
+ // Add to first/last directory?
+ // Introduce a "current" directory? And a way to advance/go back
+ // Remove form the first directory that contains entry?
+ @Override
+ public boolean add(final Entry entry) {
+ throw new UnsupportedOperationException("Directory is read-only");
+ }
+
+ @Override
+ public boolean remove(final Object entry) {
+ throw new UnsupportedOperationException("Directory is read-only");
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return true;
+ }
+
+ @Override
+ public int size() {
+ int size = 0;
+
+ for (Directory directory : directories) {
+ size += directory.size();
+ }
+
+ return size;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s%s", getClass().getSimpleName(), directories.toString());
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 0;
+
+ for (Directory ifd : directories) {
+ hash ^= ifd.hashCode();
+ }
+
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object pOther) {
+ if (pOther == this) {
+ return true;
+ }
+ if (pOther == null) {
+ return false;
+ }
+ if (pOther.getClass() != getClass()) {
+ return false;
+ }
+
+ CompoundDirectory other = (CompoundDirectory) pOther;
+
+ if (directoryCount() != other.directoryCount()) {
+ return false;
+ }
+
+ for (int i = 0; i < directoryCount(); i++) {
+ if (!getDirectory(i).equals(other.getDirectory(i))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/AbstractDirectory.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/AbstractDirectory.java
index e1c7a2ab..93cf0c0f 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/AbstractDirectory.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/AbstractDirectory.java
@@ -28,10 +28,9 @@
package com.twelvemonkeys.imageio.metadata;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
+import java.util.*;
+
+import static com.twelvemonkeys.lang.Validate.noNullElements;
/**
* AbstractDirectory
@@ -42,10 +41,11 @@ import java.util.List;
*/
public abstract class AbstractDirectory implements Directory {
private final List entries = new ArrayList();
+ private final List unmodifiable = Collections.unmodifiableList(entries);
- protected AbstractDirectory(final Collection extends Entry> pEntries) {
- if (pEntries != null) {
- entries.addAll(pEntries);
+ protected AbstractDirectory(final Collection extends Entry> entries) {
+ if (entries != null) {
+ this.entries.addAll(noNullElements(entries));
}
}
@@ -70,7 +70,7 @@ public abstract class AbstractDirectory implements Directory {
}
public Iterator iterator() {
- return entries.iterator();
+ return isReadOnly() ? unmodifiable.iterator() : entries.iterator();
}
/**
@@ -127,7 +127,7 @@ public abstract class AbstractDirectory implements Directory {
return true;
}
- if (getClass() != pOther.getClass()) {
+ if (pOther == null || getClass() != pOther.getClass()) {
return false;
}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/AbstractEntry.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/AbstractEntry.java
index f15bf729..d9b59dc3 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/AbstractEntry.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/AbstractEntry.java
@@ -56,6 +56,17 @@ public abstract class AbstractEntry implements Entry {
return identifier;
}
+ /**
+ * Returns a format-native identifier.
+ * For example {@code "2:00"} for IPTC "Record Version" field, or {@code "0x040c"} for PSD "Thumbnail" resource.
+ * This default implementation simply returns {@code String.valueOf(getIdentifier())}.
+ *
+ * @return a format-native identifier.
+ */
+ protected String getNativeIdentifier() {
+ return String.valueOf(getIdentifier());
+ }
+
/**
* Returns {@code null}, meaning unknown or undefined.
*
@@ -109,7 +120,7 @@ public abstract class AbstractEntry implements Entry {
return String.valueOf(value) + " (" + valueCount() + ")";
}
- if (value.getClass().isArray() && Array.getLength(value) == 1) {
+ if (value != null && value.getClass().isArray() && Array.getLength(value) == 1) {
return String.valueOf(Array.get(value, 0));
}
@@ -137,7 +148,7 @@ public abstract class AbstractEntry implements Entry {
@Override
public int hashCode() {
- return identifier.hashCode() + 31 * value.hashCode();
+ return identifier.hashCode() + (value != null ? 31 * value.hashCode() : 0);
}
@Override
@@ -164,6 +175,6 @@ public abstract class AbstractEntry implements Entry {
String type = getTypeName();
String typeStr = type != null ? " (" + type + ")" : "";
- return String.format("%s%s: %s%s", getIdentifier(), nameStr, getValueAsString(), typeStr);
+ return String.format("%s%s: %s%s", getNativeIdentifier(), nameStr, getValueAsString(), typeStr);
}
}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/CompoundDirectory.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/CompoundDirectory.java
new file mode 100644
index 00000000..7bde3ae2
--- /dev/null
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/CompoundDirectory.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.imageio.metadata;
+
+/**
+ * CompoundDirectory
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: CompoundDirectory.java,v 1.0 02.01.12 12:37 haraldk Exp$
+ */
+public interface CompoundDirectory extends Directory {
+ Directory getDirectory(int index);
+
+ int directoryCount();
+}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/Entry.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/Entry.java
index 8681382e..ce7c6074 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/Entry.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/Entry.java
@@ -39,7 +39,7 @@ public interface Entry {
// "tag" identifier from spec
Object getIdentifier();
- // Human readable "tag" (field) name from sepc
+ // Human readable "tag" (field) name from spec
String getFieldName();
// The internal "tag" value as stored in the stream, may be a Directory
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFDirectory.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFDirectory.java
index 332ae6fd..1ac2a027 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFDirectory.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFDirectory.java
@@ -28,8 +28,8 @@
package com.twelvemonkeys.imageio.metadata.exif;
-import com.twelvemonkeys.imageio.metadata.AbstractDirectory;
-import com.twelvemonkeys.imageio.metadata.Entry;
+import com.twelvemonkeys.imageio.metadata.AbstractCompoundDirectory;
+import com.twelvemonkeys.imageio.metadata.Directory;
import java.util.Collection;
@@ -40,8 +40,8 @@ import java.util.Collection;
* @author last modified by $Author: haraldk$
* @version $Id: EXIFDirectory.java,v 1.0 Nov 11, 2009 5:02:59 PM haraldk Exp$
*/
-final class EXIFDirectory extends AbstractDirectory {
- EXIFDirectory(final Collection extends Entry> entries) {
- super(entries);
+final class EXIFDirectory extends AbstractCompoundDirectory {
+ EXIFDirectory(final Collection extends Directory> directories) {
+ super(directories);
}
}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFEntry.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFEntry.java
index d40d29c8..bb8e1d0d 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFEntry.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFEntry.java
@@ -43,9 +43,9 @@ 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) {
-// throw new IllegalArgumentException(String.format("Illegal EXIF type: %s", type));
-// }
+ if (type < 1 || type > TIFF.TYPE_NAMES.length) {
+ throw new IllegalArgumentException(String.format("Illegal EXIF type: %s", type));
+ }
this.type = type;
}
@@ -74,12 +74,20 @@ final class EXIFEntry extends AbstractEntry {
return "ImageHeight";
case TIFF.TAG_COMPRESSION:
return "Compression";
+ case TIFF.TAG_PHOTOMETRIC_INTERPRETATION:
+ return "PhotometricInterpretation";
+ case TIFF.TAG_IMAGE_DESCRIPTION:
+ return "ImageDescription";
case TIFF.TAG_ORIENTATION:
return "Orientation";
+ case TIFF.TAG_SAMPLES_PER_PIXELS:
+ return "SamplesPerPixels";
case TIFF.TAG_X_RESOLUTION:
return "XResolution";
case TIFF.TAG_Y_RESOLUTION:
return "YResolution";
+ case TIFF.TAG_PLANAR_CONFIGURATION:
+ return "PlanarConfiguration";
case TIFF.TAG_RESOLUTION_UNIT:
return "ResolutionUnit";
case TIFF.TAG_JPEG_INTERCHANGE_FORMAT:
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFReader.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFReader.java
index e5ef402e..966482f3 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFReader.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFReader.java
@@ -28,10 +28,12 @@
package com.twelvemonkeys.imageio.metadata.exif;
+import com.twelvemonkeys.imageio.metadata.AbstractCompoundDirectory;
import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.MetadataReader;
import com.twelvemonkeys.lang.StringUtil;
+import com.twelvemonkeys.lang.Validate;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
@@ -40,10 +42,7 @@ import java.io.File;
import java.io.IOException;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
/**
* EXIFReader
@@ -53,10 +52,12 @@ import java.util.List;
* @version $Id: EXIFReader.java,v 1.0 Nov 13, 2009 5:42:51 PM haraldk Exp$
*/
public final class EXIFReader extends MetadataReader {
- static final Collection KNOWN_IFDS = Arrays.asList(TIFF.TAG_EXIF_IFD, TIFF.TAG_GPS_IFD, TIFF.TAG_INTEROP_IFD);
+ static final Collection KNOWN_IFDS = Collections.unmodifiableCollection(Arrays.asList(TIFF.TAG_EXIF_IFD, TIFF.TAG_GPS_IFD, TIFF.TAG_INTEROP_IFD));
@Override
public Directory read(final ImageInputStream input) throws IOException {
+ Validate.notNull(input, "input");
+
byte[] bom = new byte[2];
input.readFully(bom);
@@ -82,7 +83,8 @@ public final class EXIFReader extends MetadataReader {
return readDirectory(input, directoryOffset);
}
- private EXIFDirectory readDirectory(final ImageInputStream pInput, final long pOffset) throws IOException {
+ private Directory readDirectory(final ImageInputStream pInput, final long pOffset) throws IOException {
+ List ifds = new ArrayList();
List entries = new ArrayList();
pInput.seek(pOffset);
int entryCount = pInput.readUnsignedShort();
@@ -92,14 +94,15 @@ public final class EXIFReader extends MetadataReader {
}
long nextOffset = pInput.readUnsignedInt();
-
+
// Read linked IFDs
if (nextOffset != 0) {
- EXIFDirectory next = readDirectory(pInput, nextOffset);
-
- for (Entry entry : next) {
- entries.add(entry);
- }
+ // TODO: This is probably not okay anymore.. Replace recursion with while loop
+ Directory next = readDirectory(pInput, nextOffset);
+ ifds.add((IFD) ((AbstractCompoundDirectory) next).getDirectory(0));
+// for (Entry entry : next) {
+// entries.add(entry);
+// }
}
// TODO: Make what sub-IFDs to parse optional? Or leave this to client code? At least skip the non-TIFF data?
@@ -113,7 +116,9 @@ public final class EXIFReader extends MetadataReader {
)
);
- return new EXIFDirectory(entries);
+ ifds.add(0, new IFD(entries));
+
+ return new EXIFDirectory(ifds);
}
// private Directory readForeignMetadata(final MetadataReader reader, final byte[] bytes) throws IOException {
@@ -161,7 +166,7 @@ public final class EXIFReader extends MetadataReader {
directory = new CompoundDocument(new ByteArrayInputStream((byte[]) entry.getValue())).getRootEntry();
}
else*/ if (KNOWN_IFDS.contains(tagId)) {
- directory = readDirectory(input, getPointerOffset(entry));
+ directory = ((AbstractCompoundDirectory) readDirectory(input, getPointerOffset(entry))).getDirectory(0);
}
else {
continue;
@@ -273,9 +278,11 @@ public final class EXIFReader extends MetadataReader {
switch (pType) {
case 2: // ASCII
// TODO: This might be UTF-8 or ISO-8859-x, even though spec says ASCII
+ // TODO: Fail if unknown chars, try parsing with ISO-8859-1 or file.encoding
byte[] ascii = new byte[pCount];
pInput.readFully(ascii);
- return StringUtil.decode(ascii, 0, ascii.length, "UTF-8"); // UTF-8 is ASCII compatible
+ int len = ascii[ascii.length - 1] == 0 ? ascii.length - 1 : ascii.length;
+ return StringUtil.decode(ascii, 0, len, "UTF-8"); // UTF-8 is ASCII compatible
case 1: // BYTE
if (pCount == 1) {
return pInput.readUnsignedByte();
@@ -399,10 +406,8 @@ public final class EXIFReader extends MetadataReader {
return longs;
default:
- // Spec says skip unknown values:
- // TODO: Rather just return null, UNKNOWN_TYPE or new Unknown(type, count, offset) for value?
+ // Spec says skip unknown values
return new Unknown(pType, pCount, pos);
-// throw new IIOException(String.format("Unknown EXIF type '%s' at pos %d", pType, pInput.getStreamPosition()));
}
}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/IFD.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/IFD.java
new file mode 100644
index 00000000..0340517d
--- /dev/null
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/IFD.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2011, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.imageio.metadata.exif;
+
+import com.twelvemonkeys.imageio.metadata.AbstractDirectory;
+import com.twelvemonkeys.imageio.metadata.Entry;
+
+import java.util.Collection;
+
+/**
+ * IFD
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: IFD.java,v 1.0 23.12.11 16:24 haraldk Exp$
+ */
+final class IFD extends AbstractDirectory {
+ protected IFD(final Collection extends Entry> pEntries) {
+ super(pEntries);
+ }
+}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/Unknown.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/Unknown.java
index dc5fb965..0fd8b7db 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/Unknown.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/Unknown.java
@@ -6,6 +6,7 @@ package com.twelvemonkeys.imageio.metadata.exif;
* @author Harald Kuhr
* @author last modified by $Author: haraldk$
* @version $Id: Unknown.java,v 1.0 Oct 8, 2010 3:38:45 PM haraldk Exp$
+ * @see There are known knowns
*/
final class Unknown {
private final short type;
@@ -24,8 +25,8 @@ final class Unknown {
}
@Override
- public boolean equals(Object other) {
- if (other != null && other.getClass() == getClass()){
+ public boolean equals(final Object other) {
+ if (other != null && other.getClass() == getClass()) {
Unknown unknown = (Unknown) other;
return pos == unknown.pos && type == unknown.type && count == unknown.count;
}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/iptc/IPTCEntry.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/iptc/IPTCEntry.java
index 2f14cd47..be129c3e 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/iptc/IPTCEntry.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/iptc/IPTCEntry.java
@@ -45,6 +45,8 @@ class IPTCEntry extends AbstractEntry {
@Override
public String getFieldName() {
switch ((Integer) getIdentifier()) {
+ case IPTC.TAG_RECORD_VERSION:
+ return "RecordVersion";
case IPTC.TAG_SOURCE:
return "Source";
// TODO: More tags...
@@ -52,4 +54,10 @@ class IPTCEntry extends AbstractEntry {
return null;
}
+
+ @Override
+ protected String getNativeIdentifier() {
+ int identifier = (Integer) getIdentifier();
+ return String.format("%d:%02d", identifier >> 8, identifier & 0xff);
+ }
}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/iptc/IPTCReader.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/iptc/IPTCReader.java
index 22dfb3bb..a9fad969 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/iptc/IPTCReader.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/iptc/IPTCReader.java
@@ -32,6 +32,7 @@ import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.MetadataReader;
import com.twelvemonkeys.lang.StringUtil;
+import com.twelvemonkeys.lang.Validate;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
@@ -62,7 +63,9 @@ public final class IPTCReader extends MetadataReader {
@Override
public Directory read(final ImageInputStream input) throws IOException {
- final List entries = new ArrayList();
+ Validate.notNull(input, "input");
+
+ List entries = new ArrayList();
// 0x1c identifies start of a tag
while (input.read() == 0x1c) {
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/jpeg/JPEG.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/jpeg/JPEG.java
index bdc8726d..dd2f781b 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/jpeg/JPEG.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/jpeg/JPEG.java
@@ -43,6 +43,7 @@ public interface JPEG {
int APP0 = 0xFFE0;
int APP1 = 0xFFE1;
int APP2 = 0xFFE2;
+ int APP13 = 0xFFED;
int APP14 = 0xFFEE;
int SOF0 = 0xFFC0;
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/psd/PSDDirectory.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/psd/PSDDirectory.java
new file mode 100644
index 00000000..cd9af244
--- /dev/null
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/psd/PSDDirectory.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2012, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.imageio.metadata.psd;
+
+import com.twelvemonkeys.imageio.metadata.AbstractDirectory;
+import com.twelvemonkeys.imageio.metadata.Entry;
+
+import java.util.Collection;
+
+/**
+ * PhotoshopDirectory
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: PhotoshopDirectory.java,v 1.0 04.01.12 11:58 haraldk Exp$
+ */
+final class PSDDirectory extends AbstractDirectory {
+ public PSDDirectory(final Collection extends Entry> entries) {
+ super(entries);
+ }
+}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/psd/PSDEntry.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/psd/PSDEntry.java
new file mode 100644
index 00000000..c76bb6a3
--- /dev/null
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/psd/PSDEntry.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.imageio.metadata.psd;
+
+import com.twelvemonkeys.imageio.metadata.AbstractEntry;
+
+/**
+ * PhotoshopEntry
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: PhotoshopEntry.java,v 1.0 04.01.12 11:58 haraldk Exp$
+ */
+class PSDEntry extends AbstractEntry {
+ public PSDEntry(final int resourceId, final Object value) {
+ super(resourceId, value);
+ }
+
+ @Override
+ protected String getNativeIdentifier() {
+ return String.format("0x%04x", (Integer) getIdentifier());
+ }
+}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/psd/PSDReader.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/psd/PSDReader.java
new file mode 100644
index 00000000..a8ec02f4
--- /dev/null
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/psd/PSDReader.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2012, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.imageio.metadata.psd;
+
+import com.twelvemonkeys.imageio.metadata.Directory;
+import com.twelvemonkeys.imageio.metadata.MetadataReader;
+import com.twelvemonkeys.imageio.stream.SubImageInputStream;
+import com.twelvemonkeys.lang.StringUtil;
+import com.twelvemonkeys.lang.Validate;
+
+import javax.imageio.IIOException;
+import javax.imageio.stream.ImageInputStream;
+import java.io.DataInput;
+import java.io.EOFException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * PhotoshopReader
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: PhotoshopReader.java,v 1.0 04.01.12 11:56 haraldk Exp$
+ */
+public final class PSDReader extends MetadataReader {
+
+ // TODO: Add constructor to allow optional parsing of resources
+ // TODO: Maybe this should be modelled more like the JPEG segment parsing, as it's all binary data...
+ // - Segment/SegmentReader + List
+
+ @Override
+ public Directory read(final ImageInputStream input) throws IOException {
+ Validate.notNull(input, "input");
+
+ List entries = new ArrayList();
+
+ while (true) {
+ try {
+ int type = input.readInt();
+
+ if (type != PSDResource.RESOURCE_TYPE) {
+ throw new IIOException(String.format("Wrong image resource type, expected '8BIM': '%08x'", type));
+ }
+
+ short id = input.readShort();
+
+ PSDResource resource = new PSDResource(id, input);
+ entries.add(new PSDEntry(id, resource.data()));
+
+ }
+ catch (EOFException e) {
+ break;
+ }
+ }
+
+ return new PSDDirectory(entries);
+ }
+
+ protected static class PSDResource {
+ static final int RES_IPTC_NAA = 0x0404;
+ static final int RESOURCE_TYPE = ('8' << 24) + ('B' << 16) + ('I' << 8) + 'M';
+
+ static String readPascalString(final DataInput pInput) throws IOException {
+ int length = pInput.readUnsignedByte();
+
+ if (length == 0) {
+ return "";
+ }
+
+ byte[] bytes = new byte[length];
+ pInput.readFully(bytes);
+
+ return StringUtil.decode(bytes, 0, bytes.length, "ASCII");
+ }
+
+ final short id;
+ final String name;
+ final long size;
+
+ byte[] data;
+
+ PSDResource(final short resourceId, final ImageInputStream input) throws IOException {
+ id = resourceId;
+
+ name = readPascalString(input);
+
+ // Skip pad
+ int nameSize = name.length() + 1;
+ if (nameSize % 2 != 0) {
+ input.readByte();
+ }
+
+ size = input.readUnsignedInt();
+ long startPos = input.getStreamPosition();
+
+ readData(new SubImageInputStream(input, size));
+
+ // NOTE: This should never happen, however it's safer to keep it here to
+ if (input.getStreamPosition() != startPos + size) {
+ input.seek(startPos + size);
+ }
+
+ // Data is even-padded (word aligned)
+ if (size % 2 != 0) {
+ input.read();
+ }
+ }
+
+ protected void readData(final ImageInputStream pInput) throws IOException {
+ // TODO: This design is ugly, as subclasses readData is invoked BEFORE their respective constructor...
+ data = new byte[(int) size];
+ pInput.readFully(data);
+ }
+
+ public final byte[] data() {
+ return data;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = toStringBuilder();
+
+ builder.append(", data length: ");
+ builder.append(size);
+ builder.append("]");
+
+ return builder.toString();
+ }
+
+ protected StringBuilder toStringBuilder() {
+ StringBuilder builder = new StringBuilder(getClass().getSimpleName());
+
+ builder.append("[ID: 0x");
+ builder.append(Integer.toHexString(id));
+ if (name != null && name.trim().length() != 0) {
+ builder.append(", name: \"");
+ builder.append(name);
+ builder.append("\"");
+ }
+
+ return builder;
+ }
+ }
+
+}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/RDFDescription.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/RDFDescription.java
new file mode 100644
index 00000000..2e6c7eda
--- /dev/null
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/RDFDescription.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2012, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.imageio.metadata.xmp;
+
+import com.twelvemonkeys.imageio.metadata.AbstractDirectory;
+import com.twelvemonkeys.imageio.metadata.Entry;
+
+import java.util.Collection;
+
+/**
+* RDFDescription
+*
+* @author Harald Kuhr
+* @author last modified by $Author: haraldk$
+* @version $Id: RDFDescription.java,v 1.0 Nov 17, 2009 9:38:58 PM haraldk Exp$
+*/
+final class RDFDescription extends AbstractDirectory {
+ private final String namespace;
+
+ // TODO: Store size of root directory, to allow serializing
+ // TODO: XMPDirectory, maybe not even an AbstractDirectory
+ // - Keeping the Document would allow for easier serialization
+ // TODO: Or use direct SAX parsing
+ public RDFDescription(Collection extends Entry> entries) {
+ this(null, entries);
+ }
+
+ public RDFDescription(String key, Collection extends Entry> entries) {
+ super(entries);
+
+ namespace = key;
+ }
+
+ @Override
+ public String toString() {
+ return namespace != null ?
+ super.toString().replaceAll("^RDFDescription\\[", String.format("%s[%s|%s, ", getClass().getSimpleName(), XMP.DEFAULT_NS_MAPPING.get(namespace), namespace)) :
+ super.toString();
+ }
+}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMP.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMP.java
index d36cc6c4..c624afe6 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMP.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMP.java
@@ -30,6 +30,7 @@ package com.twelvemonkeys.imageio.metadata.xmp;
import java.util.Collections;
import java.util.Map;
+import java.util.Set;
/**
* XMP
@@ -59,6 +60,10 @@ public interface XMP {
String NS_XAP_MM = "http://ns.adobe.com/xap/1.0/mm/";
+ String NS_X = "adobe:ns:meta/";
+
/** Contains the mapping from URI to default namespace prefix. */
- Map DEFAULT_NS_MAPPING = Collections.unmodifiableMap(new XMPNamespaceMapping());
+ Map DEFAULT_NS_MAPPING = Collections.unmodifiableMap(new XMPNamespaceMapping(true));
+
+ Set ELEMENTS = Collections.unmodifiableSet(new XMPNamespaceMapping(false).keySet());
}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMPDirectory.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMPDirectory.java
index 484a5cf9..565f7d76 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMPDirectory.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMPDirectory.java
@@ -28,10 +28,10 @@
package com.twelvemonkeys.imageio.metadata.xmp;
-import com.twelvemonkeys.imageio.metadata.AbstractDirectory;
-import com.twelvemonkeys.imageio.metadata.Entry;
+import com.twelvemonkeys.imageio.metadata.AbstractCompoundDirectory;
+import com.twelvemonkeys.imageio.metadata.Directory;
-import java.util.List;
+import java.util.Collection;
/**
* XMPDirectory
@@ -40,12 +40,30 @@ import java.util.List;
* @author last modified by $Author: haraldk$
* @version $Id: XMPDirectory.java,v 1.0 Nov 17, 2009 9:38:58 PM haraldk Exp$
*/
-final class XMPDirectory extends AbstractDirectory {
- // TODO: Store size of root directory, to allow serializing
+final class XMPDirectory extends AbstractCompoundDirectory {
+ // TODO: Allow lookup of directories by namespace?
+ // TODO: Allow merge/sync/comparison with IPTC/EXIF/TIFF metadata
+ // TODO: Store size of root directory, to allow easy serializing (see isReadOnly comment)
// TODO: XMPDirectory, maybe not even an AbstractDirectory
// - Keeping the Document would allow for easier serialization
// TODO: Or use direct SAX parsing
- public XMPDirectory(List entries) {
+ private final String toolkit;
+
+ public XMPDirectory(Collection extends Directory> entries, String toolkit) {
super(entries);
+
+ this.toolkit = toolkit;
+ }
+
+ // TODO: Expose x:xmptk (getXMPToolkit(): String)
+ /*public*/ String getWriterToolkit() {
+ return toolkit;
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ // TODO: Depend on / for writable/read-only respectively?
+ // Spec says allow writing (even if "r"), if the container format is understood (ie. single file, known format, update checksums etc)
+ return super.isReadOnly();
}
}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMPEntry.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMPEntry.java
index 98163b69..c4c7f1da 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMPEntry.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMPEntry.java
@@ -40,6 +40,7 @@ import com.twelvemonkeys.imageio.metadata.AbstractEntry;
final class XMPEntry extends AbstractEntry {
private final String fieldName;
+ // TODO: Rewrite to use namespace + field instead of identifier (for the nativeIdentifier) method
public XMPEntry(final String identifier, final Object pValue) {
this(identifier, null, pValue);
}
@@ -49,6 +50,13 @@ final class XMPEntry extends AbstractEntry {
this.fieldName = fieldName;
}
+ @Override
+ protected String getNativeIdentifier() {
+ String identifier = (String) getIdentifier();
+ String namespace = fieldName != null && identifier.endsWith(fieldName) ? XMP.DEFAULT_NS_MAPPING.get(identifier.substring(0, identifier.length() - fieldName.length())) : null;
+ return namespace != null ? namespace + ":" + fieldName : identifier;
+ }
+
@SuppressWarnings({"SuspiciousMethodCalls"})
@Override
public String getFieldName() {
@@ -60,6 +68,6 @@ final class XMPEntry extends AbstractEntry {
String type = getTypeName();
String typeStr = type != null ? " (" + type + ")" : "";
- return String.format("%s: %s%s", getIdentifier(), getValueAsString(), typeStr);
+ return String.format("%s: %s%s", getNativeIdentifier(), getValueAsString(), typeStr);
}
}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMPNamespaceMapping.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMPNamespaceMapping.java
index 0170acb3..af1729c9 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMPNamespaceMapping.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMPNamespaceMapping.java
@@ -38,8 +38,12 @@ import java.util.HashMap;
* @version $Id: XMPNamespaceMapping.java,v 1.0 Nov 17, 2009 6:35:21 PM haraldk Exp$
*/
final class XMPNamespaceMapping extends HashMap {
- public XMPNamespaceMapping() {
- put(XMP.NS_RDF, "rdf");
+ public XMPNamespaceMapping(boolean includeNonElements) {
+ if (includeNonElements) {
+ put(XMP.NS_RDF, "rdf");
+ put(XMP.NS_X, "x");
+ }
+
put(XMP.NS_DC, "dc");
put(XMP.NS_EXIF, "exif");
put(XMP.NS_PHOTOSHOP, "photoshop");
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMPReader.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMPReader.java
index 2e470822..e43d7557 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMPReader.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/xmp/XMPReader.java
@@ -32,6 +32,7 @@ import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.MetadataReader;
import com.twelvemonkeys.imageio.util.IIOUtil;
+import com.twelvemonkeys.lang.Validate;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
@@ -55,17 +56,13 @@ import java.util.*;
* @version $Id: XMPReader.java,v 1.0 Nov 14, 2009 11:04:30 PM haraldk Exp$
*/
public final class XMPReader extends MetadataReader {
+ // See http://www.scribd.com/doc/56852716/XMPSpecificationPart1
+
+ // TODO: Types? Probably defined in XMP/RDF XML schema. Or are we happy that everything is a string?
+
@Override
public Directory read(final ImageInputStream input) throws IOException {
-// pInput.mark();
-//
-// BufferedReader reader = new BufferedReader(new InputStreamReader(IIOUtil.createStreamAdapter(pInput), Charset.forName("UTF-8")));
-// String line;
-// while ((line = reader.readLine()) != null) {
-// System.out.println(line);
-// }
-//
-// pInput.reset();
+ Validate.notNull(input, "input");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
@@ -80,14 +77,11 @@ public final class XMPReader extends MetadataReader {
// XMLSerializer serializer = new XMLSerializer(System.err, System.getProperty("file.encoding"));
// serializer.serialize(document);
-
- // Each rdf:Description is a Directory (but we can't really rely on that structure.. it's only convention)
- // - Each element inside the rdf:Desc is an Entry
-
+ String toolkit = getToolkit(document);
Node rdfRoot = document.getElementsByTagNameNS(XMP.NS_RDF, "RDF").item(0);
NodeList descriptions = document.getElementsByTagNameNS(XMP.NS_RDF, "Description");
- return parseDirectories(rdfRoot, descriptions);
+ return parseDirectories(rdfRoot, descriptions, toolkit);
}
catch (SAXException e) {
throw new IIOException(e.getMessage(), e);
@@ -97,7 +91,19 @@ public final class XMPReader extends MetadataReader {
}
}
- private XMPDirectory parseDirectories(final Node pParentNode, NodeList pNodes) {
+ private String getToolkit(Document document) {
+ NodeList xmpmeta = document.getElementsByTagNameNS(XMP.NS_X, "xmpmeta");
+
+ if (xmpmeta == null || xmpmeta.getLength() <= 0) {
+ return null;
+ }
+
+ Node toolkit = xmpmeta.item(0).getAttributes().getNamedItemNS(XMP.NS_X, "xmptk");
+
+ return toolkit != null ? toolkit.getNodeValue() : null;
+ }
+
+ private XMPDirectory parseDirectories(final Node pParentNode, NodeList pNodes, String toolkit) {
Map> subdirs = new LinkedHashMap>();
for (Node desc : asIterable(pNodes)) {
@@ -105,6 +111,9 @@ public final class XMPReader extends MetadataReader {
continue;
}
+ // Support attribute short-hand syntax
+ parseAttributesForKnownElements(subdirs, desc);
+
for (Node node : asIterable(desc.getChildNodes())) {
if (node.getNodeType() != Node.ELEMENT_NODE) {
continue;
@@ -121,6 +130,7 @@ public final class XMPReader extends MetadataReader {
Node parseType = node.getAttributes().getNamedItemNS(XMP.NS_RDF, "parseType");
if (parseType != null && "Resource".equals(parseType.getNodeValue())) {
+ // See: http://www.w3.org/TR/REC-rdf-syntax/#section-Syntax-parsetype-resource
List entries = new ArrayList();
for (Node child : asIterable(node.getChildNodes())) {
@@ -130,60 +140,119 @@ public final class XMPReader extends MetadataReader {
entries.add(new XMPEntry(child.getNamespaceURI() + child.getLocalName(), child.getLocalName(), getChildTextValue(child)));
}
- value = new XMPDirectory(entries);
+
+ value = new RDFDescription(entries);
}
else {
- // TODO: Support alternative RDF syntax (short-form), using attributes on desc
-// NamedNodeMap attributes = node.getAttributes();
-//
-// for (Node attr : asIterable(attributes)) {
-// System.out.println("attr.getNodeName(): " + attr.getNodeName());
-// System.out.println("attr.getNodeValue(): " + attr.getNodeValue());
-// }
+ // TODO: This method contains loads of duplication an should be cleaned up...
+ // Support attribute short-hand syntax
+ Map> subsubdirs = new LinkedHashMap>();
- value = getChildTextValue(node);
+ parseAttributesForKnownElements(subsubdirs, node);
+
+ if (!subsubdirs.isEmpty()) {
+ List entries = new ArrayList();
+
+ for (Map.Entry> entry : subsubdirs.entrySet()) {
+ entries.addAll(entry.getValue());
+ }
+
+ value = new RDFDescription(entries);
+ }
+ else {
+ value = getChildTextValue(node);
+ }
}
- XMPEntry entry = new XMPEntry(node.getNamespaceURI() + node.getLocalName(), node.getLocalName(), value);
- dir.add(entry);
+ dir.add(new XMPEntry(node.getNamespaceURI() + node.getLocalName(), node.getLocalName(), value));
}
}
- // TODO: Consider flattening the somewhat artificial directory structure
- List entries = new ArrayList();
+ List entries = new ArrayList();
+ // TODO: Should we still allow asking for a subdirectory by item id?
for (Map.Entry> entry : subdirs.entrySet()) {
- entries.add(new XMPEntry(entry.getKey(), new XMPDirectory(entry.getValue())));
+ entries.add(new RDFDescription(entry.getKey(), entry.getValue()));
}
- return new XMPDirectory(entries);
+ return new XMPDirectory(entries, toolkit);
}
- private Object getChildTextValue(Node node) {
- Object value;
- Node child = node.getFirstChild();
+ private void parseAttributesForKnownElements(Map> subdirs, Node desc) {
+ // NOTE: NamedNodeMap does not have any particular order...
+ NamedNodeMap attributes = desc.getAttributes();
- String strVal = null;
- if (child != null) {
- strVal = child.getNodeValue();
+ for (Node attr : asIterable(attributes)) {
+ if (!XMP.ELEMENTS.contains(attr.getNamespaceURI())) {
+ continue;
+ }
+
+ List dir = subdirs.get(attr.getNamespaceURI());
+
+ if (dir == null) {
+ dir = new ArrayList();
+ subdirs.put(attr.getNamespaceURI(), dir);
+ }
+
+ dir.add(new XMPEntry(attr.getNamespaceURI() + attr.getLocalName(), attr.getLocalName(), attr.getNodeValue()));
+ }
+ }
+
+ private Object getChildTextValue(final Node node) {
+ for (Node child : asIterable(node.getChildNodes())) {
+ if (XMP.NS_RDF.equals(child.getNamespaceURI()) && "Alt".equals(child.getLocalName())) {
+ // Support for -> return a Map (keyed on xml:lang?)
+ Map alternatives = new LinkedHashMap();
+ for (Node alternative : asIterable(child.getChildNodes())) {
+ if (XMP.NS_RDF.equals(alternative.getNamespaceURI()) && "li".equals(alternative.getLocalName())) {
+ //return getChildTextValue(alternative);
+ NamedNodeMap attributes = alternative.getAttributes();
+ Node key = attributes.getNamedItem("xml:lang");
+
+ alternatives.put(key.getTextContent(), getChildTextValue(alternative));
+ }
+ }
+
+ return alternatives;
+ }
+ else if (XMP.NS_RDF.equals(child.getNamespaceURI()) && ("Seq".equals(child.getLocalName()) || "Bag".equals(child.getLocalName()))) {
+ // Support for -> return array
+ // Support for -> return array/unordered collection (how can a serialized collection not have order?)
+ List