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