Compare commits

..
1 Commits
Author SHA1 Message Date
Atul Bhosale 0dedc32e9b Format code using 'cargo fmt' 2020-03-17 22:13:58 +05:30
12 changed files with 370 additions and 168 deletions

No files matched your search

-9
View File
@@ -1,12 +1,3 @@
# 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 # Version 0.4.0
* Several enumerations are now non_exhaustive for future extensions. * Several enumerations are now non_exhaustive for future extensions.
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "tiff" name = "tiff"
version = "0.5.0" version = "0.4.0"
authors = [ authors = [
"ccgn", "ccgn",
"bvssvni <bvssvni@gmail.com>", "bvssvni <bvssvni@gmail.com>",
+17 -17
View File
@@ -9,8 +9,7 @@ use super::stream::{ByteOrder, EndianReader, SmartReader};
use tags::{Tag, Type}; use tags::{Tag, Type};
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError}; use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational}; use self::Value::{Ascii, List, Rational, SRational, Signed, Unsigned};
#[allow(unused_qualifications)] #[allow(unused_qualifications)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -72,7 +71,7 @@ impl Value {
new_vec.push(numerator); new_vec.push(numerator);
new_vec.push(denominator); new_vec.push(denominator);
} }
_ => new_vec.push(v.into_i32()?) _ => new_vec.push(v.into_i32()?),
} }
} }
Ok(new_vec) Ok(new_vec)
@@ -182,16 +181,10 @@ impl Entry {
Ok(SRational(numerator, denominator)) Ok(SRational(numerator, denominator))
} }
(Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { (Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Rational( Ok(Rational(decoder.read_long()?, decoder.read_long()?))
decoder.read_long()?,
decoder.read_long()?,
))
}), }),
(Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { (Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(SRational( Ok(SRational(decoder.read_slong()?, decoder.read_slong()?))
decoder.read_slong()?,
decoder.read_slong()?,
))
}), }),
(Type::ASCII, n) => { (Type::ASCII, n) => {
let n = usize::try_from(n)?; let n = usize::try_from(n)?;
@@ -209,10 +202,17 @@ impl Entry {
} }
#[inline] #[inline]
fn decode_offset<R, F>(&self, value_count: u32, bo: ByteOrder, limits: &super::Limits, decoder: &mut super::Decoder<R>, decode_fn: F) -> TiffResult<Value> fn decode_offset<R, F>(
where &self,
R: Read + Seek, value_count: u32,
F: Fn(&mut super::Decoder<R>) -> TiffResult<Value>, bo: ByteOrder,
limits: &super::Limits,
decoder: &mut super::Decoder<R>,
decode_fn: F,
) -> TiffResult<Value>
where
R: Read + Seek,
F: Fn(&mut super::Decoder<R>) -> TiffResult<Value>,
{ {
let value_count = usize::try_from(value_count)?; let value_count = usize::try_from(value_count)?;
if value_count > limits.decoding_buffer_size / mem::size_of::<Value>() { if value_count > limits.decoding_buffer_size / mem::size_of::<Value>() {
@@ -235,7 +235,7 @@ fn offset_to_bytes(n: usize, entry: &Entry) -> TiffResult<Value> {
entry.offset[0..n] entry.offset[0..n]
.iter() .iter()
.map(|&e| Unsigned(u32::from(e))) .map(|&e| Unsigned(u32::from(e)))
.collect() .collect(),
)) ))
} }
@@ -246,7 +246,7 @@ fn offset_to_sbytes(n: usize, entry: &Entry) -> TiffResult<Value> {
entry.offset[0..n] entry.offset[0..n]
.iter() .iter()
.map(|&e| Signed(i32::from(e as i8))) .map(|&e| Signed(i32::from(e as i8)))
.collect() .collect(),
)) ))
} }
+72 -63
View File
@@ -1,14 +1,16 @@
use std::convert::TryFrom;
use std::collections::HashMap;
use std::io::{self, Read, Seek};
use std::cmp; use std::cmp;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::io::{self, Read, Seek};
use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError}; use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use self::ifd::Directory; use self::ifd::Directory;
use tags::{CompressionMethod, PhotometricInterpretation, Predictor, Tag, Type}; use tags::{CompressionMethod, PhotometricInterpretation, Predictor, Tag, Type};
use self::stream::{ByteOrder, EndianReader, LZWReader, DeflateReader, PackBitsReader, SmartReader}; use self::stream::{
ByteOrder, DeflateReader, EndianReader, LZWReader, PackBitsReader, SmartReader,
};
pub mod ifd; pub mod ifd;
mod stream; mod stream;
@@ -217,8 +219,14 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType)
let samples = match color_type { let samples = match color_type {
ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) | ColorType::Gray(64) => 1, 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::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::RGBA(8)
| ColorType::CMYK(8) | ColorType::CMYK(16) | ColorType::CMYK(32) | ColorType::CMYK(64) => 4, | 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( return Err(TiffError::UnsupportedError(
TiffUnsupportedError::HorizontalPredictor(color_type), TiffUnsupportedError::HorizontalPredictor(color_type),
@@ -273,28 +281,24 @@ impl<R: Read + Seek> Decoder<R> {
pub fn colortype(&mut self) -> TiffResult<ColorType> { pub fn colortype(&mut self) -> TiffResult<ColorType> {
match self.photometric_interpretation { match self.photometric_interpretation {
PhotometricInterpretation::RGB => { PhotometricInterpretation::RGB => match self.bits_per_sample[..] {
match self.bits_per_sample[..] { [r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)),
[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)),
[r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)), _ => Err(TiffError::UnsupportedError(
_ => Err(TiffError::UnsupportedError( TiffUnsupportedError::InterpretationWithBits(
TiffUnsupportedError::InterpretationWithBits( self.photometric_interpretation,
self.photometric_interpretation, self.bits_per_sample.clone(),
self.bits_per_sample.clone(), ),
), )),
)),
}
}, },
PhotometricInterpretation::CMYK => { PhotometricInterpretation::CMYK => match self.bits_per_sample[..] {
match self.bits_per_sample[..] { [c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)),
[c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)), _ => Err(TiffError::UnsupportedError(
_ => Err(TiffError::UnsupportedError( TiffUnsupportedError::InterpretationWithBits(
TiffUnsupportedError::InterpretationWithBits( self.photometric_interpretation,
self.photometric_interpretation, self.bits_per_sample.clone(),
self.bits_per_sample.clone(), ),
), )),
)),
}
}, },
PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero
if self.bits_per_sample.len() == 1 => if self.bits_per_sample.len() == 1 =>
@@ -371,9 +375,11 @@ impl<R: Read + Seek> Decoder<R> {
self.samples = val; self.samples = val;
} }
match self.samples { match self.samples {
1 | 3 | 4 => if let Some(val) = self.find_tag_unsigned_vec(Tag::BitsPerSample)? { 1 | 3 | 4 => {
self.bits_per_sample = val; if let Some(val) = self.find_tag_unsigned_vec(Tag::BitsPerSample)? {
}, self.bits_per_sample = val;
}
}
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()), _ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()),
} }
Ok(()) Ok(())
@@ -516,20 +522,30 @@ impl<R: Read + Seek> Decoder<R> {
/// Tries to retrieve a tag and convert it to the desired unsigned type. /// Tries to retrieve a tag and convert it to the desired unsigned type.
pub fn find_tag_unsigned<T: TryFrom<u32>>(&mut self, tag: Tag) -> TiffResult<Option<T>> { pub fn find_tag_unsigned<T: TryFrom<u32>>(&mut self, tag: Tag) -> TiffResult<Option<T>> {
self.find_tag(tag)? self.find_tag(tag)?
.map(|v| v.into_u32()).transpose()? .map(|v| v.into_u32())
.map(|value| T::try_from(value) .transpose()?
.map_err(|_| TiffFormatError::InvalidTagValueType(tag).into())) .map(|value| {
T::try_from(value).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into())
})
.transpose() .transpose()
} }
/// Tries to retrieve a vector of all a tag's values and convert them to /// Tries to retrieve a vector of all a tag's values and convert them to
/// the desired unsigned type. /// the desired unsigned type.
pub fn find_tag_unsigned_vec<T: TryFrom<u32>>(&mut self, tag: Tag) -> TiffResult<Option<Vec<T>>> { pub fn find_tag_unsigned_vec<T: TryFrom<u32>>(
&mut self,
tag: Tag,
) -> TiffResult<Option<Vec<T>>> {
self.find_tag(tag)? self.find_tag(tag)?
.map(|v| v.into_u32_vec()).transpose()? .map(|v| v.into_u32_vec())
.map(|v| v.into_iter() .transpose()?
.map(|u| T::try_from(u).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into())) .map(|v| {
.collect()) v.into_iter()
.map(|u| {
T::try_from(u).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into())
})
.collect()
})
.transpose() .transpose()
} }
@@ -584,21 +600,19 @@ impl<R: Read + Seek> Decoder<R> {
let (bytes, reader) = LZWReader::new( let (bytes, reader) = LZWReader::new(
&mut self.reader, &mut self.reader,
usize::try_from(length)?, usize::try_from(length)?,
max_uncompressed_length * buffer.byte_len() max_uncompressed_length,
)?; )?;
(bytes, Box::new(reader)) (bytes, Box::new(reader))
} }
CompressionMethod::PackBits => { CompressionMethod::PackBits => {
let order = self.reader.byte_order; let order = self.reader.byte_order;
let (bytes, reader) = PackBitsReader::new( let (bytes, reader) =
&mut self.reader, PackBitsReader::new(&mut self.reader, order, usize::try_from(length)?)?;
order,
usize::try_from(length)?,
)?;
(bytes, Box::new(reader)) (bytes, Box::new(reader))
} }
CompressionMethod::OldDeflate => { CompressionMethod::OldDeflate => {
let (bytes, reader) = DeflateReader::new(&mut self.reader, max_uncompressed_length)?; let (bytes, reader) =
DeflateReader::new(&mut self.reader, max_uncompressed_length)?;
(bytes, Box::new(reader)) (bytes, Box::new(reader))
} }
method => { method => {
@@ -685,9 +699,7 @@ impl<R: Read + Seek> Decoder<R> {
/// Number of strips in image /// Number of strips in image
pub fn strip_count(&mut self) -> TiffResult<u32> { pub fn strip_count(&mut self) -> TiffResult<u32> {
let rows_per_strip = self let rows_per_strip = self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height);
.get_tag_u32(Tag::RowsPerStrip)
.unwrap_or(self.height);
if rows_per_strip == 0 { if rows_per_strip == 0 {
return Ok(0); return Ok(0);
@@ -733,9 +745,8 @@ impl<R: Read + Seek> Decoder<R> {
TiffFormatError::InconsistentSizesEncountered, TiffFormatError::InconsistentSizesEncountered,
))?; ))?;
let rows_per_strip = usize::try_from(self let rows_per_strip =
.get_tag_u32(Tag::RowsPerStrip) usize::try_from(self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height))?;
.unwrap_or(self.height))?;
let strip_height = cmp::min( let strip_height = cmp::min(
rows_per_strip, rows_per_strip,
@@ -744,7 +755,9 @@ impl<R: Read + Seek> Decoder<R> {
let buffer_size = usize::try_from(self.width)? * strip_height * self.bits_per_sample.len(); let buffer_size = usize::try_from(self.width)? * strip_height * self.bits_per_sample.len();
if buffer.len() < buffer_size { if buffer.len() < buffer_size
|| usize::try_from(byte_count)? / buffer.byte_len() > buffer_size
{
return Err(TiffError::FormatError( return Err(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered, TiffFormatError::InconsistentSizesEncountered,
)); ));
@@ -792,11 +805,9 @@ impl<R: Read + Seek> Decoder<R> {
n if n <= 16 => DecodingResult::new_u16(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 <= 32 => DecodingResult::new_u32(buffer_size, &self.limits),
n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits), n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits),
n => { n => Err(TiffError::UnsupportedError(
Err(TiffError::UnsupportedError( TiffUnsupportedError::UnsupportedBitsPerChannel(n),
TiffUnsupportedError::UnsupportedBitsPerChannel(n), )),
))
}
} }
} }
@@ -805,9 +816,8 @@ impl<R: Read + Seek> Decoder<R> {
self.initialize_strip_decoder()?; self.initialize_strip_decoder()?;
let index = self.strip_decoder.as_ref().unwrap().strip_index; let index = self.strip_decoder.as_ref().unwrap().strip_index;
let rows_per_strip = usize::try_from(self let rows_per_strip =
.get_tag_u32(Tag::RowsPerStrip) usize::try_from(self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height))?;
.unwrap_or(self.height))?;
let strip_height = cmp::min( let strip_height = cmp::min(
rows_per_strip, rows_per_strip,
@@ -824,9 +834,8 @@ impl<R: Read + Seek> Decoder<R> {
/// Decodes the entire image and return it as a Vector /// Decodes the entire image and return it as a Vector
pub fn read_image(&mut self) -> TiffResult<DecodingResult> { pub fn read_image(&mut self) -> TiffResult<DecodingResult> {
self.initialize_strip_decoder()?; self.initialize_strip_decoder()?;
let rows_per_strip = usize::try_from(self let rows_per_strip =
.get_tag_u32(Tag::RowsPerStrip) usize::try_from(self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height))?;
.unwrap_or(self.height))?;
let samples_per_strip = let samples_per_strip =
usize::try_from(self.width)? * rows_per_strip * self.bits_per_sample.len(); usize::try_from(self.width)? * rows_per_strip * self.bits_per_sample.len();
+47 -20
View File
@@ -4,8 +4,8 @@ use std::convert::TryFrom;
use std::io::{Seek, Write}; use std::io::{Seek, Write};
use std::mem; use std::mem;
use tags::{self, ResolutionUnit, Tag, Type};
use error::{TiffError, TiffFormatError, TiffResult}; use error::{TiffError, TiffFormatError, TiffResult};
use tags::{self, ResolutionUnit, Tag, Type};
pub mod colortype; pub mod colortype;
mod writer; mod writer;
@@ -61,8 +61,7 @@ impl TiffValue for [i8] {
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> { fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this should be safe // We write using nativeedian so this should be safe
let slice = let slice = unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len()) };
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len()) };
writer.write_bytes(slice)?; writer.write_bytes(slice)?;
Ok(()) Ok(())
} }
@@ -95,8 +94,12 @@ impl TiffValue for [i16] {
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> { fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this should be safe // We write using nativeedian so this should be safe
let slice = let slice = unsafe {
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) }; ::std::slice::from_raw_parts(
self.as_ptr() as *const u8,
self.len() * Self::BYTE_LEN as usize,
)
};
writer.write_bytes(slice)?; writer.write_bytes(slice)?;
Ok(()) Ok(())
} }
@@ -129,8 +132,12 @@ impl TiffValue for [i32] {
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> { fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this should be safe // We write using nativeedian so this should be safe
let slice = let slice = unsafe {
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) }; ::std::slice::from_raw_parts(
self.as_ptr() as *const u8,
self.len() * Self::BYTE_LEN as usize,
)
};
writer.write_bytes(slice)?; writer.write_bytes(slice)?;
Ok(()) Ok(())
} }
@@ -407,14 +414,20 @@ impl<W: Write + Seek> TiffEncoder<W> {
where where
[C::Inner]: TiffValue, [C::Inner]: TiffValue,
{ {
let num_pix = usize::try_from(width)?.checked_mul(usize::try_from(height)?) let num_pix = usize::try_from(width)?
.ok_or_else(|| ::std::io::Error::new( .checked_mul(usize::try_from(height)?)
::std::io::ErrorKind::InvalidInput, .ok_or_else(|| {
"Image width * height exceeds usize"))?; ::std::io::Error::new(
::std::io::ErrorKind::InvalidInput,
"Image width * height exceeds usize",
)
})?;
if data.len() < num_pix { if data.len() < num_pix {
return Err(::std::io::Error::new( return Err(::std::io::Error::new(
::std::io::ErrorKind::InvalidData, ::std::io::ErrorKind::InvalidData,
"Input data slice is undersized for provided dimensions").into()); "Input data slice is undersized for provided dimensions",
)
.into());
} }
let encoder = DirectoryEncoder::new(&mut self.writer)?; let encoder = DirectoryEncoder::new(&mut self.writer)?;
@@ -464,8 +477,10 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
value.write(&mut writer)?; value.write(&mut writer)?;
} }
self.ifd self.ifd.insert(
.insert(tag.to_u16(), (<T>::FIELD_TYPE.to_u16(), value.count(), bytes)); tag.to_u16(),
(<T>::FIELD_TYPE.to_u16(), value.count(), bytes),
);
Ok(()) Ok(())
} }
@@ -597,7 +612,10 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?; encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?;
encoder.write_tag(Tag::SamplesPerPixel, u16::try_from(<T>::BITS_PER_SAMPLE.len())?)?; encoder.write_tag(
Tag::SamplesPerPixel,
u16::try_from(<T>::BITS_PER_SAMPLE.len())?,
)?;
encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?; encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?;
encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 })?; encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 })?;
encoder.write_tag(Tag::ResolutionUnit, ResolutionUnit::None.to_u16())?; encoder.write_tag(Tag::ResolutionUnit, ResolutionUnit::None.to_u16())?;
@@ -622,7 +640,8 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
return 0; return 0;
} }
let start_row = ::std::cmp::min(u64::from(self.height), self.strip_idx * self.rows_per_strip); let start_row =
::std::cmp::min(u64::from(self.height), self.strip_idx * self.rows_per_strip);
let end_row = ::std::cmp::min( let end_row = ::std::cmp::min(
u64::from(self.height), u64::from(self.height),
(self.strip_idx + 1) * self.rows_per_strip, (self.strip_idx + 1) * self.rows_per_strip,
@@ -641,7 +660,9 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
if u64::try_from(value.len())? != samples { if u64::try_from(value.len())? != samples {
return Err(::std::io::Error::new( return Err(::std::io::Error::new(
::std::io::ErrorKind::InvalidData, ::std::io::ErrorKind::InvalidData,
"Slice is wrong size for strip").into()); "Slice is wrong size for strip",
)
.into());
} }
let offset = self.encoder.write_data(value)?; let offset = self.encoder.write_data(value)?;
@@ -654,14 +675,20 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
/// Set image resolution /// Set image resolution
pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) { pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) {
self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap(); self.encoder
self.encoder.write_tag(Tag::XResolution, value.clone()).unwrap(); .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(); self.encoder.write_tag(Tag::YResolution, value).unwrap();
} }
/// Set image resolution unit /// Set image resolution unit
pub fn resolution_unit(&mut self, unit: ResolutionUnit) { pub fn resolution_unit(&mut self, unit: ResolutionUnit) {
self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap(); self.encoder
.write_tag(Tag::ResolutionUnit, unit.to_u16())
.unwrap();
} }
/// Set image x-resolution /// Set image x-resolution
+1 -1
View File
@@ -1,5 +1,5 @@
use error::TiffResult;
use byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian, WriteBytesExt}; use byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian, WriteBytesExt};
use error::TiffResult;
use std::io::{self, Seek, SeekFrom, Write}; use std::io::{self, Seek, SeekFrom, Write};
pub trait TiffByteOrder: ByteOrder { pub trait TiffByteOrder: ByteOrder {
+5 -4
View File
@@ -3,9 +3,9 @@ use std::fmt;
use std::io; use std::io;
use std::string; use std::string;
use decoder::ifd::{Value}; use decoder::ifd::Value;
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Tag};
use miniz_oxide::inflate::TINFLStatus; use miniz_oxide::inflate::TINFLStatus;
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Tag};
use ColorType; use ColorType;
/// Tiff error kinds. /// Tiff error kinds.
@@ -62,8 +62,9 @@ impl fmt::Display for TiffFormatError {
ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."), ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."),
InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."), InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."),
InvalidTag => write!(fmt, "Image contains invalid tag."), InvalidTag => write!(fmt, "Image contains invalid tag."),
InvalidTagValueType(ref tag) => InvalidTagValueType(ref tag) => {
write!(fmt, "Tag `{:?}` did not have the expected value type.", tag), write!(fmt, "Tag `{:?}` did not have the expected value type.", tag)
}
RequiredTagNotFound(ref tag) => write!(fmt, "Required tag `{:?}` not found.", tag), RequiredTagNotFound(ref tag) => write!(fmt, "Required tag `{:?}` not found.", tag),
UnknownPredictor(ref predictor) => { UnknownPredictor(ref predictor) => {
write!(fmt, "Unknown predictor “{}” encountered", predictor) write!(fmt, "Unknown predictor “{}” encountered", predictor)
+2 -3
View File
@@ -131,7 +131,6 @@ pub enum Type(u16) {
} }
} }
tags! { tags! {
pub enum CompressionMethod(u16) { pub enum CompressionMethod(u16) {
None = 1, None = 1,
@@ -173,11 +172,11 @@ pub enum Predictor(u16) {
} }
} }
tags!{ tags! {
/// Type to represent resolution units /// Type to represent resolution units
pub enum ResolutionUnit(u16) { pub enum ResolutionUnit(u16) {
None = 1, None = 1,
Inch = 2, Inch = 2,
Centimeter = 3, Centimeter = 3,
} }
} }
+4 -8
View File
@@ -21,7 +21,7 @@ macro_rules! test_image_sum {
DecodingResult::$buffer(res) => { DecodingResult::$buffer(res) => {
let sum: $sum_ty = res.into_iter().map(<$sum_ty>::from).sum(); let sum: $sum_ty = res.into_iter().map(<$sum_ty>::from).sum();
assert_eq!(sum, expected_sum); assert_eq!(sum, expected_sum);
}, }
_ => panic!("Wrong bit depth"), _ => panic!("Wrong bit depth"),
} }
} }
@@ -42,7 +42,9 @@ fn test_image_color_type_unsupported(file: &str, expected_type: ColorType) {
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
assert_eq!(decoder.colortype().unwrap(), expected_type); assert_eq!(decoder.colortype().unwrap(), expected_type);
assert!(match decoder.read_image() { assert!(match decoder.read_image() {
Err(tiff::TiffError::UnsupportedError(tiff::TiffUnsupportedError::UnsupportedColorType(_))) => true, Err(tiff::TiffError::UnsupportedError(
tiff::TiffUnsupportedError::UnsupportedColorType(_),
)) => true,
_ => false, _ => false,
}); });
} }
@@ -149,12 +151,6 @@ fn test_decode_data() {
} }
} }
#[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 // TODO: GrayA support
//#[test] //#[test]
//fn test_gray_alpha_u8() //fn test_gray_alpha_u8()
+221 -42
View File
@@ -1,9 +1,9 @@
extern crate tiff; extern crate tiff;
use tiff::decoder::{Decoder, DecodingResult}; use tiff::decoder::{Decoder, DecodingResult};
use tiff::encoder::{colortype, TiffEncoder, SRational}; use tiff::encoder::{colortype, SRational, TiffEncoder};
use tiff::ColorType;
use tiff::tags::Tag; use tiff::tags::Tag;
use tiff::ColorType;
use std::fs::File; use std::fs::File;
use std::io::{Cursor, Seek, SeekFrom}; use std::io::{Cursor, Seek, SeekFrom};
@@ -58,7 +58,10 @@ const TEST_IMAGE_DIR: &str = "./tests/images/";
macro_rules! test_roundtrip { macro_rules! test_roundtrip {
($name:ident, $buffer:ident, $buffer_ty:ty) => { ($name:ident, $buffer:ident, $buffer_ty:ty) => {
fn $name<C: colortype::ColorType<Inner=$buffer_ty>>(file: &str, expected_type: ColorType) { fn $name<C: colortype::ColorType<Inner = $buffer_ty>>(
file: &str,
expected_type: ColorType,
) {
let path = PathBuf::from(TEST_IMAGE_DIR).join(file); let path = PathBuf::from(TEST_IMAGE_DIR).join(file);
let img_file = File::open(path).expect("Cannot find test image!"); let img_file = File::open(path).expect("Cannot find test image!");
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
@@ -74,8 +77,7 @@ macro_rules! test_roundtrip {
let mut tiff = TiffEncoder::new(&mut file).unwrap(); let mut tiff = TiffEncoder::new(&mut file).unwrap();
let (width, height) = decoder.dimensions().unwrap(); let (width, height) = decoder.dimensions().unwrap();
tiff.write_image::<C>(width, height, &image_data) tiff.write_image::<C>(width, height, &image_data).unwrap();
.unwrap();
} }
file.seek(SeekFrom::Start(0)).unwrap(); file.seek(SeekFrom::Start(0)).unwrap();
{ {
@@ -155,21 +157,64 @@ fn test_multiple_byte() {
let encoder = image_encoder.encoder(); let encoder = image_encoder.encoder();
encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]).unwrap(); encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]).unwrap();
encoder.write_tag(Tag::Unknown(65001), &[1_u8, 2][..]).unwrap(); encoder
encoder.write_tag(Tag::Unknown(65002), &[1_u8, 2, 3][..]).unwrap(); .write_tag(Tag::Unknown(65001), &[1_u8, 2][..])
encoder.write_tag(Tag::Unknown(65003), &[1_u8, 2, 3, 4][..]).unwrap(); .unwrap();
encoder.write_tag(Tag::Unknown(65004), &[1_u8, 2, 3, 4, 5][..]).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); data.set_position(0);
{ {
let mut decoder = Decoder::new(&mut data).unwrap(); let mut decoder = Decoder::new(&mut data).unwrap();
assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_u32_vec().unwrap(), [1]); assert_eq!(
assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_u32_vec().unwrap(), [1, 2]); decoder
assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_u32_vec().unwrap(), [1, 2, 3]); .get_tag(Tag::Unknown(65000))
assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_u32_vec().unwrap(), [1, 2, 3, 4]); .unwrap()
assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_u32_vec().unwrap(), [1, 2, 3, 4, 5]); .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]
);
} }
} }
@@ -185,23 +230,48 @@ fn test_signed() {
//Use the "reusable" tags section as per the TIFF6 spec //Use the "reusable" tags section as per the TIFF6 spec
encoder.write_tag(Tag::Unknown(65000), -1_i8).unwrap(); encoder.write_tag(Tag::Unknown(65000), -1_i8).unwrap();
encoder.write_tag(Tag::Unknown(65001), &[-1_i8][..]).unwrap(); encoder
encoder.write_tag(Tag::Unknown(65002), &[-1_i8, 2][..]).unwrap(); .write_tag(Tag::Unknown(65001), &[-1_i8][..])
encoder.write_tag(Tag::Unknown(65003), &[-1_i8, 2, -3][..]).unwrap(); .unwrap();
encoder.write_tag(Tag::Unknown(65004), &[-1_i8, 2, -3, 4][..]).unwrap(); encoder
encoder.write_tag(Tag::Unknown(65005), &[-1_i8, 2, -3, 4, -5][..]).unwrap(); .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(65010), -1_i16).unwrap();
encoder.write_tag(Tag::Unknown(65011), -1_i16).unwrap(); encoder.write_tag(Tag::Unknown(65011), -1_i16).unwrap();
encoder.write_tag(Tag::Unknown(65012), &[-1_i16, 2][..]).unwrap(); encoder
encoder.write_tag(Tag::Unknown(65013), &[-1_i16, 2, -3][..]).unwrap(); .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(65020), -1_i32).unwrap();
encoder.write_tag(Tag::Unknown(65021), &[-1_i32][..]).unwrap(); encoder
encoder.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..]).unwrap(); .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
encoder.write_tag(Tag::Unknown(65031), &[SRational { n: -1, d: 100 }, SRational { n: 2, d: 100 }][..]).unwrap(); .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 //Rewind the cursor for reading
@@ -209,24 +279,129 @@ fn test_signed() {
{ {
let mut decoder = Decoder::new(&mut data).unwrap(); let mut decoder = Decoder::new(&mut data).unwrap();
assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_i32().unwrap(), -1, ); assert_eq!(
assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_i32_vec().unwrap(), [-1]); decoder
assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_i32_vec().unwrap(), [-1, 2]); .get_tag(Tag::Unknown(65000))
assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3]); .unwrap()
assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3, 4]); .into_i32()
assert_eq!(decoder.get_tag(Tag::Unknown(65005)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3, 4, -5], ); .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!(
assert_eq!(decoder.get_tag(Tag::Unknown(65011)).unwrap().into_i32_vec().unwrap(), [-1]); decoder
assert_eq!(decoder.get_tag(Tag::Unknown(65012)).unwrap().into_i32_vec().unwrap(), [-1, 2]); .get_tag(Tag::Unknown(65010))
assert_eq!(decoder.get_tag(Tag::Unknown(65013)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3]); .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!(
assert_eq!(decoder.get_tag(Tag::Unknown(65021)).unwrap().into_i32_vec().unwrap(), [-1]); decoder
assert_eq!(decoder.get_tag(Tag::Unknown(65022)).unwrap().into_i32_vec().unwrap(), [-1, 2]); .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!(
assert_eq!(decoder.get_tag(Tag::Unknown(65031)).unwrap().into_i32_vec().unwrap(), [-1_i32, 100, 2, 100]); 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]
);
} }
} }
@@ -241,10 +416,14 @@ fn test_multipage_image() {
// write first grayscale image (2x2 16-bit) // write first grayscale image (2x2 16-bit)
let img1: Vec<u16> = [1, 2, 3, 4].to_vec(); let img1: Vec<u16> = [1, 2, 3, 4].to_vec();
img_encoder.write_image::<colortype::Gray16>(2, 2, &img1[..]).unwrap(); img_encoder
.write_image::<colortype::Gray16>(2, 2, &img1[..])
.unwrap();
// write second grayscale image (3x3 8-bit) // write second grayscale image (3x3 8-bit)
let img2: Vec<u8> = [9, 8, 7, 6, 5, 4, 3, 2, 1].to_vec(); let img2: Vec<u8> = [9, 8, 7, 6, 5, 4, 3, 2, 1].to_vec();
img_encoder.write_image::<colortype::Gray8>(3, 3, &img2[..]).unwrap(); img_encoder
.write_image::<colortype::Gray8>(3, 3, &img2[..])
.unwrap();
} }
// seek to the beginning of the file, so that it can be decoded // seek to the beginning of the file, so that it can be decoded
Binary file not shown.
Binary file not shown.