From 9d981d46f2dff27a2186e3ee9f7d053ef480ddc0 Mon Sep 17 00:00:00 2001 From: swackhamer Date: Sat, 1 Aug 2026 23:11:32 -0500 Subject: [PATCH] fix(makernotes): honour ExifTool's Priority, so a sub-directory copy stops overwriting the Main tag Five corpus files carry the same Pentax tag twice -- once in `%Pentax::Main` and once inside a binary sub-directory -- and oxidex printed the sub-directory copy. That is the worst shape a defect can take here: a real ExifTool tag name carrying the wrong value, with nothing downstream able to tell. Pentax/PentaxK100D.jpg LensType M-42 or No Lens -> smc PENTAX-DA 21mm F3.2 AL Limited Pentax/PentaxK-50.jpg LensType M-42 or No Lens -> Sigma Pentax/PentaxK-5II.jpg LensType K or M Lens -> Sigma Pentax/PentaxOptioSVi.jpg PentaxModelID Optio SV -> Optio SVi Pentax/PentaxOptioL20.jpg PentaxModelID Optio S7 -> Optio L20 ExifTool decides this with `Priority`. `FoundTag` (ExifTool.pm:9448) reads it at :9469-9473, and on a duplicate name compares against the priority already recorded (:9564). Two details make the rule: a 0-priority value records no entry in `PRIORITY` (:9589 stores it "only if exists and is non-zero"), and a missing entry is promoted to 1 (:9545-9551, "promote existing 0-priority tag so it takes precedence over a new 0-tag"). Those compose to exactly two rules -- a `Priority => 0` value never displaces a value already present, and a normal-priority value always displaces whatever is present. The second is what a plain `HashMap::insert` already does, so only the first needed writing: `shared::tag_priority::insert_low_priority`. `%Pentax::LensRec` and `%Pentax::LensInfo*` are BOTH `Priority => 0` (Pentax.pm:4202, :4248), so the winner is decided by first-found, not by a priority difference -- and `0 0` is a real lens id ("M-42 or No Lens"), not a null, which is why the clobber looked like data. `%Pentax::CameraInfo` carries ExifTool's own reason on the line: "(Optio SVi uses incorrect Optio SV ID here)" (Pentax.pm:4723). The generated sub-directory decoder now carries the flag too, so this is fixed for every vendor's tables rather than for Pentax alone. `codegen_subdirs.py` had `Priority` in the list of keys that "do not change what a reader produces", which is how it went missing; it now reads `Priority`/`Avoid`, honours a table-level `PRIORITY`, and refuses any priority value it does not model rather than silently treating it as normal. Regenerating the three affected table files from ExifTool 13.55 reproduces them byte-for-byte apart from the added flag, and all 155 fields are `low_priority: false` -- no generated table is affected today, so the codegen change is prevention, not behaviour. Measured over 1227 files (Pentax, Panasonic, FujiFilm and the corpus root), 105708 compared Group1:Name pairs: 5 fixed, 0 regressed. Co-Authored-By: Claude Opus 5 --- .../makernotes/fujifilm/settings_tables.rs | 13 ++ .../tiff/makernotes/panasonic/face_tables.rs | 16 ++ src/parsers/tiff/makernotes/pentax.rs | 135 +++++++++++++- .../tiff/makernotes/pentax/subdir_tables.rs | 168 ++++++++++++++++++ .../tiff/makernotes/shared/binary_subdir.rs | 30 +++- src/parsers/tiff/makernotes/shared/mod.rs | 2 + .../tiff/makernotes/shared/tag_priority.rs | 138 ++++++++++++++ tools/exiftool-tables/codegen_subdirs.py | 67 +++++-- 8 files changed, 552 insertions(+), 17 deletions(-) create mode 100644 src/parsers/tiff/makernotes/shared/tag_priority.rs diff --git a/src/parsers/tiff/makernotes/fujifilm/settings_tables.rs b/src/parsers/tiff/makernotes/fujifilm/settings_tables.rs index 1ed7e01ea..976e33708 100644 --- a/src/parsers/tiff/makernotes/fujifilm/settings_tables.rs +++ b/src/parsers/tiff/makernotes/fujifilm/settings_tables.rs @@ -48,6 +48,7 @@ pub(crate) static FUJIFILM_PRIORITYSETTINGS: BinaryTable = BinaryTable { mask: Some(15), value_conv: ValueConv::None, print_conv: PrintConv::Map(FUJIFILM_CONV1), + low_priority: false, }, Field { key: "0.2", @@ -61,6 +62,7 @@ pub(crate) static FUJIFILM_PRIORITYSETTINGS: BinaryTable = BinaryTable { mask: Some(240), value_conv: ValueConv::None, print_conv: PrintConv::Map(FUJIFILM_CONV1), + low_priority: false, }, ], }; @@ -86,6 +88,7 @@ pub(crate) static FUJIFILM_FOCUSSETTINGS: BinaryTable = BinaryTable { mask: Some(15), value_conv: ValueConv::None, print_conv: PrintConv::Map(FUJIFILM_CONV2), + low_priority: false, }, Field { key: "0.2", @@ -99,6 +102,7 @@ pub(crate) static FUJIFILM_FOCUSSETTINGS: BinaryTable = BinaryTable { mask: Some(240), value_conv: ValueConv::None, print_conv: PrintConv::Map(FUJIFILM_CONV3), + low_priority: false, }, Field { key: "0.3", @@ -112,6 +116,7 @@ pub(crate) static FUJIFILM_FOCUSSETTINGS: BinaryTable = BinaryTable { mask: Some(3840), value_conv: ValueConv::None, print_conv: PrintConv::Map(FUJIFILM_CONV4), + low_priority: false, }, Field { key: "0.4", @@ -125,6 +130,7 @@ pub(crate) static FUJIFILM_FOCUSSETTINGS: BinaryTable = BinaryTable { mask: Some(61440), value_conv: ValueConv::None, print_conv: PrintConv::MapOr(FUJIFILM_CONV5, super::print_conv::identity), + low_priority: false, }, Field { key: "0.5", @@ -138,6 +144,7 @@ pub(crate) static FUJIFILM_FOCUSSETTINGS: BinaryTable = BinaryTable { mask: Some(16711680), value_conv: ValueConv::None, print_conv: PrintConv::MapOr(FUJIFILM_CONV5, super::print_conv::zone_size), + low_priority: false, }, ], }; @@ -163,6 +170,7 @@ pub(crate) static FUJIFILM_AFCSETTINGS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::MapOr(FUJIFILM_CONV6, super::print_conv::custom_afc_set), + low_priority: false, }, Field { key: "0.1", @@ -176,6 +184,7 @@ pub(crate) static FUJIFILM_AFCSETTINGS: BinaryTable = BinaryTable { mask: Some(15), value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "0.2", @@ -189,6 +198,7 @@ pub(crate) static FUJIFILM_AFCSETTINGS: BinaryTable = BinaryTable { mask: Some(240), value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "0.3", @@ -202,6 +212,7 @@ pub(crate) static FUJIFILM_AFCSETTINGS: BinaryTable = BinaryTable { mask: Some(3840), value_conv: ValueConv::None, print_conv: PrintConv::Map(FUJIFILM_CONV7), + low_priority: false, }, ], }; @@ -227,6 +238,7 @@ pub(crate) static FUJIFILM_DRIVESETTINGS: BinaryTable = BinaryTable { mask: Some(255), value_conv: ValueConv::None, print_conv: PrintConv::Map(FUJIFILM_CONV8), + low_priority: false, }, Field { key: "0.2", @@ -240,6 +252,7 @@ pub(crate) static FUJIFILM_DRIVESETTINGS: BinaryTable = BinaryTable { mask: Some(4278190080), value_conv: ValueConv::None, print_conv: PrintConv::MapOr(FUJIFILM_CONV5, super::print_conv::fps), + low_priority: false, }, ], }; diff --git a/src/parsers/tiff/makernotes/panasonic/face_tables.rs b/src/parsers/tiff/makernotes/panasonic/face_tables.rs index fb3ae60b8..d619d1e10 100644 --- a/src/parsers/tiff/makernotes/panasonic/face_tables.rs +++ b/src/parsers/tiff/makernotes/panasonic/face_tables.rs @@ -32,6 +32,7 @@ pub(crate) static PANASONIC_FACEDETINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "1", @@ -45,6 +46,7 @@ pub(crate) static PANASONIC_FACEDETINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "5", @@ -58,6 +60,7 @@ pub(crate) static PANASONIC_FACEDETINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "9", @@ -71,6 +74,7 @@ pub(crate) static PANASONIC_FACEDETINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "13", @@ -84,6 +88,7 @@ pub(crate) static PANASONIC_FACEDETINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "17", @@ -97,6 +102,7 @@ pub(crate) static PANASONIC_FACEDETINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, ], }; @@ -122,6 +128,7 @@ pub(crate) static PANASONIC_FACERECINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "4", @@ -135,6 +142,7 @@ pub(crate) static PANASONIC_FACERECINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "24", @@ -148,6 +156,7 @@ pub(crate) static PANASONIC_FACERECINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "32", @@ -161,6 +170,7 @@ pub(crate) static PANASONIC_FACERECINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "52", @@ -174,6 +184,7 @@ pub(crate) static PANASONIC_FACERECINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "72", @@ -187,6 +198,7 @@ pub(crate) static PANASONIC_FACERECINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "80", @@ -200,6 +212,7 @@ pub(crate) static PANASONIC_FACERECINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "100", @@ -213,6 +226,7 @@ pub(crate) static PANASONIC_FACERECINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "120", @@ -226,6 +240,7 @@ pub(crate) static PANASONIC_FACERECINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "128", @@ -239,6 +254,7 @@ pub(crate) static PANASONIC_FACERECINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, ], }; diff --git a/src/parsers/tiff/makernotes/pentax.rs b/src/parsers/tiff/makernotes/pentax.rs index daaa205f4..cc3951cdd 100644 --- a/src/parsers/tiff/makernotes/pentax.rs +++ b/src/parsers/tiff/makernotes/pentax.rs @@ -47,6 +47,7 @@ use super::shared::MakerNoteParser; use super::shared::array_extractors::{extract_i16_array, extract_u16_array, extract_u32_array}; use super::shared::binary_subdir::{self, BinaryTable, Cond, ModelPat}; use super::shared::generic_decoders::ON_OFF; +use super::shared::tag_priority::insert_low_priority; use subdir_tables::{ PENTAX_AFINFO, PENTAX_AWBINFO, PENTAX_BATTERYINFO, PENTAX_EVSTEPINFO, PENTAX_FACEINFO, PENTAX_FACEPOS, PENTAX_FACESIZE, PENTAX_FILTERINFO, PENTAX_FLASHINFO, PENTAX_KELVINWB, @@ -1092,7 +1093,11 @@ impl PentaxParser { let sub_id = raw[1] as u16; let name = lookup_lens_type_pair(series, sub_id) .unwrap_or_else(|| format!("Unknown ({} {})", series, sub_id)); - tags.insert("Pentax:LensType".to_string(), name); + // `%Pentax::LensRec` key 0 is `Priority => 0` + // (Pentax.pm:4202), as is the 0x0207 copy below. Between + // two 0-priority instances ExifTool keeps the first + // (ExifTool.pm:9541-9551), and 0x003f is read first. + insert_low_priority(tags, "Pentax:LensType".to_string(), name); } if raw.len() >= 4 { let extender = if raw[3] == 0 { @@ -1785,7 +1790,13 @@ impl PentaxParser { let series = raw[0] & 0x0f; let sub_id = (raw[2] as u16) * 256 + raw[3] as u16; if let Some(name) = lookup_lens_type_pair(series, sub_id) { - tags.insert("Pentax:LensType".to_string(), name); + // `Priority => 0` in every `%Pentax::LensInfo*` + // (Pentax.pm:4226, :4248, :4284, :4320, :4357): the + // 0x003f copy read earlier is the one that prints. + // PentaxK100D.jpg carries `0 0 0 0` here, which + // decodes to a real lens name ("M-42 or No Lens") + // and so overwrote the correct one silently. + insert_low_priority(tags, "Pentax:LensType".to_string(), name); } } if raw.len() >= 4 + 17 { @@ -1812,7 +1823,10 @@ impl PentaxParser { let focal_raw = ld[9] as i32; let focal = 10.0 * (focal_raw >> 2) as f64 * 4f64.powi((focal_raw & 0x03) - 2); - tags.insert( + // `%Pentax::LensData` key 9 is `Priority => 0` + // (Pentax.pm:4506). + insert_low_priority( + tags, "Pentax:LensFocalLength".to_string(), format!("{:.1} mm", focal), ); @@ -1850,7 +1864,16 @@ impl PentaxParser { }; let model_id = read_u32(&raw[0..4]); if let Some(name) = pentax_model_id_name(model_id) { - tags.insert("Pentax:PentaxModelID".to_string(), name.to_string()); + // `%Pentax::CameraInfo` key 0 is `Priority => 0`, + // with ExifTool's own reason on the line: + // "(Optio SVi uses incorrect Optio SV ID here)" + // (Pentax.pm:4723). The 0x0005 copy read earlier is + // the one that prints. + insert_low_priority( + tags, + "Pentax:PentaxModelID".to_string(), + name.to_string(), + ); } let manufacture_date = read_u32(&raw[4..8]); let date_str = manufacture_date.to_string(); @@ -2933,6 +2956,110 @@ fn pentax_model_id_name(id: u32) -> Option<&'static str> { mod tests { use super::*; + /// A "PENTAX \0" MakerNote block carrying `entries` (tag, type, count, + /// value-or-offset) big-endian, plus `trailer` laid down at offset 64. + /// + /// Offsets in a "PENTAX \0" MakerNote are measured from the block itself + /// (`Base => '$start - 10'`), so a value at index 64 of the returned buffer + /// is addressed as 64. + #[cfg(test)] + fn pentax_block(entries: &[(u16, u16, u32, u32)], trailer: &[u8]) -> Vec { + let mut out = Vec::new(); + out.extend_from_slice(PENTAX_HEADER_PENTAX); // 0..8 + out.extend_from_slice(b"MM"); // 8..10 + out.extend_from_slice(&(entries.len() as u16).to_be_bytes()); // 10..12 + for &(tag, ftype, count, value) in entries { + out.extend_from_slice(&tag.to_be_bytes()); + out.extend_from_slice(&ftype.to_be_bytes()); + out.extend_from_slice(&count.to_be_bytes()); + out.extend_from_slice(&value.to_be_bytes()); + } + out.extend_from_slice(&0u32.to_be_bytes()); // next-IFD pointer + out.resize(64, 0); + out.extend_from_slice(trailer); + out + } + + /// ExifTool's `Priority => 0`, end to end through the parse loop. + /// + /// Both of these ids are read by the same walk, in ascending order, and + /// both write a tag the `Main` table already reported. Before the priority + /// rule was applied they simply overwrote it, which is not a missing tag + /// or a crash -- it is a real ExifTool tag name carrying the wrong value. + /// + /// The bytes are the ones `exiftool -v3` prints for two corpus files: + /// + /// * `Pentax/PentaxK100D.jpg` -- `LensRec` (0x003f) holds `7 244` and + /// `LensInfo` (0x0207) holds `0 0 0 0`. Both tables are `Priority => 0` + /// (Pentax.pm:4202, :4248), so ExifTool keeps the first (ExifTool.pm:9541-9551) + /// and reports `smc PENTAX-DA 21mm F3.2 AL Limited`. `0 0` is a real lens + /// id, not a null -- it decodes to `M-42 or No Lens`. + /// * `Pentax/PentaxOptioSVi.jpg` -- `Main` 0x0005 holds 76405 and + /// `CameraInfo` (0x0215) holds 76400. ExifTool marks only the latter + /// `Priority => 0`, with its reason on the line: "(Optio SVi uses + /// incorrect Optio SV ID here)" (Pentax.pm:4723). ExifTool reports + /// `Optio SVi`. + #[test] + fn low_priority_subdirectory_does_not_overwrite_the_main_table() { + let mut camera_info = Vec::new(); + camera_info.extend_from_slice(&76_400u32.to_be_bytes()); // PentaxModelID + camera_info.extend_from_slice(&20_040_101u32.to_be_bytes()); // ManufactureDate + camera_info.extend_from_slice(&1u32.to_be_bytes()); // ProductionCode major + camera_info.extend_from_slice(&0u32.to_be_bytes()); // ProductionCode minor + camera_info.extend_from_slice(&7u32.to_be_bytes()); // InternalSerialNumber + + let data = pentax_block( + &[ + // 0x0005 PentaxModelID, LONG: 76405 -> "Optio SVi" + (0x0005, 4, 1, 76_405), + // 0x003f LensRec, 4 BYTEs `07 f4 00 00` -> LensType "7 244" + (0x003F, 1, 4, 0x07F4_0000), + // 0x0207 LensInfo, 4 BYTEs `00 00 00 00` -> LensType "0 0" + (0x0207, 1, 4, 0x0000_0000), + // 0x0215 CameraInfo, 5 LONGs at offset 64 + (0x0215, 4, 5, 64), + ], + &camera_info, + ); + + let mut tags = HashMap::new(); + PentaxParser::default() + .parse(&data, ByteOrder::BigEndian, &mut tags) + .expect("Pentax MakerNote should parse"); + + assert_eq!( + tags.get("Pentax:LensType").map(String::as_str), + Some("smc PENTAX-DA 21mm F3.2 AL Limited"), + "0x0207 LensInfo is Priority => 0 and must not overwrite 0x003f LensRec" + ); + assert_eq!( + tags.get("Pentax:PentaxModelID").map(String::as_str), + Some("Optio SVi"), + "0x0215 CameraInfo is Priority => 0 and must not overwrite Main 0x0005" + ); + } + + /// The rule suppresses a clobber, not the tag: with no `Main` copy present, + /// the `Priority => 0` sub-directory value is still the one reported. + #[test] + fn low_priority_subdirectory_still_reports_when_it_is_the_only_source() { + let mut camera_info = Vec::new(); + camera_info.extend_from_slice(&76_400u32.to_be_bytes()); + camera_info.resize(20, 0); + + let data = pentax_block(&[(0x0215, 4, 5, 64)], &camera_info); + + let mut tags = HashMap::new(); + PentaxParser::default() + .parse(&data, ByteOrder::BigEndian, &mut tags) + .expect("Pentax MakerNote should parse"); + + assert_eq!( + tags.get("Pentax:PentaxModelID").map(String::as_str), + Some("Optio SV"), + ); + } + #[test] fn test_decode_quality() { assert_eq!(QUALITY.decode(2), "Best"); diff --git a/src/parsers/tiff/makernotes/pentax/subdir_tables.rs b/src/parsers/tiff/makernotes/pentax/subdir_tables.rs index 1c13ed8a7..88f64f73c 100644 --- a/src/parsers/tiff/makernotes/pentax/subdir_tables.rs +++ b/src/parsers/tiff/makernotes/pentax/subdir_tables.rs @@ -280,6 +280,7 @@ pub(crate) static PENTAX_SRINFO2: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV1), + low_priority: false, }], }; @@ -304,6 +305,7 @@ pub(crate) static PENTAX_FACEINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "2", @@ -317,6 +319,7 @@ pub(crate) static PENTAX_FACEINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, ], }; @@ -342,6 +345,7 @@ pub(crate) static PENTAX_AWBINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV2), + low_priority: false, }, Field { key: "1", @@ -355,6 +359,7 @@ pub(crate) static PENTAX_AWBINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV3), + low_priority: false, }, ], }; @@ -380,6 +385,7 @@ pub(crate) static PENTAX_TIMEINFO: BinaryTable = BinaryTable { mask: Some(1), value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV4), + low_priority: false, }, Field { key: "0.2", @@ -393,6 +399,7 @@ pub(crate) static PENTAX_TIMEINFO: BinaryTable = BinaryTable { mask: Some(2), value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV5), + low_priority: false, }, Field { key: "0.3", @@ -406,6 +413,7 @@ pub(crate) static PENTAX_TIMEINFO: BinaryTable = BinaryTable { mask: Some(4), value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV5), + low_priority: false, }, Field { key: "2", @@ -419,6 +427,7 @@ pub(crate) static PENTAX_TIMEINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV6), + low_priority: false, }, Field { key: "3", @@ -432,6 +441,7 @@ pub(crate) static PENTAX_TIMEINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV6), + low_priority: false, }, ], }; @@ -457,6 +467,7 @@ pub(crate) static PENTAX_LENSCORR: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV2), + low_priority: false, }, Field { key: "1", @@ -470,6 +481,7 @@ pub(crate) static PENTAX_LENSCORR: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV2), + low_priority: false, }, Field { key: "2", @@ -483,6 +495,7 @@ pub(crate) static PENTAX_LENSCORR: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV2), + low_priority: false, }, Field { key: "3", @@ -496,6 +509,7 @@ pub(crate) static PENTAX_LENSCORR: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV7), + low_priority: false, }, ], }; @@ -521,6 +535,7 @@ pub(crate) static PENTAX_FLASHINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV8), + low_priority: false, }, Field { key: "1", @@ -534,6 +549,7 @@ pub(crate) static PENTAX_FLASHINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV9), + low_priority: false, }, Field { key: "2", @@ -547,6 +563,7 @@ pub(crate) static PENTAX_FLASHINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV10), + low_priority: false, }, Field { key: "3", @@ -560,6 +577,7 @@ pub(crate) static PENTAX_FLASHINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "4", @@ -573,6 +591,7 @@ pub(crate) static PENTAX_FLASHINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "5", @@ -586,6 +605,7 @@ pub(crate) static PENTAX_FLASHINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "6", @@ -599,6 +619,7 @@ pub(crate) static PENTAX_FLASHINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "7", @@ -612,6 +633,7 @@ pub(crate) static PENTAX_FLASHINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "25", @@ -625,6 +647,7 @@ pub(crate) static PENTAX_FLASHINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV11), + low_priority: false, }, Field { key: "26", @@ -638,6 +661,7 @@ pub(crate) static PENTAX_FLASHINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV12), + low_priority: false, }, ], }; @@ -663,6 +687,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "5", @@ -676,6 +701,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "9", @@ -689,6 +715,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "13", @@ -702,6 +729,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "17", @@ -715,6 +743,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "21", @@ -728,6 +757,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "25", @@ -741,6 +771,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "29", @@ -754,6 +785,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "33", @@ -767,6 +799,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "37", @@ -780,6 +813,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "41", @@ -793,6 +827,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "45", @@ -806,6 +841,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "49", @@ -819,6 +855,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "53", @@ -832,6 +869,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "57", @@ -845,6 +883,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "61", @@ -858,6 +897,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "65", @@ -871,6 +911,7 @@ pub(crate) static PENTAX_KELVINWB: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::List(super::value_conv::kelvin_wb), print_conv: PrintConv::None, + low_priority: false, }, ], }; @@ -896,6 +937,7 @@ pub(crate) static PENTAX_EVSTEPINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV13), + low_priority: false, }, Field { key: "1", @@ -909,6 +951,7 @@ pub(crate) static PENTAX_EVSTEPINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV14), + low_priority: false, }, Field { key: "3", @@ -922,6 +965,7 @@ pub(crate) static PENTAX_EVSTEPINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV2), + low_priority: false, }, ], }; @@ -947,6 +991,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "2", @@ -960,6 +1005,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "4", @@ -973,6 +1019,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "6", @@ -986,6 +1033,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "8", @@ -999,6 +1047,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "10", @@ -1012,6 +1061,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "12", @@ -1025,6 +1075,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "14", @@ -1038,6 +1089,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "16", @@ -1051,6 +1103,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "18", @@ -1064,6 +1117,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "20", @@ -1077,6 +1131,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "22", @@ -1090,6 +1145,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "24", @@ -1103,6 +1159,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "26", @@ -1116,6 +1173,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "28", @@ -1129,6 +1187,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "30", @@ -1142,6 +1201,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "32", @@ -1155,6 +1215,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "34", @@ -1168,6 +1229,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "36", @@ -1181,6 +1243,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "38", @@ -1194,6 +1257,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "40", @@ -1207,6 +1271,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "42", @@ -1220,6 +1285,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "44", @@ -1233,6 +1299,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "46", @@ -1246,6 +1313,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "48", @@ -1259,6 +1327,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "50", @@ -1272,6 +1341,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "52", @@ -1285,6 +1355,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "54", @@ -1298,6 +1369,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "56", @@ -1311,6 +1383,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "58", @@ -1324,6 +1397,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "60", @@ -1337,6 +1411,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "62", @@ -1350,6 +1425,7 @@ pub(crate) static PENTAX_FACEPOS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, ], }; @@ -1375,6 +1451,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "2", @@ -1388,6 +1465,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "4", @@ -1401,6 +1479,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "6", @@ -1414,6 +1493,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "8", @@ -1427,6 +1507,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "10", @@ -1440,6 +1521,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "12", @@ -1453,6 +1535,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "14", @@ -1466,6 +1549,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "16", @@ -1479,6 +1563,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "18", @@ -1492,6 +1577,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "20", @@ -1505,6 +1591,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "22", @@ -1518,6 +1605,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "24", @@ -1531,6 +1619,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "26", @@ -1544,6 +1633,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "28", @@ -1557,6 +1647,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "30", @@ -1570,6 +1661,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "32", @@ -1583,6 +1675,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "34", @@ -1596,6 +1689,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "36", @@ -1609,6 +1703,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "38", @@ -1622,6 +1717,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "40", @@ -1635,6 +1731,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "42", @@ -1648,6 +1745,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "44", @@ -1661,6 +1759,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "46", @@ -1674,6 +1773,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "48", @@ -1687,6 +1787,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "50", @@ -1700,6 +1801,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "52", @@ -1713,6 +1815,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "54", @@ -1726,6 +1829,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "56", @@ -1739,6 +1843,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "58", @@ -1752,6 +1857,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "60", @@ -1765,6 +1871,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "62", @@ -1778,6 +1885,7 @@ pub(crate) static PENTAX_FACESIZE: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, ], }; @@ -1803,6 +1911,7 @@ pub(crate) static PENTAX_LEVELINFO: BinaryTable = BinaryTable { mask: Some(15), value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV15), + low_priority: false, }, Field { key: "0.1", @@ -1816,6 +1925,7 @@ pub(crate) static PENTAX_LEVELINFO: BinaryTable = BinaryTable { mask: Some(240), value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV16), + low_priority: false, }, Field { key: "1", @@ -1829,6 +1939,7 @@ pub(crate) static PENTAX_LEVELINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::Each(super::value_conv::negate_half), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "2", @@ -1842,6 +1953,7 @@ pub(crate) static PENTAX_LEVELINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::Each(super::value_conv::negate_half), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "5", @@ -1855,6 +1967,7 @@ pub(crate) static PENTAX_LEVELINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::Each(super::value_conv::negate), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "6", @@ -1868,6 +1981,7 @@ pub(crate) static PENTAX_LEVELINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::Each(super::value_conv::negate), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "7", @@ -1881,6 +1995,7 @@ pub(crate) static PENTAX_LEVELINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::Each(super::value_conv::negate_half), print_conv: PrintConv::None, + low_priority: false, }, ], }; @@ -1906,6 +2021,7 @@ pub(crate) static PENTAX_WBLEVELS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "11", @@ -1919,6 +2035,7 @@ pub(crate) static PENTAX_WBLEVELS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "20", @@ -1932,6 +2049,7 @@ pub(crate) static PENTAX_WBLEVELS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "29", @@ -1945,6 +2063,7 @@ pub(crate) static PENTAX_WBLEVELS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "38", @@ -1958,6 +2077,7 @@ pub(crate) static PENTAX_WBLEVELS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "47", @@ -1971,6 +2091,7 @@ pub(crate) static PENTAX_WBLEVELS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "56", @@ -1984,6 +2105,7 @@ pub(crate) static PENTAX_WBLEVELS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "65", @@ -1997,6 +2119,7 @@ pub(crate) static PENTAX_WBLEVELS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "74", @@ -2010,6 +2133,7 @@ pub(crate) static PENTAX_WBLEVELS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "92", @@ -2023,6 +2147,7 @@ pub(crate) static PENTAX_WBLEVELS: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, ], }; @@ -2047,6 +2172,7 @@ pub(crate) static PENTAX_LENSINFOQ: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }], }; @@ -2071,6 +2197,7 @@ pub(crate) static PENTAX_AFINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "6", @@ -2084,6 +2211,7 @@ pub(crate) static PENTAX_AFINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "7", @@ -2097,6 +2225,7 @@ pub(crate) static PENTAX_AFINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::Each(super::value_conv::times_2), print_conv: PrintConv::Expr(super::print_conv::millis), + low_priority: false, }, Field { key: "11", @@ -2138,6 +2267,7 @@ pub(crate) static PENTAX_AFINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV17), + low_priority: false, }, Field { key: "506", @@ -2157,6 +2287,7 @@ pub(crate) static PENTAX_AFINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV2), + low_priority: false, }, Field { key: "509", @@ -2170,6 +2301,7 @@ pub(crate) static PENTAX_AFINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV18), + low_priority: false, }, Field { key: "543", @@ -2189,6 +2321,7 @@ pub(crate) static PENTAX_AFINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV19), + low_priority: false, }, Field { key: "544", @@ -2208,6 +2341,7 @@ pub(crate) static PENTAX_AFINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV20), + low_priority: false, }, Field { key: "545", @@ -2227,6 +2361,7 @@ pub(crate) static PENTAX_AFINFO: BinaryTable = BinaryTable { mask: Some(3), value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV21), + low_priority: false, }, Field { key: "545.1", @@ -2246,6 +2381,7 @@ pub(crate) static PENTAX_AFINFO: BinaryTable = BinaryTable { mask: Some(12), value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV22), + low_priority: false, }, Field { key: "545.2", @@ -2265,6 +2401,7 @@ pub(crate) static PENTAX_AFINFO: BinaryTable = BinaryTable { mask: Some(112), value_conv: ValueConv::None, print_conv: PrintConv::Expr(super::print_conv::five_minus), + low_priority: false, }, Field { key: "2400", @@ -2284,6 +2421,7 @@ pub(crate) static PENTAX_AFINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV2), + low_priority: false, }, ], }; @@ -2315,6 +2453,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: Some(15), value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV23), + low_priority: false, }, Field { key: "0.1", @@ -2328,6 +2467,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: Some(15), value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV24), + low_priority: false, }, Field { key: "1.1", @@ -2385,6 +2525,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: Some(240), value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV25), + low_priority: false, }, Field { key: "1.1", @@ -2418,6 +2559,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: Some(240), value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV26), + low_priority: false, }, Field { key: "1.2", @@ -2451,6 +2593,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: Some(15), value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV25), + low_priority: false, }, Field { key: "2", @@ -2484,6 +2627,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Expr(super::print_conv::ad_no_load), + low_priority: false, }, Field { key: "2", @@ -2525,6 +2669,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "2", @@ -2609,6 +2754,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::Each(super::value_conv::div_100), print_conv: PrintConv::Expr(super::print_conv::volts_2dp), + low_priority: false, }, Field { key: "2", @@ -2628,6 +2774,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV27), + low_priority: false, }, Field { key: "3", @@ -2661,6 +2808,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Expr(super::print_conv::ad_load), + low_priority: false, }, Field { key: "3", @@ -2690,6 +2838,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "3", @@ -2709,6 +2858,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "4", @@ -2758,6 +2908,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "4", @@ -2842,6 +2993,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::Each(super::value_conv::div_100), print_conv: PrintConv::Expr(super::print_conv::volts_2dp), + low_priority: false, }, Field { key: "4", @@ -2861,6 +3013,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::Each(super::value_conv::k3_iii_voltage), print_conv: PrintConv::Expr(super::print_conv::volts_2dp), + low_priority: false, }, Field { key: "5", @@ -2898,6 +3051,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "6", @@ -2927,6 +3081,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::Each(super::value_conv::div_100), print_conv: PrintConv::Expr(super::print_conv::volts_2dp), + low_priority: false, }, Field { key: "8", @@ -2952,6 +3107,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::Each(super::value_conv::div_100), print_conv: PrintConv::Expr(super::print_conv::volts_2dp), + low_priority: false, }, Field { key: "16", @@ -2971,6 +3127,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV27), + low_priority: false, }, Field { key: "17", @@ -2990,6 +3147,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "18", @@ -3009,6 +3167,7 @@ pub(crate) static PENTAX_BATTERYINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::Each(super::value_conv::k3_iii_voltage), print_conv: PrintConv::Expr(super::print_conv::volts_2dp), + low_priority: false, }, ], }; @@ -3040,6 +3199,7 @@ pub(crate) static PENTAX_TEMPINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::Each(super::value_conv::plus_1), print_conv: PrintConv::None, + low_priority: false, }, Field { key: "12", @@ -3059,6 +3219,7 @@ pub(crate) static PENTAX_TEMPINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::Each(super::value_conv::div_10), print_conv: PrintConv::Expr(super::print_conv::celsius_1dp), + low_priority: false, }, Field { key: "14", @@ -3078,6 +3239,7 @@ pub(crate) static PENTAX_TEMPINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::Each(super::value_conv::div_10), print_conv: PrintConv::Expr(super::print_conv::celsius_1dp), + low_priority: false, }, Field { key: "20", @@ -3097,6 +3259,7 @@ pub(crate) static PENTAX_TEMPINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Expr(super::print_conv::celsius), + low_priority: false, }, Field { key: "22", @@ -3116,6 +3279,7 @@ pub(crate) static PENTAX_TEMPINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Expr(super::print_conv::celsius), + low_priority: false, }, Field { key: "42", @@ -3135,6 +3299,7 @@ pub(crate) static PENTAX_TEMPINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::Each(super::value_conv::div_10), print_conv: PrintConv::Expr(super::print_conv::celsius_1dp), + low_priority: false, }, ], }; @@ -3179,6 +3344,7 @@ pub(crate) static PENTAX_SHOTINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(PENTAX_CONV28), + low_priority: false, }], }; @@ -3203,6 +3369,7 @@ pub(crate) static PENTAX_FILTERINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "2", @@ -3216,6 +3383,7 @@ pub(crate) static PENTAX_FILTERINFO: BinaryTable = BinaryTable { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, ], }; diff --git a/src/parsers/tiff/makernotes/shared/binary_subdir.rs b/src/parsers/tiff/makernotes/shared/binary_subdir.rs index fe5287223..aff211413 100644 --- a/src/parsers/tiff/makernotes/shared/binary_subdir.rs +++ b/src/parsers/tiff/makernotes/shared/binary_subdir.rs @@ -45,6 +45,7 @@ use std::collections::HashMap; +use super::tag_priority; use crate::parsers::tiff::ifd_parser::ByteOrder; /// How the bytes at a field's offset are read. @@ -225,6 +226,13 @@ pub(crate) struct Field { /// Runs after `Mask` and before `print_conv`, as in ExifTool. pub(crate) value_conv: ValueConv, pub(crate) print_conv: PrintConv, + /// ExifTool's `Priority => 0` (or `Avoid => 1`, which implies it at + /// `ExifTool.pm:9472`): this field never displaces a value already reported + /// under the same name. A sub-directory copy of a tag the vendor's `Main` + /// table also carries is normally marked this way, so that the `Main` copy + /// is the one that prints. See + /// [`shared::tag_priority`](super::tag_priority). + pub(crate) low_priority: bool, } /// A `ProcessBinaryData` table. @@ -470,7 +478,15 @@ pub(crate) fn decode_binary_subdir_with( if let (Some(member), Some(value)) = (field.set_member, first_num) { members.insert(member, value); } - tags.insert(format!("{prefix}:{}", field.name), parts.join(" ")); + let key = format!("{prefix}:{}", field.name); + let value = parts.join(" "); + if field.low_priority { + // ExifTool's `Priority => 0`: a sub-directory copy of a tag the + // `Main` table also reports must not overwrite it. + tag_priority::insert_low_priority(tags, key, value); + } else { + tags.insert(key, value); + } } } @@ -497,6 +513,7 @@ mod tests { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "1", @@ -510,6 +527,7 @@ mod tests { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "5", @@ -523,6 +541,7 @@ mod tests { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, ], }; @@ -601,6 +620,7 @@ mod tests { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }], }; let mut tags = HashMap::new(); @@ -633,6 +653,7 @@ mod tests { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(T_CONV), + low_priority: false, }, Field { key: "1", @@ -646,6 +667,7 @@ mod tests { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::Map(T_CONV), + low_priority: false, }, ], }; @@ -677,6 +699,7 @@ mod tests { mask: Some(0x000f), value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { // ExifTool's own key for a second tag at one offset: `0.1`, @@ -693,6 +716,7 @@ mod tests { mask: Some(0xf000), value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, ], }; @@ -795,6 +819,7 @@ mod tests { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, Field { key: "0", @@ -820,6 +845,7 @@ mod tests { mask: None, value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, // A different key at the same offset: a masked neighbour, not // an alternative, so it is decoded independently. @@ -835,6 +861,7 @@ mod tests { mask: Some(0xf0), value_conv: ValueConv::None, print_conv: PrintConv::None, + low_priority: false, }, ], }; @@ -896,6 +923,7 @@ mod tests { mask: None, value_conv: ValueConv::Each(div_100), print_conv: PrintConv::Expr(volts), + low_priority: false, }], }; let mut tags = HashMap::new(); diff --git a/src/parsers/tiff/makernotes/shared/mod.rs b/src/parsers/tiff/makernotes/shared/mod.rs index 8ac3fa7d1..80778846b 100644 --- a/src/parsers/tiff/makernotes/shared/mod.rs +++ b/src/parsers/tiff/makernotes/shared/mod.rs @@ -49,6 +49,8 @@ pub mod print_im; /// Table-driven walker for plain TIFF-style MakerNote IFDs. pub mod table_ifd; +/// ExifTool's `Priority`, for a tag one file reports more than once +pub mod tag_priority; pub mod tag_registry; /// Common value interpretation and decoding functions /// Common value extraction helpers (strings, integers) diff --git a/src/parsers/tiff/makernotes/shared/tag_priority.rs b/src/parsers/tiff/makernotes/shared/tag_priority.rs new file mode 100644 index 000000000..74ca79be3 --- /dev/null +++ b/src/parsers/tiff/makernotes/shared/tag_priority.rs @@ -0,0 +1,138 @@ +//! ExifTool's tag priority, for the case where one file reports the same tag +//! name twice. +//! +//! A MakerNote routinely carries the same tag in two places: once in the +//! vendor's `Main` table and once inside a binary sub-directory. ExifTool keeps +//! both -- they are separate tag keys, `LensType` and `LensType (1)` -- but only +//! one of them is the tag that prints under the plain name, and `Priority` +//! decides which. oxidex reports one value per `Group:Name`, so the same +//! decision has to be made at insert time. +//! +//! # ExifTool's rule +//! +//! `Image::ExifTool::FoundTag` (`ExifTool.pm:9448`) reads the tag's priority at +//! `:9469-9473` -- the tag's own `Priority`, else the table's `PRIORITY`, else +//! `0` when the tag is marked `Avoid`. When the name is already present +//! (`:9515`) it compares against the priority already recorded for it: +//! +//! ```text +//! my $oldPriority = $$self{PRIORITY}{$tag}; # ExifTool.pm:9544 +//! unless ($oldPriority) { ... $oldPriority = 1; } # :9545-9551 +//! ... +//! if ($priority >= $oldPriority and ...) { # :9564 +//! # the new value takes the plain name, the old one moves to "$tag (1)" +//! } else { +//! $tag = $nextTag; # :9585 +//! # the new value goes to "$tag (1)"; the old one keeps the plain name +//! } +//! ``` +//! +//! Two details make the rule what it is. A value stored with priority 0 records +//! no entry in `PRIORITY` at all (`:9589` stores it "only if exists and is +//! non-zero"), and a missing entry is *promoted to 1* at `:9545-9551` -- the +//! comment there reads "promote existing 0-priority tag so it takes precedence +//! over a new 0-tag". So the comparison at `:9564` resolves the four possible +//! orderings like this: +//! +//! | first | second | winner | +//! |-------|--------|--------| +//! | normal | `Priority => 0` | first — `0 >= 1` is false | +//! | `Priority => 0` | normal | second — old promoted to 1, `1 >= 1` holds | +//! | `Priority => 0` | `Priority => 0` | first — old promoted to 1, `0 >= 1` is false | +//! | normal | normal | second — `1 >= 1` holds | +//! +//! Which is exactly two rules, and they compose to the same outcome for any +//! number of instances in any order: +//! +//! * a `Priority => 0` value never displaces a value already present; +//! * a normal-priority value always displaces whatever is present. +//! +//! The second is what a plain [`HashMap::insert`] already does, so only the +//! first needs a function. Writing a `Priority => 0` tag with `insert` is the +//! bug this exists to prevent: it does not fail, it does not drop a tag, it +//! silently prints the sub-directory's value under a real ExifTool tag name. +//! +//! [`HashMap::insert`]: std::collections::HashMap::insert + +use std::collections::HashMap; + +/// Records a value ExifTool declares `Priority => 0`. +/// +/// Keeps whatever is already under `key` and discards `value`; stores `value` +/// only when the name has not been reported yet. See the module documentation +/// for why this, plus an ordinary `insert` for every normal-priority tag, +/// reproduces `FoundTag`'s comparison exactly. +pub(crate) fn insert_low_priority(tags: &mut HashMap, key: String, value: String) { + tags.entry(key).or_insert(value); +} + +#[cfg(test)] +mod tests { + use super::*; + + fn map(pairs: &[(&str, &str)]) -> HashMap { + pairs + .iter() + .map(|(k, v)| ((*k).to_string(), (*v).to_string())) + .collect() + } + + /// `normal` then `Priority => 0`: `0 >= 1` is false, so the first value + /// keeps the plain tag name (ExifTool.pm:9564, :9585). + #[test] + fn low_priority_does_not_displace_a_present_value() { + let mut tags = map(&[("Pentax:LensType", "smc PENTAX-DA 21mm F3.2 AL Limited")]); + insert_low_priority( + &mut tags, + "Pentax:LensType".to_string(), + "M-42 or No Lens".to_string(), + ); + assert_eq!( + tags["Pentax:LensType"], + "smc PENTAX-DA 21mm F3.2 AL Limited" + ); + } + + /// Two `Priority => 0` instances: the first is promoted to 1 when the + /// second arrives, so the first still wins (ExifTool.pm:9541-9551). + #[test] + fn first_of_two_low_priority_values_wins() { + let mut tags = HashMap::new(); + insert_low_priority( + &mut tags, + "Pentax:LensType".to_string(), + "first".to_string(), + ); + insert_low_priority( + &mut tags, + "Pentax:LensType".to_string(), + "second".to_string(), + ); + assert_eq!(tags["Pentax:LensType"], "first"); + } + + /// A `Priority => 0` value is still the reported one when nothing else + /// reports the tag -- the rule suppresses a clobber, not the tag. + #[test] + fn low_priority_value_is_kept_when_it_is_the_only_one() { + let mut tags = HashMap::new(); + insert_low_priority( + &mut tags, + "Pentax:PentaxModelID".to_string(), + "Optio SV".to_string(), + ); + assert_eq!(tags["Pentax:PentaxModelID"], "Optio SV"); + } + + /// `Priority => 0` then normal: the stored value recorded no priority, so + /// it is promoted to 1 and the normal value's `1 >= 1` displaces it + /// (ExifTool.pm:9545-9551, :9564). That is a plain `insert`, and this test + /// pins that the helper does not turn it into an `or_insert` too. + #[test] + fn a_normal_priority_value_still_displaces_a_low_priority_one() { + let mut tags = HashMap::new(); + insert_low_priority(&mut tags, "Pentax:LensType".to_string(), "low".to_string()); + tags.insert("Pentax:LensType".to_string(), "normal".to_string()); + assert_eq!(tags["Pentax:LensType"], "normal"); + } +} diff --git a/tools/exiftool-tables/codegen_subdirs.py b/tools/exiftool-tables/codegen_subdirs.py index fbf4fc7d1..0c170f23e 100644 --- a/tools/exiftool-tables/codegen_subdirs.py +++ b/tools/exiftool-tables/codegen_subdirs.py @@ -278,6 +278,30 @@ def field_raw_conv(tag, table, key): raise Unsupported(table, key, f"RawConv expression not in the known vocabulary: {expr!r}") +def field_priority(tag, table_priority, table, key): + """True when ExifTool would give this field priority 0. + + `FoundTag` reads a tag's priority at ExifTool.pm:9469-9473 -- the tag's own + `Priority`, else the table's `PRIORITY`, else 0 when the tag is `Avoid` -- + and a 0 there means the field never displaces a value already reported + under the same name (see `shared::tag_priority`). Dropping it silently + prints a sub-directory's copy of a tag over the `Main` table's copy. + + Only 0 is modelled. ExifTool also uses -1, 1 and 2, but none of those + appears in a `ProcessBinaryData` table, and treating an unmodelled value as + "normal" would be a guess at which of two real values prints. + """ + priority = tag.get("Priority") + if priority is None: + if table_priority is not None: + return table_priority == 0 + return bool(tag.get("Avoid")) + priority = int(str(priority), 0) + if priority != 0: + raise Unsupported(table, key, f"Priority => {priority} is not modelled") + return True + + def normalize_deparse(text): """A Perl sub body reduced to a stable key. @@ -494,16 +518,26 @@ def emit(self): ) -# Keys that are documentation or writer-side only: present on a field without -# changing what a reader produces. -IGNORED_TAG_KEYS = { - "Name", "Format", "RawConv", "PrintConv", "ValueConv", "Mask", "Notes", "Description", - # Handled by `field_cond`, which refuses any test it cannot reproduce. - "Condition", - "DataMember", "Writable", "Groups", "PrintConvInv", "ValueConvInv", - "Protected", "Permanent", "SeparateTable", "PrintHex", "Priority", - "_shorthand", "_extra_keys", "Unknown", "Hidden", "Avoid", "Binary", - "RelatedTag", "Count", "Flags", +# Every tag key this generator knows about. A key outside this set stops the +# table rather than being skipped, so an ExifTool edit that adds one cannot pass +# silently as "no change". +# +# The first group is read above -- `Name`, `Format`/`Count`, `RawConv`, +# `PrintConv`, `ValueConv`, `Mask`, `Condition` (by `field_cond`, which refuses +# any test it cannot reproduce), and `Priority`/`Avoid` (by `field_priority`) +# all change what a reader produces. `Priority` in particular sat in the second +# group until it was found printing a sub-directory's `LensType` over the one +# `%Pentax::Main` reports; it is not documentation. +# +# The second group is documentation or writer-side only: present on a field +# without changing what a reader produces. +KNOWN_TAG_KEYS = { + "Name", "Format", "Count", "RawConv", "PrintConv", "ValueConv", "Mask", + "Condition", "Priority", "Avoid", + "Notes", "Description", "DataMember", "Writable", "Groups", "PrintConvInv", + "ValueConvInv", "Protected", "Permanent", "SeparateTable", "PrintHex", + "_shorthand", "_extra_keys", "Unknown", "Hidden", "Binary", + "RelatedTag", "Flags", } @@ -523,6 +557,13 @@ def gen_table(module, tname, tbl, pool, skips, allow_skip, conv_prefix, vc_prefi raise Unsupported(tname, "-", f"table FORMAT {fmt_name!r} is not a scalar format") default_fmt, _ = SCALARS[fmt_name] first_entry = int(str(meta.get("FIRST_ENTRY", "0")), 0) + # The table's default priority, used for any tag without its own `Priority` + # (`$priority = $$tbl{PRIORITY}`, ExifTool.pm:9471). No `ProcessBinaryData` + # table declares one today, so this is here to keep a future one from + # arriving as a silent behaviour change. + table_priority = meta.get("PRIORITY") + if table_priority is not None: + table_priority = int(str(table_priority), 0) rows = [] for key in sorted(tbl["tags"], key=lambda k: parse_index(k, tname)): @@ -554,12 +595,13 @@ def gen_table(module, tname, tbl, pool, skips, allow_skip, conv_prefix, vc_prefi refused = True continue for k in tag: - if k not in IGNORED_TAG_KEYS: + if k not in KNOWN_TAG_KEYS: raise Unsupported(tname, key, f"unhandled tag key {k!r}") idx = parse_index(key, tname) cond = field_cond(tag, tname, key) fmt, count = field_format(tag, tname, key) member, gate = field_raw_conv(tag, tname, key) + low_priority = field_priority(tag, table_priority, tname, key) pc = field_print_conv(tag, tname, key, pool, conv_prefix) vc = field_value_conv(tag, tname, key, vc_prefix) if vc != "ValueConv::None" and pc.startswith("PrintConv::Map"): @@ -588,7 +630,8 @@ def gen_table(module, tname, tbl, pool, skips, allow_skip, conv_prefix, vc_prefi f"format: {'None' if fmt is None else f'Some({fmt})'}, count: {count}, " f"set_member: {'None' if member is None else f'Some(\"{member}\")'}, " f"gate: {'None' if gate is None else f'Some((\"{gate[0]}\", {gate[1]}))'}, " - f"mask: {mask_s}, value_conv: {vc}, print_conv: {pc} }}," + f"mask: {mask_s}, value_conv: {vc}, print_conv: {pc}, " + f"low_priority: {'true' if low_priority else 'false'} }}," ) if refused and len(variants) > 1: for text in group: