rexif/
types.rs

1use super::ifdformat::tag_value_eq;
2use super::rational::{IRational, URational};
3use std::borrow::Cow;
4use std::{fmt, io};
5
6/// The value of the Exif header.
7pub const EXIF_HEADER: &[u8] = &[b'E', b'x', b'i', b'f', 0x00, 0x00];
8const INTEL_TIFF_HEADER: &[u8] = &[b'I', b'I', 0x2a, 0x00];
9const MOTOROLA_TIFF_HEADER: &[u8] = &[b'M', b'M', 0x00, 0x2a];
10const DATA_WIDTH: usize = 4;
11
12/// Top-level structure that contains all parsed metadata inside an image
13#[derive(Debug, PartialEq)]
14pub struct ExifData {
15    /// MIME type of the parsed image. It may be "image/jpeg", "image/tiff", or empty if unrecognized.
16    pub mime: &'static str,
17    /// Collection of EXIF entries found in the image
18    pub entries: Vec<ExifEntry>,
19    /// If `true`, this uses little-endian byte ordering for the raw bytes. Otherwise, it uses big-endian ordering.
20    pub le: bool,
21}
22
23impl ExifData {
24    #[must_use]
25    pub fn new(mime: &'static str, entries: Vec<ExifEntry>, le: bool) -> Self {
26        Self { mime, entries, le }
27    }
28}
29
30impl ExifData {
31    /// Serialize the metadata entries, and return the result.
32    ///
33    /// *Note*: this serializes the metadata according to its original endianness (specified
34    /// through the `le` attribute).
35    pub fn serialize(&self) -> Result<Vec<u8>, ExifError> {
36        // Select the right TIFF header based on the endianness.
37        let tiff_header = if self.le {
38            INTEL_TIFF_HEADER
39        } else {
40            MOTOROLA_TIFF_HEADER
41        };
42
43        // The result buffer.
44        let mut serialized = vec![];
45
46        // Generate the TIFF header.
47        serialized.extend(tiff_header);
48
49        // The offset to IFD-0. IFD-0 follows immediately after the TIFF header.
50        // The offset is a 4-byte value - serialize it to bytes:
51        let offset = if self.le {
52            (tiff_header.len() as u32 + std::mem::size_of::<u32>() as u32).to_le_bytes()
53        } else {
54            (tiff_header.len() as u32 + std::mem::size_of::<u32>() as u32).to_be_bytes()
55        };
56        serialized.extend(&offset);
57
58        let mut ifd0 = vec![];
59        let mut ifd1 = vec![];
60        let mut exif = vec![];
61        let mut gps = vec![];
62
63        for e in &self.entries {
64            match e.kind {
65                IfdKind::Ifd0 => ifd0.push(e),
66                IfdKind::Ifd1 => ifd1.push(e),
67                IfdKind::Exif => exif.push(e),
68                IfdKind::Gps => gps.push(e),
69                _ => {
70                    // XXX Silently ignore Makernote and Interoperability IFDs
71                },
72            }
73        }
74
75        // IFD-1 contains the thumbnail. For now, the parser discards IFD-1, so its serialization
76        // has not yet been implemented.
77        assert!(ifd1.is_empty());
78
79        // Serialize the number of directory entries in this IFD.
80        if self.le {
81            serialized.extend(&(ifd0.len() as u16).to_le_bytes());
82        } else {
83            serialized.extend(&(ifd0.len() as u16).to_be_bytes());
84        };
85
86        // The position of the data in an Exif Offset entry.
87        let mut exif_ifd_pointer = None;
88
89        // The position of the data in an GPS Offset entry.
90        let mut gps_ifd_pointer = None;
91
92        // The positions which contain offsets pointing to values in the data section of IFD-0.
93        // These offsets will be filled out (patched) later.
94        let mut data_patches = vec![];
95        for entry in ifd0 {
96            entry.ifd.serialize(&mut serialized, &mut data_patches)?;
97
98            // If IFD-0 points to an Exif/GPS sub-IFD, the offset of the sub-IFD must be serialized
99            // inside IFD-0. Subtract `DATA_WIDTH` from the length, because the pointer to the
100            // sub-IFD will be written in the data section of the previously serialized entry
101            // (which is of type ExifOffset/GPSOffset precisely because its data section contains
102            // an offset to a sub-IFD).
103            if entry.tag == ExifTag::ExifOffset {
104                exif_ifd_pointer = Some(serialized.len() - DATA_WIDTH);
105            }
106            if entry.tag == ExifTag::GPSOffset {
107                gps_ifd_pointer = Some(serialized.len() - DATA_WIDTH);
108            }
109        }
110
111        if ifd1.is_empty() {
112            serialized.extend(&[0, 0, 0, 0]);
113        } else {
114            // Otherwise, serialize the pointer to IFD-1 (which is just the offset of IFD-1 in the
115            // file).
116            unimplemented!("IFD-1");
117        }
118
119        // Patch the offsets serialized above.
120        for patch in &data_patches {
121            // The position of the data pointed to by the IFD entries serialized above.
122            let bytes = if self.le {
123                (serialized.len() as u32).to_le_bytes()
124            } else {
125                (serialized.len() as u32).to_be_bytes()
126            };
127
128            serialized.extend(patch.data);
129            for (place, byte) in serialized.iter_mut().skip(patch.offset_pos as usize).zip(bytes.iter()) {
130                *place = *byte;
131            }
132        }
133
134        if !exif.is_empty() {
135            self.serialize_ifd(&mut serialized, exif, exif_ifd_pointer)?;
136        }
137
138        if !gps.is_empty() {
139            self.serialize_ifd(&mut serialized, gps, gps_ifd_pointer)?;
140        }
141
142        // TODO Makernote, Interoperability IFD, Thumbnail image
143
144        Ok(if self.mime == "image/jpeg" {
145            [EXIF_HEADER, &serialized].concat()
146        } else {
147            serialized
148        })
149    }
150
151    /// Serialize GPS/Exif IFD entries.
152    fn serialize_ifd(
153        &self,
154        serialized: &mut Vec<u8>,
155        entries: Vec<&ExifEntry>,
156        pos: Option<usize>,
157    ) -> Result<(), ExifError> {
158        let bytes = if self.le {
159            (serialized.len() as u32).to_le_bytes()
160        } else {
161            (serialized.len() as u32).to_be_bytes()
162        };
163
164        // Serialize the number of directory entries in this IFD
165        if self.le {
166            serialized.extend(&(entries.len() as u16).to_le_bytes());
167        } else {
168            serialized.extend(&(entries.len() as u16).to_be_bytes());
169        }
170
171        // Write the offset of this IFD in IFD-0.
172        let pos = pos.ok_or(ExifError::MissingExifOffset)?;
173        for (place, byte) in serialized.iter_mut().skip(pos).zip(bytes.iter()) {
174            *place = *byte;
175        }
176
177        let mut data_patches = vec![];
178
179        for entry in entries {
180            entry.ifd.serialize(serialized, &mut data_patches)?;
181        }
182
183        serialized.extend(&[0, 0, 0, 0]);
184        for patch in &data_patches {
185            // The position of the data pointed to by the IFD entries serialized above.
186            let bytes = if self.le {
187                (serialized.len() as u32).to_le_bytes()
188            } else {
189                (serialized.len() as u32).to_be_bytes()
190            };
191            serialized.extend(patch.data);
192            for (place, byte) in serialized.iter_mut().skip(patch.offset_pos as usize).zip(bytes.iter()) {
193                *place = *byte;
194            }
195        }
196        Ok(())
197    }
198}
199
200pub(super) struct Patch<'a> {
201    /// The position where to write the offset in the file where the data will be located.
202    offset_pos: u32,
203    /// The data to add to the data section of the current IFD.
204    data: &'a [u8],
205}
206
207impl Patch<'_> {
208    #[must_use]
209    pub const fn new(offset_pos: u32, data: &[u8]) -> Patch {
210        Patch {
211            offset_pos,
212            data,
213        }
214    }
215}
216
217/// Possible fatal errors that may happen when an image is parsed.
218#[derive(Debug)]
219pub enum ExifError {
220    IoError(io::Error),
221    FileTypeUnknown,
222    JpegWithoutExif(String),
223    TiffTruncated,
224    TiffBadPreamble(String),
225    IfdTruncated,
226    ExifIfdTruncated(String),
227    ExifIfdEntryNotFound,
228    UnsupportedNamespace,
229    MissingExifOffset,
230}
231
232/// Structure that represents a parsed IFD entry of a TIFF image
233#[derive(Clone, Debug)]
234pub struct IfdEntry {
235    /// Namespace of the entry. Standard is a tag found in normal TIFF IFD structure,
236    /// other namespaces are entries found e.g. within `MarkerNote` blobs that are
237    /// manufacturer-specific.
238    pub namespace: Namespace,
239    /// IFD tag value, may or not be an EXIF tag
240    pub tag: u16,
241    /// IFD data format
242    pub format: IfdFormat,
243    /// Number of items, each one in the data format specified by format
244    pub count: u32,
245    /// Raw data as a vector of bytes. Length is sizeof(format) * count.
246    /// Depending on its size, it came from different parts of the image file.
247    pub data: Vec<u8>,
248    /// Raw data contained within the IFD structure. If count * sizeof(format) >= 4,
249    /// this item contains the offset where the actual data can be found
250    pub ifd_data: Vec<u8>,
251    /// Raw data contained outside of the IFD structure and pointed by `ifd_data`,
252    /// if data would not fit within the IFD structure
253    pub ext_data: Vec<u8>,
254    /// If true, integer and offset formats must be parsed from raw data as little-endian.
255    /// If false, integer and offset formats must be parsed from raw data as big-endian.
256    ///
257    /// It is important to have 'endianess' per IFD entry, because some manufacturer-specific
258    /// entries may have fixed endianess (regardeless of TIFF container's general endianess).
259    pub le: bool,
260}
261
262// Do not include `ifd_data` in the comparison, as it may in fact contain the offset to the data,
263// and two Exif entries may contain the same data, but at different offsets. In that case, the
264// entries should still be considered equal.
265impl PartialEq for IfdEntry {
266    fn eq(&self, other: &Self) -> bool {
267        let data_eq = if self.in_ifd() && !self.tag == ExifTag::ExifOffset as u16 && !self.tag == ExifTag::GPSOffset as u16 {
268            self.data == other.data && self.ifd_data == other.ifd_data && self.ext_data == other.ext_data
269        } else {
270            true
271        };
272
273        self.namespace == other.namespace && self.tag == other.tag && self.count == other.count &&
274            data_eq && self.le == other.le
275    }
276}
277
278impl IfdEntry {
279    pub(crate) fn serialize<'a>(
280        &'a self,
281        serialized: &mut Vec<u8>,
282        data_patches: &mut Vec<Patch<'a>>,
283    ) -> Result<(), ExifError> {
284        // Serialize the entry
285        if self.namespace != Namespace::Standard {
286            return Err(ExifError::UnsupportedNamespace);
287        }
288
289        // Serialize the tag (2 bytes)
290        if self.le {
291            serialized.extend(&self.tag.to_le_bytes());
292        } else {
293            serialized.extend(&self.tag.to_be_bytes());
294        };
295
296        // Serialize the data format (2 bytes)
297        if self.le {
298            serialized.extend(&(self.format as u16).to_le_bytes());
299        } else {
300            serialized.extend(&(self.format as u16).to_be_bytes());
301        }
302
303        // Serialize the number of components (4 bytes)
304        if self.le {
305            serialized.extend(&self.count.to_le_bytes());
306        } else {
307            serialized.extend(&self.count.to_be_bytes());
308        }
309
310        // Serialize the data value/offset to data value (4 bytes)
311        if self.in_ifd() {
312            serialized.extend(&self.data);
313        } else {
314            data_patches.push(Patch::new(serialized.len() as u32, &self.data));
315            // 4 bytes that will be filled out later
316            serialized.extend(&[0, 0, 0, 0]);
317        }
318        Ok(())
319    }
320}
321
322/// Enumeration that represent EXIF tag namespaces. Namespaces exist to
323/// accomodate future parsing of the manufacturer-specific tags embedded within
324/// the `MarkerNote` tag.
325#[derive(Copy, Clone, Debug, PartialEq)]
326pub enum Namespace {
327    Standard = 0x0000,
328    Nikon = 0x0001,
329    Canon = 0x0002,
330}
331
332/// Enumeration that represents recognized EXIF tags found in TIFF IFDs.
333///
334/// Items can be cast to u32 in order to get the namespace (most significant word)
335/// and tag code (least significant word). The tag code matches the Exif, or the
336/// Makernote standard, depending on the namespace that the tag belongs to.
337///
338/// On the other hand, the namespace code is arbitrary, it only matches
339/// the `Namespace` enumeration. The namespace is 0 for standard Exif tags.
340/// The non-standard namespaces exist to accomodate future parsing of the
341/// `MarkerNote` tag, that contains embedded manufacturer-specific tags.
342#[derive(Copy, Clone, Debug, PartialEq, Hash)]
343#[repr(u32)]
344pub enum ExifTag {
345    /// Tag not recognized are partially parsed. The client may still try to interpret
346    /// the tag by reading into the `IfdFormat` structure.
347    UnknownToMe = 0x0000_ffff,
348    ImageDescription = 0x0000_010e,
349    Make = 0x0000_010f,
350    Model = 0x0000_0110,
351    Orientation = 0x0000_0112,
352    XResolution = 0x0000_011a,
353    YResolution = 0x0000_011b,
354    ResolutionUnit = 0x0000_0128,
355    Software = 0x0000_0131,
356    DateTime = 0x0000_0132,
357    HostComputer = 0x0000_013c,
358    WhitePoint = 0x0000_013e,
359    PrimaryChromaticities = 0x0000_013f,
360    YCbCrCoefficients = 0x0000_0211,
361    ReferenceBlackWhite = 0x0000_0214,
362    Copyright = 0x0000_8298,
363    ExifOffset = 0x0000_8769,
364    GPSOffset = 0x0000_8825,
365
366    ExposureTime = 0x0000_829a,
367    FNumber = 0x0000_829d,
368    ExposureProgram = 0x0000_8822,
369    SpectralSensitivity = 0x0000_8824,
370    ISOSpeedRatings = 0x0000_8827,
371    OECF = 0x0000_8828,
372    SensitivityType = 0x0000_8830,
373    ExifVersion = 0x0000_9000,
374    DateTimeOriginal = 0x0000_9003,
375    DateTimeDigitized = 0x0000_9004,
376    ShutterSpeedValue = 0x0000_9201,
377    ApertureValue = 0x0000_9202,
378    BrightnessValue = 0x0000_9203,
379    ExposureBiasValue = 0x0000_9204,
380    MaxApertureValue = 0x0000_9205,
381    SubjectDistance = 0x0000_9206,
382    MeteringMode = 0x0000_9207,
383    LightSource = 0x0000_9208,
384    Flash = 0x0000_9209,
385    FocalLength = 0x0000_920a,
386    SubjectArea = 0x0000_9214,
387    MakerNote = 0x0000_927c,
388    UserComment = 0x0000_9286,
389    FlashPixVersion = 0x0000_a000,
390    ColorSpace = 0x0000_a001,
391    RelatedSoundFile = 0x0000_a004,
392    FlashEnergy = 0x0000_a20b,
393    FocalPlaneXResolution = 0x0000_a20e,
394    FocalPlaneYResolution = 0x0000_a20f,
395    FocalPlaneResolutionUnit = 0x0000_a210,
396    SubjectLocation = 0x0000_a214,
397    ExposureIndex = 0x0000_a215,
398    SensingMethod = 0x0000_a217,
399    FileSource = 0x0000_a300,
400    SceneType = 0x0000_a301,
401    CFAPattern = 0x0000_a302,
402    CustomRendered = 0x0000_a401,
403    ExposureMode = 0x0000_a402,
404    WhiteBalanceMode = 0x0000_a403,
405    DigitalZoomRatio = 0x0000_a404,
406    FocalLengthIn35mmFilm = 0x0000_a405,
407    SceneCaptureType = 0x0000_a406,
408    GainControl = 0x0000_a407,
409    Contrast = 0x0000_a408,
410    Saturation = 0x0000_a409,
411    Sharpness = 0x0000_a40a,
412    DeviceSettingDescription = 0x0000_a40b,
413    SubjectDistanceRange = 0x0000_a40c,
414    ImageUniqueID = 0x0000_a420,
415    LensSpecification = 0x0000_a432,
416    LensMake = 0x0000_a433,
417    LensModel = 0x0000_a434,
418    Gamma = 0xa500,
419
420    GPSVersionID = 0x00000,
421    GPSLatitudeRef = 0x00001,
422    GPSLatitude = 0x00002,
423    GPSLongitudeRef = 0x00003,
424    GPSLongitude = 0x00004,
425    GPSAltitudeRef = 0x00005,
426    GPSAltitude = 0x00006,
427    GPSTimeStamp = 0x00007,
428    GPSSatellites = 0x00008,
429    GPSStatus = 0x00009,
430    GPSMeasureMode = 0x0000a,
431    GPSDOP = 0x0000b,
432    GPSSpeedRef = 0x0000c,
433    GPSSpeed = 0x0000d,
434    GPSTrackRef = 0x0000e,
435    GPSTrack = 0x0000f,
436    GPSImgDirectionRef = 0x0000_0010,
437    GPSImgDirection = 0x0000_0011,
438    GPSMapDatum = 0x0000_0012,
439    GPSDestLatitudeRef = 0x0000_0013,
440    GPSDestLatitude = 0x0000_0014,
441    GPSDestLongitudeRef = 0x0000_0015,
442    GPSDestLongitude = 0x0000_0016,
443    GPSDestBearingRef = 0x0000_0017,
444    GPSDestBearing = 0x0000_0018,
445    GPSDestDistanceRef = 0x0000_0019,
446    GPSDestDistance = 0x0000_001a,
447    GPSProcessingMethod = 0x0000_001b,
448    GPSAreaInformation = 0x0000_001c,
449    GPSDateStamp = 0x0000_001d,
450    GPSDifferential = 0x0000_001e,
451}
452
453impl Eq for ExifTag {}
454
455impl fmt::Display for ExifTag {
456    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
457        write!(
458            f,
459            "{}",
460            match *self {
461                ExifTag::ImageDescription => "Image Description",
462                ExifTag::Make => "Manufacturer",
463                ExifTag::HostComputer => "Host computer",
464                ExifTag::Model => "Model",
465                ExifTag::Orientation => "Orientation",
466                ExifTag::XResolution => "X Resolution",
467                ExifTag::YResolution => "Y Resolution",
468                ExifTag::ResolutionUnit => "Resolution Unit",
469                ExifTag::Software => "Software",
470                ExifTag::DateTime => "Image date",
471                ExifTag::WhitePoint => "White Point",
472                ExifTag::PrimaryChromaticities => "Primary Chromaticities",
473                ExifTag::YCbCrCoefficients => "YCbCr Coefficients",
474                ExifTag::ReferenceBlackWhite => "Reference Black/White",
475                ExifTag::Copyright => "Copyright",
476                ExifTag::ExifOffset => "This image has an Exif SubIFD",
477                ExifTag::GPSOffset => "This image has a GPS SubIFD",
478                ExifTag::ExposureTime => "Exposure time",
479                ExifTag::SensitivityType => "Sensitivity type",
480                ExifTag::FNumber => "Aperture",
481                ExifTag::ExposureProgram => "Exposure program",
482                ExifTag::SpectralSensitivity => "Spectral sensitivity",
483                ExifTag::ISOSpeedRatings => "ISO speed ratings",
484                ExifTag::OECF => "OECF",
485                ExifTag::ExifVersion => "Exif version",
486                ExifTag::DateTimeOriginal => "Date of original image",
487                ExifTag::DateTimeDigitized => "Date of image digitalization",
488                ExifTag::ShutterSpeedValue => "Shutter speed",
489                ExifTag::ApertureValue => "Aperture value",
490                ExifTag::BrightnessValue => "Brightness value",
491                ExifTag::ExposureBiasValue => "Exposure bias value",
492                ExifTag::MaxApertureValue => "Maximum aperture value",
493                ExifTag::SubjectDistance => "Subject distance",
494                ExifTag::MeteringMode => "Meteting mode",
495                ExifTag::LightSource => "Light source",
496                ExifTag::Flash => "Flash",
497                ExifTag::FocalLength => "Focal length",
498                ExifTag::SubjectArea => "Subject area",
499                ExifTag::MakerNote => "Maker note",
500                ExifTag::UserComment => "User comment",
501                ExifTag::FlashPixVersion => "Flashpix version",
502                ExifTag::ColorSpace => "Color space",
503                ExifTag::FlashEnergy => "Flash energy",
504                ExifTag::RelatedSoundFile => "Related sound file",
505                ExifTag::FocalPlaneXResolution => "Focal plane X resolution",
506                ExifTag::FocalPlaneYResolution => "Focal plane Y resolution",
507                ExifTag::FocalPlaneResolutionUnit => "Focal plane resolution unit",
508                ExifTag::SubjectLocation => "Subject location",
509                ExifTag::ExposureIndex => "Exposure index",
510                ExifTag::SensingMethod => "Sensing method",
511                ExifTag::FileSource => "File source",
512                ExifTag::SceneType => "Scene type",
513                ExifTag::CFAPattern => "CFA Pattern",
514                ExifTag::CustomRendered => "Custom rendered",
515                ExifTag::ExposureMode => "Exposure mode",
516                ExifTag::WhiteBalanceMode => "White balance mode",
517                ExifTag::DigitalZoomRatio => "Digital zoom ratio",
518                ExifTag::FocalLengthIn35mmFilm => "Equivalent focal length in 35mm",
519                ExifTag::SceneCaptureType => "Scene capture type",
520                ExifTag::GainControl => "Gain control",
521                ExifTag::Contrast => "Contrast",
522                ExifTag::Saturation => "Saturation",
523                ExifTag::Sharpness => "Sharpness",
524                ExifTag::LensSpecification => "Lens specification",
525                ExifTag::LensMake => "Lens manufacturer",
526                ExifTag::LensModel => "Lens model",
527                ExifTag::Gamma => "Gamma",
528                ExifTag::DeviceSettingDescription => "Device setting description",
529                ExifTag::SubjectDistanceRange => "Subject distance range",
530                ExifTag::ImageUniqueID => "Image unique ID",
531                ExifTag::GPSVersionID => "GPS version ID",
532                ExifTag::GPSLatitudeRef => "GPS latitude ref",
533                ExifTag::GPSLatitude => "GPS latitude",
534                ExifTag::GPSLongitudeRef => "GPS longitude ref",
535                ExifTag::GPSLongitude => "GPS longitude",
536                ExifTag::GPSAltitudeRef => "GPS altitude ref",
537                ExifTag::GPSAltitude => "GPS altitude",
538                ExifTag::GPSTimeStamp => "GPS timestamp",
539                ExifTag::GPSSatellites => "GPS satellites",
540                ExifTag::GPSStatus => "GPS status",
541                ExifTag::GPSMeasureMode => "GPS measure mode",
542                ExifTag::GPSDOP => "GPS Data Degree of Precision (DOP)",
543                ExifTag::GPSSpeedRef => "GPS speed ref",
544                ExifTag::GPSSpeed => "GPS speed",
545                ExifTag::GPSTrackRef => "GPS track ref",
546                ExifTag::GPSTrack => "GPS track",
547                ExifTag::GPSImgDirectionRef => "GPS image direction ref",
548                ExifTag::GPSImgDirection => "GPS image direction",
549                ExifTag::GPSMapDatum => "GPS map datum",
550                ExifTag::GPSDestLatitudeRef => "GPS destination latitude ref",
551                ExifTag::GPSDestLatitude => "GPS destination latitude",
552                ExifTag::GPSDestLongitudeRef => "GPS destination longitude ref",
553                ExifTag::GPSDestLongitude => "GPS destination longitude",
554                ExifTag::GPSDestBearingRef => "GPS destination bearing ref",
555                ExifTag::GPSDestBearing => "GPS destination bearing",
556                ExifTag::GPSDestDistanceRef => "GPS destination distance ref",
557                ExifTag::GPSDestDistance => "GPS destination distance",
558                ExifTag::GPSProcessingMethod => "GPS processing method",
559                ExifTag::GPSAreaInformation => "GPS area information",
560                ExifTag::GPSDateStamp => "GPS date stamp",
561                ExifTag::GPSDifferential => "GPS differential",
562                ExifTag::UnknownToMe => "Unknown to this library, or manufacturer-specific",
563            }
564        )
565    }
566}
567
568/// Enumeration that represents the possible data formats of an IFD entry.
569///
570/// Any enumeration item can be cast to u16 to get the low-level format code
571/// as defined by the TIFF format.
572#[derive(Copy, Clone, Debug, PartialEq)]
573pub enum IfdFormat {
574    Unknown = 0,
575    U8 = 1,
576    Ascii = 2,
577    U16 = 3,
578    U32 = 4,
579    URational = 5,
580    I8 = 6,
581    Undefined = 7, // u8
582    I16 = 8,
583    I32 = 9,
584    IRational = 10,
585    F32 = 11,
586    F64 = 12,
587}
588
589/// Structure that represents a parsed EXIF tag.
590#[derive(Clone, Debug)]
591pub struct ExifEntry {
592    /// Namespace of the tag. If Standard (0x0000), it is an EXIF tag defined in the
593    /// official standard. Other namespaces accomodate manufacturer-specific tags that
594    /// may be embedded in `MarkerNote` blob tag.
595    pub namespace: Namespace,
596    /// Low-level IFD entry that contains the EXIF tag. The client may look into this
597    /// structure to get tag's raw data, or to parse the tag herself if `tag` is `UnknownToMe`.
598    pub ifd: IfdEntry,
599    /// EXIF tag type as an enumeration. If `UnknownToMe`, the crate did not know the
600    /// tag in detail, and parsing will be incomplete. The client may read into
601    /// `ifd` to discover more about the unparsed tag.
602    pub tag: ExifTag,
603    /// EXIF tag value as an enumeration. Behaves as a "variant" value
604    pub value: TagValue,
605    /// Unit of the value, if applicable. If tag is `UnknownToMe`, unit will be empty.
606    /// If the tag has been parsed and it is indeed unitless, it will be `"none"`.
607    ///
608    /// Note that
609    /// unit refers to the contents of `value`, not to the readable string. For example,
610    /// a GPS latitude is a triplet of rational values, so unit is D/M/S, even though
611    /// `value_more_readable` contains a single string with all three parts
612    /// combined.
613    pub unit: Cow<'static, str>,
614    /// Human-readable and "pretty" version of `value`.
615    /// Enumerations and tuples are interpreted and combined. If `value`
616    /// has a unit, it is also added.
617    /// If tag is `UnknownToMe`, this member contains tag ID.
618    pub value_more_readable: Cow<'static, str>,
619    pub kind: IfdKind,
620}
621
622impl PartialEq for ExifEntry {
623    fn eq(&self, other: &Self) -> bool {
624        // If the ExifEntry is an ExifOffset or a GPSOffset, the value it contains is an offset.
625        // Two entries can be equal even if they do not point to the same offset.
626        let value_eq = match self.tag {
627            ExifTag::ExifOffset | ExifTag::GPSOffset => true,
628            _ => {
629                self.value_more_readable == other.value_more_readable && tag_value_eq(&self.value, &other.value)
630            },
631        };
632
633        self.namespace == other.namespace && self.ifd == other.ifd && self.tag == other.tag &&
634            self.unit == other.unit && self.kind == other.kind && value_eq
635    }
636}
637
638/// Tag value enumeration. It works as a variant type. Each value is
639/// actually a vector because many EXIF tags are collections of values.
640/// Exif tags with single values are represented as single-item vectors.
641#[derive(Clone, Debug, PartialEq)]
642pub enum TagValue {
643    /// Array of unsigned byte integers
644    U8(Vec<u8>),
645    /// ASCII string. (The standard specifies 7-bit ASCII, but this parser accepts UTF-8 strings.)
646    Ascii(String),
647    U16(Vec<u16>),
648    U32(Vec<u32>),
649    /// Array of `URational` structures (tuples with integer numerator and denominator)
650    URational(Vec<URational>),
651    I8(Vec<i8>),
652    /// Array of bytes with opaque internal structure. Used by manufacturer-specific
653    /// tags, SIG-specific tags, tags that contain Unicode (UCS-2) or Japanese (JIS)
654    /// strings (i.e. strings that are not 7-bit-clean), tags that contain
655    /// dissimilar or variant types, etc.
656    ///
657    /// This item has a "little endian"
658    /// boolean parameter that reports the whole TIFF's endianness.
659    /// Any sort of internal structure that is sensitive to endianess
660    /// should be interpreted accordignly to this parameter (true=LE, false=BE).
661    Undefined(Vec<u8>, bool),
662    I16(Vec<i16>),
663    I32(Vec<i32>),
664    /// Array of `IRational` structures (tuples with signed integer numerator and denominator)
665    IRational(Vec<IRational>),
666    /// Array of IEEE 754 floating-points
667    F32(Vec<f32>),
668    /// Array of IEEE 754 floating-points
669    F64(Vec<f64>),
670    /// Array of bytes with unknown internal structure.
671    /// This is different from `Undefined` because `Undefined` is actually a specified
672    /// format, while `Unknown` is an unexpected format type. A tag of `Unknown` format
673    /// is most likely a corrupted tag.
674    ///
675    /// This variant has a "little endian"
676    /// boolean parameter that reports the whole TIFF's endianness.
677    /// Any sort of internal structure that is sensitive to endianess
678    /// should be interpreted accordignly to this parameter (true=LE, false=BE).
679    Unknown(Vec<u8>, bool),
680    /// Type that could not be parsed due to some sort of error (e.g. buffer too
681    /// short for the count and type size). Variant contains raw data, LE/BE,
682    /// format (as u16) and count.
683    Invalid(Vec<u8>, bool, u16, u32),
684}
685
686impl TagValue {
687    /// Get value as an integer
688    /// Out of bounds indexes and invalid types return `None`
689    pub fn to_i64(&self, index: usize) -> Option<i64> {
690        match *self {
691            TagValue::U8(ref v) => v.get(index).copied().map(From::from),
692            TagValue::U16(ref v) => v.get(index).copied().map(From::from),
693            TagValue::U32(ref v) => v.get(index).copied().map(From::from),
694            TagValue::I8(ref v) => v.get(index).copied().map(From::from),
695            TagValue::I16(ref v) => v.get(index).copied().map(From::from),
696            TagValue::I32(ref v) => v.get(index).copied().map(From::from),
697            _ => None,
698        }
699    }
700
701    /// Get value as a floating-point number
702    /// Out of bounds indexes and invalid types return `None`
703    pub fn to_f64(&self, index: usize) -> Option<f64> {
704        match *self {
705            TagValue::U8(ref v) => v.get(index).copied().map(From::from),
706            TagValue::U16(ref v) => v.get(index).copied().map(From::from),
707            TagValue::U32(ref v) => v.get(index).copied().map(From::from),
708            TagValue::I8(ref v) => v.get(index).copied().map(From::from),
709            TagValue::I16(ref v) => v.get(index).copied().map(From::from),
710            TagValue::I32(ref v) => v.get(index).copied().map(From::from),
711            TagValue::F32(ref v) => v.get(index).copied().map(From::from),
712            TagValue::F64(ref v) => v.get(index).copied().map(From::from),
713            TagValue::IRational(ref v) => v.get(index).copied().map(|v| v.value()),
714            TagValue::URational(ref v) => v.get(index).copied().map(|v| v.value()),
715            _ => None,
716        }
717    }
718}
719
720/// Type returned by image file parsing
721pub type ExifResult = Result<ExifData, ExifError>;
722
723/// Type resturned by lower-level parsing functions
724pub type ExifEntryResult = Result<Vec<ExifEntry>, ExifError>;
725
726#[derive(Copy, Clone, Debug, PartialEq)]
727pub enum IfdKind {
728    Ifd0,
729    Ifd1,
730    Exif,
731    Gps,
732    Makernote,
733    Interoperability,
734}