TMI-18: Better filtering of APP segments, now only takes 'Exif' APP1 segments into account.

+ Updated failing test.
This commit is contained in:
Harald Kuhr 2012-04-15 22:10:20 +02:00
parent 08b5891298
commit 2f07329296
2 changed files with 43 additions and 6 deletions

View File

@ -29,11 +29,13 @@
package com.twelvemonkeys.imageio.plugins.jpeg; package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG; import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import com.twelvemonkeys.lang.Validate;
import javax.imageio.IIOException; import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageInputStreamImpl; import javax.imageio.stream.ImageInputStreamImpl;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -90,7 +92,7 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
int marker = stream.readUnsignedShort(); int marker = stream.readUnsignedShort();
// TODO: Refactor to make various segments optional, we probably only want the "Adobe" APP14 segment, 'Exif' APP1 and very few others // 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.APP0 && marker != JPEG.APP1 && marker != JPEG.APP14) { if (isAppSegmentMarker(marker) && marker != JPEG.APP0 && !(marker == JPEG.APP1 && isAppSegmentWithId("Exif", stream)) && marker != JPEG.APP14) {
int length = stream.readUnsignedShort(); // Length including length field itself int length = stream.readUnsignedShort(); // Length including length field itself
stream.seek(realPosition + 2 + length); // Skip marker (2) + length stream.seek(realPosition + 2 + length); // Skip marker (2) + length
} }
@ -138,6 +140,38 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
return segment; return segment;
} }
private static boolean isAppSegmentWithId(String segmentId, ImageInputStream stream) throws IOException {
Validate.notNull(segmentId, "segmentId");
stream.mark();
try {
int length = stream.readUnsignedShort(); // Length including length field itself
byte[] data = new byte[Math.max(20, length - 2)];
stream.readFully(data);
return segmentId.equals(asNullTerminatedAsciiString(data, 0));
}
finally {
stream.reset();
}
}
static String asNullTerminatedAsciiString(final byte[] data, final int offset) {
for (int i = 0; i < data.length - offset; i++) {
if (data[i] == 0 || i > 255) {
return asAsciiString(data, offset, offset + i);
}
}
return null;
}
static String asAsciiString(final byte[] data, final int offset, final int length) {
return new String(data, offset, length, Charset.forName("ascii"));
}
private void streamInit() throws IOException { private void streamInit() throws IOException {
stream.seek(0); stream.seek(0);

View File

@ -102,7 +102,7 @@ public class JPEGSegmentImageInputStreamTest {
assertThat(length, new LessOrEqual<Long>(10203l)); // In no case should length increase assertThat(length, new LessOrEqual<Long>(10203l)); // In no case should length increase
assertEquals(9495l, length); // May change, if more chunks are passed to reader... assertEquals(9625l, length); // May change, if more chunks are passed to reader...
} }
@Test @Test
@ -110,14 +110,17 @@ public class JPEGSegmentImageInputStreamTest {
ImageInputStream stream = new JPEGSegmentImageInputStream(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/no-image-types-rgb-us-web-coated-v2-ms-photogallery-exif.jpg"))); ImageInputStream stream = new JPEGSegmentImageInputStream(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/no-image-types-rgb-us-web-coated-v2-ms-photogallery-exif.jpg")));
List<JPEGSegment> appSegments = JPEGSegmentUtil.readSegments(stream, JPEGSegmentUtil.APP_SEGMENTS); List<JPEGSegment> appSegments = JPEGSegmentUtil.readSegments(stream, JPEGSegmentUtil.APP_SEGMENTS);
assertEquals(2, appSegments.size()); assertEquals(3, appSegments.size());
assertEquals(JPEG.APP0, appSegments.get(0).marker()); assertEquals(JPEG.APP0, appSegments.get(0).marker());
assertEquals("JFIF", appSegments.get(0).identifier()); assertEquals("JFIF", appSegments.get(0).identifier());
assertEquals(JPEG.APP14, appSegments.get(1).marker()); assertEquals(JPEG.APP1, appSegments.get(1).marker());
assertEquals("Adobe", appSegments.get(1).identifier()); assertEquals("Exif", appSegments.get(1).identifier());
// And thus, no Exif, no ICC_PROFILE or other segments assertEquals(JPEG.APP14, appSegments.get(2).marker());
assertEquals("Adobe", appSegments.get(2).identifier());
// And thus, no XMP, no ICC_PROFILE or other segments
} }
} }