diff --git a/src/exif.rs b/src/exif.rs index b013714..9b35d46 100644 --- a/src/exif.rs +++ b/src/exif.rs @@ -9,403 +9,898 @@ type ReadableFn = fn(u16, &TagValue) -> Option>; /// and make sure that EXIF tags have the right data types /// /// Returns (tag, unit, format, `min_count`, `max_count`, `more_readable`) -pub(crate) fn tag_to_exif(f: u16) -> (ExifTag, &'static str, IfdFormat, i32, i32, ReadableFn) -{ +pub(crate) fn tag_to_exif(f: u16) -> (ExifTag, &'static str, IfdFormat, i32, i32, ReadableFn) { match f { - 0x010e => - (ExifTag::ImageDescription, "none", IfdFormat::Ascii, - -1i32, -1i32, strpass), - - 0x010f => - (ExifTag::Make, "none", IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x013c => - (ExifTag::HostComputer, "none", IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x0110 => - (ExifTag::Model, "none", IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x0112 => - (ExifTag::Orientation, "none", IfdFormat::U16, 1, 1, orientation), - - 0x011a => - (ExifTag::XResolution, "pixels per res unit", - IfdFormat::URational, 1, 1, rational_value), - - 0x011b => - (ExifTag::YResolution, "pixels per res unit", - IfdFormat::URational, 1, 1, rational_value), - - 0x0128 => - (ExifTag::ResolutionUnit, "none", IfdFormat::U16, 1, 1, resolution_unit), - - 0x0131 => - (ExifTag::Software, "none", IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x0132 => - (ExifTag::DateTime, "none", IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x013e => - (ExifTag::WhitePoint, "CIE 1931 coordinates", - IfdFormat::URational, 2, 2, rational_values), - - 0x013f => - (ExifTag::PrimaryChromaticities, "CIE 1931 coordinates", - IfdFormat::URational, 6, 6, rational_values), - - 0x0211 => - (ExifTag::YCbCrCoefficients, "none", - IfdFormat::URational, 3, 3, rational_values), - - 0x0214 => - (ExifTag::ReferenceBlackWhite, "RGB or YCbCr", - IfdFormat::URational, 6, 6, rational_values), - - 0x8298 => - (ExifTag::Copyright, "none", IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x8769 => - (ExifTag::ExifOffset, "byte offset", - IfdFormat::U32, 1, 1, strpass), - - 0x8825 => - (ExifTag::GPSOffset, "byte offset", - IfdFormat::U32, 1, 1, strpass), - - 0x829a => - (ExifTag::ExposureTime, "s", - IfdFormat::URational, 1, 1, exposure_time), - - 0x829d => - (ExifTag::FNumber, "f-number", - IfdFormat::URational, 1, 1, f_number), - - 0x8822 => - (ExifTag::ExposureProgram, "none", - IfdFormat::U16, 1, 1, exposure_program), - - 0x8824 => - (ExifTag::SpectralSensitivity, "ASTM string", - IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x8830 => - (ExifTag::SensitivityType, "none", - IfdFormat::U16, 1, 1, sensitivity_type), - - 0x8827 => - (ExifTag::ISOSpeedRatings, "ISO", - IfdFormat::U16, 1, 3, iso_speeds), - - 0x8828 => - (ExifTag::OECF, "none", - IfdFormat::Undefined, -1i32, -1i32, undefined_as_blob), - - 0x9000 => - (ExifTag::ExifVersion, "none", - IfdFormat::Undefined, -1i32, -1i32, undefined_as_ascii), - - 0x9003 => - (ExifTag::DateTimeOriginal, "none", - IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x9004 => - (ExifTag::DateTimeDigitized, "none", - IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x9201 => - (ExifTag::ShutterSpeedValue, "APEX", - IfdFormat::IRational, 1, 1, apex_tv), - - 0x9202 => - (ExifTag::ApertureValue, "APEX", - IfdFormat::URational, 1, 1, apex_av), - - 0x9203 => - (ExifTag::BrightnessValue, "APEX", - IfdFormat::IRational, 1, 1, apex_brightness), - - 0x9204 => - (ExifTag::ExposureBiasValue, "APEX", - IfdFormat::IRational, 1, 1, apex_ev), - - 0x9205 => - (ExifTag::MaxApertureValue, - "APEX", IfdFormat::URational, 1, 1, apex_av), - - 0x9206 => - (ExifTag::SubjectDistance, "m", - IfdFormat::URational, 1, 1, meters), - - 0x9207 => - (ExifTag::MeteringMode, "none", - IfdFormat::U16, 1, 1, metering_mode), - - 0x9208 => - (ExifTag::LightSource, "none", - IfdFormat::U16, 1, 1, light_source), - - 0x9209 => (ExifTag::Flash, "none", - IfdFormat::U16, 1, 2, flash), - - 0x920a => - (ExifTag::FocalLength, "mm", - IfdFormat::URational, 1, 1, focal_length), - - 0x9214 => - (ExifTag::SubjectArea, "px", - IfdFormat::U16, 2, 4, subject_area), - - 0x927c => - (ExifTag::MakerNote, "none", - IfdFormat::Undefined, -1i32, -1i32, undefined_as_blob), - - 0x9286 => - (ExifTag::UserComment, "none", - IfdFormat::Undefined, -1i32, -1i32, undefined_as_encoded_string), - - 0xa000 => - (ExifTag::FlashPixVersion, "none", - IfdFormat::Undefined, -1i32, -1i32, undefined_as_ascii), - - 0xa001 => - (ExifTag::ColorSpace, "none", - IfdFormat::U16, 1, 1, color_space), - - 0xa004 => - (ExifTag::RelatedSoundFile, "none", - IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0xa20b => (ExifTag::FlashEnergy, "BCPS", - IfdFormat::URational, 1, 1, flash_energy), - - 0xa20e => - (ExifTag::FocalPlaneXResolution, "@FocalPlaneResolutionUnit", - IfdFormat::URational, 1, 1, rational_value), - - 0xa20f => - (ExifTag::FocalPlaneYResolution, "@FocalPlaneResolutionUnit", - IfdFormat::URational, 1, 1, rational_value), - - 0xa210 => - (ExifTag::FocalPlaneResolutionUnit, "none", - IfdFormat::U16, 1, 1, resolution_unit), - - 0xa214 => - (ExifTag::SubjectLocation, "X,Y", - IfdFormat::U16, 2, 2, subject_location), + 0x010e => ( + ExifTag::ImageDescription, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x010f => ( + ExifTag::Make, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x013c => ( + ExifTag::HostComputer, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x0110 => ( + ExifTag::Model, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x0112 => ( + ExifTag::Orientation, + "none", + IfdFormat::U16, + 1, + 1, + orientation, + ), + + 0x011a => ( + ExifTag::XResolution, + "pixels per res unit", + IfdFormat::URational, + 1, + 1, + rational_value, + ), + + 0x011b => ( + ExifTag::YResolution, + "pixels per res unit", + IfdFormat::URational, + 1, + 1, + rational_value, + ), + + 0x0128 => ( + ExifTag::ResolutionUnit, + "none", + IfdFormat::U16, + 1, + 1, + resolution_unit, + ), + + 0x0131 => ( + ExifTag::Software, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x0132 => ( + ExifTag::DateTime, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x013e => ( + ExifTag::WhitePoint, + "CIE 1931 coordinates", + IfdFormat::URational, + 2, + 2, + rational_values, + ), + + 0x013f => ( + ExifTag::PrimaryChromaticities, + "CIE 1931 coordinates", + IfdFormat::URational, + 6, + 6, + rational_values, + ), + + 0x0211 => ( + ExifTag::YCbCrCoefficients, + "none", + IfdFormat::URational, + 3, + 3, + rational_values, + ), + + 0x0214 => ( + ExifTag::ReferenceBlackWhite, + "RGB or YCbCr", + IfdFormat::URational, + 6, + 6, + rational_values, + ), + + 0x8298 => ( + ExifTag::Copyright, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x8769 => ( + ExifTag::ExifOffset, + "byte offset", + IfdFormat::U32, + 1, + 1, + strpass, + ), + + 0x8825 => ( + ExifTag::GPSOffset, + "byte offset", + IfdFormat::U32, + 1, + 1, + strpass, + ), + + 0x829a => ( + ExifTag::ExposureTime, + "s", + IfdFormat::URational, + 1, + 1, + exposure_time, + ), + + 0x829d => ( + ExifTag::FNumber, + "f-number", + IfdFormat::URational, + 1, + 1, + f_number, + ), + + 0x8822 => ( + ExifTag::ExposureProgram, + "none", + IfdFormat::U16, + 1, + 1, + exposure_program, + ), + + 0x8824 => ( + ExifTag::SpectralSensitivity, + "ASTM string", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x8830 => ( + ExifTag::SensitivityType, + "none", + IfdFormat::U16, + 1, + 1, + sensitivity_type, + ), + + 0x8827 => ( + ExifTag::ISOSpeedRatings, + "ISO", + IfdFormat::U16, + 1, + 3, + iso_speeds, + ), + + 0x8828 => ( + ExifTag::OECF, + "none", + IfdFormat::Undefined, + -1i32, + -1i32, + undefined_as_blob, + ), + + 0x9000 => ( + ExifTag::ExifVersion, + "none", + IfdFormat::Undefined, + -1i32, + -1i32, + undefined_as_ascii, + ), + + 0x9003 => ( + ExifTag::DateTimeOriginal, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x9004 => ( + ExifTag::DateTimeDigitized, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x9201 => ( + ExifTag::ShutterSpeedValue, + "APEX", + IfdFormat::IRational, + 1, + 1, + apex_tv, + ), + + 0x9202 => ( + ExifTag::ApertureValue, + "APEX", + IfdFormat::URational, + 1, + 1, + apex_av, + ), + + 0x9203 => ( + ExifTag::BrightnessValue, + "APEX", + IfdFormat::IRational, + 1, + 1, + apex_brightness, + ), + + 0x9204 => ( + ExifTag::ExposureBiasValue, + "APEX", + IfdFormat::IRational, + 1, + 1, + apex_ev, + ), + + 0x9205 => ( + ExifTag::MaxApertureValue, + "APEX", + IfdFormat::URational, + 1, + 1, + apex_av, + ), + + 0x9206 => ( + ExifTag::SubjectDistance, + "m", + IfdFormat::URational, + 1, + 1, + meters, + ), + + 0x9207 => ( + ExifTag::MeteringMode, + "none", + IfdFormat::U16, + 1, + 1, + metering_mode, + ), + + 0x9208 => ( + ExifTag::LightSource, + "none", + IfdFormat::U16, + 1, + 1, + light_source, + ), + + 0x9209 => (ExifTag::Flash, "none", IfdFormat::U16, 1, 2, flash), + + 0x920a => ( + ExifTag::FocalLength, + "mm", + IfdFormat::URational, + 1, + 1, + focal_length, + ), + + 0x9214 => ( + ExifTag::SubjectArea, + "px", + IfdFormat::U16, + 2, + 4, + subject_area, + ), + + 0x927c => ( + ExifTag::MakerNote, + "none", + IfdFormat::Undefined, + -1i32, + -1i32, + undefined_as_blob, + ), + + 0x9286 => ( + ExifTag::UserComment, + "none", + IfdFormat::Undefined, + -1i32, + -1i32, + undefined_as_encoded_string, + ), + + 0xa000 => ( + ExifTag::FlashPixVersion, + "none", + IfdFormat::Undefined, + -1i32, + -1i32, + undefined_as_ascii, + ), + + 0xa001 => ( + ExifTag::ColorSpace, + "none", + IfdFormat::U16, + 1, + 1, + color_space, + ), + + 0xa004 => ( + ExifTag::RelatedSoundFile, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0xa20b => ( + ExifTag::FlashEnergy, + "BCPS", + IfdFormat::URational, + 1, + 1, + flash_energy, + ), + + 0xa20e => ( + ExifTag::FocalPlaneXResolution, + "@FocalPlaneResolutionUnit", + IfdFormat::URational, + 1, + 1, + rational_value, + ), + + 0xa20f => ( + ExifTag::FocalPlaneYResolution, + "@FocalPlaneResolutionUnit", + IfdFormat::URational, + 1, + 1, + rational_value, + ), + + 0xa210 => ( + ExifTag::FocalPlaneResolutionUnit, + "none", + IfdFormat::U16, + 1, + 1, + resolution_unit, + ), + + 0xa214 => ( + ExifTag::SubjectLocation, + "X,Y", + IfdFormat::U16, + 2, + 2, + subject_location, + ), // TODO check if rational as decimal value is the best for this one - 0xa215 => - (ExifTag::ExposureIndex, "EI", - IfdFormat::URational, 1, 1, rational_value), - - 0xa217 => - (ExifTag::SensingMethod, "none", - IfdFormat::U16, 1, 1, sensing_method), - - 0xa300 => - (ExifTag::FileSource, "none", - IfdFormat::Undefined, 1, 1, file_source), - - 0xa301 => - (ExifTag::SceneType, "none", - IfdFormat::Undefined, 1, 1, scene_type), - - 0xa302 => - (ExifTag::CFAPattern, "none", - IfdFormat::Undefined, -1i32, -1i32, undefined_as_u8), - - 0xa401 => - (ExifTag::CustomRendered, "none", - IfdFormat::U16, 1, 1, custom_rendered), - - 0xa402 => - (ExifTag::ExposureMode, "none", - IfdFormat::U16, 1, 1, exposure_mode), - - 0xa403 => - (ExifTag::WhiteBalanceMode, "none", - IfdFormat::U16, 1, 1, white_balance_mode), - - 0xa404 => - (ExifTag::DigitalZoomRatio, "none", - IfdFormat::URational, 1, 1, rational_value), - - 0xa405 => - (ExifTag::FocalLengthIn35mmFilm, "mm", - IfdFormat::U16, 1, 1, focal_length_35), - - 0xa406 => - (ExifTag::SceneCaptureType, "none", - IfdFormat::U16, 1, 1, scene_capture_type), - - 0xa407 => - (ExifTag::GainControl, "none", - IfdFormat::U16, 1, 1, gain_control), - - 0xa408 => - (ExifTag::Contrast, "none", - IfdFormat::U16, 1, 1, contrast), - - 0xa409 => - (ExifTag::Saturation, "none", - IfdFormat::U16, 1, 1, saturation), - - 0xa40a => - (ExifTag::Sharpness, "none", - IfdFormat::U16, 1, 1, sharpness), - - 0xa432 => - (ExifTag::LensSpecification, "none", - IfdFormat::URational, 4, 4, lens_spec), - - 0xa433 => - (ExifTag::LensMake, "none", - IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0xa434 => - (ExifTag::LensModel, "none", - IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0xa500 => - (ExifTag::Gamma, "none", - IfdFormat::URational, 1, 1, rational_value), + 0xa215 => ( + ExifTag::ExposureIndex, + "EI", + IfdFormat::URational, + 1, + 1, + rational_value, + ), + + 0xa217 => ( + ExifTag::SensingMethod, + "none", + IfdFormat::U16, + 1, + 1, + sensing_method, + ), + + 0xa300 => ( + ExifTag::FileSource, + "none", + IfdFormat::Undefined, + 1, + 1, + file_source, + ), + + 0xa301 => ( + ExifTag::SceneType, + "none", + IfdFormat::Undefined, + 1, + 1, + scene_type, + ), + + 0xa302 => ( + ExifTag::CFAPattern, + "none", + IfdFormat::Undefined, + -1i32, + -1i32, + undefined_as_u8, + ), + + 0xa401 => ( + ExifTag::CustomRendered, + "none", + IfdFormat::U16, + 1, + 1, + custom_rendered, + ), + + 0xa402 => ( + ExifTag::ExposureMode, + "none", + IfdFormat::U16, + 1, + 1, + exposure_mode, + ), + + 0xa403 => ( + ExifTag::WhiteBalanceMode, + "none", + IfdFormat::U16, + 1, + 1, + white_balance_mode, + ), + + 0xa404 => ( + ExifTag::DigitalZoomRatio, + "none", + IfdFormat::URational, + 1, + 1, + rational_value, + ), + + 0xa405 => ( + ExifTag::FocalLengthIn35mmFilm, + "mm", + IfdFormat::U16, + 1, + 1, + focal_length_35, + ), + + 0xa406 => ( + ExifTag::SceneCaptureType, + "none", + IfdFormat::U16, + 1, + 1, + scene_capture_type, + ), + + 0xa407 => ( + ExifTag::GainControl, + "none", + IfdFormat::U16, + 1, + 1, + gain_control, + ), + + 0xa408 => (ExifTag::Contrast, "none", IfdFormat::U16, 1, 1, contrast), + + 0xa409 => ( + ExifTag::Saturation, + "none", + IfdFormat::U16, + 1, + 1, + saturation, + ), + + 0xa40a => (ExifTag::Sharpness, "none", IfdFormat::U16, 1, 1, sharpness), + + 0xa432 => ( + ExifTag::LensSpecification, + "none", + IfdFormat::URational, + 4, + 4, + lens_spec, + ), + + 0xa433 => ( + ExifTag::LensMake, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0xa434 => ( + ExifTag::LensModel, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0xa500 => ( + ExifTag::Gamma, + "none", + IfdFormat::URational, + 1, + 1, + rational_value, + ), // collaborate if you have any idea how to interpret this - 0xa40b => - (ExifTag::DeviceSettingDescription, "none", - IfdFormat::Undefined, -1i32, -1i32, undefined_as_blob), - - 0xa40c => - (ExifTag::SubjectDistanceRange, "none", - IfdFormat::U16, 1, 1, subject_distance_range), - - 0xa420 => - (ExifTag::ImageUniqueID, "none", - IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x0 => - (ExifTag::GPSVersionID, "none", - IfdFormat::U8, 4, 4, strpass), - - 0x1 => - (ExifTag::GPSLatitudeRef, "none", - IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x2 => - (ExifTag::GPSLatitude, "D/M/S", - IfdFormat::URational, 3, 3, dms), - - 0x3 => - (ExifTag::GPSLongitudeRef, "none", - IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x4 => - (ExifTag::GPSLongitude, "D/M/S", - IfdFormat::URational, 3, 3, dms), - - 0x5 => - (ExifTag::GPSAltitudeRef, "none", - IfdFormat::U8, 1, 1, gps_alt_ref), - - 0x6 => - (ExifTag::GPSAltitude, "m", - IfdFormat::URational, 1, 1, meters), - - 0x7 => - (ExifTag::GPSTimeStamp, "UTC time", - IfdFormat::URational, 3, 3, gpstimestamp), - - 0x8 => (ExifTag::GPSSatellites, "none", - IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x9 => (ExifTag::GPSStatus, "none", - IfdFormat::Ascii, -1i32, -1i32, gpsstatus), - - 0xa => (ExifTag::GPSMeasureMode, "none", - IfdFormat::Ascii, -1i32, -1i32, gpsmeasuremode), - - 0xb => - (ExifTag::GPSDOP, "none", - IfdFormat::URational, 1, 1, rational_value), - - 0xc => - (ExifTag::GPSSpeedRef, "none", - IfdFormat::Ascii, -1i32, -1i32, gpsspeedref), - - 0xd => - (ExifTag::GPSSpeed, "@GPSSpeedRef", - IfdFormat::URational, 1, 1, gpsspeed), - - 0xe => - (ExifTag::GPSTrackRef, "none", - IfdFormat::Ascii, -1i32, -1i32, gpsbearingref), - - 0xf => - (ExifTag::GPSTrack, "deg", - IfdFormat::URational, 1, 1, gpsbearing), - - 0x10 => - (ExifTag::GPSImgDirectionRef, "none", - IfdFormat::Ascii, -1i32, -1i32, gpsbearingref), - - 0x11 => - (ExifTag::GPSImgDirection, "deg", - IfdFormat::URational, 1, 1, gpsbearing), - - 0x12 => - (ExifTag::GPSMapDatum, "none", - IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x13 => - (ExifTag::GPSDestLatitudeRef, "none", - IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x14 => - (ExifTag::GPSDestLatitude, "D/M/S", - IfdFormat::URational, 3, 3, dms), - - 0x15 => - (ExifTag::GPSDestLongitudeRef, "none", - IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x16 => - (ExifTag::GPSDestLongitude, "D/M/S", - IfdFormat::URational, 3, 3, dms), - - 0x17 => - (ExifTag::GPSDestBearingRef, "none", - IfdFormat::Ascii, -1i32, -1i32, gpsbearingref), - - 0x18 => - (ExifTag::GPSDestBearing, "deg", - IfdFormat::URational, 1, 1, gpsbearing), - - 0x19 => - (ExifTag::GPSDestDistanceRef, "none", - IfdFormat::Ascii, -1i32, -1i32, gpsdestdistanceref), - - 0x1a => - (ExifTag::GPSDestDistance, "@GPSDestDistanceRef", - IfdFormat::URational, 1, 1, gpsdestdistance), - - 0x1b => - (ExifTag::GPSProcessingMethod, "none", - IfdFormat::Undefined, -1i32, -1i32, undefined_as_encoded_string), - - 0x1c => (ExifTag::GPSAreaInformation, - "none", IfdFormat::Undefined, -1i32, -1i32, undefined_as_encoded_string), - - 0x1d => - (ExifTag::GPSDateStamp, "none", - IfdFormat::Ascii, -1i32, -1i32, strpass), - - 0x1e => - (ExifTag::GPSDifferential, "none", - IfdFormat::U16, 1, 1, gpsdiff), - - _ => - (ExifTag::UnknownToMe, "Unknown unit", - IfdFormat::Unknown, -1i32, -1i32, unknown) + 0xa40b => ( + ExifTag::DeviceSettingDescription, + "none", + IfdFormat::Undefined, + -1i32, + -1i32, + undefined_as_blob, + ), + + 0xa40c => ( + ExifTag::SubjectDistanceRange, + "none", + IfdFormat::U16, + 1, + 1, + subject_distance_range, + ), + + 0xa420 => ( + ExifTag::ImageUniqueID, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x0 => (ExifTag::GPSVersionID, "none", IfdFormat::U8, 4, 4, strpass), + + 0x1 => ( + ExifTag::GPSLatitudeRef, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x2 => ( + ExifTag::GPSLatitude, + "D/M/S", + IfdFormat::URational, + 3, + 3, + dms, + ), + + 0x3 => ( + ExifTag::GPSLongitudeRef, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x4 => ( + ExifTag::GPSLongitude, + "D/M/S", + IfdFormat::URational, + 3, + 3, + dms, + ), + + 0x5 => ( + ExifTag::GPSAltitudeRef, + "none", + IfdFormat::U8, + 1, + 1, + gps_alt_ref, + ), + + 0x6 => ( + ExifTag::GPSAltitude, + "m", + IfdFormat::URational, + 1, + 1, + meters, + ), + + 0x7 => ( + ExifTag::GPSTimeStamp, + "UTC time", + IfdFormat::URational, + 3, + 3, + gpstimestamp, + ), + + 0x8 => ( + ExifTag::GPSSatellites, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x9 => ( + ExifTag::GPSStatus, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + gpsstatus, + ), + + 0xa => ( + ExifTag::GPSMeasureMode, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + gpsmeasuremode, + ), + + 0xb => ( + ExifTag::GPSDOP, + "none", + IfdFormat::URational, + 1, + 1, + rational_value, + ), + + 0xc => ( + ExifTag::GPSSpeedRef, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + gpsspeedref, + ), + + 0xd => ( + ExifTag::GPSSpeed, + "@GPSSpeedRef", + IfdFormat::URational, + 1, + 1, + gpsspeed, + ), + + 0xe => ( + ExifTag::GPSTrackRef, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + gpsbearingref, + ), + + 0xf => ( + ExifTag::GPSTrack, + "deg", + IfdFormat::URational, + 1, + 1, + gpsbearing, + ), + + 0x10 => ( + ExifTag::GPSImgDirectionRef, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + gpsbearingref, + ), + + 0x11 => ( + ExifTag::GPSImgDirection, + "deg", + IfdFormat::URational, + 1, + 1, + gpsbearing, + ), + + 0x12 => ( + ExifTag::GPSMapDatum, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x13 => ( + ExifTag::GPSDestLatitudeRef, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x14 => ( + ExifTag::GPSDestLatitude, + "D/M/S", + IfdFormat::URational, + 3, + 3, + dms, + ), + + 0x15 => ( + ExifTag::GPSDestLongitudeRef, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x16 => ( + ExifTag::GPSDestLongitude, + "D/M/S", + IfdFormat::URational, + 3, + 3, + dms, + ), + + 0x17 => ( + ExifTag::GPSDestBearingRef, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + gpsbearingref, + ), + + 0x18 => ( + ExifTag::GPSDestBearing, + "deg", + IfdFormat::URational, + 1, + 1, + gpsbearing, + ), + + 0x19 => ( + ExifTag::GPSDestDistanceRef, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + gpsdestdistanceref, + ), + + 0x1a => ( + ExifTag::GPSDestDistance, + "@GPSDestDistanceRef", + IfdFormat::URational, + 1, + 1, + gpsdestdistance, + ), + + 0x1b => ( + ExifTag::GPSProcessingMethod, + "none", + IfdFormat::Undefined, + -1i32, + -1i32, + undefined_as_encoded_string, + ), + + 0x1c => ( + ExifTag::GPSAreaInformation, + "none", + IfdFormat::Undefined, + -1i32, + -1i32, + undefined_as_encoded_string, + ), + + 0x1d => ( + ExifTag::GPSDateStamp, + "none", + IfdFormat::Ascii, + -1i32, + -1i32, + strpass, + ), + + 0x1e => ( + ExifTag::GPSDifferential, + "none", + IfdFormat::U16, + 1, + 1, + gpsdiff, + ), + + _ => ( + ExifTag::UnknownToMe, + "Unknown unit", + IfdFormat::Unknown, + -1i32, + -1i32, + unknown, + ), } } diff --git a/src/exifpost.rs b/src/exifpost.rs index f4cd50f..e397e01 100644 --- a/src/exifpost.rs +++ b/src/exifpost.rs @@ -1,14 +1,24 @@ use super::types::{ExifEntry, ExifTag, TagValue}; /// Find a tag of given type -fn other_tag<'a>(tag: ExifTag, entries1: &'a [ExifEntry], entries2: &'a [ExifEntry]) -> Option<&'a ExifEntry> { - entries1.iter().find(|entry| entry.tag == tag) - .or_else(|| entries2.iter().find(|entry| entry.tag == tag)) +fn other_tag<'a>( + tag: ExifTag, + entries1: &'a [ExifEntry], + entries2: &'a [ExifEntry], +) -> Option<&'a ExifEntry> { + entries1 + .iter() + .find(|entry| entry.tag == tag) + .or_else(|| entries2.iter().find(|entry| entry.tag == tag)) } /// Does postprocessing in tags that depend on other tags to have a complete interpretation /// e.g. when the unit of a tag is annotated on another tag -pub(crate) fn exif_postprocessing(entry: &mut ExifEntry, entries1: &[ExifEntry], entries2: &[ExifEntry]) { +pub(crate) fn exif_postprocessing( + entry: &mut ExifEntry, + entries1: &[ExifEntry], + entries2: &[ExifEntry], +) { match entry.tag { ExifTag::XResolution | ExifTag::YResolution => { if let Some(f) = other_tag(ExifTag::ResolutionUnit, entries1, entries2) { @@ -17,7 +27,7 @@ pub(crate) fn exif_postprocessing(entry: &mut ExifEntry, entries1: &[ExifEntry], v.push_str(" pixels per "); v.push_str(&f.value_more_readable); } - }, + } ExifTag::FocalPlaneXResolution | ExifTag::FocalPlaneYResolution => { if let Some(f) = other_tag(ExifTag::FocalPlaneResolutionUnit, entries1, entries2) { @@ -26,7 +36,7 @@ pub(crate) fn exif_postprocessing(entry: &mut ExifEntry, entries1: &[ExifEntry], v.push_str(" pixels per "); v.push_str(&f.value_more_readable); } - }, + } ExifTag::GPSLatitude => { if let Some(f) = other_tag(ExifTag::GPSLatitudeRef, entries1, entries2) { @@ -34,7 +44,7 @@ pub(crate) fn exif_postprocessing(entry: &mut ExifEntry, entries1: &[ExifEntry], v.push(' '); v.push_str(&f.value_more_readable); } - }, + } ExifTag::GPSLongitude => { if let Some(f) = other_tag(ExifTag::GPSLongitudeRef, entries1, entries2) { @@ -42,7 +52,7 @@ pub(crate) fn exif_postprocessing(entry: &mut ExifEntry, entries1: &[ExifEntry], v.push(' '); v.push_str(&f.value_more_readable); } - }, + } ExifTag::GPSAltitude => { if let Some(f) = other_tag(ExifTag::GPSAltitudeRef, entries1, entries2) { @@ -52,10 +62,13 @@ pub(crate) fn exif_postprocessing(entry: &mut ExifEntry, entries1: &[ExifEntry], }; if altref != 0 { - entry.value_more_readable.to_mut().push_str(" below sea level"); + entry + .value_more_readable + .to_mut() + .push_str(" below sea level"); } } - }, + } ExifTag::GPSDestLatitude => { if let Some(f) = other_tag(ExifTag::GPSDestLatitudeRef, entries1, entries2) { @@ -63,7 +76,7 @@ pub(crate) fn exif_postprocessing(entry: &mut ExifEntry, entries1: &[ExifEntry], v.push(' '); v.push_str(&f.value_more_readable); } - }, + } ExifTag::GPSDestLongitude => { if let Some(f) = other_tag(ExifTag::GPSDestLongitudeRef, entries1, entries2) { @@ -71,7 +84,7 @@ pub(crate) fn exif_postprocessing(entry: &mut ExifEntry, entries1: &[ExifEntry], v.push(' '); v.push_str(&f.value_more_readable); } - }, + } ExifTag::GPSDestDistance => { if let Some(f) = other_tag(ExifTag::GPSDestDistanceRef, entries1, entries2) { @@ -80,7 +93,7 @@ pub(crate) fn exif_postprocessing(entry: &mut ExifEntry, entries1: &[ExifEntry], v.push(' '); v.push_str(&f.value_more_readable); } - }, + } ExifTag::GPSSpeed => { if let Some(f) = other_tag(ExifTag::GPSSpeedRef, entries1, entries2) { @@ -89,7 +102,7 @@ pub(crate) fn exif_postprocessing(entry: &mut ExifEntry, entries1: &[ExifEntry], v.push(' '); v.push_str(&f.value_more_readable); } - }, + } _ => (), } } diff --git a/src/exifreadable.rs b/src/exifreadable.rs index ae37113..6ccd0b9 100644 --- a/src/exifreadable.rs +++ b/src/exifreadable.rs @@ -35,47 +35,55 @@ pub(crate) fn sensitivity_type(tag: u16, e: &TagValue) -> Option Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { + TagValue::U16(ref v) => Some( + match v.first()? { 1 => "Straight", 3 => "Upside down", 6 => "Rotated to left", 8 => "Rotated to right", 9 => "Undefined", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } pub(crate) fn rational_value(_tag: u16, e: &TagValue) -> Option> { - Some(match e { - TagValue::URational(v) => v.first()?.value(), - TagValue::IRational(v) => v.first()?.value(), - _ => return None, - }.to_string().into()) + Some( + match e { + TagValue::URational(v) => v.first()?.value(), + TagValue::IRational(v) => v.first()?.value(), + _ => return None, + } + .to_string() + .into(), + ) } pub(crate) fn rational_values(_tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::URational(ref v) => { - Some(NumArray::new(v.iter().map(|&x| x.value())).to_string().into()) - }, + TagValue::URational(ref v) => Some( + NumArray::new(v.iter().map(|&x| x.value())) + .to_string() + .into(), + ), _ => None, } } pub(crate) fn resolution_unit(tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { + TagValue::U16(ref v) => Some( + match v.first()? { 1 => "Unitless", 2 => "in", 3 => "cm", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } @@ -84,17 +92,20 @@ pub(crate) fn exposure_time(_tag: u16, e: &TagValue) -> Option match *e { TagValue::URational(ref v) => { let r = v.first()?; - Some(if r.numerator == 1 && r.denominator > 1 { - // traditional 1/x exposure time - format!("{r} s") - } else if r.value() < 0.1 { - format!("1/{:.0} s", 1.0 / r.value()) - } else if r.value() < 1.0 { - format!("1/{:.1} s", 1.0 / r.value()) - } else { - format!("{:.1} s", r.value()) - }.into()) - }, + Some( + if r.numerator == 1 && r.denominator > 1 { + // traditional 1/x exposure time + format!("{r} s") + } else if r.value() < 0.1 { + format!("1/{:.0} s", 1.0 / r.value()) + } else if r.value() < 1.0 { + format!("1/{:.1} s", 1.0 / r.value()) + } else { + format!("{:.1} s", r.value()) + } + .into(), + ) + } _ => None, } } @@ -108,8 +119,8 @@ pub(crate) fn f_number(_tag: u16, e: &TagValue) -> Option> { pub(crate) fn exposure_program(tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { + TagValue::U16(ref v) => Some( + match v.first()? { 1 => "Manual control", 2 => "Program control", 3 => "Aperture priority", @@ -119,8 +130,9 @@ pub(crate) fn exposure_program(tag: u16, e: &TagValue) -> Option "Portrait mode", 8 => "Landscape mode", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } @@ -148,15 +160,16 @@ pub(crate) fn meters(_tag: u16, e: &TagValue) -> Option> { pub(crate) fn iso_speeds(_tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => { - Some(if v.len() == 1 { + TagValue::U16(ref v) => Some( + if v.len() == 1 { format!("ISO {}", v[0]) } else if v.len() == 2 || v.len() == 3 { format!("ISO {} latitude {}", v[0], v[1]) } else { format!("Unknown ({})", NumArray::new(v)) - }.into()) - }, + } + .into(), + ), _ => None, } } @@ -167,48 +180,50 @@ pub(crate) fn dms(_tag: u16, e: &TagValue) -> Option> { let deg = v[0]; let min = v[1]; let sec = v[2]; - Some(if deg.denominator == 1 && min.denominator == 1 { - format!("{}°{}'{:.2}\"", deg.value(), min.value(), sec.value()) - } else if deg.denominator == 1 { - format!("{}°{:.4}'", deg.value(), min.value() + sec.value() / 60.0) - } else { - // untypical format - format!( - "{:.7}°", - deg.value() + min.value() / 60.0 + sec.value() / 3600.0 - ) - }.into()) - }, + Some( + if deg.denominator == 1 && min.denominator == 1 { + format!("{}°{}'{:.2}\"", deg.value(), min.value(), sec.value()) + } else if deg.denominator == 1 { + format!("{}°{:.4}'", deg.value(), min.value() + sec.value() / 60.0) + } else { + // untypical format + format!( + "{:.7}°", + deg.value() + min.value() / 60.0 + sec.value() / 3600.0 + ) + } + .into(), + ) + } _ => None, } } pub(crate) fn gps_alt_ref(_tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U8(ref v) => { - Some(match v.first()? { + TagValue::U8(ref v) => Some( + match v.first()? { 0 => "Above sea level", 1 => "Below sea level", n => return Some(format!("Unknown, assumed below sea level ({n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } pub(crate) fn gpsdestdistanceref(_tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::Ascii(ref v) => { - Some(if v == "N" { - "kn".into() - } else if v == "K" { - "km".into() - } else if v == "M" { - "mi".into() - } else { - format!("Unknown ({v})").into() - }) - }, + TagValue::Ascii(ref v) => Some(if v == "N" { + "kn".into() + } else if v == "K" { + "km".into() + } else if v == "M" { + "mi".into() + } else { + format!("Unknown ({v})").into() + }), _ => None, } } @@ -222,17 +237,15 @@ pub(crate) fn gpsdestdistance(_tag: u16, e: &TagValue) -> Option Option> { match *e { - TagValue::Ascii(ref v) => { - Some(if v == "N" { - "kn".into() - } else if v == "K" { - "km/h".into() - } else if v == "M" { - "mi/h".into() - } else { - format!("Unknown ({v})").into() - }) - }, + TagValue::Ascii(ref v) => Some(if v == "N" { + "kn".into() + } else if v == "K" { + "km/h".into() + } else if v == "M" { + "mi/h".into() + } else { + format!("Unknown ({v})").into() + }), _ => None, } } @@ -246,15 +259,13 @@ pub(crate) fn gpsspeed(_tag: u16, e: &TagValue) -> Option> { pub(crate) fn gpsbearingref(_tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::Ascii(ref v) => { - Some(if v == "T" { - "True bearing".into() - } else if v == "M" { - "Magnetic bearing".into() - } else { - format!("Unknown ({v})").into() - }) - }, + TagValue::Ascii(ref v) => Some(if v == "T" { + "True bearing".into() + } else if v == "M" { + "Magnetic bearing".into() + } else { + format!("Unknown ({v})").into() + }), _ => None, } } @@ -272,56 +283,53 @@ pub(crate) fn gpstimestamp(_tag: u16, e: &TagValue) -> Option> let sec = v.get(2)?; let hour = v.first()?; let min = v.get(1)?; - Some(format!( - "{:02.0}:{:02.0}:{:04.1} UTC", - hour.value(), - min.value(), - sec.value() - ).into()) - }, + Some( + format!( + "{:02.0}:{:02.0}:{:04.1} UTC", + hour.value(), + min.value(), + sec.value() + ) + .into(), + ) + } _ => None, } } pub(crate) fn gpsdiff(tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { - 0 => "Measurement without differential correction".into(), - 1 => "Differential correction applied".into(), - n => format!("Unknown ({tag:04x}={n})").into(), - }) - }, + TagValue::U16(ref v) => Some(match v.first()? { + 0 => "Measurement without differential correction".into(), + 1 => "Differential correction applied".into(), + n => format!("Unknown ({tag:04x}={n})").into(), + }), _ => None, } } pub(crate) fn gpsstatus(_tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::Ascii(ref v) => { - Some(if v == "A" { - "Measurement in progress".into() - } else if v == "V" { - "Measurement is interoperability".into() - } else { - format!("Unknown ({v})").into() - }) - }, + TagValue::Ascii(ref v) => Some(if v == "A" { + "Measurement in progress".into() + } else if v == "V" { + "Measurement is interoperability".into() + } else { + format!("Unknown ({v})").into() + }), _ => None, } } pub(crate) fn gpsmeasuremode(_tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::Ascii(ref v) => { - Some(if v == "2" { - "2-dimension".into() - } else if v == "3" { - "3-dimension".into() - } else { - format!("Unknown ({v})").into() - }) - }, + TagValue::Ascii(ref v) => Some(if v == "2" { + "2-dimension".into() + } else if v == "3" { + "3-dimension".into() + } else { + format!("Unknown ({v})").into() + }), _ => None, } } @@ -358,22 +366,25 @@ pub(crate) fn undefined_as_encoded_string(_tag: u16, e: &TagValue) -> Option { - Some(if v.len() < 8 { - format!("String w/ truncated preamble {}", NumArray::new(v)) - } else if v[0..8] == ASC[..] { - String::from_utf8_lossy(&v[8..]).into_owned() - } else if v[0..8] == JIS[..] { - format!("JIS string {}", NumArray::new(&v[8..])) - } else if v[0..8] == UNICODE[..] { - let v8 = &v[8..]; - // reinterpret as vector of u16 - let v16_size = (v8.len() / 2) as u32; - let v16 = read_u16_array(le, v16_size, v8)?; - String::from_utf16_lossy(&v16) - } else { - format!("String w/ undefined encoding {}", NumArray::new(v)) - }.into()) - }, + Some( + if v.len() < 8 { + format!("String w/ truncated preamble {}", NumArray::new(v)) + } else if v[0..8] == ASC[..] { + String::from_utf8_lossy(&v[8..]).into_owned() + } else if v[0..8] == JIS[..] { + format!("JIS string {}", NumArray::new(&v[8..])) + } else if v[0..8] == UNICODE[..] { + let v8 = &v[8..]; + // reinterpret as vector of u16 + let v16_size = (v8.len() / 2) as u32; + let v16 = read_u16_array(le, v16_size, v8)?; + String::from_utf16_lossy(&v16) + } else { + format!("String w/ undefined encoding {}", NumArray::new(v)) + } + .into(), + ) + } _ => None, } } @@ -409,7 +420,7 @@ pub(crate) fn apex_brightness(_tag: u16, e: &TagValue) -> Option None, } } @@ -423,13 +434,14 @@ pub(crate) fn apex_ev(_tag: u16, e: &TagValue) -> Option> { pub(crate) fn file_source(_tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::Undefined(ref v, _) => { - Some(if !v.is_empty() && v[0] == 3 { + TagValue::Undefined(ref v, _) => Some( + if !v.is_empty() && v[0] == 3 { "DSC" } else { "Unknown" - }.into()) - }, + } + .into(), + ), _ => None, } } @@ -443,8 +455,8 @@ pub(crate) fn flash_energy(_tag: u16, e: &TagValue) -> Option> pub(crate) fn metering_mode(tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { + TagValue::U16(ref v) => Some( + match v.first()? { 0 => "Unknown", 1 => "Average", 2 => "Center-weighted average", @@ -454,16 +466,17 @@ pub(crate) fn metering_mode(tag: u16, e: &TagValue) -> Option> 6 => "Partial", 255 => "Other", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } pub(crate) fn light_source(tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { + TagValue::U16(ref v) => Some( + match v.first()? { 0 => "Unknown", 1 => "Daylight", 2 => "Fluorescent", @@ -486,21 +499,23 @@ pub(crate) fn light_source(tag: u16, e: &TagValue) -> Option> 24 => "ISO studio tungsten", 255 => "Other", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } pub(crate) fn color_space(tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { + TagValue::U16(ref v) => Some( + match v.first()? { 1 => "sRGB", 65535 => "Uncalibrated", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } @@ -546,22 +561,25 @@ pub(crate) fn flash(_tag: u16, e: &TagValue) -> Option> { } Some(format!("{b0}{b12}{b34}{b6}").into()) - }, + } _ => None, } } pub(crate) fn subject_area(_tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => Some(match v.len() { - 2 => format!("at pixel {},{}", v[0], v[1]), - 3 => format!("at center {},{} radius {}", v[0], v[1], v[2]), - 4 => format!( - "at rectangle {},{} width {} height {}", - v[0], v[1], v[2], v[3] - ), - _ => format!("Unknown ({}) ", NumArray::new(v)), - }.into()), + TagValue::U16(ref v) => Some( + match v.len() { + 2 => format!("at pixel {},{}", v[0], v[1]), + 3 => format!("at center {},{} radius {}", v[0], v[1], v[2]), + 4 => format!( + "at rectangle {},{} width {} height {}", + v[0], v[1], v[2], v[3] + ), + _ => format!("Unknown ({}) ", NumArray::new(v)), + } + .into(), + ), _ => None, } } @@ -575,120 +593,128 @@ pub(crate) fn subject_location(_tag: u16, e: &TagValue) -> Option Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { + TagValue::U16(ref v) => Some( + match v.first()? { 0 => "Normal", 1 => "Soft", 2 => "Hard", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } pub(crate) fn saturation(tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { + TagValue::U16(ref v) => Some( + match v.first()? { 0 => "Normal", 1 => "Low", 2 => "High", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } pub(crate) fn contrast(tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { + TagValue::U16(ref v) => Some( + match v.first()? { 0 => "Normal", 1 => "Soft", 2 => "Hard", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } pub(crate) fn gain_control(tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { + TagValue::U16(ref v) => Some( + match v.first()? { 0 => "None", 1 => "Low gain up", 2 => "High gain up", 3 => "Low gain down", 4 => "High gain down", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } pub(crate) fn exposure_mode(tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { + TagValue::U16(ref v) => Some( + match v.first()? { 0 => "Auto exposure", 1 => "Manual exposure", 2 => "Auto bracket", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } pub(crate) fn scene_capture_type(tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { + TagValue::U16(ref v) => Some( + match v.first()? { 0 => "Standard", 1 => "Landscape", 2 => "Portrait", 3 => "Night scene", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } pub(crate) fn scene_type(tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::Undefined(ref v, _) => { - Some(match v.first()? { + TagValue::Undefined(ref v, _) => Some( + match v.first()? { 1 => "Directly photographed image", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } pub(crate) fn white_balance_mode(tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { + TagValue::U16(ref v) => Some( + match v.first()? { 0 => "Auto", 1 => "Manual", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } pub(crate) fn sensing_method(tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { + TagValue::U16(ref v) => Some( + match v.first()? { 1 => "Not defined", 2 => "One-chip color area sensor", 3 => "Two-chip color area sensor", @@ -697,36 +723,39 @@ pub(crate) fn sensing_method(tag: u16, e: &TagValue) -> Option 7 => "Trilinear sensor", 8 => "Color sequential linear sensor", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } pub(crate) fn custom_rendered(tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { + TagValue::U16(ref v) => Some( + match v.first()? { 0 => "Normal", 1 => "Custom", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } pub(crate) fn subject_distance_range(tag: u16, e: &TagValue) -> Option> { match *e { - TagValue::U16(ref v) => { - Some(match v.first()? { + TagValue::U16(ref v) => Some( + match v.first()? { 0 => "Unknown", 1 => "Macro", 2 => "Close view", 3 => "Distant view", n => return Some(format!("Unknown ({tag:04x}={n})").into()), - }.into()) - }, + } + .into(), + ), _ => None, } } @@ -739,18 +768,21 @@ pub(crate) fn lens_spec(_tag: u16, e: &TagValue) -> Option> { let a0 = v[2].value(); let a1 = v[3].value(); - Some(if v[0] == v[1] { - if a0.is_finite() { - format!("{f0} mm f/{a0:.1}") + Some( + if v[0] == v[1] { + if a0.is_finite() { + format!("{f0} mm f/{a0:.1}") + } else { + format!("{f0} mm f/unknown") + } + } else if a0.is_finite() && a1.is_finite() { + format!("{f0}-{f1} mm f/{a0:.1}-{a1:.1}") } else { - format!("{f0} mm f/unknown") + format!("{f0}-{f1} mm f/unknown") } - } else if a0.is_finite() && a1.is_finite() { - format!("{f0}-{f1} mm f/{a0:.1}-{a1:.1}") - } else { - format!("{f0}-{f1} mm f/unknown") - }.into()) - }, + .into(), + ) + } _ => None, } } diff --git a/src/ifdformat.rs b/src/ifdformat.rs index d4949e8..42274dc 100644 --- a/src/ifdformat.rs +++ b/src/ifdformat.rs @@ -45,54 +45,54 @@ pub(crate) fn tag_value_new(f: &IfdEntry) -> Option { let s = String::from_utf8_lossy(data); let s = s.into_owned(); TagValue::Ascii(s) - }, + } IfdFormat::U16 => { let a = read_u16_array(f.le, f.count, &f.data)?; TagValue::U16(a) - }, + } IfdFormat::I16 => { let a = read_i16_array(f.le, f.count, &f.data)?; TagValue::I16(a) - }, + } IfdFormat::U8 => { if f.data.len() < f.count as usize { return None; } TagValue::U8(f.data.clone()) - }, + } IfdFormat::I8 => { let a = read_i8_array(f.count, &f.data)?; TagValue::I8(a) - }, + } IfdFormat::U32 => { let a = read_u32_array(f.le, f.count, &f.data)?; TagValue::U32(a) - }, + } IfdFormat::I32 => { let a = read_i32_array(f.le, f.count, &f.data)?; TagValue::I32(a) - }, + } IfdFormat::F32 => { let a = read_f32_array(f.count, &f.data)?; TagValue::F32(a) - }, + } IfdFormat::F64 => { let a = read_f64_array(f.count, &f.data)?; TagValue::F64(a) - }, + } IfdFormat::URational => { let a = read_urational_array(f.le, f.count, &f.data)?; TagValue::URational(a) - }, + } IfdFormat::IRational => { let a = read_irational_array(f.le, f.count, &f.data)?; TagValue::IRational(a) - }, + } IfdFormat::Undefined => { let a = f.data.clone(); TagValue::Undefined(a, f.le) - }, + } _ => TagValue::Unknown(f.data.clone(), f.le), }) } @@ -110,8 +110,8 @@ fn vec_cmp(va: &[F], vb: &[F]) -> bool { /// values at the same positions). pub(crate) fn tag_value_eq(left: &TagValue, right: &TagValue) -> bool { match (left, right) { - (TagValue::F32(x), TagValue::F32(y)) => vec_cmp(&x, &y), - (TagValue::F64(x), TagValue::F64(y)) => vec_cmp(&x, &y), + (TagValue::F32(x), TagValue::F32(y)) => vec_cmp(x, y), + (TagValue::F64(x), TagValue::F64(y)) => vec_cmp(x, y), (x, y) => x == y, } } diff --git a/src/lib.rs b/src/lib.rs index 3b614a2..b6a5c40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,9 +75,11 @@ pub fn parse_buffer_quiet(contents: &[u8]) -> (ExifResult, Vec) { FileType::Unknown => return (Err(ExifError::FileTypeUnknown), warnings), FileType::TIFF => parse_tiff(contents, &mut warnings), FileType::JPEG => { - match find_embedded_tiff_in_jpeg(contents).map(|(offset, size)| parse_tiff(&contents[offset..offset + size], &mut warnings)) { + match find_embedded_tiff_in_jpeg(contents) + .map(|(offset, size)| parse_tiff(&contents[offset..offset + size], &mut warnings)) + { Ok(r) => r, - Err(e) => return (Err(e), warnings) + Err(e) => return (Err(e), warnings), } } }; @@ -86,7 +88,7 @@ pub fn parse_buffer_quiet(contents: &[u8]) -> (ExifResult, Vec) { entries.map(|entries| ExifData { mime: mime.as_str(), entries, - le + le, }), warnings, ) diff --git a/src/lowlevel.rs b/src/lowlevel.rs index 03a1a76..d972bac 100644 --- a/src/lowlevel.rs +++ b/src/lowlevel.rs @@ -62,7 +62,10 @@ pub(crate) fn read_f64(raw: &[u8]) -> Option { pub(crate) fn read_urational(le: bool, raw: &[u8]) -> Option { let n = read_u32(le, &raw[0..4])?; let d = read_u32(le, &raw[4..8])?; - Some(URational { numerator: n, denominator: d }) + Some(URational { + numerator: n, + denominator: d, + }) } /// Read value from a stream of bytes @@ -70,50 +73,106 @@ pub(crate) fn read_urational(le: bool, raw: &[u8]) -> Option { pub(crate) fn read_irational(le: bool, raw: &[u8]) -> Option { let n = read_i32(le, &raw[0..4])?; let d = read_i32(le, &raw[4..8])?; - Some(IRational { numerator: n, denominator: d }) + Some(IRational { + numerator: n, + denominator: d, + }) } /// Read array from a stream of bytes. Caller must be sure of count and buffer size pub(crate) fn read_i8_array(count: u32, raw: &[u8]) -> Option> { - Some(raw.get(..count as usize)?.iter().map(|&i| i as i8).collect()) + Some( + raw.get(..count as usize)? + .iter() + .map(|&i| i as i8) + .collect(), + ) } /// Read array from a stream of bytes. Caller must be sure of count and buffer size pub(crate) fn read_u16_array(le: bool, count: u32, raw: &[u8]) -> Option> { - Some(raw.get(..count as usize * 2)?.chunks_exact(2).take(count as usize).map(|ch| read_u16(le, ch).unwrap()).collect()) + Some( + raw.get(..count as usize * 2)? + .chunks_exact(2) + .take(count as usize) + .map(|ch| read_u16(le, ch).unwrap()) + .collect(), + ) } /// Read array from a stream of bytes. Caller must be sure of count and buffer size pub(crate) fn read_i16_array(le: bool, count: u32, raw: &[u8]) -> Option> { - Some(raw.get(..count as usize * 2)?.chunks_exact(2).take(count as usize).map(|ch| read_i16(le, ch).unwrap()).collect()) + Some( + raw.get(..count as usize * 2)? + .chunks_exact(2) + .take(count as usize) + .map(|ch| read_i16(le, ch).unwrap()) + .collect(), + ) } /// Read array from a stream of bytes. Caller must be sure of count and buffer size pub(crate) fn read_u32_array(le: bool, count: u32, raw: &[u8]) -> Option> { - Some(raw.get(..count as usize * 4)?.chunks_exact(4).take(count as usize).map(|ch| read_u32(le, ch).unwrap()).collect()) + Some( + raw.get(..count as usize * 4)? + .chunks_exact(4) + .take(count as usize) + .map(|ch| read_u32(le, ch).unwrap()) + .collect(), + ) } /// Read array from a stream of bytes. Caller must be sure of count and buffer size pub(crate) fn read_i32_array(le: bool, count: u32, raw: &[u8]) -> Option> { - Some(raw.get(..count as usize * 4)?.chunks_exact(4).take(count as usize).map(|ch| read_i32(le, ch).unwrap()).collect()) + Some( + raw.get(..count as usize * 4)? + .chunks_exact(4) + .take(count as usize) + .map(|ch| read_i32(le, ch).unwrap()) + .collect(), + ) } /// Read array from a stream of bytes. Caller must be sure of count and buffer size pub(crate) fn read_f32_array(count: u32, raw: &[u8]) -> Option> { - Some(raw.get(..count as usize * 4)?.chunks_exact(4).take(count as usize).map(|ch| read_f32(ch).unwrap()).collect()) + Some( + raw.get(..count as usize * 4)? + .chunks_exact(4) + .take(count as usize) + .map(|ch| read_f32(ch).unwrap()) + .collect(), + ) } /// Read array from a stream of bytes. Caller must be sure of count and buffer size pub(crate) fn read_f64_array(count: u32, raw: &[u8]) -> Option> { - Some(raw.get(..count as usize * 8)?.chunks_exact(8).take(count as usize).map(|ch| read_f64(ch).unwrap()).collect()) + Some( + raw.get(..count as usize * 8)? + .chunks_exact(8) + .take(count as usize) + .map(|ch| read_f64(ch).unwrap()) + .collect(), + ) } /// Read array from a stream of bytes. Caller must be sure of count and buffer size pub(crate) fn read_urational_array(le: bool, count: u32, raw: &[u8]) -> Option> { - Some(raw.get(..count as usize * 8)?.chunks_exact(8).take(count as usize).map(|ch| read_urational(le, ch).unwrap()).collect()) + Some( + raw.get(..count as usize * 8)? + .chunks_exact(8) + .take(count as usize) + .map(|ch| read_urational(le, ch).unwrap()) + .collect(), + ) } /// Read array from a stream of bytes. Caller must be sure of count and buffer size pub(crate) fn read_irational_array(le: bool, count: u32, raw: &[u8]) -> Option> { - Some(raw.get(..count as usize * 8)?.chunks_exact(8).take(count as usize).map(|ch| read_irational(le, ch).unwrap()).collect()) + Some( + raw.get(..count as usize * 8)? + .chunks_exact(8) + .take(count as usize) + .map(|ch| read_irational(le, ch).unwrap()) + .collect(), + ) } diff --git a/src/main.rs b/src/main.rs index 829be7a..b9753e7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,10 +23,10 @@ fn main() { println!("\t{}: {}", entry.tag, entry.value_more_readable); } } - }, + } Err(e) => { eprintln!("Error in {}: {}", &arg, e); - }, + } } } } diff --git a/src/tiff.rs b/src/tiff.rs index 70e14e7..a002087 100644 --- a/src/tiff.rs +++ b/src/tiff.rs @@ -10,7 +10,11 @@ type InExifResult = Result<(), ExifError>; /// Parse of raw IFD entry into EXIF data, if it is of a known type, and returns /// an `ExifEntry` object. If the tag is unknown, the enumeration is set to `UnknownToMe`, /// but the raw information of tag is still available in the ifd member. -pub(crate) fn parse_exif_entry(ifd: IfdEntry, warnings: &mut Vec, kind: IfdKind) -> ExifEntry { +pub(crate) fn parse_exif_entry( + ifd: IfdEntry, + warnings: &mut Vec, + kind: IfdKind, +) -> ExifEntry { let (tag, unit, format, min_count, max_count, more_readable) = tag_to_exif(ifd.tag); let value = match tag_value_new(&ifd) { Some(v) => v, @@ -67,7 +71,7 @@ pub fn parse_ifd( subifd: bool, le: bool, count: u16, - contents: &[u8] + contents: &[u8], ) -> Option<(Vec, usize)> { let mut entries: Vec = Vec::new(); @@ -115,17 +119,16 @@ fn parse_exif_ifd( let mut offset = ioffset; if contents.len() < (offset + 2) { - return Err(ExifError::ExifIfdTruncated( - format!("Truncated {:?} at dir entry count ({} < {})", kind, contents.len(), (offset + 2)), - )); + return Err(ExifError::ExifIfdTruncated(format!( + "Truncated {:?} at dir entry count ({} < {})", + kind, + contents.len(), + (offset + 2) + ))); } - let count = read_u16( - le, - contents - .get(offset..) - .ok_or(ExifError::IfdTruncated)?, - ).ok_or(ExifError::IfdTruncated)?; + let count = read_u16(le, contents.get(offset..).ok_or(ExifError::IfdTruncated)?) + .ok_or(ExifError::IfdTruncated)?; let ifd_length = (count as usize) * 12; offset += 2; @@ -164,12 +167,19 @@ pub fn parse_ifds( // fills exif_entries with data from IFD0 - match parse_exif_ifd(le, contents, offset, &mut exif_entries, warnings, IfdKind::Ifd0) { + match parse_exif_ifd( + le, + contents, + offset, + &mut exif_entries, + warnings, + IfdKind::Ifd0, + ) { Ok(()) => true, Err(e) => return Err(e), }; - // at this point we knot that IFD0 is good + // at this point we know that IFD0 is good // looks for SubIFD (EXIF) let count = read_u16( @@ -177,7 +187,8 @@ pub fn parse_ifds( contents .get(offset..offset + 2) .ok_or(ExifError::IfdTruncated)?, - ).ok_or(ExifError::IfdTruncated)?; + ) + .ok_or(ExifError::IfdTruncated)?; let ifd_length = (count as usize) * 12 + 4; offset += 2; @@ -203,7 +214,14 @@ pub fn parse_ifds( "Exif SubIFD goes past EOF".to_string(), )); } - parse_exif_ifd(le, contents, exif_offset, &mut exif_entries, warnings, ifd_kind)?; + parse_exif_ifd( + le, + contents, + exif_offset, + &mut exif_entries, + warnings, + ifd_kind, + )?; } for n in 0..exif_entries.len() { diff --git a/src/types.rs b/src/types.rs index ecbb5aa..f520836 100644 --- a/src/types.rs +++ b/src/types.rs @@ -68,7 +68,7 @@ impl ExifData { IfdKind::Gps => gps.push(e), _ => { // XXX Silently ignore Makernote and Interoperability IFDs - }, + } } } @@ -126,7 +126,11 @@ impl ExifData { }; serialized.extend(patch.data); - for (place, byte) in serialized.iter_mut().skip(patch.offset_pos as usize).zip(bytes.iter()) { + for (place, byte) in serialized + .iter_mut() + .skip(patch.offset_pos as usize) + .zip(bytes.iter()) + { *place = *byte; } } @@ -189,7 +193,11 @@ impl ExifData { (serialized.len() as u32).to_be_bytes() }; serialized.extend(patch.data); - for (place, byte) in serialized.iter_mut().skip(patch.offset_pos as usize).zip(bytes.iter()) { + for (place, byte) in serialized + .iter_mut() + .skip(patch.offset_pos as usize) + .zip(bytes.iter()) + { *place = *byte; } } @@ -206,10 +214,7 @@ pub(super) struct Patch<'a> { impl Patch<'_> { pub fn new(offset_pos: u32, data: &[u8]) -> Patch { - Patch { - offset_pos, - data, - } + Patch { offset_pos, data } } } @@ -263,14 +268,22 @@ pub struct IfdEntry { // entries should still be considered equal. impl PartialEq for IfdEntry { fn eq(&self, other: &IfdEntry) -> bool { - let data_eq = if self.in_ifd() && !self.tag == ExifTag::ExifOffset as u16 && !self.tag == ExifTag::GPSOffset as u16 { - self.data == other.data && self.ifd_data == other.ifd_data && self.ext_data == other.ext_data + let data_eq = if self.in_ifd() + && !self.tag == ExifTag::ExifOffset as u16 + && !self.tag == ExifTag::GPSOffset as u16 + { + self.data == other.data + && self.ifd_data == other.ifd_data + && self.ext_data == other.ext_data } else { true }; - self.namespace == other.namespace && self.tag == other.tag && self.count == other.count && - data_eq && self.le == other.le + self.namespace == other.namespace + && self.tag == other.tag + && self.count == other.count + && data_eq + && self.le == other.le } } @@ -282,7 +295,7 @@ impl IfdEntry { ) -> Result<(), ExifError> { // Serialize the entry if self.namespace != Namespace::Standard { - return Err(ExifError::UnsupportedNamespace) + return Err(ExifError::UnsupportedNamespace); } // Serialize the tag (2 bytes) @@ -625,12 +638,17 @@ impl PartialEq for ExifEntry { let value_eq = match self.tag { ExifTag::ExifOffset | ExifTag::GPSOffset => true, _ => { - self.value_more_readable == other.value_more_readable && tag_value_eq(&self.value, &other.value) - }, + self.value_more_readable == other.value_more_readable + && tag_value_eq(&self.value, &other.value) + } }; - self.namespace == other.namespace && self.ifd == other.ifd && self.tag == other.tag && - self.unit == other.unit && self.kind == other.kind && value_eq + self.namespace == other.namespace + && self.ifd == other.ifd + && self.tag == other.tag + && self.unit == other.unit + && self.kind == other.kind + && value_eq } } diff --git a/src/types_impl.rs b/src/types_impl.rs index ea33235..d41acda 100644 --- a/src/types_impl.rs +++ b/src/types_impl.rs @@ -114,8 +114,7 @@ impl IfdEntry { } } -impl Error for ExifError { -} +impl Error for ExifError {} impl Display for ExifError { #[cold] @@ -129,8 +128,12 @@ impl Display for ExifError { ExifError::IfdTruncated => f.write_str("TIFF IFD truncated"), ExifError::ExifIfdTruncated(ref s) => write!(f, "TIFF Exif IFD truncated: {s}"), ExifError::ExifIfdEntryNotFound => f.write_str("TIFF Exif IFD not found"), - ExifError::UnsupportedNamespace => f.write_str("Only standar namespace can be serialized"), - ExifError::MissingExifOffset => f.write_str("Expected to have seen ExifOffset tagin IFD0"), + ExifError::UnsupportedNamespace => { + f.write_str("Only standar namespace can be serialized") + } + ExifError::MissingExifOffset => { + f.write_str("Expected to have seen ExifOffset tagin IFD0") + } } } } diff --git a/tests/integration_test.rs b/tests/integration_test.rs index db3309b..ae45521 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -36,8 +36,12 @@ fn test_parse_simple_morotola_jpeg() { ]; check_tags(&exif.entries, expected_tags); - assert!(exif.entries.iter().all(|e| e.namespace == Namespace::Standard), - "Expected all tags to be from the standard namespace"); + assert!( + exif.entries + .iter() + .all(|e| e.namespace == Namespace::Standard), + "Expected all tags to be from the standard namespace" + ); } #[test] @@ -73,7 +77,10 @@ fn test_parse_jpeg_with_gps() -> Result<(), std::io::Error> { ]; for t in expected_tags { - assert!(exif.entries.iter().any(|e| e.tag == t), "Could not find exif tag: {t:?}"); + assert!( + exif.entries.iter().any(|e| e.tag == t), + "Could not find exif tag: {t:?}" + ); } Ok(()) @@ -149,5 +156,8 @@ fn test_tiff_exif_serialization() -> Result<(), std::io::Error> { fn test_serialize_empty() { let exif = ExifData::new("image/jpeg", vec![], false); let tiff_header = [b'M', b'M', 0, 42, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0]; - assert_eq!(exif.serialize().unwrap(), [EXIF_HEADER, &tiff_header].concat()); + assert_eq!( + exif.serialize().unwrap(), + [EXIF_HEADER, &tiff_header].concat() + ); }