mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-04 03:55:28 -04:00
Fix for IIOInvalidTreeException: Invalid DHT node #559
(cherry picked from commit 9a6b8c9bfe8c7ae677d89ff5423e5739fd2bdb4d)
This commit is contained in:
parent
be60f307f7
commit
975e23c28f
@ -35,6 +35,7 @@ import com.twelvemonkeys.imageio.metadata.Directory;
|
|||||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||||
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
|
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
|
||||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||||
|
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
import javax.imageio.IIOException;
|
import javax.imageio.IIOException;
|
||||||
@ -158,21 +159,25 @@ class JPEGImage10Metadata extends AbstractMetadata {
|
|||||||
|
|
||||||
case JPEG.DHT:
|
case JPEG.DHT:
|
||||||
HuffmanTable huffmanTable = (HuffmanTable) segment;
|
HuffmanTable huffmanTable = (HuffmanTable) segment;
|
||||||
IIOMetadataNode dht = new IIOMetadataNode("dht");
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
IIOMetadataNode dcTables = new IIOMetadataNode("dht");
|
||||||
for (int c = 0; c < 2; c++) {
|
IIOMetadataNode acTables = new IIOMetadataNode("dht");
|
||||||
if (huffmanTable.isPresent(i, c)) {
|
|
||||||
IIOMetadataNode dhtable = new IIOMetadataNode("dhtable");
|
appendHuffmanTables(huffmanTable, 0, dcTables);
|
||||||
dhtable.setAttribute("class", String.valueOf(c));
|
appendHuffmanTables(huffmanTable, 1, acTables);
|
||||||
dhtable.setAttribute("htableId", String.valueOf(i));
|
|
||||||
dhtable.setUserObject(huffmanTable.toNativeTable(i, c));
|
markerSequence.appendChild(dcTables);
|
||||||
dht.appendChild(dhtable);
|
|
||||||
}
|
// Native metadata has a limit of max 4 children of the DHT, we split by class only if we must...
|
||||||
|
if (dcTables.getLength() + acTables.getLength() > 4) {
|
||||||
|
markerSequence.appendChild(acTables);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
while (acTables.hasChildNodes()) {
|
||||||
|
dcTables.appendChild(acTables.removeChild(acTables.getFirstChild()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
markerSequence.appendChild(dht);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JPEG.DQT:
|
case JPEG.DQT:
|
||||||
@ -270,6 +275,18 @@ class JPEGImage10Metadata extends AbstractMetadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void appendHuffmanTables(HuffmanTable huffmanTable, int tableClass, IIOMetadataNode dht) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (huffmanTable.isPresent(i, tableClass)) {
|
||||||
|
IIOMetadataNode dhtable = new IIOMetadataNode("dhtable");
|
||||||
|
dhtable.setAttribute("class", String.valueOf(tableClass));
|
||||||
|
dhtable.setAttribute("htableId", String.valueOf(i));
|
||||||
|
dhtable.setUserObject(huffmanTable.toNativeTable(i, tableClass));
|
||||||
|
dht.appendChild(dhtable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void appendICCProfile(IIOMetadataNode app0JFIF) {
|
private void appendICCProfile(IIOMetadataNode app0JFIF) {
|
||||||
if (embeddedICCProfile != null) {
|
if (embeddedICCProfile != null) {
|
||||||
IIOMetadataNode app2ICC = new IIOMetadataNode("app2ICC");
|
IIOMetadataNode app2ICC = new IIOMetadataNode("app2ICC");
|
||||||
|
@ -1,345 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013, Harald Kuhr
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
|
||||||
* list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
|
|
||||||
import com.twelvemonkeys.xml.XMLSerializer;
|
|
||||||
|
|
||||||
import org.w3c.dom.Element;
|
|
||||||
import org.w3c.dom.Node;
|
|
||||||
import org.w3c.dom.NodeList;
|
|
||||||
|
|
||||||
import javax.imageio.metadata.IIOInvalidTreeException;
|
|
||||||
import javax.imageio.metadata.IIOMetadata;
|
|
||||||
import javax.imageio.metadata.IIOMetadataNode;
|
|
||||||
import java.awt.color.ICC_Profile;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JPEGImage10MetadataCleaner
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
|
||||||
* @author last modified by $Author: haraldk$
|
|
||||||
* @version $Id: JPEGImage10MetadataCleaner.java,v 1.0 22.10.13 14:41 haraldk Exp$
|
|
||||||
*/
|
|
||||||
final class JPEGImage10MetadataCleaner {
|
|
||||||
|
|
||||||
private final JPEGImageReader reader;
|
|
||||||
|
|
||||||
JPEGImage10MetadataCleaner(final JPEGImageReader reader) {
|
|
||||||
this.reader = reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
IIOMetadata cleanMetadata(final IIOMetadata imageMetadata) throws IOException {
|
|
||||||
// We filter out pretty much everything from the stream..
|
|
||||||
// Meaning we have to read get *all APP segments* and re-insert into metadata.
|
|
||||||
List<Application> appSegments = reader.getAppSegments(JPEGImageReader.ALL_APP_MARKERS, null);
|
|
||||||
|
|
||||||
// NOTE: There's a bug in the merging code in JPEGMetadata mergeUnknownNode that makes sure all "unknown" nodes are added twice in certain conditions.... ARGHBL...
|
|
||||||
// DONE: 1: Work around
|
|
||||||
// TODO: 2: REPORT BUG!
|
|
||||||
// TODO: Report dht inconsistency bug (reads any amount of tables but only allows setting 4 tables)
|
|
||||||
|
|
||||||
// TODO: Allow EXIF (as app1EXIF) in the JPEGvariety (sic) node. Need new format, might as well create a completely new format...
|
|
||||||
// As EXIF is (a subset of) TIFF, (and the EXIF data is a valid TIFF stream) probably use something like:
|
|
||||||
// http://download.java.net/media/jai-imageio/javadoc/1.1/com/sun/media/imageio/plugins/tiff/package-summary.html#ImageMetadata
|
|
||||||
/*
|
|
||||||
from: http://docs.oracle.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html
|
|
||||||
|
|
||||||
In future versions of the JPEG metadata format, other varieties of JPEG metadata may be supported (e.g. Exif)
|
|
||||||
by defining other types of nodes which may appear as a child of the JPEGvariety node.
|
|
||||||
|
|
||||||
(Note that an application wishing to interpret Exif metadata given a metadata tree structure in the
|
|
||||||
javax_imageio_jpeg_image_1.0 format must check for an unknown marker segment with a tag indicating an
|
|
||||||
APP1 marker and containing data identifying it as an Exif marker segment. Then it may use application-specific
|
|
||||||
code to interpret the data in the marker segment. If such an application were to encounter a metadata tree
|
|
||||||
formatted according to a future version of the JPEG metadata format, the Exif marker segment might not be
|
|
||||||
unknown in that format - it might be structured as a child node of the JPEGvariety node.
|
|
||||||
|
|
||||||
Thus, it is important for an application to specify which version to use by passing the string identifying
|
|
||||||
the version to the method/constructor used to obtain an IIOMetadata object.)
|
|
||||||
*/
|
|
||||||
|
|
||||||
IIOMetadataNode tree = (IIOMetadataNode) imageMetadata.getAsTree(JPEGImage10Metadata.JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
|
|
||||||
IIOMetadataNode jpegVariety = (IIOMetadataNode) tree.getElementsByTagName("JPEGvariety").item(0);
|
|
||||||
IIOMetadataNode markerSequence = (IIOMetadataNode) tree.getElementsByTagName("markerSequence").item(0);
|
|
||||||
|
|
||||||
JFIF jfifSegment = reader.getJFIF();
|
|
||||||
JFXX jfxx = reader.getJFXX();
|
|
||||||
AdobeDCT adobeDCT = reader.getAdobeDCT();
|
|
||||||
ICC_Profile embeddedICCProfile = reader.getEmbeddedICCProfile(true);
|
|
||||||
Frame sof = reader.getSOF();
|
|
||||||
|
|
||||||
boolean hasRealJFIF = false;
|
|
||||||
boolean hasRealJFXX = false;
|
|
||||||
boolean hasRealICC = false;
|
|
||||||
|
|
||||||
if (jfifSegment != null) {
|
|
||||||
// Normal case, conformant JFIF with 1 or 3 components
|
|
||||||
// TODO: Test if we have CMY or other non-JFIF color space?
|
|
||||||
if (sof.componentsInFrame() == 1 || sof.componentsInFrame() == 3) {
|
|
||||||
IIOMetadataNode jfif = new IIOMetadataNode("app0JFIF");
|
|
||||||
jfif.setAttribute("majorVersion", String.valueOf(jfifSegment.majorVersion));
|
|
||||||
jfif.setAttribute("minorVersion", String.valueOf(jfifSegment.minorVersion));
|
|
||||||
jfif.setAttribute("resUnits", String.valueOf(jfifSegment.units));
|
|
||||||
jfif.setAttribute("Xdensity", String.valueOf(Math.max(1, jfifSegment.xDensity))); // Avoid 0 density
|
|
||||||
jfif.setAttribute("Ydensity", String.valueOf(Math.max(1,jfifSegment.yDensity)));
|
|
||||||
jfif.setAttribute("thumbWidth", String.valueOf(jfifSegment.xThumbnail));
|
|
||||||
jfif.setAttribute("thumbHeight", String.valueOf(jfifSegment.yThumbnail));
|
|
||||||
|
|
||||||
jpegVariety.appendChild(jfif);
|
|
||||||
hasRealJFIF = true;
|
|
||||||
|
|
||||||
// Add app2ICC and JFXX as proper nodes
|
|
||||||
if (embeddedICCProfile != null) {
|
|
||||||
IIOMetadataNode app2ICC = new IIOMetadataNode("app2ICC");
|
|
||||||
app2ICC.setUserObject(embeddedICCProfile);
|
|
||||||
jfif.appendChild(app2ICC);
|
|
||||||
hasRealICC = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jfxx != null) {
|
|
||||||
IIOMetadataNode JFXX = new IIOMetadataNode("JFXX");
|
|
||||||
jfif.appendChild(JFXX);
|
|
||||||
IIOMetadataNode app0JFXX = new IIOMetadataNode("app0JFXX");
|
|
||||||
app0JFXX.setAttribute("extensionCode", String.valueOf(jfxx.extensionCode));
|
|
||||||
|
|
||||||
ThumbnailReader thumbnailReader = JFXXThumbnail.from(jfxx, reader.getThumbnailReader());
|
|
||||||
IIOMetadataNode jfifThumb;
|
|
||||||
|
|
||||||
switch (jfxx.extensionCode) {
|
|
||||||
case com.twelvemonkeys.imageio.plugins.jpeg.JFXX.JPEG:
|
|
||||||
jfifThumb = new IIOMetadataNode("JFIFthumbJPEG");
|
|
||||||
// Contains it's own "markerSequence" with full DHT, DQT, SOF etc...
|
|
||||||
IIOMetadata thumbMeta = thumbnailReader.readMetadata();
|
|
||||||
Node thumbTree = thumbMeta.getAsTree(JPEGImage10Metadata.JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
|
|
||||||
jfifThumb.appendChild(thumbTree.getLastChild());
|
|
||||||
app0JFXX.appendChild(jfifThumb);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case com.twelvemonkeys.imageio.plugins.jpeg.JFXX.INDEXED:
|
|
||||||
jfifThumb = new IIOMetadataNode("JFIFthumbPalette");
|
|
||||||
jfifThumb.setAttribute("thumbWidth", String.valueOf(thumbnailReader.getWidth()));
|
|
||||||
jfifThumb.setAttribute("thumbHeight", String.valueOf(thumbnailReader.getHeight()));
|
|
||||||
app0JFXX.appendChild(jfifThumb);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case com.twelvemonkeys.imageio.plugins.jpeg.JFXX.RGB:
|
|
||||||
jfifThumb = new IIOMetadataNode("JFIFthumbRGB");
|
|
||||||
jfifThumb.setAttribute("thumbWidth", String.valueOf(thumbnailReader.getWidth()));
|
|
||||||
jfifThumb.setAttribute("thumbHeight", String.valueOf(thumbnailReader.getHeight()));
|
|
||||||
app0JFXX.appendChild(jfifThumb);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
reader.processWarningOccurred(String.format("Unknown JFXX extension code: %d", jfxx.extensionCode));
|
|
||||||
}
|
|
||||||
|
|
||||||
JFXX.appendChild(app0JFXX);
|
|
||||||
hasRealJFXX = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Typically CMYK JPEG with JFIF segment (Adobe or similar).
|
|
||||||
reader.processWarningOccurred(String.format(
|
|
||||||
"Incompatible JFIF marker segment in stream. " +
|
|
||||||
"SOF%d has %d color components, JFIF allows only 1 or 3 components. Ignoring JFIF marker.",
|
|
||||||
sof.marker & 0xf, sof.componentsInFrame()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case: Broken AdobeDCT segment, inconsistent with SOF, use values from SOF
|
|
||||||
if (adobeDCT != null && (adobeDCT.transform == AdobeDCT.YCCK && sof.componentsInFrame() < 4 ||
|
|
||||||
adobeDCT.transform == AdobeDCT.YCC && sof.componentsInFrame() < 3)) {
|
|
||||||
reader.processWarningOccurred(String.format(
|
|
||||||
"Invalid Adobe App14 marker. Indicates %s data, but SOF%d has %d color component(s). " +
|
|
||||||
"Ignoring Adobe App14 marker.",
|
|
||||||
adobeDCT.transform == AdobeDCT.YCCK ? "YCCK/CMYK" : "YCC/RGB",
|
|
||||||
sof.marker & 0xf, sof.componentsInFrame()
|
|
||||||
));
|
|
||||||
|
|
||||||
// Remove bad AdobeDCT
|
|
||||||
NodeList app14Adobe = tree.getElementsByTagName("app14Adobe");
|
|
||||||
for (int i = app14Adobe.getLength() - 1; i >= 0; i--) {
|
|
||||||
Node item = app14Adobe.item(i);
|
|
||||||
item.getParentNode().removeChild(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't add this as unknown marker, as we are certain it's bogus by now
|
|
||||||
}
|
|
||||||
|
|
||||||
Node next = null;
|
|
||||||
for (Application segment : appSegments) {
|
|
||||||
// Except real app0JFIF, app0JFXX, app2ICC and app14Adobe, add all the app segments that we filtered away as "unknown" markers
|
|
||||||
if (segment.marker == JPEG.APP0 && "JFIF".equals(segment.identifier) && hasRealJFIF) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (segment.marker == JPEG.APP0 && "JFXX".equals(segment.identifier) && hasRealJFXX) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (segment.marker == JPEG.APP1 && "Exif".equals(segment.identifier) /* always inserted */) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (segment.marker == JPEG.APP2 && "ICC_PROFILE".equals(segment.identifier) && hasRealICC) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (segment.marker == JPEG.APP14 && "Adobe".equals(segment.identifier) /* always inserted */) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
IIOMetadataNode unknown = new IIOMetadataNode("unknown");
|
|
||||||
unknown.setAttribute("MarkerTag", Integer.toString(segment.marker & 0xff));
|
|
||||||
|
|
||||||
unknown.setUserObject(segment.data);
|
|
||||||
|
|
||||||
// try (DataInputStream stream = new DataInputStream(new ByteArrayInputStream(segment.data))) {
|
|
||||||
// String identifier = segment.identifier;
|
|
||||||
// int off = identifier != null ? identifier.length() + 1 : 0;
|
|
||||||
//
|
|
||||||
// byte[] data = new byte[off + segment.data.length];
|
|
||||||
//
|
|
||||||
// if (identifier != null) {
|
|
||||||
// System.arraycopy(identifier.getBytes(Charset.forName("ASCII")), 0, data, 0, identifier.length());
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// stream.readFully(data, off, segment.data.length);
|
|
||||||
//
|
|
||||||
// unknown.setUserObject(data);
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (next == null) {
|
|
||||||
// To be semi-compatible with the functionality in mergeTree,
|
|
||||||
// let's insert after the last unknown tag, or before any other tag if no unknown tag exists
|
|
||||||
NodeList unknowns = markerSequence.getElementsByTagName("unknown");
|
|
||||||
|
|
||||||
if (unknowns.getLength() > 0) {
|
|
||||||
next = unknowns.item(unknowns.getLength() - 1).getNextSibling();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
next = markerSequence.getFirstChild();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
markerSequence.insertBefore(unknown, next);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Known issues in the com.sun classes, if sof/sos component id or selector is negative,
|
|
||||||
// setFromTree will fail. We'll fix the range from -128...127 to be 0...255.
|
|
||||||
NodeList sofs = markerSequence.getElementsByTagName("sof");
|
|
||||||
|
|
||||||
if (sofs.getLength() > 0) {
|
|
||||||
NodeList components = sofs.item(0).getChildNodes();
|
|
||||||
for (int i = 0; i < components.getLength(); i++) {
|
|
||||||
forceComponentIdInRange((IIOMetadataNode) components.item(i), "componentId");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeList sos = markerSequence.getElementsByTagName("sos");
|
|
||||||
|
|
||||||
for (int i = 0; i < sos.getLength(); i++) {
|
|
||||||
NodeList specs = sos.item(i).getChildNodes();
|
|
||||||
for (int j = 0; j < specs.getLength(); j++) {
|
|
||||||
forceComponentIdInRange((IIOMetadataNode) specs.item(j), "componentSelector");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inconsistency issue in the com.sun classes, it can read metadata with dht containing
|
|
||||||
// more than 4 children, but will not allow setting such a tree...
|
|
||||||
// We'll split AC/DC tables into separate dht nodes.
|
|
||||||
NodeList dhts = markerSequence.getElementsByTagName("dht");
|
|
||||||
for (int j = 0; j < dhts.getLength(); j++) {
|
|
||||||
Node dht = dhts.item(j);
|
|
||||||
NodeList dhtables = dht.getChildNodes();
|
|
||||||
|
|
||||||
if (dhtables.getLength() < 1) {
|
|
||||||
// Why is there an empty DHT node?
|
|
||||||
dht.getParentNode().removeChild(dht);
|
|
||||||
reader.processWarningOccurred("Metadata contains empty dht node. Ignoring.");
|
|
||||||
}
|
|
||||||
else if (dhtables.getLength() > 4) {
|
|
||||||
IIOMetadataNode acTables = new IIOMetadataNode("dht");
|
|
||||||
dht.getParentNode().insertBefore(acTables, dht.getNextSibling());
|
|
||||||
|
|
||||||
// Split into 2 dht nodes, one for AC and one for DC
|
|
||||||
for (int i = dhtables.getLength() - 1; i >= 0 ; i--) {
|
|
||||||
Element dhtable = (Element) dhtables.item(i);
|
|
||||||
String tableClass = dhtable.getAttribute("class");
|
|
||||||
if ("1".equals(tableClass)) {
|
|
||||||
dht.removeChild(dhtable);
|
|
||||||
acTables.insertBefore(dhtable, acTables.getFirstChild());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
imageMetadata.setFromTree(JPEGImage10Metadata.JAVAX_IMAGEIO_JPEG_IMAGE_1_0, tree);
|
|
||||||
}
|
|
||||||
catch (IIOInvalidTreeException e) {
|
|
||||||
if (JPEGImageReader.DEBUG) {
|
|
||||||
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(imageMetadata.getAsTree(JPEGImage10Metadata.JAVAX_IMAGEIO_JPEG_IMAGE_1_0), false);
|
|
||||||
System.out.println("-- 8< --");
|
|
||||||
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(tree, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
return imageMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void forceComponentIdInRange(final IIOMetadataNode component, final String attributeName) {
|
|
||||||
String attribute = component.getAttribute(attributeName);
|
|
||||||
|
|
||||||
if (attribute != null) {
|
|
||||||
try {
|
|
||||||
int componentId = Integer.parseInt(attribute);
|
|
||||||
|
|
||||||
if (componentId < 0) {
|
|
||||||
// Metadata doesn't like negative component ids/specs
|
|
||||||
// We'll convert to the positive value it probably should have been
|
|
||||||
componentId = ((byte) componentId) & 0xff;
|
|
||||||
component.setAttribute(attributeName, String.valueOf(componentId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (NumberFormatException ignore) {
|
|
||||||
if ("scanComponentSpec".equals(component.getNodeName())) {
|
|
||||||
reader.processWarningOccurred("Bad SOS component selector: " + attribute);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
reader.processWarningOccurred("Bad SOF component id: " + attribute);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -31,6 +31,7 @@
|
|||||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.stream.URLImageInputStreamSpi;
|
import com.twelvemonkeys.imageio.stream.URLImageInputStreamSpi;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
@ -42,6 +43,7 @@ import javax.imageio.metadata.IIOMetadataNode;
|
|||||||
import javax.imageio.spi.IIORegistry;
|
import javax.imageio.spi.IIORegistry;
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -50,14 +52,13 @@ import static org.junit.Assert.assertEquals;
|
|||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JPEGImage10MetadataCleanerTest.
|
* JPEGImage10MetadataTest.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: harald.kuhr$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: JPEGImage10MetadataCleanerTest.java,v 1.0 08/08/16 harald.kuhr Exp$
|
* @version $Id: JPEGImage10MetadataTest.java,v 1.0 12/01/2022 haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public class JPEGImage10MetadataCleanerTest {
|
public class JPEGImage10MetadataTest {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
IIORegistry.getDefaultInstance().registerServiceProvider(new URLImageInputStreamSpi());
|
IIORegistry.getDefaultInstance().registerServiceProvider(new URLImageInputStreamSpi());
|
||||||
ImageIO.setUseCache(false);
|
ImageIO.setUseCache(false);
|
||||||
@ -69,14 +70,14 @@ public class JPEGImage10MetadataCleanerTest {
|
|||||||
return lookupProviderByName(IIORegistry.getDefaultInstance(), "com.sun.imageio.plugins.jpeg.JPEGImageReaderSpi", ImageReaderSpi.class);
|
return lookupProviderByName(IIORegistry.getDefaultInstance(), "com.sun.imageio.plugins.jpeg.JPEGImageReaderSpi", ImageReaderSpi.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unit/regression test for #276
|
// Unit/regression test for #276, #559
|
||||||
@Test
|
@Test
|
||||||
public void cleanMetadataMoreThan4DHTSegments() throws Exception {
|
public void splitMoreThan4DHTSegments() throws Exception {
|
||||||
List<String> testData = Arrays.asList("/jpeg/5dhtsegments.jpg", "/jpeg/6dhtsegments.jpg");
|
List<String> testData = Arrays.asList("/jpeg/5dhtsegments.jpg", "/jpeg/6dhtsegments.jpg");
|
||||||
|
|
||||||
for (String data : testData) {
|
for (String data : testData) {
|
||||||
try (ImageInputStream origInput = ImageIO.createImageInputStream(getClass().getResource(data));
|
try (ImageInputStream origInput = ImageIO.createImageInputStream(getClassResource(data));
|
||||||
ImageInputStream input = ImageIO.createImageInputStream(getClass().getResource(data))) {
|
ImageInputStream input = ImageIO.createImageInputStream(getClassResource(data))) {
|
||||||
|
|
||||||
ImageReader origReader = SPI.delegateProvider.createReaderInstance();
|
ImageReader origReader = SPI.delegateProvider.createReaderInstance();
|
||||||
origReader.setInput(origInput);
|
origReader.setInput(origInput);
|
||||||
@ -87,9 +88,7 @@ public class JPEGImage10MetadataCleanerTest {
|
|||||||
IIOMetadata original = origReader.getImageMetadata(0);
|
IIOMetadata original = origReader.getImageMetadata(0);
|
||||||
IIOMetadataNode origTree = (IIOMetadataNode) original.getAsTree(JPEGImage10Metadata.JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
|
IIOMetadataNode origTree = (IIOMetadataNode) original.getAsTree(JPEGImage10Metadata.JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
|
||||||
|
|
||||||
JPEGImage10MetadataCleaner cleaner = new JPEGImage10MetadataCleaner((JPEGImageReader) reader);
|
JPEGImage10Metadata cleaned = (JPEGImage10Metadata) reader.getImageMetadata(0);
|
||||||
IIOMetadata cleaned = cleaner.cleanMetadata(origReader.getImageMetadata(0));
|
|
||||||
|
|
||||||
IIOMetadataNode cleanTree = (IIOMetadataNode) cleaned.getAsTree(JPEGImage10Metadata.JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
|
IIOMetadataNode cleanTree = (IIOMetadataNode) cleaned.getAsTree(JPEGImage10Metadata.JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
|
||||||
|
|
||||||
NodeList origDHT = origTree.getElementsByTagName("dht");
|
NodeList origDHT = origTree.getElementsByTagName("dht");
|
||||||
@ -124,4 +123,8 @@ public class JPEGImage10MetadataCleanerTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private URL getClassResource(String name) {
|
||||||
|
return getClass().getResource(name);
|
||||||
|
}
|
||||||
}
|
}
|
@ -30,34 +30,15 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import com.twelvemonkeys.imageio.color.ColorSpaces;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||||
import static org.mockito.Mockito.mock;
|
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||||
import static org.mockito.Mockito.when;
|
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
|
||||||
|
|
||||||
import java.awt.*;
|
import org.junit.Test;
|
||||||
import java.awt.color.ColorSpace;
|
import org.w3c.dom.NodeList;
|
||||||
import java.awt.color.ICC_Profile;
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.awt.image.ColorModel;
|
|
||||||
import java.awt.image.ComponentColorModel;
|
|
||||||
import java.awt.image.DataBuffer;
|
|
||||||
import java.awt.image.RenderedImage;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.imageio.IIOImage;
|
import javax.imageio.*;
|
||||||
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;
|
||||||
@ -66,14 +47,22 @@ import javax.imageio.spi.ImageWriterSpi;
|
|||||||
import javax.imageio.stream.ImageInputStream;
|
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.color.ColorSpace;
|
||||||
|
import java.awt.color.ICC_Profile;
|
||||||
|
import java.awt.image.*;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.Test;
|
import static org.junit.Assert.assertEquals;
|
||||||
import org.w3c.dom.NodeList;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import com.twelvemonkeys.imageio.color.ColorSpaces;
|
import static org.mockito.Mockito.when;
|
||||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
|
||||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
|
||||||
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JPEGImageWriterTest
|
* JPEGImageWriterTest
|
||||||
@ -161,6 +150,20 @@ public class JPEGImageWriterTest extends ImageWriterAbstractTest<JPEGImageWriter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unit/regression test for #559
|
||||||
|
@Test
|
||||||
|
public void testTranscodeMoreThan4DHTSegments() throws IOException {
|
||||||
|
ImageWriter writer = createWriter();
|
||||||
|
ImageReader reader = ImageIO.getImageReader(writer);
|
||||||
|
|
||||||
|
ByteArrayOutputStream stream = transcode(reader, getClassLoaderResource("/jpeg/5dhtsegments.jpg"), writer, ColorSpace.TYPE_RGB);
|
||||||
|
|
||||||
|
reader.reset();
|
||||||
|
reader.setInput(new ByteArrayImageInputStream(stream.toByteArray()));
|
||||||
|
BufferedImage image = reader.read(0);
|
||||||
|
assertNotNull(image);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTranscodeWithMetadataRGBtoRGB() throws IOException {
|
public void testTranscodeWithMetadataRGBtoRGB() throws IOException {
|
||||||
ImageWriter writer = createWriter();
|
ImageWriter writer = createWriter();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user