Skip to content

Commit 73bee6f

Browse files
committed
Track non-image Directory sequence in Reader
A bit nasty, since we are storing the current Directory within the Image instead of a separate attribute so we can not have one without the other directly. Instead we prioritize with another attribute.
1 parent f993cfb commit 73bee6f

File tree

4 files changed

+59
-19
lines changed

4 files changed

+59
-19
lines changed

src/decoder/image.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,18 @@ impl Image {
8585
pub fn from_reader<R: Read + Seek>(
8686
decoder: &mut ValueReader<R>,
8787
ifd: Directory,
88+
) -> TiffResult<Image> {
89+
let img = Self::from_ref(decoder, &ifd)?;
90+
91+
Ok(Image {
92+
ifd: Some(ifd),
93+
..img
94+
})
95+
}
96+
97+
pub(crate) fn from_ref<R: Read + Seek>(
98+
decoder: &mut ValueReader<R>,
99+
ifd: &Directory,
88100
) -> TiffResult<Image> {
89101
let mut tag_reader = TagReader { decoder, ifd: &ifd };
90102

@@ -288,7 +300,7 @@ impl Image {
288300
};
289301

290302
Ok(Image {
291-
ifd: Some(ifd),
303+
ifd: None,
292304
width,
293305
height,
294306
bits_per_sample: bits_per_sample[0],

src/decoder/mod.rs

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,10 @@ where
349349
ifd_offsets: Vec<IfdPointer>,
350350
/// Map from the ifd into the `ifd_offsets` ordered list.
351351
seen_ifds: cycles::IfdCycles,
352+
/// The directory, if we have not yet read it as an image.
353+
/// This is prioritized _over_ the image. Hence it must _not_ be set if we are currently
354+
/// reading a true image IFD that is instead stored in the `image` attribute.
355+
non_image_ifd: Option<Directory>,
352356
image: Image,
353357
}
354358

@@ -624,6 +628,7 @@ impl<R: Read + Seek> Decoder<R> {
624628
chunk_offsets: Vec::new(),
625629
chunk_bytes: Vec::new(),
626630
},
631+
non_image_ifd: None,
627632
};
628633
decoder.next_image()?;
629634
Ok(decoder)
@@ -654,6 +659,7 @@ impl<R: Read + Seek> Decoder<R> {
654659
/// Loads the IFD at the specified index in the list, if one exists
655660
pub fn seek_to_image(&mut self, ifd_index: usize) -> TiffResult<()> {
656661
let ifd = self.seek_to_directory(ifd_index)?;
662+
self.non_image_ifd = None;
657663
self.image = Image::from_reader(&mut self.value_reader, ifd)?;
658664
Ok(())
659665
}
@@ -699,6 +705,7 @@ impl<R: Read + Seek> Decoder<R> {
699705
/// recovered by calling [`Self::restart_at_image`] with a valid offset and a successful seek.
700706
pub fn restart_at_image(&mut self, offset: IfdPointer) -> TiffResult<()> {
701707
let ifd = self.restart_at_offset(offset)?;
708+
self.non_image_ifd = None;
702709
self.image = Image::from_reader(&mut self.value_reader, ifd)?;
703710
Ok(())
704711
}
@@ -709,8 +716,10 @@ impl<R: Read + Seek> Decoder<R> {
709716
/// interpret the directory in the sequence as an image. Instead, it may be used to read a
710717
/// sequence of auxiliary IFDs that are not necessarily images. For instance, a directory
711718
/// referred to in the SubIfd tag may be a thumbnail
712-
pub fn restart_at_directory(&mut self, offset: IfdPointer) -> TiffResult<Directory> {
713-
self.restart_at_offset(offset)
719+
pub fn restart_at_directory(&mut self, offset: IfdPointer) -> TiffResult<()> {
720+
let ifd = self.restart_at_offset(offset)?;
721+
self.non_image_ifd = Some(ifd);
722+
Ok(())
714723
}
715724

716725
fn restart_at_offset(&mut self, offset: IfdPointer) -> TiffResult<Directory> {
@@ -726,6 +735,7 @@ impl<R: Read + Seek> Decoder<R> {
726735
fn next_ifd(&mut self) -> TiffResult<Directory> {
727736
let Some(next_ifd) = self.next_ifd.take() else {
728737
self.current_ifd = None;
738+
self.non_image_ifd = None;
729739

730740
return Err(TiffError::FormatError(
731741
TiffFormatError::ImageFileDirectoryNotFound,
@@ -753,6 +763,7 @@ impl<R: Read + Seek> Decoder<R> {
753763
/// To determine whether there are more images call `TIFFDecoder::more_images` instead.
754764
pub fn next_image(&mut self) -> TiffResult<()> {
755765
let ifd = self.next_ifd()?;
766+
self.non_image_ifd = None;
756767
self.image = Image::from_reader(&mut self.value_reader, ifd)?;
757768
Ok(())
758769
}
@@ -761,8 +772,10 @@ impl<R: Read + Seek> Decoder<R> {
761772
///
762773
/// If there is no further image in the TIFF file a format error is returned. To determine
763774
/// whether there are more images call `TIFFDecoder::more_directories` instead.
764-
pub fn next_directory(&mut self) -> TiffResult<Directory> {
765-
self.next_ifd()
775+
pub fn next_directory(&mut self) -> TiffResult<()> {
776+
let ifd = self.next_ifd()?;
777+
self.non_image_ifd = Some(ifd);
778+
Ok(())
766779
}
767780

768781
/// Interpret the current directory as an image.
@@ -775,8 +788,15 @@ impl<R: Read + Seek> Decoder<R> {
775788
TiffFormatError::ImageFileDirectoryNotFound,
776789
))?;
777790

778-
let ifd = self.read_directory(current_ifd)?;
779-
self.image = Image::from_reader(&mut self.value_reader, ifd)?;
791+
if let Some(ifd) = &self.non_image_ifd {
792+
self.image = Image::from_ref(&mut self.value_reader, ifd)?;
793+
// Definitely sets an IFD but this works without unwraps.
794+
self.image.ifd = self.non_image_ifd.take();
795+
} else {
796+
// Probably re-reading an image but that's fine.
797+
let ifd = self.read_directory(current_ifd)?;
798+
self.image = Image::from_reader(&mut self.value_reader, ifd)?;
799+
}
780800

781801
Ok(())
782802
}
@@ -1283,11 +1303,22 @@ impl<R: Read + Seek> Decoder<R> {
12831303
}
12841304

12851305
/// Get the IFD decoder for our current image IFD.
1286-
fn image_ifd(&mut self) -> IfdDecoder<'_> {
1306+
fn current_directory_ifd(&mut self) -> IfdDecoder<'_> {
1307+
// Special fallback. We do not want to error handle not having read a current directory, in
1308+
// particular as the behavior having a directory without tags will produce these errors
1309+
// anyways (most likely). Note that an empty directory is invalid in a TIFF.
1310+
static NO_IFD: Directory = Directory::empty();
1311+
1312+
let ifd = self
1313+
.non_image_ifd
1314+
.as_ref()
1315+
.or_else(|| self.image.ifd.as_ref())
1316+
.unwrap_or(&NO_IFD);
1317+
12871318
IfdDecoder {
12881319
inner: tag_reader::TagReader {
12891320
decoder: &mut self.value_reader,
1290-
ifd: self.image.ifd.as_ref().unwrap(),
1321+
ifd,
12911322
},
12921323
}
12931324
}
@@ -1328,25 +1359,25 @@ impl<R: Read + Seek> Decoder<R> {
13281359
/// Tries to retrieve a tag from the current image directory.
13291360
/// Return `Ok(None)` if the tag is not present.
13301361
pub fn find_tag(&mut self, tag: Tag) -> TiffResult<Option<ifd::Value>> {
1331-
self.image_ifd().find_tag(tag)
1362+
self.current_directory_ifd().find_tag(tag)
13321363
}
13331364

13341365
/// Tries to retrieve a tag in the current image directory and convert it to the desired
13351366
/// unsigned type.
13361367
pub fn find_tag_unsigned<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<Option<T>> {
1337-
self.image_ifd().find_tag_unsigned(tag)
1368+
self.current_directory_ifd().find_tag_unsigned(tag)
13381369
}
13391370

13401371
/// Tries to retrieve a tag from the current image directory and convert it to the desired
13411372
/// unsigned type. Returns an error if the tag is not present.
13421373
pub fn get_tag_unsigned<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<T> {
1343-
self.image_ifd().get_tag_unsigned(tag)
1374+
self.current_directory_ifd().get_tag_unsigned(tag)
13441375
}
13451376

13461377
/// Tries to retrieve a tag from the current image directory.
13471378
/// Returns an error if the tag is not present
13481379
pub fn get_tag(&mut self, tag: Tag) -> TiffResult<ifd::Value> {
1349-
self.image_ifd().get_tag(tag)
1380+
self.current_directory_ifd().get_tag(tag)
13501381
}
13511382

13521383
pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult<u32> {
@@ -1401,7 +1432,7 @@ impl<R: Read + Seek> Decoder<R> {
14011432
}
14021433

14031434
pub fn tag_iter(&mut self) -> impl Iterator<Item = TiffResult<(Tag, ifd::Value)>> + '_ {
1404-
self.image_ifd().tag_iter()
1435+
self.current_directory_ifd().tag_iter()
14051436
}
14061437
}
14071438

src/directory.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ pub struct Directory {
2929
impl Directory {
3030
/// Create a directory in an initial state without entries. Note that an empty directory can
3131
/// not be encoded in a file, it must contain at least one entry.
32-
pub fn empty() -> Self {
32+
pub const fn empty() -> Self {
3333
Directory {
3434
entries: BTreeMap::new(),
3535
next_ifd: None,

tests/subsubifds.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,7 @@ fn decode_subifds() {
123123
loop {
124124
len += 1;
125125

126-
let position = decoder.ifd_pointer().unwrap();
127-
let ifd = decoder.read_directory(position).unwrap();
128-
129-
if let Ok(subifd) = decoder.read_directory_tags(&ifd).get_tag(Tag::SubIfd) {
126+
if let Ok(subifd) = decoder.get_tag(Tag::SubIfd) {
130127
let prelen = subifds.len();
131128
subifds.extend(subifd.into_ifd_vec().expect("Failed to decode SubIfd tag"));
132129
eprintln!("New roots: {:?}", &subifds[prelen..]);

0 commit comments

Comments
 (0)