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