Extra utils for reading tags
This commit is contained in:
1 parent
8a1b65fe68
commit
f93a6380e6
2 files changed
+91
-22
No files matched your search
@@ -852,6 +852,15 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
self.value_reader.reader.byte_order
|
||||
}
|
||||
|
||||
/// Check if this file is a BigTIFF or a standard TIFF.
|
||||
pub fn variant(&self) -> TiffVariant {
|
||||
if self.value_reader.bigtiff {
|
||||
TiffVariant::BigTiff
|
||||
} else {
|
||||
TiffVariant::Standard
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_ifd_offset(&mut self) -> Result<u64, io::Error> {
|
||||
if self.value_reader.bigtiff {
|
||||
@@ -945,10 +954,32 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
/// Create a byte reader for the value of an arbitrary IFD entry.
|
||||
///
|
||||
/// Small values are stored inline inside the offset field of the entry. The threshold for this
|
||||
/// depends on the [`Self::variant`] of the file. Interpret entries that have not been
|
||||
/// extracted from an IFD of the current decoder with care.
|
||||
///
|
||||
/// The reader returned form this method reads raw bytes. Values retrieved in this way are not
|
||||
/// yet converted to the host endianess.
|
||||
///
|
||||
/// Returns an error if the entry count exceeds the possible offset values or if the offset
|
||||
/// of the entry would overflow the offset in the underlying stream. Otherwise returns a reader
|
||||
/// that yields the bytes of the value.
|
||||
pub fn read_entry(&mut self, entry: ifd::Entry) -> TiffResult<EntryBytesReader<'_, R>> {
|
||||
EntryBytesReader::from_entry(&mut self.value_reader, entry)
|
||||
}
|
||||
|
||||
/// Utility to create a reader like [`Self::read_entry`], from the current IFD.
|
||||
///
|
||||
/// Returns an `Ok(None)` if the tag is not found in the current directory.
|
||||
pub fn read_tag(&mut self, tag: Tag) -> TiffResult<Option<EntryBytesReader<'_, R>>> {
|
||||
match self.current_ifd().find_entry(tag) {
|
||||
None => Ok(None),
|
||||
Some(entry) => self.read_entry(entry).map(Some),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a TIFF IFA offset/value field
|
||||
#[inline]
|
||||
pub fn read_offset_u64(&mut self) -> Result<[u8; 8], io::Error> {
|
||||
|
||||
+60
-22
@@ -94,9 +94,7 @@ impl<'lt, R> EntryBytesReader<'lt, R> {
|
||||
impl<R: Read> Read for EntryBytesReader<'_, R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match &mut self.variant {
|
||||
Variant::Immediate { data, .. } => {
|
||||
data.read(buf)
|
||||
}
|
||||
Variant::Immediate { data, .. } => data.read(buf),
|
||||
Variant::InFile { remainder, .. } => {
|
||||
let bound = usize::try_from(*remainder).unwrap_or(usize::MAX);
|
||||
let avail = buf.len().min(bound);
|
||||
@@ -112,9 +110,7 @@ impl<R: Read> Read for EntryBytesReader<'_, R> {
|
||||
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
match &mut self.variant {
|
||||
Variant::Immediate { data, .. } => {
|
||||
data.read_exact(buf)
|
||||
}
|
||||
Variant::Immediate { data, .. } => data.read_exact(buf),
|
||||
Variant::InFile { remainder, .. } => {
|
||||
let bound = usize::try_from(*remainder).unwrap_or(usize::MAX);
|
||||
|
||||
@@ -197,7 +193,7 @@ impl<R: Read + Seek> Seek for EntryBytesReader<'_, R> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{decoder::Decoder, encoder, tags::Tag};
|
||||
use crate::{decoder::Decoder, encoder, tags};
|
||||
use std::io::{Read as _, Seek as _, SeekFrom};
|
||||
|
||||
#[test]
|
||||
@@ -211,22 +207,21 @@ mod tests {
|
||||
let mut decoder = Decoder::open(file).unwrap();
|
||||
decoder.next_directory().unwrap();
|
||||
|
||||
let strips = decoder
|
||||
.current_ifd()
|
||||
.find_entry(Tag::StripByteCounts)
|
||||
.expect("some strip byte count is present");
|
||||
|
||||
let mut buffer = crate::tags::ValueBuffer::default();
|
||||
|
||||
decoder
|
||||
.current_ifd()
|
||||
.find_tag_buf(Tag::StripByteCounts, &mut buffer)
|
||||
.find_tag_buf(tags::Tag::StripByteCounts, &mut buffer)
|
||||
.expect("some strip byte count is present");
|
||||
|
||||
let mut reader = decoder.read_entry(strips)?;
|
||||
let mut reader = decoder
|
||||
.read_tag(tags::Tag::StripByteCounts)?
|
||||
.expect("some strip byte count is present");
|
||||
|
||||
let mut data = vec![];
|
||||
reader.read_to_end(&mut data)?;
|
||||
|
||||
// `ValueBuffer` also stores its data in the file endianess until converted.
|
||||
assert_eq!(buffer.as_bytes(), data);
|
||||
|
||||
reader.seek(SeekFrom::Start(0))?;
|
||||
@@ -247,7 +242,7 @@ mod tests {
|
||||
let mut encoder = encoder::TiffEncoder::new(&mut data)?;
|
||||
|
||||
let mut dir = encoder.image_directory()?;
|
||||
dir.write_tag(Tag::Artist, ARTIST)?;
|
||||
dir.write_tag(tags::Tag::Artist, ARTIST)?;
|
||||
dir.finish()?;
|
||||
|
||||
data.into_inner()
|
||||
@@ -257,12 +252,9 @@ mod tests {
|
||||
let mut decoder = Decoder::open(file).unwrap();
|
||||
decoder.next_directory().unwrap();
|
||||
|
||||
let artist = decoder
|
||||
.current_ifd()
|
||||
.find_entry(Tag::Artist)
|
||||
.expect("some artist is present");
|
||||
|
||||
let mut reader = decoder.read_entry(artist)?;
|
||||
let mut reader = decoder
|
||||
.read_tag(tags::Tag::Artist)?
|
||||
.expect("the artist is present?");
|
||||
|
||||
let mut value = vec![];
|
||||
reader.read_to_end(&mut value)?;
|
||||
@@ -274,12 +266,58 @@ mod tests {
|
||||
assert_eq!(reader.seek(SeekFrom::End(0))?, ARTIST.len() as u64 + 1);
|
||||
assert!(reader.seek(SeekFrom::End(1)).is_err());
|
||||
|
||||
reader.seek(SeekFrom::End(-5))?;
|
||||
// Check relative seeks by comparing against the "code"
|
||||
let code_offset = reader.seek(SeekFrom::End(-5))?;
|
||||
let mut is_code = [0u8; 4];
|
||||
reader.read_exact(&mut is_code)?;
|
||||
assert_eq!(is_code, *value.last_chunk::<4>().unwrap());
|
||||
|
||||
assert_eq!(reader.seek(SeekFrom::Start(0))?, 0);
|
||||
assert_eq!(reader.seek(SeekFrom::Current(0))?, 0);
|
||||
assert!(reader.seek(SeekFrom::Current(-1)).is_err());
|
||||
|
||||
reader.seek(SeekFrom::Start(code_offset))?;
|
||||
reader.read_exact(&mut is_code)?;
|
||||
assert_eq!(is_code, *value.last_chunk::<4>().unwrap());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check that the reader from an `offset` field also works.
|
||||
#[test]
|
||||
fn read_from_offset() -> Result<(), crate::error::TiffError> {
|
||||
let data = {
|
||||
let mut data = std::io::Cursor::new(vec![]);
|
||||
let mut encoder = encoder::TiffEncoder::new(&mut data)?;
|
||||
|
||||
let mut dir = encoder.image_directory()?;
|
||||
dir.write_tag(
|
||||
tags::Tag::PlanarConfiguration,
|
||||
tags::PlanarConfiguration::Planar,
|
||||
)?;
|
||||
dir.finish()?;
|
||||
|
||||
data.into_inner()
|
||||
};
|
||||
|
||||
let file = std::io::Cursor::new(data);
|
||||
let mut decoder = Decoder::open(file).unwrap();
|
||||
decoder.next_directory().unwrap();
|
||||
let bo = decoder.byte_order();
|
||||
|
||||
let mut reader = decoder
|
||||
.read_tag(tags::Tag::PlanarConfiguration)?
|
||||
.expect("some artist is present");
|
||||
|
||||
let mut buf = [0; 2];
|
||||
reader.read_exact(&mut buf)?;
|
||||
|
||||
bo.convert(tags::Type::SHORT, &mut buf, tags::ByteOrder::BigEndian);
|
||||
assert_eq!(buf, [0, 2]);
|
||||
|
||||
// Check relative seeks, this item is only two bytes long.
|
||||
assert!(reader.seek(SeekFrom::End(-3)).is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user