From cef21c47fb5fac8de14e05533a45b7f9f4a5feae Mon Sep 17 00:00:00 2001 From: "A. Molzer" <5550310+197g@users.noreply.github.com> Date: Sat, 7 Feb 2026 04:23:31 +0100 Subject: [PATCH] Add example listing a files directories --- examples/tiff-ls.rs | 124 ++++++++++++++++++++++++++++++++++++++++++++ src/decoder/mod.rs | 7 ++- src/tags.rs | 13 +++++ 3 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 examples/tiff-ls.rs diff --git a/examples/tiff-ls.rs b/examples/tiff-ls.rs new file mode 100644 index 0000000..39da35e --- /dev/null +++ b/examples/tiff-ls.rs @@ -0,0 +1,124 @@ +use std::borrow::Cow; + +use tiff::decoder::Decoder; + +fn main() -> Result<(), Box> { + let Some(image) = std::env::args_os().nth(1) else { + eprintln!("Usage: decode FILE"); + return Ok(()); + }; + + let file = std::fs::File::open(image)?; + let io = std::io::BufReader::new(file); + let mut reader = Decoder::new(io)?; + + loop { + ls_dir(&mut reader); + + if !reader.more_images() { + break; + } + } + + Ok(()) +} + +fn ls_dir(reader: &mut Decoder) { + if let Some(ifd) = reader.ifd_pointer() { + println!("Directory at {ifd:x}"); + } + + println!("Name\tHex\tType\tCount"); + let ifd = reader.image_ifd(); + + for (tag, entry) in ifd.directory().iter() { + let name: &'static str = match tag { + tiff::tags::Tag::Artist => "Artist", + tiff::tags::Tag::BitsPerSample => "BitsPerSample", + tiff::tags::Tag::CellLength => "CellLength", + tiff::tags::Tag::CellWidth => "CellWidth", + tiff::tags::Tag::ColorMap => "ColorMap", + tiff::tags::Tag::Compression => "Compression", + tiff::tags::Tag::DateTime => "DateTime", + tiff::tags::Tag::ExtraSamples => "ExtraSamples", + tiff::tags::Tag::FillOrder => "FillOrder", + tiff::tags::Tag::FreeByteCounts => "FreeByteCounts", + tiff::tags::Tag::FreeOffsets => "FreeOffsets", + tiff::tags::Tag::GrayResponseCurve => "GrayResponseCurve", + tiff::tags::Tag::GrayResponseUnit => "GrayResponseUnit", + tiff::tags::Tag::HostComputer => "HostComputer", + tiff::tags::Tag::ImageDescription => "ImageDescription", + tiff::tags::Tag::ImageLength => "ImageLength", + tiff::tags::Tag::ImageWidth => "ImageWidth", + tiff::tags::Tag::Make => "Make", + tiff::tags::Tag::MaxSampleValue => "MaxSampleValue", + tiff::tags::Tag::MinSampleValue => "MinSampleValue", + tiff::tags::Tag::Model => "Model", + tiff::tags::Tag::NewSubfileType => "NewSubfileType", + tiff::tags::Tag::Orientation => "Orientation", + tiff::tags::Tag::PhotometricInterpretation => "PhotometricInterpretation", + tiff::tags::Tag::PlanarConfiguration => "PlanarConfiguration", + tiff::tags::Tag::ResolutionUnit => "ResolutionUnit", + tiff::tags::Tag::RowsPerStrip => "RowsPerStrip", + tiff::tags::Tag::SamplesPerPixel => "SamplesPerPixel", + tiff::tags::Tag::Software => "Software", + tiff::tags::Tag::StripByteCounts => "StripByteCounts", + tiff::tags::Tag::StripOffsets => "StripOffsets", + tiff::tags::Tag::SubfileType => "SubfileType", + tiff::tags::Tag::Threshholding => "Threshholding", + tiff::tags::Tag::XResolution => "XResolution", + tiff::tags::Tag::YResolution => "YResolution", + tiff::tags::Tag::Predictor => "Predictor", + tiff::tags::Tag::TileWidth => "TileWidth", + tiff::tags::Tag::TileLength => "TileLength", + tiff::tags::Tag::TileOffsets => "TileOffsets", + tiff::tags::Tag::TileByteCounts => "TileByteCounts", + tiff::tags::Tag::SubIfd => "SubIfd", + tiff::tags::Tag::SampleFormat => "SampleFormat", + tiff::tags::Tag::SMinSampleValue => "SMinSampleValue", + tiff::tags::Tag::SMaxSampleValue => "SMaxSampleValue", + tiff::tags::Tag::JPEGTables => "JPEGTables", + tiff::tags::Tag::ChromaSubsampling => "ChromaSubsampling", + tiff::tags::Tag::ChromaPositioning => "ChromaPositioning", + tiff::tags::Tag::ModelPixelScaleTag => "ModelPixelScaleTag", + tiff::tags::Tag::ModelTransformationTag => "ModelTransformationTag", + tiff::tags::Tag::ModelTiepointTag => "ModelTiepointTag", + tiff::tags::Tag::Copyright => "Copyright", + tiff::tags::Tag::ExifDirectory => "ExifDirectory", + tiff::tags::Tag::GpsDirectory => "GpsDirectory", + tiff::tags::Tag::IccProfile => "IccProfile", + tiff::tags::Tag::GeoKeyDirectoryTag => "GeoKeyDirectoryTag", + tiff::tags::Tag::GeoDoubleParamsTag => "GeoDoubleParamsTag", + tiff::tags::Tag::GeoAsciiParamsTag => "GeoAsciiParamsTag", + tiff::tags::Tag::ExifVersion => "ExifVersion", + tiff::tags::Tag::GdalNodata => "GdalNodata", + _ => "", + }; + + let ty: Cow<'static, str> = match entry.field_type() { + tiff::tags::Type::BYTE => "u8".into(), + tiff::tags::Type::ASCII => "ascii".into(), + tiff::tags::Type::SHORT => "u16".into(), + tiff::tags::Type::LONG => "u32".into(), + tiff::tags::Type::RATIONAL => "r32".into(), + tiff::tags::Type::SBYTE => "i8".into(), + tiff::tags::Type::UNDEFINED => "byte".into(), + tiff::tags::Type::SSHORT => "s16".into(), + tiff::tags::Type::SLONG => "s32".into(), + tiff::tags::Type::SRATIONAL => "sr32".into(), + tiff::tags::Type::FLOAT => "f32".into(), + tiff::tags::Type::DOUBLE => "f64".into(), + tiff::tags::Type::IFD => "ifd32".into(), + tiff::tags::Type::LONG8 => "u64".into(), + tiff::tags::Type::SLONG8 => "i64".into(), + tiff::tags::Type::IFD8 => "ifd64".into(), + other => format!("{:x}", other.to_u16()).into(), + }; + + eprintln!( + "{name:16}\t{tag:4x}\t{ty}\t{count}", + tag = tag.to_u16(), + count = entry.count(), + ); + } +} diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index a131f0e..12009f3 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -1538,7 +1538,7 @@ impl Decoder { } /// Get the IFD decoder for our current image IFD. - fn image_ifd(&mut self) -> IfdDecoder<'_> { + pub fn image_ifd(&mut self) -> IfdDecoder<'_> { IfdDecoder { inner: tag_reader::TagReader { decoder: &mut self.value_reader, @@ -1885,6 +1885,11 @@ impl IfdDecoder<'_> { pub fn get_tag_ascii_string(&mut self, tag: Tag) -> TiffResult { self.get_tag(tag)?.into_string() } + + /// Inspect the raw underlying directory. + pub fn directory(&self) -> &Directory { + self.inner.ifd + } } impl<'l> IfdDecoder<'l> { diff --git a/src/tags.rs b/src/tags.rs index 245d203..9d18084 100644 --- a/src/tags.rs +++ b/src/tags.rs @@ -1,4 +1,5 @@ use crate::encoder::TiffValue; +use core::fmt; macro_rules! tags { { @@ -169,6 +170,18 @@ pub enum Tag(u16) unknown( // practice it just returns garbage and the validity does not matter greatly to us). pub struct IfdPointer(pub u64); +impl fmt::LowerHex for IfdPointer { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(&self.0, f) + } +} + +impl core::fmt::UpperHex for IfdPointer { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::UpperHex::fmt(&self.0, f) + } +} + tags! { /// The type of an IFD entry (a 2 byte field). pub enum Type(u16) {