1use std::io::{BufRead, ErrorKind};
28
29use crate::endian::{Endian, BigEndian};
30use crate::error::Error;
31use crate::util::{BufReadExt as _, ReadExt as _};
32
33const PNG_SIG: [u8; 8] = *b"\x89PNG\x0d\x0a\x1a\x0a";
35const EXIF_CHUNK_TYPE: [u8; 4] = *b"eXIf";
37
38pub fn get_exif_attr<R>(reader: &mut R)
40 -> Result<Vec<u8>, Error> where R: BufRead {
41 match get_exif_attr_sub(reader) {
42 Err(Error::Io(ref e)) if e.kind() == ErrorKind::UnexpectedEof =>
43 Err(Error::InvalidFormat("Broken PNG file")),
44 r => r,
45 }
46}
47
48fn get_exif_attr_sub<R>(reader: &mut R)
51 -> Result<Vec<u8>, Error> where R: BufRead {
52 let mut sig = [0u8; 8];
53 reader.read_exact(&mut sig)?;
54 if sig != PNG_SIG {
55 return Err(Error::InvalidFormat("Not a PNG file"));
56 }
57 loop {
59 if reader.is_eof()? {
60 return Err(Error::NotFound("PNG"));
61 }
62 let mut lenbuf = [0; 4];
63 reader.read_exact(&mut lenbuf)?;
64 let len = BigEndian::loadu32(&lenbuf, 0) as usize;
65 let mut ctype = [0u8; 4];
66 reader.read_exact(&mut ctype)?;
67 if ctype == EXIF_CHUNK_TYPE {
68 let mut data = Vec::new();
69 reader.read_exact_len(&mut data, len)?;
70 return Ok(data);
71 }
72 reader.discard_exact(len.checked_add(4).ok_or(
74 Error::InvalidFormat("Invalid chunk length"))?)?;
75 }
76}
77
78pub fn is_png(buf: &[u8]) -> bool {
79 buf.starts_with(&PNG_SIG)
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn truncated() {
88 let sets: &[&[u8]] = &[
89 b"",
90 b"\x89",
91 b"\x89PNG\x0d\x0a\x1a",
92 ];
93 for &data in sets {
94 assert_err_pat!(get_exif_attr(&mut &data[..]),
95 Error::InvalidFormat("Broken PNG file"));
96 }
97
98 let mut data = b"\x89PNG\x0d\x0a\x1a\x0a\0\0\0\x04eXIfExif".to_vec();
99 assert_eq!(get_exif_attr(&mut &data[..]).unwrap(), b"Exif");
100 while let Some(_) = data.pop() {
101 get_exif_attr(&mut &data[..]).unwrap_err();
102 }
103 }
104
105 #[test]
106 fn no_exif() {
107 let data = b"\x89PNG\x0d\x0a\x1a\x0a";
108 assert_err_pat!(get_exif_attr(&mut &data[..]),
109 Error::NotFound(_));
110 }
111
112 #[test]
113 fn empty() {
114 let data = b"\x89PNG\x0d\x0a\x1a\x0a\0\0\0\0eXIfCRC_";
115 assert_ok!(get_exif_attr(&mut &data[..]), []);
116 }
117
118 #[test]
119 fn non_empty() {
120 let data = b"\x89PNG\x0d\x0a\x1a\x0a\0\0\0\x02eXIf\xbe\xadCRC_";
121 assert_ok!(get_exif_attr(&mut &data[..]), [0xbe, 0xad]);
122 }
123}