1use std::collections::HashMap;
25
26use crate::error::TiffError;
27use crate::io::{read_u16_be, read_u16_le, read_u32_be, read_u32_le, read_u64_be, read_u64_le};
28
29use super::tags::{FieldType, TiffTag};
30
31const BYTE_ORDER_LITTLE_ENDIAN: u16 = 0x4949;
37
38const BYTE_ORDER_BIG_ENDIAN: u16 = 0x4D4D;
40
41const VERSION_TIFF: u16 = 42;
43
44const VERSION_BIGTIFF: u16 = 43;
46
47pub const TIFF_HEADER_SIZE: usize = 8;
49
50pub const BIGTIFF_HEADER_SIZE: usize = 16;
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum ByteOrder {
63 LittleEndian,
65 BigEndian,
67}
68
69impl ByteOrder {
70 #[inline]
72 pub fn read_u16(self, bytes: &[u8]) -> u16 {
73 match self {
74 ByteOrder::LittleEndian => read_u16_le(bytes),
75 ByteOrder::BigEndian => read_u16_be(bytes),
76 }
77 }
78
79 #[inline]
81 pub fn read_u32(self, bytes: &[u8]) -> u32 {
82 match self {
83 ByteOrder::LittleEndian => read_u32_le(bytes),
84 ByteOrder::BigEndian => read_u32_be(bytes),
85 }
86 }
87
88 #[inline]
90 pub fn read_u64(self, bytes: &[u8]) -> u64 {
91 match self {
92 ByteOrder::LittleEndian => read_u64_le(bytes),
93 ByteOrder::BigEndian => read_u64_be(bytes),
94 }
95 }
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub struct TiffHeader {
110 pub byte_order: ByteOrder,
112
113 pub is_bigtiff: bool,
115
116 pub first_ifd_offset: u64,
118}
119
120impl TiffHeader {
121 pub fn parse(bytes: &[u8], file_size: u64) -> Result<Self, TiffError> {
137 if bytes.len() < TIFF_HEADER_SIZE {
139 return Err(TiffError::FileTooSmall {
140 required: TIFF_HEADER_SIZE as u64,
141 actual: bytes.len() as u64,
142 });
143 }
144
145 let magic = u16::from_le_bytes([bytes[0], bytes[1]]);
148 let byte_order = match magic {
149 BYTE_ORDER_LITTLE_ENDIAN => ByteOrder::LittleEndian,
150 BYTE_ORDER_BIG_ENDIAN => ByteOrder::BigEndian,
151 _ => return Err(TiffError::InvalidMagic(magic)),
152 };
153
154 let version = byte_order.read_u16(&bytes[2..4]);
156
157 match version {
158 VERSION_TIFF => {
159 let first_ifd_offset = byte_order.read_u32(&bytes[4..8]) as u64;
161
162 if first_ifd_offset >= file_size {
164 return Err(TiffError::InvalidIfdOffset(first_ifd_offset));
165 }
166
167 Ok(TiffHeader {
168 byte_order,
169 is_bigtiff: false,
170 first_ifd_offset,
171 })
172 }
173 VERSION_BIGTIFF => {
174 if bytes.len() < BIGTIFF_HEADER_SIZE {
176 return Err(TiffError::FileTooSmall {
177 required: BIGTIFF_HEADER_SIZE as u64,
178 actual: bytes.len() as u64,
179 });
180 }
181
182 let offset_size = byte_order.read_u16(&bytes[4..6]);
184 if offset_size != 8 {
185 return Err(TiffError::InvalidBigTiffOffsetSize(offset_size));
186 }
187
188 let first_ifd_offset = byte_order.read_u64(&bytes[8..16]);
192
193 if first_ifd_offset >= file_size {
195 return Err(TiffError::InvalidIfdOffset(first_ifd_offset));
196 }
197
198 Ok(TiffHeader {
199 byte_order,
200 is_bigtiff: true,
201 first_ifd_offset,
202 })
203 }
204 _ => Err(TiffError::InvalidVersion(version)),
205 }
206 }
207
208 #[inline]
213 pub const fn ifd_entry_size(&self) -> usize {
214 if self.is_bigtiff {
215 20
216 } else {
217 12
218 }
219 }
220
221 #[inline]
226 pub const fn ifd_count_size(&self) -> usize {
227 if self.is_bigtiff {
228 8
229 } else {
230 2
231 }
232 }
233
234 #[inline]
239 pub const fn ifd_next_offset_size(&self) -> usize {
240 if self.is_bigtiff {
241 8
242 } else {
243 4
244 }
245 }
246
247 #[inline]
253 pub const fn value_offset_size(&self) -> usize {
254 if self.is_bigtiff {
255 8
256 } else {
257 4
258 }
259 }
260}
261
262#[derive(Debug, Clone)]
287pub struct IfdEntry {
288 pub tag_id: u16,
290
291 pub field_type: Option<FieldType>,
293
294 pub field_type_raw: u16,
296
297 pub count: u64,
299
300 pub value_offset_bytes: Vec<u8>,
304
305 pub is_inline: bool,
307}
308
309impl IfdEntry {
310 fn parse(bytes: &[u8], header: &TiffHeader) -> Self {
316 let byte_order = header.byte_order;
317
318 let tag_id = byte_order.read_u16(&bytes[0..2]);
320
321 let field_type_raw = byte_order.read_u16(&bytes[2..4]);
323 let field_type = FieldType::from_u16(field_type_raw);
324
325 let (count, value_offset_bytes) = if header.is_bigtiff {
327 let count = byte_order.read_u64(&bytes[4..12]);
329 let value_offset_bytes = bytes[12..20].to_vec();
330 (count, value_offset_bytes)
331 } else {
332 let count = byte_order.read_u32(&bytes[4..8]) as u64;
334 let value_offset_bytes = bytes[8..12].to_vec();
335 (count, value_offset_bytes)
336 };
337
338 let is_inline = field_type
340 .map(|ft| ft.fits_inline(count, header.is_bigtiff))
341 .unwrap_or(false);
342
343 IfdEntry {
344 tag_id,
345 field_type,
346 field_type_raw,
347 count,
348 value_offset_bytes,
349 is_inline,
350 }
351 }
352
353 pub fn tag(&self) -> Option<TiffTag> {
355 TiffTag::from_u16(self.tag_id)
356 }
357
358 pub fn value_offset(&self, byte_order: ByteOrder) -> u64 {
366 if self.value_offset_bytes.len() == 8 {
367 byte_order.read_u64(&self.value_offset_bytes)
368 } else {
369 byte_order.read_u32(&self.value_offset_bytes) as u64
370 }
371 }
372
373 pub fn inline_u16(&self, byte_order: ByteOrder) -> Option<u16> {
381 if !self.is_inline || self.count != 1 {
382 return None;
383 }
384 match self.field_type? {
385 FieldType::Short => Some(byte_order.read_u16(&self.value_offset_bytes)),
386 _ => None,
387 }
388 }
389
390 pub fn inline_u32(&self, byte_order: ByteOrder) -> Option<u32> {
398 if !self.is_inline || self.count != 1 {
399 return None;
400 }
401 match self.field_type? {
402 FieldType::Short => Some(byte_order.read_u16(&self.value_offset_bytes) as u32),
403 FieldType::Long => Some(byte_order.read_u32(&self.value_offset_bytes)),
404 _ => None,
405 }
406 }
407
408 pub fn inline_u64(&self, byte_order: ByteOrder) -> Option<u64> {
416 if !self.is_inline || self.count != 1 {
417 return None;
418 }
419 match self.field_type? {
420 FieldType::Short => Some(byte_order.read_u16(&self.value_offset_bytes) as u64),
421 FieldType::Long => Some(byte_order.read_u32(&self.value_offset_bytes) as u64),
422 FieldType::Long8 => {
423 if self.value_offset_bytes.len() >= 8 {
424 Some(byte_order.read_u64(&self.value_offset_bytes))
425 } else {
426 None
427 }
428 }
429 _ => None,
430 }
431 }
432
433 pub fn value_byte_size(&self) -> Option<u64> {
435 self.field_type
436 .map(|ft| ft.size_in_bytes() as u64 * self.count)
437 }
438}
439
440#[derive(Debug, Clone)]
452pub struct Ifd {
453 pub entries: Vec<IfdEntry>,
455
456 pub(crate) entries_by_tag: HashMap<u16, usize>,
458
459 pub next_ifd_offset: u64,
461}
462
463impl Ifd {
464 pub fn parse(bytes: &[u8], header: &TiffHeader) -> Result<Self, TiffError> {
478 let byte_order = header.byte_order;
479 let count_size = header.ifd_count_size();
480 let entry_size = header.ifd_entry_size();
481 let next_offset_size = header.ifd_next_offset_size();
482
483 if bytes.len() < count_size {
485 return Err(TiffError::FileTooSmall {
486 required: count_size as u64,
487 actual: bytes.len() as u64,
488 });
489 }
490
491 let entry_count = if header.is_bigtiff {
492 byte_order.read_u64(&bytes[0..8])
493 } else {
494 byte_order.read_u16(&bytes[0..2]) as u64
495 };
496
497 let entries_start = count_size;
499 let entries_size = entry_count as usize * entry_size;
500 let next_offset_start = entries_start + entries_size;
501 let total_required = next_offset_start + next_offset_size;
502
503 if bytes.len() < total_required {
504 return Err(TiffError::FileTooSmall {
505 required: total_required as u64,
506 actual: bytes.len() as u64,
507 });
508 }
509
510 let mut entries = Vec::with_capacity(entry_count as usize);
512 let mut entries_by_tag = HashMap::with_capacity(entry_count as usize);
513
514 for i in 0..entry_count as usize {
515 let entry_start = entries_start + i * entry_size;
516 let entry_bytes = &bytes[entry_start..entry_start + entry_size];
517 let entry = IfdEntry::parse(entry_bytes, header);
518
519 entries_by_tag.insert(entry.tag_id, entries.len());
520 entries.push(entry);
521 }
522
523 let next_ifd_offset = if header.is_bigtiff {
525 byte_order.read_u64(&bytes[next_offset_start..next_offset_start + 8])
526 } else {
527 byte_order.read_u32(&bytes[next_offset_start..next_offset_start + 4]) as u64
528 };
529
530 Ok(Ifd {
531 entries,
532 entries_by_tag,
533 next_ifd_offset,
534 })
535 }
536
537 pub fn calculate_size(entry_count: u64, header: &TiffHeader) -> usize {
547 header.ifd_count_size()
548 + (entry_count as usize * header.ifd_entry_size())
549 + header.ifd_next_offset_size()
550 }
551
552 pub fn get_entry(&self, tag_id: u16) -> Option<&IfdEntry> {
554 self.entries_by_tag
555 .get(&tag_id)
556 .map(|&idx| &self.entries[idx])
557 }
558
559 pub fn get_entry_by_tag(&self, tag: TiffTag) -> Option<&IfdEntry> {
561 self.get_entry(tag.as_u16())
562 }
563
564 pub fn get_u32(&self, tag: TiffTag, byte_order: ByteOrder) -> Option<u32> {
569 self.get_entry_by_tag(tag)?.inline_u32(byte_order)
570 }
571
572 pub fn get_u64(&self, tag: TiffTag, byte_order: ByteOrder) -> Option<u64> {
574 self.get_entry_by_tag(tag)?.inline_u64(byte_order)
575 }
576
577 pub fn get_u16(&self, tag: TiffTag, byte_order: ByteOrder) -> Option<u16> {
579 self.get_entry_by_tag(tag)?.inline_u16(byte_order)
580 }
581
582 pub fn is_tiled(&self) -> bool {
586 self.get_entry_by_tag(TiffTag::TileWidth).is_some()
587 && self.get_entry_by_tag(TiffTag::TileLength).is_some()
588 }
589
590 pub fn is_stripped(&self) -> bool {
594 self.get_entry_by_tag(TiffTag::StripOffsets).is_some()
595 }
596
597 pub fn image_width(&self, byte_order: ByteOrder) -> Option<u32> {
599 self.get_u32(TiffTag::ImageWidth, byte_order)
600 }
601
602 pub fn image_height(&self, byte_order: ByteOrder) -> Option<u32> {
604 self.get_u32(TiffTag::ImageLength, byte_order)
605 }
606
607 pub fn tile_width(&self, byte_order: ByteOrder) -> Option<u32> {
609 self.get_u32(TiffTag::TileWidth, byte_order)
610 }
611
612 pub fn tile_height(&self, byte_order: ByteOrder) -> Option<u32> {
614 self.get_u32(TiffTag::TileLength, byte_order)
615 }
616
617 pub fn compression(&self, byte_order: ByteOrder) -> Option<u16> {
619 self.get_u16(TiffTag::Compression, byte_order)
620 }
621
622 pub fn entry_count(&self) -> usize {
624 self.entries.len()
625 }
626
627 #[cfg(test)]
629 pub fn empty() -> Self {
630 Ifd {
631 entries: Vec::new(),
632 entries_by_tag: HashMap::new(),
633 next_ifd_offset: 0,
634 }
635 }
636}
637
638#[cfg(test)]
643mod tests {
644 use super::*;
645
646 #[test]
651 fn test_byte_order_read_u16() {
652 let bytes = [0x01, 0x02];
653 assert_eq!(ByteOrder::LittleEndian.read_u16(&bytes), 0x0201);
654 assert_eq!(ByteOrder::BigEndian.read_u16(&bytes), 0x0102);
655 }
656
657 #[test]
658 fn test_byte_order_read_u32() {
659 let bytes = [0x01, 0x02, 0x03, 0x04];
660 assert_eq!(ByteOrder::LittleEndian.read_u32(&bytes), 0x04030201);
661 assert_eq!(ByteOrder::BigEndian.read_u32(&bytes), 0x01020304);
662 }
663
664 #[test]
665 fn test_byte_order_read_u64() {
666 let bytes = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
667 assert_eq!(ByteOrder::LittleEndian.read_u64(&bytes), 0x0807060504030201);
668 assert_eq!(ByteOrder::BigEndian.read_u64(&bytes), 0x0102030405060708);
669 }
670
671 #[test]
676 fn test_parse_tiff_little_endian() {
677 let header = [
679 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, ];
683
684 let result = TiffHeader::parse(&header, 1000).unwrap();
685 assert_eq!(result.byte_order, ByteOrder::LittleEndian);
686 assert!(!result.is_bigtiff);
687 assert_eq!(result.first_ifd_offset, 8);
688 }
689
690 #[test]
691 fn test_parse_tiff_big_endian() {
692 let header = [
694 0x4D, 0x4D, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x08, ];
698
699 let result = TiffHeader::parse(&header, 1000).unwrap();
700 assert_eq!(result.byte_order, ByteOrder::BigEndian);
701 assert!(!result.is_bigtiff);
702 assert_eq!(result.first_ifd_offset, 8);
703 }
704
705 #[test]
706 fn test_parse_tiff_larger_offset() {
707 let header = [
709 0x49, 0x49, 0x2A, 0x00, 0xE8, 0x03, 0x00, 0x00, ];
713
714 let result = TiffHeader::parse(&header, 2000).unwrap();
715 assert_eq!(result.first_ifd_offset, 1000);
716 }
717
718 #[test]
723 fn test_parse_bigtiff_little_endian() {
724 let header = [
726 0x49, 0x49, 0x2B, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
732
733 let result = TiffHeader::parse(&header, 1000).unwrap();
734 assert_eq!(result.byte_order, ByteOrder::LittleEndian);
735 assert!(result.is_bigtiff);
736 assert_eq!(result.first_ifd_offset, 16);
737 }
738
739 #[test]
740 fn test_parse_bigtiff_big_endian() {
741 let header = [
743 0x4D, 0x4D, 0x00, 0x2B, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, ];
749
750 let result = TiffHeader::parse(&header, 1000).unwrap();
751 assert_eq!(result.byte_order, ByteOrder::BigEndian);
752 assert!(result.is_bigtiff);
753 assert_eq!(result.first_ifd_offset, 16);
754 }
755
756 #[test]
757 fn test_parse_bigtiff_large_offset() {
758 let header = [
760 0x49, 0x49, 0x2B, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, ];
766
767 let result = TiffHeader::parse(&header, 10_000_000_000).unwrap();
768 assert!(result.is_bigtiff);
769 assert_eq!(result.first_ifd_offset, 0x0000_0001_0000_0000); }
771
772 #[test]
777 fn test_parse_invalid_magic() {
778 let header = [
779 0x00, 0x00, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00,
781 ];
782
783 let result = TiffHeader::parse(&header, 1000);
784 assert!(matches!(result, Err(TiffError::InvalidMagic(0x0000))));
785 }
786
787 #[test]
788 fn test_parse_invalid_version() {
789 let header = [
790 0x49, 0x49, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
793 ];
794
795 let result = TiffHeader::parse(&header, 1000);
796 assert!(matches!(result, Err(TiffError::InvalidVersion(0))));
797 }
798
799 #[test]
800 fn test_parse_bigtiff_invalid_offset_size() {
801 let header = [
802 0x49, 0x49, 0x2B, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
806 ];
807
808 let result = TiffHeader::parse(&header, 1000);
809 assert!(matches!(
810 result,
811 Err(TiffError::InvalidBigTiffOffsetSize(4))
812 ));
813 }
814
815 #[test]
816 fn test_parse_file_too_small_tiff() {
817 let header = [0x49, 0x49, 0x2A, 0x00]; let result = TiffHeader::parse(&header, 1000);
820 assert!(matches!(
821 result,
822 Err(TiffError::FileTooSmall {
823 required: 8,
824 actual: 4
825 })
826 ));
827 }
828
829 #[test]
830 fn test_parse_file_too_small_bigtiff() {
831 let header = [
833 0x49, 0x49, 0x2B, 0x00, 0x08, 0x00, 0x00, 0x00, ];
838
839 let result = TiffHeader::parse(&header, 1000);
840 assert!(matches!(
841 result,
842 Err(TiffError::FileTooSmall {
843 required: 16,
844 actual: 8
845 })
846 ));
847 }
848
849 #[test]
850 fn test_parse_invalid_ifd_offset() {
851 let header = [
853 0x49, 0x49, 0x2A, 0x00, 0xE8, 0x03, 0x00, 0x00, ];
857
858 let result = TiffHeader::parse(&header, 500); assert!(matches!(result, Err(TiffError::InvalidIfdOffset(1000))));
860 }
861
862 #[test]
867 fn test_ifd_entry_size() {
868 let tiff = TiffHeader {
869 byte_order: ByteOrder::LittleEndian,
870 is_bigtiff: false,
871 first_ifd_offset: 8,
872 };
873 assert_eq!(tiff.ifd_entry_size(), 12);
874
875 let bigtiff = TiffHeader {
876 byte_order: ByteOrder::LittleEndian,
877 is_bigtiff: true,
878 first_ifd_offset: 16,
879 };
880 assert_eq!(bigtiff.ifd_entry_size(), 20);
881 }
882
883 #[test]
884 fn test_ifd_count_size() {
885 let tiff = TiffHeader {
886 byte_order: ByteOrder::LittleEndian,
887 is_bigtiff: false,
888 first_ifd_offset: 8,
889 };
890 assert_eq!(tiff.ifd_count_size(), 2);
891
892 let bigtiff = TiffHeader {
893 byte_order: ByteOrder::LittleEndian,
894 is_bigtiff: true,
895 first_ifd_offset: 16,
896 };
897 assert_eq!(bigtiff.ifd_count_size(), 8);
898 }
899
900 #[test]
901 fn test_value_offset_size() {
902 let tiff = TiffHeader {
903 byte_order: ByteOrder::LittleEndian,
904 is_bigtiff: false,
905 first_ifd_offset: 8,
906 };
907 assert_eq!(tiff.value_offset_size(), 4);
908
909 let bigtiff = TiffHeader {
910 byte_order: ByteOrder::LittleEndian,
911 is_bigtiff: true,
912 first_ifd_offset: 16,
913 };
914 assert_eq!(bigtiff.value_offset_size(), 8);
915 }
916
917 fn make_tiff_header() -> TiffHeader {
922 TiffHeader {
923 byte_order: ByteOrder::LittleEndian,
924 is_bigtiff: false,
925 first_ifd_offset: 8,
926 }
927 }
928
929 fn make_bigtiff_header() -> TiffHeader {
930 TiffHeader {
931 byte_order: ByteOrder::LittleEndian,
932 is_bigtiff: true,
933 first_ifd_offset: 16,
934 }
935 }
936
937 #[test]
938 fn test_ifd_entry_parse_tiff_inline_short() {
939 let entry_bytes = [
942 0x00, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, ];
947
948 let header = make_tiff_header();
949 let entry = IfdEntry::parse(&entry_bytes, &header);
950
951 assert_eq!(entry.tag_id, 256);
952 assert_eq!(entry.tag(), Some(TiffTag::ImageWidth));
953 assert_eq!(entry.field_type, Some(FieldType::Short));
954 assert_eq!(entry.count, 1);
955 assert!(entry.is_inline);
956 assert_eq!(entry.inline_u16(header.byte_order), Some(1024));
957 assert_eq!(entry.inline_u32(header.byte_order), Some(1024));
958 }
959
960 #[test]
961 fn test_ifd_entry_parse_tiff_inline_long() {
962 let entry_bytes = [
965 0x00, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0xC3, 0x00, 0x00, ];
970
971 let header = make_tiff_header();
972 let entry = IfdEntry::parse(&entry_bytes, &header);
973
974 assert_eq!(entry.tag_id, 256);
975 assert_eq!(entry.field_type, Some(FieldType::Long));
976 assert!(entry.is_inline);
977 assert_eq!(entry.inline_u32(header.byte_order), Some(50000));
978 }
979
980 #[test]
981 fn test_ifd_entry_parse_tiff_offset() {
982 let entry_bytes = [
985 0x44, 0x01, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0xE8, 0x03, 0x00, 0x00, ];
990
991 let header = make_tiff_header();
992 let entry = IfdEntry::parse(&entry_bytes, &header);
993
994 assert_eq!(entry.tag_id, 324);
995 assert_eq!(entry.tag(), Some(TiffTag::TileOffsets));
996 assert_eq!(entry.field_type, Some(FieldType::Long));
997 assert_eq!(entry.count, 100);
998 assert!(!entry.is_inline); assert_eq!(entry.value_offset(header.byte_order), 1000);
1000 assert_eq!(entry.value_byte_size(), Some(400)); }
1002
1003 #[test]
1004 fn test_ifd_entry_parse_bigtiff_inline_long8() {
1005 let entry_bytes = [
1007 0x00, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x86, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, ];
1012
1013 let header = make_bigtiff_header();
1014 let entry = IfdEntry::parse(&entry_bytes, &header);
1015
1016 assert_eq!(entry.tag_id, 256);
1017 assert_eq!(entry.field_type, Some(FieldType::Long8));
1018 assert!(entry.is_inline);
1019 assert_eq!(entry.inline_u64(header.byte_order), Some(100000));
1020 }
1021
1022 #[test]
1023 fn test_ifd_entry_unknown_field_type() {
1024 let entry_bytes = [
1026 0x00, 0x01, 0x63, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
1031
1032 let header = make_tiff_header();
1033 let entry = IfdEntry::parse(&entry_bytes, &header);
1034
1035 assert_eq!(entry.field_type, None);
1036 assert_eq!(entry.field_type_raw, 99);
1037 assert!(!entry.is_inline); }
1039
1040 #[test]
1045 fn test_ifd_parse_tiff_simple() {
1046 let ifd_bytes = [
1052 0x03, 0x00, 0x00, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
1055 0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
1057 0x03, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
1059 0xF4, 0x01, 0x00, 0x00,
1061 ];
1062
1063 let header = make_tiff_header();
1064 let ifd = Ifd::parse(&ifd_bytes, &header).unwrap();
1065
1066 assert_eq!(ifd.entry_count(), 3);
1067 assert_eq!(ifd.next_ifd_offset, 500);
1068
1069 assert_eq!(ifd.image_width(header.byte_order), Some(1024));
1071 assert_eq!(ifd.image_height(header.byte_order), Some(768));
1072 assert_eq!(ifd.compression(header.byte_order), Some(7));
1073
1074 let width_entry = ifd.get_entry_by_tag(TiffTag::ImageWidth).unwrap();
1076 assert_eq!(width_entry.count, 1);
1077 }
1078
1079 #[test]
1080 fn test_ifd_parse_bigtiff() {
1081 let ifd_bytes = [
1083 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1085 0x00, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1097 ];
1098
1099 let header = make_bigtiff_header();
1100 let ifd = Ifd::parse(&ifd_bytes, &header).unwrap();
1101
1102 assert_eq!(ifd.entry_count(), 2);
1103 assert_eq!(ifd.next_ifd_offset, 1000);
1104 assert_eq!(ifd.image_width(header.byte_order), Some(50000));
1105 assert_eq!(ifd.image_height(header.byte_order), Some(40000));
1106 }
1107
1108 #[test]
1109 fn test_ifd_parse_with_tiles() {
1110 let ifd_bytes = [
1112 0x04, 0x00, 0x00, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00,
1115 0x01, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x00, 0x00,
1117 0x42, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
1119 0x43, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
1121 0x00, 0x00, 0x00, 0x00,
1123 ];
1124
1125 let header = make_tiff_header();
1126 let ifd = Ifd::parse(&ifd_bytes, &header).unwrap();
1127
1128 assert!(ifd.is_tiled());
1129 assert!(!ifd.is_stripped());
1130 assert_eq!(ifd.tile_width(header.byte_order), Some(256));
1131 assert_eq!(ifd.tile_height(header.byte_order), Some(256));
1132 assert_eq!(ifd.next_ifd_offset, 0);
1133 }
1134
1135 #[test]
1136 fn test_ifd_parse_big_endian() {
1137 let ifd_bytes = [
1139 0x00, 0x01, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1147 ];
1148
1149 let header = TiffHeader {
1150 byte_order: ByteOrder::BigEndian,
1151 is_bigtiff: false,
1152 first_ifd_offset: 8,
1153 };
1154
1155 let ifd = Ifd::parse(&ifd_bytes, &header).unwrap();
1156
1157 assert_eq!(ifd.entry_count(), 1);
1158 assert_eq!(ifd.image_width(header.byte_order), Some(2048));
1159 }
1160
1161 #[test]
1162 fn test_ifd_calculate_size() {
1163 let tiff_header = make_tiff_header();
1164 let bigtiff_header = make_bigtiff_header();
1165
1166 assert_eq!(Ifd::calculate_size(10, &tiff_header), 126);
1168
1169 assert_eq!(Ifd::calculate_size(10, &bigtiff_header), 216);
1171 }
1172
1173 #[test]
1174 fn test_ifd_parse_error_too_small() {
1175 let ifd_bytes = [
1177 0x05, 0x00, 0x00, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x01,
1179 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
1180 ];
1181
1182 let header = make_tiff_header();
1183 let result = Ifd::parse(&ifd_bytes, &header);
1184
1185 assert!(matches!(result, Err(TiffError::FileTooSmall { .. })));
1186 }
1187
1188 #[test]
1189 fn test_ifd_get_entry_not_found() {
1190 let ifd_bytes = [
1192 0x01, 0x00, 0x00, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
1193 0x00, 0x00, 0x00, 0x00,
1194 ];
1195
1196 let header = make_tiff_header();
1197 let ifd = Ifd::parse(&ifd_bytes, &header).unwrap();
1198
1199 assert!(ifd.get_entry_by_tag(TiffTag::ImageWidth).is_some());
1201
1202 assert!(ifd.get_entry_by_tag(TiffTag::ImageLength).is_none());
1204 assert_eq!(ifd.image_height(header.byte_order), None);
1205 }
1206}