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, PartialResult};
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    continue_on_error: bool,
64}
65
66impl Reader {
67    /// Constructs a new `Reader`.
68    pub fn new() -> Self {
69        Self {
70            continue_on_error: false,
71        }
72    }
73
74    /// Sets the option to continue parsing on non-fatal errors.
75    ///
76    /// When this option is enabled, the parser will not stop on non-fatal
77    /// errors and returns the results as far as they can be parsed.
78    /// In such a case, `read_raw` and `read_from_container`
79    /// return `Error::PartialResult`.
80    /// The partial result and ignored errors can be obtained by
81    /// [`Error::distill_partial_result`] or [`PartialResult::into_inner`].
82    ///
83    /// Note that a hard error (other than `Error::PartialResult`) may be
84    /// returned even if this option is enabled.
85    ///
86    /// # Examples
87    /// ```
88    /// # use std::fmt::{Display, Formatter, Result};
89    /// # fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
90    /// use exif::Reader;
91    /// let file = std::fs::File::open("tests/exif.jpg")?;
92    /// let exif = Reader::new()
93    ///     .continue_on_error(true)
94    ///     .read_from_container(&mut std::io::BufReader::new(&file))
95    ///     .or_else(|e| e.distill_partial_result(|errors| {
96    ///         errors.iter().for_each(|e| eprintln!("Warning: {}", e));
97    ///     }))?;
98    /// # Ok(()) }
99    /// ```
100    pub fn continue_on_error(&mut self, continue_on_error: bool) -> &mut Self {
101        self.continue_on_error = continue_on_error;
102        self
103    }
104
105    /// Parses the Exif attributes from raw Exif data.
106    /// If an error occurred, `exif::Error` is returned.
107    pub fn read_raw(&self, data: Vec<u8>) -> Result<Exif, Error> {
108        let mut parser = tiff::Parser::new();
109        parser.continue_on_error = self.continue_on_error.then(|| Vec::new());
110        parser.parse(&data)?;
111        let entry_map = parser.entries.iter().enumerate()
112            .map(|(i, e)| (e.ifd_num_tag(), i)).collect();
113        let exif = Exif {
114            buf: data,
115            entries: parser.entries,
116            entry_map: entry_map,
117            little_endian: parser.little_endian,
118        };
119        match parser.continue_on_error {
120            Some(v) if !v.is_empty() =>
121                Err(Error::PartialResult(PartialResult::new(exif, v))),
122            _ => Ok(exif),
123        }
124    }
125
126    /// Reads an image file and parses the Exif attributes in it.
127    /// If an error occurred, `exif::Error` is returned.
128    ///
129    /// Supported formats are:
130    /// - TIFF and some RAW image formats based on it
131    /// - JPEG
132    /// - HEIF and coding-specific variations including HEIC and AVIF
133    /// - PNG
134    /// - WebP
135    ///
136    /// This method is provided for the convenience even though
137    /// parsing containers is basically out of the scope of this library.
138    pub fn read_from_container<R>(&self, reader: &mut R) -> Result<Exif, Error>
139    where R: io::BufRead + io::Seek {
140        let mut buf = Vec::new();
141        reader.by_ref().take(4096).read_to_end(&mut buf)?;
142        if tiff::is_tiff(&buf) {
143            reader.read_to_end(&mut buf)?;
144        } else if jpeg::is_jpeg(&buf) {
145            buf = jpeg::get_exif_attr(&mut buf.chain(reader))?;
146        } else if png::is_png(&buf) {
147            buf = png::get_exif_attr(&mut buf.chain(reader))?;
148        } else if isobmff::is_heif(&buf) {
149            reader.seek(io::SeekFrom::Start(0))?;
150            buf = isobmff::get_exif_attr(reader)?;
151        } else if webp::is_webp(&buf) {
152            buf = webp::get_exif_attr(&mut buf.chain(reader))?;
153        } else {
154            return Err(Error::InvalidFormat("Unknown image format"));
155        }
156
157        self.read_raw(buf)
158    }
159}
160
161/// A struct that holds the parsed Exif attributes.
162///
163/// # Examples
164/// ```
165/// # fn main() { sub(); }
166/// # fn sub() -> Option<()> {
167/// # use exif::{In, Reader, Tag};
168/// # let file = std::fs::File::open("tests/exif.jpg").unwrap();
169/// # let exif = Reader::new().read_from_container(
170/// #     &mut std::io::BufReader::new(&file)).unwrap();
171/// // Get a specific field.
172/// let xres = exif.get_field(Tag::XResolution, In::PRIMARY)?;
173/// assert_eq!(xres.display_value().with_unit(&exif).to_string(),
174///            "72 pixels per inch");
175/// // Iterate over all fields.
176/// for f in exif.fields() {
177///     println!("{} {} {}", f.tag, f.ifd_num, f.display_value());
178/// }
179/// # Some(()) }
180/// ```
181pub struct Exif {
182    // TIFF data.
183    buf: Vec<u8>,
184    // Exif fields.  Vec is used to keep the ability to enumerate all fields
185    // even if there are duplicates.
186    entries: Vec<IfdEntry>,
187    // HashMap to the index of the Vec for faster random access.
188    entry_map: HashMap<(In, Tag), usize>,
189    // True if the TIFF data is little endian.
190    little_endian: bool,
191}
192
193impl Exif {
194    /// Returns the slice that contains the TIFF data.
195    #[inline]
196    pub fn buf(&self) -> &[u8] {
197        &self.buf[..]
198    }
199
200    /// Returns an iterator of Exif fields.
201    #[inline]
202    pub fn fields(&self) -> impl ExactSizeIterator<Item = &Field> {
203        self.entries.iter()
204            .map(move |e| e.ref_field(&self.buf, self.little_endian))
205    }
206
207    /// Returns true if the Exif data (TIFF structure) is in the
208    /// little-endian byte order.
209    #[inline]
210    pub fn little_endian(&self) -> bool {
211        self.little_endian
212    }
213
214    /// Returns a reference to the Exif field specified by the tag
215    /// and the IFD number.
216    #[inline]
217    pub fn get_field(&self, tag: Tag, ifd_num: In) -> Option<&Field> {
218        self.entry_map.get(&(ifd_num, tag))
219            .map(|&i| self.entries[i].ref_field(&self.buf, self.little_endian))
220    }
221}
222
223impl<'a> ProvideUnit<'a> for &'a Exif {
224    fn get_field(self, tag: Tag, ifd_num: In) -> Option<&'a Field> {
225        self.get_field(tag, ifd_num)
226    }
227}
228
229#[cfg(test)]
230mod tests {
231    use std::fs::File;
232    use std::io::BufReader;
233    use crate::tag::Context;
234    use crate::value::Value;
235    use super::*;
236
237    #[test]
238    fn get_field() {
239        let file = File::open("tests/yaminabe.tif").unwrap();
240        let exif = Reader::new().read_from_container(
241            &mut BufReader::new(&file)).unwrap();
242        match exif.get_field(Tag::ImageDescription, In(0)).unwrap().value {
243            Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test image"]),
244            ref v => panic!("wrong variant {:?}", v)
245        }
246        match exif.get_field(Tag::ImageDescription, In(1)).unwrap().value {
247            Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test thumbnail"]),
248            ref v => panic!("wrong variant {:?}", v)
249        }
250        match exif.get_field(Tag::ImageDescription, In(2)).unwrap().value {
251            Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test 2nd IFD"]),
252            ref v => panic!("wrong variant {:?}", v)
253        }
254    }
255
256    #[test]
257    fn display_value_with_unit() {
258        let file = File::open("tests/yaminabe.tif").unwrap();
259        let exif = Reader::new().read_from_container(
260            &mut BufReader::new(&file)).unwrap();
261        // No unit.
262        let exifver = exif.get_field(Tag::ExifVersion, In::PRIMARY).unwrap();
263        assert_eq!(exifver.display_value().with_unit(&exif).to_string(),
264                   "2.31");
265        // Fixed string.
266        let width = exif.get_field(Tag::ImageWidth, In::PRIMARY).unwrap();
267        assert_eq!(width.display_value().with_unit(&exif).to_string(),
268                   "17 pixels");
269        // Unit tag (with a non-default value).
270        let gpsalt = exif.get_field(Tag::GPSAltitude, In::PRIMARY).unwrap();
271        assert_eq!(gpsalt.display_value().with_unit(&exif).to_string(),
272                   "0.5 meters below sea level");
273        // Unit tag is missing but the default is specified.
274        let xres = exif.get_field(Tag::XResolution, In::PRIMARY).unwrap();
275        assert_eq!(xres.display_value().with_unit(&exif).to_string(),
276                   "72 pixels per inch");
277        // Unit tag is missing and the default is not specified.
278        let gpslat = exif.get_field(Tag::GPSLatitude, In::PRIMARY).unwrap();
279        assert_eq!(gpslat.display_value().with_unit(&exif).to_string(),
280                   "10 deg 0 min 0 sec [GPSLatitudeRef missing]");
281    }
282
283    #[test]
284    fn yaminabe() {
285        let file = File::open("tests/yaminabe.tif").unwrap();
286        let be = Reader::new().read_from_container(
287            &mut BufReader::new(&file)).unwrap();
288        let file = File::open("tests/yaminale.tif").unwrap();
289        let le = Reader::new().read_from_container(
290            &mut BufReader::new(&file)).unwrap();
291        assert!(!be.little_endian());
292        assert!(le.little_endian());
293        for exif in &[be, le] {
294            assert_eq!(exif.fields().len(), 26);
295            let f = exif.get_field(Tag::ImageWidth, In(0)).unwrap();
296            assert_eq!(f.display_value().to_string(), "17");
297            let f = exif.get_field(Tag::Humidity, In(0)).unwrap();
298            assert_eq!(f.display_value().to_string(), "65");
299            let f = exif.get_field(Tag(Context::Tiff, 65000), In(0)).unwrap();
300            match f.value {
301                Value::Float(ref v) => assert_eq!(v[0], std::f32::MIN),
302                _ => panic!(),
303            }
304            let f = exif.get_field(Tag(Context::Tiff, 65001), In(0)).unwrap();
305            match f.value {
306                Value::Double(ref v) => assert_eq!(v[0], std::f64::MIN),
307                _ => panic!(),
308            }
309        }
310    }
311
312    #[test]
313    fn heif() {
314        let file = std::fs::File::open("tests/exif.heic").unwrap();
315        let exif = Reader::new().read_from_container(
316            &mut std::io::BufReader::new(&file)).unwrap();
317        assert_eq!(exif.fields().len(), 2);
318        let exifver = exif.get_field(Tag::ExifVersion, In::PRIMARY).unwrap();
319        assert_eq!(exifver.display_value().to_string(), "2.31");
320    }
321
322    #[test]
323    fn png() {
324        let file = std::fs::File::open("tests/exif.png").unwrap();
325        let exif = Reader::new().read_from_container(
326            &mut std::io::BufReader::new(&file)).unwrap();
327        assert_eq!(exif.fields().len(), 6);
328        let exifver = exif.get_field(Tag::ExifVersion, In::PRIMARY).unwrap();
329        assert_eq!(exifver.display_value().to_string(), "2.32");
330    }
331
332    #[test]
333    fn webp() {
334        let file = std::fs::File::open("tests/exif.webp").unwrap();
335        let exif = Reader::new().read_from_container(
336            &mut std::io::BufReader::new(&file)).unwrap();
337        assert_eq!(exif.fields().len(), 6);
338        let exifver = exif.get_field(Tag::ExifVersion, In::PRIMARY).unwrap();
339        assert_eq!(exifver.display_value().to_string(), "2.32");
340        let desc = exif.get_field(Tag::ImageDescription, In::PRIMARY).unwrap();
341        assert_eq!(desc.display_value().to_string(), "\"WebP test\"");
342    }
343
344    #[test]
345    fn continue_on_error() {
346        let data = b"MM\0\x2a\0\0\0\x08\
347                     \0\x02\x01\x00\0\x03\0\0\0\x01\0\x14\0\0\
348                           \x01\x01\0\x03\0\0\0\x01\0\x15\0";
349        let result = Reader::new()
350            .continue_on_error(true)
351            .read_raw(data.to_vec());
352        if let Err(Error::PartialResult(partial)) = result {
353            let (exif, errors) = partial.into_inner();
354            assert_pat!(exif.fields().collect::<Vec<_>>().as_slice(),
355                        [Field { tag: Tag::ImageWidth, ifd_num: In(0),
356                                 value: Value::Short(_) }]);
357            assert_pat!(&errors[..], [Error::InvalidFormat("Truncated IFD")]);
358        } else {
359            panic!("partial result expected");
360        }
361    }
362}