From 0286fa42683ba5f2cf5920ebd7bc7d93f7da07ae Mon Sep 17 00:00:00 2001 From: Harald Kuhr Date: Fri, 26 Feb 2021 18:27:58 +0100 Subject: [PATCH] JPEG Exif/thumbnail refactoring pt II. --- .../imageio/plugins/jpeg/EXIFThumbnail.java | 38 ++-- .../imageio/plugins/jpeg/JFIFThumbnail.java | 12 +- .../imageio/plugins/jpeg/JFXXThumbnail.java | 13 +- .../jpeg/JPEGImage10MetadataCleaner.java | 2 +- .../imageio/plugins/jpeg/JPEGImageReader.java | 48 ++--- .../jpeg/AbstractThumbnailReaderTest.java | 3 - .../plugins/jpeg/EXIFThumbnailReaderTest.java | 164 +++++++++++++++++- .../plugins/jpeg/JFIFThumbnailReaderTest.java | 34 ++-- .../plugins/jpeg/JFXXThumbnailReaderTest.java | 55 +++--- 9 files changed, 254 insertions(+), 115 deletions(-) diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/EXIFThumbnail.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/EXIFThumbnail.java index 514e6088..0cdece31 100644 --- a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/EXIFThumbnail.java +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/EXIFThumbnail.java @@ -57,31 +57,30 @@ final class EXIFThumbnail { private EXIFThumbnail() { } - static ThumbnailReader from(final EXIF exif, final CompoundDirectory exifMetadata, final ImageReader jpegThumbnailReader, final JPEGSegmentWarningListener listener) throws IOException { - if (exif != null && exifMetadata != null && exifMetadata.directoryCount() == 2) { - ImageInputStream stream = exif.exifData(); // NOTE This is an in-memory stream and must not be closed... + static ThumbnailReader from(final EXIF segment, final CompoundDirectory exif, final ImageReader jpegThumbnailReader) throws IOException { + if (segment != null && exif != null && exif.directoryCount() >= 2) { + ImageInputStream stream = segment.exifData(); // NOTE This is an in-memory stream and must not be closed... - Directory ifd1 = exifMetadata.getDirectory(1); + Directory ifd1 = exif.getDirectory(1); // Compression: 1 = no compression, 6 = JPEG compression (default) Entry compressionEntry = ifd1.getEntryById(TIFF.TAG_COMPRESSION); int compression = compressionEntry == null ? 6 : ((Number) compressionEntry.getValue()).intValue(); switch (compression) { - case 6: - return createJPEGThumbnailReader(exif, jpegThumbnailReader, listener, stream, ifd1); case 1: - return createUncompressedThumbnailReader(listener, stream, ifd1); + return createUncompressedThumbnailReader(stream, ifd1); + case 6: + return createJPEGThumbnailReader(segment, jpegThumbnailReader, stream, ifd1); default: - listener.warningOccurred("EXIF IFD with unknown thumbnail compression (expected 1 or 6): " + compression); - break; + throw new IIOException("EXIF IFD with unknown thumbnail compression (expected 1 or 6): " + compression); } } return null; } - private static UncompressedThumbnailReader createUncompressedThumbnailReader(JPEGSegmentWarningListener listener, ImageInputStream stream, Directory ifd1) throws IOException { + private static UncompressedThumbnailReader createUncompressedThumbnailReader(ImageInputStream stream, Directory ifd1) throws IOException { Entry stripOffEntry = ifd1.getEntryById(TIFF.TAG_STRIP_OFFSETS); Entry width = ifd1.getEntryById(TIFF.TAG_IMAGE_WIDTH); Entry height = ifd1.getEntryById(TIFF.TAG_IMAGE_HEIGHT); @@ -95,12 +94,8 @@ final class EXIFThumbnail { int w = ((Number) width.getValue()).intValue(); int h = ((Number) height.getValue()).intValue(); - // TODO: Decide on warning OR exception! - if (bitsPerSample != null) { - int[] bpp = (int[]) bitsPerSample.getValue(); - if (!Arrays.equals(bpp, new int[] {8, 8, 8})) { - throw new IIOException("Unknown BitsPerSample value for uncompressed EXIF thumbnail (expected [8, 8, 8]): " + bitsPerSample.getValueAsString()); - } + if (bitsPerSample != null && !Arrays.equals((int[]) bitsPerSample.getValue(), new int[] {8, 8, 8})) { + throw new IIOException("Unknown BitsPerSample value for uncompressed EXIF thumbnail (expected [8, 8, 8]): " + bitsPerSample.getValueAsString()); } if (samplesPerPixel != null && ((Number) samplesPerPixel.getValue()).intValue() != 3) { @@ -111,7 +106,7 @@ final class EXIFThumbnail { long stripOffset = ((Number) stripOffEntry.getValue()).longValue(); int thumbLength = w * h * 3; - if (stripOffset >= 0 && stripOffset + thumbLength < stream.length()) { + if (stripOffset >= 0 && stripOffset + thumbLength <= stream.length()) { // Read raw image data, either RGB or YCbCr stream.seek(stripOffset); byte[] thumbData = new byte[thumbLength]; @@ -135,11 +130,10 @@ final class EXIFThumbnail { } } - listener.warningOccurred("EXIF IFD with empty or incomplete uncompressed thumbnail"); - return null; + throw new IIOException("EXIF IFD with empty or incomplete uncompressed thumbnail"); } - private static JPEGThumbnailReader createJPEGThumbnailReader(EXIF exif, ImageReader jpegThumbnailReader, JPEGSegmentWarningListener listener, ImageInputStream stream, Directory ifd1) throws IOException { + private static JPEGThumbnailReader createJPEGThumbnailReader(EXIF exif, ImageReader jpegThumbnailReader, ImageInputStream stream, Directory ifd1) throws IOException { Entry jpegOffEntry = ifd1.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT); if (jpegOffEntry != null) { Entry jpegLenEntry = ifd1.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); @@ -152,13 +146,13 @@ final class EXIFThumbnail { // Verify first bytes are FFD8 stream.seek(jpegOffset); stream.setByteOrder(ByteOrder.BIG_ENDIAN); + if (stream.readUnsignedShort() == JPEG.SOI) { return new JPEGThumbnailReader(jpegThumbnailReader, stream, jpegOffset); } } } - listener.warningOccurred("EXIF IFD with empty or incomplete JPEG thumbnail"); - return null; + throw new IIOException("EXIF IFD with empty or incomplete JPEG thumbnail"); } } diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JFIFThumbnail.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JFIFThumbnail.java index e5dc432f..a8d5b67b 100644 --- a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JFIFThumbnail.java +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JFIFThumbnail.java @@ -32,6 +32,9 @@ package com.twelvemonkeys.imageio.plugins.jpeg; import com.twelvemonkeys.imageio.plugins.jpeg.ThumbnailReader.UncompressedThumbnailReader; +import javax.imageio.IIOException; +import java.io.IOException; + /** * JFIFThumbnail * @@ -43,14 +46,13 @@ final class JFIFThumbnail { private JFIFThumbnail() { } - static ThumbnailReader from(final JFIF segment, final JPEGSegmentWarningListener listener) { + static ThumbnailReader from(final JFIF segment) throws IOException { if (segment != null && segment.xThumbnail > 0 && segment.yThumbnail > 0) { if (segment.thumbnail == null || segment.thumbnail.length < segment.xThumbnail * segment.yThumbnail) { - listener.warningOccurred("Ignoring truncated JFIF thumbnail"); - } - else { - return new UncompressedThumbnailReader(segment.xThumbnail, segment.yThumbnail, segment.thumbnail); + throw new IIOException("Truncated JFIF thumbnail"); } + + return new UncompressedThumbnailReader(segment.xThumbnail, segment.yThumbnail, segment.thumbnail); } return null; diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JFXXThumbnail.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JFXXThumbnail.java index 4cdf9bf7..accf9b67 100644 --- a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JFXXThumbnail.java +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JFXXThumbnail.java @@ -36,7 +36,9 @@ import com.twelvemonkeys.imageio.plugins.jpeg.ThumbnailReader.JPEGThumbnailReade import com.twelvemonkeys.imageio.plugins.jpeg.ThumbnailReader.UncompressedThumbnailReader; import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream; +import javax.imageio.IIOException; import javax.imageio.ImageReader; +import java.io.IOException; /** * JFXXThumbnailReader @@ -50,7 +52,7 @@ final class JFXXThumbnail { private JFXXThumbnail() { } - static ThumbnailReader from(final JFXX segment, final ImageReader thumbnailReader, final JPEGSegmentWarningListener listener) { + static ThumbnailReader from(final JFXX segment, final ImageReader thumbnailReader) throws IOException { if (segment != null) { if (segment.thumbnail != null && segment.thumbnail.length > 2) { switch (segment.extensionCode) { @@ -64,26 +66,29 @@ final class JFXXThumbnail { case JFXX.INDEXED: int w = segment.thumbnail[0] & 0xff; int h = segment.thumbnail[1] & 0xff; + if (segment.thumbnail.length >= 2 + 768 + w * h) { return new IndexedThumbnailReader(w, h, segment.thumbnail, 2, segment.thumbnail, 2 + 768); } + break; case JFXX.RGB: w = segment.thumbnail[0] & 0xff; h = segment.thumbnail[1] & 0xff; + if (segment.thumbnail.length >= 2 + w * h * 3) { return new UncompressedThumbnailReader(w, h, segment.thumbnail, 2); } + break; default: - listener.warningOccurred(String.format("Unknown JFXX extension code: %d, ignoring thumbnail", segment.extensionCode)); - return null; + throw new IIOException(String.format("Unknown JFXX extension code: %d, ignoring thumbnail", segment.extensionCode)); } } - listener.warningOccurred("JFXX segment truncated, ignoring thumbnail"); + throw new IIOException("JFXX segment truncated, ignoring thumbnail"); } return null; diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImage10MetadataCleaner.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImage10MetadataCleaner.java index c96faf95..4843bace 100644 --- a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImage10MetadataCleaner.java +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImage10MetadataCleaner.java @@ -133,7 +133,7 @@ final class JPEGImage10MetadataCleaner { IIOMetadataNode app0JFXX = new IIOMetadataNode("app0JFXX"); app0JFXX.setAttribute("extensionCode", String.valueOf(jfxx.extensionCode)); - ThumbnailReader thumbnailReader = JFXXThumbnail.from(jfxx, reader.getThumbnailReader(), JPEGSegmentWarningListener.NULL_LISTENER); + ThumbnailReader thumbnailReader = JFXXThumbnail.from(jfxx, reader.getThumbnailReader()); IIOMetadataNode jfifThumb; switch (jfxx.extensionCode) { diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java index 5a4a6b89..c8c7b7d2 100644 --- a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java @@ -909,17 +909,6 @@ public final class JPEGImageReader extends ImageReaderBase { return null; } - // TODO: Util method? - static byte[] readFully(DataInput stream, int len) throws IOException { - if (len == 0) { - throw new IllegalArgumentException("len == 0"); - } - - byte[] data = new byte[len]; - stream.readFully(data); - return data; - } - ICC_Profile getEmbeddedICCProfile(final boolean allowBadIndexes) throws IOException { // ICC v 1.42 (2006) annex B: // APP2 marker (0xFFE2) + 2 byte length + ASCII 'ICC_PROFILE' + 0 (termination) @@ -1086,25 +1075,38 @@ public final class JPEGImageReader extends ImageReaderBase { if (thumbnails == null) { thumbnails = new ArrayList<>(); - JPEGSegmentWarningDelegate listenerDelegate = new JPEGSegmentWarningDelegate(); - // Read JFIF thumbnails if present - ThumbnailReader thumbnailReader = JFIFThumbnail.from(getJFIF(), listenerDelegate); - if (thumbnailReader != null) { - thumbnails.add(thumbnailReader); + try { + ThumbnailReader thumbnail = JFIFThumbnail.from(getJFIF()); + if (thumbnail != null) { + thumbnails.add(thumbnail); + } + } + catch (IOException e) { + processWarningOccurred(e.getMessage()); } // Read JFXX thumbnails if present - thumbnailReader = JFXXThumbnail.from(getJFXX(), getThumbnailReader(), listenerDelegate); - if (thumbnailReader != null) { - thumbnails.add(thumbnailReader); + try { + ThumbnailReader thumbnail = JFXXThumbnail.from(getJFXX(), getThumbnailReader()); + if (thumbnail != null) { + thumbnails.add(thumbnail); + } + } + catch (IOException e) { + processWarningOccurred(e.getMessage()); } // Read Exif thumbnails if present - EXIF exif = getExif(); - thumbnailReader = EXIFThumbnail.from(exif, parseExif(exif), getThumbnailReader(), listenerDelegate); - if (thumbnailReader != null) { - thumbnails.add(thumbnailReader); + try { + EXIF exif = getExif(); + ThumbnailReader thumbnailReader = EXIFThumbnail.from(exif, parseExif(exif), getThumbnailReader()); + if (thumbnailReader != null) { + thumbnails.add(thumbnailReader); + } + } + catch (IOException e) { + processWarningOccurred(e.getMessage()); } } } diff --git a/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/AbstractThumbnailReaderTest.java b/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/AbstractThumbnailReaderTest.java index 118c4a53..70864919 100644 --- a/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/AbstractThumbnailReaderTest.java +++ b/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/AbstractThumbnailReaderTest.java @@ -39,7 +39,6 @@ import java.io.IOException; import java.net.URL; import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.mock; /** * AbstractThumbnailReaderTest @@ -53,8 +52,6 @@ public abstract class AbstractThumbnailReaderTest { IIORegistry.getDefaultInstance().registerServiceProvider(new URLImageInputStreamSpi()); } - protected final JPEGSegmentWarningListener listener = mock(JPEGSegmentWarningListener.class); - protected abstract ThumbnailReader createReader(ImageInputStream stream) throws IOException; protected final ImageInputStream createStream(final String name) throws IOException { diff --git a/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/EXIFThumbnailReaderTest.java b/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/EXIFThumbnailReaderTest.java index 1361cd0c..5e42c150 100644 --- a/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/EXIFThumbnailReaderTest.java +++ b/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/EXIFThumbnailReaderTest.java @@ -30,14 +30,21 @@ package com.twelvemonkeys.imageio.plugins.jpeg; +import com.twelvemonkeys.imageio.metadata.AbstractCompoundDirectory; import com.twelvemonkeys.imageio.metadata.CompoundDirectory; +import com.twelvemonkeys.imageio.metadata.Entry; import com.twelvemonkeys.imageio.metadata.jpeg.JPEG; import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment; import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil; +import com.twelvemonkeys.imageio.metadata.tiff.IFD; +import com.twelvemonkeys.imageio.metadata.tiff.TIFF; +import com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry; import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader; +import org.junit.After; import org.junit.Test; +import javax.imageio.IIOException; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; @@ -45,6 +52,8 @@ import java.awt.image.BufferedImage; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import static org.junit.Assert.*; @@ -60,6 +69,153 @@ public class EXIFThumbnailReaderTest extends AbstractThumbnailReaderTest { private final ImageReader thumbnailReader = ImageIO.getImageReadersByFormatName("jpeg").next(); + @After + public void tearDown() { + thumbnailReader.dispose(); + } + + @Test + public void testFromNullSegment() throws IOException { + assertNull(EXIFThumbnail.from(null, null, thumbnailReader)); + } + + @Test + public void testFromNullIFD() throws IOException { + assertNull(EXIFThumbnail.from(new EXIF(new byte[0]), null, thumbnailReader)); + } + + @Test + public void testFromEmptyIFD() throws IOException { + assertNull(EXIFThumbnail.from(new EXIF(new byte[0]), new EXIFDirectory(), thumbnailReader)); + } + + @Test + public void testFromSingleIFD() throws IOException { + assertNull(EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.emptyList())), thumbnailReader)); + } + + @Test(expected = IIOException.class) + public void testFromMissingThumbnail() throws IOException { + EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.emptyList()), new IFD(Collections.emptyList())), thumbnailReader); + } + + @Test(expected = IIOException.class) + public void testFromUnsupportedThumbnailCompression() throws IOException { + List entries = Collections.singletonList(new TIFFEntry(TIFF.TAG_COMPRESSION, 42)); + EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.emptyList()), new IFD(entries)), thumbnailReader); + } + + @Test(expected = IIOException.class) + public void testFromMissingOffsetUncompressed() throws IOException { + List entries = Arrays.asList( + new TIFFEntry(TIFF.TAG_COMPRESSION, 1), + new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 16), + new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 9) + ); + EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.emptyList()), new IFD(entries)), thumbnailReader); + } + + @Test(expected = IIOException.class) + public void testFromMissingWidthUncompressed() throws IOException { + List entries = Arrays.asList( + new TIFFEntry(TIFF.TAG_COMPRESSION, 1), + new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0), + new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 9) + ); + EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.emptyList()), new IFD(entries)), thumbnailReader); + } + + @Test(expected = IIOException.class) + public void testFromMissingHeightUncompressed() throws IOException { + List entries = Arrays.asList( + new TIFFEntry(TIFF.TAG_COMPRESSION, 1), + new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0), + new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 16) + ); + EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.emptyList()), new IFD(entries)), thumbnailReader); + } + + @Test(expected = IIOException.class) + public void testFromUnsupportedPhotometricUncompressed() throws IOException { + List entries = Arrays.asList( + new TIFFEntry(TIFF.TAG_COMPRESSION, 1), + new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0), + new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 16), + new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 9), + new TIFFEntry(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, 42) + ); + EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.emptyList()), new IFD(entries)), thumbnailReader); + } + + @Test(expected = IIOException.class) + public void testFromUnsupportedBitsPerSampleUncompressed() throws IOException { + List entries = Arrays.asList( + new TIFFEntry(TIFF.TAG_COMPRESSION, 1), + new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0), + new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 16), + new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 9), + new TIFFEntry(TIFF.TAG_BITS_PER_SAMPLE, new int[]{5, 6, 5}) + ); + EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.emptyList()), new IFD(entries)), thumbnailReader); + } + + @Test(expected = IIOException.class) + public void testFromUnsupportedSamplesPerPixelUncompressed() throws IOException { + List entries = Arrays.asList( + new TIFFEntry(TIFF.TAG_COMPRESSION, 1), + new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0), + new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 160), + new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 90), + new TIFFEntry(TIFF.TAG_SAMPLES_PER_PIXEL, 1) + ); + EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9]), new EXIFDirectory(new IFD(Collections.emptyList()), new IFD(entries)), thumbnailReader); + } + + @Test(expected = IIOException.class) + public void testFromTruncatedUncompressed() throws IOException { + List entries = Arrays.asList( + new TIFFEntry(TIFF.TAG_COMPRESSION, 1), + new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0), + new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 160), + new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 90) + ); + EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.emptyList()), new IFD(entries)), thumbnailReader); + } + + @Test + public void testValidUncompressed() throws IOException { + List entries = Arrays.asList( + new TIFFEntry(TIFF.TAG_COMPRESSION, 1), + new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0), + new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 16), + new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 9) + ); + + ThumbnailReader reader = EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.emptyList()), new IFD(entries)), thumbnailReader); + assertNotNull(reader); + + // Sanity check below + assertEquals(16, reader.getWidth()); + assertEquals(9, reader.getHeight()); + assertNotNull(reader.read()); + } + + @Test(expected = IIOException.class) + public void testFromMissingOffsetJPEG() throws IOException { + List entries = Collections.singletonList(new TIFFEntry(TIFF.TAG_COMPRESSION, 6)); + EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.emptyList()), new IFD(entries)), thumbnailReader); + } + + @Test(expected = IIOException.class) + public void testFromTruncatedJPEG() throws IOException { + List entries = Arrays.asList( + new TIFFEntry(TIFF.TAG_COMPRESSION, 6), + new TIFFEntry(TIFF.TAG_JPEG_INTERCHANGE_FORMAT, 0) + ); + EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.emptyList()), new IFD(entries)), thumbnailReader); + } + + @Override protected ThumbnailReader createReader(final ImageInputStream stream) throws IOException { List segments = JPEGSegmentUtil.readSegments(stream, JPEG.APP1, "Exif"); @@ -74,7 +230,7 @@ public class EXIFThumbnailReaderTest extends AbstractThumbnailReaderTest { new DataInputStream(data).readFully(exifData); EXIF exif = new EXIF(exifData); - return EXIFThumbnail.from(exif, (CompoundDirectory) new TIFFReader().read(exif.exifData()), thumbnailReader, listener); + return EXIFThumbnail.from(exif, (CompoundDirectory) new TIFFReader().read(exif.exifData()), thumbnailReader); } @Test @@ -102,4 +258,10 @@ public class EXIFThumbnailReaderTest extends AbstractThumbnailReaderTest { assertEquals(80, thumbnail.getWidth()); assertEquals(60, thumbnail.getHeight()); } + + private static class EXIFDirectory extends AbstractCompoundDirectory { + public EXIFDirectory(IFD... ifds) { + super(Arrays.asList(ifds)); + } + } } diff --git a/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JFIFThumbnailReaderTest.java b/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JFIFThumbnailReaderTest.java index 60df7e48..5a9ade44 100644 --- a/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JFIFThumbnailReaderTest.java +++ b/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JFIFThumbnailReaderTest.java @@ -36,6 +36,7 @@ import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil; import org.junit.Test; +import javax.imageio.IIOException; import javax.imageio.stream.ImageInputStream; import java.awt.image.BufferedImage; import java.io.DataInputStream; @@ -43,7 +44,6 @@ import java.io.IOException; import java.util.List; import static org.junit.Assert.*; -import static org.mockito.Mockito.*; /** * JFIFThumbnailReaderTest @@ -64,44 +64,34 @@ public class JFIFThumbnailReaderTest extends AbstractThumbnailReaderTest { JPEGSegment segment = segments.get(0); - return JFIFThumbnail.from(JFIF.read(new DataInputStream(segment.segmentData()), segment.segmentLength()), listener); + return JFIFThumbnail.from(JFIF.read(new DataInputStream(segment.segmentData()), segment.segmentLength())); } @Test - public void testFromNull() { - assertNull(JFIFThumbnail.from(null, listener)); - - verify(listener, never()).warningOccurred(anyString()); + public void testFromNull() throws IOException { + assertNull(JFIFThumbnail.from(null)); } @Test - public void testFromNullThumbnail() { - assertNull(JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 0, 0, null), listener)); - - verify(listener, never()).warningOccurred(anyString()); + public void testFromNullThumbnail() throws IOException { + assertNull(JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 0, 0, null))); } @Test - public void testFromEmpty() { - assertNull(JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 0, 0, new byte[0]), listener)); - - verify(listener, never()).warningOccurred(anyString()); + public void testFromEmpty() throws IOException { + assertNull(JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 0, 0, new byte[0]))); } - @Test - public void testFromTruncated() { - assertNull(JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 255, 170, new byte[99]), listener)); - - verify(listener, only()).warningOccurred(anyString()); + @Test(expected = IIOException.class) + public void testFromTruncated() throws IOException { + JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 255, 170, new byte[99])); } @Test public void testFromValid() throws IOException { - ThumbnailReader reader = JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 30, 20, new byte[30 * 20 * 3]), listener); + ThumbnailReader reader = JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 30, 20, new byte[30 * 20 * 3])); assertNotNull(reader); - verify(listener, never()).warningOccurred(anyString()); - // Sanity check below assertEquals(30, reader.getWidth()); assertEquals(20, reader.getHeight()); diff --git a/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JFXXThumbnailReaderTest.java b/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JFXXThumbnailReaderTest.java index e0c01bc8..339d3eae 100644 --- a/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JFXXThumbnailReaderTest.java +++ b/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JFXXThumbnailReaderTest.java @@ -37,6 +37,7 @@ import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil; import org.junit.After; import org.junit.Test; +import javax.imageio.IIOException; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; @@ -46,8 +47,6 @@ import java.io.IOException; import java.util.List; import static org.junit.Assert.*; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.*; /** * JFXXThumbnailReaderTest @@ -60,7 +59,7 @@ public class JFXXThumbnailReaderTest extends AbstractThumbnailReaderTest { private final ImageReader thumbnailReader = ImageIO.getImageReadersByFormatName("jpeg").next(); @Override - protected ThumbnailReader createReader(ImageInputStream stream) throws IOException { + protected ThumbnailReader createReader(final ImageInputStream stream) throws IOException { List segments = JPEGSegmentUtil.readSegments(stream, JPEG.APP0, "JFXX"); stream.close(); @@ -68,7 +67,7 @@ public class JFXXThumbnailReaderTest extends AbstractThumbnailReaderTest { assertFalse(segments.isEmpty()); JPEGSegment jfxx = segments.get(0); - return JFXXThumbnail.from(JFXX.read(new DataInputStream(jfxx.segmentData()), jfxx.length()), thumbnailReader, listener); + return JFXXThumbnail.from(JFXX.read(new DataInputStream(jfxx.segmentData()), jfxx.length()), thumbnailReader); } @After @@ -77,51 +76,41 @@ public class JFXXThumbnailReaderTest extends AbstractThumbnailReaderTest { } @Test - public void testFromNull() { - assertNull(JFXXThumbnail.from(null, thumbnailReader, listener)); - - verify(listener, never()).warningOccurred(anyString()); + public void testFromNull() throws IOException { + assertNull(JFXXThumbnail.from(null, thumbnailReader)); } - @Test - public void testFromNullThumbnail() { - assertNull(JFXXThumbnail.from(new JFXX(JFXX.JPEG, null), thumbnailReader, listener)); - - verify(listener, only()).warningOccurred(anyString()); + @Test(expected = IIOException.class) + public void testFromNullThumbnail() throws IOException { + JFXXThumbnail.from(new JFXX(JFXX.JPEG, null), thumbnailReader); } - @Test - public void testFromEmpty() { - assertNull(JFXXThumbnail.from(new JFXX(JFXX.JPEG, new byte[0]), thumbnailReader, listener)); - - verify(listener, only()).warningOccurred(anyString()); + @Test(expected = IIOException.class) + public void testFromEmpty() throws IOException { + JFXXThumbnail.from(new JFXX(JFXX.JPEG, new byte[0]), thumbnailReader); } - @Test - public void testFromTruncatedJPEG() { - assertNull(JFXXThumbnail.from(new JFXX(JFXX.JPEG, new byte[99]), thumbnailReader, listener)); - - verify(listener, only()).warningOccurred(anyString()); + @Test(expected = IIOException.class) + public void testFromTruncatedJPEG() throws IOException { + JFXXThumbnail.from(new JFXX(JFXX.JPEG, new byte[99]), thumbnailReader); } - @Test - public void testFromTruncatedRGB() { + @Test(expected = IIOException.class) + public void testFromTruncatedRGB() throws IOException { byte[] thumbnail = new byte[765]; thumbnail[0] = (byte) 160; thumbnail[1] = 90; - assertNull(JFXXThumbnail.from(new JFXX(JFXX.RGB, thumbnail), thumbnailReader, listener)); - verify(listener, only()).warningOccurred(anyString()); + JFXXThumbnail.from(new JFXX(JFXX.RGB, thumbnail), thumbnailReader); } - @Test - public void testFromTruncatedIndexed() { + @Test(expected = IIOException.class) + public void testFromTruncatedIndexed() throws IOException { byte[] thumbnail = new byte[365]; thumbnail[0] = (byte) 160; thumbnail[1] = 90; - assertNull(JFXXThumbnail.from(new JFXX(JFXX.INDEXED, thumbnail), thumbnailReader, listener)); - verify(listener, only()).warningOccurred(anyString()); + JFXXThumbnail.from(new JFXX(JFXX.INDEXED, thumbnail), thumbnailReader); } @Test @@ -129,11 +118,9 @@ public class JFXXThumbnailReaderTest extends AbstractThumbnailReaderTest { byte[] thumbnail = new byte[14]; thumbnail[0] = 2; thumbnail[1] = 2; - ThumbnailReader reader = JFXXThumbnail.from(new JFXX(JFXX.RGB, thumbnail), thumbnailReader, listener); + ThumbnailReader reader = JFXXThumbnail.from(new JFXX(JFXX.RGB, thumbnail), thumbnailReader); assertNotNull(reader); - verify(listener, never()).warningOccurred(anyString()); - // Sanity check below assertEquals(2, reader.getWidth()); assertEquals(2, reader.getHeight());