1use crate::error::{Error, Result};
7
8const MAGIC: [u8; 8] = [0x89, b'P', b'N', b'G', 0x0D, 0x0A, 0x1A, 0x0A];
9
10pub fn dimensions(data: &[u8]) -> Result<(u32, u32)> {
12 if data.len() < 24 {
13 return Err(Error::Shape(format!("png too short: {} bytes", data.len())));
14 }
15 if data[..8] != MAGIC {
16 return Err(Error::Shape("not a png (bad magic)".into()));
17 }
18 if &data[12..16] != b"IHDR" {
19 return Err(Error::Shape("first chunk is not IHDR".into()));
20 }
21 let w = u32::from_be_bytes([data[16], data[17], data[18], data[19]]);
22 let h = u32::from_be_bytes([data[20], data[21], data[22], data[23]]);
23 Ok((w, h))
24}
25
26#[cfg(test)]
27mod tests {
28 use super::*;
29
30 fn header(w: u32, h: u32) -> Vec<u8> {
31 let mut v = Vec::new();
32 v.extend_from_slice(&MAGIC);
33 v.extend_from_slice(&13u32.to_be_bytes());
34 v.extend_from_slice(b"IHDR");
35 v.extend_from_slice(&w.to_be_bytes());
36 v.extend_from_slice(&h.to_be_bytes());
37 v
38 }
39
40 #[test]
41 fn reads_dimensions() {
42 assert_eq!(dimensions(&header(1290, 2796)).unwrap(), (1290, 2796));
43 }
44
45 #[test]
46 fn rejects_non_png() {
47 let mut bad = header(1, 1);
48 bad[1] = b'X';
49 assert!(dimensions(&bad).is_err());
50 }
51
52 #[test]
53 fn rejects_truncated() {
54 assert!(dimensions(&[0u8; 10]).is_err());
55 }
56}