Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_file/src/exif.rs

40.7 KiB, 123 runs

created by r1870400018:17735, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! A dependency-free reader for TIFF-structured image metadata, better known as EXIF.
2//!
3//! The same byte layout appears in several containers. A JPEG carries it inside an `APP1`
4//! segment prefixed with `Exif\0\0`, a TIFF file *is* the structure, and formats such as HEIC
5//! embed the identical block inside a box. [`Exif::from_jpeg`] and [`Exif::from_tiff`] cover the
6//! first two, and [`Exif::from_tiff`] serves the third once the caller has located the payload.
7//!
8//! Parsing is total: no input, however damaged, causes a panic. Every failure returns a tagged
9//! [`Error`] naming the byte offset and the structure that failed. Fields whose tag or type this
10//! module does not recognise are preserved as raw bytes rather than dropped, so a caller can
11//! recover a maker note or a vendor extension that arrived after this code was written.
12//!
13//! # Example
14//! ```no_run
15//! use oxedyne_fe2o3_core::prelude::*;
16//! use oxedyne_fe2o3_file::exif::Exif;
17//!
18//! fn describe(path: &str) -> Outcome<()> {
19//! let dat = res!(std::fs::read(path), IO, File);
20//! if let Some(exif) = res!(Exif::from_jpeg(&dat)) {
21//! let meta = exif.meta();
22//! println!("{:?} {:?}", meta.make, meta.datetime_original);
23//! }
24//! Ok(())
25//! }
26//!
27//! assert!(describe("photo.jpg").is_ok());
28//! ```
29//!
30//! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\
31//! Anthropic Claude
32
33use oxedyne_fe2o3_core::prelude::*;
34
35use std::{
36 collections::BTreeSet,
37 fmt,
38};
39
40
41// Ceilings on a structure that a malformed file could otherwise make unbounded. Crossing either
42// is an error, not a truncation.
43const MAX_IFD_CHAIN: usize = 16;
44const MAX_IFD_ENTRIES: u64 = 4096;
45
46/// Size in bytes of one IFD entry, fixed by TIFF.
47const ENTRY_LEN: u64 = 12;
48
49/// The `Exif\0\0` signature that opens an EXIF `APP1` payload.
50const APP1_SIG: &[u8] = b"Exif\0\0";
51
52/// Numeric identifiers of the tags this module gives names to.
53pub mod tag {
54 // IFD0 and IFD1.
55 /// Image width in pixels, as recorded in IFD0.
56 pub const IMAGE_WIDTH: u16 = 0x0100;
57 /// Image height in pixels, as recorded in IFD0.
58 pub const IMAGE_LENGTH: u16 = 0x0101;
59 /// Camera manufacturer.
60 pub const MAKE: u16 = 0x010F;
61 /// Camera model.
62 pub const MODEL: u16 = 0x0110;
63 /// Orientation of the rows and columns, 1 to 8.
64 pub const ORIENTATION: u16 = 0x0112;
65 /// File change date and time.
66 pub const DATE_TIME: u16 = 0x0132;
67 /// Byte offset of the IFD1 thumbnail.
68 pub const JPEG_INTERCHANGE_FORMAT: u16 = 0x0201;
69 /// Byte length of the IFD1 thumbnail.
70 pub const JPEG_INTERCHANGE_LENGTH: u16 = 0x0202;
71 /// Pointer from IFD0 to the EXIF sub-IFD.
72 pub const EXIF_IFD_POINTER: u16 = 0x8769;
73 /// Pointer from IFD0 to the GPS sub-IFD.
74 pub const GPS_IFD_POINTER: u16 = 0x8825;
75
76 // ExifIFD.
77 /// Exposure time in seconds.
78 pub const EXPOSURE_TIME: u16 = 0x829A;
79 /// Lens aperture as an f-number.
80 pub const F_NUMBER: u16 = 0x829D;
81 /// Sensitivity, historically ISOSpeedRatings.
82 pub const ISO_SPEED_RATINGS: u16 = 0x8827;
83 /// Date and time the original image was captured.
84 pub const DATE_TIME_ORIGINAL: u16 = 0x9003;
85 /// Date and time the image was digitised.
86 pub const CREATE_DATE: u16 = 0x9004;
87 /// UTC offset applying to `DATE_TIME`.
88 pub const OFFSET_TIME: u16 = 0x9010;
89 /// UTC offset applying to `DATE_TIME_ORIGINAL`.
90 pub const OFFSET_TIME_ORIGINAL: u16 = 0x9011;
91 /// UTC offset applying to `CREATE_DATE`.
92 pub const OFFSET_TIME_DIGITIZED: u16 = 0x9012;
93 /// Sub-second fraction for `DATE_TIME`.
94 pub const SUBSEC_TIME: u16 = 0x9290;
95 /// Sub-second fraction for `DATE_TIME_ORIGINAL`.
96 pub const SUBSEC_TIME_ORIGINAL: u16 = 0x9291;
97 /// Sub-second fraction for `CREATE_DATE`.
98 pub const SUBSEC_TIME_DIGITIZED: u16 = 0x9292;
99 /// Actual focal length of the lens in millimetres.
100 pub const FOCAL_LENGTH: u16 = 0x920A;
101 /// Valid image width in pixels, as recorded in the EXIF sub-IFD.
102 pub const EXIF_IMAGE_WIDTH: u16 = 0xA002;
103 /// Valid image height in pixels, as recorded in the EXIF sub-IFD.
104 pub const EXIF_IMAGE_HEIGHT: u16 = 0xA003;
105 /// Pointer from the EXIF sub-IFD to the interoperability sub-IFD.
106 pub const INTEROP_IFD_POINTER: u16 = 0xA005;
107 /// Focal length expressed for a 35 mm film frame.
108 pub const FOCAL_LENGTH_35MM: u16 = 0xA405;
109 /// Lens manufacturer.
110 pub const LENS_MAKE: u16 = 0xA433;
111 /// Lens model.
112 pub const LENS_MODEL: u16 = 0xA434;
113
114 // GPS IFD.
115 /// Hemisphere of the latitude, `N` or `S`.
116 pub const GPS_LATITUDE_REF: u16 = 0x0001;
117 /// Latitude as degrees, minutes and seconds.
118 pub const GPS_LATITUDE: u16 = 0x0002;
119 /// Hemisphere of the longitude, `E` or `W`.
120 pub const GPS_LONGITUDE_REF: u16 = 0x0003;
121 /// Longitude as degrees, minutes and seconds.
122 pub const GPS_LONGITUDE: u16 = 0x0004;
123 /// Datum of the altitude, 0 above sea level and 1 below.
124 pub const GPS_ALTITUDE_REF: u16 = 0x0005;
125 /// Altitude in metres relative to `GPS_ALTITUDE_REF`.
126 pub const GPS_ALTITUDE: u16 = 0x0006;
127 /// UTC time of the fix, as hours, minutes and seconds.
128 pub const GPS_TIMESTAMP: u16 = 0x0007;
129 /// UTC date of the fix, as `YYYY:MM:DD`.
130 pub const GPS_DATESTAMP: u16 = 0x001D;
131}
132
133/// Byte order declared by the TIFF header.
134#[derive(Clone, Copy, Debug, Eq, PartialEq)]
135pub enum ByteOrder {
136 Little, // Intel, written II, least significant byte first
137 Big, // Motorola, written MM, most significant byte first
138}
139
140impl fmt::Display for ByteOrder {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 match self {
143 Self::Little => write!(f, "II"),
144 Self::Big => write!(f, "MM"),
145 }
146 }
147}
148
149impl ByteOrder {
150
151 pub fn u16(&self, b: [u8; 2]) -> u16 {
152 match self {
153 Self::Little => u16::from_le_bytes(b),
154 Self::Big => u16::from_be_bytes(b),
155 }
156 }
157
158 pub fn u32(&self, b: [u8; 4]) -> u32 {
159 match self {
160 Self::Little => u32::from_le_bytes(b),
161 Self::Big => u32::from_be_bytes(b),
162 }
163 }
164}
165
166/// The IFD in which a field was found.
167#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
168pub enum IfdKind {
169 Ifd0, // the primary image
170 Exif, // sub-IFD reached through EXIF_IFD_POINTER
171 Gps, // sub-IFD reached through GPS_IFD_POINTER
172 Interop, // sub-IFD reached through INTEROP_IFD_POINTER
173 Ifd1, // the thumbnail
174}
175
176impl fmt::Display for IfdKind {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 match self {
179 Self::Ifd0 => write!(f, "IFD0"),
180 Self::Exif => write!(f, "ExifIFD"),
181 Self::Gps => write!(f, "GpsIFD"),
182 Self::Interop => write!(f, "InteropIFD"),
183 Self::Ifd1 => write!(f, "IFD1"),
184 }
185 }
186}
187
188/// An unsigned ratio of two 32 bit integers, the TIFF `RATIONAL` type.
189#[derive(Clone, Copy, Debug, Eq, PartialEq)]
190pub struct Rational {
191 pub num: u32,
192 pub den: u32,
193}
194
195impl fmt::Display for Rational {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 write!(f, "{}/{}", self.num, self.den)
198 }
199}
200
201impl Rational {
202 /// `None` when the denominator is zero.
203 pub fn to_f64(&self) -> Option<f64> {
204 if self.den == 0 {
205 None
206 } else {
207 Some(self.num as f64 / self.den as f64)
208 }
209 }
210}
211
212/// A signed ratio of two 32 bit integers, the TIFF `SRATIONAL` type.
213#[derive(Clone, Copy, Debug, Eq, PartialEq)]
214pub struct SRational {
215 pub num: i32,
216 pub den: i32,
217}
218
219impl fmt::Display for SRational {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 write!(f, "{}/{}", self.num, self.den)
222 }
223}
224
225impl SRational {
226 /// `None` when the denominator is zero.
227 pub fn to_f64(&self) -> Option<f64> {
228 if self.den == 0 {
229 None
230 } else {
231 Some(self.num as f64 / self.den as f64)
232 }
233 }
234}
235
236/// The decoded payload of one IFD entry.
237#[derive(Clone, Debug, PartialEq)]
238pub enum Value {
239 Byte(Vec<u8>), // TIFF type 1
240 Ascii(String), // TIFF type 2, NUL terminated
241 Short(Vec<u16>), // TIFF type 3
242 Long(Vec<u32>), // TIFF type 4
243 Rational(Vec<Rational>), // TIFF type 5
244 SByte(Vec<i8>), // TIFF type 6
245 Undefined(Vec<u8>), // TIFF type 7, an opaque run whose meaning the tag defines
246 SShort(Vec<i16>), // TIFF type 8
247 SLong(Vec<i32>), // TIFF type 9
248 SRational(Vec<SRational>), // TIFF type 10
249 Float(Vec<f32>), // TIFF type 11
250 Double(Vec<f64>), // TIFF type 12
251 Ifd(Vec<u32>), // TIFF type 13, an offset to a nested IFD
252 // A type this module does not know, preserved as the raw entry payload.
253 Unknown {
254 typ: u16, // the type code as written in the file
255 count: u32, // the count as written in the file
256 raw: [u8; 4], // the four bytes of the value or offset field
257 },
258}
259
260impl fmt::Display for Value {
261 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262 match self {
263 Self::Ascii(s) => write!(f, "{}", s),
264 Self::Byte(v) => write!(f, "{:?}", v),
265 Self::Short(v) => write!(f, "{:?}", v),
266 Self::Long(v) => write!(f, "{:?}", v),
267 Self::Rational(v) => write!(f, "{}", Self::join(v)),
268 Self::SByte(v) => write!(f, "{:?}", v),
269 Self::Undefined(v) => write!(f, "<{} bytes>", v.len()),
270 Self::SShort(v) => write!(f, "{:?}", v),
271 Self::SLong(v) => write!(f, "{:?}", v),
272 Self::SRational(v) => write!(f, "{}", Self::join(v)),
273 Self::Float(v) => write!(f, "{:?}", v),
274 Self::Double(v) => write!(f, "{:?}", v),
275 Self::Ifd(v) => write!(f, "{:?}", v),
276 Self::Unknown { typ, count, raw } =>
277 write!(f, "<unknown type {} count {} raw {:02x?}>", typ, count, raw),
278 }
279 }
280}
281
282impl Value {
283
284 /// Joins items with a space, the separator the ratio variants display under.
285 fn join<T: fmt::Display>(v: &[T]) -> String {
286 let mut s = String::new();
287 for (i, item) in v.iter().enumerate() {
288 if i > 0 {
289 s.push(' ');
290 }
291 s.push_str(&fmt!("{}", item));
292 }
293 s
294 }
295
296 pub fn len(&self) -> usize {
297 match self {
298 Self::Byte(v) => v.len(),
299 Self::Ascii(s) => s.len(),
300 Self::Short(v) => v.len(),
301 Self::Long(v) => v.len(),
302 Self::Rational(v) => v.len(),
303 Self::SByte(v) => v.len(),
304 Self::Undefined(v) => v.len(),
305 Self::SShort(v) => v.len(),
306 Self::SLong(v) => v.len(),
307 Self::SRational(v) => v.len(),
308 Self::Float(v) => v.len(),
309 Self::Double(v) => v.len(),
310 Self::Ifd(v) => v.len(),
311 Self::Unknown { count, .. } => *count as usize,
312 }
313 }
314
315 pub fn is_empty(&self) -> bool {
316 self.len() == 0
317 }
318
319 pub fn as_str(&self) -> Option<&str> {
320 match self {
321 Self::Ascii(s) => Some(s.as_str()),
322 _ => None,
323 }
324 }
325
326 /// Returns the first element only, where the type widens.
327 pub fn as_u32(&self) -> Option<u32> {
328 match self {
329 Self::Byte(v) => v.first().map(|n| *n as u32),
330 Self::Short(v) => v.first().map(|n| *n as u32),
331 Self::Long(v) => v.first().copied(),
332 Self::Ifd(v) => v.first().copied(),
333 Self::SShort(v) => v.first().and_then(|n| u32::try_from(*n).ok()),
334 Self::SLong(v) => v.first().and_then(|n| u32::try_from(*n).ok()),
335 _ => None,
336 }
337 }
338
339 /// Returns the first element only, where the type converts.
340 pub fn as_f64(&self) -> Option<f64> {
341 match self {
342 Self::Byte(v) => v.first().map(|n| *n as f64),
343 Self::Short(v) => v.first().map(|n| *n as f64),
344 Self::Long(v) => v.first().map(|n| *n as f64),
345 Self::SByte(v) => v.first().map(|n| *n as f64),
346 Self::SShort(v) => v.first().map(|n| *n as f64),
347 Self::SLong(v) => v.first().map(|n| *n as f64),
348 Self::Float(v) => v.first().map(|n| *n as f64),
349 Self::Double(v) => v.first().copied(),
350 Self::Rational(v) => v.first().and_then(|r| r.to_f64()),
351 Self::SRational(v) => v.first().and_then(|r| r.to_f64()),
352 _ => None,
353 }
354 }
355
356 /// Returns every element, where the type converts.
357 pub fn as_f64_vec(&self) -> Option<Vec<f64>> {
358 match self {
359 Self::Byte(v) => Some(v.iter().map(|n| *n as f64).collect()),
360 Self::Short(v) => Some(v.iter().map(|n| *n as f64).collect()),
361 Self::Long(v) => Some(v.iter().map(|n| *n as f64).collect()),
362 Self::SByte(v) => Some(v.iter().map(|n| *n as f64).collect()),
363 Self::SShort(v) => Some(v.iter().map(|n| *n as f64).collect()),
364 Self::SLong(v) => Some(v.iter().map(|n| *n as f64).collect()),
365 Self::Float(v) => Some(v.iter().map(|n| *n as f64).collect()),
366 Self::Double(v) => Some(v.clone()),
367 Self::Rational(v) => v.iter().map(|r| r.to_f64()).collect(),
368 Self::SRational(v) => v.iter().map(|r| r.to_f64()).collect(),
369 _ => None,
370 }
371 }
372}
373
374#[derive(Clone, Debug, PartialEq)]
375pub struct Field {
376 pub tag: u16,
377 pub ifd: IfdKind,
378 pub value: Value,
379}
380
381/// The fields of every IFD reached, flattened into one list.
382#[derive(Clone, Debug)]
383pub struct Exif {
384 pub order: ByteOrder, // as declared by the TIFF header
385 pub fields: Vec<Field>, // in the order the IFDs were walked
386 pub thumbnail: Option<(u32, u32)>, // IFD1 offset and length within the TIFF block
387}
388
389impl Exif {
390
391 /// Parses the EXIF `APP1` segment of a JPEG byte stream, returning `None` when there is none.
392 pub fn from_jpeg(dat: &[u8]) -> Outcome<Option<Self>> {
393 match res!(Self::find_jpeg_app1(dat)) {
394 Some(payload) => Ok(Some(res!(Self::from_tiff(payload)))),
395 None => Ok(None),
396 }
397 }
398
399 /// Parses a bare TIFF block, the form used by TIFF files and by HEIC `Exif` boxes.
400 pub fn from_tiff(dat: &[u8]) -> Outcome<Self> {
401 if dat.len() < 8 {
402 return Err(err!(
403 "TIFF header: {} bytes available at offset 0, at least 8 are needed for the \
404 byte order mark, magic and first IFD offset.", dat.len();
405 Input, Invalid, TooSmall, Decode));
406 }
407 let order = match &dat[0..2] {
408 b"II" => ByteOrder::Little,
409 b"MM" => ByteOrder::Big,
410 other => return Err(err!(
411 "TIFF header at offset 0: byte order mark {:02x?} is neither II nor MM.", other;
412 Input, Invalid, Decode)),
413 };
414 let magic = order.u16([dat[2], dat[3]]);
415 if magic != 42 {
416 return Err(err!(
417 "TIFF header at offset 2: magic number is {}, expected 42 in {} order.",
418 magic, order;
419 Input, Invalid, Decode));
420 }
421 let first = order.u32([dat[4], dat[5], dat[6], dat[7]]);
422
423 let mut rdr = Reader { dat, order };
424 let mut fields = Vec::new();
425 let mut seen = BTreeSet::new();
426
427 // Walk the IFD0 chain, whose second link is the thumbnail IFD1.
428 let mut next = first;
429 let mut idx = 0usize;
430 while next != 0 {
431 if !seen.insert(next) {
432 return Err(err!(
433 "IFD chain: the pointer at link {} returns to offset {}, which has already \
434 been read, so the chain loops.", idx, next;
435 Input, Invalid, Duplicate, Decode));
436 }
437 if idx >= MAX_IFD_CHAIN {
438 return Err(err!(
439 "IFD chain: more than {} linked IFDs, refusing to follow the pointer to \
440 offset {}.", MAX_IFD_CHAIN, next;
441 Input, Invalid, Excessive, Decode));
442 }
443 let kind = if idx == 0 { IfdKind::Ifd0 } else { IfdKind::Ifd1 };
444 next = res!(rdr.read_ifd(next, kind, &mut fields));
445 idx += 1;
446 }
447
448 // Follow the sub-IFD pointers found in IFD0, then the interoperability pointer in the
449 // EXIF sub-IFD. Each is read once, and a pointer back into a visited IFD is refused.
450 let subs = [
451 (IfdKind::Ifd0, tag::EXIF_IFD_POINTER, IfdKind::Exif),
452 (IfdKind::Ifd0, tag::GPS_IFD_POINTER, IfdKind::Gps),
453 ];
454 for (from, ptr_tag, kind) in subs {
455 if let Some(off) = Self::pointer(&fields, from, ptr_tag) {
456 res!(rdr.read_sub_ifd(off, kind, &mut seen, &mut fields));
457 }
458 }
459 if let Some(off) = Self::pointer(&fields, IfdKind::Exif, tag::INTEROP_IFD_POINTER) {
460 res!(rdr.read_sub_ifd(off, IfdKind::Interop, &mut seen, &mut fields));
461 }
462
463 let thumbnail = match (
464 Self::find(&fields, IfdKind::Ifd1, tag::JPEG_INTERCHANGE_FORMAT),
465 Self::find(&fields, IfdKind::Ifd1, tag::JPEG_INTERCHANGE_LENGTH),
466 ) {
467 (Some(off), Some(len)) => match (off.value.as_u32(), len.value.as_u32()) {
468 (Some(o), Some(l)) => Some((o, l)),
469 _ => None,
470 },
471 _ => None,
472 };
473
474 Ok(Self { order, fields, thumbnail })
475 }
476
477 /// Parses whichever of JPEG or bare TIFF the leading bytes indicate.
478 pub fn from_bytes(dat: &[u8]) -> Outcome<Option<Self>> {
479 if dat.len() >= 2 && dat[0] == 0xFF && dat[1] == 0xD8 {
480 Self::from_jpeg(dat)
481 } else if dat.len() >= 2 && (&dat[0..2] == b"II" || &dat[0..2] == b"MM") {
482 Ok(Some(res!(Self::from_tiff(dat))))
483 } else {
484 Err(err!(
485 "Byte stream at offset 0: leading bytes {:02x?} are neither a JPEG start of \
486 image nor a TIFF byte order mark.",
487 &dat[0..dat.len().min(2)];
488 Input, Invalid, Decode))
489 }
490 }
491
492 pub fn field(&self, ifd: IfdKind, tag: u16) -> Option<&Field> {
493 Self::find(&self.fields, ifd, tag)
494 }
495
496 /// Searches every IFD, in walk order.
497 pub fn any_field(&self, tag: u16) -> Option<&Field> {
498 self.fields.iter().find(|f| f.tag == tag)
499 }
500
501 pub fn ifd(&self, ifd: IfdKind) -> Vec<&Field> {
502 self.fields.iter().filter(|f| f.ifd == ifd).collect()
503 }
504
505 pub fn meta(&self) -> PhotoMeta {
506 PhotoMeta::from_exif(self)
507 }
508
509 fn pointer(fields: &[Field], ifd: IfdKind, tag: u16) -> Option<u32> {
510 Self::find(fields, ifd, tag).and_then(|f| f.value.as_u32())
511 }
512
513 fn find(fields: &[Field], ifd: IfdKind, tag: u16) -> Option<&Field> {
514 fields.iter().find(|f| f.ifd == ifd && f.tag == tag)
515 }
516
517 /// Returns the bytes following the `Exif\0\0` signature, which begin the TIFF header. A
518 /// stream with no such segment yields `None`; a stream whose marker structure is broken
519 /// yields an error naming the offset.
520 pub fn find_jpeg_app1(dat: &[u8]) -> Outcome<Option<&[u8]>> {
521 for seg in res!(JpegSegments::new(dat)) {
522 let seg = res!(seg);
523 if seg.marker == 0xE1 && seg.body.len() > APP1_SIG.len()
524 && &seg.body[0..APP1_SIG.len()] == APP1_SIG
525 {
526 return Ok(Some(&seg.body[APP1_SIG.len()..]));
527 }
528 if seg.marker == 0xDA {
529 break; // Scan data follows; no more metadata segments.
530 }
531 }
532 Ok(None)
533 }
534
535 /// Reads the frame dimensions from a JPEG start of frame marker, as `(width, height)`.
536 ///
537 /// This is the size a decoder will produce, which is not always what the EXIF sub-IFD
538 /// claims, so a photo application wanting the truth should prefer it.
539 pub fn jpeg_dimensions(dat: &[u8]) -> Outcome<Option<(u32, u32)>> {
540 for seg in res!(JpegSegments::new(dat)) {
541 let seg = res!(seg);
542 let is_sof = matches!(seg.marker,
543 0xC0..=0xC3 | 0xC5..=0xC7 | 0xC9..=0xCB | 0xCD..=0xCF);
544 if is_sof {
545 if seg.body.len() < 5 {
546 return Err(err!(
547 "JPEG start of frame marker FF{:02X} at offset {}: body is {} bytes, \
548 at least 5 are needed for the precision and dimensions.",
549 seg.marker, seg.offset, seg.body.len();
550 Input, Invalid, TooSmall, Decode));
551 }
552 let h = u16::from_be_bytes([seg.body[1], seg.body[2]]) as u32;
553 let w = u16::from_be_bytes([seg.body[3], seg.body[4]]) as u32;
554 return Ok(Some((w, h)));
555 }
556 if seg.marker == 0xDA {
557 break;
558 }
559 }
560 Ok(None)
561 }
562}
563
564#[derive(Clone, Copy, Debug)]
565pub struct JpegSegment<'a> {
566 pub marker: u8, // the byte following FF
567 pub offset: usize, // of the FF that introduced the marker
568 pub body: &'a [u8], // excludes the two byte length field itself
569}
570
571/// Iteration stops after the start of scan marker, since entropy coded data follows it and
572/// cannot be walked as segments.
573pub struct JpegSegments<'a> {
574 dat: &'a [u8],
575 pos: usize,
576 done: bool,
577}
578
579impl<'a> JpegSegments<'a> {
580
581 /// Verifies the start of image marker before anything is yielded.
582 pub fn new(dat: &'a [u8]) -> Outcome<Self> {
583 if dat.len() < 2 {
584 return Err(err!(
585 "JPEG stream: {} bytes available at offset 0, at least 2 are needed for the \
586 start of image marker.", dat.len();
587 Input, Invalid, TooSmall, Decode));
588 }
589 if dat[0] != 0xFF || dat[1] != 0xD8 {
590 return Err(err!(
591 "JPEG stream at offset 0: found {:02X}{:02X}, expected the start of image \
592 marker FFD8.", dat[0], dat[1];
593 Input, Invalid, Decode));
594 }
595 Ok(Self { dat, pos: 2, done: false })
596 }
597}
598
599impl<'a> Iterator for JpegSegments<'a> {
600 type Item = Outcome<JpegSegment<'a>>;
601
602 fn next(&mut self) -> Option<Self::Item> {
603 if self.done {
604 return None;
605 }
606 // Skip any fill bytes, which the standard permits before a marker.
607 let mut p = self.pos;
608 while p < self.dat.len() && self.dat[p] == 0xFF {
609 p += 1;
610 }
611 if p == self.pos {
612 // No FF at all, so either the stream ended or the structure is broken.
613 self.done = true;
614 if self.pos >= self.dat.len() {
615 return None;
616 }
617 return Some(Err(err!(
618 "JPEG stream at offset {}: expected a marker introducer FF, found {:02X}.",
619 self.pos, self.dat[self.pos];
620 Input, Invalid, Decode)));
621 }
622 if p >= self.dat.len() {
623 self.done = true;
624 return Some(Err(err!(
625 "JPEG stream at offset {}: the stream ends inside a run of FF fill bytes \
626 with no marker code.", self.pos;
627 Input, Invalid, TooSmall, Decode)));
628 }
629 let marker = self.dat[p];
630 let start = p - 1;
631 p += 1;
632
633 // Markers that stand alone and carry no length or body.
634 if marker == 0x01 || marker == 0xD8 || (0xD0..=0xD7).contains(&marker) {
635 self.pos = p;
636 return Some(Ok(JpegSegment { marker, offset: start, body: &self.dat[p..p] }));
637 }
638 if marker == 0xD9 {
639 self.done = true;
640 return Some(Ok(JpegSegment { marker, offset: start, body: &self.dat[p..p] }));
641 }
642 if p + 2 > self.dat.len() {
643 self.done = true;
644 return Some(Err(err!(
645 "JPEG segment FF{:02X} at offset {}: the stream ends before its two byte \
646 length field.", marker, start;
647 Input, Invalid, TooSmall, Decode)));
648 }
649 let len = u16::from_be_bytes([self.dat[p], self.dat[p + 1]]) as usize;
650 if len < 2 {
651 self.done = true;
652 return Some(Err(err!(
653 "JPEG segment FF{:02X} at offset {}: declared length {} is below the two \
654 bytes the length field itself occupies.", marker, start, len;
655 Input, Invalid, Decode)));
656 }
657 let body_start = p + 2;
658 let body_end = p + len;
659 if body_end > self.dat.len() {
660 self.done = true;
661 return Some(Err(err!(
662 "JPEG segment FF{:02X} at offset {}: body runs to offset {} but the stream is \
663 only {} bytes.", marker, start, body_end, self.dat.len();
664 Input, Invalid, TooSmall, Decode)));
665 }
666 self.pos = body_end;
667 if marker == 0xDA {
668 self.done = true; // Entropy coded data follows the scan header.
669 }
670 Some(Ok(JpegSegment { marker, offset: start, body: &self.dat[body_start..body_end] }))
671 }
672}
673
674/// A bounds-checked cursor over the TIFF block.
675struct Reader<'a> {
676 dat: &'a [u8],
677 order: ByteOrder,
678}
679
680impl<'a> Reader<'a> {
681
682 /// The offset is absolute within the TIFF block.
683 fn u16_at(&self, off: usize, what: &str) -> Outcome<u16> {
684 if off + 2 > self.dat.len() {
685 return Err(err!(
686 "{}: two bytes wanted at offset {} but the TIFF block is only {} bytes.",
687 what, off, self.dat.len();
688 Input, Invalid, TooSmall, Decode));
689 }
690 Ok(self.order.u16([self.dat[off], self.dat[off + 1]]))
691 }
692
693 /// The offset is absolute within the TIFF block.
694 fn u32_at(&self, off: usize, what: &str) -> Outcome<u32> {
695 if off + 4 > self.dat.len() {
696 return Err(err!(
697 "{}: four bytes wanted at offset {} but the TIFF block is only {} bytes.",
698 what, off, self.dat.len();
699 Input, Invalid, TooSmall, Decode));
700 }
701 Ok(self.order.u32([
702 self.dat[off],
703 self.dat[off + 1],
704 self.dat[off + 2],
705 self.dat[off + 3],
706 ]))
707 }
708
709 /// Reads a sub-IFD, refusing an offset already visited.
710 fn read_sub_ifd(
711 &mut self,
712 off: u32,
713 kind: IfdKind,
714 seen: &mut BTreeSet<u32>,
715 fields: &mut Vec<Field>,
716 )
717 -> Outcome<()>
718 {
719 if !seen.insert(off) {
720 return Err(err!(
721 "{} pointer: offset {} has already been read as another IFD, so the pointers \
722 loop.", kind, off;
723 Input, Invalid, Duplicate, Decode));
724 }
725 res!(self.read_ifd(off, kind, fields));
726 Ok(())
727 }
728
729 /// Appends the IFD's fields and returns the pointer to the next in the chain.
730 fn read_ifd(
731 &mut self,
732 off: u32,
733 kind: IfdKind,
734 fields: &mut Vec<Field>,
735 )
736 -> Outcome<u32>
737 {
738 let base = off as usize;
739 let count = res!(self.u16_at(base, &fmt!("{} entry count", kind))) as u64;
740 if count > MAX_IFD_ENTRIES {
741 return Err(err!(
742 "{} at offset {}: declares {} entries, above the {} entry ceiling.",
743 kind, base, count, MAX_IFD_ENTRIES;
744 Input, Invalid, Excessive, Decode));
745 }
746 let end = (base as u64) + 2 + count * ENTRY_LEN + 4;
747 if end > self.dat.len() as u64 {
748 return Err(err!(
749 "{} at offset {}: {} entries plus the next pointer run to offset {} but the \
750 TIFF block is only {} bytes.", kind, base, count, end, self.dat.len();
751 Input, Invalid, TooSmall, Decode));
752 }
753 for i in 0..count as usize {
754 let eoff = base + 2 + i * ENTRY_LEN as usize;
755 let field = res!(self.read_entry(eoff, i, kind));
756 fields.push(field);
757 }
758 let noff = base + 2 + count as usize * ENTRY_LEN as usize;
759 let next = res!(self.u32_at(noff, &fmt!("{} next IFD pointer", kind)));
760 Ok(next)
761 }
762
763 /// Reads one twelve byte IFD entry.
764 fn read_entry(&self, eoff: usize, idx: usize, kind: IfdKind) -> Outcome<Field> {
765 if eoff + ENTRY_LEN as usize > self.dat.len() {
766 return Err(err!(
767 "{} entry {}: the twelve byte entry at offset {} extends beyond the {} byte \
768 TIFF block.", kind, idx, eoff, self.dat.len();
769 Input, Invalid, TooSmall, Decode));
770 }
771 let tag = res!(self.u16_at(eoff, &fmt!("{} entry {} tag", kind, idx)));
772 let typ = res!(self.u16_at(eoff + 2, &fmt!("{} entry {} type", kind, idx)));
773 let count = res!(self.u32_at(eoff + 4, &fmt!("{} entry {} count", kind, idx)));
774 let raw = [
775 self.dat[eoff + 8],
776 self.dat[eoff + 9],
777 self.dat[eoff + 10],
778 self.dat[eoff + 11],
779 ];
780
781 let unit = match type_size(typ) {
782 Some(n) => n,
783 None => {
784 // An unrecognised type has no known width, so the payload cannot be located.
785 // Keep the entry with its raw bytes rather than dropping it.
786 return Ok(Field {
787 tag,
788 ifd: kind,
789 value: Value::Unknown { typ, count, raw },
790 });
791 },
792 };
793 let total = (count as u64) * (unit as u64);
794 if total > self.dat.len() as u64 {
795 return Err(err!(
796 "{} entry {} at offset {} (tag 0x{:04X}, type {}): count {} needs {} bytes but \
797 the TIFF block is only {} bytes.",
798 kind, idx, eoff, tag, typ, count, total, self.dat.len();
799 Input, Invalid, Excessive, Decode));
800 }
801 let total = total as usize;
802
803 let body: &[u8] = if total <= 4 {
804 &self.dat[eoff + 8..eoff + 8 + total]
805 } else {
806 let voff = self.order.u32(raw) as usize;
807 let vend = voff + total;
808 if vend > self.dat.len() {
809 return Err(err!(
810 "{} entry {} at offset {} (tag 0x{:04X}, type {}): value block [{}..{}] \
811 extends beyond the {} byte TIFF block.",
812 kind, idx, eoff, tag, typ, voff, vend, self.dat.len();
813 Input, Invalid, TooSmall, Decode));
814 }
815 &self.dat[voff..vend]
816 };
817
818 let value = res!(self.decode(typ, count as usize, body, eoff, idx, kind, tag));
819 Ok(Field { tag, ifd: kind, value })
820 }
821
822 #[allow(clippy::too_many_arguments)]
823 fn decode(
824 &self,
825 typ: u16,
826 count: usize,
827 body: &[u8],
828 eoff: usize,
829 idx: usize,
830 kind: IfdKind,
831 tag: u16,
832 )
833 -> Outcome<Value>
834 {
835 let o = self.order;
836 Ok(match typ {
837 1 => Value::Byte(body.to_vec()),
838 2 => {
839 // ASCII fields are NUL terminated. Some cameras write a string that is not
840 // valid UTF-8, so the conversion is lossy rather than fatal.
841 let cut = body.iter().position(|b| *b == 0).unwrap_or(body.len());
842 Value::Ascii(String::from_utf8_lossy(&body[..cut]).trim_end().to_string())
843 },
844 3 => {
845 let mut v = Vec::with_capacity(count);
846 for i in 0..count {
847 v.push(o.u16([body[i * 2], body[i * 2 + 1]]));
848 }
849 Value::Short(v)
850 },
851 4 => Value::Long(res!(Self::u32s(o, body, count))),
852 5 => {
853 let mut v = Vec::with_capacity(count);
854 for i in 0..count {
855 let n = o.u32([body[i * 8], body[i * 8 + 1], body[i * 8 + 2], body[i * 8 + 3]]);
856 let d = o.u32([body[i * 8 + 4], body[i * 8 + 5], body[i * 8 + 6], body[i * 8 + 7]]);
857 v.push(Rational { num: n, den: d });
858 }
859 Value::Rational(v)
860 },
861 6 => Value::SByte(body.iter().map(|b| *b as i8).collect()),
862 7 => Value::Undefined(body.to_vec()),
863 8 => {
864 let mut v = Vec::with_capacity(count);
865 for i in 0..count {
866 v.push(o.u16([body[i * 2], body[i * 2 + 1]]) as i16);
867 }
868 Value::SShort(v)
869 },
870 9 => Value::SLong(res!(Self::u32s(o, body, count)).into_iter()
871 .map(|n| n as i32).collect()),
872 10 => {
873 let mut v = Vec::with_capacity(count);
874 for i in 0..count {
875 let n = o.u32([body[i * 8], body[i * 8 + 1], body[i * 8 + 2], body[i * 8 + 3]]) as i32;
876 let d = o.u32([body[i * 8 + 4], body[i * 8 + 5], body[i * 8 + 6], body[i * 8 + 7]]) as i32;
877 v.push(SRational { num: n, den: d });
878 }
879 Value::SRational(v)
880 },
881 11 => Value::Float(res!(Self::u32s(o, body, count)).into_iter()
882 .map(f32::from_bits).collect()),
883 12 => {
884 let mut v = Vec::with_capacity(count);
885 for i in 0..count {
886 let lo = o.u32([body[i * 8], body[i * 8 + 1], body[i * 8 + 2], body[i * 8 + 3]]) as u64;
887 let hi = o.u32([body[i * 8 + 4], body[i * 8 + 5], body[i * 8 + 6], body[i * 8 + 7]]) as u64;
888 let bits = match o {
889 ByteOrder::Little => (hi << 32) | lo,
890 ByteOrder::Big => (lo << 32) | hi,
891 };
892 v.push(f64::from_bits(bits));
893 }
894 Value::Double(v)
895 },
896 13 => Value::Ifd(res!(Self::u32s(o, body, count))),
897 _ => return Err(err!(
898 "{} entry {} at offset {} (tag 0x{:04X}): type {} has a known width but no \
899 decoder.", kind, idx, eoff, tag, typ;
900 Bug, Unreachable, Decode)),
901 })
902 }
903
904 fn u32s(o: ByteOrder, body: &[u8], count: usize) -> Outcome<Vec<u32>> {
905 let mut v = Vec::with_capacity(count);
906 for i in 0..count {
907 v.push(o.u32([body[i * 4], body[i * 4 + 1], body[i * 4 + 2], body[i * 4 + 3]]));
908 }
909 Ok(v)
910 }
911}
912
913/// `None` for a type this module does not know, whose payload cannot then be located.
914pub fn type_size(typ: u16) -> Option<usize> {
915 Some(match typ {
916 1 => 1, // BYTE
917 2 => 1, // ASCII
918 3 => 2, // SHORT
919 4 => 4, // LONG
920 5 => 8, // RATIONAL
921 6 => 1, // SBYTE
922 7 => 1, // UNDEFINED
923 8 => 2, // SSHORT
924 9 => 4, // SLONG
925 10 => 8, // SRATIONAL
926 11 => 4, // FLOAT
927 12 => 8, // DOUBLE
928 13 => 4, // IFD
929 _ => return None,
930 })
931}
932
933/// The typed view of the fields a photo application reaches for first.
934///
935/// Every member is optional, since no tag is guaranteed to be present. Values are normalised:
936/// coordinates are signed decimal degrees, altitude is signed metres, and exposure time is
937/// seconds rather than the recorded ratio.
938#[derive(Clone, Debug, Default, PartialEq)]
939pub struct PhotoMeta {
940 pub datetime_original: Option<String>, // DateTimeOriginal, as YYYY:MM:DD HH:MM:SS
941 pub create_date: Option<String>, // CreateDate, when the image was digitised
942 pub modify_date: Option<String>, // ModifyDate from IFD0, the file's last write
943 pub subsec_time_original: Option<String>, // fractional seconds of datetime_original
944 pub subsec_time_digitized: Option<String>, // fractional seconds of create_date
945 pub offset_time_original: Option<String>, // UTC offset of datetime_original, such as +10:00
946 pub make: Option<String>, // camera manufacturer
947 pub model: Option<String>,
948 pub lens_model: Option<String>,
949 pub orientation: Option<u16>, // 1 to 8
950 pub width: Option<u32>, // pixels
951 pub height: Option<u32>, // pixels
952 pub gps_latitude: Option<f64>, // north positive
953 pub gps_longitude: Option<f64>, // east positive
954 pub gps_altitude: Option<f64>, // below sea level negative
955 pub gps_datestamp: Option<String>, // UTC date of the fix, as YYYY:MM:DD
956 pub f_number: Option<f64>,
957 pub exposure_time: Option<f64>,
958 pub iso: Option<u32>, // arithmetic speed
959 pub focal_length: Option<f64>, // millimetres
960 pub focal_length_35mm: Option<u32>, // millimetres, for a 35 mm film frame
961}
962
963impl PhotoMeta {
964
965 pub fn from_exif(exif: &Exif) -> Self {
966 let mut m = Self::default();
967
968 m.make = Self::text(exif, IfdKind::Ifd0, tag::MAKE);
969 m.model = Self::text(exif, IfdKind::Ifd0, tag::MODEL);
970 m.modify_date = Self::text(exif, IfdKind::Ifd0, tag::DATE_TIME);
971 m.orientation = exif.field(IfdKind::Ifd0, tag::ORIENTATION)
972 .and_then(|f| f.value.as_u32())
973 .and_then(|n| u16::try_from(n).ok());
974
975 m.datetime_original = Self::text(exif, IfdKind::Exif, tag::DATE_TIME_ORIGINAL);
976 m.create_date = Self::text(exif, IfdKind::Exif, tag::CREATE_DATE);
977 m.subsec_time_original = Self::text(exif, IfdKind::Exif, tag::SUBSEC_TIME_ORIGINAL)
978 .or_else(|| Self::text(exif, IfdKind::Exif, tag::SUBSEC_TIME));
979 m.subsec_time_digitized = Self::text(exif, IfdKind::Exif, tag::SUBSEC_TIME_DIGITIZED);
980 m.offset_time_original = Self::text(exif, IfdKind::Exif, tag::OFFSET_TIME_ORIGINAL)
981 .or_else(|| Self::text(exif, IfdKind::Exif, tag::OFFSET_TIME));
982 m.lens_model = Self::text(exif, IfdKind::Exif, tag::LENS_MODEL);
983
984 // The EXIF sub-IFD records the dimensions after any in-camera processing, so it is
985 // preferred; IFD0 carries them for a plain TIFF.
986 m.width = exif.field(IfdKind::Exif, tag::EXIF_IMAGE_WIDTH)
987 .and_then(|f| f.value.as_u32())
988 .or_else(|| exif.field(IfdKind::Ifd0, tag::IMAGE_WIDTH)
989 .and_then(|f| f.value.as_u32()));
990 m.height = exif.field(IfdKind::Exif, tag::EXIF_IMAGE_HEIGHT)
991 .and_then(|f| f.value.as_u32())
992 .or_else(|| exif.field(IfdKind::Ifd0, tag::IMAGE_LENGTH)
993 .and_then(|f| f.value.as_u32()));
994
995 m.f_number = Self::number(exif, IfdKind::Exif, tag::F_NUMBER);
996 m.exposure_time = Self::number(exif, IfdKind::Exif, tag::EXPOSURE_TIME);
997 m.focal_length = Self::number(exif, IfdKind::Exif, tag::FOCAL_LENGTH);
998 m.iso = exif.field(IfdKind::Exif, tag::ISO_SPEED_RATINGS)
999 .and_then(|f| f.value.as_u32());
1000 m.focal_length_35mm = exif.field(IfdKind::Exif, tag::FOCAL_LENGTH_35MM)
1001 .and_then(|f| f.value.as_u32());
1002
1003 m.gps_latitude = Self::coord(
1004 exif,
1005 tag::GPS_LATITUDE,
1006 tag::GPS_LATITUDE_REF,
1007 'S',
1008 );
1009 m.gps_longitude = Self::coord(
1010 exif,
1011 tag::GPS_LONGITUDE,
1012 tag::GPS_LONGITUDE_REF,
1013 'W',
1014 );
1015 m.gps_altitude = Self::number(exif, IfdKind::Gps, tag::GPS_ALTITUDE).map(|a| {
1016 let below = exif.field(IfdKind::Gps, tag::GPS_ALTITUDE_REF)
1017 .and_then(|f| f.value.as_u32())
1018 .map(|r| r == 1)
1019 .unwrap_or(false);
1020 if below { -a } else { a }
1021 });
1022 m.gps_datestamp = Self::text(exif, IfdKind::Gps, tag::GPS_DATESTAMP);
1023
1024 m
1025 }
1026
1027 /// Trims, and treats an empty string as absent.
1028 fn text(exif: &Exif, ifd: IfdKind, tag: u16) -> Option<String> {
1029 exif.field(ifd, tag)
1030 .and_then(|f| f.value.as_str())
1031 .map(|s| s.trim().to_string())
1032 .filter(|s| !s.is_empty())
1033 }
1034
1035 fn number(exif: &Exif, ifd: IfdKind, tag: u16) -> Option<f64> {
1036 exif.field(ifd, tag).and_then(|f| f.value.as_f64())
1037 }
1038
1039 /// Converts a degrees, minutes and seconds triple plus a hemisphere into signed degrees.
1040 fn coord(exif: &Exif, val_tag: u16, ref_tag: u16, negative: char) -> Option<f64> {
1041 let parts = match exif.field(IfdKind::Gps, val_tag)
1042 .and_then(|f| f.value.as_f64_vec())
1043 {
1044 Some(p) if !p.is_empty() => p,
1045 _ => return None,
1046 };
1047 let deg = parts.first().copied().unwrap_or(0.0);
1048 let min = parts.get(1).copied().unwrap_or(0.0);
1049 let sec = parts.get(2).copied().unwrap_or(0.0);
1050 let mut d = deg + min / 60.0 + sec / 3600.0;
1051 if !d.is_finite() {
1052 return None;
1053 }
1054 let hemi = exif.field(IfdKind::Gps, ref_tag)
1055 .and_then(|f| f.value.as_str())
1056 .and_then(|s| s.chars().next())
1057 .map(|c| c.to_ascii_uppercase());
1058 if hemi == Some(negative) {
1059 d = -d;
1060 }
1061 Some(d)
1062 }
1063}