Compare commits

..
Author SHA1 Message Date
Atul Bhosale 0ed3d90f19 Add cargo fmt to travis build config 2020-03-18 23:29:36 +05:30
16 changed files with 150 additions and 476 deletions

No files matched your search

+5
View File
@@ -4,3 +4,8 @@ rust:
- stable
- beta
- nightly
script:
- if [[ "$TRAVIS_RUST_VERSION" == *stable* ]]; then rustup component add rustfmt; cargo fmt --version; cargo fmt -- --check || (echo "Please reformat your code with 'cargo fmt' (version $(cargo fmt --version))"; false); fi
- cargo build -v
- cargo test -v
-18
View File
@@ -1,21 +1,3 @@
# Version 0.6.0-alpha
* Support for decoding BigTIFF with 64-bit offsets
* Better support for adding auxiliary tags before encoding image data
* Fixed decoding of inline ASCII in tags
* Fixed handling after null terminator in ASCII data
* Recognize tile and sample format tags
* Note: these notes will be merged into 0.6.0 when it is released
# 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.
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tiff"
version = "0.6.0-alpha"
version = "0.4.0"
authors = [
"ccgn",
"bvssvni <bvssvni@gmail.com>",
+88 -259
View File
@@ -1,7 +1,7 @@
//! Function for reading TIFF tags
use std::collections::HashMap;
use std::convert::{TryInto, TryFrom};
use std::convert::TryFrom;
use std::io::{self, Read, Seek};
use std::mem;
@@ -9,21 +9,17 @@ use super::stream::{ByteOrder, EndianReader, SmartReader};
use tags::{Tag, Type};
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational, RationalBig, UnsignedBig, SignedBig, SRationalBig};
use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational};
#[allow(unused_qualifications)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Value {
Signed(i32),
SignedBig(i64),
Unsigned(u32),
UnsignedBig(u64),
List(Vec<Value>),
Rational(u32, u32),
RationalBig(u64, u64),
SRational(i32, i32),
SRationalBig(i64, i64),
Ascii(String),
#[doc(hidden)] // Do not match against this.
__NonExhaustive,
@@ -33,7 +29,6 @@ impl Value {
pub fn into_u32(self) -> TiffResult<u32> {
match self {
Unsigned(val) => Ok(val),
UnsignedBig(val) => Ok(u32::try_from(val)?),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
@@ -43,27 +38,6 @@ impl Value {
pub fn into_i32(self) -> TiffResult<i32> {
match self {
Signed(val) => Ok(val),
SignedBig(val) => Ok(i32::try_from(val)?),
val => Err(TiffError::FormatError(
TiffFormatError::SignedIntegerExpected(val),
)),
}
}
pub fn into_u64(self) -> TiffResult<u64> {
match self {
Unsigned(val) => Ok(val.into()),
UnsignedBig(val) => Ok(val),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
}
}
pub fn into_i64(self) -> TiffResult<i64> {
match self {
Signed(val) => Ok(val.into()),
SignedBig(val) => Ok(val),
val => Err(TiffError::FormatError(
TiffFormatError::SignedIntegerExpected(val),
)),
@@ -80,9 +54,7 @@ impl Value {
Ok(new_vec)
}
Unsigned(val) => Ok(vec![val]),
UnsignedBig(val) => Ok(vec![u32::try_from(val)?]),
Rational(numerator, denominator) => Ok(vec![numerator, denominator]),
RationalBig(numerator, denominator) => Ok(vec![u32::try_from(numerator)?, u32::try_from(denominator)?]),
Ascii(val) => Ok(val.chars().map(u32::from).collect()),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
@@ -100,68 +72,13 @@ impl Value {
new_vec.push(numerator);
new_vec.push(denominator);
}
SRationalBig(numerator, denominator) => {
new_vec.push(i32::try_from(numerator)?);
new_vec.push(i32::try_from(denominator)?);
}
_ => new_vec.push(v.into_i32()?),
_ => new_vec.push(v.into_i32()?)
}
}
Ok(new_vec)
}
Signed(val) => Ok(vec![val]),
SignedBig(val) => Ok(vec![i32::try_from(val)?]),
SRational(numerator, denominator) => Ok(vec![numerator, denominator]),
SRationalBig(numerator, denominator) => Ok(vec![i32::try_from(numerator)?, i32::try_from(denominator)?]),
val => Err(TiffError::FormatError(
TiffFormatError::SignedIntegerExpected(val),
)),
}
}
pub fn into_u64_vec(self) -> TiffResult<Vec<u64>> {
match self {
List(vec) => {
let mut new_vec = Vec::with_capacity(vec.len());
for v in vec {
new_vec.push(v.into_u64()?)
}
Ok(new_vec)
}
Unsigned(val) => Ok(vec![val.into()]),
UnsignedBig(val) => Ok(vec![val]),
Rational(numerator, denominator) => Ok(vec![numerator.into(), denominator.into()]),
RationalBig(numerator, denominator) => Ok(vec![numerator, denominator]),
Ascii(val) => Ok(val.chars().map(u32::from).map(u64::from).collect()),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
}
}
pub fn into_i64_vec(self) -> TiffResult<Vec<i64>> {
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.into());
new_vec.push(denominator.into());
}
SRationalBig(numerator, denominator) => {
new_vec.push(numerator);
new_vec.push(denominator);
}
_ => new_vec.push(v.into_i64()?),
}
}
Ok(new_vec)
}
Signed(val) => Ok(vec![val.into()]),
SignedBig(val) => Ok(vec![val]),
SRational(numerator, denominator) => Ok(vec![numerator.into(), denominator.into()]),
SRationalBig(numerator, denominator) => Ok(vec![numerator, denominator]),
val => Err(TiffError::FormatError(
TiffFormatError::SignedIntegerExpected(val),
)),
@@ -172,8 +89,8 @@ impl Value {
#[derive(Clone)]
pub struct Entry {
type_: Type,
count: u64,
offset: [u8; 8],
count: u32,
offset: [u8; 4],
}
impl ::std::fmt::Debug for Entry {
@@ -187,12 +104,6 @@ impl ::std::fmt::Debug for Entry {
impl Entry {
pub fn new(type_: Type, count: u32, offset: [u8; 4]) -> Entry {
let mut offset = offset.to_vec();
offset.append(&mut vec![0; 4]);
Entry::new_u64(type_, count.into(), offset[..].try_into().unwrap())
}
pub fn new_u64(type_: Type, count: u64, offset: [u8; 8]) -> Entry {
Entry {
type_,
count,
@@ -211,172 +122,94 @@ impl Entry {
decoder: &mut super::Decoder<R>,
) -> TiffResult<Value> {
let bo = decoder.byte_order();
let bigtiff_value = if decoder.bigtiff {
match (self.type_, self.count) {
(Type::BYTE, 5) => Some(offset_to_bytes(5, self)?),
(Type::BYTE, 6) => Some(offset_to_bytes(6, self)?),
(Type::BYTE, 7) => Some(offset_to_bytes(7, self)?),
(Type::BYTE, 8) => Some(offset_to_bytes(8, self)?),
(Type::SBYTE, 5) => Some(offset_to_sbytes(5, self)?),
(Type::SBYTE, 6) => Some(offset_to_sbytes(6, self)?),
(Type::SBYTE, 7) => Some(offset_to_sbytes(7, self)?),
(Type::SBYTE, 8) => Some(offset_to_sbytes(8, self)?),
(Type::SHORT, 3) => {
let mut r = self.r(bo);
Some(List(vec![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
]))
}
(Type::SSHORT, 3) => {
let mut r = self.r(bo);
Some(List(vec![
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
]))
}
(Type::SHORT, 4) => {
let mut r = self.r(bo);
Some(List(vec![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
]))
}
(Type::SSHORT, 4) => {
let mut r = self.r(bo);
Some(List(vec![
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
]))
}
(Type::LONG, 2) => {
let mut r = self.r(bo);
Some(List(vec![
Unsigned(r.read_u32()?),
Unsigned(r.read_u32()?),
]))
}
(Type::SLONG, 2) => {
let mut r = self.r(bo);
Some(List(vec![
Signed(r.read_i32()?),
Signed(r.read_i32()?),
]))
}
(Type::RATIONAL, 1) => {
let mut r = self.r(bo);
let numerator = r.read_u32()?;
let denominator = r.read_u32()?;
Some(Rational(numerator, denominator))
}
(Type::SRATIONAL, 1) => {
let mut r = self.r(bo);
let numerator = r.read_i32()?;
let denominator = r.read_i32()?;
Some(SRational(numerator, denominator))
}
_ => None
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()?)),
]))
}
} else {
None
};
if let Some(v) = bigtiff_value {
Ok(v)
} else {
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(UnsignedBig(u64::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(SignedBig(i64::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(UnsignedBig(u64::from(decoder.read_short()?)))
}),
(Type::SSHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(SignedBig(i64::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, 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::LONG8, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(decoder.read_long8()?))
}),
(Type::ASCII, n) => {
let n = usize::try_from(n)?;
if n > limits.decoding_buffer_size {
return Err(TiffError::LimitsExceeded);
}
if (n <= 4 && !decoder.bigtiff) || (n <= 8 && decoder.bigtiff) {
let mut buf = vec![0; n];
self.r(bo).read_exact(&mut buf )?;
let v = String::from_utf8(buf)?;
let v = v.trim_matches(char::from(0));
Ok(Ascii(v.into()))
} else {
if decoder.bigtiff {
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
} else {
decoder.goto_offset(self.r(bo).read_u32()?)?
}
let string = decoder.read_string(n)?;
Ok(Ascii(string))
}
}
_ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedDataType,
)),
(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<R, F>(&self, value_count: u64, bo: ByteOrder, limits: &super::Limits, decoder: &mut super::Decoder<R>, decode_fn: F) -> TiffResult<Value>
fn decode_offset<R, F>(&self, value_count: u32, 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>,
@@ -387,11 +220,7 @@ impl Entry {
}
let mut v = Vec::with_capacity(value_count);
if decoder.bigtiff {
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
} else {
decoder.goto_offset(self.r(bo).read_u32()?)?
}
decoder.goto_offset(self.r(bo).read_u32()?)?;
for _ in 0..value_count {
v.push(decode_fn(decoder)?)
}
+34 -101
View File
@@ -116,8 +116,8 @@ impl<'a> DecodingBuffer<'a> {
#[derive(Debug)]
struct StripDecodeState {
strip_index: usize,
strip_offsets: Vec<u64>,
strip_bytes: Vec<u64>,
strip_offsets: Vec<u32>,
strip_bytes: Vec<u32>,
}
/// Decoding limits
@@ -157,9 +157,8 @@ where
{
reader: SmartReader<R>,
byte_order: ByteOrder,
bigtiff: bool,
limits: Limits,
next_ifd: Option<u64>,
next_ifd: Option<u32>,
ifd: Option<Directory>,
width: u32,
height: u32,
@@ -249,7 +248,6 @@ impl<R: Read + Seek> Decoder<R> {
Decoder {
reader: SmartReader::wrap(r, ByteOrder::LittleEndian),
byte_order: ByteOrder::LittleEndian,
bigtiff: false,
limits: Default::default(),
next_ifd: None,
ifd: None,
@@ -332,28 +330,12 @@ impl<R: Read + Seek> Decoder<R> {
))
}
}
match self.read_short()? {
42 => self.bigtiff = false,
43 => {
self.bigtiff = true;
// Read bytesize of offsets (in bigtiff it's alway 8 but provide a way to move to 16 some day)
if self.read_short()? != 8 {
return Err(TiffError::FormatError(
TiffFormatError::TiffSignatureNotFound,
))
}
// This constant should always be 0
if self.read_short()? != 0 {
return Err(TiffError::FormatError(
TiffFormatError::TiffSignatureNotFound,
))
}
},
_ => return Err(TiffError::FormatError(
if self.read_short()? != 42 {
return Err(TiffError::FormatError(
TiffFormatError::TiffSignatureInvalid,
))
));
}
self.next_ifd = match self.read_ifd_offset()? {
self.next_ifd = match self.read_long()? {
0 => None,
n => Some(n),
};
@@ -407,15 +389,6 @@ impl<R: Read + Seek> Decoder<R> {
self.byte_order
}
#[inline]
pub fn read_ifd_offset(&mut self) -> Result<u64, io::Error> {
if self.bigtiff {
self.read_long8()
} else {
self.read_long().map(u64::from)
}
}
/// Reads a TIFF byte value
#[inline]
pub fn read_byte(&mut self) -> Result<u8, io::Error> {
@@ -448,53 +421,29 @@ impl<R: Read + Seek> Decoder<R> {
self.reader.read_i32()
}
#[inline]
pub fn read_long8(&mut self) -> Result<u64, io::Error> {
self.reader.read_u64()
}
/// Reads a string
#[inline]
pub fn read_string(&mut self, length: usize) -> TiffResult<String> {
let mut out = vec![0; length];
self.reader.read_exact(&mut out)?;
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
if let Some(first) = out.iter().position(|&b| b == 0) {
out.truncate(first);
}
Ok(String::from_utf8(out)?)
let trimmed = out.bytes().take_while(|&n| n != 0).collect::<Vec<u8>>();
Ok(String::from_utf8(trimmed)?)
}
/// Reads a TIFF IFA offset/value field
#[inline]
pub fn read_offset(&mut self) -> TiffResult<[u8; 4]> {
if self.bigtiff {
return Err(TiffError::FormatError(TiffFormatError::InconsistentSizesEncountered))
}
pub fn read_offset(&mut self) -> Result<[u8; 4], io::Error> {
let mut val = [0; 4];
self.reader.read_exact(&mut val)?;
Ok(val)
}
/// Reads a TIFF IFA offset/value field
#[inline]
pub fn read_offset_u64(&mut self) -> Result<[u8; 8], io::Error> {
let mut val = [0; 8];
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.goto_offset_u64(offset.into())
}
#[inline]
pub fn goto_offset_u64(&mut self, offset: u64) -> io::Result<()> {
self.reader
.seek(io::SeekFrom::Start(offset))
.seek(io::SeekFrom::Start(u64::from(offset)))
.map(|_| ())
}
@@ -516,22 +465,13 @@ impl<R: Read + Seek> Decoder<R> {
return Ok(None);
}
};
let entry = if self.bigtiff {
ifd::Entry::new_u64(
type_,
self.read_long8()?,
self.read_offset_u64()?
)
} else {
ifd::Entry::new(
type_,
self.read_long()?,
self.read_offset()?
)
};
Ok(Some((
tag,
entry,
ifd::Entry::new(
type_,
self.read_long()?, // count
self.read_offset()?, // offset
),
)))
}
@@ -544,17 +484,16 @@ impl<R: Read + Seek> Decoder<R> {
TiffFormatError::ImageFileDirectoryNotFound,
))
}
Some(offset) => self.goto_offset_u64(offset)?,
Some(offset) => self.goto_offset(offset)?,
}
let num_tags = if self.bigtiff { self.read_long8()? } else { self.read_short()?.into() };
for _ in 0..num_tags {
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_ifd_offset()? {
self.next_ifd = match self.read_long()? {
0 => None,
n => Some(n),
};
@@ -575,9 +514,9 @@ impl<R: Read + Seek> Decoder<R> {
}
/// Tries to retrieve a tag and convert it to the desired unsigned type.
pub fn find_tag_unsigned<T: TryFrom<u64>>(&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)?
.map(|v| v.into_u64()).transpose()?
.map(|v| v.into_u32()).transpose()?
.map(|value| T::try_from(value)
.map_err(|_| TiffFormatError::InvalidTagValueType(tag).into()))
.transpose()
@@ -585,9 +524,9 @@ impl<R: Read + Seek> Decoder<R> {
/// 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<T: TryFrom<u64>>(&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)?
.map(|v| v.into_u64_vec()).transpose()?
.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())
@@ -596,7 +535,7 @@ impl<R: Read + Seek> Decoder<R> {
/// 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<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<T> {
pub fn get_tag_unsigned<T: TryFrom<u32>>(&mut self, tag: Tag) -> TiffResult<T> {
self.find_tag_unsigned(tag)?
.ok_or_else(|| TiffFormatError::RequiredTagNotFound(tag).into())
}
@@ -616,30 +555,24 @@ impl<R: Read + Seek> Decoder<R> {
pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult<u32> {
self.get_tag(tag)?.into_u32()
}
pub fn get_tag_u64(&mut self, tag: Tag) -> TiffResult<u64> {
self.get_tag(tag)?.into_u64()
}
/// Tries to retrieve a tag and convert it to the desired type.
pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult<Vec<u32>> {
self.get_tag(tag)?.into_u32_vec()
}
pub fn get_tag_u64_vec(&mut self, tag: Tag) -> TiffResult<Vec<u64>> {
self.get_tag(tag)?.into_u64_vec()
}
/// Decompresses the strip into the supplied buffer.
/// Returns the number of bytes read.
fn expand_strip<'a>(
&mut self,
buffer: DecodingBuffer<'a>,
offset: u64,
length: u64,
offset: u32,
length: u32,
max_uncompressed_length: usize,
) -> TiffResult<usize> {
let color_type = self.colortype()?;
self.goto_offset_u64(offset)?;
let (bytes, mut reader): (usize, Box<dyn EndianReader>) = match self.compression_method {
self.goto_offset(offset)?;
let (bytes, mut reader): (usize, Box<EndianReader>) = match self.compression_method {
CompressionMethod::None => {
let order = self.reader.byte_order;
(
@@ -651,7 +584,7 @@ impl<R: Read + Seek> Decoder<R> {
let (bytes, reader) = LZWReader::new(
&mut self.reader,
usize::try_from(length)?,
max_uncompressed_length * buffer.byte_len()
max_uncompressed_length
)?;
(bytes, Box::new(reader))
}
@@ -765,8 +698,8 @@ impl<R: Read + Seek> Decoder<R> {
fn initialize_strip_decoder(&mut self) -> TiffResult<()> {
if self.strip_decoder.is_none() {
let strip_offsets = self.get_tag_u64_vec(Tag::StripOffsets)?;
let strip_bytes = self.get_tag_u64_vec(Tag::StripByteCounts)?;
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,
@@ -811,7 +744,7 @@ impl<R: Read + Seek> Decoder<R> {
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(
TiffFormatError::InconsistentSizesEncountered,
));
+19 -33
View File
@@ -407,9 +407,26 @@ impl<W: Write + Seek> TiffEncoder<W> {
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 image: ImageEncoder<W, C> = ImageEncoder::new(encoder, width, height)?;
image.write_data(data)
let mut image: ImageEncoder<W, C> = 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()
}
}
@@ -531,14 +548,10 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
/// # let mut file = std::io::Cursor::new(Vec::new());
/// # let image_data = vec![0; 100*100*3];
/// use tiff::encoder::*;
/// use tiff::tags::Tag;
///
/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
/// let mut image = tiff.new_image::<colortype::RGB8>(100, 100).unwrap();
///
/// // You can encode tags here
/// image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap();
///
/// let mut idx = 0;
/// while image.next_strip_sample_count() > 0 {
/// let sample_count = image.next_strip_sample_count() as usize;
@@ -548,13 +561,11 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
/// image.finish().unwrap();
/// # }
/// ```
/// You can also call write_data function wich will encode by strip and finish
pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType> {
encoder: DirectoryEncoder<'a, W>,
strip_idx: u64,
strip_count: u64,
row_samples: u64,
width: u32,
height: u32,
rows_per_strip: u64,
strip_offsets: Vec<u32>,
@@ -597,7 +608,6 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
strip_idx: 0,
row_samples,
rows_per_strip,
width,
height,
strip_offsets: Vec::new(),
strip_byte_count: Vec::new(),
@@ -642,30 +652,6 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
Ok(())
}
/// Write strips from data
pub fn write_data(mut self, data: &[T::Inner]) -> TiffResult<()>
where
[T::Inner]: TiffValue,
{
let num_pix = usize::try_from(self.width)?.checked_mul(usize::try_from(self.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 mut idx = 0;
while self.next_strip_sample_count() > 0 {
let sample_count = usize::try_from(self.next_strip_sample_count())?;
self.write_strip(&data[idx..idx + sample_count])?;
idx += sample_count;
}
self.finish()?;
Ok(())
}
/// Set image resolution
pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) {
self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap();
+1 -1
View File
@@ -4,7 +4,7 @@
//! lossless and lossy compression.
//!
//! # Related Links
//! * <https://www.adobe.io/open/standards/TIFF.html> - The TIFF specification
//! * <http://partners.adobe.com/public/developer/tiff/index.html> - The TIFF specification
extern crate byteorder;
extern crate lzw;
-5
View File
@@ -105,11 +105,6 @@ pub enum Tag(u16) unknown("A private or extension tag") {
YResolution = 283,
// Advanced tags
Predictor = 317,
TileWidth = 322,
TileLength = 323,
TileOffsets = 324,
TileByteCounts = 325,
SampleFormat = 339,
}
}
-46
View File
@@ -1,46 +0,0 @@
extern crate tiff;
use tiff::decoder::Decoder;
use tiff::tags::Tag;
use tiff::ColorType;
use std::fs::File;
use std::path::PathBuf;
const TEST_IMAGE_DIR: &str = "./tests/images/bigtiff";
#[test]
fn test_big_tiff() {
let filenames = ["BigTIFF.tif", "BigTIFFMotorola.tif", "BigTIFFLong.tif"];
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");
assert_eq!(
decoder.dimensions().expect("Cannot get dimensions"),
(64, 64)
);
assert_eq!(
decoder.colortype().expect("Cannot get colortype"),
ColorType::RGB(8)
);
assert_eq!(
decoder
.get_tag_u64(Tag::StripOffsets)
.expect("Cannot get StripOffsets"),
16
);
assert_eq!(
decoder
.get_tag_u64(Tag::RowsPerStrip)
.expect("Cannot get RowsPerStrip"),
64
);
assert_eq!(
decoder
.get_tag_u64(Tag::StripByteCounts)
.expect("Cannot get StripByteCounts"),
12288
)
}
}
-6
View File
@@ -149,12 +149,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
//#[test]
//fn test_gray_alpha_u8()
+2 -6
View File
@@ -1,6 +1,6 @@
extern crate tiff;
use tiff::decoder::{Decoder, DecodingResult, ifd};
use tiff::decoder::{Decoder, DecodingResult};
use tiff::encoder::{colortype, TiffEncoder, SRational};
use tiff::ColorType;
use tiff::tags::Tag;
@@ -24,18 +24,14 @@ fn encode_decode() {
{
let mut tiff = TiffEncoder::new(&mut file).unwrap();
let mut image = tiff.new_image::<colortype::RGB8>(100, 100)
tiff.write_image::<colortype::RGB8>(100, 100, &image_data)
.unwrap();
image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap();
image.write_data(&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));
assert_eq!(decoder.get_tag(Tag::Artist).unwrap(), ifd::Value::Ascii("Image-tiff".into()));
if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() {
assert_eq!(image_data, img_res);
} else {
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.