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