1use crate::util::*;
2use crate::{ImageResult, ImageSize};
3
4use std::io::{BufRead, Cursor, Seek, SeekFrom};
5
6pub fn size<R: BufRead + Seek>(reader: &mut R) -> ImageResult<ImageSize> {
7 reader.seek(SeekFrom::Start(0))?;
8
9 let mut endian_marker = [0; 2];
10 reader.read_exact(&mut endian_marker)?;
11
12 // Get the endianness which determines how we read the input
13 let endianness = if &endian_marker[0..2] == b"II" {
14 Endian::Little
15 } else if &endian_marker[0..2] == b"MM" {
16 Endian::Big
17 } else {
18 // Shouldn't get here by normal means, but handle invalid header anyway
19 return Err(
20 std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid TIFF header").into(),
21 );
22 };
23
24 // Read the IFD offset from the header
25 reader.seek(SeekFrom::Start(4))?;
26 let ifd_offset = read_u32(reader, &endianness)?;
27
28 // IFD offset cannot be 0
29 if ifd_offset == 0 {
30 return Err(
31 std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid IFD offset").into(),
32 );
33 }
34
35 // Jump to the IFD offset
36 reader.seek(SeekFrom::Start(ifd_offset.into()))?;
37
38 // Read how many IFD records there are
39 let ifd_count = read_u16(reader, &endianness)?;
40 let mut width = None;
41 let mut height = None;
42
43 for _ifd in 0..ifd_count {
44 let tag = read_u16(reader, &endianness)?;
45 let kind = read_u16(reader, &endianness)?;
46 let _count = read_u32(reader, &endianness)?;
47
48 let value_bytes = match kind {
49 // BYTE | ASCII | SBYTE | UNDEFINED
50 1 | 2 | 6 | 7 => 1,
51 // SHORT | SSHORT
52 3 | 8 => 2,
53 // LONG | SLONG | FLOAT | IFD
54 4 | 9 | 11 | 13 => 4,
55 // RATIONAL | SRATIONAL
56 5 | 10 => 4 * 2,
57 // DOUBLE | LONG8 | SLONG8 | IFD8
58 12 | 16 | 17 | 18 => 8,
59 // Anything else is invalid
60 _ => {
61 return Err(std::io::Error::new(
62 std::io::ErrorKind::InvalidData,
63 "Invalid IFD type",
64 )
65 .into())
66 }
67 };
68
69 let mut value_buffer = [0; 4];
70 reader.read_exact(&mut value_buffer)?;
71
72 let mut r = Cursor::new(&value_buffer[..]);
73 let value = match value_bytes {
74 2 => Some(read_u16(&mut r, &endianness)? as u32),
75 4 => Some(read_u32(&mut r, &endianness)?),
76 _ => None,
77 };
78
79 // Tag 0x100 is the image width, 0x101 is image height
80 if tag == 0x100 {
81 width = value;
82 } else if tag == 0x101 {
83 height = value;
84 }
85
86 // If we've read both values we need, return the data
87 if let (Some(width), Some(height)) = (width, height) {
88 return Ok(ImageSize {
89 width: width as usize,
90 height: height as usize,
91 });
92 }
93 }
94
95 // If no width/height pair was found return invalid data
96 Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "No dimensions in IFD tags").into())
97}
98
99pub fn matches(header: &[u8]) -> bool {
100 header.starts_with(needle:b"II\x2A\x00") || header.starts_with(needle:b"MM\x00\x2A")
101}
102