exif/
png.rs

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
27use std::io::{BufRead, ErrorKind};
28
29use crate::endian::{Endian, BigEndian};
30use crate::error::Error;
31use crate::util::{BufReadExt as _, ReadExt as _};
32
33// PNG file signature [PNG12 12.12].
34const PNG_SIG: [u8; 8] = *b"\x89PNG\x0d\x0a\x1a\x0a";
35// The four-byte chunk type for Exif data.
36const EXIF_CHUNK_TYPE: [u8; 4] = *b"eXIf";
37
38// Get the contents of the eXIf chunk from a PNG file.
39pub 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.
50fn 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
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}