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 55a3ba45..04f8c678 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
@@ -225,11 +225,11 @@ interface PSD {
// 03f8
/** Color transfer functions */
- int RES_COLOR_TRANSFER_FUNCITON = 0x03f8;
+ int RES_COLOR_TRANSFER_FUNCTION = 0x03f8;
// 03f9
/** Duotone transfer functions */
- int RES_DUOTONE_TRANSFER_FUNCITON = 0x03f9;
+ int RES_DUOTONE_TRANSFER_FUNCTOON = 0x03f9;
// 03fa
/** Duotone image information */
@@ -385,7 +385,7 @@ interface PSD {
* (Photoshop 5.0) Unicode Alpha Names
* Unicode string (4 bytes length followed by string).
*/
- int RES_UNICODE_ALPHA_NAME = 0x0415;
+ int RES_UNICODE_ALPHA_NAMES = 0x0415;
// 1046
/**
diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDGridAndGuideInfo.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDGridAndGuideInfo.java
new file mode 100644
index 00000000..2b4e0b14
--- /dev/null
+++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDGridAndGuideInfo.java
@@ -0,0 +1,58 @@
+package com.twelvemonkeys.imageio.plugins.psd;
+
+import javax.imageio.stream.ImageInputStream;
+import java.io.IOException;
+
+/**
+ * PSDGridAndGuideInfo
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: PSDGridAndGuideInfo.java,v 1.0 Nov 7, 2009 8:46:13 PM haraldk Exp$
+ */
+final class PSDGridAndGuideInfo extends PSDImageResource {
+/* Grid & guide header */
+//typedef struct {
+// guint32 fVersion; /* Version - always 1 for PS */
+// guint32 fGridCycleV; /* Vertical grid size */
+// guint32 fGridCycleH; /* Horizontal grid size */
+// guint32 fGuideCount; /* Number of guides */
+//} GuideHeader;
+
+/* Guide resource block */
+//typedef struct {
+// guint32 fLocation; /* Guide position in Pixels * 100 */
+// gchar fDirection; /* Guide orientation */
+//} GuideResource;
+
+ int mVersion;
+ int mGridCycleVertical;
+ int mGridCycleHorizontal;
+ int mGuideCount;
+
+ GuideResource[] mGuides;
+
+ PSDGridAndGuideInfo(final short pId, final ImageInputStream pInput) throws IOException {
+ super(pId, pInput);
+ }
+
+ @Override
+ protected void readData(final ImageInputStream pInput) throws IOException {
+ mVersion = pInput.readInt();
+ mGridCycleVertical = pInput.readInt();
+ mGridCycleHorizontal = pInput.readInt();
+ mGuideCount = pInput.readInt();
+
+ mGuides = new GuideResource[mGuideCount];
+
+ for (GuideResource guide : mGuides) {
+ guide.mLocation = pInput.readInt();
+ guide.mDirection = pInput.readByte();
+ }
+ }
+
+ static class GuideResource {
+ int mLocation;
+ byte mDirection; // 0: vertical, 1: horizontal
+ }
+}
diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDIPTCData.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDIPTCData.java
new file mode 100644
index 00000000..000084e7
--- /dev/null
+++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDIPTCData.java
@@ -0,0 +1,418 @@
+package com.twelvemonkeys.imageio.plugins.psd;
+
+import com.twelvemonkeys.lang.StringUtil;
+
+import javax.imageio.IIOException;
+import javax.imageio.stream.ImageInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.*;
+import java.util.*;
+
+/**
+ * PSDIPTCData
+ *
+ * @author Harald Kuhr
+ * @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 {
+ // TODO: Refactor to be more like PSDEXIF1Data...
+ // TODO: Extract IPTC/EXIF/XMP metadata extraction/parsing to separate module(s)
+ Directory mDirectory;
+
+ PSDIPTCData(final short pId, final ImageInputStream pInput) throws IOException {
+ super(pId, pInput);
+ }
+
+ @Override
+ protected void readData(final ImageInputStream pInput) throws IOException {
+ mDirectory = Directory.read(pInput, mSize);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = toStringBuilder();
+ builder.append(", ").append(mDirectory);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ static class Entry {
+ private int mTagId;
+ private String mValue;
+
+ public Entry(int pTagId, String pValue) {
+ mTagId = pTagId;
+ mValue = pValue;
+ }
+
+ @Override
+ public String toString() {
+ return (mTagId >> 8) + ":" + (mTagId & 0xff) + ": " + mValue;
+ }
+ }
+
+ static class Directory implements Iterable {
+ private static final int ENCODING_UNKNOWN = -1;
+ private static final int ENCODING_UNSPECIFIED = 0;
+ private static final int ENCODING_UTF_8 = 0x1b2547;
+
+ private int mEncoding = ENCODING_UNSPECIFIED;
+ final List mEntries = new ArrayList();
+
+ private Directory() {}
+
+ @Override
+ public String toString() {
+ return "Directory" + mEntries.toString();
+ }
+
+ public Iterator iterator() {
+ return mEntries.iterator();
+ }
+
+ public static Directory read(final ImageInputStream pInput, final long pSize) throws IOException {
+ Directory directory = new Directory();
+
+ final long streamEnd = pInput.getStreamPosition() + pSize;
+
+ // For each tag
+ while (pInput.getStreamPosition() < streamEnd) {
+ // Identifies start of a tag
+ byte b = pInput.readByte();
+ if (b != 0x1c) {
+ throw new IIOException("Corrupt IPTC stream segment");
+ }
+
+ // We need at least four bytes left to read a tag
+ if (pInput.getStreamPosition() + 4 >= streamEnd) {
+ break;
+ }
+
+ int directoryType = pInput.readUnsignedByte();
+ int tagType = pInput.readUnsignedByte();
+ int tagByteCount = pInput.readUnsignedShort();
+
+ if (pInput.getStreamPosition() + tagByteCount > streamEnd) {
+ throw new IIOException("Data for tag extends beyond end of IPTC segment: " + (tagByteCount + pInput.getStreamPosition() - streamEnd));
+ }
+
+ directory.processTag(pInput, directoryType, tagType, tagByteCount);
+ }
+
+ return directory;
+ }
+
+ private void processTag(ImageInputStream pInput, int directoryType, int tagType, int tagByteCount) throws IOException {
+ int tagIdentifier = (directoryType << 8) | tagType;
+
+ String str = null;
+ switch (tagIdentifier) {
+ case IPTC.TAG_CODED_CHARACTER_SET:
+ // TODO: Use this encoding!?
+ // TODO: Move somewhere else?
+ mEncoding = parseEncoding(pInput, tagByteCount);
+ return;
+ case IPTC.TAG_RECORD_VERSION:
+ // short
+ str = Integer.toString(pInput.readUnsignedShort());
+ break;
+// case IPTC.TAG_RELEASE_DATE:
+// case IPTC.TAG_EXPIRATION_DATE:
+// case IPTC.TAG_REFERENCE_DATE:
+// case IPTC.TAG_DATE_CREATED:
+// case IPTC.TAG_DIGITAL_CREATION_DATE:
+// // Date object
+// Date date = parseISO8601DatePart(pInput, tagByteCount);
+// if (date != null) {
+// directory.setDate(tagIdentifier, date);
+// return;
+// }
+// case IPTC.TAG_RELEASE_TIME:
+// case IPTC.TAG_EXPIRATION_TIME:
+// case IPTC.TAG_TIME_CREATED:
+// case IPTC.TAG_DIGITAL_CREATION_TIME:
+// // NOTE: Spec says fields should be sent in order, so this is okay
+// date = getDateForTime(directory, tagIdentifier);
+//
+// Date time = parseISO8601TimePart(pInput, tagByteCount, date);
+// if (time != null) {
+// directory.setDate(tagIdentifier, time);
+// return;
+// }
+//
+ default:
+ // fall through
+ }
+
+ // Skip non-Application fields, as they are typically not human readable
+ if (directoryType << 8 != IPTC.APPLICATION_RECORD) {
+ return;
+ }
+
+ // If we don't have a value, treat it as a string
+ if (str == null) {
+ if (tagByteCount < 1) {
+ str = "(No value)";
+ }
+ else {
+ str = String.format("\"%s\"", parseString(pInput, tagByteCount));
+ }
+ }
+
+ mEntries.add(new Entry(tagIdentifier, str));
+
+// if (directory.containsTag(tagIdentifier)) {
+// // TODO: Does that REALLY help for performance?!
+// // this fancy string[] business avoids using an ArrayList for performance reasons
+// String[] oldStrings;
+// String[] newStrings;
+// try {
+// oldStrings = directory.getStringArray(tagIdentifier);
+// }
+// catch (MetadataException e) {
+// oldStrings = null;
+// }
+// if (oldStrings == null) {
+// newStrings = new String[1];
+// }
+// else {
+// newStrings = new String[oldStrings.length + 1];
+// System.arraycopy(oldStrings, 0, newStrings, 0, oldStrings.length);
+// }
+// newStrings[newStrings.length - 1] = str;
+// directory.setStringArray(tagIdentifier, newStrings);
+// }
+// else {
+// directory.setString(tagIdentifier, str);
+// }
+ }
+
+// private Date getDateForTime(final Directory directory, final int tagIdentifier) {
+// int dateTag;
+//
+// switch (tagIdentifier) {
+// case IPTC.TAG_RELEASE_TIME:
+// dateTag = IPTC.TAG_RELEASE_DATE;
+// break;
+// case IPTC.TAG_EXPIRATION_TIME:
+// dateTag = IPTC.TAG_EXPIRATION_DATE;
+// break;
+// case IPTC.TAG_TIME_CREATED:
+// dateTag = IPTC.TAG_DATE_CREATED;
+// break;
+// case IPTC.TAG_DIGITAL_CREATION_TIME:
+// dateTag = IPTC.TAG_DIGITAL_CREATION_DATE;
+// break;
+// default:
+// return new Date(0l);
+// }
+//
+// return directory.containsTag(dateTag) ? directory.getDate(dateTag) : new Date(0l);
+// }
+
+
+ private int parseEncoding(final ImageInputStream pInput, int tagByteCount) throws IOException {
+ return tagByteCount == 3
+ && (pInput.readUnsignedByte() << 16 | pInput.readUnsignedByte() << 8 | pInput.readUnsignedByte()) == ENCODING_UTF_8
+ ? ENCODING_UTF_8 : ENCODING_UNKNOWN;
+ }
+
+// private Date parseISO8601TimePart(final ImageInputStream pInputStream, int tagByteCount, final Date date) throws IOException {
+// // ISO 8601: HHMMSS±HHMM
+// if (tagByteCount >= 11) {
+// String timeStr = parseString(pInputStream, tagByteCount);
+// try {
+// int hour = Integer.parseInt(timeStr.substring(0, 2));
+// int minute = Integer.parseInt(timeStr.substring(2, 4));
+// int second = Integer.parseInt(timeStr.substring(4, 6));
+// String tzOffset = timeStr.substring(6, 11);
+//
+// TimeZone zone = new SimpleTimeZone(Integer.parseInt(tzOffset.charAt(0) == '+' ? tzOffset.substring(1) : tzOffset), tzOffset);
+//
+// GregorianCalendar calendar = new GregorianCalendar(zone);
+// calendar.setTime(date);
+//
+// calendar.add(Calendar.HOUR_OF_DAY, hour);
+// calendar.add(Calendar.MINUTE, minute);
+// calendar.add(Calendar.SECOND, second);
+//
+// return calendar.getTime();
+// }
+// catch (NumberFormatException e) {
+// // fall through and we'll store whatever was there as a String
+// }
+// }
+// return null;
+// }
+//
+// private Date parseISO8601DatePart(final ImageInputStream pInputStream, int tagByteCount) throws IOException {
+// // ISO 8601: CCYYMMDD
+// if (tagByteCount >= 8) {
+// String dateStr = parseString(pInputStream, tagByteCount);
+// try {
+// int year = Integer.parseInt(dateStr.substring(0, 4));
+// int month = Integer.parseInt(dateStr.substring(4, 6)) - 1;
+// int day = Integer.parseInt(dateStr.substring(6, 8));
+// GregorianCalendar calendar = new GregorianCalendar(year, month, day);
+// return calendar.getTime();
+// }
+// catch (NumberFormatException e) {
+// // fall through and we'll store whatever was there as a String
+// }
+// }
+// return null;
+// }
+
+ // TODO: Pass encoding as parameter? Use if specified
+ private String parseString(final ImageInputStream pInput, int length) throws IOException {
+ // NOTE: The IPTC "spec" says ISO 646 or ISO 2022 encoding. UTF-8 contains all 646 characters, but not 2022.
+ // This is however close to what libiptcdata does, see: http://libiptcdata.sourceforge.net/docs/iptc-i18n.html
+ // First try to decode using UTF-8 (which seems to be the de-facto standard)
+ String str;
+ Charset charset = Charset.forName("UTF-8");
+ CharsetDecoder decoder = charset.newDecoder();
+ CharBuffer chars;
+ byte[] data = new byte[length];
+ pInput.readFully(data);
+ try {
+ // Will fail fast on illegal UTF-8-sequences
+ chars = decoder.onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT)
+ .decode(ByteBuffer.wrap(data));
+ str = chars.toString();
+ }
+ catch (CharacterCodingException notUTF8) {
+ if (mEncoding == ENCODING_UTF_8) {
+ throw new IIOException("Wrong encoding of IPTC data, explicitly set to UTF-8 in DataSet 1:90", notUTF8);
+ }
+
+ // Fall back to use ISO-8859-1
+ // This will not fail, but may may create wrong fallback-characters
+ str = StringUtil.decode(data, 0, data.length, "ISO8859_1");
+ }
+
+ return str;
+ }
+ }
+
+ static interface IPTC {
+ static final int ENVELOPE_RECORD = 1 << 8;
+ static final int APPLICATION_RECORD = 2 << 8;
+
+ static final int TAG_CODED_CHARACTER_SET = ENVELOPE_RECORD | 90;
+
+ /** 2:00 Record Version (mandatory) */
+ public static final int TAG_RECORD_VERSION = APPLICATION_RECORD; // 0x0200
+// /** 2:03 Object Type Reference */
+// public static final int TAG_OBJECT_TYPE_REFERENCE = APPLICATION_RECORD | 3;
+// /** 2:04 Object Attribute Reference (repeatable) */
+// public static final int TAG_OBJECT_ATTRIBUTE_REFERENCE = APPLICATION_RECORD | 4;
+// /** 2:05 Object Name */
+// public static final int TAG_OBJECT_NAME = APPLICATION_RECORD | 5; // 0x0205
+// /** 2:07 Edit Status */
+// public static final int TAG_EDIT_STATUS = APPLICATION_RECORD | 7;
+// /** 2:08 Editorial Update */
+// public static final int TAG_EDITORIAL_UPDATE = APPLICATION_RECORD | 8;
+// /** 2:10 Urgency */
+// public static final int TAG_URGENCY = APPLICATION_RECORD | 10;
+// /** 2:12 Subect Reference (repeatable) */
+// public static final int TAG_SUBJECT_REFERENCE = APPLICATION_RECORD | 12;
+// /** 2:15 Category */
+// public static final int TAG_CATEGORY = APPLICATION_RECORD | 15; // 0x020f
+// /** 2:20 Supplemental Category (repeatable) */
+// public static final int TAG_SUPPLEMENTAL_CATEGORIES = APPLICATION_RECORD | 20;
+// /** 2:22 Fixture Identifier */
+// public static final int TAG_FIXTURE_IDENTIFIER = APPLICATION_RECORD | 22;
+// /** 2:25 Keywords (repeatable) */
+// public static final int TAG_KEYWORDS = APPLICATION_RECORD | 25;
+// /** 2:26 Content Locataion Code (repeatable) */
+// public static final int TAG_CONTENT_LOCATION_CODE = APPLICATION_RECORD | 26;
+// /** 2:27 Content Locataion Name (repeatable) */
+// public static final int TAG_CONTENT_LOCATION_NAME = APPLICATION_RECORD | 27;
+// /** 2:30 Release Date */
+// public static final int TAG_RELEASE_DATE = APPLICATION_RECORD | 30;
+// /** 2:35 Release Time */
+// public static final int TAG_RELEASE_TIME = APPLICATION_RECORD | 35;
+// /** 2:37 Expiration Date */
+// public static final int TAG_EXPIRATION_DATE = APPLICATION_RECORD | 37;
+// /** 2:38 Expiration Time */
+// public static final int TAG_EXPIRATION_TIME = APPLICATION_RECORD | 38;
+// /** 2:40 Special Instructions */
+// public static final int TAG_SPECIAL_INSTRUCTIONS = APPLICATION_RECORD | 40; // 0x0228
+// /** 2:42 Action Advised (1: Kill, 2: Replace, 3: Append, 4: Reference) */
+// public static final int TAG_ACTION_ADVICED = APPLICATION_RECORD | 42;
+// /** 2:45 Reference Service (repeatable in triplets with 2:47 and 2:50) */
+// public static final int TAG_REFERENCE_SERVICE = APPLICATION_RECORD | 45;
+// /** 2:47 Reference Date (mandatory if 2:45 present) */
+// public static final int TAG_REFERENCE_DATE = APPLICATION_RECORD | 47;
+// /** 2:50 Reference Number (mandatory if 2:45 present) */
+// public static final int TAG_REFERENCE_NUMBER = APPLICATION_RECORD | 50;
+// /** 2:55 Date Created */
+// public static final int TAG_DATE_CREATED = APPLICATION_RECORD | 55; // 0x0237
+// /** 2:60 Time Created */
+// public static final int TAG_TIME_CREATED = APPLICATION_RECORD | 60;
+// /** 2:62 Digital Creation Date */
+// public static final int TAG_DIGITAL_CREATION_DATE = APPLICATION_RECORD | 62;
+// /** 2:63 Digital Creation Date */
+// public static final int TAG_DIGITAL_CREATION_TIME = APPLICATION_RECORD | 63;
+// /** 2:65 Originating Program */
+// public static final int TAG_ORIGINATING_PROGRAM = APPLICATION_RECORD | 65;
+// /** 2:70 Program Version (only valid if 2:65 present) */
+// public static final int TAG_PROGRAM_VERSION = APPLICATION_RECORD | 70;
+// /** 2:75 Object Cycle (a: morning, p: evening, b: both) */
+// public static final int TAG_OBJECT_CYCLE = APPLICATION_RECORD | 75;
+// /** 2:80 By-line (repeatable) */
+// public static final int TAG_BY_LINE = APPLICATION_RECORD | 80; // 0x0250
+// /** 2:85 By-line Title (repeatable) */
+// public static final int TAG_BY_LINE_TITLE = APPLICATION_RECORD | 85; // 0x0255
+// /** 2:90 City */
+// public static final int TAG_CITY = APPLICATION_RECORD | 90; // 0x025a
+// /** 2:92 Sub-location */
+// public static final int TAG_SUB_LOCATION = APPLICATION_RECORD | 92;
+// /** 2:95 Province/State */
+// public static final int TAG_PROVINCE_OR_STATE = APPLICATION_RECORD | 95; // 0x025f
+// /** 2:100 Country/Primary Location Code */
+// public static final int TAG_COUNTRY_OR_PRIMARY_LOCATION_CODE = APPLICATION_RECORD | 100;
+// /** 2:101 Country/Primary Location Name */
+// public static final int TAG_COUNTRY_OR_PRIMARY_LOCATION = APPLICATION_RECORD | 101; // 0x0265
+// /** 2:103 Original Transmission Reference */
+// public static final int TAG_ORIGINAL_TRANSMISSION_REFERENCE = APPLICATION_RECORD | 103; // 0x0267
+// /** 2:105 Headline */
+// public static final int TAG_HEADLINE = APPLICATION_RECORD | 105; // 0x0269
+// /** 2:110 Credit */
+// public static final int TAG_CREDIT = APPLICATION_RECORD | 110; // 0x026e
+// /** 2:115 Source */
+// public static final int TAG_SOURCE = APPLICATION_RECORD | 115; // 0x0273
+// /** 2:116 Copyright Notice */
+// public static final int TAG_COPYRIGHT_NOTICE = APPLICATION_RECORD | 116; // 0x0274
+// /** 2:118 Contact */
+// public static final int TAG_CONTACT = APPLICATION_RECORD | 118;
+// /** 2:120 Catption/Abstract */
+// public static final int TAG_CAPTION = APPLICATION_RECORD | 120; // 0x0278
+// /** 2:122 Writer/Editor (repeatable) */
+// public static final int TAG_WRITER = APPLICATION_RECORD | 122; // 0x027a
+// /** 2:125 Rasterized Caption (binary data) */
+// public static final int TAG_RASTERIZED_CATPTION = APPLICATION_RECORD | 125;
+// /** 2:130 Image Type */
+// public static final int TAG_IMAGE_TYPE = APPLICATION_RECORD | 130;
+// /** 2:131 Image Orientation */
+// public static final int TAG_IMAGE_ORIENTATION = APPLICATION_RECORD | 131;
+// /** 2:135 Language Identifier */
+// public static final int TAG_LANGUAGE_IDENTIFIER = APPLICATION_RECORD | 135;
+//
+// // TODO: Should we expose this field?
+// /**
+// * 2:199 JobMinder Assignment Data (Custom IPTC field).
+// * A common custom IPTC field used by a now discontinued application called JobMinder.
+// *
+// * @see JobMinder Homepage
+// */
+// static final int CUSTOM_TAG_JOBMINDER_ASSIGMENT_DATA = APPLICATION_RECORD | 199;
+//
+// // TODO: 2:150-2:154 Audio and 2:200-2:202 Object Preview Data
+
+ }
+}
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 8dd3770a..180ce4d7 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
@@ -43,6 +43,9 @@ import java.lang.reflect.Field;
* @version $Id: PSDImageResource.java,v 1.0 Apr 29, 2008 5:49:06 PM haraldk Exp$
*/
class PSDImageResource {
+ // TODO: Refactor image resources to separate package
+ // TODO: Change constructor to store stream offset and length only (+ possibly the name), defer reading
+
final short mId;
final String mName;
final long mSize;
@@ -61,6 +64,8 @@ class PSDImageResource {
mSize = pInput.readUnsignedInt();
readData(pInput);
+ // TODO: Sanity check reading here?
+
// Data is even-padded
if (mSize % 2 != 0) {
pInput.read();
@@ -114,6 +119,8 @@ class PSDImageResource {
case PSD.RES_ALPHA_CHANNEL_INFO:
case PSD.RES_DISPLAY_INFO:
case PSD.RES_PRINT_FLAGS:
+ case PSD.RES_IPTC_NAA:
+ case PSD.RES_GRID_AND_GUIDES_INFO:
case PSD.RES_THUMBNAIL_PS4:
case PSD.RES_THUMBNAIL:
case PSD.RES_ICC_PROFILE:
@@ -121,6 +128,8 @@ class PSDImageResource {
case PSD.RES_EXIF_DATA_1:
// case PSD.RES_EXIF_DATA_3:
case PSD.RES_XMP_DATA:
+ case PSD.RES_PRINT_SCALE:
+ case PSD.RES_PIXEL_ASPECT_RATIO:
case PSD.RES_PRINT_FLAGS_INFORMATION:
return null;
default:
@@ -135,7 +144,7 @@ class PSDImageResource {
catch (IllegalAccessException ignore) {
}
- return "unknown resource";
+ return "UnknownResource";
}
}
@@ -157,17 +166,27 @@ class PSDImageResource {
return new PSDDisplayInfo(id, pInput);
case PSD.RES_PRINT_FLAGS:
return new PSDPrintFlags(id, pInput);
+ case PSD.RES_IPTC_NAA:
+ return new PSDIPTCData(id, pInput);
+ case PSD.RES_GRID_AND_GUIDES_INFO:
+ return new PSDGridAndGuideInfo(id, pInput);
case PSD.RES_THUMBNAIL_PS4:
case PSD.RES_THUMBNAIL:
return new PSDThumbnail(id, pInput);
case PSD.RES_ICC_PROFILE:
return new ICCProfile(id, pInput);
+ case PSD.RES_UNICODE_ALPHA_NAMES:
+ return new PSDUnicodeAlphaNames(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:
return new PSDXMPData(id, pInput);
+ case PSD.RES_PRINT_SCALE:
+ return new PSDPrintScale(id, pInput);
+ case PSD.RES_PIXEL_ASPECT_RATIO:
+ return new PSDPixelAspectRatio(id, pInput);
case PSD.RES_PRINT_FLAGS_INFORMATION:
return new PSDPrintFlagsInformation(id, pInput);
default:
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 69188525..e622bc01 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
@@ -43,11 +43,24 @@ public final class PSDMetadata extends IIOMetadata implements Cloneable {
static final String[] DISPLAY_INFO_CS = {
"RGB", "HSB", "CMYK", "PANTONE", "FOCOLTONE", "TRUMATCH", "TOYO", "LAB", "GRAYSCALE", null, "HKS", "DIC",
- null, // ... (until index 2999),
+ null, // TODO: ... (until index 2999),
"ANPA"
};
static final String[] DISPLAY_INFO_KINDS = {"selected", "protected"};
+ static final String[] RESOLUTION_UNITS = {null, "pixels/inch", "pixels/cm"};
+ static final String[] DIMENSION_UNITS = {null, "in", "cm", "pt", "picas", "columns"};
+
+ static final String[] JAVA_CS = {
+ "XYZ", "Lab", "Yuv", "YCbCr", "Yxy", "RGB", "GRAY", "HSV", "HLS", "CMYK", "CMY",
+ "2CLR", "3CLR", "4CLR", "5CLR", "6CLR", "7CLR", "8CLR", "9CLR", "ACLR", "BCLR", "CCLR", "DCLR", "ECLR", "FCLR"
+ };
+
+ static final String[] GUIDE_ORIENTATIONS = {"vertical", "horizontal"};
+
+ static final String[] PRINT_SCALE_STYLES = {"centered", "scaleToFit", "userDefined"};
+
+
protected PSDMetadata() {
// TODO: Allow XMP, EXIF and IPTC as extra formats?
super(true, NATIVE_METADATA_FORMAT_NAME, NATIVE_METADATA_FORMAT_CLASS_NAME, null, null);
@@ -155,8 +168,9 @@ public final class PSDMetadata extends IIOMetadata implements Cloneable {
}
private Node createHeaderNode() {
- IIOMetadataNode header = new IIOMetadataNode("PSDHeader");
+ IIOMetadataNode header = new IIOMetadataNode("Header");
+ header.setAttribute("type", "PSD");
header.setAttribute("version", "1");
header.setAttribute("channels", Integer.toString(mHeader.mChannels));
header.setAttribute("height", Integer.toString(mHeader.mHeight));
@@ -175,7 +189,15 @@ public final class PSDMetadata extends IIOMetadata implements Cloneable {
// 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) {
+ if (imageResource instanceof ICCProfile) {
+ ICCProfile profile = (ICCProfile) imageResource;
+
+ // TODO: Format spec
+ node = new IIOMetadataNode("ICCProfile");
+ node.setAttribute("colorSpaceType", JAVA_CS[profile.getProfile().getColorSpaceType()]);
+ node.setUserObject(profile.getProfile());
+ }
+ else if (imageResource instanceof PSDAlphaChannelInfo) {
PSDAlphaChannelInfo alphaChannelInfo = (PSDAlphaChannelInfo) imageResource;
node = new IIOMetadataNode("AlphaChannelInfo");
@@ -185,8 +207,6 @@ public final class PSDMetadata extends IIOMetadata implements Cloneable {
nameNode.setAttribute("value", name);
node.appendChild(nameNode);
}
-
- resource.appendChild(node);
}
else if (imageResource instanceof PSDDisplayInfo) {
PSDDisplayInfo displayInfo = (PSDDisplayInfo) imageResource;
@@ -205,14 +225,121 @@ public final class PSDMetadata extends IIOMetadata implements Cloneable {
node.setAttribute("colors", builder.toString());
node.setAttribute("opacity", Integer.toString(displayInfo.mOpacity));
node.setAttribute("kind", DISPLAY_INFO_KINDS[displayInfo.mKind]);
+ }
+ else if (imageResource instanceof PSDGridAndGuideInfo) {
+ PSDGridAndGuideInfo info = (PSDGridAndGuideInfo) imageResource;
- resource.appendChild(node);
+ node = new IIOMetadataNode("GridAndGuideInfo");
+ node.setAttribute("version", String.valueOf(info.mVersion));
+ node.setAttribute("verticalGridCycle", String.valueOf(info.mGridCycleVertical));
+ node.setAttribute("horizontalGridCycle", String.valueOf(info.mGridCycleHorizontal));
+
+ for (PSDGridAndGuideInfo.GuideResource guide : info.mGuides) {
+ IIOMetadataNode guideNode = new IIOMetadataNode("Guide");
+ guideNode.setAttribute("location", Integer.toString(guide.mLocation));
+ guideNode.setAttribute("orientation", GUIDE_ORIENTATIONS[guide.mDirection]);
+ }
+ }
+ else if (imageResource instanceof PSDPixelAspectRatio) {
+ PSDPixelAspectRatio aspectRatio = (PSDPixelAspectRatio) imageResource;
+
+ node = new IIOMetadataNode("PixelAspectRatio");
+ node.setAttribute("version", String.valueOf(aspectRatio.mVersion));
+ node.setAttribute("aspectRatio", String.valueOf(aspectRatio.mAspect));
+ }
+ else if (imageResource instanceof PSDPrintFlags) {
+ PSDPrintFlags flags = (PSDPrintFlags) imageResource;
+
+ node = new IIOMetadataNode("PrintFlags");
+ node.setAttribute("labels", String.valueOf(flags.mLabels));
+ node.setAttribute("cropMarks", String.valueOf(flags.mCropMasks));
+ node.setAttribute("colorBars", String.valueOf(flags.mColorBars));
+ node.setAttribute("registrationMarks", String.valueOf(flags.mRegistrationMarks));
+ node.setAttribute("negative", String.valueOf(flags.mNegative));
+ node.setAttribute("flip", String.valueOf(flags.mFlip));
+ node.setAttribute("interpolate", String.valueOf(flags.mInterpolate));
+ node.setAttribute("caption", String.valueOf(flags.mCaption));
+ }
+ else if (imageResource instanceof PSDPrintFlagsInformation) {
+ PSDPrintFlagsInformation information = (PSDPrintFlagsInformation) imageResource;
+
+ node = new IIOMetadataNode("PrintFlagsInformation");
+ node.setAttribute("version", String.valueOf(information.mVersion));
+ node.setAttribute("cropMarks", String.valueOf(information.mCropMasks));
+ node.setAttribute("field", String.valueOf(information.mField));
+ node.setAttribute("bleedWidth", String.valueOf(information.mBleedWidth));
+ node.setAttribute("bleedScale", String.valueOf(information.mBleedScale));
+ }
+ else if (imageResource instanceof PSDPrintScale) {
+ PSDPrintScale printScale = (PSDPrintScale) imageResource;
+
+ node = new IIOMetadataNode("PrintScale");
+ node.setAttribute("style", PRINT_SCALE_STYLES[printScale.mStyle]);
+ node.setAttribute("xLocation", String.valueOf(printScale.mXLocation));
+ node.setAttribute("yLocation", String.valueOf(printScale.mYlocation));
+ node.setAttribute("scale", String.valueOf(printScale.mScale));
+ }
+ else if (imageResource instanceof PSDResolutionInfo) {
+ PSDResolutionInfo information = (PSDResolutionInfo) imageResource;
+
+ node = new IIOMetadataNode("ResolutionInfo");
+ node.setAttribute("horizontalResolution", String.valueOf(information.mHRes));
+ node.setAttribute("horizontalResolutionUnit", RESOLUTION_UNITS[information.mHResUnit]);
+ node.setAttribute("widthUnit", DIMENSION_UNITS[information.mWidthUnit]);
+ node.setAttribute("verticalResolution", String.valueOf(information.mVRes));
+ node.setAttribute("verticalResolutionUnit", RESOLUTION_UNITS[information.mVResUnit]);
+ node.setAttribute("heightUnit", DIMENSION_UNITS[information.mHeightUnit]);
+ }
+ else if (imageResource instanceof PSDUnicodeAlphaNames) {
+ PSDUnicodeAlphaNames alphaNames = (PSDUnicodeAlphaNames) imageResource;
+
+ node = new IIOMetadataNode("UnicodeAlphaNames");
+
+ for (String name : alphaNames.mNames) {
+ IIOMetadataNode nameNode = new IIOMetadataNode("Name");
+ nameNode.setAttribute("value", name);
+ node.appendChild(nameNode);
+ }
+ }
+ else if (imageResource instanceof PSDVersionInfo) {
+ PSDVersionInfo information = (PSDVersionInfo) imageResource;
+
+ node = new IIOMetadataNode("VersionInfo");
+ node.setAttribute("version", String.valueOf(information.mVersion));
+ node.setAttribute("hasRealMergedData", String.valueOf(information.mHasRealMergedData));
+ node.setAttribute("writer", information.mWriter);
+ node.setAttribute("reader", information.mReader);
+ node.setAttribute("fileVersion", String.valueOf(information.mFileVersion));
+ }
+ else if (imageResource instanceof PSDThumbnail) {
+ // TODO: Revise/rethink this...
+ PSDThumbnail thumbnail = (PSDThumbnail) imageResource;
+
+ node = new IIOMetadataNode("Thumbnail");
+ // TODO: Thumbnail attributes + access to data, to avoid JPEG re-compression problems
+ node.setUserObject(thumbnail.getThumbnail());
+ }
+ else if (imageResource instanceof PSDIPTCData) {
+ // TODO: Revise/rethink this...
+ // Transcode to XMP? ;-)
+ PSDIPTCData iptc = (PSDIPTCData) imageResource;
+
+ node = new IIOMetadataNode("IPTC");
+ node.setUserObject(iptc.mDirectory);
+ }
+ else if (imageResource instanceof PSDEXIF1Data) {
+ // TODO: Revise/rethink this...
+ // Transcode to XMP? ;-)
+ PSDEXIF1Data exif = (PSDEXIF1Data) imageResource;
+
+ node = new IIOMetadataNode("EXIF");
+ node.setUserObject(exif.mDirectory);
}
else if (imageResource instanceof PSDXMPData) {
- // TODO: Revise/rethink this...
+ // TODO: Revise/rethink this... Would it be possible to parse XMP as IIOMetadataNodes? Or is that just stupid...
PSDXMPData xmp = (PSDXMPData) imageResource;
- node = new IIOMetadataNode("XMPData");
+ node = new IIOMetadataNode("XMP");
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
@@ -225,20 +352,16 @@ public final class PSDMetadata extends IIOMetadata implements Cloneable {
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));
+ node.setAttribute("resourceId", String.format("0x%04x", imageResource.mId));
+ resource.appendChild(node);
}
return resource;
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 6d2a765d..38ac1276 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
@@ -4,6 +4,7 @@ import org.w3c.dom.Document;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadataFormatImpl;
+import java.awt.image.BufferedImage;
import java.util.Arrays;
/**
@@ -32,20 +33,19 @@ public final class PSDMetadataFormat extends IIOMetadataFormatImpl {
// root -> PSDHeader
// TODO: How do I specify that the header is required?
- addElement("PSDHeader", PSDMetadata.NATIVE_METADATA_FORMAT_NAME, CHILD_POLICY_EMPTY);
+ addElement("Header", 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", "version", DATATYPE_INTEGER, false, "1", Arrays.asList("1"));
+ addAttribute("Header", "type", DATATYPE_STRING, false, "PSD", Arrays.asList("PSD", "PSB"));
+ addAttribute("Header", "version", DATATYPE_INTEGER, false, "1", Arrays.asList("1"));
- addAttribute("PSDHeader", "channels", DATATYPE_INTEGER, true, null, "1", "24", true, true);
+ addAttribute("Header", "channels", DATATYPE_INTEGER, true, null, "1", "24", true, true);
// rows?
- addAttribute("PSDHeader", "height", DATATYPE_INTEGER, true, null, "1", "30000", true, true);
+ addAttribute("Header", "height", DATATYPE_INTEGER, true, null, "1", "30000", true, true);
// 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("Header", "width", DATATYPE_INTEGER, true, null, "1", "30000", true, true);
+ addAttribute("Header", "bits", DATATYPE_INTEGER, true, null, Arrays.asList("1", "8", "16"));
// TODO: Consider using more readable names?!
- addAttribute("PSDHeader", "mode", DATATYPE_INTEGER, true, null, Arrays.asList(PSDMetadata.COLOR_MODES));
+ addAttribute("Header", "mode", DATATYPE_STRING, true, null, Arrays.asList(PSDMetadata.COLOR_MODES));
/*
Contains the required data to define the color mode.
@@ -85,22 +85,46 @@ public final class PSDMetadataFormat extends IIOMetadataFormatImpl {
// root -> ImageResources -> AlphaChannelInfo
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);
+ addAttribute("Name", "value", DATATYPE_STRING, true, null);
// 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", "colorSpace", DATATYPE_STRING, true, null, Arrays.asList(PSDMetadata.DISPLAY_INFO_CS));
addAttribute("DisplayInfo", "colors", DATATYPE_INTEGER, true, 4, 4);
addAttribute("DisplayInfo", "opacity", DATATYPE_INTEGER, true, null, "0", "100", true, true);
// TODO: Consider using human readable strings
- addAttribute("DisplayInfo", "kind", DATATYPE_INTEGER, true, null, Arrays.asList(PSDMetadata.DISPLAY_INFO_KINDS));
+ addAttribute("DisplayInfo", "kind", DATATYPE_STRING, true, null, Arrays.asList(PSDMetadata.DISPLAY_INFO_KINDS));
- // root -> ImageResources -> EXIF1Data
- addElement("EXIF1Data", "ImageResources", CHILD_POLICY_ALL);
+ // root -> ImageResources -> EXIF
+ addElement("EXIF", "ImageResources", CHILD_POLICY_EMPTY);
+ addObjectValue("EXIF", PSDEXIF1Data.Directory.class, true, null);
// TODO: Incorporate EXIF / TIFF metadata here somehow... (or treat as opaque bytes?)
+ // root -> ImageResources -> GridAndGuideInfo
+ addElement("GridAndGuideInfo", "ImageResources", 0, Integer.MAX_VALUE);
+ addAttribute("GridAndGuideInfo", "version", DATATYPE_INTEGER, false, "1");
+ addAttribute("GridAndGuideInfo", "verticalGridCycle", DATATYPE_INTEGER, false, "576");
+ addAttribute("GridAndGuideInfo", "horizontalGridCycle", DATATYPE_INTEGER, false, "576");
+ addElement("Guide", "GridAndGuideInfo", CHILD_POLICY_EMPTY);
+ addAttribute("Guide", "location", DATATYPE_INTEGER, true, null, "0", Integer.toString(Integer.MAX_VALUE), true, true);
+ addAttribute("Guide", "orientation", DATATYPE_STRING, true, null, Arrays.asList(PSDMetadata.GUIDE_ORIENTATIONS));
+
+ // root -> ImageResources -> ICCProfile
+ addElement("ICCProfile", "ImageResources", CHILD_POLICY_EMPTY);
+ addAttribute("ICCProfile", "colorSpaceType", DATATYPE_STRING, true, null, Arrays.asList(PSDMetadata.JAVA_CS));
+
+ // root -> ImageResources -> IPTC
+ addElement("IPTC", "ImageResources", CHILD_POLICY_EMPTY);
+ addObjectValue("IPTC", PSDIPTCData.Directory.class, true, null);
+ // TODO: Incorporate IPTC metadata here somehow... (or treat as opaque bytes?)
+
+ // root -> ImageResources -> PixelAspectRatio
+ addElement("PixelAspectRatio", "ImageResources", CHILD_POLICY_EMPTY);
+ addAttribute("PixelAspectRatio", "version", DATATYPE_STRING, false, "1");
+ addAttribute("PixelAspectRatio", "aspectRatio", DATATYPE_DOUBLE, true, null, "0", Double.toString(Double.POSITIVE_INFINITY), true, false);
+
// root -> ImageResources -> PrintFlags
addElement("PrintFlags", "ImageResources", CHILD_POLICY_EMPTY);
addBooleanAttribute("PrintFlags", "labels", false, false);
@@ -114,29 +138,53 @@ public final class PSDMetadataFormat extends IIOMetadataFormatImpl {
// root -> ImageResources -> PrintFlagsInformation
addElement("PrintFlagsInformation", "ImageResources", CHILD_POLICY_EMPTY);
- addAttribute("PrintFlagsInformation", "version", DATATYPE_INTEGER, true, null);
+ addAttribute("PrintFlagsInformation", "version", DATATYPE_INTEGER, false, "1");
addBooleanAttribute("PrintFlagsInformation", "cropMarks", false, false);
- addAttribute("PrintFlagsInformation", "field", DATATYPE_INTEGER, true, null);
+ addAttribute("PrintFlagsInformation", "field", DATATYPE_INTEGER, true, "0");
addAttribute("PrintFlagsInformation", "bleedWidth", DATATYPE_INTEGER, true, null, "0", String.valueOf(Long.MAX_VALUE), true, true); // TODO: LONG??!
addAttribute("PrintFlagsInformation", "bleedScale", DATATYPE_INTEGER, true, null, "0", String.valueOf(Integer.MAX_VALUE), true, true);
+ // root -> ImageResources -> PrintScale
+ addElement("PrintScale", "ImageResources", CHILD_POLICY_EMPTY);
+ addAttribute("PrintScale", "style", DATATYPE_STRING, false, null, Arrays.asList(PSDMetadata.PRINT_SCALE_STYLES));
+ addAttribute("PrintScale", "xLocation", DATATYPE_FLOAT, true, null);
+ addAttribute("PrintScale", "yLocation", DATATYPE_FLOAT, true, null);
+ addAttribute("PrintScale", "scale", DATATYPE_FLOAT, true, null);
+
// root -> ImageResources -> ResolutionInfo
addElement("ResolutionInfo", "ImageResources", CHILD_POLICY_EMPTY);
addAttribute("ResolutionInfo", "hRes", DATATYPE_FLOAT, true, null);
- // TODO: Or use string and more friendly names? "pixels/inch"/"pixels/cm" and "inch"/"cm"/"pt"/"pica"/"column"
- addAttribute("ResolutionInfo", "hResUnit", DATATYPE_INTEGER, true, null, Arrays.asList("1", "2"));
- addAttribute("ResolutionInfo", "widthUnit", DATATYPE_INTEGER, true, null, Arrays.asList("1", "2", "3", "4", "5"));
+ addAttribute("ResolutionInfo", "hResUnit", DATATYPE_STRING, true, null, Arrays.asList(PSDMetadata.RESOLUTION_UNITS));
+ addAttribute("ResolutionInfo", "widthUnit", DATATYPE_STRING, true, null, Arrays.asList(PSDMetadata.DIMENSION_UNITS));
addAttribute("ResolutionInfo", "vRes", DATATYPE_FLOAT, true, null);
- // TODO: Or use more friendly names?
- addAttribute("ResolutionInfo", "vResUnit", DATATYPE_INTEGER, true, null, Arrays.asList("1", "2"));
- addAttribute("ResolutionInfo", "heightUnit", DATATYPE_INTEGER, true, null, Arrays.asList("1", "2", "3", "4", "5"));
+ addAttribute("ResolutionInfo", "vResUnit", DATATYPE_STRING, true, null, Arrays.asList(PSDMetadata.RESOLUTION_UNITS));
+ addAttribute("ResolutionInfo", "heightUnit", DATATYPE_STRING, true, null, Arrays.asList(PSDMetadata.DIMENSION_UNITS));
- // ??? addElement("Thumbnail", "ImageResources", CHILD_POLICY_CHOICE);
+ // root -> ImageResources -> UnicodeAlphaNames
+ addElement("UnicodeAlphaNames", "ImageResources", 0, Integer.MAX_VALUE);
+ addChildElement("UnicodeAlphaNames", "Name"); // TODO: Does this really work?
- // root -> ImageResources -> XMPData
- addElement("XMPData", "ImageResources", CHILD_POLICY_CHOICE);
+
+ // root -> ImageResources -> VersionInfo
+ addElement("VersionInfo", "ImageResources", CHILD_POLICY_EMPTY);
+ addAttribute("VersionInfo", "version", DATATYPE_INTEGER, false, "1");
+ addBooleanAttribute("VersionInfo", "hasRealMergedData", false, false);
+ addAttribute("VersionInfo", "writer", DATATYPE_STRING, true, null);
+ addAttribute("VersionInfo", "reader", DATATYPE_STRING, true, null);
+ addAttribute("VersionInfo", "fileVersion", DATATYPE_INTEGER, true, "1");
+
+ // root -> ImageResources -> Thumbnail
+ addElement("Thumbnail", "ImageResources", CHILD_POLICY_EMPTY);
+ addObjectValue("Thumbnail", BufferedImage.class, true, null);
+
+ // root -> ImageResources -> UnicodeAlphaName
+ addElement("UnicodeAlphaName", "ImageResources", CHILD_POLICY_EMPTY);
+ addAttribute("UnicodeAlphaName", "value", DATATYPE_STRING, true, null);
+
+ // root -> ImageResources -> XMP
+ addElement("XMP", "ImageResources", CHILD_POLICY_CHOICE);
// TODO: Incorporate XMP metadata here somehow (or treat as opaque bytes?)
- addObjectValue("XMPData", Document.class, true, null);
+ addObjectValue("XMP", 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/PSDPixelAspectRatio.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPixelAspectRatio.java
new file mode 100644
index 00000000..612e309a
--- /dev/null
+++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPixelAspectRatio.java
@@ -0,0 +1,27 @@
+package com.twelvemonkeys.imageio.plugins.psd;
+
+import javax.imageio.stream.ImageInputStream;
+import java.io.IOException;
+
+/**
+ * PSDPixelAspectRatio
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: PSDPixelAspectRatio.java,v 1.0 Nov 7, 2009 8:23:09 PM haraldk Exp$
+ */
+final class PSDPixelAspectRatio extends PSDImageResource {
+ // 4 bytes (version = 1), 8 bytes double, x / y of a pixel
+ int mVersion;
+ double mAspect;
+
+ PSDPixelAspectRatio(final short pId, final ImageInputStream pInput) throws IOException {
+ super(pId, pInput);
+ }
+
+ @Override
+ protected void readData(final ImageInputStream pInput) throws IOException {
+ mVersion = pInput.readInt();
+ mAspect = pInput.readDouble();
+ }
+}
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 bb802354..606fd156 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
@@ -11,14 +11,14 @@ import java.io.IOException;
* @version $Id: PSDPrintFlagsInfo.java,v 1.0 Jul 28, 2009 5:16:27 PM haraldk Exp$
*/
final class PSDPrintFlags extends PSDImageResource {
- private boolean mLabels;
- private boolean mCropMasks;
- private boolean mColorBars;
- private boolean mRegistrationMarks;
- private boolean mNegative;
- private boolean mFlip;
- private boolean mInterpolate;
- private boolean mCaption;
+ boolean mLabels;
+ boolean mCropMasks;
+ boolean mColorBars;
+ boolean mRegistrationMarks;
+ boolean mNegative;
+ boolean mFlip;
+ boolean mInterpolate;
+ boolean mCaption;
PSDPrintFlags(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/PSDPrintFlagsInformation.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintFlagsInformation.java
index c3aa31d3..d5de5eb2 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
@@ -11,11 +11,11 @@ import java.io.IOException;
* @version $Id: PSDPrintFlagsInfo.java,v 1.0 Jul 28, 2009 5:16:27 PM haraldk Exp$
*/
final class PSDPrintFlagsInformation extends PSDImageResource {
- private int mVersion;
- private boolean mCropMasks;
- private int mField;
- private long mBleedWidth;
- private int mBleedScale;
+ int mVersion;
+ boolean mCropMasks;
+ int mField;
+ long mBleedWidth;
+ int mBleedScale;
PSDPrintFlagsInformation(final short pId, final ImageInputStream pInput) throws IOException {
super(pId, pInput);
@@ -25,7 +25,7 @@ final class PSDPrintFlagsInformation extends PSDImageResource {
protected void readData(final ImageInputStream pInput) throws IOException {
mVersion = pInput.readUnsignedShort();
mCropMasks = pInput.readBoolean();
- mField = pInput.readUnsignedByte();
+ mField = pInput.readUnsignedByte(); // TODO: Is this really pad?
mBleedWidth = pInput.readUnsignedInt();
mBleedScale = pInput.readUnsignedShort();
diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintScale.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintScale.java
new file mode 100644
index 00000000..35d14b1f
--- /dev/null
+++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintScale.java
@@ -0,0 +1,35 @@
+package com.twelvemonkeys.imageio.plugins.psd;
+
+import javax.imageio.stream.ImageInputStream;
+import java.io.IOException;
+
+/**
+ * PSDPrintScale
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: PSDPrintScale.java,v 1.0 Nov 7, 2009 9:41:17 PM haraldk Exp$
+ */
+final class PSDPrintScale extends PSDImageResource {
+ // 2 bytes style (0 = centered, 1 = size to fit, 2 = user defined).
+ // 4 bytes x location (floating point).
+ // 4 bytes y location (floating point).
+ // 4 bytes scale (floating point)
+
+ short mStyle;
+ float mXLocation;
+ float mYlocation;
+ float mScale;
+
+ PSDPrintScale(final short pId, final ImageInputStream pInput) throws IOException {
+ super(pId, pInput);
+ }
+
+ @Override
+ protected void readData(final ImageInputStream pInput) throws IOException {
+ mStyle = pInput.readShort();
+ mXLocation = pInput.readFloat();
+ mYlocation = pInput.readFloat();
+ mScale = pInput.readFloat();
+ }
+}
diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUnicodeAlphaNames.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUnicodeAlphaNames.java
new file mode 100644
index 00000000..12c927e9
--- /dev/null
+++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUnicodeAlphaNames.java
@@ -0,0 +1,33 @@
+package com.twelvemonkeys.imageio.plugins.psd;
+
+import javax.imageio.stream.ImageInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * PSDUnicodeAlphaNames
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: PSDUnicodeAlphaNames.java,v 1.0 Nov 7, 2009 9:16:56 PM haraldk Exp$
+ */
+final class PSDUnicodeAlphaNames extends PSDImageResource {
+ List mNames;
+
+ PSDUnicodeAlphaNames(final short pId, final ImageInputStream pInput) throws IOException {
+ super(pId, pInput);
+ }
+
+ @Override
+ protected void readData(final ImageInputStream pInput) throws IOException {
+ mNames = new ArrayList();
+
+ long left = mSize;
+ while (left > 0) {
+ String name = PSDUtil.readUTF16String(pInput);
+ mNames.add(name);
+ left -= name.length() * 2 + 4;
+ }
+ }
+}
diff --git a/twelvemonkeys-imageio/psd/todo.txt b/twelvemonkeys-imageio/psd/todo.txt
index efe9ceee..c7d6d53c 100755
--- a/twelvemonkeys-imageio/psd/todo.txt
+++ b/twelvemonkeys-imageio/psd/todo.txt
@@ -2,4 +2,5 @@ Implement source subsampling and region of interest
Separate package for the resources (seems to be a lot)?
Possibility to read only some resources? readResources(int[] resourceKeys)?
- Probably faster when we only need the color space
-PSDImageWriter
\ No newline at end of file
+Support for Photoshop specific TIFF tags (extension for TIFFImageReader)?
+PSDImageWriter?
\ No newline at end of file