Skip to content

Commit 857973c

Browse files
authored
Merge pull request #339 from image-rs/file-listing-and-utils
File listing utils and example
2 parents a0191ed + cef21c4 commit 857973c

File tree

3 files changed

+143
-1
lines changed

3 files changed

+143
-1
lines changed

examples/tiff-ls.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
use std::borrow::Cow;
2+
3+
use tiff::decoder::Decoder;
4+
5+
fn main() -> Result<(), Box<dyn std::error::Error>> {
6+
let Some(image) = std::env::args_os().nth(1) else {
7+
eprintln!("Usage: decode FILE");
8+
return Ok(());
9+
};
10+
11+
let file = std::fs::File::open(image)?;
12+
let io = std::io::BufReader::new(file);
13+
let mut reader = Decoder::new(io)?;
14+
15+
loop {
16+
ls_dir(&mut reader);
17+
18+
if !reader.more_images() {
19+
break;
20+
}
21+
}
22+
23+
Ok(())
24+
}
25+
26+
fn ls_dir<R: std::io::Read + std::io::Seek>(reader: &mut Decoder<R>) {
27+
if let Some(ifd) = reader.ifd_pointer() {
28+
println!("Directory at {ifd:x}");
29+
}
30+
31+
println!("Name\tHex\tType\tCount");
32+
let ifd = reader.image_ifd();
33+
34+
for (tag, entry) in ifd.directory().iter() {
35+
let name: &'static str = match tag {
36+
tiff::tags::Tag::Artist => "Artist",
37+
tiff::tags::Tag::BitsPerSample => "BitsPerSample",
38+
tiff::tags::Tag::CellLength => "CellLength",
39+
tiff::tags::Tag::CellWidth => "CellWidth",
40+
tiff::tags::Tag::ColorMap => "ColorMap",
41+
tiff::tags::Tag::Compression => "Compression",
42+
tiff::tags::Tag::DateTime => "DateTime",
43+
tiff::tags::Tag::ExtraSamples => "ExtraSamples",
44+
tiff::tags::Tag::FillOrder => "FillOrder",
45+
tiff::tags::Tag::FreeByteCounts => "FreeByteCounts",
46+
tiff::tags::Tag::FreeOffsets => "FreeOffsets",
47+
tiff::tags::Tag::GrayResponseCurve => "GrayResponseCurve",
48+
tiff::tags::Tag::GrayResponseUnit => "GrayResponseUnit",
49+
tiff::tags::Tag::HostComputer => "HostComputer",
50+
tiff::tags::Tag::ImageDescription => "ImageDescription",
51+
tiff::tags::Tag::ImageLength => "ImageLength",
52+
tiff::tags::Tag::ImageWidth => "ImageWidth",
53+
tiff::tags::Tag::Make => "Make",
54+
tiff::tags::Tag::MaxSampleValue => "MaxSampleValue",
55+
tiff::tags::Tag::MinSampleValue => "MinSampleValue",
56+
tiff::tags::Tag::Model => "Model",
57+
tiff::tags::Tag::NewSubfileType => "NewSubfileType",
58+
tiff::tags::Tag::Orientation => "Orientation",
59+
tiff::tags::Tag::PhotometricInterpretation => "PhotometricInterpretation",
60+
tiff::tags::Tag::PlanarConfiguration => "PlanarConfiguration",
61+
tiff::tags::Tag::ResolutionUnit => "ResolutionUnit",
62+
tiff::tags::Tag::RowsPerStrip => "RowsPerStrip",
63+
tiff::tags::Tag::SamplesPerPixel => "SamplesPerPixel",
64+
tiff::tags::Tag::Software => "Software",
65+
tiff::tags::Tag::StripByteCounts => "StripByteCounts",
66+
tiff::tags::Tag::StripOffsets => "StripOffsets",
67+
tiff::tags::Tag::SubfileType => "SubfileType",
68+
tiff::tags::Tag::Threshholding => "Threshholding",
69+
tiff::tags::Tag::XResolution => "XResolution",
70+
tiff::tags::Tag::YResolution => "YResolution",
71+
tiff::tags::Tag::Predictor => "Predictor",
72+
tiff::tags::Tag::TileWidth => "TileWidth",
73+
tiff::tags::Tag::TileLength => "TileLength",
74+
tiff::tags::Tag::TileOffsets => "TileOffsets",
75+
tiff::tags::Tag::TileByteCounts => "TileByteCounts",
76+
tiff::tags::Tag::SubIfd => "SubIfd",
77+
tiff::tags::Tag::SampleFormat => "SampleFormat",
78+
tiff::tags::Tag::SMinSampleValue => "SMinSampleValue",
79+
tiff::tags::Tag::SMaxSampleValue => "SMaxSampleValue",
80+
tiff::tags::Tag::JPEGTables => "JPEGTables",
81+
tiff::tags::Tag::ChromaSubsampling => "ChromaSubsampling",
82+
tiff::tags::Tag::ChromaPositioning => "ChromaPositioning",
83+
tiff::tags::Tag::ModelPixelScaleTag => "ModelPixelScaleTag",
84+
tiff::tags::Tag::ModelTransformationTag => "ModelTransformationTag",
85+
tiff::tags::Tag::ModelTiepointTag => "ModelTiepointTag",
86+
tiff::tags::Tag::Copyright => "Copyright",
87+
tiff::tags::Tag::ExifDirectory => "ExifDirectory",
88+
tiff::tags::Tag::GpsDirectory => "GpsDirectory",
89+
tiff::tags::Tag::IccProfile => "IccProfile",
90+
tiff::tags::Tag::GeoKeyDirectoryTag => "GeoKeyDirectoryTag",
91+
tiff::tags::Tag::GeoDoubleParamsTag => "GeoDoubleParamsTag",
92+
tiff::tags::Tag::GeoAsciiParamsTag => "GeoAsciiParamsTag",
93+
tiff::tags::Tag::ExifVersion => "ExifVersion",
94+
tiff::tags::Tag::GdalNodata => "GdalNodata",
95+
_ => "<unknown>",
96+
};
97+
98+
let ty: Cow<'static, str> = match entry.field_type() {
99+
tiff::tags::Type::BYTE => "u8".into(),
100+
tiff::tags::Type::ASCII => "ascii".into(),
101+
tiff::tags::Type::SHORT => "u16".into(),
102+
tiff::tags::Type::LONG => "u32".into(),
103+
tiff::tags::Type::RATIONAL => "r32".into(),
104+
tiff::tags::Type::SBYTE => "i8".into(),
105+
tiff::tags::Type::UNDEFINED => "byte".into(),
106+
tiff::tags::Type::SSHORT => "s16".into(),
107+
tiff::tags::Type::SLONG => "s32".into(),
108+
tiff::tags::Type::SRATIONAL => "sr32".into(),
109+
tiff::tags::Type::FLOAT => "f32".into(),
110+
tiff::tags::Type::DOUBLE => "f64".into(),
111+
tiff::tags::Type::IFD => "ifd32".into(),
112+
tiff::tags::Type::LONG8 => "u64".into(),
113+
tiff::tags::Type::SLONG8 => "i64".into(),
114+
tiff::tags::Type::IFD8 => "ifd64".into(),
115+
other => format!("{:x}", other.to_u16()).into(),
116+
};
117+
118+
eprintln!(
119+
"{name:16}\t{tag:4x}\t{ty}\t{count}",
120+
tag = tag.to_u16(),
121+
count = entry.count(),
122+
);
123+
}
124+
}

src/decoder/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1538,7 +1538,7 @@ impl<R: Read + Seek> Decoder<R> {
15381538
}
15391539

15401540
/// Get the IFD decoder for our current image IFD.
1541-
fn image_ifd(&mut self) -> IfdDecoder<'_> {
1541+
pub fn image_ifd(&mut self) -> IfdDecoder<'_> {
15421542
IfdDecoder {
15431543
inner: tag_reader::TagReader {
15441544
decoder: &mut self.value_reader,
@@ -1885,6 +1885,11 @@ impl IfdDecoder<'_> {
18851885
pub fn get_tag_ascii_string(&mut self, tag: Tag) -> TiffResult<String> {
18861886
self.get_tag(tag)?.into_string()
18871887
}
1888+
1889+
/// Inspect the raw underlying directory.
1890+
pub fn directory(&self) -> &Directory {
1891+
self.inner.ifd
1892+
}
18881893
}
18891894

18901895
impl<'l> IfdDecoder<'l> {

src/tags.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::encoder::TiffValue;
2+
use core::fmt;
23

34
macro_rules! tags {
45
{
@@ -169,6 +170,18 @@ pub enum Tag(u16) unknown(
169170
// practice it just returns garbage and the validity does not matter greatly to us).
170171
pub struct IfdPointer(pub u64);
171172

173+
impl fmt::LowerHex for IfdPointer {
174+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175+
fmt::LowerHex::fmt(&self.0, f)
176+
}
177+
}
178+
179+
impl core::fmt::UpperHex for IfdPointer {
180+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181+
fmt::UpperHex::fmt(&self.0, f)
182+
}
183+
}
184+
172185
tags! {
173186
/// The type of an IFD entry (a 2 byte field).
174187
pub enum Type(u16) {

0 commit comments

Comments
 (0)