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 28abce4f..aeb47bb7 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
@@ -39,6 +39,7 @@ import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
@@ -73,6 +74,7 @@ public final class XMPReader extends MetadataReader {
// 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();
+ builder.setErrorHandler(new DefaultHandler());
Document document = builder.parse(new InputSource(IIOUtil.createStreamAdapter(input)));
// XMLSerializer serializer = new XMLSerializer(System.err, System.getProperty("file.encoding"));
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDDirectoryResource.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDDirectoryResource.java
new file mode 100644
index 00000000..1a2e0205
--- /dev/null
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDDirectoryResource.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2017, 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.plugins.psd;
+
+import com.twelvemonkeys.imageio.metadata.Directory;
+import com.twelvemonkeys.lang.StringUtil;
+
+import javax.imageio.stream.ImageInputStream;
+import java.io.IOException;
+
+/**
+ * PSDDirectoryResource
+ */
+abstract class PSDDirectoryResource extends PSDImageResource {
+ byte[] data;
+ private Directory directory;
+
+ PSDDirectoryResource(short resourceId, ImageInputStream input) throws IOException {
+ super(resourceId, input);
+ }
+
+ @Override
+ protected void readData(final ImageInputStream pInput) throws IOException {
+ data = new byte[(int) size]; // TODO: Fix potential overflow, or document why that can't happen (read spec)
+ pInput.readFully(data);
+ }
+
+ abstract Directory parseDirectory() throws IOException;
+
+ final void initDirectory() throws IOException {
+ if (directory == null) {
+ directory = parseDirectory();
+ }
+ }
+
+ Directory getDirectory() {
+ return directory;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = toStringBuilder();
+
+ int length = Math.min(256, data.length);
+ String data = StringUtil.decode(this.data, 0, length, "UTF-8").replace('\n', ' ').replaceAll("\\s+", " ");
+ builder.append(", data: \"").append(data);
+
+ if (length < this.data.length) {
+ builder.append("...");
+ }
+
+ builder.append("\"]");
+
+ return builder.toString();
+ }
+}
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDEXIF1Data.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDEXIF1Data.java
index 8fe3c3c8..d952f215 100644
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDEXIF1Data.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDEXIF1Data.java
@@ -30,6 +30,7 @@ package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
+import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
@@ -45,22 +46,26 @@ import java.io.IOException;
* @see Aware systems TIFF tag reference
* @see Adobe TIFF developer resources
*/
-final class PSDEXIF1Data extends PSDImageResource {
- protected Directory directory;
+final class PSDEXIF1Data extends PSDDirectoryResource {
PSDEXIF1Data(final short pId, final ImageInputStream pInput) throws IOException {
super(pId, pInput);
}
@Override
- protected void readData(final ImageInputStream pInput) throws IOException {
- // This is in essence an embedded TIFF file.
- // TODO: Instead, read the byte data, store for later parsing (or better yet, store offset, and read on request)
- directory = new TIFFReader().read(pInput);
+ Directory parseDirectory() throws IOException {
+ // The data is in essence an embedded TIFF file.
+ return new TIFFReader().read(new ByteArrayImageInputStream(data));
}
@Override
public String toString() {
+ Directory directory = getDirectory();
+
+ if (directory == null) {
+ return super.toString();
+ }
+
StringBuilder builder = toStringBuilder();
builder.append(", ").append(directory);
builder.append("]");
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDGlobalLayerMask.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDGlobalLayerMask.java
index 6d26f396..ba9563ac 100755
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDGlobalLayerMask.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDGlobalLayerMask.java
@@ -39,6 +39,9 @@ import java.io.IOException;
* @version $Id: PSDGlobalLayerMask.java,v 1.0 May 8, 2008 5:33:48 PM haraldk Exp$
*/
final class PSDGlobalLayerMask {
+
+ static final PSDGlobalLayerMask NULL_MASK = new PSDGlobalLayerMask();
+
final int colorSpace;
final short[] colors = new short[4];
final int opacity;
@@ -58,6 +61,12 @@ final class PSDGlobalLayerMask {
pInput.skipBytes(globalLayerMaskLength - 17);
}
+ private PSDGlobalLayerMask() {
+ colorSpace = 0;
+ opacity = 0;
+ kind = 0;
+ }
+
@Override
public String toString() {
StringBuilder builder = new StringBuilder(getClass().getSimpleName());
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDHeader.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDHeader.java
index 8892a6a8..12a4c07b 100755
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDHeader.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDHeader.java
@@ -40,9 +40,7 @@ import java.io.IOException;
* @version $Id: PSDHeader.java,v 1.0 Apr 29, 2008 5:18:22 PM haraldk Exp$
*/
final class PSDHeader {
- private static final int PSD_MAX_SIZE = 30000;
- private static final int PSB_MAX_SIZE = 300000;
- // The header is 26 bytes in length and is structured as follows:
+// The header is 26 bytes in length and is structured as follows:
//
// typedef struct _PSD_HEADER
// {
@@ -57,6 +55,9 @@ final class PSDHeader {
// WORD Mode; /* Color mode */
// } PSD_HEADER;
+ private static final int PSD_MAX_SIZE = 30000;
+ private static final int PSB_MAX_SIZE = 300000;
+
final short channels;
final int width;
final int height;
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDIPTCData.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDIPTCData.java
index 367ee46b..b3952ee8 100644
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDIPTCData.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDIPTCData.java
@@ -30,6 +30,7 @@ package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.iptc.IPTCReader;
+import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
@@ -41,21 +42,24 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$
* @version $Id: PSDIPTCData.java,v 1.0 Nov 7, 2009 9:52:14 PM haraldk Exp$
*/
-final class PSDIPTCData extends PSDImageResource {
- Directory directory;
-
+final class PSDIPTCData extends PSDDirectoryResource {
PSDIPTCData(final short pId, final ImageInputStream pInput) throws IOException {
super(pId, pInput);
}
@Override
- protected void readData(final ImageInputStream pInput) throws IOException {
- // Read IPTC directory
- directory = new IPTCReader().read(pInput);
+ Directory parseDirectory() throws IOException {
+ return new IPTCReader().read(new ByteArrayImageInputStream(data));
}
@Override
public String toString() {
+ Directory directory = getDirectory();
+
+ if (directory == null) {
+ return super.toString();
+ }
+
StringBuilder builder = toStringBuilder();
builder.append(", ").append(directory);
builder.append("]");
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java
index 0b50a536..b19f3e4c 100644
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java
@@ -892,8 +892,8 @@ public final class PSDImageReader extends ImageReaderBase {
long imageResourcesLength = imageInput.readUnsignedInt();
if (pParseData && metadata.imageResources == null && imageResourcesLength > 0) {
- metadata.imageResources = new ArrayList<>();
long expectedEnd = imageInput.getStreamPosition() + imageResourcesLength;
+ metadata.imageResources = new ArrayList<>();
while (imageInput.getStreamPosition() < expectedEnd) {
PSDImageResource resource = PSDImageResource.read(imageInput);
@@ -975,6 +975,9 @@ public final class PSDImageReader extends ImageReaderBase {
}
// TODO: Else skip?
}
+ else {
+ metadata.globalLayerMask = PSDGlobalLayerMask.NULL_MASK;
+ }
// TODO: Parse "Additional layer information"
@@ -982,9 +985,9 @@ public final class PSDImageReader extends ImageReaderBase {
// imageInput.seek(metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4));
// imageInput.flushBefore(metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4));
- if (DEBUG) {
+ if (pParseData && DEBUG) {
System.out.println("layerInfo: " + metadata.layerInfo);
- System.out.println("globalLayerMask: " + metadata.globalLayerMask);
+ System.out.println("globalLayerMask: " + (metadata.globalLayerMask != PSDGlobalLayerMask.NULL_MASK ? metadata.globalLayerMask : null));
}
//}
}
@@ -1171,8 +1174,25 @@ public final class PSDImageReader extends ImageReaderBase {
readLayerAndMaskInfo(true);
// NOTE: Need to make sure compression is set in metadata, even without reading the image data!
- imageInput.seek(metadata.imageDataStart);
- metadata.compression = imageInput.readShort();
+ // TODO: Move this to readLayerAndMaskInfo?
+ if (metadata.compression == -1) {
+ imageInput.seek(metadata.imageDataStart);
+ metadata.compression = imageInput.readShort();
+ }
+
+ // Initialize XMP data etc.
+ for (PSDImageResource resource : metadata.imageResources) {
+ if (resource instanceof PSDDirectoryResource) {
+ PSDDirectoryResource directoryResource = (PSDDirectoryResource) resource;
+
+ try {
+ directoryResource.initDirectory();
+ }
+ catch (IOException e) {
+ processWarningOccurred(String.format("Error parsing %s: %s", resource.getClass().getSimpleName(), e.getMessage()));
+ }
+ }
+ }
return metadata; // TODO: clone if we change to mutable metadata
}
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadata.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadata.java
index 2f2ba8be..8a9303eb 100755
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadata.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadata.java
@@ -53,7 +53,7 @@ import java.util.List;
*/
public final class PSDMetadata extends AbstractMetadata {
- public static final String NATIVE_METADATA_FORMAT_NAME = "com_twelvemonkeys_imageio_psd_image_1.0";
+ static final String NATIVE_METADATA_FORMAT_NAME = "com_twelvemonkeys_imageio_psd_image_1.0";
static final String NATIVE_METADATA_FORMAT_CLASS_NAME = "com.twelvemonkeys.imageio.plugins.psd.PSDMetadataFormat";
// TODO: Support TIFF metadata, based on EXIF/XMP + merge in PSD specifics
@@ -93,7 +93,7 @@ public final class PSDMetadata extends AbstractMetadata {
static final String[] PRINT_SCALE_STYLES = {"centered", "scaleToFit", "userDefined"};
- protected PSDMetadata() {
+ PSDMetadata() {
// TODO: Allow XMP, EXIF (TIFF) and IPTC as extra formats?
super(true, NATIVE_METADATA_FORMAT_NAME, NATIVE_METADATA_FORMAT_CLASS_NAME, null, null);
}
@@ -118,7 +118,7 @@ public final class PSDMetadata extends AbstractMetadata {
root.appendChild(createLayerInfoNode());
}
- if (globalLayerMask != null) {
+ if (globalLayerMask != null && globalLayerMask != PSDGlobalLayerMask.NULL_MASK) {
root.appendChild(createGlobalLayerMaskNode());
}
@@ -291,9 +291,11 @@ public final class PSDMetadata extends AbstractMetadata {
node = new IIOMetadataNode("DirectoryResource");
node.setAttribute("type", "IPTC");
- node.setUserObject(iptc.directory);
+ node.setUserObject(iptc.data);
- appendEntries(node, "IPTC", iptc.directory);
+ if (iptc.getDirectory() != null) {
+ appendEntries(node, "IPTC", iptc.getDirectory());
+ }
}
else if (imageResource instanceof PSDEXIF1Data) {
// TODO: Revise/rethink this...
@@ -302,9 +304,11 @@ public final class PSDMetadata extends AbstractMetadata {
node = new IIOMetadataNode("DirectoryResource");
node.setAttribute("type", "TIFF");
// TODO: Set byte[] data instead
- node.setUserObject(exif.directory);
+ node.setUserObject(exif.data);
- appendEntries(node, "EXIF", exif.directory);
+ if (exif.getDirectory() != null) {
+ appendEntries(node, "EXIF", exif.getDirectory());
+ }
}
else if (imageResource instanceof PSDXMPData) {
// TODO: Revise/rethink this... Would it be possible to parse XMP as IIOMetadataNodes? Or is that just stupid...
@@ -313,10 +317,12 @@ public final class PSDMetadata extends AbstractMetadata {
node = new IIOMetadataNode("DirectoryResource");
node.setAttribute("type", "XMP");
- appendEntries(node, "XMP", xmp.directory);
-
// Set the entire XMP document as user data
node.setUserObject(xmp.data);
+
+ if (xmp.getDirectory() != null) {
+ appendEntries(node, "XMP", xmp.getDirectory());
+ }
}
else {
// Generic resource..
@@ -662,7 +668,7 @@ public final class PSDMetadata extends AbstractMetadata {
PSDEXIF1Data data = exif.next();
// Get the EXIF DateTime (aka ModifyDate) tag if present
- Entry dateTime = data.directory.getEntryById(TIFF.TAG_DATE_TIME);
+ Entry dateTime = data.getDirectory().getEntryById(TIFF.TAG_DATE_TIME);
if (dateTime != null) {
IIOMetadataNode imageCreationTime = new IIOMetadataNode("ImageCreationTime"); // As TIFF, but could just as well be ImageModificationTime
// Format: "YYYY:MM:DD hh:mm:ss"
@@ -707,7 +713,7 @@ public final class PSDMetadata extends AbstractMetadata {
if (textResource instanceof PSDIPTCData) {
PSDIPTCData iptc = (PSDIPTCData) textResource;
- appendTextEntriesFlat(text, iptc.directory, new FilterIterator.Filter() {
+ appendTextEntriesFlat(text, iptc.getDirectory(), new FilterIterator.Filter() {
public boolean accept(final Entry pEntry) {
Integer tagId = (Integer) pEntry.getIdentifier();
@@ -727,7 +733,7 @@ public final class PSDMetadata extends AbstractMetadata {
else if (textResource instanceof PSDEXIF1Data) {
PSDEXIF1Data exif = (PSDEXIF1Data) textResource;
- appendTextEntriesFlat(text, exif.directory, new FilterIterator.Filter() {
+ appendTextEntriesFlat(text, exif.getDirectory(), new FilterIterator.Filter() {
public boolean accept(final Entry pEntry) {
Integer tagId = (Integer) pEntry.getIdentifier();
@@ -743,11 +749,12 @@ public final class PSDMetadata extends AbstractMetadata {
}
});
}
- else if (textResource instanceof PSDXMPData) {
+ //else if (textResource instanceof PSDXMPData) {
// TODO: Parse XMP (heavy) ONLY if we don't have required fields from IPTC/EXIF?
// TODO: Use XMP IPTC/EXIF/TIFF NativeDigest field to validate if the values are in sync..?
- PSDXMPData xmp = (PSDXMPData) textResource;
- }
+ // TODO: Use XMP IPTC/EXIF/TIFF NativeDigest field to validate if the values are in sync..?
+ //PSDXMPData xmp = (PSDXMPData) textResource;
+ //}
}
return text;
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDXMPData.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDXMPData.java
index dfd6c76c..78cc002c 100644
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDXMPData.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDXMPData.java
@@ -31,11 +31,9 @@ package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.xmp.XMPReader;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
-import com.twelvemonkeys.lang.StringUtil;
import javax.imageio.stream.ImageInputStream;
-import java.io.*;
-import java.nio.charset.Charset;
+import java.io.IOException;
/**
* XMP metadata.
@@ -47,19 +45,12 @@ import java.nio.charset.Charset;
* @see Adobe Extensible Metadata Platform (XMP)
* @see Adobe XMP Developer Center
*/
-final class PSDXMPData extends PSDImageResource {
- protected byte[] data;
- Directory directory;
-
+final class PSDXMPData extends PSDDirectoryResource {
PSDXMPData(final short pId, final ImageInputStream pInput) throws IOException {
super(pId, pInput);
}
- @Override
- protected void readData(final ImageInputStream pInput) throws IOException {
- data = new byte[(int) size]; // TODO: Fix potential overflow, or document why that can't happen (read spec)
- pInput.readFully(data);
-
+ Directory parseDirectory() throws IOException {
// Chop off potential trailing null-termination/padding that SAX parsers don't like...
int len = data.length;
for (; len > 0; len--) {
@@ -68,32 +59,6 @@ final class PSDXMPData extends PSDImageResource {
}
}
- directory = new XMPReader().read(new ByteArrayImageInputStream(data, 0, len));
- }
-
- @Override
- public String toString() {
- StringBuilder builder = toStringBuilder();
-
- int length = Math.min(256, data.length);
- String data = StringUtil.decode(this.data, 0, length, "UTF-8").replace('\n', ' ').replaceAll("\\s+", " ");
- builder.append(", data: \"").append(data);
-
- if (length < this.data.length) {
- builder.append("...");
- }
-
- builder.append("\"]");
-
- return builder.toString();
- }
-
- /**
- * Returns a character stream containing the XMP metadata (XML).
- *
- * @return the XMP metadata.
- */
- public Reader getData() {
- return new InputStreamReader(new ByteArrayInputStream(data), Charset.forName("UTF-8"));
+ return new XMPReader().read(new ByteArrayImageInputStream(data, 0, len));
}
}