Extra utils for reading tags

This commit is contained in:
A. Molzer committed 2026-03-31 18:34:14 +02:00
1 parent 8a1b65fe68
commit f93a6380e6
2 files changed
+91 -22

No files matched your search

+31
View File
@@ -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
View File
@@ -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(())
}
}