diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSD.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSD.java index 30c77218..55a3ba45 100755 --- a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSD.java +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSD.java @@ -101,7 +101,7 @@ interface PSD { int COMPRESSION_ZIP = 2; /** ZIP compression with prediction */ - int COMPRESSION_ZIP_PREDICTON = 3; + int COMPRESSION_ZIP_PREDICTION = 3; // Color Modes /** Bitmap (monochrome) */ diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDAlphaChannelInfo.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDAlphaChannelInfo.java index 7d3ae079..ed0b4edd 100755 --- a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDAlphaChannelInfo.java +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDAlphaChannelInfo.java @@ -48,11 +48,11 @@ class PSDAlphaChannelInfo extends PSDImageResource { } @Override - protected void readData(ImageInputStream pInput) throws IOException { + protected void readData(final ImageInputStream pInput) throws IOException { mNames = new ArrayList(); long left = mSize; while (left > 0) { - String name = PSDUtil.readPascalStringByte(pInput); + String name = PSDUtil.readPascalString(pInput); mNames.add(name); left -= name.length() + 1; } diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDDisplayInfo.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDDisplayInfo.java index d4d41347..f74e5a7b 100755 --- a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDDisplayInfo.java +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDDisplayInfo.java @@ -40,7 +40,22 @@ import java.io.IOException; * @version $Id: PSDResolutionInfo.java,v 1.0 May 2, 2008 3:58:19 PM haraldk Exp$ */ class PSDDisplayInfo extends PSDImageResource { - // TODO: Size of this struct should be 14.. Does not compute... Something bogus here + // TODO: Size of this struct should be 14.. Does not compute... Something bogus here + + // ColorSpace definitions: + // PSD_CS_RGB = 0, /* RGB */ + // PSD_CS_HSB = 1, /* Hue, Saturation, Brightness */ + // PSD_CS_CMYK = 2, /* CMYK */ + // PSD_CS_PANTONE = 3, /* Pantone matching system (Lab)*/ + // PSD_CS_FOCOLTONE = 4, /* Focoltone colour system (CMYK)*/ + // PSD_CS_TRUMATCH = 5, /* Trumatch color (CMYK)*/ + // PSD_CS_TOYO = 6, /* Toyo 88 colorfinder 1050 (Lab)*/ + // PSD_CS_LAB = 7, /* L*a*b*/ + // PSD_CS_GRAYSCALE = 8, /* Grey scale */ + // PSD_CS_HKS = 10, /* HKS colors (CMYK)*/ + // PSD_CS_DIC = 11, /* DIC color guide (Lab)*/ + // PSD_CS_ANPA = 3000, /* Anpa color (Lab)*/ + //typedef _DisplayInfo //{ // WORD ColorSpace; @@ -50,10 +65,10 @@ class PSDDisplayInfo extends PSDImageResource { // BYTE Padding; /* Always zero */ //} DISPLAYINFO; - private int mColorSpace; - private short[] mColors; - private short mOpacity; - private byte mKind; + int mColorSpace; + short[] mColors; + short mOpacity; + byte mKind; PSDDisplayInfo(final short pId, final ImageInputStream pInput) throws IOException { super(pId, pInput); diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDEXIF1Data.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDEXIF1Data.java index a2f6f486..17f181aa 100644 --- a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDEXIF1Data.java +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDEXIF1Data.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; +import java.util.Iterator; import java.util.List; /** @@ -67,8 +68,8 @@ final class PSDEXIF1Data extends PSDImageResource { } // TIFF Image file directory (IFD) - private static class Directory { - List mEntries = new ArrayList(); + static class Directory implements Iterable { + private List mEntries = new ArrayList(); private Directory() {} @@ -90,6 +91,20 @@ final class PSDEXIF1Data extends PSDImageResource { return directory; } + public Entry get(int pTag) { + for (Entry entry : mEntries) { + if (entry.mTag == pTag) { + return entry; + } + } + + return null; + } + + public Iterator iterator() { + return mEntries.iterator(); + } + @Override public String toString() { return String.format("Directory%s", mEntries); @@ -97,7 +112,7 @@ final class PSDEXIF1Data extends PSDImageResource { } // TIFF IFD Entry - private static class Entry { + static class Entry { private static final int EXIF_IFD = 0x8769; private final static String[] TYPE_NAMES = { diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDHeader.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDHeader.java index 6f26d147..77e6659a 100644 --- a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDHeader.java +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDHeader.java @@ -28,6 +28,9 @@ package com.twelvemonkeys.imageio.plugins.psd; +import org.w3c.dom.Node; + +import javax.imageio.metadata.IIOMetadataNode; import javax.imageio.stream.ImageInputStream; import javax.imageio.IIOException; import java.io.IOException; @@ -61,7 +64,7 @@ class PSDHeader { final short mBits; final short mMode; - PSDHeader(ImageInputStream pInput) throws IOException { + PSDHeader(final ImageInputStream pInput) throws IOException { int signature = pInput.readInt(); if (signature != PSD.SIGNATURE_8BPS) { throw new IIOException("Not a PSD document, expected signature \"8BPS\": \"" + PSDUtil.intToStr(signature) + "\" (0x" + Integer.toHexString(signature) + ")"); diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java index d2f05792..5eb7112e 100644 --- a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java @@ -69,11 +69,12 @@ import java.util.List; // See http://www.adobeforums.com/webx?14@@.3bc381dc/0 public class PSDImageReader extends ImageReaderBase { private PSDHeader mHeader; - private PSDColorData mColorData; - private List mImageResources; - private PSDGlobalLayerMask mGlobalLayerMask; - private List mLayerInfo; +// private PSDColorData mColorData; +// private List mImageResources; +// private PSDGlobalLayerMask mGlobalLayerMask; +// private List mLayerInfo; private ICC_ColorSpace mColorSpace; + protected PSDMetadata mMetadata; protected PSDImageReader(final ImageReaderSpi pOriginatingProvider) { super(pOriginatingProvider); @@ -81,8 +82,9 @@ public class PSDImageReader extends ImageReaderBase { protected void resetMembers() { mHeader = null; - mColorData = null; - mImageResources = null; +// mColorData = null; +// mImageResources = null; + mMetadata = null; mColorSpace = null; } @@ -122,7 +124,7 @@ public class PSDImageReader extends ImageReaderBase { case PSD.COLOR_MODE_INDEXED: // TODO: 16 bit indexed?! Does it exist? if (mHeader.mChannels == 1 && mHeader.mBits == 8) { - return IndexedImageTypeSpecifier.createFromIndexColorModel(mColorData.getIndexColorModel()); + return IndexedImageTypeSpecifier.createFromIndexColorModel(mMetadata.mColorData.getIndexColorModel()); } throw new IIOException( @@ -264,7 +266,7 @@ public class PSDImageReader extends ImageReaderBase { if (mColorSpace == null) { ICC_Profile profile = null; - for (PSDImageResource resource : mImageResources) { + for (PSDImageResource resource : mMetadata.mImageResources) { if (resource instanceof ICCProfile) { profile = ((ICCProfile) resource).getProfile(); break; @@ -333,6 +335,8 @@ public class PSDImageReader extends ImageReaderBase { int[] byteCounts = null; int compression = mImageInput.readShort(); + // TODO: Need to make sure compression is set in metadata, even without reading the image data! + mMetadata.mCompression = compression; switch (compression) { case PSD.COMPRESSION_NONE: @@ -346,7 +350,7 @@ public class PSDImageReader extends ImageReaderBase { break; case PSD.COMPRESSION_ZIP: // TODO: Could probably use the ZIPDecoder (DeflateDecoder) here.. - case PSD.COMPRESSION_ZIP_PREDICTON: + case PSD.COMPRESSION_ZIP_PREDICTION: // TODO: Need to find out if the normal java.util.zip can handle this... // Could be same as PNG prediction? Read up... throw new IIOException("ZIP compression not supported yet"); @@ -693,6 +697,9 @@ public class PSDImageReader extends ImageReaderBase { if (mHeader == null) { mHeader = new PSDHeader(mImageInput); + mMetadata = new PSDMetadata(); + mMetadata.mHeader = mHeader; + /* Contains the required data to define the color mode. @@ -705,7 +712,7 @@ public class PSDImageReader extends ImageReaderBase { around as a black box for use when saving the file. */ if (mHeader.mMode == PSD.COLOR_MODE_INDEXED) { - mColorData = new PSDColorData(mImageInput); + mMetadata.mColorData = new PSDColorData(mImageInput); } else { // TODO: We need to store the duotone spec if we decide to create a writer... @@ -729,14 +736,14 @@ public class PSDImageReader extends ImageReaderBase { long length = mImageInput.readUnsignedInt(); if (pParseData && length > 0) { - if (mImageResources == null) { - mImageResources = new ArrayList(); + if (mMetadata.mImageResources == null) { + mMetadata.mImageResources = new ArrayList(); long expectedEnd = mImageInput.getStreamPosition() + length; while (mImageInput.getStreamPosition() < expectedEnd) { // TODO: Have PSDImageResources defer actual parsing? (Just store stream offsets) PSDImageResource resource = PSDImageResource.read(mImageInput); - mImageResources.add(resource); + mMetadata.mImageResources.add(resource); } if (mImageInput.getStreamPosition() != expectedEnd) { @@ -770,7 +777,7 @@ public class PSDImageReader extends ImageReaderBase { for (int i = 0; i < layerInfos.length; i++) { layerInfos[i] = new PSDLayerInfo(mImageInput); } - mLayerInfo = Arrays.asList(layerInfos); + mMetadata.mLayerInfo = Arrays.asList(layerInfos); // TODO: Clean-up mImageInput.mark(); @@ -783,9 +790,9 @@ public class PSDImageReader extends ImageReaderBase { BufferedImage layer = readLayerData(layerInfo, raw, imageType); // TODO: Don't show! Store in meta data somehow... - if (layer != null) { - showIt(layer, layerInfo.mLayerName + " " + layerInfo.mBlendMode.toString()); - } +// if (layer != null) { +// showIt(layer, layerInfo.mLayerName + " " + layerInfo.mBlendMode.toString()); +// } } long read = mImageInput.getStreamPosition() - pos; @@ -799,7 +806,7 @@ public class PSDImageReader extends ImageReaderBase { long layerMaskInfoLength = mImageInput.readUnsignedInt(); // System.out.println("GlobalLayerMaskInfo length: " + layerMaskInfoLength); if (layerMaskInfoLength > 0) { - mGlobalLayerMask = new PSDGlobalLayerMask(mImageInput); + mMetadata.mGlobalLayerMask = new PSDGlobalLayerMask(mImageInput); // System.out.println("mGlobalLayerMask: " + mGlobalLayerMask); } @@ -877,7 +884,7 @@ public class PSDImageReader extends ImageReaderBase { break; case PSD.COMPRESSION_ZIP: - case PSD.COMPRESSION_ZIP_PREDICTON: + case PSD.COMPRESSION_ZIP_PREDICTION: default: // Explicitly skipped above throw new AssertionError(String.format("Unsupported layer data. Compression: %d", compression)); @@ -985,16 +992,19 @@ public class PSDImageReader extends ImageReaderBase { readImageResources(true); readLayerAndMaskInfo(true); - PSDMetadata metadata = new PSDMetadata(); - metadata.mHeader = mHeader; - metadata.mColorData = mColorData; - metadata.mImageResources = mImageResources; - return metadata; + // TODO: Need to make sure compression is set in metadata, even without reading the image data! + mMetadata.mCompression = mImageInput.readShort(); + +// mMetadata.mHeader = mHeader; +// mMetadata.mColorData = mColorData; +// mMetadata.mImageResources = mImageResources; + + return mMetadata; // TODO: clone if we change to mutable metadata } @Override public IIOMetadata getImageMetadata(final int imageIndex, final String formatName, final Set nodeNames) throws IOException { - // TODO: This might make sense, as there's loads of meta data in the file + // TODO: It might make sense to overload this, as there's loads of meta data in the file return super.getImageMetadata(imageIndex, formatName, nodeNames); } @@ -1011,14 +1021,14 @@ public class PSDImageReader extends ImageReaderBase { List thumbnails = null; - if (mImageResources == null) { + if (mMetadata.mImageResources == null) { // TODO: Need flag here, to specify what resources to read... readImageResources(true); // TODO: Skip this, requires storing some stream offsets readLayerAndMaskInfo(false); } - for (PSDImageResource resource : mImageResources) { + for (PSDImageResource resource : mMetadata.mImageResources) { if (resource instanceof PSDThumbnail) { if (thumbnails == null) { thumbnails = new ArrayList(); @@ -1120,15 +1130,25 @@ public class PSDImageReader extends ImageReaderBase { // System.out.println("imageReader.mHeader: " + imageReader.mHeader); imageReader.readImageResources(true); - System.out.println("imageReader.mImageResources: " + imageReader.mImageResources); + System.out.println("imageReader.mImageResources: " + imageReader.mMetadata.mImageResources); + System.out.println(); imageReader.readLayerAndMaskInfo(true); - System.out.println("imageReader.mLayerInfo: " + imageReader.mLayerInfo); + System.out.println("imageReader.mLayerInfo: " + imageReader.mMetadata.mLayerInfo); // System.out.println("imageReader.mGlobalLayerMask: " + imageReader.mGlobalLayerMask); + System.out.println(); IIOMetadata metadata = imageReader.getImageMetadata(0); - Node node = metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName); - XMLSerializer serializer = new XMLSerializer(System.out, System.getProperty("file.encoding")); + Node node; + XMLSerializer serializer; + + node = metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName); + serializer = new XMLSerializer(System.out, System.getProperty("file.encoding")); + serializer.serialize(node, true); + System.out.println(); + + node = metadata.getAsTree(PSDMetadata.NATIVE_METADATA_FORMAT_NAME); + serializer = new XMLSerializer(System.out, System.getProperty("file.encoding")); serializer.serialize(node, true); if (imageReader.hasThumbnails(0)) { diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageResource.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageResource.java index 5aa95625..8dd3770a 100644 --- a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageResource.java +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageResource.java @@ -28,6 +28,8 @@ package com.twelvemonkeys.imageio.plugins.psd; +import com.twelvemonkeys.lang.StringUtil; + import javax.imageio.stream.ImageInputStream; import javax.imageio.IIOException; import java.io.IOException; @@ -50,6 +52,12 @@ class PSDImageResource { mName = PSDUtil.readPascalString(pInput); + // Skip pad + int nameSize = mName.length() + 1; + if (nameSize % 2 != 0) { + pInput.readByte(); + } + mSize = pInput.readUnsignedInt(); readData(pInput); @@ -84,7 +92,10 @@ class PSDImageResource { protected StringBuilder toStringBuilder() { StringBuilder builder = new StringBuilder(getClass().getSimpleName()); - builder.append(resourceTypeForId(mId)); + String fakeType = resourceTypeForId(mId); + if (fakeType != null) { + builder.append("(").append(fakeType).append(")"); + } builder.append("[ID: 0x"); builder.append(Integer.toHexString(mId)); @@ -106,23 +117,25 @@ class PSDImageResource { case PSD.RES_THUMBNAIL_PS4: case PSD.RES_THUMBNAIL: case PSD.RES_ICC_PROFILE: + case PSD.RES_VERSION_INFO: case PSD.RES_EXIF_DATA_1: // case PSD.RES_EXIF_DATA_3: case PSD.RES_XMP_DATA: case PSD.RES_PRINT_FLAGS_INFORMATION: - return ""; + return null; default: try { for (Field field : PSD.class.getDeclaredFields()) { if (field.getName().startsWith("RES_") && field.getInt(null) == pId) { - return "(" + field.getName().substring(4) + ")"; + String name = field.getName().substring(4); + return StringUtil.lispToCamel(name.replace("_", "-").toLowerCase(), true); } } } catch (IllegalAccessException ignore) { } - return "(unknown resource)"; + return "unknown resource"; } } @@ -149,6 +162,8 @@ class PSDImageResource { return new PSDThumbnail(id, pInput); case PSD.RES_ICC_PROFILE: return new ICCProfile(id, pInput); + case PSD.RES_VERSION_INFO: + return new PSDVersionInfo(id, pInput); case PSD.RES_EXIF_DATA_1: return new PSDEXIF1Data(id, pInput); case PSD.RES_XMP_DATA: diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerInfo.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerInfo.java index da10552c..56a62f26 100755 --- a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerInfo.java +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerInfo.java @@ -98,14 +98,12 @@ class PSDLayerInfo { mLayerName = PSDUtil.readPascalString(pInput); int layerNameSize = mLayerName.length() + 1; - // readPascalString has already read pad byte for word alignment - if (layerNameSize % 2 != 0) { - layerNameSize++; - } - // Skip two more pad bytes if needed + + // Skip pad bytes for long word alignment if (layerNameSize % 4 != 0) { - pInput.skipBytes(2); - layerNameSize += 2; + int skip = layerNameSize % 4; + pInput.skipBytes(skip); + layerNameSize += skip; } // TODO: There's some data skipped here... diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadata.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadata.java index a9755fad..69188525 100644 --- a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadata.java +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadata.java @@ -1,15 +1,20 @@ package com.twelvemonkeys.imageio.plugins.psd; import com.twelvemonkeys.lang.StringUtil; +import com.twelvemonkeys.util.FilterIterator; +import org.w3c.dom.Document; import org.w3c.dom.Node; +import org.xml.sax.InputSource; import javax.imageio.metadata.IIOInvalidTreeException; import javax.imageio.metadata.IIOMetadata; import javax.imageio.metadata.IIOMetadataFormatImpl; import javax.imageio.metadata.IIOMetadataNode; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; import java.awt.image.IndexColorModel; -import java.util.ArrayList; import java.util.Arrays; +import java.util.Iterator; import java.util.List; /** @@ -21,16 +26,28 @@ import java.util.List; */ public final class PSDMetadata extends IIOMetadata implements Cloneable { - static final String NATIVE_METADATA_FORMAT_NAME = "com_twelvemonkeys_imageio_psd_1.0"; + // TODO: Decide on image/stream metadata... + 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: Move fields from PSDImageReader (header, color map, resources, etc) here PSDHeader mHeader; PSDColorData mColorData; + int mCompression = -1; List mImageResources; PSDGlobalLayerMask mGlobalLayerMask; List mLayerInfo; + static final String[] COLOR_MODES = { + "MONOCHROME", "GRAYSCALE", "INDEXED", "RGB", "CMYK", null, null, "MULTICHANNEL", "DUOTONE", "LAB" + }; + + static final String[] DISPLAY_INFO_CS = { + "RGB", "HSB", "CMYK", "PANTONE", "FOCOLTONE", "TRUMATCH", "TOYO", "LAB", "GRAYSCALE", null, "HKS", "DIC", + null, // ... (until index 2999), + "ANPA" + }; + static final String[] DISPLAY_INFO_KINDS = {"selected", "protected"}; + protected PSDMetadata() { // TODO: Allow XMP, EXIF and IPTC as extra formats? super(true, NATIVE_METADATA_FORMAT_NAME, NATIVE_METADATA_FORMAT_CLASS_NAME, null, null); @@ -119,8 +136,112 @@ public final class PSDMetadata extends IIOMetadata implements Cloneable { } } + /// Native format support + private Node getNativeTree() { - throw new UnsupportedOperationException("getNativeTree"); + IIOMetadataNode root = new IIOMetadataNode(NATIVE_METADATA_FORMAT_NAME); + + root.appendChild(createHeaderNode()); + + if (mHeader.mMode == PSD.COLOR_MODE_INDEXED) { + root.appendChild(createPaletteNode()); + } + + if (mImageResources != null && !mImageResources.isEmpty()) { + root.appendChild(createImageResourcesNode()); + } + + return root; + } + + private Node createHeaderNode() { + IIOMetadataNode header = new IIOMetadataNode("PSDHeader"); + + header.setAttribute("version", "1"); + header.setAttribute("channels", Integer.toString(mHeader.mChannels)); + header.setAttribute("height", Integer.toString(mHeader.mHeight)); + header.setAttribute("width", Integer.toString(mHeader.mWidth)); + header.setAttribute("bits", Integer.toString(mHeader.mBits)); + header.setAttribute("mode", COLOR_MODES[mHeader.mMode]); + + return header; + } + + private Node createImageResourcesNode() { + IIOMetadataNode resource = new IIOMetadataNode("ImageResources"); + IIOMetadataNode node; + + for (PSDImageResource imageResource : mImageResources) { + // TODO: Always add name (if set) and id (as resourceId) to all nodes? + // Resource Id is useful for people with access to the PSD spec.. + + if (imageResource instanceof PSDAlphaChannelInfo) { + PSDAlphaChannelInfo alphaChannelInfo = (PSDAlphaChannelInfo) imageResource; + + node = new IIOMetadataNode("AlphaChannelInfo"); + + for (String name : alphaChannelInfo.mNames) { + IIOMetadataNode nameNode = new IIOMetadataNode("Name"); + nameNode.setAttribute("value", name); + node.appendChild(nameNode); + } + + resource.appendChild(node); + } + else if (imageResource instanceof PSDDisplayInfo) { + PSDDisplayInfo displayInfo = (PSDDisplayInfo) imageResource; + + node = new IIOMetadataNode("DisplayInfo"); + node.setAttribute("colorSpace", DISPLAY_INFO_CS[displayInfo.mColorSpace]); + + StringBuilder builder = new StringBuilder(); + for (short color : displayInfo.mColors) { + if (builder.length() > 0) { + builder.append(" "); + } + builder.append(Integer.toString(color)); + } + + node.setAttribute("colors", builder.toString()); + node.setAttribute("opacity", Integer.toString(displayInfo.mOpacity)); + node.setAttribute("kind", DISPLAY_INFO_KINDS[displayInfo.mKind]); + + resource.appendChild(node); + } + else if (imageResource instanceof PSDXMPData) { + // TODO: Revise/rethink this... + PSDXMPData xmp = (PSDXMPData) imageResource; + + node = new IIOMetadataNode("XMPData"); + + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.parse(new InputSource(xmp.getData())); + + // Set the entire XMP document as user data + node.setUserObject(document); + } + catch (Exception e) { + e.printStackTrace(); + } + + resource.appendChild(node); + } + else { + // Generic resource.. + node = new IIOMetadataNode(PSDImageResource.resourceTypeForId(imageResource.mId)); + + resource.appendChild(node); + } + + + // TODO: More resources + + node.setAttribute("resourceId", Integer.toHexString(imageResource.mId)); + } + + return resource; } /// Standard format support @@ -135,7 +256,7 @@ public final class PSDMetadata extends IIOMetadata implements Cloneable { switch (mHeader.mMode) { case PSD.COLOR_MODE_MONOCHROME: case PSD.COLOR_MODE_GRAYSCALE: - case PSD.COLOR_MODE_DUOTONE: // Rationale is spec says treat as gray... + case PSD.COLOR_MODE_DUOTONE: // Rationale: Spec says treat as gray... cs = "GRAY"; break; case PSD.COLOR_MODE_RGB: @@ -146,8 +267,7 @@ public final class PSDMetadata extends IIOMetadata implements Cloneable { cs = "CMYK"; break; case PSD.COLOR_MODE_MULTICHANNEL: - // TODO: FixMe - cs = "???"; + cs = getMultiChannelCS(mHeader.mChannels); break; case PSD.COLOR_MODE_LAB: cs = "Lab"; @@ -158,42 +278,22 @@ public final class PSDMetadata extends IIOMetadata implements Cloneable { node.setAttribute("name", cs); chroma_node.appendChild(node); - // TODO: Channels might be 5 for RGB + A + Mask... + // TODO: Channels might be 5 for RGB + A + Mask... Probably not correct node = new IIOMetadataNode("NumChannels"); node.setAttribute("value", Integer.toString(mHeader.mChannels)); chroma_node.appendChild(node); -// if (gAMA_present) { -// node = new IIOMetadataNode("Gamma"); -// node.setAttribute("value", Float.toString(gAMA_gamma*1.0e-5F)); -// chroma_node.appendChild(node); -// } - // TODO: Check if this is correct with bitmap (monchrome) node = new IIOMetadataNode("BlackIsZero"); node.setAttribute("value", "true"); chroma_node.appendChild(node); if (mHeader.mMode == PSD.COLOR_MODE_INDEXED) { - node = new IIOMetadataNode("Palette"); - - IndexColorModel cm = mColorData.getIndexColorModel(); - for (int i = 0; i < cm.getMapSize(); i++) { - IIOMetadataNode entry = - new IIOMetadataNode("PaletteEntry"); - entry.setAttribute("index", Integer.toString(i)); - entry.setAttribute("red", - Integer.toString(cm.getRed(i))); - entry.setAttribute("green", - Integer.toString(cm.getGreen(i))); - entry.setAttribute("blue", - Integer.toString(cm.getBlue(i))); - - node.appendChild(entry); - } + node = createPaletteNode(); chroma_node.appendChild(node); } + // TODO: Hardcode background color to white? // if (bKGD_present) { // if (bKGD_colorType == PNGImageReader.PNG_COLOR_PALETTE) { // node = new IIOMetadataNode("BackgroundIndex"); @@ -219,22 +319,59 @@ public final class PSDMetadata extends IIOMetadata implements Cloneable { return chroma_node; } + private IIOMetadataNode createPaletteNode() { + IIOMetadataNode node = new IIOMetadataNode("Palette"); + IndexColorModel cm = mColorData.getIndexColorModel(); + + for (int i = 0; i < cm.getMapSize(); i++) { + IIOMetadataNode entry = new IIOMetadataNode("PaletteEntry"); + entry.setAttribute("index", Integer.toString(i)); + entry.setAttribute("red", Integer.toString(cm.getRed(i))); + entry.setAttribute("green", Integer.toString(cm.getGreen(i))); + entry.setAttribute("blue", Integer.toString(cm.getBlue(i))); + + node.appendChild(entry); + } + + return node; + } + + private String getMultiChannelCS(short pChannels) { + if (pChannels < 16) { + return Integer.toHexString(pChannels) + "CLR"; + } + + throw new UnsupportedOperationException("Standard meta data format does not support more than 15 channels"); + } + @Override protected IIOMetadataNode getStandardCompressionNode() { IIOMetadataNode compression_node = new IIOMetadataNode("Compression"); IIOMetadataNode node; // scratch node node = new IIOMetadataNode("CompressionTypeName"); - // TODO: Only if set... - node.setAttribute("value", "PackBits"); + String compression; + switch (mCompression) { + case PSD.COMPRESSION_NONE: + compression = "none"; + break; + case PSD.COMPRESSION_RLE: + compression = "packbits"; + break; + case PSD.COMPRESSION_ZIP: + case PSD.COMPRESSION_ZIP_PREDICTION: + compression = "zip"; + break; + default: + throw new AssertionError("Unreachable"); + } + node.setAttribute("value", compression); compression_node.appendChild(node); node = new IIOMetadataNode("Lossless"); node.setAttribute("value", "true"); compression_node.appendChild(node); -// compression_node.appendChild(node); - return compression_node; } @@ -280,9 +417,9 @@ public final class PSDMetadata extends IIOMetadata implements Cloneable { node.setAttribute("value", "Normal"); dimension_node.appendChild(node); - List resolutionInfos = getResources(PSDResolutionInfo.class); - if (!resolutionInfos.isEmpty()) { - PSDResolutionInfo resolutionInfo = resolutionInfos.get(0); + Iterator resolutionInfos = getResources(PSDResolutionInfo.class); + if (!resolutionInfos.hasNext()) { + PSDResolutionInfo resolutionInfo = resolutionInfos.next(); node = new IIOMetadataNode("HorizontalPixelSize"); node.setAttribute("value", Float.toString(asMM(resolutionInfo.mHResUnit, resolutionInfo.mHRes))); @@ -330,31 +467,49 @@ public final class PSDMetadata extends IIOMetadata implements Cloneable { @Override protected IIOMetadataNode getStandardDocumentNode() { - // TODO: PSDVersionInfo + IIOMetadataNode document_node = new IIOMetadataNode("Document"); + IIOMetadataNode node; // scratch node -// if (!tIME_present) { -// return null; -// } -// -// IIOMetadataNode document_node = new IIOMetadataNode("Document"); -// IIOMetadataNode node = null; // scratch node -// -// node = new IIOMetadataNode("ImageModificationTime"); -// node.setAttribute("year", Integer.toString(tIME_year)); -// node.setAttribute("month", Integer.toString(tIME_month)); -// node.setAttribute("day", Integer.toString(tIME_day)); -// node.setAttribute("hour", Integer.toString(tIME_hour)); -// node.setAttribute("minute", Integer.toString(tIME_minute)); -// node.setAttribute("second", Integer.toString(tIME_second)); -// document_node.appendChild(node); -// -// return document_node; - return null; + node = new IIOMetadataNode("FormatVersion"); + node.setAttribute("value", "1"); // PSD format version is always 1 + document_node.appendChild(node); + + // Get EXIF data if present + Iterator exif = getResources(PSDEXIF1Data.class); + if (exif.hasNext()) { + PSDEXIF1Data data = exif.next(); + + // Get the EXIF DateTime (aka ModifyDate) tag if present + PSDEXIF1Data.Entry dateTime = data.mDirectory.get(0x0132); // TODO: Constant + if (dateTime != null) { + node = new IIOMetadataNode("ImageModificationTime"); + // Format: "YYYY:MM:DD hh:mm:ss" (with quotes! :-P) + String value = dateTime.getValueAsString(); + + node.setAttribute("year", value.substring(1, 5)); + node.setAttribute("month", value.substring(6, 8)); + node.setAttribute("day", value.substring(9, 11)); + node.setAttribute("hour", value.substring(12, 14)); + node.setAttribute("minute", value.substring(15, 17)); + node.setAttribute("second", value.substring(18, 20)); + + document_node.appendChild(node); + } + } + + return document_node; } @Override protected IIOMetadataNode getStandardTextNode() { // TODO: CaptionDigest?, EXIF, XMP + + Iterator textResources = getResources(PSDEXIF1Data.class, PSDXMPData.class); + + while (textResources.hasNext()) { + PSDImageResource textResource = textResources.next(); + + } // int numEntries = tEXt_keyword.size() + // iTXt_keyword.size() + zTXt_keyword.size(); @@ -411,8 +566,7 @@ public final class PSDMetadata extends IIOMetadata implements Cloneable { @Override protected IIOMetadataNode getStandardTransparencyNode() { - IIOMetadataNode transparency_node = - new IIOMetadataNode("Transparency"); + IIOMetadataNode transparency_node = new IIOMetadataNode("Transparency"); IIOMetadataNode node; // scratch node node = new IIOMetadataNode("Alpha"); @@ -427,20 +581,31 @@ public final class PSDMetadata extends IIOMetadata implements Cloneable { mHeader.mMode == PSD.COLOR_MODE_CMYK & mHeader.mChannels >= 5; } - // TODO: Replace with filter iterator? - List getResources(final Class pResourceType) { - List filtered = null; + Iterator getResources(final Class pResourceType) { + // NOTE: The cast here is wrong, strictly speaking, but it does not matter... + @SuppressWarnings({"unchecked"}) + Iterator iterator = (Iterator) mImageResources.iterator(); - for (PSDImageResource resource : mImageResources) { - if (pResourceType.isInstance(resource)) { - if (filtered == null) { - filtered = new ArrayList(); + return new FilterIterator(iterator, new FilterIterator.Filter() { + public boolean accept(final T pElement) { + return pResourceType.isInstance(pElement); + } + }); + } + + Iterator getResources(final Class... pResourceTypes) { + Iterator iterator = mImageResources.iterator(); + + return new FilterIterator(iterator, new FilterIterator.Filter() { + public boolean accept(final PSDImageResource pElement) { + for (Class type : pResourceTypes) { + if (type.isInstance(pElement)) { + return true; + } } - filtered.add(pResourceType.cast(resource)); + return false; } - } - - return filtered; + }); } } diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadataFormat.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadataFormat.java index 072748ce..6d2a765d 100644 --- a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadataFormat.java +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadataFormat.java @@ -1,5 +1,7 @@ package com.twelvemonkeys.imageio.plugins.psd; +import org.w3c.dom.Document; + import javax.imageio.ImageTypeSpecifier; import javax.imageio.metadata.IIOMetadataFormatImpl; import java.util.Arrays; @@ -33,7 +35,7 @@ public final class PSDMetadataFormat extends IIOMetadataFormatImpl { addElement("PSDHeader", PSDMetadata.NATIVE_METADATA_FORMAT_NAME, CHILD_POLICY_EMPTY); // TODO: Do the first two make sense? - addAttribute("PSDHeader", "signature", DATATYPE_STRING, false, "8BPS", Arrays.asList("8BPS")); +// addAttribute("PSDHeader", "signature", DATATYPE_STRING, false, "8BPS", Arrays.asList("8BPS")); addAttribute("PSDHeader", "version", DATATYPE_INTEGER, false, "1", Arrays.asList("1")); addAttribute("PSDHeader", "channels", DATATYPE_INTEGER, true, null, "1", "24", true, true); @@ -42,16 +44,8 @@ public final class PSDMetadataFormat extends IIOMetadataFormatImpl { // columns? addAttribute("PSDHeader", "width", DATATYPE_INTEGER, true, null, "1", "30000", true, true); addAttribute("PSDHeader", "bits", DATATYPE_INTEGER, true, null, Arrays.asList("1", "8", "16")); - addAttribute("PSDHeader", "mode", DATATYPE_INTEGER, true, null, Arrays.asList( - String.valueOf(PSD.COLOR_MODE_MONOCHROME), - String.valueOf(PSD.COLOR_MODE_GRAYSCALE), - String.valueOf(PSD.COLOR_MODE_INDEXED), - String.valueOf(PSD.COLOR_MODE_RGB), - String.valueOf(PSD.COLOR_MODE_CMYK), - String.valueOf(PSD.COLOR_MODE_MULTICHANNEL), - String.valueOf(PSD.COLOR_MODE_DUOTONE), - String.valueOf(PSD.COLOR_MODE_LAB) - )); + // TODO: Consider using more readable names?! + addAttribute("PSDHeader", "mode", DATATYPE_INTEGER, true, null, Arrays.asList(PSDMetadata.COLOR_MODES)); /* Contains the required data to define the color mode. @@ -69,7 +63,7 @@ public final class PSDMetadataFormat extends IIOMetadataFormatImpl { // NOTE: Palette, PaletteEntry naming taken from the standard format, native PSD naming is ColorModeData // NOTE: PSD stores these as 256 Red, 256 Green, 256 Blue.. Should we do the same in the meta data? addElement("Palette", PSDMetadata.NATIVE_METADATA_FORMAT_NAME, 256, 256); // 768 = 256 * 3 - addElement("PaletteEntry", "PSDColorData", CHILD_POLICY_EMPTY); + addElement("PaletteEntry", "Palette", CHILD_POLICY_EMPTY); addAttribute("PaletteEntry", "index", DATATYPE_INTEGER, true, null, "0", "255", true, true); addAttribute("PaletteEntry", "red", DATATYPE_INTEGER, true, null, "0", "255", true, true); addAttribute("PaletteEntry", "green", DATATYPE_INTEGER, true, null, "0", "255", true, true); @@ -89,15 +83,19 @@ public final class PSDMetadataFormat extends IIOMetadataFormatImpl { // TODO: Allow arbitrary values to be added as a generic resource... // root -> ImageResources -> AlphaChannelInfo - addElement("AlphaChannelInfo", "ImageResources", CHILD_POLICY_EMPTY); - addAttribute("AlphaChannelInfo", "names", DATATYPE_STRING, true, 0, Integer.MAX_VALUE); + addElement("AlphaChannelInfo", "ImageResources", 0, Integer.MAX_VALUE); // The format probably does not support that many layers.. + addElement("Name", "AlphaChannelInfo", CHILD_POLICY_EMPTY); + addAttribute("Name", "value", DATATYPE_STRING, true, 0, Integer.MAX_VALUE); // root -> ImageResources -> DisplayInfo addElement("DisplayInfo", "ImageResources", CHILD_POLICY_EMPTY); + // TODO: Consider using human readable strings + // TODO: Limit values (0-8, 10, 11, 3000) addAttribute("DisplayInfo", "colorSpace", DATATYPE_INTEGER, true, null); addAttribute("DisplayInfo", "colors", DATATYPE_INTEGER, true, 4, 4); addAttribute("DisplayInfo", "opacity", DATATYPE_INTEGER, true, null, "0", "100", true, true); - addAttribute("DisplayInfo", "kind", DATATYPE_INTEGER, true, null, Arrays.asList("0", "1")); + // TODO: Consider using human readable strings + addAttribute("DisplayInfo", "kind", DATATYPE_INTEGER, true, null, Arrays.asList(PSDMetadata.DISPLAY_INFO_KINDS)); // root -> ImageResources -> EXIF1Data addElement("EXIF1Data", "ImageResources", CHILD_POLICY_ALL); @@ -138,6 +136,7 @@ public final class PSDMetadataFormat extends IIOMetadataFormatImpl { // root -> ImageResources -> XMPData addElement("XMPData", "ImageResources", CHILD_POLICY_CHOICE); // TODO: Incorporate XMP metadata here somehow (or treat as opaque bytes?) + addObjectValue("XMPData", Document.class, true, null); // TODO: Layers //addElement("ChannelSourceDestinationRange", "LayerSomething", CHILD_POLICY_CHOICE); diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintFlags.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintFlags.java index c64380b9..bb802354 100644 --- a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintFlags.java +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintFlags.java @@ -26,14 +26,14 @@ final class PSDPrintFlags extends PSDImageResource { @Override protected void readData(final ImageInputStream pInput) throws IOException { - mLabels = pInput.readUnsignedByte() != 0; - mCropMasks = pInput.readUnsignedByte() != 0; - mColorBars = pInput.readUnsignedByte() != 0; - mRegistrationMarks = pInput.readUnsignedByte() != 0; - mNegative = pInput.readUnsignedByte() != 0; - mFlip = pInput.readUnsignedByte() != 0; - mInterpolate = pInput.readUnsignedByte() != 0; - mCaption = pInput.readUnsignedByte() != 0; + mLabels = pInput.readBoolean(); + mCropMasks = pInput.readBoolean(); + mColorBars = pInput.readBoolean(); + mRegistrationMarks = pInput.readBoolean(); + mNegative = pInput.readBoolean(); + mFlip = pInput.readBoolean(); + mInterpolate = pInput.readBoolean(); + mCaption = pInput.readBoolean(); pInput.skipBytes(mSize - 8); } diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintFlagsInformation.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintFlagsInformation.java index d7c879d0..c3aa31d3 100644 --- a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintFlagsInformation.java +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintFlagsInformation.java @@ -24,7 +24,7 @@ final class PSDPrintFlagsInformation extends PSDImageResource { @Override protected void readData(final ImageInputStream pInput) throws IOException { mVersion = pInput.readUnsignedShort(); - mCropMasks = pInput.readUnsignedByte() != 0; + mCropMasks = pInput.readBoolean(); mField = pInput.readUnsignedByte(); mBleedWidth = pInput.readUnsignedInt(); mBleedScale = pInput.readUnsignedShort(); diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDThumbnail.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDThumbnail.java index 4d989865..be49a19f 100644 --- a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDThumbnail.java +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDThumbnail.java @@ -37,7 +37,7 @@ class PSDThumbnail extends PSDImageResource { */ @Override protected void readData(final ImageInputStream pInput) throws IOException { - // TODO: Support for RAW RGB (format == 0) + // TODO: Support for RAW RGB (format == 0): Extract RAW reader from PICT RAW QuickTime decompressor int format = pInput.readInt(); switch (format) { case 0: diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUtil.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUtil.java index 58a5fd4c..f3e1ab41 100644 --- a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUtil.java +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUtil.java @@ -31,8 +31,10 @@ package com.twelvemonkeys.imageio.plugins.psd; import com.twelvemonkeys.imageio.util.IIOUtil; import com.twelvemonkeys.io.enc.DecoderStream; import com.twelvemonkeys.io.enc.PackBitsDecoder; +import com.twelvemonkeys.lang.StringUtil; import javax.imageio.stream.ImageInputStream; +import java.io.DataInput; import java.io.DataInputStream; import java.io.IOException; import java.util.zip.ZipInputStream; @@ -57,23 +59,22 @@ final class PSDUtil { ); } - // TODO: Proably also useful for PICT reader, move to some common util? - static String readPascalString(ImageInputStream pInput) throws IOException { + // TODO: Proably also useful for PICT reader, move to some common util? + // TODO: Is this REALLY different from the previous method? Maybe the pad should not be read.. + static String readPascalString(final DataInput pInput) throws IOException { int length = pInput.readUnsignedByte(); -// int length = pInput.readUnsignedShort(); byte[] bytes = new byte[length]; pInput.readFully(bytes); - if (length % 2 == 0) { - pInput.readByte(); // Pad - } - return new String(bytes); + + return StringUtil.decode(bytes, 0, bytes.length, "ASCII"); } - static String readPascalStringByte(ImageInputStream pInput) throws IOException { - int length = pInput.readUnsignedByte(); - byte[] bytes = new byte[length]; + static String readUTF16String(final DataInput pInput) throws IOException { + int length = pInput.readInt(); + byte[] bytes = new byte[length * 2]; pInput.readFully(bytes); - return new String(bytes); + + return StringUtil.decode(bytes, 0, bytes.length, "UTF-16"); } static DataInputStream createPackBitsStream(final ImageInputStream pInput, long pLength) { diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDVersionInfo.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDVersionInfo.java new file mode 100644 index 00000000..683fa3a3 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDVersionInfo.java @@ -0,0 +1,57 @@ +package com.twelvemonkeys.imageio.plugins.psd; + +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; + +/** + * PSDVersionInfo + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDVersionInfo.java,v 1.0 Nov 6, 2009 1:02:19 PM haraldk Exp$ + */ +final class PSDVersionInfo extends PSDImageResource { + + int mVersion; + boolean mHasRealMergedData; + String mWriter; + String mReader; + int mFileVersion; + + PSDVersionInfo(final short pId, final ImageInputStream pInput) throws IOException { + super(pId, pInput); + } + + @Override + protected void readData(final ImageInputStream pInput) throws IOException { + /* + 4 bytes version + 1 byte hasRealMergedData + Unicode string: writer name + Unicode string: reader name + 4 bytes file version. + */ + + mVersion = pInput.readInt(); + mHasRealMergedData = pInput.readBoolean(); + + mWriter = PSDUtil.readUTF16String(pInput); + mReader = PSDUtil.readUTF16String(pInput); + + mFileVersion = pInput.readInt(); + } + + @Override + public String toString() { + StringBuilder builder = toStringBuilder(); + + builder.append(", version: ").append(mVersion); + builder.append(", hasRealMergedData: ").append(mHasRealMergedData); + builder.append(", writer: ").append(mWriter); + builder.append(", reader: ").append(mReader); + builder.append(", file version: ").append(mFileVersion); + builder.append("]"); + + return builder.toString(); + } +}