1use super::{
5 Entry, Result, TiffError, Value, apply_corr,
6 entry::RawEntry,
7 read_from_file,
8 reader::{EndianReader, ReadByteOrder},
9};
10use crate::{
11 bits::Endian,
12 rawsource::RawSource,
13 tags::{ExifTag, TiffCommonTag, TiffTag},
14};
15use byteorder::{LittleEndian, ReadBytesExt};
16use log::debug;
17use serde::{Deserialize, Serialize};
18use std::{
19 collections::{BTreeMap, HashMap},
20 io::{Read, Seek, SeekFrom},
21};
22
23const MAX_IFD_ENTRIES: usize = 4096;
24
25#[derive(Debug)]
26pub enum OffsetMode {
27 Absolute,
28 RelativeToIFD,
29}
30
31#[derive(Debug)]
32pub enum DataMode {
33 Strips,
34 Tiles,
35}
36
37#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
38pub struct IFD {
39 pub offset: u32,
40 pub base: u32,
41 pub corr: i32,
42 pub next_ifd: u32,
43 pub entries: BTreeMap<u16, Entry>,
44 pub endian: Endian,
45 pub sub: HashMap<u16, Vec<IFD>>,
46 pub chain: Vec<IFD>,
47}
48
49impl IFD {
51 pub fn new_root<R: Read + Seek>(reader: &mut R, base: u32) -> Result<IFD> {
53 Self::new_root_with_correction(reader, 0, base, 0, 10, &[TiffCommonTag::SubIFDs.into(), TiffCommonTag::ExifIFDPointer.into()])
54 }
55
56 pub fn new_root_with_correction<R: Read + Seek>(reader: &mut R, offset: u32, base: u32, corr: i32, max_chain: usize, sub_tags: &[u16]) -> Result<IFD> {
57 reader.seek(SeekFrom::Start((base + offset) as u64))?;
58 let endian = match reader.read_u16::<LittleEndian>()? {
59 0x4949 => Endian::Little,
60 0x4d4d => Endian::Big,
61 x => {
62 return Err(TiffError::General(format!("TIFF: don't know marker 0x{:x}", x)));
63 }
64 };
65 let mut reader = EndianReader::new(reader, endian);
66 let magic = reader.read_u16()?;
67 if magic != 42 {
68 return Err(TiffError::General(format!("Invalid magic marker for TIFF: {}", magic)));
69 }
70 let mut next_ifd = reader.read_u32()?;
71 if next_ifd == 0 {
72 return Err(TiffError::General("Invalid TIFF header, contains no root IFD".to_string()));
73 }
74
75 let reader = reader.into_inner();
76 let mut multi_sub_tags = vec![];
77 multi_sub_tags.extend_from_slice(sub_tags);
78
79 next_ifd = apply_corr(next_ifd, corr);
80 let mut root = IFD::new(reader, next_ifd, base, corr, endian, &multi_sub_tags)?;
81 if root.entries.is_empty() {
82 return Err(TiffError::General("TIFF is invalid, IFD must contain at least one entry".to_string()));
83 }
84 next_ifd = root.next_ifd;
85
86 while next_ifd != 0 {
87 next_ifd = apply_corr(next_ifd, corr);
88 let ifd = IFD::new(reader, next_ifd, base, corr, endian, &multi_sub_tags)?;
89 if ifd.entries.is_empty() {
90 return Err(TiffError::General("TIFF is invalid, IFD must contain at least one entry".to_string()));
91 }
92 next_ifd = ifd.next_ifd;
93 root.chain.push(ifd);
94
95 if root.chain.len() > max_chain && max_chain > 0 {
96 break;
97 }
98 }
99
100 Ok(root)
101 }
102
103 pub fn new<R: Read + Seek>(reader: &mut R, offset: u32, base: u32, corr: i32, endian: Endian, sub_tags: &[u16]) -> Result<IFD> {
104 reader.seek(SeekFrom::Start((base + offset) as u64))?;
105 let mut sub_ifd_offsets = HashMap::new();
106 let mut reader = EndianReader::new(reader, endian);
107 let entry_count = reader.read_u16()?;
108
109 if entry_count as usize > MAX_IFD_ENTRIES {
110 log::warn!(
111 "TIFF: IFD entry count {} is suspicious (limit {}). The file might be corrupt.",
112 entry_count,
113 MAX_IFD_ENTRIES
114 );
115 }
116
117 let mut entries = BTreeMap::new();
118 let mut sub = HashMap::new();
119 let mut next_pos = reader.position()?;
120 debug!("Parse entries");
121 let mut consecutive_errors = 0;
122
123 for i in 0..entry_count {
124 if i as usize >= MAX_IFD_ENTRIES {
125 log::warn!(
126 "TIFF: Reached maximum IFD entry limit ({}). Stopping parse to prevent infinite loops.",
127 MAX_IFD_ENTRIES
128 );
129 break;
130 }
131
132 if let Err(e) = reader.goto(next_pos) {
133 log::warn!("Truncated IFD: Could not seek to next entry position. Stopping parse. Error: {}", e);
134 break;
135 }
136
137 next_pos += 12;
138
139 let tag = match reader.read_u16() {
140 Ok(t) => t,
141 Err(e) => {
142 log::warn!("Truncated IFD: Could not read tag ID (Index {}). Stopping parse. Error: {}", i, e);
143 break;
144 }
145 };
146
147 match Entry::parse(&mut reader, base, corr, tag) {
148 Ok(entry) => {
149 consecutive_errors = 0;
150
151 if sub_tags.contains(&tag) {
152 match &entry.value {
153 Value::Long(offsets) => {
154 sub_ifd_offsets.insert(tag, offsets.clone());
155 }
156 Value::Unknown(tag, offsets) => {
157 sub_ifd_offsets.insert(*tag, vec![offsets[0] as u32]);
158 }
159 Value::Undefined(_) => {
160 sub_ifd_offsets.insert(tag, vec![entry.offset().unwrap() as u32]);
161 }
162 val => {
163 log::info!(
164 "Found IFD offset tag, but type mismatch: {:?}. Ignoring SubIFD parsing for tag 0x{:X}",
165 val,
166 tag
167 );
168 }
169 }
170 }
171 entries.insert(entry.tag, entry);
172 }
173 Err(err) => {
174 consecutive_errors += 1;
175 log::warn!("Failed to parse TIFF tag 0x{:X} (Index {}). Error: {:?}", tag, i, err);
176
177 if consecutive_errors >= 5 {
179 log::warn!("Too many consecutive parsing errors ({}). Stopping parse to prevent flood.", consecutive_errors);
180 break;
181 }
182 }
183 }
184 }
185
186 let next_ifd = match reader.read_u32() {
189 Ok(ptr) => ptr,
190 Err(e) => {
191 debug!(
192 "TIFF IFD reader failed to get next IFD pointer, fallback to 0 and continue. Original error was: {}",
193 e
194 );
195 0
196 }
197 };
198
199 let pos = reader.position()?;
201 let reader = reader.into_inner();
202 for subs in sub_ifd_offsets {
203 let mut ifds = Vec::new();
204 for offset in subs.1 {
205 match Self::new(reader, apply_corr(offset, corr), base, corr, endian, &[]) {
206 Ok(ifd) => ifds.push(ifd),
207 Err(err) => {
208 log::warn!("Error while processing TIFF sub-IFD for tag 0x{:X}, ignoring it: {}", subs.0, err);
209 }
210 };
211 }
212 sub.insert(subs.0, ifds);
213 }
214 EndianReader::new(reader, endian).goto(pos)?; Ok(IFD {
216 offset,
217 base,
218 corr,
219 next_ifd: if next_ifd == 0 { 0 } else { apply_corr(next_ifd, corr) },
220 entries,
221 endian,
222 sub,
223 chain: vec![],
224 })
225 }
226
227 pub fn copy_tag(dst: &mut Self, src: &Self, tag: impl Into<u16>) {
228 if let Some(entry) = src.get_entry(tag.into()) {
229 dst.entries.insert(entry.tag, entry.clone());
230 }
231 }
232
233 pub fn value_iter(&self) -> impl Iterator<Item = (&u16, &Value)> {
234 self.entries().iter().map(|(tag, entry)| (tag, &entry.value))
235 }
236
237 pub fn extend_sub_ifds<R: Read + Seek>(&mut self, reader: &mut R, tag: u16) -> Result<Option<&Vec<Self>>> {
289 if let Some(entry) = self.get_entry(tag) {
290 let mut subs = Vec::new();
291 match &entry.value {
292 Value::Long(offsets) => {
293 for off in offsets {
294 let ifd = Self::new_root_with_correction(reader, *off, self.base, self.corr, 10, &[])?;
295 subs.push(ifd);
296 }
297 self.sub.insert(tag, subs);
298 Ok(self.sub.get(&tag))
299 }
300 val => {
301 debug!("Found IFD offset tag, but type mismatch: {:?}", val);
302 todo!()
303 }
304 }
305 } else {
306 Ok(None)
307 }
308 }
309
310 pub fn extend_sub_ifds_custom<R, F>(&mut self, reader: &mut R, tag: u16, op: F) -> Result<Option<&Vec<Self>>>
311 where
312 R: Read + Seek,
313 F: FnOnce(&mut R, &IFD, &Entry) -> Result<Option<Vec<IFD>>>,
314 {
315 if let Some(entry) = self.get_entry(tag) {
316 if let Some(subs) = op(reader, self, entry)? {
317 self.sub.insert(tag, subs);
318 Ok(self.sub.get(&tag))
319 } else {
320 Ok(None)
321 }
322 } else {
323 Ok(None)
324 }
325 }
326
327 pub fn sub_ifds(&self) -> &HashMap<u16, Vec<IFD>> {
328 &self.sub
329 }
330
331 pub fn entry_count(&self) -> u16 {
332 self.entries.len() as u16
333 }
334
335 pub fn next_ifd(&self) -> u32 {
336 self.next_ifd
337 }
338
339 pub fn entries(&self) -> &BTreeMap<u16, Entry> {
340 &self.entries
341 }
342
343 pub fn get_entry<T: TiffTag>(&self, tag: T) -> Option<&Entry> {
344 self.entries.get(&tag.into())
345 }
346
347 pub fn get_entry_subs<T: TiffTag>(&self, tag: T) -> Option<&Entry> {
348 for subs in &self.sub {
349 for ifd in subs.1 {
350 if let Some(entry) = ifd.get_entry_recursive(tag) {
351 return Some(entry);
352 }
353 }
354 }
355 None
356 }
357
358 pub fn get_entry_recursive<T: TiffTag>(&self, tag: T) -> Option<&Entry> {
359 self.entries.get(&tag.into()).or_else(|| self.get_entry_subs(tag))
360 }
361
362 pub fn get_entry_raw<'a, T: TiffTag, R: Read + Seek>(&'a self, tag: T, file: &mut R) -> Result<Option<RawEntry<'a>>> {
363 if let Some(entry) = self.get_entry(tag) {
364 return Ok(Some(RawEntry {
365 entry,
366 endian: self.endian,
367 data: read_from_file(file, self.base + entry.offset().unwrap() as u32, entry.byte_size())?,
368 }));
369 }
370 Ok(None)
371 }
372
373 pub fn get_entry_raw_with_len<'a, T: TiffTag, R: Read + Seek>(&'a self, tag: T, file: &mut R, len: usize) -> Result<Option<RawEntry<'a>>> {
375 if let Some(entry) = self.get_entry(tag) {
376 return Ok(Some(RawEntry {
377 entry,
378 endian: self.endian,
379 data: read_from_file(file, self.base + entry.offset().unwrap() as u32, len)?,
380 }));
381 }
382 Ok(None)
383 }
384
385 pub fn get_sub_ifd_all<T: TiffTag>(&self, tag: T) -> Option<&Vec<IFD>> {
386 self.sub.get(&tag.into())
387 }
388
389 pub fn get_sub_ifd<T: TiffTag>(&self, tag: T) -> Option<&IFD> {
390 if let Some(ifds) = self.get_sub_ifd_all(tag) {
391 if ifds.len() == 1 {
392 ifds.get(0)
393 } else {
394 log::warn!(
395 "get_sub_ifd() for tag {:?} found more IFDs than expected: {}. Fallback to first IFD!",
396 tag,
397 ifds.len()
398 );
399 ifds.get(0)
400 }
401 } else {
402 None
403 }
404 }
405
406 pub fn get_new_sub_file_type(&self) -> Option<u16> {
407 if let Some(entry) = self.get_entry(TiffCommonTag::NewSubFileType) {
408 Some(entry.force_u16(0))
409 } else {
410 None
411 }
412 }
413
414 pub fn find_ifds_with_tag<T: TiffTag>(&self, tag: T) -> Vec<&IFD> {
415 let mut ifds = Vec::new();
416 if self.get_entry(tag).is_some() {
417 ifds.push(self);
418 }
419 for subs in self.sub_ifds() {
421 for ifd in subs.1 {
422 ifds.append(&mut ifd.find_ifds_with_tag(tag));
423 }
424 }
425 ifds
426 }
427
428 pub fn find_first_ifd_with_tag<T: TiffTag>(&self, tag: T) -> Option<&IFD> {
429 self.find_ifds_with_tag(tag).get(0).copied()
430 }
431
432 pub fn has_entry<T: TiffTag>(&self, tag: T) -> bool {
454 self.get_entry(tag).is_some()
455 }
456
457 pub fn sub_buf<R: Read + Seek>(&self, reader: &mut R, offset: usize, len: usize) -> Result<Vec<u8>> {
458 let mut buf = vec![0; len];
460 reader.seek(SeekFrom::Start(self.base as u64 + offset as u64))?;
461 reader.read_exact(&mut buf)?;
462 Ok(buf)
463 }
464
465 pub fn contains_singlestrip_image(&self) -> bool {
466 self.get_entry(TiffCommonTag::StripOffsets).map(Entry::count).unwrap_or(0) == 1
467 }
468
469 pub fn singlestrip_data_rawsource<'a>(&self, rawsource: &'a RawSource) -> Result<&'a [u8]> {
470 assert!(self.contains_singlestrip_image());
471
472 let offset = self
473 .get_entry(TiffCommonTag::StripOffsets)
474 .ok_or_else(|| TiffError::General(("tag not found").to_string()))?
475 .value
476 .force_u32(0);
477 let len = self
478 .get_entry(TiffCommonTag::StripByteCounts)
479 .ok_or_else(|| TiffError::General(("tag not found").to_string()))?
480 .value
481 .force_usize(0);
482
483 Ok(rawsource.subview((self.base + offset) as u64, len as u64)?)
484 }
485
486 pub fn strip_data<'a>(&self, rawsource: &'a RawSource) -> Result<(Vec<&'a [u8]>, Option<&'a [u8]>)> {
490 if !self.has_entry(TiffCommonTag::StripOffsets) {
491 return Err(TiffError::General("IFD contains no strip data".into()));
492 }
493 let offsets = if let Some(Entry { value: Value::Long(data), .. }) = self.get_entry(TiffCommonTag::StripOffsets) {
494 data
495 } else {
496 return Err(TiffError::General("Invalid datatype for StripOffsets".to_string()));
497 };
498 let sizes = if let Some(Entry { value: Value::Long(data), .. }) = self.get_entry(TiffCommonTag::StripByteCounts) {
499 data
500 } else {
501 return Err(TiffError::General("Invalid datatype for StripByteCounts".to_string()));
502 };
503
504 if offsets.len() != sizes.len() {
505 return Err(TiffError::General(format!(
506 "Can't get data from strips: offsets has len {} but sizes has len {}",
507 offsets.len(),
508 sizes.len()
509 )));
510 }
511
512 let (is_continous, end_off) =
514 offsets.iter().zip(sizes.iter()).fold(
515 (true, offsets[0]),
516 |acc, val| {
517 if acc.0 && acc.1 == *val.0 { (true, acc.1 + *val.1) } else { (false, 0) }
518 },
519 );
520
521 let mut subviews = Vec::with_capacity(offsets.len());
522 for (offset, size) in offsets.iter().zip(sizes.iter()) {
523 subviews.push(rawsource.subview((self.base + *offset) as u64, *size as u64)?);
524 }
525
526 let continous = if is_continous {
527 Some(rawsource.subview((self.base + offsets[0]) as u64, (end_off - offsets[0]) as u64)?)
528 } else {
529 None
530 };
531
532 Ok((subviews, continous))
533 }
534
535 pub fn tile_data<'a>(&self, rawsource: &'a RawSource) -> Result<Vec<&'a [u8]>> {
536 let offsets = if let Some(Entry { value: Value::Long(data), .. }) = self.get_entry(TiffCommonTag::TileOffsets) {
537 data
538 } else {
539 return Err(TiffError::General("Invalid datatype for TileOffsets".to_string()));
540 };
541
542 let byte_counts = if let Some(Entry { value: Value::Long(data), .. }) = self.get_entry(TiffCommonTag::TileByteCounts) {
543 data
544 } else {
545 return Err(TiffError::General("Invalid datatype for TileByteCounts".to_string()));
546 };
547
548 let mut tile_slices = Vec::with_capacity(offsets.len());
549 offsets.iter().zip(byte_counts.iter()).for_each(|(offset, size)| {
550 tile_slices.push(rawsource.subview(*offset as u64, *size as u64).map_err(TiffError::Io));
551 });
552 Ok(tile_slices.into_iter().collect::<Result<Vec<_>>>()?)
553 }
554
555 pub fn data_mode(&self) -> Result<DataMode> {
557 if self.has_entry(TiffCommonTag::StripOffsets) {
558 Ok(DataMode::Strips)
559 } else if self.has_entry(TiffCommonTag::TileOffsets) {
560 Ok(DataMode::Tiles)
561 } else {
562 Err(TiffError::General("IFD has no StripOffsets or TileOffsets tag".into()))
563 }
564 }
565
566 pub fn parse_makernote<R: Read + Seek>(&self, reader: &mut R, offset_mode: OffsetMode, sub_tags: &[u16]) -> Result<Option<IFD>> {
567 if let Some(exif) = self.get_entry(ExifTag::MakerNotes) {
568 let offset = exif.offset().unwrap() as u32;
569 debug!("Makernote offset: {}", offset);
570 match &exif.value {
571 Value::Undefined(data) => {
572 let mut off = 0;
573 let mut endian = self.endian;
574
575 if data[0..5] == b"OLYMP"[..] {
577 off += 8;
578 if data[0..7] == b"OLYMPUS"[..] {
579 off += 4;
580 }
581 }
582
583 if data[0..5] == b"EPSON"[..] {
585 off += 8;
586 }
587
588 if data[0..8] == b"FUJIFILM"[..] {
590 off += 12;
591 }
592
593 if data[0..9] == b"SONY DSC "[..] {
595 off += 12;
596 }
597
598 if data[0..4] == b"AOC\0"[..] {
600 off += 4;
601 }
602
603 if data[0..6] == b"PENTAX"[..] {
605 off += 8;
606 let endian = if data[off..off + 2] == b"II"[..] { Endian::Little } else { Endian::Big };
607 let corr = offset as i32;
610 return Ok(Some(IFD::new(reader, offset + 10, self.base, corr, endian, sub_tags)?));
612 }
613
614 if data[0..7] == b"Nikon\0\x02"[..] {
615 off += 10;
616 let endian = if data[off..off + 2] == b"II"[..] { Endian::Little } else { Endian::Big };
617 return Ok(Some(IFD::new(reader, 8, self.base + offset + 10, 0, endian, sub_tags)?));
618 }
619
620 if data[off..off + 2] == b"II"[..] {
622 off += 2;
623 endian = Endian::Little;
624 }
625 if data[off..off + 2] == b"MM"[..] {
626 off += 2;
627 endian = Endian::Big;
628 }
629
630 match offset_mode {
631 OffsetMode::Absolute => Ok(Some(IFD::new(reader, offset + off as u32, self.base, self.corr, endian, sub_tags)?)),
632 OffsetMode::RelativeToIFD => {
633 let corr = offset + off as u32;
635 Ok(Some(IFD::new(reader, offset + off as u32, self.base, corr as i32, endian, sub_tags)?))
636 }
637 }
638 }
639 _ => Err(TiffError::General("EXIF makernote has unknown type".to_string())),
640 }
641 } else {
642 Ok(None)
643 }
644 }
645
646 pub fn dump<T: TiffTag>(&self, limit: usize) -> Vec<String> {
647 let mut out = Vec::new();
648 out.push(format!("IFD entries: {}\n", self.entries.len()));
649 out.push(format!("{0:<34} | {1:<10} | {2:<6} | {3}\n", "Tag", "Type", "Count", "Data"));
650 for (tag, entry) in &self.entries {
651 let mut line = String::new();
652 let tag_name = {
653 if let Ok(name) = T::try_from(*tag) {
654 format!("{:?}", name)
655 } else {
656 format!("<?{}>", tag)
657 }
658 };
659 line.push_str(&format!(
660 "{0:#06x} : {0:<6} {1:<20}| {2:<10} | {3:<6} | ",
661 tag,
662 tag_name,
663 entry.type_name(),
664 entry.count()
665 ));
666 line.push_str(&entry.visual_rep(limit));
667 out.push(line);
668 }
669 for subs in self.sub_ifds().iter() {
670 for (i, sub) in subs.1.iter().enumerate() {
671 out.push(format!("SubIFD({}:{})", subs.0, i));
672 for line in sub.dump::<T>(limit) {
673 out.push(format!(" {}", line));
674 }
675 }
676 }
677 out
678 }
679}