diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index af05a78..cfbc646 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -342,6 +342,11 @@ impl Decoder { PhotometricInterpretation::RGB => match self.bits_per_sample[..] { [r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)), [r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)), + // FIXME: We should _ignore_ other components. In particular: + // > Beware of extra components. Some TIFF files may have more components per pixel + // than you think. A Baseline TIFF reader must skip over them gracefully,using the + // values of the SamplesPerPixel and BitsPerSample fields. + // > -- TIFF 6.0 Specification, Section 7, Additional Baseline requirements. _ => Err(TiffError::UnsupportedError( TiffUnsupportedError::InterpretationWithBits( self.photometric_interpretation, @@ -764,7 +769,7 @@ impl Decoder { buffer: DecodingBuffer<'a>, offset: u64, length: u64, - max_uncompressed_length: usize, + strip_sample_count: usize, ) -> TiffResult { let color_type = self.colortype()?; self.goto_offset_u64(offset)?; @@ -780,7 +785,7 @@ impl Decoder { let (bytes, reader) = LZWReader::new( &mut self.reader, usize::try_from(length)?, - max_uncompressed_length * buffer.byte_len(), + strip_sample_count * buffer.byte_len(), )?; (bytes, Box::new(reader)) } @@ -791,8 +796,7 @@ impl Decoder { (bytes, Box::new(reader)) } CompressionMethod::OldDeflate => { - let (bytes, reader) = - DeflateReader::new(&mut self.reader, max_uncompressed_length)?; + let (bytes, reader) = DeflateReader::new(&mut self.reader, strip_sample_count)?; (bytes, Box::new(reader)) } method => { @@ -802,9 +806,14 @@ impl Decoder { } }; - if bytes / buffer.byte_len() > max_uncompressed_length { + // FIXME: this might be suboptimal. We might default remaining bits to ยด0`, which some + // other decoders might do. + if bytes / buffer.byte_len() > strip_sample_count { return Err(TiffError::FormatError( - TiffFormatError::InconsistentSizesEncountered, + TiffFormatError::UnexpectedCompressedData { + actual_bytes: bytes, + required_bytes: strip_sample_count * buffer.byte_len(), + }, )); } @@ -1037,7 +1046,10 @@ impl Decoder { if units_read < buffer_size { return Err(TiffError::FormatError( - TiffFormatError::InconsistentSizesEncountered, + TiffFormatError::InconsistentStripSamples { + actual_samples: units_read, + required_samples: buffer_size, + }, )); } if let Ok(predictor) = self.get_tag_unsigned(Tag::Predictor) { diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index 327e6de..9b89046 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -267,10 +267,11 @@ impl LZWReader { let mut uncompressed = Vec::with_capacity(max_uncompressed_length); let mut decoder = weezl::decode::Decoder::with_tiff_size_switch(weezl::BitOrder::Msb, 8); let mut bytes_read = 0; - while bytes_read < compressed_length && uncompressed.len() < max_uncompressed_length { + + while uncompressed.len() < max_uncompressed_length { let bytes_written = uncompressed.len(); uncompressed.reserve(1 << 12); - let buffer_space = uncompressed.capacity(); + let buffer_space = uncompressed.capacity().min(max_uncompressed_length); uncompressed.resize(buffer_space, 0u8); let result = decoder.decode_bytes( diff --git a/src/error.rs b/src/error.rs index f1af2fd..30a2635 100644 --- a/src/error.rs +++ b/src/error.rs @@ -43,6 +43,14 @@ pub enum TiffFormatError { TiffSignatureInvalid, ImageFileDirectoryNotFound, InconsistentSizesEncountered, + UnexpectedCompressedData { + actual_bytes: usize, + required_bytes: usize, + }, + InconsistentStripSamples { + actual_samples: usize, + required_samples: usize, + }, InvalidTag, InvalidTagValueType(Tag), RequiredTagNotFound(Tag), @@ -66,6 +74,26 @@ impl fmt::Display for TiffFormatError { TiffSignatureInvalid => write!(fmt, "TIFF signature invalid."), ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."), InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."), + UnexpectedCompressedData { + actual_bytes, + required_bytes, + } => { + write!( + fmt, + "Decompression returned different amount of bytes than expected: got {}, expected {}.", + actual_bytes, required_bytes + ) + } + InconsistentStripSamples { + actual_samples, + required_samples, + } => { + write!( + fmt, + "Inconsistent elements in strip: got {}, expected {}.", + actual_samples, required_samples + ) + } InvalidTag => write!(fmt, "Image contains invalid tag."), InvalidTagValueType(ref tag) => { write!(fmt, "Tag `{:?}` did not have the expected value type.", tag)