tiff-0.5.0/.gitignore010064400017500001750000000000401344723122100126460ustar0000000000000000 /target/ **/*.rs.bk Cargo.lock tiff-0.5.0/.travis.yml010064400017500001750000000001101355036066000127720ustar0000000000000000language: rust rust: - 1.34.2 - stable - beta - nightly tiff-0.5.0/CHANGES.md010064400017500001750000000020601366524157600122740ustar0000000000000000# Version 0.5.0 * Added support for 32-bit and 64-bit decoded values. * Added CMYK(16|32|64) color type support. * Check many internal integer conversions to increase stability. This should only lead to images being reported as faulty that would previously silently break platform limits. If there are any false positives, please report them. * Remove an erroneous check of decoded length in lzw compressed images. # Version 0.4.0 * Several enumerations are now non_exhaustive for future extensions. These are `Tag`, `Type`, `Value`, `PhotometricInterpretation`, `CompressionMethod`, `Predictor`. * Enums gained a dedicated method to convert to their TIFF variant value with the specified type. Performing these conversions by casting the discriminant with `as` is not guaranteed to be stable, except where documented explicitly. * Removed the num-derive and num dependencies. * Added support for decoding `deflate` compressed images. * Make the decoder `Limits` customizable by exposing members. * Fixed multi-page TIFF encoding writing incorrect offsets. tiff-0.5.0/Cargo.toml.orig010064400017500001750000000007771366523513200135750ustar0000000000000000[package] name = "tiff" version = "0.5.0" authors = [ "ccgn", "bvssvni ", "nwin", "TyOverby ", "HeroicKatora", "Calum", "CensoredUsername ", "Robzz", "birktj"] license = "MIT" description = "TIFF decoding and encoding library in pure Rust" repository = "https://github.com/image-rs/image-tiff" exclude = ["tests/images/*", "tests/fuzz_images/*"] [dependencies] byteorder = "1.2" lzw = "0.10" miniz_oxide = "0.3" tiff-0.5.0/Cargo.toml0000644000000020360000000000000100240ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "tiff" version = "0.5.0" authors = ["ccgn", "bvssvni ", "nwin", "TyOverby ", "HeroicKatora", "Calum", "CensoredUsername ", "Robzz", "birktj"] exclude = ["tests/images/*", "tests/fuzz_images/*"] description = "TIFF decoding and encoding library in pure Rust" license = "MIT" repository = "https://github.com/image-rs/image-tiff" [dependencies.byteorder] version = "1.2" [dependencies.lzw] version = "0.10" [dependencies.miniz_oxide] version = "0.3" tiff-0.5.0/LICENSE010064400017500001750000000020611344723122100116700ustar0000000000000000MIT License Copyright (c) 2018 PistonDevelopers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tiff-0.5.0/README.md010064400017500001750000000027531366254333000121600ustar0000000000000000# image-tiff [![Build Status](https://travis-ci.org/image-rs/image-tiff.svg?branch=master)](https://travis-ci.org/image-rs/image-tiff) [![Documentation](https://docs.rs/tiff/badge.svg)](https://docs.rs/tiff) [![Further crate info](https://img.shields.io/crates/v/tiff.svg)](https://crates.io/crates/tiff) TIFF decoding and encoding library in pure Rust ## Supported ### Features - Baseline spec (other than formats and tags listed below as not supported) - Multipage - Incremental decoding ### Formats This table lists photometric interpretations and sample formats which are supported for encoding and decoding. The entries are `ColorType` variants for which sample bit depths are supported. Only samples where all bit depths are equal are currently supported. For example, `RGB(8)` means that the bit depth [8, 8, 8] is supported and will be interpreted as an 8 bit per channel RGB color type. | `PhotometricInterpretation` | UINT Format | IEEEFP Format | |-|-|-| | `WhiteIsZero` | Gray(8\|16\|32\|64) | | | `BlackIsZero` | Gray(8\|16\|32\|64) | | | `RGB` | RGB(8\|16\|32\|64), RGBA(8\|16\|32\|64) | | | `RGBPalette` | | | | `Mask` | | | | `CMYK` | CMYK(8\|16\|32\|64) | | | `YCbCr` | | | | `CIELab` | | | ### Compressions | | Decoding | Encoding | |-|-|-| | None | ✓ | ✓ | | LZW | ✓ | | | Deflate | ✓ | | | PackBits | ✓ | | ## Not yet supported Formats and interpretations not listed above or with empty entries are unsupported. - Baseline tags - `ExtraSamples` - Extension tags - BigTIFF tiff-0.5.0/src/decoder/ifd.rs010064400017500001750000000213201366254333000141740ustar0000000000000000//! Function for reading TIFF tags use std::collections::HashMap; use std::convert::TryFrom; use std::io::{self, Read, Seek}; use std::mem; use super::stream::{ByteOrder, EndianReader, SmartReader}; use tags::{Tag, Type}; use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError}; use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational}; #[allow(unused_qualifications)] #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Value { Signed(i32), Unsigned(u32), List(Vec), Rational(u32, u32), SRational(i32, i32), Ascii(String), #[doc(hidden)] // Do not match against this. __NonExhaustive, } impl Value { pub fn into_u32(self) -> TiffResult { match self { Unsigned(val) => Ok(val), val => Err(TiffError::FormatError( TiffFormatError::UnsignedIntegerExpected(val), )), } } pub fn into_i32(self) -> TiffResult { match self { Signed(val) => Ok(val), val => Err(TiffError::FormatError( TiffFormatError::SignedIntegerExpected(val), )), } } pub fn into_u32_vec(self) -> TiffResult> { match self { List(vec) => { let mut new_vec = Vec::with_capacity(vec.len()); for v in vec { new_vec.push(v.into_u32()?) } Ok(new_vec) } Unsigned(val) => Ok(vec![val]), Rational(numerator, denominator) => Ok(vec![numerator, denominator]), Ascii(val) => Ok(val.chars().map(u32::from).collect()), val => Err(TiffError::FormatError( TiffFormatError::UnsignedIntegerExpected(val), )), } } pub fn into_i32_vec(self) -> TiffResult> { match self { List(vec) => { let mut new_vec = Vec::with_capacity(vec.len()); for v in vec { match v { SRational(numerator, denominator) => { new_vec.push(numerator); new_vec.push(denominator); } _ => new_vec.push(v.into_i32()?) } } Ok(new_vec) } Signed(val) => Ok(vec![val]), SRational(numerator, denominator) => Ok(vec![numerator, denominator]), val => Err(TiffError::FormatError( TiffFormatError::SignedIntegerExpected(val), )), } } } #[derive(Clone)] pub struct Entry { type_: Type, count: u32, offset: [u8; 4], } impl ::std::fmt::Debug for Entry { fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { fmt.write_str(&format!( "Entry {{ type_: {:?}, count: {:?}, offset: {:?} }}", self.type_, self.count, &self.offset )) } } impl Entry { pub fn new(type_: Type, count: u32, offset: [u8; 4]) -> Entry { Entry { type_, count, offset, } } /// Returns a mem_reader for the offset/value field fn r(&self, byte_order: ByteOrder) -> SmartReader>> { SmartReader::wrap(io::Cursor::new(self.offset.to_vec()), byte_order) } pub fn val( &self, limits: &super::Limits, decoder: &mut super::Decoder, ) -> TiffResult { let bo = decoder.byte_order(); match (self.type_, self.count) { // TODO check if this could give wrong results // at a different endianess of file/computer. (Type::BYTE, 1) => Ok(Unsigned(u32::from(self.offset[0]))), (Type::BYTE, 2) => offset_to_bytes(2, self), (Type::BYTE, 3) => offset_to_bytes(3, self), (Type::BYTE, 4) => offset_to_bytes(4, self), (Type::BYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { Ok(Unsigned(u32::from(decoder.read_byte()?))) }), (Type::SBYTE, 1) => Ok(Signed(i32::from(self.offset[0] as i8))), (Type::SBYTE, 2) => offset_to_sbytes(2, self), (Type::SBYTE, 3) => offset_to_sbytes(3, self), (Type::SBYTE, 4) => offset_to_sbytes(4, self), (Type::SBYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { Ok(Signed(i32::from(decoder.read_byte()? as i8))) }), (Type::SHORT, 1) => Ok(Unsigned(u32::from(self.r(bo).read_u16()?))), (Type::SSHORT, 1) => Ok(Signed(i32::from(self.r(bo).read_i16()?))), (Type::SHORT, 2) => { let mut r = self.r(bo); Ok(List(vec![ Unsigned(u32::from(r.read_u16()?)), Unsigned(u32::from(r.read_u16()?)), ])) } (Type::SSHORT, 2) => { let mut r = self.r(bo); Ok(List(vec![ Signed(i32::from(r.read_i16()?)), Signed(i32::from(r.read_i16()?)), ])) } (Type::SHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { Ok(Unsigned(u32::from(decoder.read_short()?))) }), (Type::SSHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { Ok(Signed(i32::from(decoder.read_sshort()?))) }), (Type::LONG, 1) => Ok(Unsigned(self.r(bo).read_u32()?)), (Type::SLONG, 1) => Ok(Signed(self.r(bo).read_i32()?)), (Type::LONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { Ok(Unsigned(decoder.read_long()?)) }), (Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { Ok(Signed(decoder.read_slong()?)) }), (Type::RATIONAL, 1) => { decoder.goto_offset(self.r(bo).read_u32()?)?; let numerator = decoder.read_long()?; let denominator = decoder.read_long()?; Ok(Rational(numerator, denominator)) } (Type::SRATIONAL, 1) => { decoder.goto_offset(self.r(bo).read_u32()?)?; let numerator = decoder.read_slong()?; let denominator = decoder.read_slong()?; Ok(SRational(numerator, denominator)) } (Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { Ok(Rational( decoder.read_long()?, decoder.read_long()?, )) }), (Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { Ok(SRational( decoder.read_slong()?, decoder.read_slong()?, )) }), (Type::ASCII, n) => { let n = usize::try_from(n)?; if n > limits.decoding_buffer_size { return Err(TiffError::LimitsExceeded); } decoder.goto_offset(self.r(bo).read_u32()?)?; let string = decoder.read_string(n)?; Ok(Ascii(string)) } _ => Err(TiffError::UnsupportedError( TiffUnsupportedError::UnsupportedDataType, )), } } #[inline] fn decode_offset(&self, value_count: u32, bo: ByteOrder, limits: &super::Limits, decoder: &mut super::Decoder, decode_fn: F) -> TiffResult where R: Read + Seek, F: Fn(&mut super::Decoder) -> TiffResult, { let value_count = usize::try_from(value_count)?; if value_count > limits.decoding_buffer_size / mem::size_of::() { return Err(TiffError::LimitsExceeded); } let mut v = Vec::with_capacity(value_count); decoder.goto_offset(self.r(bo).read_u32()?)?; for _ in 0..value_count { v.push(decode_fn(decoder)?) } Ok(List(v)) } } /// Extracts a list of BYTE tags stored in an offset #[inline] fn offset_to_bytes(n: usize, entry: &Entry) -> TiffResult { Ok(List( entry.offset[0..n] .iter() .map(|&e| Unsigned(u32::from(e))) .collect() )) } /// Extracts a list of SBYTE tags stored in an offset #[inline] fn offset_to_sbytes(n: usize, entry: &Entry) -> TiffResult { Ok(List( entry.offset[0..n] .iter() .map(|&e| Signed(i32::from(e as i8))) .collect() )) } /// Type representing an Image File Directory pub type Directory = HashMap; tiff-0.5.0/src/decoder/mod.rs010064400017500001750000000705741366254333000142300ustar0000000000000000use std::convert::TryFrom; use std::collections::HashMap; use std::io::{self, Read, Seek}; use std::cmp; use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError}; use self::ifd::Directory; use tags::{CompressionMethod, PhotometricInterpretation, Predictor, Tag, Type}; use self::stream::{ByteOrder, EndianReader, LZWReader, DeflateReader, PackBitsReader, SmartReader}; pub mod ifd; mod stream; /// Result of a decoding process #[derive(Debug)] pub enum DecodingResult { /// A vector of unsigned bytes U8(Vec), /// A vector of unsigned words U16(Vec), /// A vector of 32 bit unsigned ints U32(Vec), /// A vector of 64 bit unsigned ints U64(Vec), } impl DecodingResult { fn new_u8(size: usize, limits: &Limits) -> TiffResult { if size > limits.decoding_buffer_size { Err(TiffError::LimitsExceeded) } else { Ok(DecodingResult::U8(vec![0; size])) } } fn new_u16(size: usize, limits: &Limits) -> TiffResult { if size > limits.decoding_buffer_size / 2 { Err(TiffError::LimitsExceeded) } else { Ok(DecodingResult::U16(vec![0; size])) } } fn new_u32(size: usize, limits: &Limits) -> TiffResult { if size > limits.decoding_buffer_size / 4 { Err(TiffError::LimitsExceeded) } else { Ok(DecodingResult::U32(vec![0; size])) } } fn new_u64(size: usize, limits: &Limits) -> TiffResult { if size > limits.decoding_buffer_size / 8 { Err(TiffError::LimitsExceeded) } else { Ok(DecodingResult::U64(vec![0; size])) } } pub fn as_buffer(&mut self, start: usize) -> DecodingBuffer { match *self { DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]), DecodingResult::U16(ref mut buf) => DecodingBuffer::U16(&mut buf[start..]), DecodingResult::U32(ref mut buf) => DecodingBuffer::U32(&mut buf[start..]), DecodingResult::U64(ref mut buf) => DecodingBuffer::U64(&mut buf[start..]), } } } // A buffer for image decoding pub enum DecodingBuffer<'a> { /// A slice of unsigned bytes U8(&'a mut [u8]), /// A slice of unsigned words U16(&'a mut [u16]), /// A slice of 32 bit unsigned ints U32(&'a mut [u32]), /// A slice of 64 bit unsigned ints U64(&'a mut [u64]), } impl<'a> DecodingBuffer<'a> { fn len(&self) -> usize { match *self { DecodingBuffer::U8(ref buf) => buf.len(), DecodingBuffer::U16(ref buf) => buf.len(), DecodingBuffer::U32(ref buf) => buf.len(), DecodingBuffer::U64(ref buf) => buf.len(), } } fn byte_len(&self) -> usize { match *self { DecodingBuffer::U8(_) => 1, DecodingBuffer::U16(_) => 2, DecodingBuffer::U32(_) => 4, DecodingBuffer::U64(_) => 8, } } fn copy<'b>(&'b mut self) -> DecodingBuffer<'b> where 'a: 'b, { match *self { DecodingBuffer::U8(ref mut buf) => DecodingBuffer::U8(buf), DecodingBuffer::U16(ref mut buf) => DecodingBuffer::U16(buf), DecodingBuffer::U32(ref mut buf) => DecodingBuffer::U32(buf), DecodingBuffer::U64(ref mut buf) => DecodingBuffer::U64(buf), } } } #[derive(Debug)] struct StripDecodeState { strip_index: usize, strip_offsets: Vec, strip_bytes: Vec, } /// Decoding limits #[derive(Clone, Debug)] pub struct Limits { /// The maximum size of any `DecodingResult` in bytes, the default is /// 256MiB. If the entire image is decoded at once, then this will /// be the maximum size of the image. If it is decoded one strip at a /// time, this will be the maximum size of a strip. pub decoding_buffer_size: usize, /// The maximum size of any ifd value in bytes, the default is /// 1MiB. pub ifd_value_size: usize, /// The purpose of this is to prevent all the fields of the struct from /// being public, as this would make adding new fields a major version /// bump. _non_exhaustive: (), } impl Default for Limits { fn default() -> Limits { Limits { decoding_buffer_size: 256 * 1024 * 1024, ifd_value_size: 1024 * 1024, _non_exhaustive: (), } } } /// The representation of a TIFF decoder /// /// Currently does not support decoding of interlaced images #[derive(Debug)] pub struct Decoder where R: Read + Seek, { reader: SmartReader, byte_order: ByteOrder, limits: Limits, next_ifd: Option, ifd: Option, width: u32, height: u32, bits_per_sample: Vec, samples: u8, photometric_interpretation: PhotometricInterpretation, compression_method: CompressionMethod, strip_decoder: Option, } trait Wrapping { fn wrapping_add(&self, other: Self) -> Self; } impl Wrapping for u8 { fn wrapping_add(&self, other: Self) -> Self { u8::wrapping_add(*self, other) } } impl Wrapping for u16 { fn wrapping_add(&self, other: Self) -> Self { u16::wrapping_add(*self, other) } } impl Wrapping for u32 { fn wrapping_add(&self, other: Self) -> Self { u32::wrapping_add(*self, other) } } impl Wrapping for u64 { fn wrapping_add(&self, other: Self) -> Self { u64::wrapping_add(*self, other) } } fn rev_hpredict_nsamp(image: &mut [T], size: (u32, u32), samples: usize) -> TiffResult<()> where T: Copy + Wrapping, { let width = usize::try_from(size.0)?; let height = usize::try_from(size.1)?; for row in 0..height { for col in samples..width * samples { let prev_pixel = image[(row * width * samples + col - samples)]; let pixel = &mut image[(row * width * samples + col)]; *pixel = pixel.wrapping_add(prev_pixel); } } Ok(()) } fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) -> TiffResult<()> { let samples = match color_type { ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) | ColorType::Gray(64) => 1, ColorType::RGB(8) | ColorType::RGB(16) | ColorType::RGB(32) | ColorType::RGB(64) => 3, ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::RGBA(32) | ColorType::RGBA(64) | ColorType::CMYK(8) | ColorType::CMYK(16) | ColorType::CMYK(32) | ColorType::CMYK(64) => 4, _ => { return Err(TiffError::UnsupportedError( TiffUnsupportedError::HorizontalPredictor(color_type), )) } }; match image { DecodingBuffer::U8(buf) => { rev_hpredict_nsamp(buf, size, samples)?; } DecodingBuffer::U16(buf) => { rev_hpredict_nsamp(buf, size, samples)?; } DecodingBuffer::U32(buf) => { rev_hpredict_nsamp(buf, size, samples)?; } DecodingBuffer::U64(buf) => { rev_hpredict_nsamp(buf, size, samples)?; } } Ok(()) } impl Decoder { /// Create a new decoder that decodes from the stream ```r``` pub fn new(r: R) -> TiffResult> { Decoder { reader: SmartReader::wrap(r, ByteOrder::LittleEndian), byte_order: ByteOrder::LittleEndian, limits: Default::default(), next_ifd: None, ifd: None, width: 0, height: 0, bits_per_sample: vec![1], samples: 1, photometric_interpretation: PhotometricInterpretation::BlackIsZero, compression_method: CompressionMethod::None, strip_decoder: None, } .init() } pub fn with_limits(mut self, limits: Limits) -> Decoder { self.limits = limits; self } pub fn dimensions(&mut self) -> TiffResult<(u32, u32)> { Ok((self.width, self.height)) } pub fn colortype(&mut self) -> TiffResult { match self.photometric_interpretation { 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)), _ => Err(TiffError::UnsupportedError( TiffUnsupportedError::InterpretationWithBits( self.photometric_interpretation, self.bits_per_sample.clone(), ), )), } }, PhotometricInterpretation::CMYK => { match self.bits_per_sample[..] { [c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)), _ => Err(TiffError::UnsupportedError( TiffUnsupportedError::InterpretationWithBits( self.photometric_interpretation, self.bits_per_sample.clone(), ), )), } }, PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero if self.bits_per_sample.len() == 1 => { Ok(ColorType::Gray(self.bits_per_sample[0])) } // TODO: this is bad we should not fail at this point _ => Err(TiffError::UnsupportedError( TiffUnsupportedError::InterpretationWithBits( self.photometric_interpretation, self.bits_per_sample.clone(), ), )), } } fn read_header(&mut self) -> TiffResult<()> { let mut endianess = Vec::with_capacity(2); self.reader.by_ref().take(2).read_to_end(&mut endianess)?; match &*endianess { b"II" => { self.byte_order = ByteOrder::LittleEndian; self.reader.byte_order = ByteOrder::LittleEndian; } b"MM" => { self.byte_order = ByteOrder::BigEndian; self.reader.byte_order = ByteOrder::BigEndian; } _ => { return Err(TiffError::FormatError( TiffFormatError::TiffSignatureNotFound, )) } } if self.read_short()? != 42 { return Err(TiffError::FormatError( TiffFormatError::TiffSignatureInvalid, )); } self.next_ifd = match self.read_long()? { 0 => None, n => Some(n), }; Ok(()) } /// Initializes the decoder. pub fn init(mut self) -> TiffResult> { self.read_header()?; self.next_image()?; Ok(self) } /// Reads in the next image. /// If there is no further image in the TIFF file a format error is returned. /// To determine whether there are more images call `TIFFDecoder::more_images` instead. pub fn next_image(&mut self) -> TiffResult<()> { self.ifd = Some(self.read_ifd()?); self.width = self.get_tag_u32(Tag::ImageWidth)?; self.height = self.get_tag_u32(Tag::ImageLength)?; self.strip_decoder = None; self.photometric_interpretation = self .find_tag_unsigned(Tag::PhotometricInterpretation)? .and_then(PhotometricInterpretation::from_u16) .ok_or(TiffUnsupportedError::UnknownInterpretation)?; if let Some(val) = self.find_tag_unsigned(Tag::Compression)? { self.compression_method = CompressionMethod::from_u16(val) .ok_or(TiffUnsupportedError::UnknownCompressionMethod)?; } if let Some(val) = self.find_tag_unsigned(Tag::SamplesPerPixel)? { self.samples = val; } match self.samples { 1 | 3 | 4 => if let Some(val) = self.find_tag_unsigned_vec(Tag::BitsPerSample)? { self.bits_per_sample = val; }, _ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()), } Ok(()) } /// Returns `true` if there is at least one more image available. pub fn more_images(&self) -> bool { self.next_ifd.is_some() } /// Returns the byte_order pub fn byte_order(&self) -> ByteOrder { self.byte_order } /// Reads a TIFF byte value #[inline] pub fn read_byte(&mut self) -> Result { let mut buf = [0; 1]; self.reader.read_exact(&mut buf)?; Ok(buf[0]) } /// Reads a TIFF short value #[inline] pub fn read_short(&mut self) -> Result { self.reader.read_u16() } /// Reads a TIFF sshort value #[inline] pub fn read_sshort(&mut self) -> Result { self.reader.read_i16() } /// Reads a TIFF long value #[inline] pub fn read_long(&mut self) -> Result { self.reader.read_u32() } /// Reads a TIFF slong value #[inline] pub fn read_slong(&mut self) -> Result { self.reader.read_i32() } /// Reads a string #[inline] pub fn read_string(&mut self, length: usize) -> TiffResult { let mut out = String::with_capacity(length); self.reader.read_to_string(&mut out)?; // Strings may be null-terminated, so we trim anything downstream of the null byte let trimmed = out.bytes().take_while(|&n| n != 0).collect::>(); Ok(String::from_utf8(trimmed)?) } /// Reads a TIFF IFA offset/value field #[inline] pub fn read_offset(&mut self) -> Result<[u8; 4], io::Error> { let mut val = [0; 4]; self.reader.read_exact(&mut val)?; Ok(val) } /// Moves the cursor to the specified offset #[inline] pub fn goto_offset(&mut self, offset: u32) -> io::Result<()> { self.reader .seek(io::SeekFrom::Start(u64::from(offset))) .map(|_| ()) } /// Reads a IFD entry. // An IFD entry has four fields: // // Tag 2 bytes // Type 2 bytes // Count 4 bytes // Value 4 bytes either a pointer the value itself fn read_entry(&mut self) -> TiffResult> { let tag = Tag::from_u16_exhaustive(self.read_short()?); let type_ = match Type::from_u16(self.read_short()?) { Some(t) => t, None => { // Unknown type. Skip this entry according to spec. self.read_long()?; self.read_long()?; return Ok(None); } }; Ok(Some(( tag, ifd::Entry::new( type_, self.read_long()?, // count self.read_offset()?, // offset ), ))) } /// Reads the next IFD fn read_ifd(&mut self) -> TiffResult { let mut dir: Directory = HashMap::new(); match self.next_ifd { None => { return Err(TiffError::FormatError( TiffFormatError::ImageFileDirectoryNotFound, )) } Some(offset) => self.goto_offset(offset)?, } for _ in 0..self.read_short()? { let (tag, entry) = match self.read_entry()? { Some(val) => val, None => continue, // Unknown data type in tag, skip }; dir.insert(tag, entry); } self.next_ifd = match self.read_long()? { 0 => None, n => Some(n), }; Ok(dir) } /// Tries to retrieve a tag. /// Return `Ok(None)` if the tag is not present. pub fn find_tag(&mut self, tag: Tag) -> TiffResult> { let entry = match self.ifd.as_ref().unwrap().get(&tag) { None => return Ok(None), Some(entry) => entry.clone(), }; let limits = self.limits.clone(); Ok(Some(entry.val(&limits, self)?)) } /// Tries to retrieve a tag and convert it to the desired unsigned type. pub fn find_tag_unsigned>(&mut self, tag: Tag) -> TiffResult> { self.find_tag(tag)? .map(|v| v.into_u32()).transpose()? .map(|value| T::try_from(value) .map_err(|_| TiffFormatError::InvalidTagValueType(tag).into())) .transpose() } /// Tries to retrieve a vector of all a tag's values and convert them to /// the desired unsigned type. pub fn find_tag_unsigned_vec>(&mut self, tag: Tag) -> TiffResult>> { self.find_tag(tag)? .map(|v| v.into_u32_vec()).transpose()? .map(|v| v.into_iter() .map(|u| T::try_from(u).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into())) .collect()) .transpose() } /// Tries to retrieve a tag and convert it to the desired unsigned type. /// Returns an error if the tag is not present. pub fn get_tag_unsigned>(&mut self, tag: Tag) -> TiffResult { self.find_tag_unsigned(tag)? .ok_or_else(|| TiffFormatError::RequiredTagNotFound(tag).into()) } /// Tries to retrieve a tag. /// Returns an error if the tag is not present pub fn get_tag(&mut self, tag: Tag) -> TiffResult { match self.find_tag(tag)? { Some(val) => Ok(val), None => Err(TiffError::FormatError( TiffFormatError::RequiredTagNotFound(tag), )), } } /// Tries to retrieve a tag and convert it to the desired type. pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult { self.get_tag(tag)?.into_u32() } /// Tries to retrieve a tag and convert it to the desired type. pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult> { self.get_tag(tag)?.into_u32_vec() } /// Decompresses the strip into the supplied buffer. /// Returns the number of bytes read. fn expand_strip<'a>( &mut self, buffer: DecodingBuffer<'a>, offset: u32, length: u32, max_uncompressed_length: usize, ) -> TiffResult { let color_type = self.colortype()?; self.goto_offset(offset)?; let (bytes, mut reader): (usize, Box) = match self.compression_method { CompressionMethod::None => { let order = self.reader.byte_order; ( usize::try_from(length)?, Box::new(SmartReader::wrap(&mut self.reader, order)), ) } CompressionMethod::LZW => { let (bytes, reader) = LZWReader::new( &mut self.reader, usize::try_from(length)?, max_uncompressed_length * buffer.byte_len() )?; (bytes, Box::new(reader)) } CompressionMethod::PackBits => { let order = self.reader.byte_order; let (bytes, reader) = PackBitsReader::new( &mut self.reader, order, usize::try_from(length)?, )?; (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), )) } }; if bytes / buffer.byte_len() > max_uncompressed_length { return Err(TiffError::FormatError( TiffFormatError::InconsistentSizesEncountered, )); } Ok(match (color_type, buffer) { (ColorType::RGB(8), DecodingBuffer::U8(ref mut buffer)) | (ColorType::RGBA(8), DecodingBuffer::U8(ref mut buffer)) | (ColorType::CMYK(8), DecodingBuffer::U8(ref mut buffer)) => { reader.read_exact(&mut buffer[..bytes])?; bytes } (ColorType::RGBA(16), DecodingBuffer::U16(ref mut buffer)) | (ColorType::RGB(16), DecodingBuffer::U16(ref mut buffer)) | (ColorType::CMYK(16), DecodingBuffer::U16(ref mut buffer)) => { reader.read_u16_into(&mut buffer[..bytes / 2])?; bytes / 2 } (ColorType::RGBA(32), DecodingBuffer::U32(ref mut buffer)) | (ColorType::RGB(32), DecodingBuffer::U32(ref mut buffer)) | (ColorType::CMYK(32), DecodingBuffer::U32(ref mut buffer)) => { reader.read_u32_into(&mut buffer[..bytes / 4])?; bytes / 4 } (ColorType::RGBA(64), DecodingBuffer::U64(ref mut buffer)) | (ColorType::RGB(64), DecodingBuffer::U64(ref mut buffer)) | (ColorType::CMYK(64), DecodingBuffer::U64(ref mut buffer)) => { reader.read_u64_into(&mut buffer[..bytes / 8])?; bytes / 8 } (ColorType::Gray(64), DecodingBuffer::U64(ref mut buffer)) => { reader.read_u64_into(&mut buffer[..bytes / 8])?; if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { for datum in buffer[..bytes / 8].iter_mut() { *datum = 0xffff_ffff_ffff_ffff - *datum } } bytes / 8 } (ColorType::Gray(32), DecodingBuffer::U32(ref mut buffer)) => { reader.read_u32_into(&mut buffer[..bytes / 4])?; if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { for datum in buffer[..bytes / 4].iter_mut() { *datum = 0xffff_ffff - *datum } } bytes / 4 } (ColorType::Gray(16), DecodingBuffer::U16(ref mut buffer)) => { reader.read_u16_into(&mut buffer[..bytes / 2])?; if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { for datum in buffer[..bytes / 2].iter_mut() { *datum = 0xffff - *datum } } bytes / 2 } (ColorType::Gray(n), DecodingBuffer::U8(ref mut buffer)) if n <= 8 => { reader.read_exact(&mut buffer[..bytes])?; if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero { for byte in buffer[..bytes].iter_mut() { *byte = 0xff - *byte } } bytes } (type_, _) => { return Err(TiffError::UnsupportedError( TiffUnsupportedError::UnsupportedColorType(type_), )) } }) } /// Number of strips in image pub fn strip_count(&mut self) -> TiffResult { let rows_per_strip = self .get_tag_u32(Tag::RowsPerStrip) .unwrap_or(self.height); if rows_per_strip == 0 { return Ok(0); } Ok((self.height + rows_per_strip - 1) / rows_per_strip) } fn initialize_strip_decoder(&mut self) -> TiffResult<()> { if self.strip_decoder.is_none() { let strip_offsets = self.get_tag_u32_vec(Tag::StripOffsets)?; let strip_bytes = self.get_tag_u32_vec(Tag::StripByteCounts)?; self.strip_decoder = Some(StripDecodeState { strip_index: 0, strip_offsets, strip_bytes, }); } Ok(()) } pub fn read_strip_to_buffer(&mut self, mut buffer: DecodingBuffer) -> TiffResult<()> { self.initialize_strip_decoder()?; let index = self.strip_decoder.as_ref().unwrap().strip_index; let offset = *self .strip_decoder .as_ref() .unwrap() .strip_offsets .get(index) .ok_or(TiffError::FormatError( TiffFormatError::InconsistentSizesEncountered, ))?; let byte_count = *self .strip_decoder .as_ref() .unwrap() .strip_bytes .get(index) .ok_or(TiffError::FormatError( TiffFormatError::InconsistentSizesEncountered, ))?; let rows_per_strip = usize::try_from(self .get_tag_u32(Tag::RowsPerStrip) .unwrap_or(self.height))?; let strip_height = cmp::min( rows_per_strip, usize::try_from(self.height)? - index * rows_per_strip, ); let buffer_size = usize::try_from(self.width)? * strip_height * self.bits_per_sample.len(); if buffer.len() < buffer_size { return Err(TiffError::FormatError( TiffFormatError::InconsistentSizesEncountered, )); } let units_read = self.expand_strip(buffer.copy(), offset, byte_count, buffer_size)?; self.strip_decoder.as_mut().unwrap().strip_index += 1; if u32::try_from(index)? == self.strip_count()? { self.strip_decoder = None; } if units_read < buffer_size { return Err(TiffError::FormatError( TiffFormatError::InconsistentSizesEncountered, )); } if let Ok(predictor) = self.get_tag_unsigned(Tag::Predictor) { match Predictor::from_u16(predictor) { Some(Predictor::None) => (), Some(Predictor::Horizontal) => { rev_hpredict( buffer.copy(), (self.width, u32::try_from(strip_height)?), self.colortype()?, )?; } None => { return Err(TiffError::FormatError(TiffFormatError::UnknownPredictor( predictor, ))) } Some(Predictor::__NonExhaustive) => unreachable!(), } } Ok(()) } fn result_buffer(&self, height: usize) -> TiffResult { let buffer_size = usize::try_from(self.width)? * height * self.bits_per_sample.len(); match self.bits_per_sample.iter().cloned().max().unwrap_or(8) { n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits), n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits), n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits), n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits), n => { Err(TiffError::UnsupportedError( TiffUnsupportedError::UnsupportedBitsPerChannel(n), )) } } } /// Read a single strip from the image and return it as a Vector pub fn read_strip(&mut self) -> TiffResult { self.initialize_strip_decoder()?; let index = self.strip_decoder.as_ref().unwrap().strip_index; let rows_per_strip = usize::try_from(self .get_tag_u32(Tag::RowsPerStrip) .unwrap_or(self.height))?; let strip_height = cmp::min( rows_per_strip, usize::try_from(self.height)? - index * rows_per_strip, ); let mut result = self.result_buffer(strip_height)?; self.read_strip_to_buffer(result.as_buffer(0))?; Ok(result) } /// Decodes the entire image and return it as a Vector pub fn read_image(&mut self) -> TiffResult { self.initialize_strip_decoder()?; let rows_per_strip = usize::try_from(self .get_tag_u32(Tag::RowsPerStrip) .unwrap_or(self.height))?; let samples_per_strip = usize::try_from(self.width)? * rows_per_strip * self.bits_per_sample.len(); let mut result = self.result_buffer(usize::try_from(self.height)?)?; for i in 0..usize::try_from(self.strip_count()?)? { self.read_strip_to_buffer(result.as_buffer(samples_per_strip * i))?; } Ok(result) } } tiff-0.5.0/src/decoder/stream.rs010064400017500001750000000230561366254333000147350ustar0000000000000000//! 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. #[derive(Clone, Copy, Debug)] pub enum ByteOrder { /// little endian byte order LittleEndian, /// big endian byte order BigEndian, } /// Reader that is aware of the byte order. pub trait EndianReader: Read { /// Byte order that should be adhered to fn byte_order(&self) -> ByteOrder; /// Reads an u16 #[inline(always)] fn read_u16(&mut self) -> Result { match self.byte_order() { ByteOrder::LittleEndian => ::read_u16::(self), ByteOrder::BigEndian => ::read_u16::(self), } } #[inline(always)] fn read_u16_into(&mut self, buffer: &mut [u16]) -> Result<(), io::Error> { match self.byte_order() { ByteOrder::LittleEndian => { ::read_u16_into::(self, buffer) } ByteOrder::BigEndian => { ::read_u16_into::(self, buffer) } } } /// Reads an i16 #[inline(always)] fn read_i16(&mut self) -> Result { match self.byte_order() { ByteOrder::LittleEndian => ::read_i16::(self), ByteOrder::BigEndian => ::read_i16::(self), } } /// Reads an u32 #[inline(always)] fn read_u32(&mut self) -> Result { match self.byte_order() { ByteOrder::LittleEndian => ::read_u32::(self), ByteOrder::BigEndian => ::read_u32::(self), } } #[inline(always)] fn read_u32_into(&mut self, buffer: &mut [u32]) -> Result<(), io::Error> { match self.byte_order() { ByteOrder::LittleEndian => { ::read_u32_into::(self, buffer) } ByteOrder::BigEndian => { ::read_u32_into::(self, buffer) } } } /// Reads an u64 #[inline(always)] fn read_u64(&mut self) -> Result { match self.byte_order() { ByteOrder::LittleEndian => ::read_u64::(self), ByteOrder::BigEndian => ::read_u64::(self), } } #[inline(always)] fn read_u64_into(&mut self, buffer: &mut [u64]) -> Result<(), io::Error> { match self.byte_order() { ByteOrder::LittleEndian => { ::read_u64_into::(self, buffer) } ByteOrder::BigEndian => { ::read_u64_into::(self, buffer) } } } /// Reads an i32 #[inline(always)] fn read_i32(&mut self) -> Result { match self.byte_order() { ByteOrder::LittleEndian => ::read_i32::(self), ByteOrder::BigEndian => ::read_i32::(self), } } } /// 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>, byte_order: ByteOrder, } impl LZWReader { /// Wraps a reader pub fn new( reader: &mut SmartReader, compressed_length: usize, max_uncompressed_length: usize, ) -> io::Result<(usize, LZWReader)> where R: Read + Seek, { let order = reader.byte_order; let mut compressed = vec![0; compressed_length as usize]; reader.read_exact(&mut compressed[..])?; let mut uncompressed = Vec::with_capacity(max_uncompressed_length); let mut decoder = lzw::DecoderEarlyChange::new(lzw::MsbReader::new(), 8); let mut bytes_read = 0; while bytes_read < compressed_length && uncompressed.len() < max_uncompressed_length { let (len, bytes) = decoder.decode_bytes(&compressed[bytes_read..])?; bytes_read += len; uncompressed.extend_from_slice(bytes); } let bytes = uncompressed.len(); Ok(( bytes, LZWReader { buffer: io::Cursor::new(uncompressed), byte_order: order, }, )) } } impl Read for LZWReader { #[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result { self.buffer.read(buf) } } impl EndianReader for LZWReader { #[inline(always)] fn byte_order(&self) -> ByteOrder { self.byte_order } } /// Reader that unpacks Apple's `PackBits` format pub struct PackBitsReader { buffer: io::Cursor>, byte_order: ByteOrder, } impl PackBitsReader { /// Wraps a reader pub fn new( mut reader: R, byte_order: ByteOrder, length: usize, ) -> io::Result<(usize, PackBitsReader)> { let mut buffer = Vec::new(); let mut read: usize = 0; while read < length { let lread = read_packbits_run(&mut reader, &mut buffer)?; if lread == 0 { return Err(io::ErrorKind::UnexpectedEof.into()); } read += lread; } Ok(( buffer.len(), PackBitsReader { buffer: io::Cursor::new(buffer), byte_order, }, )) } } fn read_packbits_run(reader: &mut R, buffer: &mut Vec) -> io::Result { let mut header: [u8; 1] = [0]; let bytes = reader.read(&mut header)?; match bytes { 0 => Ok(0), _ => match header[0] as i8 { -128 => Ok(1), h if h >= -127 && h <= -1 => { let new_len = buffer.len() + (1 - h as isize) as usize; reader.read_exact(&mut header)?; buffer.resize(new_len, header[0]); Ok(2) } h => { let num_vals = h as usize + 1; let start = buffer.len(); buffer.resize(start + num_vals, 0); reader.read_exact(&mut buffer[start..])?; Ok(num_vals + 1) } }, } } impl Read for PackBitsReader { #[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result { self.buffer.read(buf) } } impl EndianReader for PackBitsReader { #[inline(always)] fn byte_order(&self) -> ByteOrder { self.byte_order } } /// Reader that is aware of the byte order. #[derive(Debug)] pub struct SmartReader where R: Read + Seek, { reader: R, pub byte_order: ByteOrder, } impl SmartReader where R: Read + Seek, { /// Wraps a reader pub fn wrap(reader: R, byte_order: ByteOrder) -> SmartReader { SmartReader { reader, byte_order } } } impl EndianReader for SmartReader where R: Read + Seek, { #[inline(always)] fn byte_order(&self) -> ByteOrder { self.byte_order } } impl Read for SmartReader { #[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result { self.reader.read(buf) } } impl Seek for SmartReader { #[inline] fn seek(&mut self, pos: io::SeekFrom) -> io::Result { self.reader.seek(pos) } } #[cfg(test)] mod test { use super::*; #[test] fn test_packbits() { let encoded = vec![ 0xFE, 0xAA, 0x02, 0x80, 0x00, 0x2A, 0xFD, 0xAA, 0x03, 0x80, 0x00, 0x2A, 0x22, 0xF7, 0xAA, ]; let encoded_len = encoded.len(); let buff = io::Cursor::new(encoded); let (_, mut decoder) = PackBitsReader::new(buff, ByteOrder::LittleEndian, encoded_len).unwrap(); let mut decoded = Vec::new(); decoder.read_to_end(&mut decoded).unwrap(); let expected = vec![ 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0x22, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, ]; assert_eq!(decoded, expected); } } tiff-0.5.0/src/encoder/colortype.rs010064400017500001750000000073521366254333000154750ustar0000000000000000use tags::PhotometricInterpretation; /// Trait for different colortypes that can be encoded. pub trait ColorType { /// The type of each sample of this colortype type Inner: super::TiffValue; /// The value of the tiff tag `PhotometricInterpretation` const TIFF_VALUE: PhotometricInterpretation; /// The value of the tiff tag `BitsPerSample` const BITS_PER_SAMPLE: &'static [u16]; } pub struct Gray8; impl ColorType for Gray8 { type Inner = u8; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero; const BITS_PER_SAMPLE: &'static [u16] = &[8]; } pub struct Gray16; impl ColorType for Gray16 { type Inner = u16; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero; const BITS_PER_SAMPLE: &'static [u16] = &[16]; } pub struct Gray32; impl ColorType for Gray32 { type Inner = u32; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero; const BITS_PER_SAMPLE: &'static [u16] = &[32]; } pub struct Gray64; impl ColorType for Gray64 { type Inner = u64; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero; const BITS_PER_SAMPLE: &'static [u16] = &[64]; } pub struct RGB8; impl ColorType for RGB8 { type Inner = u8; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8]; } pub struct RGB16; impl ColorType for RGB16 { type Inner = u16; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16]; } pub struct RGB32; impl ColorType for RGB32 { type Inner = u32; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32]; } pub struct RGB64; impl ColorType for RGB64 { type Inner = u64; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64]; } pub struct RGBA8; impl ColorType for RGBA8 { type Inner = u8; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8]; } pub struct RGBA16; impl ColorType for RGBA16 { type Inner = u16; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16]; } pub struct RGBA32; impl ColorType for RGBA32 { type Inner = u16; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32]; } pub struct RGBA64; impl ColorType for RGBA64 { type Inner = u16; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64]; } pub struct CMYK8; impl ColorType for CMYK8 { type Inner = u8; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK; const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8]; } pub struct CMYK16; impl ColorType for CMYK16 { type Inner = u16; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK; const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16]; } pub struct CMYK32; impl ColorType for CMYK32 { type Inner = u32; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK; const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32]; } pub struct CMYK64; impl ColorType for CMYK64 { type Inner = u64; const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK; const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64]; } tiff-0.5.0/src/encoder/mod.rs010064400017500001750000000465341366254333000142410ustar0000000000000000use byteorder::NativeEndian; use std::collections::BTreeMap; use std::convert::TryFrom; use std::io::{Seek, Write}; use std::mem; use tags::{self, ResolutionUnit, Tag, Type}; use error::{TiffError, TiffFormatError, TiffResult}; pub mod colortype; mod writer; use self::colortype::*; use self::writer::*; /// Type to represent tiff values of type `RATIONAL` #[derive(Clone)] pub struct Rational { pub n: u32, pub d: u32, } /// Type to represent tiff values of type `SRATIONAL` pub struct SRational { pub n: i32, pub d: i32, } /// Trait for types that can be encoded in a tiff file pub trait TiffValue { const BYTE_LEN: u32; const FIELD_TYPE: Type; fn count(&self) -> u32; fn bytes(&self) -> u32 { self.count() * Self::BYTE_LEN } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()>; } impl TiffValue for [u8] { const BYTE_LEN: u32 = 1; const FIELD_TYPE: Type = Type::BYTE; fn count(&self) -> u32 { self.len() as u32 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { writer.write_bytes(self)?; Ok(()) } } impl TiffValue for [i8] { const BYTE_LEN: u32 = 1; const FIELD_TYPE: Type = Type::SBYTE; fn count(&self) -> u32 { self.len() as u32 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { // We write using nativeedian so this should be safe let slice = unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len()) }; writer.write_bytes(slice)?; Ok(()) } } impl TiffValue for [u16] { const BYTE_LEN: u32 = 2; const FIELD_TYPE: Type = Type::SHORT; fn count(&self) -> u32 { self.len() as u32 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { // We write using nativeedian so this sould be safe let slice = unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 2) }; writer.write_bytes(slice)?; Ok(()) } } impl TiffValue for [i16] { const BYTE_LEN: u32 = 2; const FIELD_TYPE: Type = Type::SSHORT; fn count(&self) -> u32 { self.len() as u32 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { // We write using nativeedian so this should be safe let slice = unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) }; writer.write_bytes(slice)?; Ok(()) } } impl TiffValue for [u32] { const BYTE_LEN: u32 = 4; const FIELD_TYPE: Type = Type::LONG; fn count(&self) -> u32 { self.len() as u32 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { // We write using nativeedian so this sould be safe let slice = unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 4) }; writer.write_bytes(slice)?; Ok(()) } } impl TiffValue for [i32] { const BYTE_LEN: u32 = 4; const FIELD_TYPE: Type = Type::SLONG; fn count(&self) -> u32 { self.len() as u32 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { // We write using nativeedian so this should be safe let slice = unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) }; writer.write_bytes(slice)?; Ok(()) } } impl TiffValue for [u64] { const BYTE_LEN: u32 = 8; const FIELD_TYPE: Type = Type::LONG8; fn count(&self) -> u32 { self.len() as u32 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { // We write using nativeedian so this sould be safe let slice = unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 8) }; writer.write_bytes(slice)?; Ok(()) } } impl TiffValue for [Rational] { const BYTE_LEN: u32 = 8; const FIELD_TYPE: Type = Type::RATIONAL; fn count(&self) -> u32 { self.len() as u32 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { for x in self { x.write(writer)?; } Ok(()) } } impl TiffValue for [SRational] { const BYTE_LEN: u32 = 8; const FIELD_TYPE: Type = Type::SRATIONAL; fn count(&self) -> u32 { self.len() as u32 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { for x in self { x.write(writer)?; } Ok(()) } } impl TiffValue for u8 { const BYTE_LEN: u32 = 1; const FIELD_TYPE: Type = Type::BYTE; fn count(&self) -> u32 { 1 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { writer.write_u8(*self)?; Ok(()) } } impl TiffValue for i8 { const BYTE_LEN: u32 = 1; const FIELD_TYPE: Type = Type::SBYTE; fn count(&self) -> u32 { 1 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { writer.write_i8(*self)?; Ok(()) } } impl TiffValue for u16 { const BYTE_LEN: u32 = 2; const FIELD_TYPE: Type = Type::SHORT; fn count(&self) -> u32 { 1 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { writer.write_u16(*self)?; Ok(()) } } impl TiffValue for i16 { const BYTE_LEN: u32 = 2; const FIELD_TYPE: Type = Type::SSHORT; fn count(&self) -> u32 { 1 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { writer.write_i16(*self)?; Ok(()) } } impl TiffValue for u32 { const BYTE_LEN: u32 = 4; const FIELD_TYPE: Type = Type::LONG; fn count(&self) -> u32 { 1 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { writer.write_u32(*self)?; Ok(()) } } impl TiffValue for i32 { const BYTE_LEN: u32 = 4; const FIELD_TYPE: Type = Type::SLONG; fn count(&self) -> u32 { 1 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { writer.write_i32(*self)?; Ok(()) } } impl TiffValue for u64 { const BYTE_LEN: u32 = 8; const FIELD_TYPE: Type = Type::LONG8; fn count(&self) -> u32 { 1 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { writer.write_u64(*self)?; Ok(()) } } impl TiffValue for Rational { const BYTE_LEN: u32 = 8; const FIELD_TYPE: Type = Type::RATIONAL; fn count(&self) -> u32 { 1 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { writer.write_u32(self.n)?; writer.write_u32(self.d)?; Ok(()) } } impl TiffValue for SRational { const BYTE_LEN: u32 = 8; const FIELD_TYPE: Type = Type::SRATIONAL; fn count(&self) -> u32 { 1 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { writer.write_i32(self.n)?; writer.write_i32(self.d)?; Ok(()) } } impl TiffValue for str { const BYTE_LEN: u32 = 1; const FIELD_TYPE: Type = Type::ASCII; fn count(&self) -> u32 { self.len() as u32 + 1 } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { if self.is_ascii() && !self.bytes().any(|b| b == 0) { writer.write_bytes(self.as_bytes())?; writer.write_u8(0)?; Ok(()) } else { Err(TiffError::FormatError(TiffFormatError::InvalidTag)) } } } impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T { const BYTE_LEN: u32 = T::BYTE_LEN; const FIELD_TYPE: Type = T::FIELD_TYPE; fn count(&self) -> u32 { (*self).count() } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { (*self).write(writer) } } /// Tiff encoder. /// /// With this type you can get a `DirectoryEncoder` or a `ImageEncoder` /// to encode tiff ifd directories with images. /// /// See `DirectoryEncoder` and `ImageEncoder`. /// /// # Examples /// ``` /// # extern crate tiff; /// # fn main() { /// # let mut file = std::io::Cursor::new(Vec::new()); /// # let image_data = vec![0; 100*100*3]; /// use tiff::encoder::*; /// /// let mut tiff = TiffEncoder::new(&mut file).unwrap(); /// /// tiff.write_image::(100, 100, &image_data).unwrap(); /// # } /// ``` pub struct TiffEncoder { writer: TiffWriter, } impl TiffEncoder { pub fn new(writer: W) -> TiffResult> { let mut encoder = TiffEncoder { writer: TiffWriter::new(writer), }; NativeEndian::write_header(&mut encoder.writer)?; // blank the IFD offset location encoder.writer.write_u32(0)?; Ok(encoder) } /// Create a `DirectoryEncoder` to encode an ifd directory. pub fn new_directory(&mut self) -> TiffResult> { DirectoryEncoder::new(&mut self.writer) } /// Create an 'ImageEncoder' to encode an image one slice at a time. pub fn new_image( &mut self, width: u32, height: u32, ) -> TiffResult> { let encoder = DirectoryEncoder::new(&mut self.writer)?; ImageEncoder::new(encoder, width, height) } /// Convenience function to write an entire image from memory. pub fn write_image( &mut self, width: u32, height: u32, data: &[C::Inner], ) -> TiffResult<()> where [C::Inner]: TiffValue, { let num_pix = usize::try_from(width)?.checked_mul(usize::try_from(height)?) .ok_or_else(|| ::std::io::Error::new( ::std::io::ErrorKind::InvalidInput, "Image width * height exceeds usize"))?; if data.len() < num_pix { return Err(::std::io::Error::new( ::std::io::ErrorKind::InvalidData, "Input data slice is undersized for provided dimensions").into()); } let encoder = DirectoryEncoder::new(&mut self.writer)?; let mut image: ImageEncoder = ImageEncoder::new(encoder, width, height)?; let mut idx = 0; while image.next_strip_sample_count() > 0 { let sample_count = usize::try_from(image.next_strip_sample_count())?; image.write_strip(&data[idx..idx + sample_count])?; idx += sample_count; } image.finish() } } /// Low level interface to encode ifd directories. /// /// You should call `finish` on this when you are finished with it. /// Encoding can silently fail while this is dropping. pub struct DirectoryEncoder<'a, W: 'a + Write + Seek> { writer: &'a mut TiffWriter, dropped: bool, // We use BTreeMap to make sure tags are written in correct order ifd_pointer_pos: u64, ifd: BTreeMap)>, } impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> { fn new(writer: &'a mut TiffWriter) -> TiffResult> { // the previous word is the IFD offset position let ifd_pointer_pos = writer.offset() - mem::size_of::() as u64; writer.pad_word_boundary()?; Ok(DirectoryEncoder { writer, dropped: false, ifd_pointer_pos, ifd: BTreeMap::new(), }) } /// Write a single ifd tag. pub fn write_tag(&mut self, tag: Tag, value: T) -> TiffResult<()> { let len = ::BYTE_LEN * value.count(); let mut bytes = Vec::with_capacity(usize::try_from(len)?); { let mut writer = TiffWriter::new(&mut bytes); value.write(&mut writer)?; } self.ifd .insert(tag.to_u16(), (::FIELD_TYPE.to_u16(), value.count(), bytes)); Ok(()) } fn write_directory(&mut self) -> TiffResult { // Start by writing out all values for &mut (_, _, ref mut bytes) in self.ifd.values_mut() { if bytes.len() > 4 { let offset = self.writer.offset(); self.writer.write_bytes(bytes)?; *bytes = vec![0, 0, 0, 0]; let mut writer = TiffWriter::new(bytes as &mut [u8]); writer.write_u32(u32::try_from(offset)?)?; } else { while bytes.len() < 4 { bytes.push(0); } } } let offset = self.writer.offset(); self.writer.write_u16(u16::try_from(self.ifd.len())?)?; for (tag, &(ref field_type, ref count, ref offset)) in self.ifd.iter() { self.writer.write_u16(*tag)?; self.writer.write_u16(*field_type)?; self.writer.write_u32(*count)?; self.writer.write_bytes(offset)?; } Ok(offset) } /// Write some data to the tiff file, the offset of the data is returned. /// /// This could be used to write tiff strips. pub fn write_data(&mut self, value: T) -> TiffResult { let offset = self.writer.offset(); value.write(&mut self.writer)?; Ok(offset) } fn finish_internal(&mut self) -> TiffResult<()> { let ifd_pointer = self.write_directory()?; let curr_pos = self.writer.offset(); self.writer.goto_offset(self.ifd_pointer_pos)?; self.writer.write_u32(u32::try_from(ifd_pointer)?)?; self.writer.goto_offset(curr_pos)?; self.writer.write_u32(0)?; self.dropped = true; Ok(()) } /// Write out the ifd directory. pub fn finish(mut self) -> TiffResult<()> { self.finish_internal() } } impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> { fn drop(&mut self) { if !self.dropped { let _ = self.finish_internal(); } } } /// Type to encode images strip by strip. /// /// You should call `finish` on this when you are finished with it. /// Encoding can silently fail while this is dropping. /// /// # Examples /// ``` /// # extern crate tiff; /// # fn main() { /// # let mut file = std::io::Cursor::new(Vec::new()); /// # let image_data = vec![0; 100*100*3]; /// use tiff::encoder::*; /// /// let mut tiff = TiffEncoder::new(&mut file).unwrap(); /// let mut image = tiff.new_image::(100, 100).unwrap(); /// /// let mut idx = 0; /// while image.next_strip_sample_count() > 0 { /// let sample_count = image.next_strip_sample_count() as usize; /// image.write_strip(&image_data[idx..idx+sample_count]).unwrap(); /// idx += sample_count; /// } /// image.finish().unwrap(); /// # } /// ``` pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType> { encoder: DirectoryEncoder<'a, W>, strip_idx: u64, strip_count: u64, row_samples: u64, height: u32, rows_per_strip: u64, strip_offsets: Vec, strip_byte_count: Vec, dropped: bool, _phantom: ::std::marker::PhantomData, } impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> { fn new( mut encoder: DirectoryEncoder<'a, W>, width: u32, height: u32, ) -> TiffResult> { let row_samples = u64::from(width) * u64::try_from(::BITS_PER_SAMPLE.len())?; let row_bytes = row_samples * u64::from(::BYTE_LEN); // As per tiff spec each strip should be about 8k long let rows_per_strip = (8000 + row_bytes - 1) / row_bytes; let strip_count = (u64::from(height) + rows_per_strip - 1) / rows_per_strip; encoder.write_tag(Tag::ImageWidth, width)?; encoder.write_tag(Tag::ImageLength, height)?; encoder.write_tag(Tag::Compression, tags::CompressionMethod::None.to_u16())?; encoder.write_tag(Tag::BitsPerSample, ::BITS_PER_SAMPLE)?; encoder.write_tag(Tag::PhotometricInterpretation, ::TIFF_VALUE.to_u16())?; encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?; encoder.write_tag(Tag::SamplesPerPixel, u16::try_from(::BITS_PER_SAMPLE.len())?)?; encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?; encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 })?; encoder.write_tag(Tag::ResolutionUnit, ResolutionUnit::None.to_u16())?; Ok(ImageEncoder { encoder, strip_count, strip_idx: 0, row_samples, rows_per_strip, height, strip_offsets: Vec::new(), strip_byte_count: Vec::new(), dropped: false, _phantom: ::std::marker::PhantomData, }) } /// Number of samples the next strip should have. pub fn next_strip_sample_count(&self) -> u64 { if self.strip_idx >= self.strip_count { return 0; } let start_row = ::std::cmp::min(u64::from(self.height), self.strip_idx * self.rows_per_strip); let end_row = ::std::cmp::min( u64::from(self.height), (self.strip_idx + 1) * self.rows_per_strip, ); (end_row - start_row) * self.row_samples } /// Write a single strip. pub fn write_strip(&mut self, value: &[T::Inner]) -> TiffResult<()> where [T::Inner]: TiffValue, { // TODO: Compression let samples = self.next_strip_sample_count(); if u64::try_from(value.len())? != samples { return Err(::std::io::Error::new( ::std::io::ErrorKind::InvalidData, "Slice is wrong size for strip").into()); } let offset = self.encoder.write_data(value)?; self.strip_offsets.push(u32::try_from(offset)?); self.strip_byte_count.push(value.bytes()); self.strip_idx += 1; Ok(()) } /// Set image resolution pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) { self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap(); self.encoder.write_tag(Tag::XResolution, value.clone()).unwrap(); self.encoder.write_tag(Tag::YResolution, value).unwrap(); } /// Set image resolution unit pub fn resolution_unit(&mut self, unit: ResolutionUnit) { self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap(); } /// Set image x-resolution pub fn x_resolution(&mut self, value: Rational) { self.encoder.write_tag(Tag::XResolution, value).unwrap(); } /// Set image y-resolution pub fn y_resolution(&mut self, value: Rational) { self.encoder.write_tag(Tag::YResolution, value).unwrap(); } fn finish_internal(&mut self) -> TiffResult<()> { self.encoder .write_tag(Tag::StripOffsets, &*self.strip_offsets)?; self.encoder .write_tag(Tag::StripByteCounts, &*self.strip_byte_count)?; self.dropped = true; self.encoder.finish_internal() } /// Get a reference of the underlying `DirectoryEncoder` pub fn encoder(&mut self) -> &mut DirectoryEncoder<'a, W> { &mut self.encoder } /// Write out image and ifd directory. pub fn finish(mut self) -> TiffResult<()> { self.finish_internal() } } impl<'a, W: Write + Seek, C: ColorType> Drop for ImageEncoder<'a, W, C> { fn drop(&mut self) { if !self.dropped { let _ = self.finish_internal(); } } } tiff-0.5.0/src/encoder/writer.rs010064400017500001750000000055271356505474300150020ustar0000000000000000use error::TiffResult; use byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian, WriteBytesExt}; use std::io::{self, Seek, SeekFrom, Write}; pub trait TiffByteOrder: ByteOrder { fn write_header(writer: &mut TiffWriter) -> TiffResult<()>; } impl TiffByteOrder for LittleEndian { fn write_header(writer: &mut TiffWriter) -> TiffResult<()> { writer.writer.write_u16::(0x4949)?; writer.writer.write_u16::(42)?; writer.offset += 4; Ok(()) } } impl TiffByteOrder for BigEndian { fn write_header(writer: &mut TiffWriter) -> TiffResult<()> { writer.writer.write_u16::(0x4d4d)?; writer.writer.write_u16::(42)?; writer.offset += 4; Ok(()) } } pub struct TiffWriter { writer: W, offset: u64, } impl TiffWriter { pub fn new(writer: W) -> Self { Self { writer, offset: 0 } } pub fn offset(&self) -> u64 { self.offset } pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), io::Error> { self.writer.write_all(bytes)?; self.offset += bytes.len() as u64; Ok(()) } pub fn write_u8(&mut self, n: u8) -> Result<(), io::Error> { self.writer.write_u8(n)?; self.offset += 1; Ok(()) } pub fn write_i8(&mut self, n: i8) -> Result<(), io::Error> { self.writer.write_i8(n)?; self.offset += 1; Ok(()) } pub fn write_u16(&mut self, n: u16) -> Result<(), io::Error> { self.writer.write_u16::(n)?; self.offset += 2; Ok(()) } pub fn write_i16(&mut self, n: i16) -> Result<(), io::Error> { self.writer.write_i16::(n)?; self.offset += 2; Ok(()) } pub fn write_u32(&mut self, n: u32) -> Result<(), io::Error> { self.writer.write_u32::(n)?; self.offset += 4; Ok(()) } pub fn write_i32(&mut self, n: i32) -> Result<(), io::Error> { self.writer.write_i32::(n)?; self.offset += 4; Ok(()) } pub fn write_u64(&mut self, n: u64) -> Result<(), io::Error> { self.writer.write_u64::(n)?; self.offset += 8; Ok(()) } pub fn pad_word_boundary(&mut self) -> Result<(), io::Error> { if self.offset % 4 != 0 { let padding = [0, 0, 0]; let padd_len = 4 - (self.offset % 4); self.writer.write_all(&padding[..padd_len as usize])?; self.offset += padd_len; } Ok(()) } } impl TiffWriter { pub fn goto_offset(&mut self, offset: u64) -> Result<(), io::Error> { self.offset = offset; self.writer.seek(SeekFrom::Start(offset as u64))?; Ok(()) } } tiff-0.5.0/src/error.rs010064400017500001750000000201331366254333000131570ustar0000000000000000use std::error::Error; use std::fmt; use std::io; use std::string; use decoder::ifd::{Value}; use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Tag}; use miniz_oxide::inflate::TINFLStatus; use ColorType; /// Tiff error kinds. #[derive(Debug)] pub enum TiffError { /// The Image is not formatted properly. FormatError(TiffFormatError), /// The Decoder does not support features required by the image. UnsupportedError(TiffUnsupportedError), /// An I/O Error occurred while decoding the image. IoError(io::Error), /// The Limits of the Decoder is exceeded. LimitsExceeded, /// An integer conversion to or from a platform size failed, either due to /// limits of the platform size or limits of the format. IntSizeError, } /// The image is not formatted properly. /// /// This indicates that the encoder producing the image might behave incorrectly or that the input /// file has been corrupted. /// /// The list of variants may grow to incorporate errors of future features. Matching against this /// exhaustively is not covered by interface stability guarantees. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum TiffFormatError { TiffSignatureNotFound, TiffSignatureInvalid, ImageFileDirectoryNotFound, InconsistentSizesEncountered, InvalidTag, InvalidTagValueType(Tag), RequiredTagNotFound(Tag), UnknownPredictor(u16), UnsignedIntegerExpected(Value), SignedIntegerExpected(Value), InflateError(InflateError), #[doc(hidden)] /// Do not match against this variant. It may get removed. __NonExhaustive, } impl fmt::Display for TiffFormatError { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { use self::TiffFormatError::*; match *self { TiffSignatureNotFound => write!(fmt, "TIFF signature not found."), TiffSignatureInvalid => write!(fmt, "TIFF signature invalid."), ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."), InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."), InvalidTag => write!(fmt, "Image contains invalid tag."), InvalidTagValueType(ref tag) => write!(fmt, "Tag `{:?}` did not have the expected value type.", tag), RequiredTagNotFound(ref tag) => write!(fmt, "Required tag `{:?}` not found.", tag), UnknownPredictor(ref predictor) => { write!(fmt, "Unknown predictor “{}” encountered", predictor) } UnsignedIntegerExpected(ref val) => { write!(fmt, "Expected unsigned integer, {:?} found.", val) } SignedIntegerExpected(ref val) => { write!(fmt, "Expected signed integer, {:?} found.", val) } InflateError(_) => write!(fmt, "Failed to decode inflate data."), __NonExhaustive => unreachable!(), } } } /// Decompression failed due to faulty compressed data. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct InflateError { status: TINFLStatus, } impl InflateError { pub(crate) 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))) } } /// The Decoder does not support features required by the image. /// /// This only captures known failures for which the standard either does not require support or an /// implementation has been planned but not yet completed. Some variants may become unused over /// time and will then get deprecated before being removed. /// /// The list of variants may grow. Matching against this exhaustively is not covered by interface /// stability guarantees. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum TiffUnsupportedError { HorizontalPredictor(ColorType), InterpretationWithBits(PhotometricInterpretation, Vec), UnknownInterpretation, UnknownCompressionMethod, UnsupportedCompressionMethod(CompressionMethod), UnsupportedSampleDepth(u8), UnsupportedColorType(ColorType), UnsupportedBitsPerChannel(u8), UnsupportedPlanarConfig(Option), UnsupportedDataType, #[doc(hidden)] /// Do not match against this variant. It may get removed. __NonExhaustive, } impl fmt::Display for TiffUnsupportedError { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { use self::TiffUnsupportedError::*; match *self { HorizontalPredictor(color_type) => write!( fmt, "Horizontal predictor for {:?} is unsupported.", color_type ), InterpretationWithBits(ref photometric_interpretation, ref bits_per_sample) => write!( fmt, "{:?} with {:?} bits per sample is unsupported", photometric_interpretation, bits_per_sample ), UnknownInterpretation => write!( fmt, "The image is using an unknown photometric interpretation." ), UnknownCompressionMethod => write!(fmt, "Unknown compression method."), UnsupportedCompressionMethod(method) => { write!(fmt, "Compression method {:?} is unsupported", method) } UnsupportedSampleDepth(samples) => { write!(fmt, "{} samples per pixel is unsupported.", samples) } UnsupportedColorType(color_type) => { write!(fmt, "Color type {:?} is unsupported", color_type) } UnsupportedBitsPerChannel(bits) => { write!(fmt, "{} bits per channel not supported", bits) } UnsupportedPlanarConfig(config) => { write!(fmt, "Unsupported planar configuration “{:?}”.", config) } UnsupportedDataType => write!(fmt, "Unsupported data type."), __NonExhaustive => unreachable!(), } } } impl fmt::Display for TiffError { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { match *self { TiffError::FormatError(ref e) => write!(fmt, "Format error: {}", e), TiffError::UnsupportedError(ref f) => write!( fmt, "The Decoder does not support the \ image format `{}`", f ), TiffError::IoError(ref e) => e.fmt(fmt), TiffError::LimitsExceeded => write!(fmt, "The Decoder limits are exceeded"), TiffError::IntSizeError => write!(fmt, "Platform or format size limits exceeded"), } } } impl Error for TiffError { fn description(&self) -> &str { match *self { TiffError::FormatError(..) => "Format error", TiffError::UnsupportedError(..) => "Unsupported error", TiffError::IoError(..) => "IO error", TiffError::LimitsExceeded => "Decoder limits exceeded", TiffError::IntSizeError => "Platform or format size limits exceeded", } } fn cause(&self) -> Option<&dyn Error> { match *self { TiffError::IoError(ref e) => Some(e), _ => None, } } } impl From for TiffError { fn from(err: io::Error) -> TiffError { TiffError::IoError(err) } } impl From for TiffError { fn from(_err: string::FromUtf8Error) -> TiffError { TiffError::FormatError(TiffFormatError::InvalidTag) } } impl From for TiffError { fn from(err: TiffFormatError) -> TiffError { TiffError::FormatError(err) } } impl From for TiffError { fn from(err: TiffUnsupportedError) -> TiffError { TiffError::UnsupportedError(err) } } impl From for TiffError { fn from(_err: std::num::TryFromIntError) -> TiffError { TiffError::IntSizeError } } /// Result of an image decoding/encoding process pub type TiffResult = Result; tiff-0.5.0/src/lib.rs010064400017500001750000000017111366254333000125750ustar0000000000000000//! Decoding and Encoding of TIFF Images //! //! TIFF (Tagged Image File Format) is a versatile image format that supports //! lossless and lossy compression. //! //! # Related Links //! * - The TIFF specification extern crate byteorder; extern crate lzw; extern crate miniz_oxide; pub mod decoder; pub mod encoder; mod error; pub mod tags; pub use self::error::{InflateError, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError}; /// An enumeration over supported color types and their bit depths #[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)] pub enum ColorType { /// Pixel is grayscale Gray(u8), /// Pixel contains R, G and B channels RGB(u8), /// Pixel is an index into a color palette Palette(u8), /// Pixel is grayscale with an alpha channel GrayA(u8), /// Pixel is RGB with an alpha channel RGBA(u8), /// Pixel is CMYK CMYK(u8), } tiff-0.5.0/src/tags.rs010064400017500001750000000110441366254333000127650ustar0000000000000000macro_rules! tags { { // Permit arbitrary meta items, which include documentation. $( #[$enum_attr:meta] )* $vis:vis enum $name:ident($ty:tt) $(unknown($unknown_doc:literal))* { // Each of the `Name = Val,` permitting documentation. $($(#[$ident_attr:meta])* $tag:ident = $val:expr,)* } } => { $( #[$enum_attr] )* #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub enum $name { $($(#[$ident_attr])* $tag,)* // FIXME: switch to non_exhaustive once stabilized and compiler requirement new enough #[doc(hidden)] __NonExhaustive, $( #[doc = $unknown_doc] Unknown($ty), )* } impl $name { #[inline(always)] fn __from_inner_type(n: $ty) -> Result { match n { $( $val => Ok($name::$tag), )* n => Err(n), } } #[inline(always)] fn __to_inner_type(&self) -> $ty { match *self { $( $name::$tag => $val, )* $( $name::Unknown(n) => { $unknown_doc; n }, )* $name::__NonExhaustive => unreachable!(), } } } tags!($name, $ty); }; // For u16 tags, provide direct inherent primitive conversion methods. ($name:tt, u16) => { impl $name { #[inline(always)] pub fn from_u16(val: u16) -> Option { Self::__from_inner_type(val).ok() } #[inline(always)] pub fn to_u16(&self) -> u16 { Self::__to_inner_type(self) } } }; // For other tag types, do nothing for now. With concat_idents one could // provide inherent conversion methods for all types. ($name:tt, $ty:tt) => {}; } // Note: These tags appear in the order they are mentioned in the TIFF reference tags! { /// TIFF tags pub enum Tag(u16) unknown("A private or extension tag") { // Baseline tags: Artist = 315, // grayscale images PhotometricInterpretation 1 or 3 BitsPerSample = 258, CellLength = 265, // TODO add support CellWidth = 264, // TODO add support // palette-color images (PhotometricInterpretation 3) ColorMap = 320, // TODO add support Compression = 259, // TODO add support for 2 and 32773 Copyright = 33_432, DateTime = 306, ExtraSamples = 338, // TODO add support FillOrder = 266, // TODO add support FreeByteCounts = 289, // TODO add support FreeOffsets = 288, // TODO add support GrayResponseCurve = 291, // TODO add support GrayResponseUnit = 290, // TODO add support HostComputer = 316, ImageDescription = 270, ImageLength = 257, ImageWidth = 256, Make = 271, MaxSampleValue = 281, // TODO add support MinSampleValue = 280, // TODO add support Model = 272, NewSubfileType = 254, // TODO add support Orientation = 274, // TODO add support PhotometricInterpretation = 262, PlanarConfiguration = 284, ResolutionUnit = 296, // TODO add support RowsPerStrip = 278, SamplesPerPixel = 277, Software = 305, StripByteCounts = 279, StripOffsets = 273, SubfileType = 255, // TODO add support Threshholding = 263, // TODO add support XResolution = 282, YResolution = 283, // Advanced tags Predictor = 317, } } impl Tag { pub fn from_u16_exhaustive(val: u16) -> Self { Self::from_u16(val).unwrap_or_else(|| Tag::Unknown(val)) } } tags! { /// The type of an IFD entry (a 2 byte field). pub enum Type(u16) { BYTE = 1, ASCII = 2, SHORT = 3, LONG = 4, RATIONAL = 5, SBYTE = 6, SSHORT = 8, SLONG = 9, SRATIONAL = 10, /// BigTIFF 64-bit unsigned integer LONG8 = 16, } } tags! { pub enum CompressionMethod(u16) { None = 1, Huffman = 2, Fax3 = 3, Fax4 = 4, LZW = 5, JPEG = 6, Deflate = 8, OldDeflate = 0x80B2, PackBits = 0x8005, } } tags! { pub enum PhotometricInterpretation(u16) { WhiteIsZero = 0, BlackIsZero = 1, RGB = 2, RGBPalette = 3, TransparencyMask = 4, CMYK = 5, YCbCr = 6, CIELab = 8, } } tags! { pub enum PlanarConfiguration(u16) { Chunky = 1, Planar = 2, } } tags! { pub enum Predictor(u16) { None = 1, Horizontal = 2, } } tags!{ /// Type to represent resolution units pub enum ResolutionUnit(u16) { None = 1, Inch = 2, Centimeter = 3, } }tiff-0.5.0/tests/decode_images.rs010064400017500001750000000123151366254333000151540ustar0000000000000000extern crate tiff; use tiff::decoder::{ifd, Decoder, DecodingResult}; use tiff::ColorType; use std::fs::File; use std::path::PathBuf; const TEST_IMAGE_DIR: &str = "./tests/images/"; macro_rules! test_image_sum { ($name:ident, $buffer:ident, $sum_ty:ty) => { fn $name(file: &str, expected_type: ColorType, expected_sum: $sum_ty) { let path = PathBuf::from(TEST_IMAGE_DIR).join(file); let img_file = File::open(path).expect("Cannot find test image!"); let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); assert_eq!(decoder.colortype().unwrap(), expected_type); let img_res = decoder.read_image().unwrap(); match img_res { DecodingResult::$buffer(res) => { let sum: $sum_ty = res.into_iter().map(<$sum_ty>::from).sum(); assert_eq!(sum, expected_sum); }, _ => panic!("Wrong bit depth"), } } }; } test_image_sum!(test_image_sum_u8, U8, u64); test_image_sum!(test_image_sum_u16, U16, u64); test_image_sum!(test_image_sum_u32, U32, u64); test_image_sum!(test_image_sum_u64, U64, u64); /// Tests that a decoder can be constructed for an image and the color type /// read from the IFD and is of the appropriate type, but the type is /// unsupported. fn test_image_color_type_unsupported(file: &str, expected_type: ColorType) { let path = PathBuf::from(TEST_IMAGE_DIR).join(file); let img_file = File::open(path).expect("Cannot find test image!"); let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); assert_eq!(decoder.colortype().unwrap(), expected_type); assert!(match decoder.read_image() { Err(tiff::TiffError::UnsupportedError(tiff::TiffUnsupportedError::UnsupportedColorType(_))) => true, _ => false, }); } #[test] fn test_cmyk_u8() { test_image_sum_u8("cmyk-3c-8b.tiff", ColorType::CMYK(8), 8522658); } #[test] fn test_cmyk_u16() { test_image_sum_u16("cmyk-3c-16b.tiff", ColorType::CMYK(16), 2181426827); } #[test] fn test_gray_u8() { test_image_sum_u8("minisblack-1c-8b.tiff", ColorType::Gray(8), 2840893); } #[test] fn test_gray_u12() { test_image_color_type_unsupported("12bit.cropped.tiff", ColorType::Gray(12)); } #[test] fn test_gray_u16() { test_image_sum_u16("minisblack-1c-16b.tiff", ColorType::Gray(16), 733126239); } #[test] fn test_gray_u32() { test_image_sum_u32("gradient-1c-32b.tiff", ColorType::Gray(32), 549892913787); } #[test] fn test_gray_u64() { test_image_sum_u64("gradient-1c-64b.tiff", ColorType::Gray(64), 549892913787); } #[test] fn test_rgb_u8() { test_image_sum_u8("rgb-3c-8b.tiff", ColorType::RGB(8), 7842108); } #[test] fn test_rgb_u12() { test_image_color_type_unsupported("12bit.cropped.rgb.tiff", ColorType::RGB(12)); } #[test] fn test_rgb_u16() { test_image_sum_u16("rgb-3c-16b.tiff", ColorType::RGB(16), 2024349944); } #[test] fn test_rgb_u32() { test_image_sum_u32("gradient-3c-32b.tiff", ColorType::RGB(32), 2030834111716); } #[test] fn test_rgb_u64() { test_image_sum_u64("gradient-3c-64b.tiff", ColorType::RGB(64), 2030834111716); } #[test] fn test_string_tags() { // these files have null-terminated strings for their Software tag. One has extra bytes after // the null byte, so we check both to ensure that we're truncating properly let filenames = ["minisblack-1c-16b.tiff", "rgb-3c-16b.tiff"]; for filename in filenames.iter() { let path = PathBuf::from(TEST_IMAGE_DIR).join(filename); let img_file = File::open(path).expect("Cannot find test image!"); let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); let software = decoder.get_tag(tiff::tags::Tag::Software).unwrap(); match software { ifd::Value::Ascii(s) => assert_eq!( &s, "GraphicsMagick 1.2 unreleased Q16 http://www.GraphicsMagick.org/" ), _ => assert!(false), }; } } #[test] fn test_decode_data() { let mut image_data = Vec::new(); for x in 0..100 { for y in 0..100u8 { let val = x + y; image_data.push(val); image_data.push(val); image_data.push(val); } } let file = File::open("./tests/decodedata-rgb-3c-8b.tiff").unwrap(); let mut decoder = Decoder::new(file).unwrap(); assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8)); assert_eq!(decoder.dimensions().unwrap(), (100, 100)); if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() { assert_eq!(image_data, img_res); } else { panic!("Wrong data type"); } } #[test] fn issue_69() { test_image_sum_u16("issue_69_lzw.tiff", ColorType::Gray(16), 1015486); test_image_sum_u16("issue_69_packbits.tiff", ColorType::Gray(16), 1015486); } // TODO: GrayA support //#[test] //fn test_gray_alpha_u8() //{ //let img_file = File::open("./tests/images/minisblack-2c-8b-alpha.tiff").expect("Cannot find test image!"); //let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); //assert_eq!(decoder.colortype().unwrap(), ColorType::GrayA(8)); //let img_res = decoder.read_image(); //assert!(img_res.is_ok()); //} tiff-0.5.0/tests/decodedata-rgb-3c-8b.tiff010064400017500001750000000727441344723122100163550ustar0000000000000000II* ddd0u(  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbccc  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccddd  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeee  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefff  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggg  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhh  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiii  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjj  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkk  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklll  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmm  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnn  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooo  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnoooppp !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqq !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrr !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsss !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuu !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvv !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwww !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxx !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyy !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{ !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{||| !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}} !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~ !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~ !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~ !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~ !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~ !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~!!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~)))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~BBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~CCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~DDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~EEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~FFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~GGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~HHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~IIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~JJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~KKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~LLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~MMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~NNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~OOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~PPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~QQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~RRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~SSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~TTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~UUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~VVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~WWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~XXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~YYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~ZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~bbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~cccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~tiff-0.5.0/tests/encode_images.rs010064400017500001750000000237161366254333000151750ustar0000000000000000extern crate tiff; use tiff::decoder::{Decoder, DecodingResult}; use tiff::encoder::{colortype, TiffEncoder, SRational}; use tiff::ColorType; use tiff::tags::Tag; use std::fs::File; use std::io::{Cursor, Seek, SeekFrom}; use std::path::PathBuf; #[test] fn encode_decode() { let mut image_data = Vec::new(); for x in 0..100 { for y in 0..100u8 { let val = x + y; image_data.push(val); image_data.push(val); image_data.push(val); } } let mut file = Cursor::new(Vec::new()); { let mut tiff = TiffEncoder::new(&mut file).unwrap(); tiff.write_image::(100, 100, &image_data) .unwrap(); } { file.seek(SeekFrom::Start(0)).unwrap(); let mut decoder = Decoder::new(&mut file).unwrap(); assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8)); assert_eq!(decoder.dimensions().unwrap(), (100, 100)); if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() { assert_eq!(image_data, img_res); } else { panic!("Wrong data type"); } } } #[test] /// Test that attempting to encode when the input buffer is undersized returns /// an error rather than panicking. /// See: https://github.com/PistonDevelopers/image-tiff/issues/35 fn test_encode_undersized_buffer() { let input_data = vec![1, 2, 3]; let output = Vec::new(); let mut output_stream = Cursor::new(output); if let Ok(mut tiff) = TiffEncoder::new(&mut output_stream) { let res = tiff.write_image::(50, 50, &input_data); assert!(res.is_err()); } } const TEST_IMAGE_DIR: &str = "./tests/images/"; macro_rules! test_roundtrip { ($name:ident, $buffer:ident, $buffer_ty:ty) => { fn $name>(file: &str, expected_type: ColorType) { let path = PathBuf::from(TEST_IMAGE_DIR).join(file); let img_file = File::open(path).expect("Cannot find test image!"); let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); assert_eq!(decoder.colortype().unwrap(), expected_type); let image_data = match decoder.read_image().unwrap() { DecodingResult::$buffer(res) => res, _ => panic!("Wrong data type"), }; let mut file = Cursor::new(Vec::new()); { let mut tiff = TiffEncoder::new(&mut file).unwrap(); let (width, height) = decoder.dimensions().unwrap(); tiff.write_image::(width, height, &image_data) .unwrap(); } file.seek(SeekFrom::Start(0)).unwrap(); { let mut decoder = Decoder::new(&mut file).unwrap(); if let DecodingResult::$buffer(img_res) = decoder.read_image().unwrap() { assert_eq!(image_data, img_res); } else { panic!("Wrong data type"); } } } }; } test_roundtrip!(test_u8_roundtrip, U8, u8); test_roundtrip!(test_u16_roundtrip, U16, u16); test_roundtrip!(test_u32_roundtrip, U32, u32); test_roundtrip!(test_u64_roundtrip, U64, u64); #[test] fn test_gray_u8_roundtrip() { test_u8_roundtrip::("minisblack-1c-8b.tiff", ColorType::Gray(8)); } #[test] fn test_rgb_u8_roundtrip() { test_u8_roundtrip::("rgb-3c-8b.tiff", ColorType::RGB(8)); } #[test] fn test_cmyk_u8_roundtrip() { test_u8_roundtrip::("cmyk-3c-8b.tiff", ColorType::CMYK(8)); } #[test] fn test_gray_u16_roundtrip() { test_u16_roundtrip::("minisblack-1c-16b.tiff", ColorType::Gray(16)); } #[test] fn test_rgb_u16_roundtrip() { test_u16_roundtrip::("rgb-3c-16b.tiff", ColorType::RGB(16)); } #[test] fn test_cmyk_u16_roundtrip() { test_u16_roundtrip::("cmyk-3c-16b.tiff", ColorType::CMYK(16)); } #[test] fn test_gray_u32_roundtrip() { test_u32_roundtrip::("gradient-1c-32b.tiff", ColorType::Gray(32)); } #[test] fn test_rgb_u32_roundtrip() { test_u32_roundtrip::("gradient-3c-32b.tiff", ColorType::RGB(32)); } #[test] fn test_gray_u64_roundtrip() { test_u64_roundtrip::("gradient-1c-64b.tiff", ColorType::Gray(64)); } #[test] fn test_rgb_u64_roundtrip() { test_u64_roundtrip::("gradient-3c-64b.tiff", ColorType::RGB(64)); } #[test] fn test_multiple_byte() { let mut data = Cursor::new(Vec::new()); { let mut tiff = TiffEncoder::new(&mut data).unwrap(); let mut image_encoder = tiff.new_image::(1, 1).unwrap(); let encoder = image_encoder.encoder(); encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]).unwrap(); encoder.write_tag(Tag::Unknown(65001), &[1_u8, 2][..]).unwrap(); encoder.write_tag(Tag::Unknown(65002), &[1_u8, 2, 3][..]).unwrap(); encoder.write_tag(Tag::Unknown(65003), &[1_u8, 2, 3, 4][..]).unwrap(); encoder.write_tag(Tag::Unknown(65004), &[1_u8, 2, 3, 4, 5][..]).unwrap(); } data.set_position(0); { let mut decoder = Decoder::new(&mut data).unwrap(); assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_u32_vec().unwrap(), [1]); assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_u32_vec().unwrap(), [1, 2]); assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_u32_vec().unwrap(), [1, 2, 3]); assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_u32_vec().unwrap(), [1, 2, 3, 4]); assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_u32_vec().unwrap(), [1, 2, 3, 4, 5]); } } #[test] /// Test writing signed tags from TIFF 6.0 fn test_signed() { let mut data = Cursor::new(Vec::new()); { let mut tiff = TiffEncoder::new(&mut data).unwrap(); let mut image_encoder = tiff.new_image::(1, 1).unwrap(); let encoder = image_encoder.encoder(); //Use the "reusable" tags section as per the TIFF6 spec encoder.write_tag(Tag::Unknown(65000), -1_i8).unwrap(); encoder.write_tag(Tag::Unknown(65001), &[-1_i8][..]).unwrap(); encoder.write_tag(Tag::Unknown(65002), &[-1_i8, 2][..]).unwrap(); encoder.write_tag(Tag::Unknown(65003), &[-1_i8, 2, -3][..]).unwrap(); encoder.write_tag(Tag::Unknown(65004), &[-1_i8, 2, -3, 4][..]).unwrap(); encoder.write_tag(Tag::Unknown(65005), &[-1_i8, 2, -3, 4, -5][..]).unwrap(); encoder.write_tag(Tag::Unknown(65010), -1_i16).unwrap(); encoder.write_tag(Tag::Unknown(65011), -1_i16).unwrap(); encoder.write_tag(Tag::Unknown(65012), &[-1_i16, 2][..]).unwrap(); encoder.write_tag(Tag::Unknown(65013), &[-1_i16, 2, -3][..]).unwrap(); encoder.write_tag(Tag::Unknown(65020), -1_i32).unwrap(); encoder.write_tag(Tag::Unknown(65021), &[-1_i32][..]).unwrap(); encoder.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..]).unwrap(); encoder.write_tag(Tag::Unknown(65030), SRational { n: -1, d: 100 }).unwrap(); encoder.write_tag(Tag::Unknown(65031), &[SRational { n: -1, d: 100 }, SRational { n: 2, d: 100 }][..]).unwrap(); } //Rewind the cursor for reading data.set_position(0); { let mut decoder = Decoder::new(&mut data).unwrap(); assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_i32().unwrap(), -1, ); assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_i32_vec().unwrap(), [-1]); assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_i32_vec().unwrap(), [-1, 2]); assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3]); assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3, 4]); assert_eq!(decoder.get_tag(Tag::Unknown(65005)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3, 4, -5], ); assert_eq!(decoder.get_tag(Tag::Unknown(65010)).unwrap().into_i32().unwrap(), -1); assert_eq!(decoder.get_tag(Tag::Unknown(65011)).unwrap().into_i32_vec().unwrap(), [-1]); assert_eq!(decoder.get_tag(Tag::Unknown(65012)).unwrap().into_i32_vec().unwrap(), [-1, 2]); assert_eq!(decoder.get_tag(Tag::Unknown(65013)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3]); assert_eq!(decoder.get_tag(Tag::Unknown(65020)).unwrap().into_i32().unwrap(), -1); assert_eq!(decoder.get_tag(Tag::Unknown(65021)).unwrap().into_i32_vec().unwrap(), [-1]); assert_eq!(decoder.get_tag(Tag::Unknown(65022)).unwrap().into_i32_vec().unwrap(), [-1, 2]); assert_eq!(decoder.get_tag(Tag::Unknown(65030)).unwrap().into_i32_vec().unwrap(), [-1, 100]); assert_eq!(decoder.get_tag(Tag::Unknown(65031)).unwrap().into_i32_vec().unwrap(), [-1_i32, 100, 2, 100]); } } #[test] /// check multipage image handling fn test_multipage_image() { let mut img_file = Cursor::new(Vec::new()); { // first create a multipage image with 2 images let mut img_encoder = TiffEncoder::new(&mut img_file).unwrap(); // write first grayscale image (2x2 16-bit) let img1: Vec = [1, 2, 3, 4].to_vec(); img_encoder.write_image::(2, 2, &img1[..]).unwrap(); // write second grayscale image (3x3 8-bit) let img2: Vec = [9, 8, 7, 6, 5, 4, 3, 2, 1].to_vec(); img_encoder.write_image::(3, 3, &img2[..]).unwrap(); } // seek to the beginning of the file, so that it can be decoded img_file.seek(SeekFrom::Start(0)).unwrap(); { let mut img_decoder = Decoder::new(&mut img_file).unwrap(); // check the dimensions of the image in the first page assert_eq!(img_decoder.dimensions().unwrap(), (2, 2)); img_decoder.next_image().unwrap(); // check the dimensions of the image in the second page assert_eq!(img_decoder.dimensions().unwrap(), (3, 3)); } } tiff-0.5.0/tests/fuzz_tests.rs010064400017500001750000000020411346042115400146120ustar0000000000000000extern crate tiff; use tiff::decoder::Decoder; use tiff::TiffResult; use std::fs::File; fn test_directory bool>(path: &str, f: F) { for entry in std::fs::read_dir(path).unwrap() { let file = File::open(entry.unwrap().path()).unwrap(); assert!(f(file)); } } fn decode_tiff(file: File) -> TiffResult<()> { let mut decoder = Decoder::new(file)?; decoder.read_image()?; Ok(()) } #[test] fn oor_panic() { test_directory("./tests/fuzz_images/oor_panic", |file| { let _ = decode_tiff(file); true }); } #[test] fn oom_crash() { test_directory("./tests/fuzz_images/oom_crash", |file| { decode_tiff(file).is_err() }); } #[test] fn inf_loop() { test_directory("./tests/fuzz_images/inf_loop", |file| { let _ = decode_tiff(file); true }); } // https://github.com/image-rs/image-tiff/issues/33 #[test] fn divide_by_zero() { test_directory("./tests/fuzz_images/divide_by_zero", |file| { let _ = decode_tiff(file); true }); } tiff-0.5.0/.cargo_vcs_info.json0000644000000001120000000000000120170ustar00{ "git": { "sha1": "ee73341bbf7ac279223829c430a73cc40d1a3de8" } }