diff --git a/Cargo.toml b/Cargo.toml index cd87c01..bdba5b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ byteorder = "1.2" lzw = "0.10" num-derive = "0.3" num-traits = "0.2" +miniz_oxide = "0.3" [dev-dependencies] tempfile = "3.0" diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 8f799d7..67e9c88 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -7,7 +7,7 @@ use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError}; use self::ifd::Directory; -use self::stream::{ByteOrder, EndianReader, LZWReader, PackBitsReader, SmartReader}; +use self::stream::{ByteOrder, EndianReader, LZWReader, DeflateReader, PackBitsReader, SmartReader}; pub mod ifd; mod stream; @@ -100,6 +100,8 @@ pub enum CompressionMethod { Fax4 = 4, LZW = 5, JPEG = 6, + Deflate = 8, + OldDeflate = 0x80B2, PackBits = 0x8005, } @@ -582,6 +584,10 @@ impl Decoder { )?; (bytes, Box::new(reader)) } + CompressionMethod::OldDeflate => { + let (bytes, reader) = DeflateReader::new(&mut self.reader, max_uncompressed_length)?; + (bytes, Box::new(reader)) + } method => { return Err(TiffError::UnsupportedError( TiffUnsupportedError::UnsupportedCompressionMethod(method), diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index 46d7d10..fe5d20c 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -1,7 +1,9 @@ //! All IO functionality needed for TIFF decoding +use crate::error::{TiffError, TiffResult}; use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; use lzw; +use miniz_oxide::inflate; use std::io::{self, Read, Seek}; /// Byte order of the TIFF file. @@ -67,6 +69,52 @@ pub trait EndianReader: Read { } } +/// Reader that decompresses DEFLATE streams +pub struct DeflateReader { + buffer: io::Cursor>, + byte_order: ByteOrder, +} + +impl DeflateReader { + pub fn new( + reader: &mut SmartReader, + max_uncompressed_length: usize, + ) -> TiffResult<(usize, Self)> { + let byte_order = reader.byte_order; + let mut compressed = Vec::new(); + reader.read_to_end(&mut compressed)?; + + // TODO: Implement streaming compression, and remove this (temporary) and somewhat + // misleading workaround. + if compressed.len() > max_uncompressed_length { + return Err(TiffError::LimitsExceeded); + } + + let uncompressed = + inflate::decompress_to_vec_zlib(&compressed).map_err(TiffError::from_inflate_status)?; + + Ok(( + uncompressed.len(), + Self { + byte_order, + buffer: io::Cursor::new(uncompressed), + }, + )) + } +} + +impl Read for DeflateReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.buffer.read(buf) + } +} + +impl EndianReader for DeflateReader { + fn byte_order(&self) -> ByteOrder { + self.byte_order + } +} + /// Reader that decompresses LZW streams pub struct LZWReader { buffer: io::Cursor>, diff --git a/src/error.rs b/src/error.rs index 4e9f127..6c6021b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,6 +5,7 @@ use std::string; use decoder::ifd::{Tag, Value}; use decoder::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration}; +use miniz_oxide::inflate::TINFLStatus; use ColorType; /// Tiff error kinds. @@ -34,6 +35,7 @@ pub enum TiffFormatError { UnknownPredictor(u32), UnsignedIntegerExpected(Value), SignedIntegerExpected(Value), + InflateError(InflateError), } impl fmt::Display for TiffFormatError { @@ -55,10 +57,28 @@ impl fmt::Display for TiffFormatError { SignedIntegerExpected(ref val) => { write!(fmt, "Expected signed integer, {:?} found.", val) } + InflateError(_) => write!(fmt, "Failed to decode inflate data."), } } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct InflateError { + status: TINFLStatus, +} + +impl InflateError { + pub fn new(status: TINFLStatus) -> Self { + Self { status } + } +} + +impl TiffError { + pub(crate) fn from_inflate_status(status: TINFLStatus) -> Self { + TiffError::FormatError(TiffFormatError::InflateError(InflateError::new(status))) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum TiffUnsupportedError { HorizontalPredictor(ColorType), @@ -138,7 +158,7 @@ impl Error for TiffError { } } - fn cause(&self) -> Option<&Error> { + fn cause(&self) -> Option<&dyn Error> { match *self { TiffError::IoError(ref e) => Some(e), _ => None, diff --git a/src/lib.rs b/src/lib.rs index 8ed1d25..fe194e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ extern crate byteorder; extern crate lzw; +extern crate miniz_oxide; #[macro_use] extern crate num_derive; extern crate num_traits;