1use 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
40pub struct Reader {
63 continue_on_error: bool,
64}
65
66impl Reader {
67 pub fn new() -> Self {
69 Self {
70 continue_on_error: false,
71 }
72 }
73
74 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 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 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
161pub struct Exif {
182 buf: Vec<u8>,
184 entries: Vec<IfdEntry>,
187 entry_map: HashMap<(In, Tag), usize>,
189 little_endian: bool,
191}
192
193impl Exif {
194 #[inline]
196 pub fn buf(&self) -> &[u8] {
197 &self.buf[..]
198 }
199
200 #[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 #[inline]
210 pub fn little_endian(&self) -> bool {
211 self.little_endian
212 }
213
214 #[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 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 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 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 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 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}