1use binrw::{BinRead, BinReaderExt, binread};
10use std::collections::{HashMap, HashSet};
11use std::io::{Read, Seek, SeekFrom};
12
13use crate::error::{ParseError, RawError, RawResult};
14use crate::tiff::tags::TiffTag;
15use crate::tiff::types::{ByteOrder, Rational, SRational, TiffType, TiffValue};
16
17pub const TIFF_MAGIC: u16 = 42;
19pub const BIGTIFF_MAGIC: u16 = 43;
21
22#[derive(Debug, Clone, BinRead)]
28pub struct RawTiffHeader {
29 pub byte_order: ByteOrder,
31 #[br(is_little = matches!(byte_order, ByteOrder::LittleEndian))]
33 pub magic: u16,
34 #[br(is_little = matches!(byte_order, ByteOrder::LittleEndian))]
36 pub ifd0_offset: u32,
37}
38
39#[derive(Debug, Clone, BinRead)]
41pub struct RawBigTiffHeader {
42 pub byte_order: ByteOrder,
44 #[br(is_little = matches!(byte_order, ByteOrder::LittleEndian))]
46 pub magic: u16,
47 #[br(is_little = matches!(byte_order, ByteOrder::LittleEndian))]
49 pub offset_bytesize: u16,
50 #[br(is_little = matches!(byte_order, ByteOrder::LittleEndian))]
52 pub always_zero: u16,
53 #[br(is_little = matches!(byte_order, ByteOrder::LittleEndian))]
55 pub ifd0_offset: u64,
56}
57
58#[binread]
60#[derive(Debug, Clone)]
61#[br(import { is_little: bool })]
62pub struct RawIfdEntry {
63 #[br(is_little = is_little)]
65 pub tag_id: u16,
66 #[br(is_little = is_little)]
68 pub data_type: u16,
69 #[br(is_little = is_little)]
71 pub count: u32,
72 #[br(is_little = is_little)]
74 pub value_offset: u32,
75}
76
77#[binread]
79#[derive(Debug, Clone)]
80#[br(import { is_little: bool })]
81pub struct RawBigTiffIfdEntry {
82 #[br(is_little = is_little)]
84 pub tag_id: u16,
85 #[br(is_little = is_little)]
87 pub data_type: u16,
88 #[br(is_little = is_little)]
90 pub count: u64,
91 #[br(is_little = is_little)]
93 pub value_offset: u64,
94}
95
96#[derive(Debug, Clone)]
102pub struct TiffHeader {
103 pub byte_order: ByteOrder,
105 pub magic: u16,
107 pub ifd0_offset: u64,
109 pub is_bigtiff: bool,
111}
112
113impl TiffHeader {
114 pub fn parse<R: Read + Seek>(reader: &mut R) -> RawResult<Self> {
116 reader.seek(SeekFrom::Start(0))?;
117
118 let raw_header: RawTiffHeader = reader
120 .read_ne()
121 .map_err(|_| RawError::Parse(ParseError::InvalidByteOrder(0)))?;
122
123 let is_bigtiff = match raw_header.magic {
125 TIFF_MAGIC => false,
126 BIGTIFF_MAGIC => true,
127 _ => {
128 return Err(RawError::Parse(ParseError::InvalidMagic {
129 expected: TIFF_MAGIC,
130 found: raw_header.magic,
131 }));
132 }
133 };
134
135 let ifd0_offset = if is_bigtiff {
137 reader.seek(SeekFrom::Start(0))?;
138 let bigtiff_header: RawBigTiffHeader = reader
139 .read_ne()
140 .map_err(|e| RawError::Parse(ParseError::BinaryParse(e.to_string())))?;
141 bigtiff_header.ifd0_offset
142 } else {
143 raw_header.ifd0_offset as u64
144 };
145
146 Ok(TiffHeader {
147 byte_order: raw_header.byte_order,
148 magic: raw_header.magic,
149 ifd0_offset,
150 is_bigtiff,
151 })
152 }
153}
154
155#[derive(Debug, Clone)]
157pub struct IfdEntry {
158 pub tag_id: u16,
160 pub data_type: u16,
162 pub count: u64,
164 pub value_offset: u64,
166 pub tiff_type: Option<TiffType>,
168 pub tag: Option<TiffTag>,
170}
171
172impl IfdEntry {
173 pub fn is_inline(&self, is_bigtiff: bool) -> bool {
175 if let Some(tiff_type) = self.tiff_type {
176 if is_bigtiff {
177 tiff_type.fits_inline_bigtiff(self.count)
178 } else {
179 tiff_type.fits_inline(self.count as u32)
180 }
181 } else {
182 false }
184 }
185
186 pub fn value_size(&self) -> u64 {
188 self.tiff_type
189 .map(|t| t.size() as u64 * self.count)
190 .unwrap_or(0)
191 }
192}
193
194#[derive(Debug, Clone)]
196pub struct Ifd {
197 pub offset: u64,
199 pub entries: HashMap<TiffTag, IfdEntry>,
201 pub other_tags: HashMap<u16, IfdEntry>,
203 pub next_ifd_offset: u64,
205 pub sub_ifds: Vec<Ifd>,
207 pub exif_ifd: Option<Box<Ifd>>,
209 pub gps_ifd: Option<Box<Ifd>>,
211}
212
213impl Ifd {
214 pub fn get(&self, tag: TiffTag) -> Option<&IfdEntry> {
216 self.entries.get(&tag)
217 }
218
219 pub fn contains(&self, tag: TiffTag) -> bool {
221 self.entries.contains_key(&tag)
222 }
223
224 pub fn has_other_tags(&self) -> bool {
226 !self.other_tags.is_empty()
227 }
228
229 pub fn other_tag_ids(&self) -> Vec<u16> {
231 self.other_tags.keys().copied().collect()
232 }
233
234 pub fn all_tag_ids(&self) -> Vec<u16> {
236 let mut ids: Vec<u16> = self.entries.keys().map(|t| t.as_u16()).collect();
237 ids.extend(self.other_tags.keys().copied());
238 ids.sort();
239 ids
240 }
241}
242
243pub struct TiffParser<R> {
245 reader: R,
246 header: TiffHeader,
247 ifd_cache: HashMap<u64, Ifd>,
249 visited_offsets: HashSet<u64>,
251}
252
253impl<R: Read + Seek> TiffParser<R> {
254 pub fn new(mut reader: R) -> RawResult<Self> {
256 let header = TiffHeader::parse(&mut reader)?;
257
258 Ok(TiffParser {
259 reader,
260 header,
261 ifd_cache: HashMap::new(),
262 visited_offsets: HashSet::new(),
263 })
264 }
265
266 pub fn header(&self) -> &TiffHeader {
268 &self.header
269 }
270
271 pub fn byte_order(&self) -> ByteOrder {
273 self.header.byte_order
274 }
275
276 pub fn is_bigtiff(&self) -> bool {
278 self.header.is_bigtiff
279 }
280
281 pub fn parse_ifd_at(&mut self, offset: u64) -> RawResult<Ifd> {
283 if self.visited_offsets.contains(&offset) {
285 return Err(RawError::Parse(ParseError::CircularReference(offset)));
286 }
287 self.visited_offsets.insert(offset);
288
289 if let Some(ifd) = self.ifd_cache.get(&offset) {
291 return Ok(ifd.clone());
292 }
293
294 self.reader.seek(SeekFrom::Start(offset))?;
295
296 let entry_count: u64 = if self.header.is_bigtiff {
298 self.read_u64()?
299 } else {
300 self.read_u16()? as u64
301 };
302
303 if entry_count > 65535 {
305 return Err(RawError::Parse(ParseError::InvalidIfd {
306 offset,
307 reason: format!("Entry count {} is unreasonably large", entry_count),
308 }));
309 }
310
311 let mut entries = HashMap::new();
312 let mut other_tags = HashMap::new();
313
314 for _ in 0..entry_count {
316 let entry = self.parse_ifd_entry()?;
317
318 if let Some(tag) = entry.tag {
319 entries.insert(tag, entry);
320 } else {
321 other_tags.insert(entry.tag_id, entry);
323 }
324 }
325
326 let next_ifd_offset = if self.header.is_bigtiff {
328 self.read_u64()?
329 } else {
330 self.read_u32()? as u64
331 };
332
333 let mut ifd = Ifd {
334 offset,
335 entries,
336 other_tags,
337 next_ifd_offset,
338 sub_ifds: Vec::new(),
339 exif_ifd: None,
340 gps_ifd: None,
341 };
342
343 if let Some(sub_ifd_entry) = ifd.entries.get(&TiffTag::SubIFDs).cloned() {
345 let offsets = self.read_value_as_u64_vec(&sub_ifd_entry)?;
346 for sub_offset in offsets {
347 if sub_offset != 0 {
348 match self.parse_ifd_at(sub_offset) {
349 Ok(sub_ifd) => ifd.sub_ifds.push(sub_ifd),
350 Err(e) => {
351 tracing::warn!(
352 "Failed to parse SubIFD at offset {}: {}",
353 sub_offset,
354 e
355 );
356 }
357 }
358 }
359 }
360 }
361
362 if let Some(exif_entry) = ifd.entries.get(&TiffTag::ExifIFDPointer).cloned()
364 && let Some(exif_offset) = self.read_value_as_u64(&exif_entry)?
365 && exif_offset != 0
366 {
367 match self.parse_ifd_at(exif_offset) {
368 Ok(exif_ifd) => ifd.exif_ifd = Some(Box::new(exif_ifd)),
369 Err(e) => {
370 tracing::warn!("Failed to parse EXIF IFD at offset {}: {}", exif_offset, e);
371 }
372 }
373 }
374
375 if let Some(gps_entry) = ifd.entries.get(&TiffTag::GPSInfoIFDPointer).cloned()
377 && let Some(gps_offset) = self.read_value_as_u64(&gps_entry)?
378 && gps_offset != 0
379 {
380 match self.parse_ifd_at(gps_offset) {
381 Ok(gps_ifd) => ifd.gps_ifd = Some(Box::new(gps_ifd)),
382 Err(e) => {
383 tracing::warn!("Failed to parse GPS IFD at offset {}: {}", gps_offset, e);
384 }
385 }
386 }
387
388 self.ifd_cache.insert(offset, ifd.clone());
390
391 Ok(ifd)
392 }
393
394 fn parse_ifd_entry(&mut self) -> RawResult<IfdEntry> {
396 let is_little = matches!(self.header.byte_order, ByteOrder::LittleEndian);
397
398 let (tag_id, data_type, count, value_offset) = if self.header.is_bigtiff {
399 let raw: RawBigTiffIfdEntry = self
400 .reader
401 .read_ne_args::<RawBigTiffIfdEntry>(binrw::args! { is_little })
402 .map_err(|e| RawError::Parse(ParseError::BinaryParse(e.to_string())))?;
403 (raw.tag_id, raw.data_type, raw.count, raw.value_offset)
404 } else {
405 let raw: RawIfdEntry = self
406 .reader
407 .read_ne_args::<RawIfdEntry>(binrw::args! { is_little })
408 .map_err(|e| RawError::Parse(ParseError::BinaryParse(e.to_string())))?;
409 (
410 raw.tag_id,
411 raw.data_type,
412 raw.count as u64,
413 raw.value_offset as u64,
414 )
415 };
416
417 let tiff_type = TiffType::from_u16(data_type);
418 let tag = TiffTag::from_u16(tag_id);
419
420 Ok(IfdEntry {
421 tag_id,
422 data_type,
423 count,
424 value_offset,
425 tiff_type,
426 tag,
427 })
428 }
429
430 pub fn walk_ifd_chain(&mut self) -> RawResult<Vec<Ifd>> {
432 self.visited_offsets.clear();
433 let mut ifds = Vec::new();
434 let mut offset = self.header.ifd0_offset;
435
436 while offset != 0 {
437 let ifd = self.parse_ifd_at(offset)?;
438 offset = ifd.next_ifd_offset;
439 ifds.push(ifd);
440 }
441
442 Ok(ifds)
443 }
444
445 pub fn parse_ifd0(&mut self) -> RawResult<Ifd> {
447 self.visited_offsets.clear();
448 self.parse_ifd_at(self.header.ifd0_offset)
449 }
450
451 pub fn parse_ifd(&mut self, offset: u64) -> RawResult<Ifd> {
453 self.parse_ifd_at(offset)
454 }
455
456 pub fn read_value(&mut self, entry: &IfdEntry) -> RawResult<TiffValue> {
458 let tiff_type = entry
459 .tiff_type
460 .ok_or(RawError::Parse(ParseError::UnknownDataType(
461 entry.data_type,
462 )))?;
463
464 let is_inline = entry.is_inline(self.header.is_bigtiff);
466
467 if !is_inline {
468 self.reader.seek(SeekFrom::Start(entry.value_offset))?;
470 }
471
472 let count = entry.count as usize;
474
475 match tiff_type {
476 TiffType::Byte => {
477 let mut data = vec![0u8; count];
478 if is_inline {
479 let value32 = entry.value_offset as u32;
482 let bytes = match self.header.byte_order {
483 ByteOrder::LittleEndian => value32.to_le_bytes(),
484 ByteOrder::BigEndian => value32.to_be_bytes(),
485 };
486 let copy_count = count.min(4);
487 data[..copy_count].copy_from_slice(&bytes[..copy_count]);
488 } else {
489 self.reader.read_exact(&mut data)?;
490 }
491 Ok(TiffValue::Bytes(data))
492 }
493 TiffType::Ascii => {
494 let mut data = vec![0u8; count];
495 if is_inline {
496 let value32 = entry.value_offset as u32;
497 let bytes = match self.header.byte_order {
498 ByteOrder::LittleEndian => value32.to_le_bytes(),
499 ByteOrder::BigEndian => value32.to_be_bytes(),
500 };
501 let copy_count = count.min(4);
502 data[..copy_count].copy_from_slice(&bytes[..copy_count]);
503 } else {
504 self.reader.read_exact(&mut data)?;
505 }
506 let s = String::from_utf8_lossy(&data)
508 .trim_end_matches('\0')
509 .trim()
510 .to_string();
511 Ok(TiffValue::Ascii(s))
512 }
513 TiffType::Short => {
514 let mut values = Vec::with_capacity(count);
515 if is_inline {
516 let value32 = entry.value_offset as u32;
529 let bytes = match self.header.byte_order {
530 ByteOrder::LittleEndian => {
531 value32.to_le_bytes()
533 }
534 ByteOrder::BigEndian => {
535 value32.to_be_bytes()
537 }
538 };
539 for i in 0..count {
540 let idx = i * 2;
541 if idx + 1 < 4 {
542 let v = match self.header.byte_order {
543 ByteOrder::LittleEndian => {
544 u16::from_le_bytes([bytes[idx], bytes[idx + 1]])
545 }
546 ByteOrder::BigEndian => {
547 u16::from_be_bytes([bytes[idx], bytes[idx + 1]])
548 }
549 };
550 values.push(v);
551 }
552 }
553 } else {
554 for _ in 0..count {
555 values.push(self.read_u16()?);
556 }
557 }
558 Ok(TiffValue::Shorts(values))
559 }
560 TiffType::Long | TiffType::Ifd => {
561 let mut values = Vec::with_capacity(count);
562 if is_inline && count == 1 {
563 values.push(entry.value_offset as u32);
564 } else {
565 for _ in 0..count {
566 values.push(self.read_u32()?);
567 }
568 }
569 Ok(TiffValue::Longs(values))
570 }
571 TiffType::Rational => {
572 let mut values = Vec::with_capacity(count);
573 for _ in 0..count {
574 let num = self.read_u32()?;
575 let den = self.read_u32()?;
576 values.push(Rational::new(num, den));
577 }
578 Ok(TiffValue::Rationals(values))
579 }
580 TiffType::SByte => {
581 let mut data = vec![0u8; count];
582 if is_inline {
583 let bytes = entry.value_offset.to_le_bytes();
584 data.copy_from_slice(&bytes[..count.min(8)]);
585 } else {
586 self.reader.read_exact(&mut data)?;
587 }
588 let signed: Vec<i8> = data.into_iter().map(|b| b as i8).collect();
589 Ok(TiffValue::SBytes(signed))
590 }
591 TiffType::Undefined => {
592 let mut data = vec![0u8; count];
593 if is_inline {
594 let bytes = entry.value_offset.to_le_bytes();
595 data.copy_from_slice(&bytes[..count.min(8)]);
596 } else {
597 self.reader.read_exact(&mut data)?;
598 }
599 Ok(TiffValue::Undefined(data))
600 }
601 TiffType::SShort => {
602 let mut values = Vec::with_capacity(count);
603 if is_inline {
604 let bytes = entry.value_offset.to_le_bytes();
605 for i in 0..count {
606 let idx = i * 2;
607 if idx + 1 < 8 {
608 let v = match self.header.byte_order {
609 ByteOrder::LittleEndian => {
610 i16::from_le_bytes([bytes[idx], bytes[idx + 1]])
611 }
612 ByteOrder::BigEndian => {
613 i16::from_be_bytes([bytes[idx], bytes[idx + 1]])
614 }
615 };
616 values.push(v);
617 }
618 }
619 } else {
620 for _ in 0..count {
621 values.push(self.read_i16()?);
622 }
623 }
624 Ok(TiffValue::SShorts(values))
625 }
626 TiffType::SLong => {
627 let mut values = Vec::with_capacity(count);
628 if is_inline && count == 1 {
629 values.push(entry.value_offset as i32);
630 } else {
631 for _ in 0..count {
632 values.push(self.read_i32()?);
633 }
634 }
635 Ok(TiffValue::SLongs(values))
636 }
637 TiffType::SRational => {
638 let mut values = Vec::with_capacity(count);
639 for _ in 0..count {
640 let num = self.read_i32()?;
641 let den = self.read_i32()?;
642 values.push(SRational::new(num, den));
643 }
644 Ok(TiffValue::SRationals(values))
645 }
646 TiffType::Float => {
647 let mut values = Vec::with_capacity(count);
648 for _ in 0..count {
649 values.push(self.read_f32()?);
650 }
651 Ok(TiffValue::Floats(values))
652 }
653 TiffType::Double => {
654 let mut values = Vec::with_capacity(count);
655 for _ in 0..count {
656 values.push(self.read_f64()?);
657 }
658 Ok(TiffValue::Doubles(values))
659 }
660 TiffType::Long8 | TiffType::Ifd8 => {
661 let mut values = Vec::with_capacity(count);
662 if is_inline && count == 1 {
663 values.push(entry.value_offset);
664 } else {
665 for _ in 0..count {
666 values.push(self.read_u64()?);
667 }
668 }
669 Ok(TiffValue::Long8s(values))
670 }
671 TiffType::SLong8 => {
672 let mut values = Vec::with_capacity(count);
673 if is_inline && count == 1 {
674 values.push(entry.value_offset as i64);
675 } else {
676 for _ in 0..count {
677 values.push(self.read_i64()?);
678 }
679 }
680 Ok(TiffValue::SLong8s(values))
681 }
682 }
683 }
684
685 fn read_value_as_u64(&mut self, entry: &IfdEntry) -> RawResult<Option<u64>> {
687 if entry.is_inline(self.header.is_bigtiff) {
688 Ok(Some(entry.value_offset))
689 } else {
690 self.reader.seek(SeekFrom::Start(entry.value_offset))?;
691 Ok(Some(self.read_u32()? as u64))
692 }
693 }
694
695 fn read_value_as_u64_vec(&mut self, entry: &IfdEntry) -> RawResult<Vec<u64>> {
697 let count = entry.count as usize;
698 let mut values = Vec::with_capacity(count);
699
700 if entry.is_inline(self.header.is_bigtiff) && count == 1 {
701 values.push(entry.value_offset);
702 } else {
703 self.reader.seek(SeekFrom::Start(entry.value_offset))?;
704 for _ in 0..count {
705 let v = if self.header.is_bigtiff {
706 self.read_u64()?
707 } else {
708 self.read_u32()? as u64
709 };
710 values.push(v);
711 }
712 }
713
714 Ok(values)
715 }
716
717 fn read_u16(&mut self) -> RawResult<u16> {
722 Ok(match self.header.byte_order {
723 ByteOrder::LittleEndian => self.reader.read_le()?,
724 ByteOrder::BigEndian => self.reader.read_be()?,
725 })
726 }
727
728 fn read_i16(&mut self) -> RawResult<i16> {
729 Ok(match self.header.byte_order {
730 ByteOrder::LittleEndian => self.reader.read_le()?,
731 ByteOrder::BigEndian => self.reader.read_be()?,
732 })
733 }
734
735 fn read_u32(&mut self) -> RawResult<u32> {
736 Ok(match self.header.byte_order {
737 ByteOrder::LittleEndian => self.reader.read_le()?,
738 ByteOrder::BigEndian => self.reader.read_be()?,
739 })
740 }
741
742 fn read_i32(&mut self) -> RawResult<i32> {
743 Ok(match self.header.byte_order {
744 ByteOrder::LittleEndian => self.reader.read_le()?,
745 ByteOrder::BigEndian => self.reader.read_be()?,
746 })
747 }
748
749 fn read_u64(&mut self) -> RawResult<u64> {
750 Ok(match self.header.byte_order {
751 ByteOrder::LittleEndian => self.reader.read_le()?,
752 ByteOrder::BigEndian => self.reader.read_be()?,
753 })
754 }
755
756 fn read_i64(&mut self) -> RawResult<i64> {
757 Ok(match self.header.byte_order {
758 ByteOrder::LittleEndian => self.reader.read_le()?,
759 ByteOrder::BigEndian => self.reader.read_be()?,
760 })
761 }
762
763 fn read_f32(&mut self) -> RawResult<f32> {
764 Ok(match self.header.byte_order {
765 ByteOrder::LittleEndian => self.reader.read_le()?,
766 ByteOrder::BigEndian => self.reader.read_be()?,
767 })
768 }
769
770 fn read_f64(&mut self) -> RawResult<f64> {
771 Ok(match self.header.byte_order {
772 ByteOrder::LittleEndian => self.reader.read_le()?,
773 ByteOrder::BigEndian => self.reader.read_be()?,
774 })
775 }
776
777 pub fn seek_to(&mut self, offset: u64) -> RawResult<()> {
783 self.reader.seek(SeekFrom::Start(offset))?;
784 Ok(())
785 }
786
787 pub fn read_bytes(&mut self, count: usize) -> RawResult<Vec<u8>> {
789 let mut buffer = vec![0u8; count];
790 self.reader.read_exact(&mut buffer)?;
791 Ok(buffer)
792 }
793
794 pub fn file_size(&mut self) -> RawResult<u64> {
796 let current = self.reader.stream_position()?;
797 let size = self.reader.seek(SeekFrom::End(0))?;
798 self.reader.seek(SeekFrom::Start(current))?;
799 Ok(size)
800 }
801
802 pub fn validate_complete(&mut self) -> RawResult<()> {
811 let file_size = self.file_size()?;
812
813 if self.header.ifd0_offset >= file_size {
815 return Err(RawError::Parse(ParseError::OffsetOutOfBounds {
816 offset: self.header.ifd0_offset,
817 size: 0,
818 file_size,
819 }));
820 }
821
822 let ifds = self.walk_ifd_chain()?;
824 for ifd in &ifds {
825 self.validate_ifd(ifd, file_size)?;
826 }
827
828 Ok(())
829 }
830
831 fn validate_ifd(&mut self, ifd: &Ifd, file_size: u64) -> RawResult<()> {
833 for entry in ifd.entries.values().chain(ifd.other_tags.values()) {
835 if !entry.is_inline(self.header.is_bigtiff) {
836 let end = entry.value_offset.saturating_add(entry.value_size());
837 if end > file_size {
838 return Err(RawError::Parse(ParseError::OffsetOutOfBounds {
839 offset: entry.value_offset,
840 size: entry.value_size(),
841 file_size,
842 }));
843 }
844 }
845 }
846
847 for sub_ifd in &ifd.sub_ifds {
849 self.validate_ifd(sub_ifd, file_size)?;
850 }
851
852 if let Some(ref exif_ifd) = ifd.exif_ifd {
854 self.validate_ifd(exif_ifd, file_size)?;
855 }
856
857 Ok(())
858 }
859}
860
861#[cfg(test)]
862mod tests {
863 use super::*;
864 use std::io::Cursor;
865
866 fn make_minimal_tiff() -> Vec<u8> {
868 let mut data = Vec::new();
869 data.extend_from_slice(b"II");
871 data.extend_from_slice(&42u16.to_le_bytes());
873 data.extend_from_slice(&8u32.to_le_bytes());
875 data.extend_from_slice(&0u16.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); data
879 }
880
881 #[test]
882 fn test_parse_header_le() {
883 let data = make_minimal_tiff();
884 let mut cursor = Cursor::new(data);
885 let header = TiffHeader::parse(&mut cursor).unwrap();
886
887 assert_eq!(header.byte_order, ByteOrder::LittleEndian);
888 assert_eq!(header.magic, 42);
889 assert_eq!(header.ifd0_offset, 8);
890 assert!(!header.is_bigtiff);
891 }
892
893 #[test]
894 fn test_parse_header_be() {
895 let mut data = Vec::new();
896 data.extend_from_slice(b"MM"); data.extend_from_slice(&42u16.to_be_bytes());
898 data.extend_from_slice(&8u32.to_be_bytes());
899 data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&0u32.to_be_bytes()); let mut cursor = Cursor::new(data);
903 let header = TiffHeader::parse(&mut cursor).unwrap();
904
905 assert_eq!(header.byte_order, ByteOrder::BigEndian);
906 assert_eq!(header.magic, 42);
907 }
908
909 #[test]
910 fn test_invalid_byte_order() {
911 let mut data = Vec::new();
912 data.extend_from_slice(b"XX"); data.extend_from_slice(&42u16.to_le_bytes());
914 data.extend_from_slice(&8u32.to_le_bytes());
915
916 let mut cursor = Cursor::new(data);
917 let result = TiffHeader::parse(&mut cursor);
918
919 assert!(matches!(
920 result,
921 Err(RawError::Parse(ParseError::InvalidByteOrder(_)))
922 ));
923 }
924
925 #[test]
926 fn test_invalid_magic() {
927 let mut data = Vec::new();
928 data.extend_from_slice(b"II");
929 data.extend_from_slice(&99u16.to_le_bytes()); data.extend_from_slice(&8u32.to_le_bytes());
931
932 let mut cursor = Cursor::new(data);
933 let result = TiffHeader::parse(&mut cursor);
934
935 assert!(matches!(
936 result,
937 Err(RawError::Parse(ParseError::InvalidMagic { .. }))
938 ));
939 }
940
941 #[test]
942 fn test_parse_empty_ifd() {
943 let data = make_minimal_tiff();
944 let cursor = Cursor::new(data);
945 let mut parser = TiffParser::new(cursor).unwrap();
946
947 let ifd = parser.parse_ifd0().unwrap();
948 assert_eq!(ifd.offset, 8);
949 assert!(ifd.entries.is_empty());
950 assert_eq!(ifd.next_ifd_offset, 0);
951 }
952
953 #[test]
954 fn test_walk_ifd_chain() {
955 let data = make_minimal_tiff();
956 let cursor = Cursor::new(data);
957 let mut parser = TiffParser::new(cursor).unwrap();
958
959 let ifds = parser.walk_ifd_chain().unwrap();
960 assert_eq!(ifds.len(), 1);
961 }
962
963 #[test]
968 fn test_truncated_header() {
969 let data = vec![b'I', b'I', 42, 0];
971 let cursor = Cursor::new(data);
972 let result = TiffParser::new(cursor);
973
974 assert!(result.is_err(), "Should fail on truncated header");
975 }
976
977 #[test]
978 fn test_truncated_header_no_offset() {
979 let mut data = Vec::new();
981 data.extend_from_slice(b"II");
982 data.extend_from_slice(&42u16.to_le_bytes());
983 let cursor = Cursor::new(data);
986 let result = TiffParser::new(cursor);
987
988 assert!(result.is_err(), "Should fail when IFD offset is missing");
989 }
990
991 #[test]
992 fn test_ifd_offset_past_eof() {
993 let mut data = Vec::new();
995 data.extend_from_slice(b"II");
996 data.extend_from_slice(&42u16.to_le_bytes());
997 data.extend_from_slice(&1000u32.to_le_bytes()); let cursor = Cursor::new(data);
1000 let mut parser = TiffParser::new(cursor).unwrap();
1001
1002 let result = parser.parse_ifd0();
1004 assert!(result.is_err(), "Should fail when IFD offset is past EOF");
1005 }
1006
1007 #[test]
1008 fn test_truncated_ifd_entry_count() {
1009 let mut data = Vec::new();
1011 data.extend_from_slice(b"II");
1012 data.extend_from_slice(&42u16.to_le_bytes());
1013 data.extend_from_slice(&8u32.to_le_bytes()); let cursor = Cursor::new(data);
1017 let mut parser = TiffParser::new(cursor).unwrap();
1018
1019 let result = parser.parse_ifd0();
1020 assert!(
1021 result.is_err(),
1022 "Should fail when IFD entry count is missing"
1023 );
1024 }
1025
1026 #[test]
1027 fn test_truncated_ifd_entries() {
1028 let mut data = Vec::new();
1030 data.extend_from_slice(b"II");
1031 data.extend_from_slice(&42u16.to_le_bytes());
1032 data.extend_from_slice(&8u32.to_le_bytes()); data.extend_from_slice(&2u16.to_le_bytes()); data.extend_from_slice(&[0u8; 6]); let cursor = Cursor::new(data);
1038 let mut parser = TiffParser::new(cursor).unwrap();
1039
1040 let result = parser.parse_ifd0();
1041 assert!(
1042 result.is_err(),
1043 "Should fail when IFD entries are truncated"
1044 );
1045 }
1046
1047 #[test]
1048 fn test_validate_complete_valid_file() {
1049 let data = make_minimal_tiff();
1051 let cursor = Cursor::new(data);
1052 let mut parser = TiffParser::new(cursor).unwrap();
1053
1054 let result = parser.validate_complete();
1055 assert!(
1056 result.is_ok(),
1057 "Valid TIFF should pass validation: {:?}",
1058 result
1059 );
1060 }
1061
1062 #[test]
1063 fn test_validate_complete_value_past_eof() {
1064 let mut data = Vec::new();
1066 data.extend_from_slice(b"II");
1067 data.extend_from_slice(&42u16.to_le_bytes());
1068 data.extend_from_slice(&8u32.to_le_bytes()); data.extend_from_slice(&1u16.to_le_bytes()); data.extend_from_slice(&0x0100u16.to_le_bytes()); data.extend_from_slice(&3u16.to_le_bytes()); data.extend_from_slice(&10u32.to_le_bytes()); data.extend_from_slice(&1000u32.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes());
1081
1082 let cursor = Cursor::new(data);
1083 let mut parser = TiffParser::new(cursor).unwrap();
1084
1085 let result = parser.validate_complete();
1086 assert!(
1087 matches!(
1088 result,
1089 Err(RawError::Parse(ParseError::OffsetOutOfBounds { .. }))
1090 ),
1091 "Should detect value data past EOF: {:?}",
1092 result
1093 );
1094 }
1095
1096 #[test]
1097 fn test_unknown_tag_preserved() {
1098 let mut data = Vec::new();
1100 data.extend_from_slice(b"II");
1101 data.extend_from_slice(&42u16.to_le_bytes());
1102 data.extend_from_slice(&8u32.to_le_bytes()); data.extend_from_slice(&1u16.to_le_bytes()); data.extend_from_slice(&0xFFFFu16.to_le_bytes()); data.extend_from_slice(&3u16.to_le_bytes()); data.extend_from_slice(&1u32.to_le_bytes()); data.extend_from_slice(&42u32.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes());
1115
1116 let cursor = Cursor::new(data);
1117 let mut parser = TiffParser::new(cursor).unwrap();
1118
1119 let ifd = parser.parse_ifd0().unwrap();
1120
1121 assert!(
1122 ifd.other_tags.contains_key(&0xFFFF),
1123 "Unknown tag should be preserved"
1124 );
1125 assert!(ifd.entries.is_empty(), "No known tags should be parsed");
1126 }
1127
1128 #[test]
1129 fn test_unknown_data_type_preserved() {
1130 let mut data = Vec::new();
1132 data.extend_from_slice(b"II");
1133 data.extend_from_slice(&42u16.to_le_bytes());
1134 data.extend_from_slice(&8u32.to_le_bytes()); data.extend_from_slice(&1u16.to_le_bytes()); data.extend_from_slice(&0x0100u16.to_le_bytes()); data.extend_from_slice(&99u16.to_le_bytes()); data.extend_from_slice(&1u32.to_le_bytes()); data.extend_from_slice(&42u32.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes());
1147
1148 let cursor = Cursor::new(data);
1149 let mut parser = TiffParser::new(cursor).unwrap();
1150
1151 let ifd = parser.parse_ifd0().unwrap();
1152
1153 let entry = ifd.entries.get(&TiffTag::ImageWidth);
1155 assert!(entry.is_some(), "Entry should exist");
1156 assert!(
1157 entry.unwrap().tiff_type.is_none(),
1158 "Unknown type should be None"
1159 );
1160 }
1161}