TMI-113: Worked around a rather nasty bug in com.sun.imageio.plugins.jpeg.AdobeMarkerSegment by filtering out all APP14/Adobe marker segments from the stream (and re-inserting to metadata later).

This commit is contained in:
Harald Kuhr
2015-03-16 12:02:31 +01:00
parent a0bd5034ab
commit e8f207ef54
4 changed files with 129 additions and 29 deletions
@@ -155,24 +155,30 @@ final class JPEGImage10MetadataCleaner {
}
}
// Special case: Broken AdobeDCT segment, inconsistent with SOF, use values from SOF
if (adobeDCT != null && (adobeDCT.getTransform() == AdobeDCTSegment.YCCK && sof.componentsInFrame() < 4 ||
if (adobeDCT != null) {
// Special case: Broken AdobeDCT segment, inconsistent with SOF, use values from SOF
if ((adobeDCT.getTransform() == AdobeDCTSegment.YCCK && sof.componentsInFrame() < 4 ||
adobeDCT.getTransform() == AdobeDCTSegment.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.getTransform() == AdobeDCTSegment.YCCK ? "YCCK/CMYK" : "YCC/RGB",
sof.marker & 0xf, sof.componentsInFrame()
));
reader.processWarningOccurred(String.format(
"Invalid Adobe App14 marker. Indicates %s data, but SOF%d has %d color component(s). " +
"Ignoring Adobe App14 marker.",
adobeDCT.getTransform() == AdobeDCTSegment.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
}
else {
// Otherwise, add back the Adobe tag we filtered out in JPEGSegmentImageInputStream
IIOMetadataNode app14Adobe = new IIOMetadataNode("app14Adobe");
app14Adobe.setAttribute("version", String.valueOf(adobeDCT.getVersion()));
app14Adobe.setAttribute("flags0", String.valueOf(adobeDCT.getFlags0()));
app14Adobe.setAttribute("flags1", String.valueOf(adobeDCT.getFlags1()));
app14Adobe.setAttribute("transform", String.valueOf(adobeDCT.getTransform()));
// We don't add this as unknown marker, as we are certain it's bogus by now
markerSequence.insertBefore(app14Adobe, markerSequence.getFirstChild());
}
}
Node next = null;
@@ -117,9 +117,9 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
marker = 0xff00 | stream.readUnsignedByte();
}
// TODO: Optionally skip JFIF only for non-JFIF conformant streams
// TODO: Refactor to make various segments optional, we probably only want the "Adobe" APP14 segment, 'Exif' APP1 and very few others
if (isAppSegmentMarker(marker) && !(marker == JPEG.APP1 && isAppSegmentWithId("Exif", stream)) && marker != JPEG.APP14) {
// TODO: Should we just skip all app marker segments?
// We are now handling all important segments ourselves
if (isAppSegmentMarker(marker) && !(marker == JPEG.APP1 && isAppSegmentWithId("Exif", stream))) {
int length = stream.readUnsignedShort(); // Length including length field itself
stream.seek(realPosition + 2 + length); // Skip marker (2) + length
}
@@ -133,8 +133,12 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
if (marker == JPEG.SOS) {
// Treat rest of stream as a single segment (scanning for EOI is too much work)
// TODO: For progressive, there will be more than one SOS...
length = Long.MAX_VALUE - realPosition;
}
// else if (marker == JPEG.APP14 && isAppSegmentWithId("Adobe", stream)) {
// length = 16;
// }
else {
// Length including length field itself
length = stream.readUnsignedShort() + 2;