exif/
reader.rs

1//
2// Copyright (c) 2017 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::collections::HashMap;
28use std::io;
29use std::io::Read;
30
31use crate::error::Error;
32use crate::isobmff;
33use crate::jpeg;
34use crate::png;
35use crate::tag::Tag;
36use crate::tiff;
37use crate::tiff::{Field, IfdEntry, In, ProvideUnit};
38use crate::webp;
39
40/// A struct to parse the Exif attributes and
41/// create an `Exif` instance that holds the results.
42///
43/// # Examples
44/// ```
45/// # use std::fmt::{Display, Formatter, Result};
46/// # #[derive(Debug)] struct Error(&'static str);
47/// # impl std::error::Error for Error {}
48/// # impl Display for Error {
49/// #     fn fmt(&self, f: &mut Formatter) -> Result { f.write_str(self.0) }
50/// # }
51/// # fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
52/// use exif::{In, Reader, Tag};
53/// let file = std::fs::File::open("tests/exif.jpg")?;
54/// let exif = Reader::new()
55///     .read_from_container(&mut std::io::BufReader::new(&file))?;
56/// let xres = exif.get_field(Tag::XResolution, In::PRIMARY)
57///     .ok_or(Error("tests/exif.jpg must have XResolution"))?;
58/// assert_eq!(xres.display_value().with_unit(&exif).to_string(),
59///            "72 pixels per inch");
60/// # Ok(()) }
61/// ```
62pub struct Reader {
63}
64
65impl Reader {
66    /// Constructs a new `Reader`.
67    pub fn new() -> Self {
68        Self {}
69    }
70
71    /// Parses the Exif attributes from raw Exif data.
72    /// If an error occurred, `exif::Error` is returned.
73    pub fn read_raw(&self, data: Vec<u8>) -> Result<Exif, Error> {
74        let mut parser = tiff::Parser::new();
75        parser.parse(&data)?;
76        let entry_map = parser.entries.iter().enumerate()
77            .map(|(i, e)| (e.ifd_num_tag(), i)).collect();
78        Ok(Exif {
79            buf: data,
80            entries: parser.entries,
81            entry_map: entry_map,
82            little_endian: parser.little_endian,
83        })
84    }
85
86    /// Reads an image file and parses the Exif attributes in it.
87    /// If an error occurred, `exif::Error` is returned.
88    ///
89    /// Supported formats are:
90    /// - TIFF and some RAW image formats based on it
91    /// - JPEG
92    /// - HEIF and coding-specific variations including HEIC and AVIF
93    /// - PNG
94    /// - WebP
95    ///
96    /// This method is provided for the convenience even though
97    /// parsing containers is basically out of the scope of this library.
98    pub fn read_from_container<R>(&self, reader: &mut R) -> Result<Exif, Error>
99    where R: io::BufRead + io::Seek {
100        let mut buf = Vec::new();
101        reader.by_ref().take(4096).read_to_end(&mut buf)?;
102        if tiff::is_tiff(&buf) {
103            reader.read_to_end(&mut buf)?;
104        } else if jpeg::is_jpeg(&buf) {
105            buf = jpeg::get_exif_attr(&mut buf.chain(reader))?;
106        } else if png::is_png(&buf) {
107            buf = png::get_exif_attr(&mut buf.chain(reader))?;
108        } else if isobmff::is_heif(&buf) {
109            reader.seek(io::SeekFrom::Start(0))?;
110            buf = isobmff::get_exif_attr(reader)?;
111        } else if webp::is_webp(&buf) {
112            buf = webp::get_exif_attr(&mut buf.chain(reader))?;
113        } else {
114            return Err(Error::InvalidFormat("Unknown image format"));
115        }
116
117        self.read_raw(buf)
118    }
119}
120
121/// A struct that holds the parsed Exif attributes.
122///
123/// # Examples
124/// ```
125/// # fn main() { sub(); }
126/// # fn sub() -> Option<()> {
127/// # use exif::{In, Reader, Tag};
128/// # let file = std::fs::File::open("tests/exif.jpg").unwrap();
129/// # let exif = Reader::new().read_from_container(
130/// #     &mut std::io::BufReader::new(&file)).unwrap();
131/// // Get a specific field.
132/// let xres = exif.get_field(Tag::XResolution, In::PRIMARY)?;
133/// assert_eq!(xres.display_value().with_unit(&exif).to_string(),
134///            "72 pixels per inch");
135/// // Iterate over all fields.
136/// for f in exif.fields() {
137///     println!("{} {} {}", f.tag, f.ifd_num, f.display_value());
138/// }
139/// # Some(()) }
140/// ```
141pub struct Exif {
142    // TIFF data.
143    buf: Vec<u8>,
144    // Exif fields.  Vec is used to keep the ability to enumerate all fields
145    // even if there are duplicates.
146    entries: Vec<IfdEntry>,
147    // HashMap to the index of the Vec for faster random access.
148    entry_map: HashMap<(In, Tag), usize>,
149    // True if the TIFF data is little endian.
150    little_endian: bool,
151}
152
153impl Exif {
154    /// Returns the slice that contains the TIFF data.
155    #[inline]
156    pub fn buf(&self) -> &[u8] {
157        &self.buf[..]
158    }
159
160    /// Returns an iterator of Exif fields.
161    #[inline]
162    pub fn fields(&self) -> impl ExactSizeIterator<Item = &Field> {
163        self.entries.iter()
164            .map(move |e| e.ref_field(&self.buf, self.little_endian))
165    }
166
167    /// Returns true if the Exif data (TIFF structure) is in the
168    /// little-endian byte order.
169    #[inline]
170    pub fn little_endian(&self) -> bool {
171        self.little_endian
172    }
173
174    /// Returns a reference to the Exif field specified by the tag
175    /// and the IFD number.
176    #[inline]
177    pub fn get_field(&self, tag: Tag, ifd_num: In) -> Option<&Field> {
178        self.entry_map.get(&(ifd_num, tag))
179            .map(|&i| self.entries[i].ref_field(&self.buf, self.little_endian))
180    }
181}
182
183impl<'a> ProvideUnit<'a> for &'a Exif {
184    fn get_field(self, tag: Tag, ifd_num: In) -> Option<&'a Field> {
185        self.get_field(tag, ifd_num)
186    }
187}
188
189#[cfg(test)]
190mod tests {
191    use std::fs::File;
192    use std::io::BufReader;
193    use crate::tag::Context;
194    use crate::value::Value;
195    use super::*;
196
197    #[test]
198    fn get_field() {
199        let file = File::open("tests/yaminabe.tif").unwrap();
200        let exif = Reader::new().read_from_container(
201            &mut BufReader::new(&file)).unwrap();
202        match exif.get_field(Tag::ImageDescription, In(0)).unwrap().value {
203            Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test image"]),
204            ref v => panic!("wrong variant {:?}", v)
205        }
206        match exif.get_field(Tag::ImageDescription, In(1)).unwrap().value {
207            Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test thumbnail"]),
208            ref v => panic!("wrong variant {:?}", v)
209        }
210        match exif.get_field(Tag::ImageDescription, In(2)).unwrap().value {
211            Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test 2nd IFD"]),
212            ref v => panic!("wrong variant {:?}", v)
213        }
214    }
215
216    #[test]
217    fn display_value_with_unit() {
218        let file = File::open("tests/yaminabe.tif").unwrap();
219        let exif = Reader::new().read_from_container(
220            &mut BufReader::new(&file)).unwrap();
221        // No unit.
222        let exifver = exif.get_field(Tag::ExifVersion, In::PRIMARY).unwrap();
223        assert_eq!(exifver.display_value().with_unit(&exif).to_string(),
224                   "2.31");
225        // Fixed string.
226        let width = exif.get_field(Tag::ImageWidth, In::PRIMARY).unwrap();
227        assert_eq!(width.display_value().with_unit(&exif).to_string(),
228                   "17 pixels");
229        // Unit tag (with a non-default value).
230        let gpsalt = exif.get_field(Tag::GPSAltitude, In::PRIMARY).unwrap();
231        assert_eq!(gpsalt.display_value().with_unit(&exif).to_string(),
232                   "0.5 meters below sea level");
233        // Unit tag is missing but the default is specified.
234        let xres = exif.get_field(Tag::XResolution, In::PRIMARY).unwrap();
235        assert_eq!(xres.display_value().with_unit(&exif).to_string(),
236                   "72 pixels per inch");
237        // Unit tag is missing and the default is not specified.
238        let gpslat = exif.get_field(Tag::GPSLatitude, In::PRIMARY).unwrap();
239        assert_eq!(gpslat.display_value().with_unit(&exif).to_string(),
240                   "10 deg 0 min 0 sec [GPSLatitudeRef missing]");
241    }
242
243    #[test]
244    fn yaminabe() {
245        let file = File::open("tests/yaminabe.tif").unwrap();
246        let be = Reader::new().read_from_container(
247            &mut BufReader::new(&file)).unwrap();
248        let file = File::open("tests/yaminale.tif").unwrap();
249        let le = Reader::new().read_from_container(
250            &mut BufReader::new(&file)).unwrap();
251        assert!(!be.little_endian());
252        assert!(le.little_endian());
253        for exif in &[be, le] {
254            assert_eq!(exif.fields().len(), 26);
255            let f = exif.get_field(Tag::ImageWidth, In(0)).unwrap();
256            assert_eq!(f.display_value().to_string(), "17");
257            let f = exif.get_field(Tag::Humidity, In(0)).unwrap();
258            assert_eq!(f.display_value().to_string(), "65");
259            let f = exif.get_field(Tag(Context::Tiff, 65000), In(0)).unwrap();
260            match f.value {
261                Value::Float(ref v) => assert_eq!(v[0], std::f32::MIN),
262                _ => panic!(),
263            }
264            let f = exif.get_field(Tag(Context::Tiff, 65001), In(0)).unwrap();
265            match f.value {
266                Value::Double(ref v) => assert_eq!(v[0], std::f64::MIN),
267                _ => panic!(),
268            }
269        }
270    }
271
272    #[test]
273    fn heif() {
274        let file = std::fs::File::open("tests/exif.heic").unwrap();
275        let exif = Reader::new().read_from_container(
276            &mut std::io::BufReader::new(&file)).unwrap();
277        assert_eq!(exif.fields().len(), 2);
278        let exifver = exif.get_field(Tag::ExifVersion, In::PRIMARY).unwrap();
279        assert_eq!(exifver.display_value().to_string(), "2.31");
280    }
281
282    #[test]
283    fn png() {
284        let file = std::fs::File::open("tests/exif.png").unwrap();
285        let exif = Reader::new().read_from_container(
286            &mut std::io::BufReader::new(&file)).unwrap();
287        assert_eq!(exif.fields().len(), 6);
288        let exifver = exif.get_field(Tag::ExifVersion, In::PRIMARY).unwrap();
289        assert_eq!(exifver.display_value().to_string(), "2.32");
290    }
291
292    #[test]
293    fn webp() {
294        let file = std::fs::File::open("tests/exif.webp").unwrap();
295        let exif = Reader::new().read_from_container(
296            &mut std::io::BufReader::new(&file)).unwrap();
297        assert_eq!(exif.fields().len(), 6);
298        let exifver = exif.get_field(Tag::ExifVersion, In::PRIMARY).unwrap();
299        assert_eq!(exifver.display_value().to_string(), "2.32");
300        let desc = exif.get_field(Tag::ImageDescription, In::PRIMARY).unwrap();
301        assert_eq!(desc.display_value().to_string(), "\"WebP test\"");
302    }
303}