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