mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-05 12:35:29 -04:00
#535: Detect incorrect compression in TIFF CCITT stream.
This commit is contained in:
parent
8bc952ba66
commit
db5635e844
@ -91,11 +91,10 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
|
|||||||
super(Validate.notNull(stream, "stream"));
|
super(Validate.notNull(stream, "stream"));
|
||||||
|
|
||||||
this.columns = Validate.isTrue(columns > 0, columns, "width must be greater than 0");
|
this.columns = Validate.isTrue(columns > 0, columns, "width must be greater than 0");
|
||||||
this.type = Validate.isTrue(
|
this.type = Validate.isTrue(type == TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE ||
|
||||||
type == TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE ||
|
type == TIFFExtension.COMPRESSION_CCITT_T4 ||
|
||||||
type == TIFFExtension.COMPRESSION_CCITT_T4 || type == TIFFExtension.COMPRESSION_CCITT_T6,
|
type == TIFFExtension.COMPRESSION_CCITT_T6,
|
||||||
type, "Only CCITT Modified Huffman RLE compression (2), CCITT T4 (3) or CCITT T6 (4) supported: %s"
|
type, "Only CCITT Modified Huffman RLE compression (2), CCITT T4 (3) or CCITT T6 (4) supported: %s");
|
||||||
);
|
|
||||||
this.fillOrder = Validate.isTrue(
|
this.fillOrder = Validate.isTrue(
|
||||||
fillOrder == TIFFBaseline.FILL_LEFT_TO_RIGHT || fillOrder == TIFFExtension.FILL_RIGHT_TO_LEFT,
|
fillOrder == TIFFBaseline.FILL_LEFT_TO_RIGHT || fillOrder == TIFFExtension.FILL_RIGHT_TO_LEFT,
|
||||||
fillOrder, "Expected fill order 1 or 2: %s"
|
fillOrder, "Expected fill order 1 or 2: %s"
|
||||||
@ -150,6 +149,46 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
|
|||||||
this(stream, columns, type, fillOrder, options, type == TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE);
|
this(stream, columns, type, fillOrder, options, type == TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int findCompressionType(final int type, final InputStream in) throws IOException {
|
||||||
|
// Discover possible incorrect type, revert to RLE
|
||||||
|
if (type == TIFFExtension.COMPRESSION_CCITT_T4 && in.markSupported()) {
|
||||||
|
byte[] streamData = new byte[20];
|
||||||
|
|
||||||
|
try {
|
||||||
|
in.mark(streamData.length);
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
while (offset < streamData.length) {
|
||||||
|
int read = in.read(streamData, offset, streamData.length - offset);
|
||||||
|
if (read <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
in.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (streamData[0] != 0 || (streamData[1] >> 4 != 1 && streamData[1] != 1)) {
|
||||||
|
// Leading EOL (0b000000000001) not found, search further and try RLE if not found
|
||||||
|
short b = (short) (((streamData[0] << 8) + streamData[1]) >> 4);
|
||||||
|
for (int i = 12; i < 160; i++) {
|
||||||
|
b = (short) ((b << 1) + ((streamData[(i / 8)] >> (7 - (i % 8))) & 0x01));
|
||||||
|
|
||||||
|
if ((b & 0xFFF) == 1) {
|
||||||
|
return TIFFExtension.COMPRESSION_CCITT_T4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
private void fetch() throws IOException {
|
private void fetch() throws IOException {
|
||||||
if (decodedPos >= decodedLength) {
|
if (decodedPos >= decodedLength) {
|
||||||
decodedLength = 0;
|
decodedLength = 0;
|
||||||
|
@ -2308,12 +2308,22 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
case TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE:
|
case TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE:
|
||||||
case TIFFExtension.COMPRESSION_CCITT_T4:
|
case TIFFExtension.COMPRESSION_CCITT_T4:
|
||||||
case TIFFExtension.COMPRESSION_CCITT_T6:
|
case TIFFExtension.COMPRESSION_CCITT_T6:
|
||||||
return new CCITTFaxDecoderStream(stream, width, compression, fillOrder, getCCITTOptions(compression));
|
return new CCITTFaxDecoderStream(stream, width, findCCITTType(compression, stream), fillOrder, getCCITTOptions(compression), compression == TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE);
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unsupported TIFF compression: " + compression);
|
throw new IllegalArgumentException("Unsupported TIFF compression: " + compression);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int findCCITTType(final int encodedCompression, final InputStream stream) throws IOException {
|
||||||
|
int compressionType = CCITTFaxDecoderStream.findCompressionType(encodedCompression, stream);
|
||||||
|
|
||||||
|
if (compressionType != encodedCompression) {
|
||||||
|
processWarningOccurred(String.format("Detected compression type %d, does not match encoded compression type: %d", compressionType, encodedCompression));
|
||||||
|
}
|
||||||
|
|
||||||
|
return compressionType;
|
||||||
|
}
|
||||||
|
|
||||||
private InputStream createFillOrderStream(final int fillOrder, final InputStream stream) {
|
private InputStream createFillOrderStream(final int fillOrder, final InputStream stream) {
|
||||||
switch (fillOrder) {
|
switch (fillOrder) {
|
||||||
case TIFFBaseline.FILL_LEFT_TO_RIGHT:
|
case TIFFBaseline.FILL_LEFT_TO_RIGHT:
|
||||||
|
@ -59,6 +59,14 @@ public class CCITTFaxDecoderStreamTest {
|
|||||||
static final byte[] DATA_G3_1D_FILL = { 0x00, 0x01, (byte) 0x84, (byte) 0xE0, 0x01, (byte) 0x84, (byte) 0xE0, 0x01,
|
static final byte[] DATA_G3_1D_FILL = { 0x00, 0x01, (byte) 0x84, (byte) 0xE0, 0x01, (byte) 0x84, (byte) 0xE0, 0x01,
|
||||||
(byte) 0x84, (byte) 0xE0, 0x1, 0x7D, (byte) 0xC0 };
|
(byte) 0x84, (byte) 0xE0, 0x1, 0x7D, (byte) 0xC0 };
|
||||||
|
|
||||||
|
// group3_1d_premature_eol.tif
|
||||||
|
// 0011 0101 | 0000 0010 1011 | 0110 0111 | 0010 1001 | 0100
|
||||||
|
// 0W | 59B | 640W | 40W
|
||||||
|
static final byte[] DATA_G3_1D_PREMATURE_EOL = {
|
||||||
|
0x35, 0x02, (byte) 0xB6, 0x72, (byte) 0x94, (byte) 0xE8, 0x74, 0x38, 0x1C, (byte) 0x81, 0x64, (byte) 0xD4,
|
||||||
|
0x0A, (byte) 0xD9, (byte) 0xD2, 0x27, 0x50, (byte) 0x90, (byte) 0xA6, (byte) 0x87, 0x43, (byte) 0xE3
|
||||||
|
};
|
||||||
|
|
||||||
// group3_2d.tif: EOL|k=1|3W|1B|2W|EOL|k=0|V|V|V|EOL|k=1|3W|1B|2W|EOL|k=0|V-1|V|V|6*F
|
// group3_2d.tif: EOL|k=1|3W|1B|2W|EOL|k=0|V|V|V|EOL|k=1|3W|1B|2W|EOL|k=0|V-1|V|V|6*F
|
||||||
static final byte[] DATA_G3_2D = { 0x00, 0x1C, 0x27, 0x00, 0x17, 0x00, 0x1C, 0x27, 0x00, 0x12, (byte) 0xC0 };
|
static final byte[] DATA_G3_2D = { 0x00, 0x1C, 0x27, 0x00, 0x17, 0x00, 0x1C, 0x27, 0x00, 0x12, (byte) 0xC0 };
|
||||||
|
|
||||||
@ -170,6 +178,27 @@ public class CCITTFaxDecoderStreamTest {
|
|||||||
assertArrayEquals(imageData, bytes);
|
assertArrayEquals(imageData, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFidCompressionType() throws IOException {
|
||||||
|
// RLE
|
||||||
|
assertEquals(TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE, CCITTFaxDecoderStream.findCompressionType(TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE, new ByteArrayInputStream(DATA_RLE_UNALIGNED)));
|
||||||
|
|
||||||
|
// Group 3/CCITT_T4
|
||||||
|
assertEquals(TIFFExtension.COMPRESSION_CCITT_T4, CCITTFaxDecoderStream.findCompressionType(TIFFExtension.COMPRESSION_CCITT_T4, new ByteArrayInputStream(DATA_G3_1D)));
|
||||||
|
assertEquals(TIFFExtension.COMPRESSION_CCITT_T4, CCITTFaxDecoderStream.findCompressionType(TIFFExtension.COMPRESSION_CCITT_T4, new ByteArrayInputStream(DATA_G3_1D_FILL)));
|
||||||
|
assertEquals(TIFFExtension.COMPRESSION_CCITT_T4, CCITTFaxDecoderStream.findCompressionType(TIFFExtension.COMPRESSION_CCITT_T4, new ByteArrayInputStream(DATA_G3_2D)));
|
||||||
|
assertEquals(TIFFExtension.COMPRESSION_CCITT_T4, CCITTFaxDecoderStream.findCompressionType(TIFFExtension.COMPRESSION_CCITT_T4, new ByteArrayInputStream(DATA_G3_2D_FILL)));
|
||||||
|
assertEquals(TIFFExtension.COMPRESSION_CCITT_T4, CCITTFaxDecoderStream.findCompressionType(TIFFExtension.COMPRESSION_CCITT_T4, new ByteArrayInputStream(DATA_G3_2D_lsb2msb)));
|
||||||
|
|
||||||
|
// Group 4/CCITT_T6
|
||||||
|
assertEquals(TIFFExtension.COMPRESSION_CCITT_T6, CCITTFaxDecoderStream.findCompressionType(TIFFExtension.COMPRESSION_CCITT_T6, new ByteArrayInputStream(DATA_G4)));
|
||||||
|
assertEquals(TIFFExtension.COMPRESSION_CCITT_T6, CCITTFaxDecoderStream.findCompressionType(TIFFExtension.COMPRESSION_CCITT_T6, new ByteArrayInputStream(DATA_G4_ALIGNED)));
|
||||||
|
|
||||||
|
// From sample file encoded with RLE, but with CCITT_T4 compression tag
|
||||||
|
assertEquals(TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE, CCITTFaxDecoderStream.findCompressionType(TIFFExtension.COMPRESSION_CCITT_T4, new ByteArrayInputStream(DATA_G3_1D_PREMATURE_EOL)));
|
||||||
|
assertEquals(TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE, CCITTFaxDecoderStream.findCompressionType(TIFFExtension.COMPRESSION_CCITT_T4, new ByteArrayInputStream(DATA_RLE_UNALIGNED)));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDecodeType3_2D() throws IOException {
|
public void testDecodeType3_2D() throws IOException {
|
||||||
InputStream stream = new CCITTFaxDecoderStream(new ByteArrayInputStream(DATA_G3_2D), 6,
|
InputStream stream = new CCITTFaxDecoderStream(new ByteArrayInputStream(DATA_G3_2D), 6,
|
||||||
|
@ -638,6 +638,25 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadIncorrectCompressionRLEAsG3() throws IOException {
|
||||||
|
TestData testData = new TestData(getClassLoaderResource("/tiff/incorrect-compression-rle-as-g3.tif"), new Dimension(1700, 32));
|
||||||
|
|
||||||
|
try (ImageInputStream stream = testData.getInputStream()) {
|
||||||
|
TIFFImageReader reader = createReader();
|
||||||
|
reader.setInput(stream);
|
||||||
|
|
||||||
|
IIOReadWarningListener warningListener = mock(IIOReadWarningListener.class);
|
||||||
|
reader.addIIOReadWarningListener(warningListener);
|
||||||
|
|
||||||
|
BufferedImage image = reader.read(0);
|
||||||
|
|
||||||
|
assertNotNull(image);
|
||||||
|
assertEquals(testData.getDimension(0), new Dimension(image.getWidth(), image.getHeight()));
|
||||||
|
verify(warningListener, atLeastOnce()).warningOccurred(eq(reader), and(contains("compression type"), contains("does not match")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadMultipleExtraSamples() throws IOException {
|
public void testReadMultipleExtraSamples() throws IOException {
|
||||||
ImageReader reader = createReader();
|
ImageReader reader = createReader();
|
||||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user