imagesize/formats/
tiff.rs1use 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    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        return Err(
20            std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid TIFF header").into(),
21        );
22    };
23
24    reader.seek(SeekFrom::Start(4))?;
26    let ifd_offset = read_u32(reader, &endianness)?;
27
28    if ifd_offset == 0 {
30        return Err(
31            std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid IFD offset").into(),
32        );
33    }
34
35    reader.seek(SeekFrom::Start(ifd_offset.into()))?;
37
38    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            1 | 2 | 6 | 7 => 1,
51            3 | 8 => 2,
53            4 | 9 | 11 | 13 => 4,
55            5 | 10 => 4 * 2,
57            12 | 16 | 17 | 18 => 8,
59            _ => {
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        if tag == 0x100 {
81            width = value;
82        } else if tag == 0x101 {
83            height = value;
84        }
85
86        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    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(b"II\x2A\x00") || header.starts_with(b"MM\x00\x2A")
101}