mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-05 12:35:29 -04:00
#852 JPEG: Preserve existing metadata when writing CMYK data
This commit is contained in:
parent
24c473ae45
commit
031937fe99
@ -35,17 +35,18 @@ import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
|
|||||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||||
|
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
import javax.imageio.IIOImage;
|
import javax.imageio.IIOImage;
|
||||||
import javax.imageio.ImageTypeSpecifier;
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
import javax.imageio.ImageWriteParam;
|
import javax.imageio.ImageWriteParam;
|
||||||
import javax.imageio.ImageWriter;
|
import javax.imageio.ImageWriter;
|
||||||
import javax.imageio.event.IIOWriteWarningListener;
|
import javax.imageio.event.IIOWriteWarningListener;
|
||||||
|
import javax.imageio.metadata.IIOInvalidTreeException;
|
||||||
import javax.imageio.metadata.IIOMetadata;
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
import javax.imageio.metadata.IIOMetadataNode;
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.color.ICC_ColorSpace;
|
|
||||||
import java.awt.color.ICC_Profile;
|
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@ -185,23 +186,67 @@ public final class JPEGImageWriter extends ImageWriterBase {
|
|||||||
RenderedImage renderedImage = image.getRenderedImage();
|
RenderedImage renderedImage = image.getRenderedImage();
|
||||||
boolean overrideDestination = param != null && param.getDestinationType() != null;
|
boolean overrideDestination = param != null && param.getDestinationType() != null;
|
||||||
ImageTypeSpecifier destinationType = overrideDestination
|
ImageTypeSpecifier destinationType = overrideDestination
|
||||||
? param.getDestinationType()
|
? param.getDestinationType()
|
||||||
: ImageTypeSpecifiers.createFromRenderedImage(renderedImage);
|
: ImageTypeSpecifiers.createFromRenderedImage(renderedImage);
|
||||||
|
|
||||||
ColorSpace cmykCS = destinationType.getColorModel().getColorSpace();
|
IIOMetadata metadata = convertCMYKMetadata(image.getMetadata(), destinationType, param);
|
||||||
|
|
||||||
IIOMetadata metadata = delegate.getDefaultImageMetadata(destinationType, param);
|
Raster raster = new InvertedRaster(getRaster(renderedImage));
|
||||||
|
|
||||||
|
// TODO: For YCCK we need oposite conversion
|
||||||
|
// for (int i = 0; i < data.length; i += 4) {
|
||||||
|
// YCbCrConverter.convertYCbCr2RGB(data, data, i);
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (overrideDestination) {
|
||||||
|
// Avoid javax.imageio.IIOException: Invalid argument to native writeImage
|
||||||
|
param.setDestinationType(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
delegate.write(streamMetadata, new IIOImage(raster, null, metadata), param);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (overrideDestination) {
|
||||||
|
param.setDestinationType(destinationType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IIOMetadata convertCMYKMetadata(IIOMetadata original, ImageTypeSpecifier destinationType, ImageWriteParam param) throws IIOInvalidTreeException {
|
||||||
IIOMetadataNode jpegMeta = new IIOMetadataNode(JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
|
IIOMetadataNode jpegMeta = new IIOMetadataNode(JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
|
||||||
jpegMeta.appendChild(new IIOMetadataNode("JPEGVariety")); // Just leave as default
|
jpegMeta.appendChild(new IIOMetadataNode("JPEGVariety")); // Just leave as default, we can't write JFIF
|
||||||
|
|
||||||
IIOMetadataNode markerSequence = new IIOMetadataNode("markerSequence");
|
IIOMetadataNode markerSequence = new IIOMetadataNode("markerSequence");
|
||||||
jpegMeta.appendChild(markerSequence);
|
jpegMeta.appendChild(markerSequence);
|
||||||
|
|
||||||
|
IIOMetadataNode originalTree = original != null
|
||||||
|
? (IIOMetadataNode) original.getAsTree(JAVAX_IMAGEIO_JPEG_IMAGE_1_0)
|
||||||
|
: new IIOMetadataNode("emptyNode");
|
||||||
|
|
||||||
|
// Append original unknown nodes, if present, but filter out any ICC Profiles
|
||||||
|
NodeList unknowns = originalTree.getElementsByTagName("unknown");
|
||||||
|
for (int i = 0; i < unknowns.getLength(); i++) {
|
||||||
|
IIOMetadataNode unknown = (IIOMetadataNode) unknowns.item(i);
|
||||||
|
|
||||||
|
// TODO: If the cmykCS is not an ICC profile, maybe it makes sense to NOT filter here? that's a corner case...
|
||||||
|
if ("226".equals(unknown.getAttribute("MarkerTag"))) {
|
||||||
|
Object userObject = unknown.getUserObject();
|
||||||
|
|
||||||
|
if (userObject instanceof byte[] && ((byte[]) userObject).length >= "ICC_PROFILE".length()
|
||||||
|
&& "ICC_PROFILE".equals(new String((byte[]) userObject, 0, "ICC_PROFILE".length()))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
markerSequence.appendChild(unknown);
|
||||||
|
}
|
||||||
|
|
||||||
IIOMetadataNode app14Adobe = new IIOMetadataNode("app14Adobe");
|
IIOMetadataNode app14Adobe = new IIOMetadataNode("app14Adobe");
|
||||||
app14Adobe.setAttribute("transform", "0"); // 0 for CMYK, 2 for YCCK
|
app14Adobe.setAttribute("transform", "0"); // 0 for CMYK, 2 for YCCK
|
||||||
markerSequence.appendChild(app14Adobe);
|
markerSequence.appendChild(app14Adobe);
|
||||||
|
|
||||||
|
ColorSpace cmykCS = destinationType.getColorModel().getColorSpace();
|
||||||
if (cmykCS instanceof ICC_ColorSpace) {
|
if (cmykCS instanceof ICC_ColorSpace) {
|
||||||
ICC_Profile profile = ((ICC_ColorSpace) cmykCS).getProfile();
|
ICC_Profile profile = ((ICC_ColorSpace) cmykCS).getProfile();
|
||||||
byte[] profileData = profile.getData();
|
byte[] profileData = profile.getData();
|
||||||
@ -234,28 +279,16 @@ public final class JPEGImageWriter extends ImageWriterBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Append original comment nodes, if present
|
||||||
|
NodeList comments = originalTree.getElementsByTagName("COM");
|
||||||
|
for (int i = 0; i < comments.getLength(); i++) {
|
||||||
|
markerSequence.appendChild(comments.item(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
IIOMetadata metadata = delegate.getDefaultImageMetadata(destinationType, param);
|
||||||
metadata.mergeTree(JAVAX_IMAGEIO_JPEG_IMAGE_1_0, jpegMeta);
|
metadata.mergeTree(JAVAX_IMAGEIO_JPEG_IMAGE_1_0, jpegMeta);
|
||||||
|
|
||||||
Raster raster = new InvertedRaster(getRaster(renderedImage));
|
return metadata;
|
||||||
|
|
||||||
// TODO: For YCCK we need oposite conversion
|
|
||||||
// for (int i = 0; i < data.length; i += 4) {
|
|
||||||
// YCbCrConverter.convertYCbCr2RGB(data, data, i);
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (overrideDestination) {
|
|
||||||
// Avoid javax.imageio.IIOException: Invalid argument to native writeImage
|
|
||||||
param.setDestinationType(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
delegate.write(streamMetadata, new IIOImage(raster, null, metadata), param);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if (overrideDestination) {
|
|
||||||
param.setDestinationType(destinationType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Candidate util method
|
// TODO: Candidate util method
|
||||||
|
@ -38,7 +38,13 @@ import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
import javax.imageio.*;
|
import javax.imageio.IIOImage;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.ImageReadParam;
|
||||||
|
import javax.imageio.ImageReader;
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
|
import javax.imageio.ImageWriteParam;
|
||||||
|
import javax.imageio.ImageWriter;
|
||||||
import javax.imageio.metadata.IIOMetadata;
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||||
import javax.imageio.metadata.IIOMetadataNode;
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
@ -48,8 +54,7 @@ import javax.imageio.stream.ImageInputStream;
|
|||||||
import javax.imageio.stream.ImageOutputStream;
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
import javax.imageio.stream.MemoryCacheImageOutputStream;
|
import javax.imageio.stream.MemoryCacheImageOutputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.color.ICC_Profile;
|
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -211,7 +216,7 @@ public class JPEGImageWriterTest extends ImageWriterAbstractTest<JPEGImageWriter
|
|||||||
// Test APP2/ICC_PROFILE segments form native metadata
|
// Test APP2/ICC_PROFILE segments form native metadata
|
||||||
IIOMetadataNode nativeMeta = (IIOMetadataNode) metadata.getAsTree(JPEGImage10Metadata.JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
|
IIOMetadataNode nativeMeta = (IIOMetadataNode) metadata.getAsTree(JPEGImage10Metadata.JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
|
||||||
NodeList unknown = nativeMeta.getElementsByTagName("unknown");
|
NodeList unknown = nativeMeta.getElementsByTagName("unknown");
|
||||||
assertEquals(11, unknown.getLength()); // We write longer segments than the original, so we get less segments
|
assertEquals(14, unknown.getLength()); // We write longer segments than the original, so we get less segments
|
||||||
|
|
||||||
ByteArrayOutputStream iccSegments = new ByteArrayOutputStream(1024 * 1024);
|
ByteArrayOutputStream iccSegments = new ByteArrayOutputStream(1024 * 1024);
|
||||||
|
|
||||||
@ -259,7 +264,7 @@ public class JPEGImageWriterTest extends ImageWriterAbstractTest<JPEGImageWriter
|
|||||||
// Test APP2/ICC_PROFILE segments form native metadata
|
// Test APP2/ICC_PROFILE segments form native metadata
|
||||||
IIOMetadataNode nativeMeta = (IIOMetadataNode) metadata.getAsTree(JPEGImage10Metadata.JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
|
IIOMetadataNode nativeMeta = (IIOMetadataNode) metadata.getAsTree(JPEGImage10Metadata.JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
|
||||||
NodeList unknown = nativeMeta.getElementsByTagName("unknown");
|
NodeList unknown = nativeMeta.getElementsByTagName("unknown");
|
||||||
assertEquals(0, unknown.getLength());
|
assertEquals(3, unknown.getLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: YCCK
|
// TODO: YCCK
|
||||||
|
Loading…
x
Reference in New Issue
Block a user