1use nom::{
2 branch::alt, bytes::complete::tag, combinator, number::Endianness, sequence, IResult, Needed,
3};
4
5use crate::{EntryValue, ExifIter, ExifTag, GPSInfo, ParsedExifEntry};
6
7use super::ifd::ParsedImageFileDirectory;
8
9#[derive(Clone, Debug, PartialEq)]
12pub struct Exif {
13 ifds: Vec<ParsedImageFileDirectory>,
14 gps_info: Option<GPSInfo>,
15}
16
17impl Exif {
18 fn new(gps_info: Option<GPSInfo>) -> Exif {
19 Exif {
20 ifds: Vec::new(),
21 gps_info,
22 }
23 }
24
25 pub fn get(&self, tag: ExifTag) -> Option<&EntryValue> {
56 self.get_by_ifd_tag_code(0, tag.code())
57 }
58
59 pub fn get_by_ifd_tag_code(&self, ifd: usize, tag: u16) -> Option<&EntryValue> {
92 self.ifds.get(ifd).and_then(|ifd| ifd.get(tag))
93 }
94
95 #[deprecated(
101 since = "1.5.0",
102 note = "please use [`Self::get`] or [`ExifIter`] instead"
103 )]
104 pub fn get_values<'b>(&self, tags: &'b [ExifTag]) -> Vec<(&'b ExifTag, EntryValue)> {
105 tags.iter()
106 .zip(tags.iter())
107 .filter_map(|x| {
108 #[allow(deprecated)]
109 self.get_value(x.0)
110 .map(|v| v.map(|v| (x.0, v)))
111 .unwrap_or(None)
112 })
113 .collect::<Vec<_>>()
114 }
115
116 #[deprecated(since = "1.5.0", note = "please use [`Self::get`] instead")]
118 pub fn get_value(&self, tag: &ExifTag) -> crate::Result<Option<EntryValue>> {
119 #[allow(deprecated)]
120 self.get_value_by_tag_code(tag.code())
121 }
122
123 #[deprecated(since = "1.5.0", note = "please use [`Self::get_by_tag_code`] instead")]
125 pub fn get_value_by_tag_code(&self, tag: u16) -> crate::Result<Option<EntryValue>> {
126 Ok(self.get_by_ifd_tag_code(0, tag).map(|x| x.to_owned()))
127 }
128
129 pub fn get_gps_info(&self) -> crate::Result<Option<GPSInfo>> {
131 Ok(self.gps_info.clone())
132 }
133
134 fn put(&mut self, res: &mut ParsedExifEntry) {
135 while self.ifds.len() < res.ifd_index() + 1 {
136 self.ifds.push(ParsedImageFileDirectory::new());
137 }
138 if let Some(v) = res.take_value() {
139 self.ifds[res.ifd_index()].put(res.tag_code(), v);
140 }
141 }
142}
143
144impl From<ExifIter> for Exif {
145 fn from(iter: ExifIter) -> Self {
146 let gps_info = iter.parse_gps_info().ok().flatten();
147 let mut exif = Exif::new(gps_info);
148
149 for mut it in iter {
150 exif.put(&mut it);
151 }
152
153 exif
154 }
155}
156
157#[derive(Clone, Debug, PartialEq, Eq)]
159pub(crate) struct TiffHeader {
160 pub endian: Endianness,
161 pub ifd0_offset: u32,
162}
163
164impl Default for TiffHeader {
165 fn default() -> Self {
166 Self {
167 endian: Endianness::Big,
168 ifd0_offset: 0,
169 }
170 }
171}
172
173pub(crate) const IFD_ENTRY_SIZE: usize = 12;
174
175impl TiffHeader {
176 pub fn parse(input: &[u8]) -> IResult<&[u8], TiffHeader> {
177 use nom::number::streaming::{u16, u32};
178 let (remain, endian) = TiffHeader::parse_endian(input)?;
179 let (_, (_, offset)) = sequence::tuple((
180 combinator::verify(u16(endian), |magic| *magic == 0x2a),
181 u32(endian),
182 ))(remain)?;
183
184 let header = Self {
185 endian,
186 ifd0_offset: offset,
187 };
188
189 Ok((remain, header))
190 }
191
192 pub fn parse_ifd_entry_num(input: &[u8], endian: Endianness) -> IResult<&[u8], u16> {
193 let (remain, num) = nom::number::streaming::u16(endian)(input)?; if num == 0 {
195 return Ok((remain, 0));
196 }
197
198 let size = (num as usize)
200 .checked_mul(IFD_ENTRY_SIZE)
201 .expect("should fit");
202
203 if size > remain.len() {
204 return Err(nom::Err::Incomplete(Needed::new(size - remain.len())));
205 }
206
207 Ok((remain, num))
208 }
209
210 fn parse_endian(input: &[u8]) -> IResult<&[u8], Endianness> {
222 combinator::map(alt((tag("MM"), tag("II"))), |endian_marker| {
223 if endian_marker == b"MM" {
224 Endianness::Big
225 } else {
226 Endianness::Little
227 }
228 })(input)
229 }
230}
231
232pub(crate) fn check_exif_header(data: &[u8]) -> bool {
234 use nom::bytes::complete;
235 assert!(data.len() >= 6);
236
237 complete::tag::<_, _, nom::error::Error<_>>(EXIF_IDENT)(data).is_ok()
238}
239
240pub(crate) fn check_exif_header2(i: &[u8]) -> IResult<&[u8], ()> {
241 let (remain, _) = nom::sequence::tuple((
242 nom::number::complete::be_u32,
243 nom::bytes::complete::tag(EXIF_IDENT),
244 ))(i)?;
245 Ok((remain, ()))
246}
247
248pub(crate) const EXIF_IDENT: &str = "Exif\0\0";
249
250#[cfg(test)]
251mod tests {
252 use std::io::Read;
253 use std::thread;
254
255 use crate::partial_vec::PartialVec;
256 use test_case::test_case;
257
258 use crate::exif::input_into_iter;
259 use crate::jpeg::extract_exif_data;
260 use crate::slice::SubsliceRange;
261 use crate::testkit::{open_sample, read_sample};
262 use crate::ParsedExifEntry;
263
264 use super::*;
265
266 #[test]
267 fn header() {
268 let _ = tracing_subscriber::fmt().with_test_writer().try_init();
269
270 let buf = [0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00];
271
272 let (_, header) = TiffHeader::parse(&buf).unwrap();
273 assert_eq!(
274 header,
275 TiffHeader {
276 endian: Endianness::Big,
277 ifd0_offset: 8,
278 }
279 );
280 }
281
282 #[test_case("exif.jpg")]
283 fn exif_iter_gps(path: &str) {
284 let buf = read_sample(path).unwrap();
285 let (_, data) = extract_exif_data(&buf).unwrap();
286 let data = data
287 .and_then(|x| buf.subslice_in_range(x))
288 .map(|x| PartialVec::from_vec_range(buf, x))
289 .unwrap();
290 let iter = input_into_iter(data, None).unwrap();
291 let gps = iter.parse_gps_info().unwrap().unwrap();
292 assert_eq!(gps.format_iso6709(), "+22.53113+114.02148/");
293 }
294
295 #[test_case("exif.jpg")]
296 fn clone_exif_iter_to_thread(path: &str) {
297 let buf = read_sample(path).unwrap();
298 let (_, data) = extract_exif_data(&buf).unwrap();
299 let data = data
300 .and_then(|x| buf.subslice_in_range(x))
301 .map(|x| PartialVec::from_vec_range(buf, x))
302 .unwrap();
303 let iter = input_into_iter(data, None).unwrap();
304 let iter2 = iter.clone();
305
306 let mut expect = String::new();
307 open_sample(&format!("{path}.txt"))
308 .unwrap()
309 .read_to_string(&mut expect)
310 .unwrap();
311
312 let jh = thread::spawn(move || iter_to_str(iter2));
313
314 let result = iter_to_str(iter);
315
316 assert_eq!(result.trim(), expect.trim());
322 assert_eq!(jh.join().unwrap().trim(), expect.trim());
323 }
324
325 fn iter_to_str(it: impl Iterator<Item = ParsedExifEntry>) -> String {
326 let ss = it
327 .map(|x| {
328 format!(
329 "ifd{}.{:<32} ยป {}",
330 x.ifd_index(),
331 x.tag()
332 .map(|t| t.to_string())
333 .unwrap_or_else(|| format!("Unknown(0x{:04x})", x.tag_code())),
334 x.get_result()
335 .map(|v| v.to_string())
336 .map_err(|e| e.to_string())
337 .unwrap_or_else(|s| s)
338 )
339 })
340 .collect::<Vec<String>>();
341 ss.join("\n")
342 }
343}