1 //
2 // Copyright (c) 2020 KAMADA Ken'ichi.
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions
7 // are met:
8 // 1. Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // 2. Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 // SUCH DAMAGE.
25 //
26
27 use std::io::{BufRead, ErrorKind};
28
29 use crate::endian::{Endian, BigEndian};
30 use crate::error::Error;
31 use crate::util::{BufReadExt as _, ReadExt as _};
32
33 // PNG file signature [PNG12 12.12].
34 const PNG_SIG: [u8; 8] = *b"\x89PNG\x0d\x0a\x1a\x0a";
35 // The four-byte chunk type for Exif data.
36 const EXIF_CHUNK_TYPE: [u8; 4] = *b"eXIf";
37
38 // Get the contents of the eXIf chunk from a PNG file.
get_exif_attr<R>(reader: &mut R) -> Result<Vec<u8>, Error> where R: BufRead39 pub 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
48 // The location of the eXIf chunk is restricted [PNGEXT150 3.7], but this
49 // reader is liberal about it.
get_exif_attr_sub<R>(reader: &mut R) -> Result<Vec<u8>, Error> where R: BufRead50 fn 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 // Scan the series of chunks.
58 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 // Chunk data and CRC.
73 reader.discard_exact(len.checked_add(4).ok_or(
74 Error::InvalidFormat("Invalid chunk length"))?)?;
75 }
76 }
77
is_png(buf: &[u8]) -> bool78 pub fn is_png(buf: &[u8]) -> bool {
79 buf.starts_with(&PNG_SIG)
80 }
81
82 #[cfg(test)]
83 mod tests {
84 use super::*;
85
86 #[test]
truncated()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]
no_exif()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]
empty()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]
non_empty()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 }
124