From f5ab3b363b33a8f4f6978473514b164210b7bdaa Mon Sep 17 00:00:00 2001 From: "A. Molzer" <5550310+197g@users.noreply.github.com> Date: Tue, 8 Jul 2025 12:10:52 +0200 Subject: [PATCH] Expose interacting with secondary IFDs --- src/decoder/ifd.rs | 32 +++++++++++++++++++++++++++++++- src/decoder/mod.rs | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/decoder/ifd.rs b/src/decoder/ifd.rs index 455a3e8..8b9aa8f 100644 --- a/src/decoder/ifd.rs +++ b/src/decoder/ifd.rs @@ -6,7 +6,7 @@ use std::mem; use std::str; use super::stream::{ByteOrder, EndianReader}; -use crate::tags::{Tag, Type}; +use crate::tags::{IfdPointer, Tag, Type}; use crate::{TiffError, TiffFormatError, TiffResult}; use self::Value::{ @@ -130,6 +130,25 @@ impl Value { } } + /// Turn this value into an `IfdPointer`. + /// + /// Notice that this does not take an argument, a 64-bit IFD is always allowed. If the + /// difference is crucial and you do not want to be permissive you're expected to filter this + /// out before. + /// + /// For compatibility the smaller sized tags should always be allowed i.e. you might use a + /// non-bigtiff's directory and its tag types and move it straight to a bigtiff. For instance + /// the SubIFD tag is defined as `LONG or IFD`: + /// + /// + pub fn into_ifd_pointer(self) -> TiffResult { + match self { + Unsigned(val) | Ifd(val) => Ok(IfdPointer(val.into())), + IfdBig(val) => Ok(IfdPointer(val)), + _ => Err(TiffError::FormatError(TiffFormatError::InvalidTypeForTag)), + } + } + pub fn into_string(self) -> TiffResult { match self { Ascii(val) => Ok(val), @@ -300,6 +319,17 @@ impl Value { _ => Err(TiffError::FormatError(TiffFormatError::InvalidTypeForTag)), } } + + pub fn into_ifd_vec(self) -> TiffResult> { + let vec = match self { + Unsigned(val) | Ifd(val) => return Ok(vec![IfdPointer(val.into())]), + IfdBig(val) => return Ok(vec![IfdPointer(val)]), + List(vec) => vec, + _ => return Err(TiffError::FormatError(TiffFormatError::InvalidTypeForTag)), + }; + + vec.into_iter().map(Self::into_ifd_pointer).collect() + } } #[derive(Clone)] diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index af09f1f..3ed7d2d 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -765,6 +765,13 @@ impl Decoder { self.value_reader.reader.goto_offset(offset) } + /// Read a directory from a known offset. + /// + /// This may always modify the position of the reader. + pub fn read_directory(&mut self, ptr: IfdPointer) -> TiffResult { + self.value_reader.read_directory(ptr) + } + fn check_chunk_type(&self, expected: ChunkType) -> TiffResult<()> { if expected != self.image().chunk_type { return Err(TiffError::UsageError(UsageError::InvalidChunkType( @@ -985,6 +992,37 @@ impl Decoder { } } + /// Prepare reading tag values for some externally given directory. + /// + /// # Examples + /// + /// This method may be used to read the values of tags in directories that have been previously + /// read with [`Decoder::read_directory`]. + /// + /// ```no_run + /// use tiff::decoder::Decoder; + /// use tiff::tags::Tag; + /// + /// # use std::io::Cursor; + /// # let mut data = Cursor::new(vec![0]); + /// let mut decoder = Decoder::new(&mut data).unwrap(); + /// let sub_ifds = decoder.get_tag(Tag::SubIfd)?.into_ifd_vec()?; + /// + /// for ifd in sub_ifds { + /// let subdir = decoder.read_directory(ifd)?; + /// let subfile = decoder.with_ifd(&subdir).find_tag(Tag::SubfileType)?; + /// // todo: handle the subfiles, e.g. thumbnails + /// } + /// + /// # Ok::<_, tiff::TiffError>(()) + /// ``` + pub fn with_ifd<'ifd>(&'ifd mut self, ifd: &'ifd Directory) -> IfdDecoder<'ifd> { + IfdDecoder { + decoder: &mut self.value_reader, + ifd, + } + } + /// Tries to retrieve a tag from the current image directory. /// Return `Ok(None)` if the tag is not present. pub fn find_tag(&mut self, tag: Tag) -> TiffResult> {