1use serde::{Deserialize, Serialize};
2
3use crate::{
4 formats::tiff::{IFD, Rational, Result, SRational, Value},
5 lens::LensDescription,
6 tags::{ExifGpsTag, ExifTag},
7};
8
9use std::convert::TryInto;
10
11#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
15pub struct Exif {
16 pub orientation: Option<u16>,
17 pub copyright: Option<String>,
18 pub artist: Option<String>,
19 pub lens_spec: Option<[Rational; 4]>,
20 pub exposure_time: Option<Rational>,
21 pub fnumber: Option<Rational>,
22 pub aperture_value: Option<Rational>,
23 pub brightness_value: Option<SRational>,
24 pub iso_speed_ratings: Option<u16>,
25 pub iso_speed: Option<u32>,
26 pub recommended_exposure_index: Option<u32>,
27 pub sensitivity_type: Option<u16>,
28 pub exposure_bias: Option<SRational>,
29 pub date_time_original: Option<String>,
30 pub create_date: Option<String>,
31 pub modify_date: Option<String>,
32 pub exposure_program: Option<u16>,
33 pub timezone_offset: Option<Vec<i16>>,
34 pub offset_time: Option<String>,
35 pub offset_time_original: Option<String>,
36 pub offset_time_digitized: Option<String>,
37 pub sub_sec_time: Option<String>,
38 pub sub_sec_time_original: Option<String>,
39 pub sub_sec_time_digitized: Option<String>,
40 pub shutter_speed_value: Option<SRational>,
41 pub max_aperture_value: Option<Rational>,
42 pub subject_distance: Option<Rational>,
43 pub metering_mode: Option<u16>,
44 pub light_source: Option<u16>,
45 pub flash: Option<u16>,
46 pub focal_length: Option<Rational>,
47 pub image_number: Option<u32>,
48 pub color_space: Option<u16>,
49 pub flash_energy: Option<Rational>,
50 pub exposure_mode: Option<u16>,
51 pub white_balance: Option<u16>,
52 pub scene_capture_type: Option<u16>,
53 pub subject_distance_range: Option<u16>,
54 pub owner_name: Option<String>,
55 pub serial_number: Option<String>,
56 pub lens_serial_number: Option<String>,
57 pub lens_make: Option<String>,
58 pub lens_model: Option<String>,
59 pub gps: Option<ExifGPS>,
60 pub user_comment: Option<String>,
61 }
63
64#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
65pub struct ExifGPS {
66 pub gps_version_id: Option<[u8; 4]>,
67 pub gps_latitude_ref: Option<String>,
68 pub gps_latitude: Option<[Rational; 3]>,
69 pub gps_longitude_ref: Option<String>,
70 pub gps_longitude: Option<[Rational; 3]>,
71 pub gps_altitude_ref: Option<u8>,
72 pub gps_altitude: Option<Rational>,
73 pub gps_timestamp: Option<[Rational; 3]>,
74 pub gps_satellites: Option<String>,
75 pub gps_status: Option<String>,
76 pub gps_measure_mode: Option<String>,
77 pub gps_dop: Option<Rational>,
78 pub gps_speed_ref: Option<String>,
79 pub gps_speed: Option<Rational>,
80 pub gps_track_ref: Option<String>,
81 pub gps_track: Option<Rational>,
82 pub gps_img_direction_ref: Option<String>,
83 pub gps_img_direction: Option<Rational>,
84 pub gps_map_datum: Option<String>,
85 pub gps_dest_latitude_ref: Option<String>,
86 pub gps_dest_latitude: Option<[Rational; 3]>,
87 pub gps_dest_longitude_ref: Option<String>,
88 pub gps_dest_longitude: Option<[Rational; 3]>,
89 pub gps_dest_bearing_ref: Option<String>,
90 pub gps_dest_bearing: Option<Rational>,
91 pub gps_dest_distance_ref: Option<String>,
92 pub gps_dest_distance: Option<Rational>,
93 pub gps_processing_method: Option<Vec<u8>>,
94 pub gps_area_information: Option<Vec<u8>>,
95 pub gps_date_stamp: Option<String>,
96 pub gps_differential: Option<u16>,
97 pub gps_h_positioning_error: Option<Rational>,
98}
99
100impl Exif {
101 pub fn new(root_or_exif: &IFD) -> Result<Self> {
104 let mut ins = Self::default();
105 ins.extend_from_ifd(root_or_exif)?;
106 if let Some(exif_ifd) = root_or_exif.get_sub_ifd(ExifTag::ExifOffset) {
107 ins.extend_from_ifd(exif_ifd)?;
108 }
109 if let Some(gpsinfo_ifd) = root_or_exif.get_sub_ifd(ExifTag::GPSInfo) {
111 ins.extend_from_gps_ifd(gpsinfo_ifd)?;
112 }
113 Ok(ins)
114 }
115
116 pub fn extend_from_ifd(&mut self, ifd: &IFD) -> Result<()> {
119 let trim = |a: &String| -> String { a.trim().into() };
120 for (tag, entry) in ifd.entries().iter() {
121 if let Ok(tag) = ExifTag::try_from(*tag) {
123 match (tag, &entry.value) {
124 (ExifTag::Orientation, Value::Short(data)) => self.orientation = data.get(0).cloned(),
125 (ExifTag::Copyright, Value::Ascii(data)) => self.copyright = data.strings().get(0).map(trim),
126 (ExifTag::Artist, Value::Ascii(data)) => self.artist = data.strings().get(0).map(trim),
127 (ExifTag::ExposureTime, Value::Rational(data)) => self.exposure_time = data.get(0).cloned(),
128 (ExifTag::FNumber, Value::Rational(data)) => self.fnumber = data.get(0).cloned(),
129 (ExifTag::BrightnessValue, Value::SRational(data)) => self.brightness_value = data.get(0).cloned(),
130 (ExifTag::ApertureValue, Value::Rational(data)) => self.aperture_value = data.get(0).cloned(),
131 (ExifTag::ISOSpeedRatings, Value::Short(data)) => self.iso_speed_ratings = data.get(0).cloned(),
132 (ExifTag::ISOSpeed, Value::Long(data)) => self.iso_speed = data.get(0).cloned(),
133 (ExifTag::RecommendedExposureIndex, Value::Long(data)) => self.recommended_exposure_index = data.get(0).cloned(),
134 (ExifTag::SensitivityType, Value::Short(data)) => self.sensitivity_type = data.get(0).cloned(),
135 (ExifTag::ExposureBiasValue, Value::SRational(data)) => self.exposure_bias = data.get(0).cloned(),
136 (ExifTag::DateTimeOriginal, Value::Ascii(data)) => self.date_time_original = data.strings().get(0).cloned(),
137 (ExifTag::CreateDate, Value::Ascii(data)) => self.create_date = data.strings().get(0).cloned(),
138 (ExifTag::ModifyDate, Value::Ascii(data)) => self.modify_date = data.strings().get(0).cloned(),
139 (ExifTag::ExposureProgram, Value::Short(data)) => self.exposure_program = data.get(0).cloned(),
140 (ExifTag::TimeZoneOffset, Value::SShort(data)) => self.timezone_offset = Some(data.clone()),
141 (ExifTag::OffsetTime, Value::Ascii(data)) => self.offset_time = data.strings().get(0).cloned(),
142 (ExifTag::OffsetTimeOriginal, Value::Ascii(data)) => self.offset_time_original = data.strings().get(0).cloned(),
143 (ExifTag::OffsetTimeDigitized, Value::Ascii(data)) => self.offset_time_digitized = data.strings().get(0).cloned(),
144 (ExifTag::SubSecTime, Value::Ascii(data)) => self.sub_sec_time = data.strings().get(0).cloned(),
145 (ExifTag::SubSecTimeOriginal, Value::Ascii(data)) => self.sub_sec_time_original = data.strings().get(0).cloned(),
146 (ExifTag::SubSecTimeDigitized, Value::Ascii(data)) => self.sub_sec_time_digitized = data.strings().get(0).cloned(),
147 (ExifTag::ShutterSpeedValue, Value::SRational(data)) => self.shutter_speed_value = data.get(0).cloned(),
148 (ExifTag::MaxApertureValue, Value::Rational(data)) => self.max_aperture_value = data.get(0).cloned(),
149 (ExifTag::SubjectDistance, Value::Rational(data)) => self.subject_distance = data.get(0).cloned(),
150 (ExifTag::MeteringMode, Value::Short(data)) => self.metering_mode = data.get(0).cloned(),
151 (ExifTag::LightSource, Value::Short(data)) => self.light_source = data.get(0).cloned(),
152 (ExifTag::Flash, Value::Short(data)) => self.flash = data.get(0).cloned(),
153 (ExifTag::FocalLength, Value::Rational(data)) => self.focal_length = data.get(0).cloned(),
154 (ExifTag::ImageNumber, Value::Long(data)) => self.image_number = data.get(0).cloned(),
155 (ExifTag::ColorSpace, Value::Short(data)) => self.color_space = data.get(0).cloned(),
156 (ExifTag::FlashEnergy, Value::Rational(data)) => self.flash_energy = data.get(0).cloned(),
157 (ExifTag::ExposureMode, Value::Short(data)) => self.exposure_mode = data.get(0).cloned(),
158 (ExifTag::WhiteBalance, Value::Short(data)) => self.white_balance = data.get(0).cloned(),
159 (ExifTag::SceneCaptureType, Value::Short(data)) => self.scene_capture_type = data.get(0).cloned(),
160 (ExifTag::SubjectDistanceRange, Value::Short(data)) => self.subject_distance_range = data.get(0).cloned(),
161 (ExifTag::OwnerName, Value::Ascii(data)) => self.owner_name = data.strings().get(0).map(trim),
162 (ExifTag::SerialNumber, Value::Ascii(data)) => self.serial_number = data.strings().get(0).map(trim),
163 (ExifTag::LensSerialNumber, Value::Ascii(data)) => self.lens_serial_number = data.strings().get(0).map(trim),
164 (ExifTag::UserComment, Value::Ascii(data)) => self.user_comment = data.strings().get(0).map(trim),
165 (tag, _value) => {
167 log::debug!("Ignoring EXIF tag: {:?}", tag);
168 }
169 }
170 }
171 }
172 Ok(())
173 }
174
175 pub fn extend_from_gps_ifd(&mut self, ifd: &IFD) -> Result<()> {
178 for (tag, entry) in ifd.entries().iter() {
179 if let Ok(tag) = ExifGpsTag::try_from(*tag) {
180 if self.gps.is_none() {
182 self.gps = Some(ExifGPS::default());
183 }
184 if let Some(gps) = &mut self.gps {
185 match (tag, &entry.value) {
186 (ExifGpsTag::GPSVersionID, Value::Byte(data)) => gps.gps_version_id = data.clone().try_into().ok(),
187 (ExifGpsTag::GPSLatitudeRef, Value::Ascii(data)) => gps.gps_latitude_ref = data.strings().get(0).cloned(),
188 (ExifGpsTag::GPSLatitude, Value::Rational(data)) => gps.gps_latitude = data.clone().try_into().ok(),
189 (ExifGpsTag::GPSLongitudeRef, Value::Ascii(data)) => gps.gps_longitude_ref = data.strings().get(0).cloned(),
190 (ExifGpsTag::GPSLongitude, Value::Rational(data)) => gps.gps_longitude = data.clone().try_into().ok(),
191 (ExifGpsTag::GPSAltitudeRef, Value::Byte(data)) => gps.gps_altitude_ref = data.get(0).cloned(),
192 (ExifGpsTag::GPSAltitude, Value::Rational(data)) => gps.gps_altitude = data.get(0).cloned(),
193 (ExifGpsTag::GPSTimeStamp, Value::Rational(data)) => gps.gps_timestamp = data.clone().try_into().ok(),
194 (ExifGpsTag::GPSSatellites, Value::Ascii(data)) => gps.gps_satellites = data.strings().get(0).cloned(),
195 (ExifGpsTag::GPSStatus, Value::Ascii(data)) => gps.gps_status = data.strings().get(0).cloned(),
196 (ExifGpsTag::GPSMeasureMode, Value::Ascii(data)) => gps.gps_measure_mode = data.strings().get(0).cloned(),
197 (ExifGpsTag::GPSDOP, Value::Rational(data)) => gps.gps_dop = data.get(0).cloned(),
198 (ExifGpsTag::GPSSpeedRef, Value::Ascii(data)) => gps.gps_speed_ref = data.strings().get(0).cloned(),
199 (ExifGpsTag::GPSSpeed, Value::Rational(data)) => gps.gps_speed = data.get(0).cloned(),
200 (ExifGpsTag::GPSTrackRef, Value::Ascii(data)) => gps.gps_track_ref = data.strings().get(0).cloned(),
201 (ExifGpsTag::GPSTrack, Value::Rational(data)) => gps.gps_track = data.get(0).cloned(),
202 (ExifGpsTag::GPSImgDirectionRef, Value::Ascii(data)) => gps.gps_img_direction_ref = data.strings().get(0).cloned(),
203 (ExifGpsTag::GPSImgDirection, Value::Rational(data)) => gps.gps_img_direction = data.get(0).cloned(),
204 (ExifGpsTag::GPSMapDatum, Value::Ascii(data)) => gps.gps_map_datum = data.strings().get(0).cloned(),
205 (ExifGpsTag::GPSDestLatitudeRef, Value::Ascii(data)) => gps.gps_dest_latitude_ref = data.strings().get(0).cloned(),
206 (ExifGpsTag::GPSDestLatitude, Value::Rational(data)) => gps.gps_dest_latitude = data.clone().try_into().ok(),
207 (ExifGpsTag::GPSDestLongitudeRef, Value::Ascii(data)) => gps.gps_dest_longitude_ref = data.strings().get(0).cloned(),
208 (ExifGpsTag::GPSDestLongitude, Value::Rational(data)) => gps.gps_dest_longitude = data.clone().try_into().ok(),
209 (ExifGpsTag::GPSDestBearingRef, Value::Ascii(data)) => gps.gps_dest_bearing_ref = data.strings().get(0).cloned(),
210 (ExifGpsTag::GPSDestBearing, Value::Rational(data)) => gps.gps_dest_bearing = data.get(0).cloned(),
211 (ExifGpsTag::GPSDestDistanceRef, Value::Ascii(data)) => gps.gps_dest_distance_ref = data.strings().get(0).cloned(),
212 (ExifGpsTag::GPSDestDistance, Value::Rational(data)) => gps.gps_dest_distance = data.get(0).cloned(),
213 (ExifGpsTag::GPSProcessingMethod, Value::Undefined(data)) => gps.gps_processing_method = Some(data.clone()),
214 (ExifGpsTag::GPSAreaInformation, Value::Undefined(data)) => gps.gps_area_information = Some(data.clone()),
215 (ExifGpsTag::GPSDateStamp, Value::Ascii(data)) => gps.gps_date_stamp = data.strings().get(0).cloned(),
216 (ExifGpsTag::GPSDifferential, Value::Short(data)) => gps.gps_differential = data.get(0).cloned(),
217 (ExifGpsTag::GPSHPositioningError, Value::Rational(data)) => gps.gps_h_positioning_error = data.get(0).cloned(),
218 (tag, _value) => {
219 log::debug!("Ignoring EXIF GPS tag: {:?}", tag);
220 }
221 }
222 }
223 }
224 }
225 Ok(())
226 }
227
228 pub(crate) fn extend_from_lens(&mut self, lens: &LensDescription) {
229 let lens_info: [Rational; 4] = [lens.focal_range[0], lens.focal_range[1], lens.aperture_range[0], lens.aperture_range[1]];
230 self.lens_spec = Some(lens_info);
231 self.lens_make = Some(lens.lens_make.clone());
232 self.lens_model = Some(lens.lens_model.clone());
233 }
234}