#214 PSDImageReader: Read long layer names

This commit is contained in:
Harald Kuhr 2016-02-03 15:35:33 +01:00
parent 1449155987
commit eeeb22666c
5 changed files with 110 additions and 8 deletions

View File

@ -51,6 +51,7 @@ interface PSD {
/** PSD Resource type identifier "8BIM" */ /** PSD Resource type identifier "8BIM" */
int RESOURCE_TYPE = ('8' << 24) + ('B' << 16) + ('I' << 8) + 'M'; int RESOURCE_TYPE = ('8' << 24) + ('B' << 16) + ('I' << 8) + 'M';
int RESOURCE_TYPE_LONG = ('8' << 24) + ('B' << 16) + ('6' << 8) + '4';;
// Blending modes // Blending modes
/** Pass through blending mode "pass"*/ /** Pass through blending mode "pass"*/
@ -689,4 +690,22 @@ interface PSD {
/** Plug-In resource(s). Resources added by a plug-in. See the plug-in API found in the SDK documentation */ /** Plug-In resource(s). Resources added by a plug-in. See the plug-in API found in the SDK documentation */
int RES_PLUGIN_MAX = 0x1387; int RES_PLUGIN_MAX = 0x1387;
// TODO: Better naming of these.. It's a kind of resource blocks as well..
// "Additional Layer Information"
int LMsk = 'L' << 24 | 'M' << 16 | 's' << 8 | 'k';
int Lr16 = 'L' << 24 | 'r' << 16 | '1' << 8 | '6';
int Lr32 = 'L' << 24 | 'r' << 16 | '3' << 8 | '2';
int Layr = 'L' << 24 | 'a' << 16 | 'y' << 8 | 'r';
int Mt16 = 'M' << 24 | 't' << 16 | '1' << 8 | '6';
int Mt32 = 'M' << 24 | 't' << 16 | '3' << 8 | '2';
int Mtrn = 'M' << 24 | 't' << 16 | 'r' << 8 | 'n';
int Alph = 'A' << 24 | 'l' << 16 | 'p' << 8 | 'h';
int FMsk = 'F' << 24 | 'M' << 16 | 's' << 8 | 'k';
int lnk2 = 'l' << 24 | 'n' << 16 | 'k' << 8 | '2';
int FEid = 'F' << 24 | 'E' << 16 | 'i' << 8 | 'd';
int FXid = 'F' << 24 | 'X' << 16 | 'i' << 8 | 'd';
int PxSD = 'P' << 24 | 'x' << 16 | 'S' << 8 | 'D';
int luni = 'l' << 24 | 'u' << 16 | 'n' << 8 | 'i';
int lyid = 'l' << 24 | 'y' << 16 | 'i' << 8 | 'd';
} }

View File

@ -50,7 +50,10 @@ final class PSDLayerInfo {
final PSDLayerBlendMode blendMode; final PSDLayerBlendMode blendMode;
final PSDLayerMaskData layerMaskData; final PSDLayerMaskData layerMaskData;
final PSDChannelSourceDestinationRange[] ranges; final PSDChannelSourceDestinationRange[] ranges;
final String layerName; private final String layerName;
private String unicodeLayerName;
private int layerId;
PSDLayerInfo(final boolean largeFormat, final ImageInputStream pInput) throws IOException { PSDLayerInfo(final boolean largeFormat, final ImageInputStream pInput) throws IOException {
top = pInput.readInt(); top = pInput.readInt();
@ -70,7 +73,7 @@ final class PSDLayerInfo {
blendMode = new PSDLayerBlendMode(pInput); blendMode = new PSDLayerBlendMode(pInput);
// Length of layer mask data // Length of layer extra data
long extraDataSize = pInput.readUnsignedInt(); long extraDataSize = pInput.readUnsignedInt();
// Layer mask/adjustment layer data // Layer mask/adjustment layer data
@ -92,19 +95,78 @@ final class PSDLayerInfo {
ranges[i] = new PSDChannelSourceDestinationRange(pInput, (i == 0 ? "Gray" : "Channel " + (i - 1))); ranges[i] = new PSDChannelSourceDestinationRange(pInput, (i == 0 ? "Gray" : "Channel " + (i - 1)));
} }
// Layer name
layerName = PSDUtil.readPascalString(pInput); layerName = PSDUtil.readPascalString(pInput);
int layerNameSize = layerName.length() + 1; int layerNameSize = layerName.length() + 1;
// Skip pad bytes for long word alignment // Skip pad bytes for long word alignment
if (layerNameSize % 4 != 0) { if (layerNameSize % 4 != 0) {
int skip = layerNameSize % 4; int skip = 4 - (layerNameSize % 4);
pInput.skipBytes(skip); pInput.skipBytes(skip);
layerNameSize += skip; layerNameSize += skip;
} }
// TODO: Consider reading this: Adjustment layer info etc... // Parse "Additional layer data"
pInput.skipBytes(extraDataSize - layerMaskDataSize - 4 - layerBlendingDataSize - 4 - layerNameSize); long additionalLayerInfoStart = pInput.getStreamPosition();
long expectedEnd = additionalLayerInfoStart + extraDataSize - layerMaskDataSize - 4 - layerBlendingDataSize - 4 - layerNameSize;
while (pInput.getStreamPosition() < expectedEnd) {
// 8BIM or 8B64
int resourceSignature = pInput.readInt();
if (resourceSignature != PSD.RESOURCE_TYPE && resourceSignature != PSD.RESOURCE_TYPE_LONG) {
// Could be a corrupt document, or some new resource (type) we don't know about,
// we'll just leave it and carry on, as this is all secondary information for the reader.
break;
}
int resourceKey = pInput.readInt();
// NOTE: Only SOME resources have long length fields...
boolean largeResource = resourceSignature != PSD.RESOURCE_TYPE;
long resourceLength = largeResource ? pInput.readLong() : pInput.readUnsignedInt();
long resourceStart = pInput.getStreamPosition();
// System.out.printf("signature: %s 0x%08x\n", PSDUtil.intToStr(resourceSignature), resourceSignature);
// System.out.println("key: " + PSDUtil.intToStr(resourceKey));
// System.out.println("length: " + resourceLength);
switch (resourceKey) {
case PSD.luni:
unicodeLayerName = PSDUtil.readUnicodeString(pInput);
// There's usually a 0-pad here, but it is skipped in the general re-aligning code below
break;
case PSD.lyid:
if (resourceLength != 4) {
throw new IIOException(String.format("Expected layerId length == 4: %d", resourceLength));
}
layerId = pInput.readInt();
break;
default:
// TODO: Parse more data...
pInput.skipBytes(resourceLength);
break;
}
// Re-align in case we got the length incorrect
if (pInput.getStreamPosition() != resourceStart + resourceLength) {
pInput.seek(resourceStart + resourceLength);
}
}
// Re-align in case we got the length incorrect
if (pInput.getStreamPosition() != expectedEnd) {
pInput.seek(expectedEnd);
}
}
String getLayerName() {
return unicodeLayerName != null ? unicodeLayerName : layerName;
}
int getLayerId() {
return layerId;
} }
@Override @Override
@ -122,7 +184,7 @@ final class PSDLayerInfo {
builder.append(", layer mask data: ").append(layerMaskData); builder.append(", layer mask data: ").append(layerMaskData);
} }
builder.append(", ranges: ").append(Arrays.toString(ranges)); builder.append(", ranges: ").append(Arrays.toString(ranges));
builder.append(", layer name: \"").append(layerName).append("\""); builder.append(", layer name: \"").append(getLayerName()).append("\"");
builder.append("]"); builder.append("]");
return builder.toString(); return builder.toString();

View File

@ -375,7 +375,7 @@ public final class PSDMetadata extends AbstractMetadata {
for (PSDLayerInfo psdLayerInfo : layerInfo) { for (PSDLayerInfo psdLayerInfo : layerInfo) {
// TODO: Group in layer and use sub node for blend mode? // TODO: Group in layer and use sub node for blend mode?
node = new IIOMetadataNode("LayerInfo"); node = new IIOMetadataNode("LayerInfo");
node.setAttribute("name", psdLayerInfo.layerName); node.setAttribute("name", psdLayerInfo.getLayerName());
node.setAttribute("top", String.valueOf(psdLayerInfo.top)); node.setAttribute("top", String.valueOf(psdLayerInfo.top));
node.setAttribute("left", String.valueOf(psdLayerInfo.left)); node.setAttribute("left", String.valueOf(psdLayerInfo.left));
node.setAttribute("bottom", String.valueOf(psdLayerInfo.bottom)); node.setAttribute("bottom", String.valueOf(psdLayerInfo.bottom));

View File

@ -31,11 +31,16 @@ package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest; import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import com.twelvemonkeys.imageio.util.ProgressListenerBase; import com.twelvemonkeys.imageio.util.ProgressListenerBase;
import org.junit.Test; import org.junit.Test;
import org.w3c.dom.NodeList;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam; import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader; import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier; import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.spi.ImageReaderSpi; import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
@ -385,4 +390,20 @@ public class PSDImageReaderTest extends ImageReaderAbstractTest<PSDImageReader>
} }
} }
} }
@Test
public void testReadUnicodeLayerName() throws IOException {
PSDImageReader imageReader = createReader();
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/psd/long-layer-names.psd"))) {
imageReader.setInput(stream);
IIOMetadata metadata = imageReader.getImageMetadata(0);
IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(PSDMetadata.NATIVE_METADATA_FORMAT_NAME);
NodeList layerInfo = root.getElementsByTagName("LayerInfo");
assertEquals(1, layerInfo.getLength()); // Sanity
assertEquals("If_The_Layer_Name_Is_Really_Long_Oh_No_What_Do_I_Do", ((IIOMetadataNode) layerInfo.item(0)).getAttribute("name"));
}
}
} }