1use 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
40pub struct Reader {
63}
64
65impl Reader {
66 pub fn new() -> Self {
68 Self {}
69 }
70
71 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 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
121pub struct Exif {
142 buf: Vec<u8>,
144 entries: Vec<IfdEntry>,
147 entry_map: HashMap<(In, Tag), usize>,
149 little_endian: bool,
151}
152
153impl Exif {
154 #[inline]
156 pub fn buf(&self) -> &[u8] {
157 &self.buf[..]
158 }
159
160 #[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 #[inline]
170 pub fn little_endian(&self) -> bool {
171 self.little_endian
172 }
173
174 #[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 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 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 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 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 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}