1use crate::tag::{Tag, TagGroup, TagId};
7use crate::value::Value;
8
9pub fn compute_composite_tags(tags: &[Tag]) -> Vec<Tag> {
11 let mut composite = Vec::new();
12
13 if let Some(canon_tags) = compute_canon_composites(tags) {
16 composite.extend(canon_tags);
17 }
18
19 if let Some(rot) = compute_quicktime_rotation(tags) {
20 composite.push(rot);
21 }
22
23 if let Some(flash) = compute_xmp_flash(tags) {
24 composite.push(flash);
25 }
26
27 if let Some(es) = compute_olympus_extender_status(tags) {
28 composite.push(es);
29 }
30
31 if let Some(fd) = compute_sony_focus_distance2(tags) {
32 composite.push(fd);
33 }
34
35 if let Some(pos) = compute_gps_position(tags) {
37 composite.push(pos);
38 }
39
40 if let Some(alt) = compute_gps_altitude(tags) {
42 composite.push(alt);
43 }
44
45 if let Some(ss) = compute_shutter_speed(tags) {
47 composite.push(ss);
48 }
49
50 if let Some(ap) = compute_aperture(tags) {
52 composite.push(ap);
53 }
54
55 if find_tag(tags, "ShutterSpeed").is_none() && find_tag(tags, "ExposureTime").is_none() {
57 if let Some(ssv) = find_tag_f64(tags, "ShutterSpeedValue") {
58 let print = crate::tags::canon_sub::print_exposure_time(2.0_f64.powf(-ssv));
60 composite.push(mk_composite(
61 "ShutterSpeed",
62 "Shutter Speed",
63 Value::String(print),
64 ));
65 }
66 }
67
68 if find_tag(tags, "SensorRightBorder").is_some() && find_tag(tags, "SensorLeftBorder").is_some()
73 {
74 if let (Some(right), Some(left)) = (
75 find_tag(tags, "SensorRightBorder").and_then(|t| t.raw_value.as_u64()),
76 find_tag(tags, "SensorLeftBorder").and_then(|t| t.raw_value.as_u64()),
77 ) {
78 composite.push(mk_composite(
79 "ImageWidth",
80 "Image Width",
81 Value::String(format!("{}", right.saturating_sub(left))),
82 ));
83 }
84 }
85 if find_tag(tags, "SensorBottomBorder").is_some() && find_tag(tags, "SensorTopBorder").is_some()
86 {
87 if let (Some(bottom), Some(top)) = (
88 find_tag(tags, "SensorBottomBorder").and_then(|t| t.raw_value.as_u64()),
89 find_tag(tags, "SensorTopBorder").and_then(|t| t.raw_value.as_u64()),
90 ) {
91 composite.push(mk_composite(
92 "ImageHeight",
93 "Image Height",
94 Value::String(format!("{}", bottom.saturating_sub(top))),
95 ));
96 }
97 }
98
99 {
101 let mut all: Vec<Tag> = tags.to_vec();
102 all.extend(composite.iter().cloned());
103 if let Some(mut sz) = compute_image_size(&all) {
104 sz.priority = 1;
107 composite.push(sz);
108 }
109 }
110
111 {
113 let mut all: Vec<Tag> = tags.to_vec();
114 all.extend(composite.iter().cloned());
115 if let Some(mut mp) = compute_megapixels(&all) {
116 mp.priority = 1;
117 composite.push(mp);
118 }
119 }
120
121 {
124 let mut all: Vec<Tag> = tags.to_vec();
125 all.extend(composite.iter().cloned());
126 if let Some(lv) = compute_light_value(&all) {
127 composite.push(lv);
128 }
129 }
130
131 if let Some(t) = make_subsec_date(
133 tags,
134 "DateTimeOriginal",
135 "SubSecTimeOriginal",
136 "OffsetTimeOriginal",
137 "SubSecDateTimeOriginal",
138 ) {
139 composite.push(t);
140 }
141 if let Some(t) = make_subsec_date(
143 tags,
144 "CreateDate",
145 "SubSecTimeDigitized",
146 "OffsetTimeDigitized",
147 "SubSecCreateDate",
148 ) {
149 composite.push(t);
150 }
151 if let Some(t) = make_subsec_date(
153 tags,
154 "ModifyDate",
155 "SubSecTime",
156 "OffsetTime",
157 "SubSecModifyDate",
158 ) {
159 composite.push(t);
160 }
161
162 if let Some(sf_tags) = compute_35efl(tags) {
167 composite.extend(sf_tags);
168 } else if find_tag(tags, "FocalLength").is_some() {
169 let fl = find_tag_f64(tags, "FocalLength").unwrap_or(0.0);
172 composite.push(mk_composite(
173 "FocalLength35efl",
174 "Focal Length (35mm equiv)",
175 Value::String(format!("{:.1} mm", fl)),
176 ));
177 }
178
179 {
181 let mut all_for_wb: Vec<Tag> = tags.to_vec();
182 all_for_wb.extend(composite.iter().cloned());
183 if let Some(wb_tags) = compute_wb_balance(&all_for_wb) {
184 composite.extend(wb_tags);
185 }
186 }
187
188 {
190 let mut all_tags: Vec<&Tag> = tags.iter().collect();
191 let comp_refs: Vec<&Tag> = composite.iter().collect();
192 all_tags.extend(comp_refs);
193 let all_slice: Vec<Tag> = all_tags.into_iter().cloned().collect();
194 if let Some(dof_tags) = compute_dof(&all_slice) {
195 composite.extend(dof_tags);
196 }
197 if let Some(hd) = compute_hyperfocal(&all_slice) {
199 composite.push(hd);
200 }
201 }
202
203 if find_tag(tags, "DateTimeCreated").is_none() {
206 if let (Some(date_tag), Some(time)) = (
207 find_tag_in_group(tags, "DateCreated", "IPTC"),
208 find_tag_value(tags, "TimeCreated"),
209 ) {
210 let date = date_tag.print_value.clone();
211 if !date.is_empty() && !time.is_empty() {
212 composite.push(mk_composite(
213 "DateTimeCreated",
214 "Date/Time Created",
215 Value::String(format!("{} {}", date, time)),
216 ));
217 }
218 }
219 }
220
221 if find_tag(tags, "DateTimeOriginal").is_none() {
224 if let (Some(date), Some(time)) = (
225 find_tag_value(tags, "DateCreated"),
226 find_tag_value(tags, "TimeCreated"),
227 ) {
228 if !date.is_empty() && !time.is_empty() {
229 composite.push(mk_composite(
230 "DateTimeOriginal",
231 "Date/Time Original",
232 Value::String(format!("{} {}", date, time)),
233 ));
234 }
235 }
236 }
237 if find_tag(tags, "DateTimeOriginal").is_none()
240 && composite.iter().all(|t| t.name != "DateTimeOriginal")
241 {
242 if let Some(year_tag) = tags
243 .iter()
244 .find(|t| t.name == "Year" && t.group.family0 == "ID3")
245 {
246 let year = year_tag.print_value.clone();
247 if !year.is_empty() {
248 composite.push(mk_composite(
249 "DateTimeOriginal",
250 "Date/Time Original",
251 Value::String(year),
252 ));
253 }
254 }
255 }
256
257 if find_tag(tags, "GPSDateStamp").is_some() && find_tag(tags, "GPSTimeStamp").is_some() {
260 let date = find_tag_value(tags, "GPSDateStamp").unwrap_or_default();
261 let time = find_tag_value(tags, "GPSTimeStamp").unwrap_or_default();
262 if !time.is_empty() {
263 composite.push(mk_composite(
264 "GPSDateTime",
265 "GPS Date/Time",
266 Value::String(format!("{} {}Z", date, time)),
267 ));
268 }
269 }
270
271 if let (Some(date), Some(time)) = (
273 find_tag_value(tags, "DigitalCreationDate"),
274 find_tag_value(tags, "DigitalCreationTime"),
275 ) {
276 if !date.is_empty() && !time.is_empty() {
277 composite.push(mk_composite(
278 "DigitalCreationDateTime",
279 "Digital Creation Date/Time",
280 Value::String(format!("{} {}", date, time)),
281 ));
282 }
283 }
284
285 if let Some(t2) = tags.iter().find(|t| t.name == "LensType2") {
291 let raw = t2.raw_value.as_u64().unwrap_or(0);
292 let named = t2.print_value != t2.raw_value.to_display_string();
293 if named && (raw & 0x8000 != 0 || raw == 0) {
294 composite.retain(|t| t.name != "LensID");
295 composite.push(mk_composite(
296 "LensID",
297 "Lens ID",
298 Value::String(t2.print_value.clone()),
299 ));
300 }
301 }
302
303 if !composite.iter().any(|t| t.name == "LensID") {
306 let nikon_lensid_possible = find_tag(tags, "LensIDNumber").is_some();
309 let lens_val = find_tag_value(tags, "LensModel")
310 .filter(|v| !v.is_empty() && (v.contains("mm") || v.to_lowercase().contains("f/")))
311 .or_else(|| {
312 if nikon_lensid_possible {
313 return None;
314 }
315 find_tag_value(tags, "Lens")
316 .filter(|v| !v.is_empty() && (v.contains("mm") || v.contains("/F")))
317 })
318 .or_else(|| {
319 find_tag_value(tags, "LensType").filter(|v| !v.is_empty() && v.contains("mm"))
320 });
321 if let Some(lm) = lens_val {
322 let lens_id = lm
324 .replace(" - ", "-")
325 .replace("mmF", "mm F")
326 .replace("/F", "mm F");
327 composite.push(mk_composite("LensID", "Lens ID", Value::String(lens_id)));
328 }
329 }
330
331 {
333 let make = find_tag_value(tags, "Make").unwrap_or_default();
334 if make.to_uppercase().contains("NIKON")
335 && maker_note_module_used(tags, "Nikon")
336 && find_tag(tags, "SerialNumber").is_none()
337 {
338 if let Some(sn) = find_tag_value(tags, "InternalSerialNumber") {
339 composite.push(mk_composite(
340 "SerialNumber",
341 "Serial Number",
342 Value::String(sn),
343 ));
344 }
345 }
346 }
347
348 {
352 let make = find_tag_value(tags, "Make").unwrap_or_default();
353 let nikon_module = maker_note_module_used(tags, "Nikon");
356 if make.to_uppercase().contains("NIKON") && nikon_module {
357 if let Some(fm_tag) = tags.iter().find(|t| t.name == "FocusMode") {
359 if find_tag(&composite, "AutoFocus").is_none() {
360 let af = if fm_tag.print_value.contains("Manual") {
361 "Off"
362 } else {
363 "On"
364 };
365 composite.push(mk_composite(
366 "AutoFocus",
367 "Auto Focus",
368 Value::String(af.into()),
369 ));
370 }
371 }
372 if find_tag(tags, "LensSpec").is_none() {
375 if let Some(lens) = find_tag_value(tags, "Lens") {
376 if !lens.is_empty() {
377 let mut spec = lens;
378 if let Some(lt) = find_tag_value(tags, "LensType") {
379 let lt = lt.trim();
380 if !lt.is_empty()
382 && lt != "AF"
383 && lt
384 .chars()
385 .all(|c| c.is_ascii_uppercase() || c == ' ' || c == '-')
386 {
387 spec = format!("{} {}", spec, lt);
388 }
389 }
390 composite.push(mk_composite("LensSpec", "Lens Spec", Value::String(spec)));
391 }
392 }
393 }
394 if !composite.iter().any(|t| t.name == "LensID")
397 && find_tag(tags, "LensIDNumber").is_some()
398 && find_tag(tags, "MinFocalLength").is_some()
399 {
400 if let Some(lens_id) = compute_nikon_lens_id(tags) {
401 composite.push(mk_composite("LensID", "Lens ID", Value::String(lens_id)));
402 }
403 }
404 }
405 }
406
407 if let (Some(year), Some(md)) = (
409 find_tag_value(tags, "YearCreated"),
410 find_tag_value(tags, "MonthDayCreated"),
411 ) {
412 if !year.is_empty() && !md.is_empty() {
413 composite.push(mk_composite(
414 "DateCreated",
415 "Date Created",
416 Value::String(format!("{}:{}", year, md)),
417 ));
418 }
419 }
420
421 if find_tag(tags, "AdvancedSceneMode").is_none() && find_tag(tags, "SensorTopBorder").is_some()
424 {
425 if let Some(adv) = compute_panasonic_advanced_scene_mode(tags) {
426 composite.push(adv);
427 }
428 }
429
430 {
438 if let (Some(pat), Some(dim)) = (
439 find_tag_value(tags, "CFAPattern2"),
440 find_tag_value(tags, "CFARepeatPatternDim"),
441 ) {
442 let dims: Vec<usize> = dim
443 .split([',', ' '])
444 .filter_map(|s| s.trim().parse().ok())
445 .collect();
446 let vals: Vec<u8> = pat
447 .split([',', ' '])
448 .filter_map(|s| s.trim().parse().ok())
449 .collect();
450 if dims.len() == 2 && dims[0] > 0 && dims[1] > 0 && vals.len() >= dims[0] * dims[1] {
451 let color = |v: u8| match v {
452 0 => "Red",
453 1 => "Green",
454 2 => "Blue",
455 3 => "Cyan",
456 4 => "Magenta",
457 5 => "Yellow",
458 6 => "White",
459 _ => "?",
460 };
461 let mut s = String::new();
462 for row in 0..dims[1] {
463 s.push('[');
464 for col in 0..dims[0] {
465 if col > 0 {
466 s.push(',');
467 }
468 s.push_str(color(vals[row * dims[0] + col]));
469 }
470 s.push(']');
471 }
472 composite.push(mk_composite("CFAPattern", "CFA Pattern", Value::String(s)));
473 }
474 }
475 }
476
477 if find_tag(tags, "ThumbnailTIFF").is_none() {
480 if let Some(t) = build_thumbnail_tiff(tags) {
481 composite.push(t);
482 }
483 }
484
485 composite
486}
487
488pub(crate) fn build_thumbnail_tiff(tags: &[Tag]) -> Option<Tag> {
494 let (tiff_data, grp0, grp1) = compute_thumbnail_tiff(tags)?;
495 let size = tiff_data.len();
496 Some(Tag {
497 id: TagId::Text("ThumbnailTIFF".into()),
498 name: "ThumbnailTIFF".into(),
499 description: "Thumbnail TIFF".into(),
500 group: TagGroup {
501 family0: grp0,
505 family1: grp1,
506 family2: "Preview".into(),
507 family3: "Main".into(),
508 },
509 raw_value: Value::Binary(tiff_data),
510 print_value: format!("(Binary data {} bytes, use -b option to extract)", size),
511 priority: 0,
512 })
513}
514
515fn compute_thumbnail_tiff(tags: &[Tag]) -> Option<(Vec<u8>, String, String)> {
525 let subfile = tags.iter().find(|t| {
527 t.name == "SubfileType"
528 && t.raw_value
529 .as_u64()
530 .or_else(|| {
531 if t.print_value.contains("Reduced") {
532 Some(1)
533 } else {
534 t.print_value.trim().parse().ok()
535 }
536 })
537 .is_some_and(|v| v == 1)
538 })?;
539 let grp0 = subfile.group.family0.clone();
540 let grp1 = subfile.group.family1.clone();
541 let scoped: Vec<Tag> = tags
543 .iter()
544 .filter(|t| t.group.family1 == grp1)
545 .cloned()
546 .collect();
547 let tags: &[Tag] = &scoped;
548
549 let comp = find_tag(tags, "Compression")?;
551 let comp_val = comp.raw_value.as_u64().or_else(|| {
552 if comp.print_value.contains("Uncompressed") {
553 Some(1)
554 } else {
555 comp.print_value.trim().parse().ok()
556 }
557 })?;
558 if comp_val != 1 {
559 return None;
560 }
561
562 let _strip_off = find_tag(tags, "StripOffsets")?.raw_value.as_u64()? as u32;
564 let strip_len = find_tag(tags, "StripByteCounts")?.raw_value.as_u64()? as u32;
565 let bps_str = find_tag_value(tags, "BitsPerSample").unwrap_or_default();
566 let bps_vals: Vec<u16> = bps_str
567 .split([',', ' '])
568 .filter_map(|s| s.trim().parse().ok())
569 .collect();
570 if bps_vals.is_empty() {
571 return None;
572 }
573 let spp = find_tag(tags, "SamplesPerPixel")?.raw_value.as_u64()? as u16;
574 let rps = find_tag(tags, "RowsPerStrip")?.raw_value.as_u64()? as u32;
575 let photo = find_tag(tags, "PhotometricInterpretation")?
576 .raw_value
577 .as_u64()
578 .or_else(|| {
579 if find_tag_value(tags, "PhotometricInterpretation")?.contains("RGB") {
580 Some(2)
581 } else {
582 Some(1)
583 }
584 })? as u16;
585 let _planar = find_tag(tags, "PlanarConfiguration")
586 .and_then(|t| t.raw_value.as_u64())
587 .unwrap_or(1) as u16;
588 let orient = find_tag(tags, "Orientation")
589 .and_then(|t| t.raw_value.as_u64())
590 .unwrap_or(1) as u16;
591
592 let bytes_per_pixel: u32 = bps_vals.iter().map(|&b| (b as u32).div_ceil(8)).sum();
594 if bytes_per_pixel == 0 || rps == 0 {
595 return None;
596 }
597 let row_bytes = strip_len / rps;
598 if row_bytes == 0 {
599 return None;
600 }
601 let w = row_bytes / bytes_per_pixel;
602 let h = rps;
603
604 let num_entries: u16 = 15;
606 let ifd_size = 2 + num_entries as usize * 12 + 4;
607 let data_offset = 8 + ifd_size; let bps_data_offset = data_offset;
610 let bps_data_size = if spp > 1 { spp as usize * 2 } else { 0 };
611 let resolution_offset = bps_data_offset + bps_data_size;
612 let strip_data_offset = resolution_offset + 16; let total_size = strip_data_offset + strip_len as usize;
614
615 let mut tiff = Vec::with_capacity(total_size);
616
617 tiff.extend_from_slice(b"II");
619 tiff.extend_from_slice(&42u16.to_le_bytes());
620 tiff.extend_from_slice(&8u32.to_le_bytes()); tiff.extend_from_slice(&num_entries.to_le_bytes());
624
625 let add_entry = |tiff: &mut Vec<u8>, tag: u16, dtype: u16, count: u32, value: u32| {
626 tiff.extend_from_slice(&tag.to_le_bytes());
627 tiff.extend_from_slice(&dtype.to_le_bytes());
628 tiff.extend_from_slice(&count.to_le_bytes());
629 tiff.extend_from_slice(&value.to_le_bytes());
630 };
631
632 add_entry(&mut tiff, 0x00FE, 4, 1, 0); add_entry(&mut tiff, 0x0100, 3, 1, w); add_entry(&mut tiff, 0x0101, 3, 1, h); if spp == 1 {
636 add_entry(&mut tiff, 0x0102, 3, 1, bps_vals[0] as u32); } else {
638 add_entry(&mut tiff, 0x0102, 3, spp as u32, bps_data_offset as u32);
639 }
640 add_entry(&mut tiff, 0x0103, 3, 1, 1); add_entry(&mut tiff, 0x0106, 3, 1, photo as u32); add_entry(&mut tiff, 0x0111, 4, 1, strip_data_offset as u32); add_entry(&mut tiff, 0x0112, 3, 1, orient as u32); add_entry(&mut tiff, 0x0115, 3, 1, spp as u32); add_entry(&mut tiff, 0x0116, 4, 1, h); add_entry(&mut tiff, 0x0117, 4, 1, strip_len); add_entry(&mut tiff, 0x011A, 5, 1, resolution_offset as u32); add_entry(&mut tiff, 0x011B, 5, 1, (resolution_offset + 8) as u32); add_entry(&mut tiff, 0x011C, 3, 1, _planar as u32); add_entry(&mut tiff, 0x0128, 3, 1, 2); tiff.extend_from_slice(&0u32.to_le_bytes());
654
655 if spp > 1 {
657 for &b in &bps_vals {
658 tiff.extend_from_slice(&b.to_le_bytes());
659 }
660 }
661
662 tiff.extend_from_slice(&72u32.to_le_bytes());
664 tiff.extend_from_slice(&1u32.to_le_bytes());
665 tiff.extend_from_slice(&72u32.to_le_bytes());
666 tiff.extend_from_slice(&1u32.to_le_bytes());
667
668 tiff.resize(total_size, 0);
672
673 Some((tiff, grp0, grp1))
674}
675
676fn find_tag<'a>(tags: &'a [Tag], name: &str) -> Option<&'a Tag> {
686 let pick = |main_only: bool| {
692 tags.iter()
693 .filter(|t| t.name == name)
694 .filter(|t| !main_only || t.group.family3 == "Main")
695 .fold(None, |best: Option<&Tag>, t| match best {
699 Some(b) if b.priority_rank() >= t.priority_rank() => Some(b),
700 _ => Some(t),
701 })
702 };
703 pick(true).or_else(|| pick(false))
704}
705
706fn maker_note_module_used(tags: &[Tag], module: &str) -> bool {
724 tags.iter().any(|t| {
725 t.group.family0 == "MakerNotes"
726 && t.group.family1 == module
727 && matches!(t.id, TagId::Numeric(_))
728 })
729}
730
731fn find_tag_in_group<'a>(tags: &'a [Tag], name: &str, group: &str) -> Option<&'a Tag> {
732 let name_lower = name.to_lowercase();
733 let group_lower = group.to_lowercase();
734 tags.iter().find(|t| {
735 t.name.to_lowercase() == name_lower
736 && (t.group.family0.to_lowercase() == group_lower
737 || t.group.family1.to_lowercase() == group_lower)
738 })
739}
740
741fn find_tag_value(tags: &[Tag], name: &str) -> Option<String> {
742 find_tag(tags, name).map(|t| t.print_value.clone())
743}
744
745fn find_tag_f64(tags: &[Tag], name: &str) -> Option<f64> {
746 find_tag(tags, name).and_then(|t| {
747 t.raw_value.as_f64().or_else(|| {
749 if let Value::List(items) = &t.raw_value {
751 items.iter().rev().find_map(|v| {
752 let f = v.as_f64()?;
753 if f > 0.0 {
754 Some(f)
755 } else {
756 None
757 }
758 })
759 } else {
760 t.print_value
762 .split(',')
763 .next_back()
764 .and_then(|s| {
765 let s = s.trim();
766 s.parse::<f64>()
768 .ok()
769 .or_else(|| s.trim_end_matches(" mm").trim().parse::<f64>().ok())
770 .or_else(|| s.trim_end_matches(" m").trim().parse::<f64>().ok())
771 .or_else(|| s.split_whitespace().next()?.parse::<f64>().ok())
772 })
773 .filter(|&v| v > 0.0)
774 }
775 })
776 })
777}
778
779pub fn gps_coordinates(tags: &[Tag]) -> Vec<Tag> {
788 let mut out = Vec::new();
789 for (coord, reftag, pos_ref, neg_ref) in [
790 ("GPSLatitude", "GPSLatitudeRef", 'N', 'S'),
791 ("GPSLongitude", "GPSLongitudeRef", 'E', 'W'),
792 ] {
793 let Some(refv) = tags
796 .iter()
797 .find(|t| t.name == reftag && t.group.family1 == "GPS")
798 .map(|t| t.raw_value.to_display_string())
799 else {
800 continue;
801 };
802 let Some(src) = tags
803 .iter()
804 .find(|t| t.name == coord && t.group.family1 == "GPS")
805 else {
806 continue;
807 };
808 let negative = refv
813 .chars()
814 .next()
815 .is_some_and(|c| c.eq_ignore_ascii_case(&neg_ref));
816 let print = if src.print_value.is_empty() {
817 String::new()
818 } else {
819 format!(
820 "{} {}",
821 src.print_value,
822 if negative { neg_ref } else { pos_ref }
823 )
824 };
825 let mut t = mk_composite_raw(
826 coord,
827 src.description.clone().as_str(),
828 src.raw_value.clone(),
829 print,
830 );
831 t.group.family2 = "Location".into();
832 out.push(t);
833 }
834 out
835}
836fn compute_gps_position(tags: &[Tag]) -> Option<Tag> {
837 let composite_coord = |name: &str| -> Option<&Tag> {
841 tags.iter()
842 .find(|t| t.name == name && t.group.family0 == "Composite")
843 .or_else(|| find_tag(tags, name))
844 };
845 let lat_tag = composite_coord("GPSLatitude")?;
846 let lon_tag = composite_coord("GPSLongitude")?;
847 let (lat, lon) = (lat_tag.print_value.clone(), lon_tag.print_value.clone());
849
850 if lat.is_empty() || lon.is_empty() || lat.contains("undef") || lon.contains("undef") {
851 return None;
852 }
853
854 Some(mk_composite(
855 "GPSPosition",
856 "GPS Position",
857 Value::String(format!("{}, {}", lat, lon)),
858 ))
859}
860
861fn compute_gps_altitude(tags: &[Tag]) -> Option<Tag> {
862 let alt = find_tag(tags, "GPSAltitude")?;
863 let alt_ref = find_tag_value(tags, "GPSAltitudeRef").unwrap_or_default();
864
865 let meters = alt.raw_value.as_f64()?;
866 let below = alt_ref.contains("Below") || alt_ref.trim() == "1";
868 let trunc = (meters.abs() * 10.0).trunc() / 10.0;
869 let pos = if below { "Below" } else { "Above" };
870
871 let mut t = mk_composite(
872 "GPSAltitude",
873 "GPS Altitude",
874 Value::String(format!(
875 "{} m {} Sea Level",
876 crate::value::format_g15(trunc),
877 pos
878 )),
879 );
880 t.priority = 1;
882 Some(t)
883}
884
885fn compute_sony_focus_distance2(tags: &[Tag]) -> Option<Tag> {
892 let pos = tags
893 .iter()
894 .find(|t| t.name == "FocusPosition2" && t.group.family1 == "Sony")
895 .and_then(|t| t.raw_value.as_f64())?;
896 let focal35 = find_tag_f64(tags, "FocalLengthIn35mmFormat")?;
897 if pos == 0.0 {
898 return None;
899 }
900 let print = if pos >= 255.0 {
901 "inf".to_string()
902 } else {
903 let metres = (2.0_f64.powf(pos / 16.0 - 5.0) + 1.0) * focal35 / 1000.0;
904 crate::tags::conv_expr::eval(
905 "sprintf(\"%.4g m\", $val)",
906 &crate::tags::conv_expr::Val::Num(metres),
907 )?
908 .as_string()
909 };
910 Some(mk_composite(
911 "FocusDistance2",
912 "Focus Distance 2",
913 Value::String(print),
914 ))
915}
916
917fn compute_shutter_speed(tags: &[Tag]) -> Option<Tag> {
918 let et = find_tag(tags, "ExposureTime")?;
919 let print = et
922 .raw_value
923 .as_f64()
924 .map(crate::tags::canon_sub::print_exposure_time)
925 .unwrap_or_else(|| et.print_value.clone());
926 Some(mk_composite(
927 "ShutterSpeed",
928 "Shutter Speed",
929 Value::String(print),
930 ))
931}
932
933fn compute_aperture(tags: &[Tag]) -> Option<Tag> {
935 let val = find_tag_f64(tags, "FNumber")
936 .or_else(|| find_tag_f64(tags, "ApertureValue").map(|av| 2.0_f64.powf(av / 2.0)))?;
937 if val <= 0.0 {
938 return None;
939 }
940 let print = if val < 1.0 {
942 format!("{:.2}", val)
943 } else {
944 format!("{:.1}", val)
945 };
946 Some(mk_composite("Aperture", "Aperture", Value::String(print)))
947}
948
949fn compute_image_size(tags: &[Tag]) -> Option<Tag> {
950 if let Some(rcs) = find_tag_value(tags, "RawImageCroppedSize") {
953 let v = rcs.trim().replace(' ', "x");
954 if !v.is_empty() {
955 return Some(mk_composite("ImageSize", "Image Size", Value::String(v)));
956 }
957 }
958 let ft = find_tag(tags, "FileType")
959 .map(|t| t.print_value.clone())
960 .unwrap_or_default();
961 if matches!(ft.as_str(), "CR2" | "IIQ" | "EIP") {
962 let ew = find_tag(tags, "ExifImageWidth").and_then(|t| {
963 t.raw_value
964 .as_u64()
965 .or_else(|| t.print_value.trim().parse().ok())
966 });
967 let eh = find_tag(tags, "ExifImageHeight").and_then(|t| {
968 t.raw_value
969 .as_u64()
970 .or_else(|| t.print_value.trim().parse().ok())
971 });
972 if let (Some(w), Some(h)) = (ew, eh) {
973 return Some(mk_composite(
974 "ImageSize",
975 "Image Size",
976 Value::String(format!("{}x{}", w, h)),
977 ));
978 }
979 }
980 let width = find_tag(tags, "ImageWidth").and_then(|t| {
981 t.raw_value
982 .as_u64()
983 .or_else(|| t.print_value.trim().parse().ok())
984 });
985 let height = find_tag(tags, "ImageHeight").and_then(|t| {
986 t.raw_value
987 .as_u64()
988 .or_else(|| t.print_value.trim().parse().ok())
989 });
990
991 let (width, height) = (width?, height?);
992
993 Some(mk_composite(
994 "ImageSize",
995 "Image Size",
996 Value::String(format!("{}x{}", width, height)),
997 ))
998}
999
1000fn compute_megapixels(tags: &[Tag]) -> Option<Tag> {
1002 let sz = find_tag_value(tags, "ImageSize").or_else(|| {
1004 let w = find_tag(tags, "ImageWidth")?;
1005 let h = find_tag(tags, "ImageHeight")?;
1006 let wv = w
1007 .raw_value
1008 .as_u64()
1009 .or_else(|| w.print_value.parse().ok())?;
1010 let hv = h
1011 .raw_value
1012 .as_u64()
1013 .or_else(|| h.print_value.parse().ok())?;
1014 Some(format!("{}x{}", wv, hv))
1015 })?;
1016
1017 let nums: Vec<f64> = sz
1018 .split(|c: char| !c.is_ascii_digit())
1019 .filter_map(|s| s.parse().ok())
1020 .collect();
1021 if nums.len() < 2 {
1022 return None;
1023 }
1024
1025 let mp = nums[0] * nums[1] / 1_000_000.0;
1026 let fmt = if mp >= 1.0 {
1028 format!("{:.1}", mp)
1029 } else if mp >= 0.001 {
1030 format!("{:.3}", mp)
1031 } else {
1032 format!("{:.6}", mp)
1033 };
1034
1035 Some(mk_composite("Megapixels", "Megapixels", Value::String(fmt)))
1036}
1037
1038fn compute_light_value(tags: &[Tag]) -> Option<Tag> {
1041 let aperture = find_tag_f64(tags, "FNumber")
1043 .or_else(|| find_tag_f64(tags, "ApertureValue").map(|av| 2.0_f64.powf(av / 2.0)))?;
1044
1045 let shutter = find_tag_f64(tags, "ExposureTime")
1047 .or_else(|| find_tag_f64(tags, "ShutterSpeedValue").map(|sv| 2.0_f64.powf(-sv)))
1048 .or_else(|| {
1049 find_tag_value(tags, "ShutterSpeed").and_then(|s| {
1051 let s = s.trim_end_matches(" s").trim();
1052 if s.contains('/') {
1053 let parts: Vec<&str> = s.split('/').collect();
1054 let n: f64 = parts[0].parse().ok()?;
1055 let d: f64 = parts[1].parse().ok()?;
1056 Some(n / d)
1057 } else {
1058 s.parse().ok()
1059 }
1060 })
1061 })?;
1062
1063 let iso = find_tag_f64(tags, "ISO")?;
1064
1065 if shutter <= 0.0 || iso <= 0.0 || aperture <= 0.0 {
1066 return None;
1067 }
1068
1069 let lv = 2.0 * aperture.log2() - shutter.log2() - (iso / 100.0).log2();
1071
1072 Some(mk_composite(
1073 "LightValue",
1074 "Light Value",
1075 Value::String(format!("{:.1}", lv)),
1076 ))
1077}
1078
1079fn canon_sensor_diag(tags: &[Tag]) -> Option<f64> {
1083 let make = find_tag_value(tags, "Make").unwrap_or_default();
1084 if !make.contains("Canon") {
1085 return None;
1086 }
1087 let rat = |name: &str| -> Option<(u32, u32)> {
1088 match find_tag(tags, name)?.raw_value {
1089 Value::URational(n, d) => Some((n, d)),
1090 _ => None,
1091 }
1092 };
1093 let (xn, xd) = rat("FocalPlaneXResolution")?;
1094 let (yn, yd) = rat("FocalPlaneYResolution")?;
1095 if xn % 1000 == 0
1097 && yn % 1000 == 0
1098 && xn >= 640_000
1099 && yn >= 480_000
1100 && xn < 10_000_000
1101 && yn < 10_000_000
1102 && (61..1500).contains(&xd)
1103 && (61..1000).contains(&yd)
1104 && xd != yd
1105 {
1106 return Some(((xd as f64).powi(2) + (yd as f64).powi(2)).sqrt() * 0.0254);
1107 }
1108 None
1109}
1110
1111fn compute_35efl(tags: &[Tag]) -> Option<Vec<Tag>> {
1112 let fl = find_tag_f64(tags, "FocalLength")?;
1113 if fl <= 0.0 {
1114 return None;
1115 }
1116
1117 let mut result = Vec::new();
1118
1119 let diag35 = (36.0_f64 * 36.0 + 24.0 * 24.0).sqrt();
1121
1122 let scale = if let Some(fl35) = find_tag_f64(tags, "FocalLengthIn35mmFormat") {
1125 if fl35 > 0.0 {
1126 fl35 / fl
1127 } else {
1128 return None;
1129 }
1130 } else if let Some(diag) = canon_sensor_diag(tags) {
1131 diag35 / diag
1133 } else if let Some(diag) = find_tag_f64(tags, "FocalPlaneDiagonal").or_else(|| {
1134 find_tag_value(tags, "FocalPlaneDiagonal")
1135 .and_then(|s| s.split_whitespace().next()?.parse().ok())
1136 }) {
1137 if diag > 1.0 && diag < 100.0 {
1139 diag35 / diag
1140 } else {
1141 return None;
1142 }
1143 } else if let Some(fpxs) = find_tag_f64(tags, "FocalPlaneXSize")
1144 .filter(|&v| v < 100.0) .or_else(|| {
1146 find_tag_value(tags, "FocalPlaneXSize")
1147 .and_then(|s| s.split_whitespace().next()?.parse::<f64>().ok())
1148 })
1149 {
1150 let fpys = find_tag_f64(tags, "FocalPlaneYSize")
1152 .filter(|&v| v < 100.0)
1153 .or_else(|| {
1154 find_tag_value(tags, "FocalPlaneYSize")
1155 .and_then(|s| s.split_whitespace().next()?.parse::<f64>().ok())
1156 })?;
1157 let diag = (fpxs * fpxs + fpys * fpys).sqrt();
1158 if diag > 1.0 && diag < 100.0 {
1159 diag35 / diag
1160 } else {
1161 return None;
1162 }
1163 } else {
1164 let fpxr = find_tag_f64(tags, "FocalPlaneXResolution")?;
1166 let fpyr = find_tag_f64(tags, "FocalPlaneYResolution").unwrap_or(fpxr);
1168 let img_w = find_tag_f64(tags, "RelatedImageWidth")
1170 .or_else(|| find_tag_f64(tags, "ExifImageWidth"))
1171 .or_else(|| find_tag_f64(tags, "ImageWidth"))?;
1172 let img_h = find_tag_f64(tags, "RelatedImageHeight")
1173 .or_else(|| find_tag_f64(tags, "ExifImageHeight"))
1174 .or_else(|| find_tag_f64(tags, "ImageHeight"))?;
1175 if fpxr <= 0.0 || fpyr <= 0.0 || img_w <= 0.0 || img_h <= 0.0 {
1176 return None;
1177 }
1178
1179 let unit = find_tag_f64(tags, "FocalPlaneResolutionUnit").unwrap_or(2.0);
1180 let factor = match unit as u32 {
1181 2 => 25.4,
1182 3 => 10.0,
1183 _ => 25.4,
1184 };
1185 let sensor_w = img_w * factor / fpxr;
1186 let sensor_h = img_h * factor / fpyr;
1187 let sensor_diag = (sensor_w * sensor_w + sensor_h * sensor_h).sqrt();
1188 if sensor_diag <= 1.0 || sensor_diag >= 100.0 {
1190 return None;
1191 }
1192 let ratio = if sensor_w > sensor_h {
1194 sensor_w / sensor_h
1195 } else {
1196 sensor_h / sensor_w
1197 };
1198 if ratio > 3.0 {
1199 return None;
1200 }
1201 diag35 / sensor_diag
1202 };
1203
1204 let fl35_val = fl * scale;
1205
1206 result.push(mk_composite(
1207 "ScaleFactor35efl",
1208 "Scale Factor To 35 mm Equivalent",
1209 Value::String(format!("{:.1}", scale)),
1210 ));
1211 result.push(mk_composite(
1212 "FocalLength35efl",
1213 "Focal Length (35mm equivalent)",
1214 Value::String(format!(
1215 "{:.1} mm (35 mm equivalent: {:.1} mm)",
1216 fl, fl35_val
1217 )),
1218 ));
1219
1220 let coc = (24.0_f64.powi(2) + 36.0_f64.powi(2)).sqrt() / (scale * 1440.0);
1222 result.push(mk_composite_raw(
1223 "CircleOfConfusion",
1224 "Circle of Confusion",
1225 Value::F64(coc),
1226 format!("{:.3} mm", coc),
1227 ));
1228
1229 {
1232 let fd = find_tag_f64(tags, "FocusDistance").filter(|v| v.is_finite() && *v > 0.0);
1233 let mut corr = 1.0;
1234 if let Some(d_m) = fd {
1235 let d = 1000.0 * d_m - fl;
1236 if d > 0.0 {
1237 corr += fl / d;
1238 }
1239 }
1240 let fd2 = 36.0_f64.atan2(2.0 * fl * scale * corr);
1241 #[allow(clippy::approx_constant)]
1244 let fov_deg = fd2 * 360.0 / 3.14159;
1245 let mut fov_str = format!("{:.1} deg", fov_deg);
1246 if let Some(d_m) = fd {
1247 if d_m > 0.0 && d_m < 10000.0 {
1248 let width = 2.0 * d_m * fd2.sin() / fd2.cos();
1249 fov_str.push_str(&format!(" ({:.2} m)", width));
1250 }
1251 }
1252 result.push(mk_composite("FOV", "Field of View", Value::String(fov_str)));
1253 }
1254
1255 let make = find_tag_value(tags, "Make").unwrap_or_default();
1257 let min_fl = find_tag_f64(tags, "MinFocalLength");
1258 let max_fl = find_tag_f64(tags, "MaxFocalLength");
1259 if let (Some(min), Some(max)) = (min_fl, max_fl) {
1260 if min > 0.0 && max > 0.0 && max > min {
1261 result.push(mk_composite(
1262 "Lens",
1263 "Lens",
1264 Value::String(format!("{:.1} - {:.1} mm", min, max)),
1265 ));
1266 if make.contains("Canon") {
1268 result.push(mk_composite(
1269 "Lens35efl",
1270 "Lens (35mm equivalent)",
1271 Value::String(format!(
1272 "{:.1} - {:.1} mm (35 mm equivalent: {:.1} - {:.1} mm)",
1273 min,
1274 max,
1275 min * scale,
1276 max * scale
1277 )),
1278 ));
1279 }
1280 }
1281 }
1282
1283 let nikon_lensid_possible =
1287 find_tag(tags, "LensIDNumber").is_some() && find_tag(tags, "MinFocalLength").is_some();
1288 if let Some(lt) = find_tag(tags, "LensType").filter(|_| !nikon_lensid_possible) {
1289 let raw_val = lt
1290 .raw_value
1291 .as_u64()
1292 .map(|v| v as i64)
1293 .unwrap_or_else(|| lt.raw_value.to_display_string().parse::<i64>().unwrap_or(0));
1294 let pv = lt.print_value.clone();
1295 if raw_val == -1 || raw_val == 65535 {
1297 let min_fl = find_tag_f64(tags, "MinFocalLength");
1298 let max_fl = find_tag_f64(tags, "MaxFocalLength");
1299 let lens_str = if let (Some(min), Some(max)) = (min_fl, max_fl) {
1300 if min > 0.0 && max > 0.0 && (max - min).abs() > 0.1 {
1301 format!("Unknown {:.0}-{:.0}mm", min, max)
1302 } else if min > 0.0 {
1303 format!("Unknown {:.0}mm", min)
1304 } else {
1305 "Unknown".to_string()
1306 }
1307 } else {
1308 "Unknown".to_string()
1309 };
1310 result.push(mk_composite("LensID", "Lens ID", Value::String(lens_str)));
1311 } else if !pv.is_empty() && pv != "0" && pv != "n/a" {
1312 let resolved =
1315 disambiguate_lens_id(&pv, find_tag_f64(tags, "FocalLength")).unwrap_or(pv);
1316 result.push(mk_composite("LensID", "Lens ID", Value::String(resolved)));
1317 }
1318 }
1319
1320 Some(result)
1321}
1322
1323fn lens_focal_range(name: &str) -> Option<(f64, f64)> {
1326 let mm = name.find("mm")?;
1328 let head = &name[..mm];
1329 let start = head
1331 .rfind(|c: char| !(c.is_ascii_digit() || c == '.' || c == '-'))
1332 .map(|i| i + 1)
1333 .unwrap_or(0);
1334 let tok = &head[start..];
1335 if tok.is_empty() {
1336 return None;
1337 }
1338 let mut it = tok.split('-').filter_map(|s| s.parse::<f64>().ok());
1339 let sf = it.next()?;
1340 let lf = it.next().unwrap_or(sf);
1341 if sf > 0.0 {
1342 Some((sf, lf))
1343 } else {
1344 None
1345 }
1346}
1347
1348fn disambiguate_lens_id(base_print: &str, focal_length: Option<f64>) -> Option<String> {
1351 const SUBVARIANTS: &[(&str, &[&str])] = &[
1353 (
1354 "Sigma or Tamron Lens (3 44)",
1355 &[
1356 "Sigma AF 10-20mm F4-5.6 EX DC",
1357 "Sigma 12-24mm F4.5-5.6 EX DG",
1358 "Sigma 17-70mm F2.8-4.5 DC Macro",
1359 "Sigma 18-50mm F3.5-5.6 DC",
1360 "Sigma 17-35mm F2.8-4 EX DG",
1361 "Tamron 35-90mm F4-5.6 AF",
1362 "Sigma AF 18-35mm F3.5-4.5 Aspherical",
1363 ],
1364 ),
1365 (
1366 "Sigma Lens (0x145)",
1367 &["Sigma 15-30mm F3.5-4.5 EX DG Aspherical"],
1368 ),
1369 ];
1370 let variants = SUBVARIANTS
1371 .iter()
1372 .find(|(b, _)| *b == base_print)
1373 .map(|(_, v)| *v)?;
1374 let fl = focal_length?;
1375 let mut matches = variants.iter().filter(|name| {
1377 lens_focal_range(name)
1378 .map(|(sf, lf)| fl >= sf - 0.5 && fl <= lf + 0.5)
1379 .unwrap_or(false)
1380 });
1381 let first = matches.next()?;
1382 if matches.next().is_none() {
1384 Some((*first).to_string())
1385 } else {
1386 None
1387 }
1388}
1389
1390fn make_subsec_date(
1393 tags: &[Tag],
1394 date_tag: &str,
1395 subsec_tag: &str,
1396 offset_tag: &str,
1397 output_name: &str,
1398) -> Option<Tag> {
1399 let dt = find_tag_value(tags, date_tag)?;
1400 if dt.is_empty() {
1401 return None;
1402 }
1403
1404 let subsec = find_tag_value(tags, subsec_tag).unwrap_or_default();
1405 let offset = find_tag_value(tags, offset_tag).unwrap_or_default();
1406
1407 let mut result = dt.clone();
1408 let mut modified = false;
1409
1410 if !subsec.is_empty() && !dt.contains('.') {
1412 result = format!("{}.{}", result, subsec.trim());
1413 modified = true;
1414 }
1415 if !(offset.is_empty() || dt.contains('+') || dt.len() > 10 && dt[10..].contains('-')) {
1417 result = format!("{}{}", result, offset.trim());
1418 modified = true;
1419 }
1420
1421 if !modified {
1422 return None;
1423 }
1424
1425 Some(mk_composite(
1426 output_name,
1427 output_name,
1428 Value::String(result),
1429 ))
1430}
1431
1432fn compute_canon_composites(tags: &[Tag]) -> Option<Vec<Tag>> {
1434 let make = find_tag_value(tags, "Make").unwrap_or_default();
1436 if !make.contains("Canon") {
1437 return None;
1438 }
1439
1440 let mut result = Vec::new();
1441
1442 if let Some(cd) = find_tag_f64(tags, "ContinuousDrive") {
1445 let st = find_tag_f64(tags, "SelfTimer").unwrap_or(0.0);
1446 let pv = if cd != 0.0 {
1447 "Continuous Shooting"
1448 } else if st != 0.0 {
1449 "Self-timer Operation"
1450 } else {
1451 "Single-frame Shooting"
1452 };
1453 result.push(mk_composite(
1454 "DriveMode",
1455 "Drive Mode",
1456 Value::String(pv.to_string()),
1457 ));
1458 }
1459
1460 if let Some(em_tag) = find_tag(tags, "CanonExposureMode") {
1464 let em_raw = em_tag.raw_value.as_f64().map(|f| f as i64);
1465 let bulb_set = find_tag(tags, "BulbDuration")
1466 .and_then(|t| t.raw_value.as_f64())
1467 .map(|v| v != 0.0)
1468 .unwrap_or(false);
1469 let print = if em_raw.is_some_and(|v| v != 0) {
1470 if em_raw == Some(4) && bulb_set {
1471 "Bulb".to_string()
1472 } else {
1473 em_tag.print_value.clone()
1474 }
1475 } else if let Some(easy) = find_tag(tags, "EasyMode") {
1476 easy.print_value.clone()
1477 } else {
1478 em_tag.print_value.clone()
1479 };
1480 result.push(mk_composite(
1481 "ShootingMode",
1482 "Shooting Mode",
1483 Value::String(print),
1484 ));
1485 }
1486
1487 if let (Some(dir_idx), Some(file_idx)) = (
1493 find_tag_value(tags, "DirectoryIndex"),
1494 find_tag_value(tags, "FileIndex"),
1495 ) {
1496 if let (Ok(di), Ok(fi)) = (
1497 dir_idx.trim().parse::<i64>(),
1498 file_idx.trim().parse::<i64>(),
1499 ) {
1500 if di > 0 || fi > 0 {
1501 let (di2, fi2) = if fi == 10000 {
1503 (di + 1, 1i64)
1504 } else {
1505 (di, fi)
1506 };
1507 let combined = format!("{:03}{:04}", di2, fi2);
1508 let len = combined.len();
1510 let print = if len > 4 {
1511 format!("{}-{}", &combined[..len - 4], &combined[len - 4..])
1512 } else {
1513 combined.clone()
1514 };
1515 let t = Tag {
1516 id: TagId::Text("FileNumber".into()),
1517 name: "FileNumber".into(),
1518 description: "File Number".into(),
1519 group: TagGroup {
1520 family0: "Composite".into(),
1521 family1: "Composite".into(),
1522 family2: "Image".into(),
1523 family3: "Main".into(),
1524 },
1525 raw_value: Value::String(combined),
1526 print_value: print,
1527 priority: 0,
1528 };
1529 result.push(t);
1530 }
1531 }
1532 }
1533
1534 if crate::metadata::exif::keep_duplicates() || find_tag(tags, "ISO").is_none() {
1540 let camera_iso_str = find_tag_value(tags, "CameraISO");
1541 let iso_val = camera_iso_str
1542 .as_deref()
1543 .and_then(|ci| ci.trim().parse::<f64>().ok())
1544 .filter(|&v| v > 0.0);
1545 let iso = iso_val.or_else(|| {
1546 let base = find_tag_f64(tags, "BaseISO")?;
1547 let auto = find_tag_f64(tags, "AutoISO")?;
1548 if base > 0.0 && auto > 0.0 {
1549 Some(base * auto / 100.0)
1550 } else {
1551 None
1552 }
1553 });
1554 if let Some(iso_v) = iso {
1555 result.push(mk_composite(
1556 "ISO",
1557 "ISO",
1558 Value::String(format!("{:.0}", iso_v)),
1559 ));
1560 }
1561 }
1562
1563 if let Some(flash_bits_tag) = find_tag(tags, "FlashBits") {
1567 let fb_raw = flash_bits_tag
1568 .raw_value
1569 .as_u64()
1570 .or_else(|| flash_bits_tag.raw_value.as_f64().map(|v| v as u64))
1571 .unwrap_or(0);
1572 if fb_raw != 0 {
1573 let flash_type = if (fb_raw & (1 << 14)) != 0 {
1574 "External"
1575 } else {
1576 "Built-In Flash"
1577 };
1578 result.push(mk_composite(
1579 "FlashType",
1580 "Flash Type",
1581 Value::String(flash_type.to_string()),
1582 ));
1583 }
1584 }
1585
1586 if let Some(flash_bits_tag) = find_tag(tags, "FlashBits") {
1590 let fb_raw = flash_bits_tag
1591 .raw_value
1592 .as_u64()
1593 .or_else(|| flash_bits_tag.raw_value.as_f64().map(|v| v as u64))
1594 .unwrap_or(0);
1595 if fb_raw != 0 {
1596 if let Some(cfm_tag) = find_tag(tags, "CanonFlashMode") {
1597 let cfm_raw = cfm_tag
1598 .raw_value
1599 .as_u64()
1600 .or_else(|| cfm_tag.print_value.parse::<u64>().ok())
1601 .unwrap_or(99);
1602 let red_eye = if cfm_raw == 3 || cfm_raw == 4 || cfm_raw == 6 {
1603 "On"
1604 } else {
1605 "Off"
1606 };
1607 result.push(mk_composite(
1608 "RedEyeReduction",
1609 "Red Eye Reduction",
1610 Value::String(red_eye.to_string()),
1611 ));
1612 }
1613 }
1614 }
1615
1616 if let Some(flash_bits_tag) = find_tag(tags, "FlashBits") {
1620 let fb_raw = flash_bits_tag
1621 .raw_value
1622 .as_u64()
1623 .or_else(|| flash_bits_tag.raw_value.as_f64().map(|v| v as u64))
1624 .unwrap_or(0);
1625 if fb_raw != 0 {
1626 if let Some(fec_tag) = find_tag(tags, "FlashExposureComp") {
1627 result.push(mk_composite(
1628 "ConditionalFEC",
1629 "Flash Exposure Compensation",
1630 Value::String(fec_tag.print_value.clone()),
1631 ));
1632 }
1633 }
1634 }
1635
1636 if let Some(flash_bits_tag) = find_tag(tags, "FlashBits") {
1641 let fb_raw = flash_bits_tag
1642 .raw_value
1643 .as_u64()
1644 .or_else(|| flash_bits_tag.raw_value.as_f64().map(|v| v as u64))
1645 .unwrap_or(0);
1646 if fb_raw != 0 {
1647 let scs = find_tag(tags, "ShutterCurtainSync")
1648 .and_then(|t| t.raw_value.as_u64())
1649 .unwrap_or(0);
1650 let pv = if scs == 0 {
1651 "1st-curtain sync"
1652 } else {
1653 "2nd-curtain sync"
1654 };
1655 result.push(mk_composite(
1656 "ShutterCurtainHack",
1657 "Shutter Curtain Sync",
1658 Value::String(pv.to_string()),
1659 ));
1660 }
1661 }
1662
1663 if find_tag(tags, "WB_RGGBLevels").is_none() {
1668 if let Some(wb_tag) = find_tag(tags, "WhiteBalance") {
1669 let wb_val = wb_tag.raw_value.as_u64().unwrap_or(0);
1670 let wb_str = if let Some(asshot) = find_tag(tags, "WB_RGGBLevelsAsShot") {
1672 Some(asshot.print_value.clone())
1673 } else {
1674 let wb_tag_name = match wb_val {
1678 0 => "WB_RGGBLevelsAuto",
1679 1 => "WB_RGGBLevelsDaylight",
1680 2 => "WB_RGGBLevelsCloudy",
1681 3 => "WB_RGGBLevelsTungsten",
1682 4 => "WB_RGGBLevelsFluorescent",
1683 5 => "WB_RGGBLevelsFlash",
1684 6 => "WB_RGGBLevelsCustom",
1685 8 => "WB_RGGBLevelsShade",
1686 9 => "WB_RGGBLevelsKelvin",
1687 _ => "WB_RGGBLevelsAuto",
1688 };
1689 find_tag(tags, wb_tag_name).map(|t| t.print_value.clone())
1690 };
1691 if let Some(wb_levels) = wb_str {
1692 if !wb_levels.is_empty() {
1693 result.push(mk_composite(
1694 "WB_RGGBLevels",
1695 "WB RGGB Levels",
1696 Value::String(wb_levels),
1697 ));
1698 }
1699 }
1700 }
1701 }
1702
1703 if result.is_empty() {
1704 None
1705 } else {
1706 Some(result)
1707 }
1708}
1709
1710fn compute_wb_balance(tags: &[Tag]) -> Option<Vec<Tag>> {
1712 let mut result = Vec::new();
1715
1716 if let Some(wb) = find_tag(tags, "WB_RGGBLevels")
1720 .or_else(|| find_tag(tags, "WB_RGBGLevels"))
1721 .or_else(|| find_tag(tags, "WB_RBLevels"))
1722 {
1723 let parts: Vec<f64> = match &wb.raw_value {
1725 Value::List(items) => items.iter().filter_map(|v| v.as_f64()).collect(),
1726 Value::String(s) => s
1727 .split_whitespace()
1728 .filter_map(|p| p.parse().ok())
1729 .collect(),
1730 _ => Vec::new(),
1731 };
1732 if parts.len() >= 4 {
1733 let (ri, g1i, g2i, bi) = if wb.name.contains("RGBG") {
1737 (0, 1, 3, 2)
1738 } else {
1739 (0, 1, 2, 3)
1740 };
1741 let green = (parts[g1i] + parts[g2i]) / 2.0;
1742 if green > 0.0 {
1743 let red_bal = (parts[ri] / green * 1e6 + 0.5).floor() * 1e-6;
1745 let blue_bal = (parts[bi] / green * 1e6 + 0.5).floor() * 1e-6;
1746 result.push(mk_composite(
1747 "RedBalance",
1748 "Red Balance",
1749 Value::String(crate::value::format_g15(red_bal)),
1750 ));
1751 result.push(mk_composite(
1752 "BlueBalance",
1753 "Blue Balance",
1754 Value::String(crate::value::format_g15(blue_bal)),
1755 ));
1756 }
1757 } else if parts.len() == 2 {
1758 let (r, b) = (parts[0], parts[1]);
1761 let red_bal = (r / 256.0 * 1e6 + 0.5).floor() * 1e-6;
1762 let blue_bal = (b / 256.0 * 1e6 + 0.5).floor() * 1e-6;
1763 result.push(mk_composite(
1764 "RedBalance",
1765 "Red Balance",
1766 Value::String(crate::value::format_g15(red_bal)),
1767 ));
1768 result.push(mk_composite(
1769 "BlueBalance",
1770 "Blue Balance",
1771 Value::String(crate::value::format_g15(blue_bal)),
1772 ));
1773 }
1774 } else if let Some(wb) = find_tag(tags, "WB_GRGBLevels") {
1775 let parts: Vec<f64> = match &wb.raw_value {
1778 Value::List(items) => items.iter().filter_map(|v| v.as_f64()).collect(),
1779 Value::String(s) => s
1780 .split_whitespace()
1781 .filter_map(|p| p.parse().ok())
1782 .collect(),
1783 _ => Vec::new(),
1784 };
1785 if parts.len() >= 4 {
1786 let g1 = parts[0]; let r = parts[1]; let g2 = parts[2]; let b = parts[3]; let g_avg = (g1 + g2) / 2.0;
1791 if g_avg > 0.0 {
1792 let red_bal = (r / g_avg * 1e6 + 0.5) as i64 as f64 * 1e-6;
1794 let blue_bal = (b / g_avg * 1e6 + 0.5) as i64 as f64 * 1e-6;
1795 let red_print = crate::value::format_g15(red_bal);
1796 let blue_print = crate::value::format_g15(blue_bal);
1797 result.push(mk_composite(
1798 "RedBalance",
1799 "Red Balance",
1800 Value::String(red_print),
1801 ));
1802 result.push(mk_composite(
1803 "BlueBalance",
1804 "Blue Balance",
1805 Value::String(blue_print),
1806 ));
1807 }
1808 }
1809 }
1810
1811 if result.is_empty() {
1814 let r_tag = find_tag(tags, "WBRedLevel").and_then(|t| t.raw_value.as_f64());
1815 let g_tag = find_tag(tags, "WBGreenLevel").and_then(|t| t.raw_value.as_f64());
1816 let b_tag = find_tag(tags, "WBBlueLevel").and_then(|t| t.raw_value.as_f64());
1817 if let (Some(r), Some(g), Some(b)) = (r_tag, g_tag, b_tag) {
1818 if g > 0.0 {
1819 let red_bal = (r / g * 1e6 + 0.5) as i64 as f64 * 1e-6;
1821 let blue_bal = (b / g * 1e6 + 0.5) as i64 as f64 * 1e-6;
1822 result.push(mk_composite(
1823 "RedBalance",
1824 "Red Balance",
1825 Value::String(crate::value::format_g15(red_bal)),
1826 ));
1827 result.push(mk_composite(
1828 "BlueBalance",
1829 "Blue Balance",
1830 Value::String(crate::value::format_g15(blue_bal)),
1831 ));
1832 }
1833 }
1834 }
1835
1836 if result.is_empty() {
1837 None
1838 } else {
1839 Some(result)
1840 }
1841}
1842
1843fn compute_panasonic_advanced_scene_mode(tags: &[Tag]) -> Option<Tag> {
1847 let model = find_tag_value(tags, "Model")?;
1849 let scene_mode_raw = find_tag(tags, "SceneMode")
1851 .and_then(|t| t.raw_value.as_u64())
1852 .unwrap_or(0);
1853 let adv_type_raw = find_tag(tags, "AdvancedSceneType")
1854 .and_then(|t| t.raw_value.as_u64())
1855 .unwrap_or(1);
1856
1857 let make = find_tag_value(tags, "Make").unwrap_or_default();
1859 if !make.contains("Panasonic") && !make.contains("Leica") {
1860 return None;
1861 }
1862
1863 let _model_key = format!("{} {} {}", model, scene_mode_raw, adv_type_raw);
1865 let generic_key = format!("{} {}", scene_mode_raw, adv_type_raw);
1866
1867 let model_val = if model == "DMC-TZ40" {
1869 match generic_key.as_str() {
1870 "90 1" => Some("Expressive"),
1871 "90 2" => Some("Retro"),
1872 "90 3" => Some("High Key"),
1873 "90 4" => Some("Sepia"),
1874 "90 5" => Some("High Dynamic"),
1875 "90 6" => Some("Miniature"),
1876 "90 9" => Some("Low Key"),
1877 "90 10" => Some("Toy Effect"),
1878 "90 11" => Some("Dynamic Monochrome"),
1879 "90 12" => Some("Soft"),
1880 _ => None,
1881 }
1882 } else {
1883 None
1884 };
1885
1886 let print_val = if let Some(v) = model_val {
1887 v.to_string()
1888 } else {
1889 match generic_key.as_str() {
1891 "0 1" => "Off".to_string(),
1892 "2 2" => "Outdoor Portrait".to_string(),
1893 "2 3" => "Indoor Portrait".to_string(),
1894 "2 4" => "Creative Portrait".to_string(),
1895 "3 2" => "Nature".to_string(),
1896 "3 3" => "Architecture".to_string(),
1897 "3 4" => "Creative Scenery".to_string(),
1898 "4 2" => "Outdoor Sports".to_string(),
1899 "4 3" => "Indoor Sports".to_string(),
1900 "4 4" => "Creative Sports".to_string(),
1901 "9 2" => "Flower".to_string(),
1902 "9 3" => "Objects".to_string(),
1903 "9 4" => "Creative Macro".to_string(),
1904 "18 1" => "High Sensitivity".to_string(),
1905 "20 1" => "Fireworks".to_string(),
1906 "21 2" => "Illuminations".to_string(),
1907 "21 4" => "Creative Night Scenery".to_string(),
1908 "26 1" => "High-speed Burst (shot 1)".to_string(),
1909 "27 1" => "High-speed Burst (shot 2)".to_string(),
1910 "29 1" => "Snow".to_string(),
1911 "30 1" => "Starry Sky".to_string(),
1912 "31 1" => "Beach".to_string(),
1913 "36 1" => "High-speed Burst (shot 3)".to_string(),
1914 "39 1" => "Aerial Photo / Underwater / Multi-aspect".to_string(),
1915 "45 2" => "Cinema".to_string(),
1916 "45 7" => "Expressive".to_string(),
1917 "45 8" => "Retro".to_string(),
1918 "45 9" => "Pure".to_string(),
1919 "45 10" => "Elegant".to_string(),
1920 "45 12" => "Monochrome".to_string(),
1921 "45 13" => "Dynamic Art".to_string(),
1922 "45 14" => "Silhouette".to_string(),
1923 "51 2" => "HDR Art".to_string(),
1924 "51 3" => "HDR B&W".to_string(),
1925 "59 1" => "Expressive".to_string(),
1926 "59 2" => "Retro".to_string(),
1927 "59 3" => "High Key".to_string(),
1928 "59 4" => "Sepia".to_string(),
1929 "59 5" => "High Dynamic".to_string(),
1930 "59 6" => "Miniature".to_string(),
1931 "59 9" => "Low Key".to_string(),
1932 "59 10" => "Toy Effect".to_string(),
1933 "59 11" => "Dynamic Monochrome".to_string(),
1934 "59 12" => "Soft".to_string(),
1935 "66 1" => "Impressive Art".to_string(),
1936 "66 2" => "Cross Process".to_string(),
1937 "66 3" => "Color Select".to_string(),
1938 "66 4" => "Star".to_string(),
1939 "90 3" => "Old Days".to_string(),
1940 "90 4" => "Sunshine".to_string(),
1941 "90 5" => "Bleach Bypass".to_string(),
1942 "90 6" => "Toy Pop".to_string(),
1943 "90 7" => "Fantasy".to_string(),
1944 "90 8" => "Monochrome".to_string(),
1945 "90 9" => "Rough Monochrome".to_string(),
1946 "90 10" => "Silky Monochrome".to_string(),
1947 "92 1" => "Handheld Night Shot".to_string(),
1948 _ => {
1949 let shooting_mode_name = panasonic_shooting_mode(scene_mode_raw);
1952 if let Some(name) = shooting_mode_name {
1953 match adv_type_raw {
1954 1 => name.to_string(),
1955 5 => format!("{} (intelligent auto)", name),
1956 7 => format!("{} (intelligent auto plus)", name),
1957 n => format!("{} ({})", name, n),
1958 }
1959 } else {
1960 format!("Unknown ({} {})", scene_mode_raw, adv_type_raw)
1961 }
1962 }
1963 }
1964 };
1965
1966 let raw_str = format!("{} {} {}", model, scene_mode_raw, adv_type_raw);
1968 Some(mk_composite_raw(
1969 "AdvancedSceneMode",
1970 "Advanced Scene Mode",
1971 Value::String(raw_str),
1972 print_val,
1973 ))
1974}
1975
1976fn panasonic_shooting_mode(val: u64) -> Option<&'static str> {
1978 match val {
1979 1 => Some("Normal"),
1980 2 => Some("Portrait"),
1981 3 => Some("Scenery"),
1982 4 => Some("Sports"),
1983 5 => Some("Night Portrait"),
1984 6 => Some("Program"),
1985 7 => Some("Aperture Priority"),
1986 8 => Some("Shutter Priority"),
1987 9 => Some("Macro"),
1988 10 => Some("Spot"),
1989 11 => Some("Manual"),
1990 12 => Some("Movie Preview"),
1991 13 => Some("Panning"),
1992 14 => Some("Simple"),
1993 15 => Some("Color Effects"),
1994 16 => Some("Self Portrait"),
1995 17 => Some("Economy"),
1996 18 => Some("Fireworks"),
1997 19 => Some("Party"),
1998 20 => Some("Snow"),
1999 21 => Some("Night Scenery"),
2000 22 => Some("Food"),
2001 23 => Some("Baby"),
2002 24 => Some("Soft Skin"),
2003 25 => Some("Candlelight"),
2004 26 => Some("Starry Night"),
2005 27 => Some("High Sensitivity"),
2006 28 => Some("Panorama Assist"),
2007 29 => Some("Underwater"),
2008 30 => Some("Beach"),
2009 31 => Some("Aerial Photo"),
2010 32 => Some("Sunset"),
2011 33 => Some("Pet"),
2012 34 => Some("Intelligent ISO"),
2013 35 => Some("Clipboard"),
2014 36 => Some("High Speed Continuous Shooting"),
2015 37 => Some("Intelligent Auto"),
2016 39 => Some("Multi-aspect"),
2017 41 => Some("Transform"),
2018 42 => Some("Flash Burst"),
2019 43 => Some("Pin Hole"),
2020 44 => Some("Film Grain"),
2021 45 => Some("My Color"),
2022 46 => Some("Photo Frame"),
2023 48 => Some("Movie"),
2024 51 => Some("HDR"),
2025 52 => Some("Peripheral Defocus"),
2026 55 => Some("Handheld Night Shot"),
2027 57 => Some("3D"),
2028 59 => Some("Creative Control"),
2029 60 => Some("Intelligent Auto Plus"),
2030 62 => Some("Panorama"),
2031 63 => Some("Glass Through"),
2032 64 => Some("HDR"),
2033 66 => Some("Digital Filter"),
2034 67 => Some("Clear Portrait"),
2035 68 => Some("Silky Skin"),
2036 69 => Some("Backlit Softness"),
2037 70 => Some("Clear in Backlight"),
2038 71 => Some("Relaxing Tone"),
2039 72 => Some("Sweet Child's Face"),
2040 73 => Some("Distinct Scenery"),
2041 74 => Some("Bright Blue Sky"),
2042 75 => Some("Romantic Sunset Glow"),
2043 76 => Some("Vivid Sunset Glow"),
2044 77 => Some("Glistening Water"),
2045 78 => Some("Clear Nightscape"),
2046 79 => Some("Cool Night Sky"),
2047 _ => None,
2048 }
2049}
2050
2051fn compute_dof(tags: &[Tag]) -> Option<Vec<Tag>> {
2056 let f = find_tag_f64(tags, "FocalLength")?; let aperture = find_tag_f64(tags, "FNumber")?;
2058 let coc = find_tag_f64(tags, "CircleOfConfusion").or_else(|| {
2059 find_tag_value(tags, "CircleOfConfusion")
2060 .and_then(|s| s.split_whitespace().next()?.parse::<f64>().ok())
2061 })?;
2062
2063 if f <= 0.0 || coc <= 0.0 {
2064 return None;
2065 }
2066
2067 let d = find_tag_f64(tags, "FocusDistance")
2069 .or_else(|| {
2070 find_tag_value(tags, "FocusDistance")
2071 .and_then(|s| s.split_whitespace().next()?.parse::<f64>().ok())
2072 .filter(|v| v.is_finite())
2073 })
2074 .or_else(|| find_tag_f64(tags, "SubjectDistance").filter(|&v| v > 0.0))
2076 .or_else(|| {
2077 find_tag_value(tags, "ObjectDistance")
2080 .and_then(|s| s.split_whitespace().next()?.parse().ok())
2081 .filter(|&v: &f64| v > 0.0)
2082 .or_else(|| find_tag_f64(tags, "ObjectDistance").filter(|&v| v > 0.0))
2083 })
2084 .or_else(|| {
2085 find_tag_f64(tags, "ApproximateFocusDistance")
2086 .filter(|&v| v > 0.0)
2087 .or_else(|| {
2088 find_tag_value(tags, "ApproximateFocusDistance")
2089 .and_then(|s| s.split_whitespace().next()?.parse().ok())
2090 .filter(|&v: &f64| v > 0.0)
2091 })
2092 })
2093 .or_else(|| {
2094 let upper = find_tag_f64(tags, "FocusDistanceUpper").or_else(|| {
2095 find_tag_value(tags, "FocusDistanceUpper")
2096 .and_then(|s| s.split_whitespace().next()?.parse().ok())
2097 });
2098 let lower = find_tag_f64(tags, "FocusDistanceLower").or_else(|| {
2099 find_tag_value(tags, "FocusDistanceLower")
2100 .and_then(|s| s.split_whitespace().next()?.parse().ok())
2101 });
2102 match (upper, lower) {
2103 (Some(u), Some(l)) => Some((u + l) / 2.0),
2104 _ => None,
2105 }
2106 });
2107
2108 let d = d?;
2110 let d = if d == 0.0 { 1e10 } else { d }; let t = aperture * coc * (d * 1000.0 - f) / (f * f);
2114 let near = d / (1.0 + t);
2115 let mut far = d / (1.0 - t);
2116 if far < 0.0 {
2117 far = 0.0;
2118 } let dof_str = if far == 0.0 {
2121 format!("inf ({:.2} m - inf)", near)
2122 } else {
2123 let dof = far - near;
2124 if dof > 0.0 && dof < 0.02 {
2125 format!("{:.3} m ({:.3} - {:.3} m)", dof, near, far)
2126 } else {
2127 format!("{:.2} m ({:.2} - {:.2} m)", dof, near, far)
2128 }
2129 };
2130
2131 Some(vec![mk_composite(
2132 "DOF",
2133 "Depth of Field",
2134 Value::String(dof_str),
2135 )])
2136}
2137
2138pub fn compute_geolocation(tags: &[Tag]) -> Option<Vec<Tag>> {
2144 use crate::geolocation::GeolocationDb;
2145 use std::sync::OnceLock;
2146
2147 let lat_tag = find_tag(tags, "GPSLatitude")?;
2149 let lon_tag = find_tag(tags, "GPSLongitude")?;
2150 let lat_ref = find_tag_value(tags, "GPSLatitudeRef").unwrap_or_default();
2151 let lon_ref = find_tag_value(tags, "GPSLongitudeRef").unwrap_or_default();
2152
2153 let lat = parse_gps_decimal(&lat_tag.raw_value, &lat_ref)?;
2154 let lon = parse_gps_decimal(&lon_tag.raw_value, &lon_ref)?;
2155
2156 static DB: OnceLock<Option<GeolocationDb>> = OnceLock::new();
2158 let db = DB.get_or_init(GeolocationDb::load_default);
2159
2160 let db = db.as_ref()?;
2161 let city = db.find_nearest(lat, lon)?;
2162
2163 let mut geo_tags = Vec::new();
2164 geo_tags.push(mk_composite(
2165 "GeolocationCity",
2166 "Geolocation City",
2167 Value::String(city.name.clone()),
2168 ));
2169 geo_tags.push(mk_composite(
2170 "GeolocationCountryCode",
2171 "Geolocation Country Code",
2172 Value::String(city.country_code.clone()),
2173 ));
2174 geo_tags.push(mk_composite(
2175 "GeolocationCountry",
2176 "Geolocation Country",
2177 Value::String(city.country.clone()),
2178 ));
2179 if !city.region.is_empty() {
2180 geo_tags.push(mk_composite(
2181 "GeolocationRegion",
2182 "Geolocation Region",
2183 Value::String(city.region.clone()),
2184 ));
2185 }
2186 if !city.subregion.is_empty() {
2187 geo_tags.push(mk_composite(
2188 "GeolocationSubregion",
2189 "Geolocation Subregion",
2190 Value::String(city.subregion.clone()),
2191 ));
2192 }
2193 if !city.timezone.is_empty() {
2194 geo_tags.push(mk_composite(
2195 "GeolocationTimeZone",
2196 "Geolocation Time Zone",
2197 Value::String(city.timezone.clone()),
2198 ));
2199 }
2200
2201 Some(geo_tags)
2202}
2203
2204fn parse_gps_decimal(value: &Value, reference: &str) -> Option<f64> {
2205 let decimal = match value {
2206 Value::List(items) if items.len() >= 3 => {
2207 let deg = items[0].as_f64()?;
2208 let min = items[1].as_f64()?;
2209 let sec = items[2].as_f64()?;
2210 deg + min / 60.0 + sec / 3600.0
2211 }
2212 Value::URational(n, d) if *d > 0 => *n as f64 / *d as f64,
2213 _ => return None,
2214 };
2215 let sign = if reference == "S" || reference == "W" {
2216 -1.0
2217 } else {
2218 1.0
2219 };
2220 Some(decimal * sign)
2221}
2222
2223fn compute_hyperfocal(tags: &[Tag]) -> Option<Tag> {
2226 let fl = find_tag_f64(tags, "FocalLength")?;
2227 let fnum = find_tag_f64(tags, "FNumber")?;
2228
2229 if fl <= 0.0 || fnum <= 0.0 {
2230 return None;
2231 }
2232
2233 let coc = find_tag_f64(tags, "CircleOfConfusion").or_else(|| {
2235 find_tag_value(tags, "CircleOfConfusion")
2236 .and_then(|s| s.split_whitespace().next()?.parse::<f64>().ok())
2237 })?;
2238
2239 if coc <= 0.0 {
2240 return None;
2241 }
2242
2243 let h_m = (fl * fl) / (fnum * coc * 1000.0);
2247
2248 Some(mk_composite(
2249 "HyperfocalDistance",
2250 "Hyperfocal Distance",
2251 Value::String(format!("{:.2} m", h_m)),
2252 ))
2253}
2254
2255fn compute_quicktime_rotation(tags: &[Tag]) -> Option<Tag> {
2262 let track = tags
2263 .iter()
2264 .find(|t| {
2265 t.name == "HandlerType"
2266 && t.group.family0 == "QuickTime"
2267 && t.print_value == "Video Track"
2268 })?
2269 .group
2270 .family1
2271 .clone();
2272 let matrix = tags.iter().find(|t| {
2273 t.name == "MatrixStructure" && t.group.family0 == "QuickTime" && t.group.family1 == track
2274 })?;
2275 let angle = crate::formats::quicktime::rotation_angle_from_matrix(&matrix.print_value)?;
2276 Some(mk_composite(
2278 "Rotation",
2279 "Rotation",
2280 Value::String(format!("{}", angle)),
2281 ))
2282}
2283
2284fn compute_olympus_extender_status(tags: &[Tag]) -> Option<Tag> {
2294 let olympus = |name: &str| -> Option<&Tag> {
2295 tags.iter()
2296 .find(|t| t.name == name && t.group.family1 == "Olympus")
2297 };
2298 let extender = olympus("Extender")?;
2299 let lens_type = olympus("LensType")?;
2300 let max_aperture: f64 = find_tag(tags, "MaxApertureValue")?
2304 .print_value
2305 .parse()
2306 .ok()?;
2307
2308 let key = crate::tags::makernotes::olympus_extender_key(&extender.print_value);
2310 let info: Vec<&str> = key.split_whitespace().collect();
2311 let status = if info.len() < 2 || u32::from_str_radix(info[1], 16).unwrap_or(0) == 0 {
2314 0u32
2315 } else if format!("{} {}", info[0], info[1]) != "0 04" {
2316 1
2318 } else {
2319 match lens_max_aperture(&lens_type.print_value) {
2322 None => 1,
2323 Some(f) => {
2324 if max_aperture - f > 0.2 {
2325 1
2326 } else {
2327 2
2328 }
2329 }
2330 }
2331 };
2332
2333 let print = match status {
2334 0 => "Not attached",
2335 1 => "Attached",
2336 _ => "Removed",
2337 };
2338 let mut t = mk_composite_raw(
2339 "ExtenderStatus",
2340 "Extender Status",
2341 Value::U32(status),
2342 print.to_string(),
2343 );
2344 t.group.family2 = "Camera".into();
2345 Some(t)
2346}
2347
2348fn lens_max_aperture(lens_type: &str) -> Option<f64> {
2350 let rest = lens_type.split(" F").nth(1)?;
2351 let end = rest
2352 .find(|c: char| !c.is_ascii_digit() && c != '.')
2353 .unwrap_or(rest.len());
2354 rest[..end].parse().ok()
2355}
2356
2357fn compute_xmp_flash(tags: &[Tag]) -> Option<Tag> {
2370 let xmp = |name: &str| -> Option<&Tag> {
2371 tags.iter()
2372 .find(|t| t.name == name && t.group.family0 == "XMP")
2373 };
2374 let get_bool = |name: &str| -> Option<bool> {
2378 xmp(name).map(|t| t.raw_value.to_display_string().eq_ignore_ascii_case("true"))
2379 };
2380 let get_int = |name: &str| -> Option<u32> {
2381 xmp(name).and_then(|t| t.raw_value.to_display_string().parse::<u32>().ok())
2382 };
2383
2384 let fired = get_bool("FlashFired");
2385 let ret = get_int("FlashReturn");
2386 let mode = get_int("FlashMode");
2387 let function = get_bool("FlashFunction");
2388 let red_eye = get_bool("FlashRedEyeMode");
2389 if fired.is_none() && ret.is_none() && mode.is_none() && function.is_none() && red_eye.is_none()
2390 {
2391 return None;
2392 }
2393
2394 let val: u32 = u32::from(fired.unwrap_or(false))
2395 | ((ret.unwrap_or(0) & 0x03) << 1)
2396 | ((mode.unwrap_or(0) & 0x03) << 3)
2397 | (if function.unwrap_or(false) { 0x20 } else { 0 })
2398 | (if red_eye.unwrap_or(false) { 0x40 } else { 0 });
2399
2400 let mut t = mk_composite_raw(
2401 "Flash",
2402 "Flash",
2403 Value::String(val.to_string()),
2404 crate::metadata::xmp::flash_numeric_to_string(val),
2405 );
2406 t.group.family2 = "Camera".into();
2408 Some(t)
2409}
2410
2411fn mk_composite_raw(name: &str, description: &str, value: Value, print_value: String) -> Tag {
2412 Tag {
2413 id: TagId::Text(name.to_string()),
2414 name: name.to_string(),
2415 description: description.to_string(),
2416 group: TagGroup {
2417 family0: "Composite".to_string(),
2418 family1: "Composite".to_string(),
2419 family2: "Other".to_string(),
2420 family3: "Main".into(),
2421 },
2422 raw_value: value,
2423 print_value,
2424 priority: 0,
2425 }
2426}
2427
2428fn mk_composite(name: &str, description: &str, value: Value) -> Tag {
2429 let pv = value.to_display_string();
2430 Tag {
2431 id: TagId::Text(name.to_string()),
2432 name: name.to_string(),
2433 description: description.to_string(),
2434 group: TagGroup {
2435 family0: "Composite".to_string(),
2436 family1: "Composite".to_string(),
2437 family2: "Other".to_string(),
2438 family3: "Main".into(),
2439 },
2440 raw_value: value,
2441 print_value: pv,
2442 priority: 0,
2443 }
2444}
2445
2446fn compute_nikon_lens_id(tags: &[Tag]) -> Option<String> {
2451 let lens_id_num = find_tag(tags, "LensIDNumber").and_then(|t| {
2453 t.raw_value
2454 .as_u64()
2455 .or_else(|| t.print_value.trim().parse::<u64>().ok())
2456 })? as u8;
2457
2458 let lens_fstops_byte = find_tag(tags, "LensFStops")
2462 .and_then(|t| {
2463 match &t.raw_value {
2464 Value::Undefined(bytes) | Value::Binary(bytes) if !bytes.is_empty() => {
2465 Some(bytes[0])
2466 }
2467 _ => {
2468 t.print_value
2470 .trim()
2471 .parse::<f64>()
2472 .ok()
2473 .map(|v| (v * 12.0).round() as u8)
2474 }
2475 }
2476 })
2477 .unwrap_or(0);
2478
2479 let min_focal_byte = find_tag(tags, "MinFocalLength")
2481 .and_then(|t| {
2482 t.raw_value
2483 .as_f64()
2484 .or_else(|| t.print_value.split_whitespace().next()?.parse::<f64>().ok())
2485 .filter(|&v| v > 0.0)
2486 .map(|v| (24.0 * (v / 5.0).log2()).round() as u8)
2487 })
2488 .unwrap_or(0);
2489
2490 let max_focal_byte = find_tag(tags, "MaxFocalLength")
2492 .and_then(|t| {
2493 t.raw_value
2494 .as_f64()
2495 .or_else(|| t.print_value.split_whitespace().next()?.parse::<f64>().ok())
2496 .filter(|&v| v > 0.0)
2497 .map(|v| (24.0 * (v / 5.0).log2()).round() as u8)
2498 })
2499 .unwrap_or(0);
2500
2501 let max_apt_min_byte = find_tag(tags, "MaxApertureAtMinFocal")
2503 .and_then(|t| {
2504 t.raw_value
2505 .as_f64()
2506 .or_else(|| t.print_value.trim().parse::<f64>().ok())
2507 .filter(|&v| v > 0.0)
2508 .map(|v| (24.0 * v.log2()).round() as u8)
2509 })
2510 .unwrap_or(0);
2511
2512 let max_apt_max_byte = find_tag(tags, "MaxApertureAtMaxFocal")
2514 .and_then(|t| {
2515 t.raw_value
2516 .as_f64()
2517 .or_else(|| t.print_value.trim().parse::<f64>().ok())
2518 .filter(|&v| v > 0.0)
2519 .map(|v| (24.0 * v.log2()).round() as u8)
2520 })
2521 .unwrap_or(0);
2522
2523 let mcu_version_byte = find_tag(tags, "MCUVersion")
2525 .and_then(|t| {
2526 t.raw_value
2527 .as_u64()
2528 .or_else(|| t.print_value.trim().parse::<u64>().ok())
2529 })
2530 .unwrap_or(0) as u8;
2531
2532 let lens_type_byte = find_tag(tags, "LensType")
2534 .and_then(|t| {
2535 t.raw_value
2536 .as_u64()
2537 .or_else(|| t.print_value.trim().parse::<u64>().ok())
2538 })
2539 .unwrap_or(0) as u8;
2540
2541 let key = [
2542 lens_id_num,
2543 lens_fstops_byte,
2544 min_focal_byte,
2545 max_focal_byte,
2546 max_apt_min_byte,
2547 max_apt_max_byte,
2548 mcu_version_byte,
2549 lens_type_byte,
2550 ];
2551
2552 nikon_lens_id_lookup(&key)
2553}
2554
2555fn nikon_lens_id_lookup(key: &[u8; 8]) -> Option<String> {
2558 for &(ref k, name) in NIKON_LENS_IDS {
2560 if k == key {
2561 return Some(name.to_string());
2562 }
2563 }
2564 let mut matches: Vec<&str> = Vec::new();
2567 for &(ref k, name) in NIKON_LENS_IDS {
2568 if k[0] == key[0]
2569 && k[1] == key[1]
2570 && k[2] == key[2]
2571 && k[3] == key[3]
2572 && k[4] == key[4]
2573 && k[5] == key[5]
2574 && k[7] == key[7]
2575 {
2576 matches.push(name);
2577 }
2578 }
2579 if matches.len() == 1 {
2580 return Some(matches[0].to_string());
2581 }
2582 None
2583}
2584
2585static NIKON_LENS_IDS: &[([u8; 8], &str)] = &[
2590 (
2591 [0x01, 0x58, 0x50, 0x50, 0x14, 0x14, 0x02, 0x00],
2592 "AF Nikkor 50mm f/1.8",
2593 ),
2594 (
2595 [0x01, 0x58, 0x50, 0x50, 0x14, 0x14, 0x05, 0x00],
2596 "AF Nikkor 50mm f/1.8",
2597 ),
2598 (
2599 [0x02, 0x42, 0x44, 0x5C, 0x2A, 0x34, 0x02, 0x00],
2600 "AF Zoom-Nikkor 35-70mm f/3.3-4.5",
2601 ),
2602 (
2603 [0x02, 0x42, 0x44, 0x5C, 0x2A, 0x34, 0x08, 0x00],
2604 "AF Zoom-Nikkor 35-70mm f/3.3-4.5",
2605 ),
2606 (
2607 [0x03, 0x48, 0x5C, 0x81, 0x30, 0x30, 0x02, 0x00],
2608 "AF Zoom-Nikkor 70-210mm f/4",
2609 ),
2610 (
2611 [0x04, 0x48, 0x3C, 0x3C, 0x24, 0x24, 0x03, 0x00],
2612 "AF Nikkor 28mm f/2.8",
2613 ),
2614 (
2615 [0x05, 0x54, 0x50, 0x50, 0x0C, 0x0C, 0x04, 0x00],
2616 "AF Nikkor 50mm f/1.4",
2617 ),
2618 (
2619 [0x06, 0x54, 0x53, 0x53, 0x24, 0x24, 0x06, 0x00],
2620 "AF Micro-Nikkor 55mm f/2.8",
2621 ),
2622 (
2623 [0x07, 0x40, 0x3C, 0x62, 0x2C, 0x34, 0x03, 0x00],
2624 "AF Zoom-Nikkor 28-85mm f/3.5-4.5",
2625 ),
2626 (
2627 [0x08, 0x40, 0x44, 0x6A, 0x2C, 0x34, 0x04, 0x00],
2628 "AF Zoom-Nikkor 35-105mm f/3.5-4.5",
2629 ),
2630 (
2631 [0x09, 0x48, 0x37, 0x37, 0x24, 0x24, 0x04, 0x00],
2632 "AF Nikkor 24mm f/2.8",
2633 ),
2634 (
2635 [0x0A, 0x48, 0x8E, 0x8E, 0x24, 0x24, 0x03, 0x00],
2636 "AF Nikkor 300mm f/2.8 IF-ED",
2637 ),
2638 (
2639 [0x0A, 0x48, 0x8E, 0x8E, 0x24, 0x24, 0x05, 0x00],
2640 "AF Nikkor 300mm f/2.8 IF-ED N",
2641 ),
2642 (
2643 [0x0B, 0x48, 0x7C, 0x7C, 0x24, 0x24, 0x05, 0x00],
2644 "AF Nikkor 180mm f/2.8 IF-ED",
2645 ),
2646 (
2647 [0x0D, 0x40, 0x44, 0x72, 0x2C, 0x34, 0x07, 0x00],
2648 "AF Zoom-Nikkor 35-135mm f/3.5-4.5",
2649 ),
2650 (
2651 [0x0E, 0x48, 0x5C, 0x81, 0x30, 0x30, 0x05, 0x00],
2652 "AF Zoom-Nikkor 70-210mm f/4",
2653 ),
2654 (
2655 [0x0F, 0x58, 0x50, 0x50, 0x14, 0x14, 0x05, 0x00],
2656 "AF Nikkor 50mm f/1.8 N",
2657 ),
2658 (
2659 [0x10, 0x48, 0x8E, 0x8E, 0x30, 0x30, 0x08, 0x00],
2660 "AF Nikkor 300mm f/4 IF-ED",
2661 ),
2662 (
2663 [0x11, 0x48, 0x44, 0x5C, 0x24, 0x24, 0x08, 0x00],
2664 "AF Zoom-Nikkor 35-70mm f/2.8",
2665 ),
2666 (
2667 [0x11, 0x48, 0x44, 0x5C, 0x24, 0x24, 0x15, 0x00],
2668 "AF Zoom-Nikkor 35-70mm f/2.8",
2669 ),
2670 (
2671 [0x12, 0x48, 0x5C, 0x81, 0x30, 0x3C, 0x09, 0x00],
2672 "AF Nikkor 70-210mm f/4-5.6",
2673 ),
2674 (
2675 [0x13, 0x42, 0x37, 0x50, 0x2A, 0x34, 0x0B, 0x00],
2676 "AF Zoom-Nikkor 24-50mm f/3.3-4.5",
2677 ),
2678 (
2679 [0x14, 0x48, 0x60, 0x80, 0x24, 0x24, 0x0B, 0x00],
2680 "AF Zoom-Nikkor 80-200mm f/2.8 ED",
2681 ),
2682 (
2683 [0x15, 0x4C, 0x62, 0x62, 0x14, 0x14, 0x0C, 0x00],
2684 "AF Nikkor 85mm f/1.8",
2685 ),
2686 (
2687 [0x17, 0x3C, 0xA0, 0xA0, 0x30, 0x30, 0x0F, 0x00],
2688 "Nikkor 500mm f/4 P ED IF",
2689 ),
2690 (
2691 [0x17, 0x3C, 0xA0, 0xA0, 0x30, 0x30, 0x11, 0x00],
2692 "Nikkor 500mm f/4 P ED IF",
2693 ),
2694 (
2695 [0x18, 0x40, 0x44, 0x72, 0x2C, 0x34, 0x0E, 0x00],
2696 "AF Zoom-Nikkor 35-135mm f/3.5-4.5 N",
2697 ),
2698 (
2699 [0x1A, 0x54, 0x44, 0x44, 0x18, 0x18, 0x11, 0x00],
2700 "AF Nikkor 35mm f/2",
2701 ),
2702 (
2703 [0x1B, 0x44, 0x5E, 0x8E, 0x34, 0x3C, 0x10, 0x00],
2704 "AF Zoom-Nikkor 75-300mm f/4.5-5.6",
2705 ),
2706 (
2707 [0x1C, 0x48, 0x30, 0x30, 0x24, 0x24, 0x12, 0x00],
2708 "AF Nikkor 20mm f/2.8",
2709 ),
2710 (
2711 [0x1D, 0x42, 0x44, 0x5C, 0x2A, 0x34, 0x12, 0x00],
2712 "AF Zoom-Nikkor 35-70mm f/3.3-4.5 N",
2713 ),
2714 (
2715 [0x1E, 0x54, 0x56, 0x56, 0x24, 0x24, 0x13, 0x00],
2716 "AF Micro-Nikkor 60mm f/2.8",
2717 ),
2718 (
2719 [0x1F, 0x54, 0x6A, 0x6A, 0x24, 0x24, 0x14, 0x00],
2720 "AF Micro-Nikkor 105mm f/2.8",
2721 ),
2722 (
2723 [0x20, 0x48, 0x60, 0x80, 0x24, 0x24, 0x15, 0x00],
2724 "AF Zoom-Nikkor 80-200mm f/2.8 ED",
2725 ),
2726 (
2727 [0x21, 0x40, 0x3C, 0x5C, 0x2C, 0x34, 0x16, 0x00],
2728 "AF Zoom-Nikkor 28-70mm f/3.5-4.5",
2729 ),
2730 (
2731 [0x22, 0x48, 0x72, 0x72, 0x18, 0x18, 0x16, 0x00],
2732 "AF DC-Nikkor 135mm f/2",
2733 ),
2734 (
2735 [0x23, 0x30, 0xBE, 0xCA, 0x3C, 0x48, 0x17, 0x00],
2736 "Zoom-Nikkor 1200-1700mm f/5.6-8 P ED IF",
2737 ),
2738 (
2739 [0x24, 0x48, 0x60, 0x80, 0x24, 0x24, 0x1A, 0x02],
2740 "AF Zoom-Nikkor 80-200mm f/2.8D ED",
2741 ),
2742 (
2743 [0x25, 0x48, 0x44, 0x5C, 0x24, 0x24, 0x1B, 0x02],
2744 "AF Zoom-Nikkor 35-70mm f/2.8D",
2745 ),
2746 (
2747 [0x25, 0x48, 0x44, 0x5C, 0x24, 0x24, 0x3A, 0x02],
2748 "AF Zoom-Nikkor 35-70mm f/2.8D",
2749 ),
2750 (
2751 [0x25, 0x48, 0x44, 0x5C, 0x24, 0x24, 0x52, 0x02],
2752 "AF Zoom-Nikkor 35-70mm f/2.8D",
2753 ),
2754 (
2755 [0x26, 0x40, 0x3C, 0x5C, 0x2C, 0x34, 0x1C, 0x02],
2756 "AF Zoom-Nikkor 28-70mm f/3.5-4.5D",
2757 ),
2758 (
2759 [0x27, 0x48, 0x8E, 0x8E, 0x24, 0x24, 0x1D, 0x02],
2760 "AF-I Nikkor 300mm f/2.8D IF-ED",
2761 ),
2762 (
2763 [0x27, 0x48, 0x8E, 0x8E, 0x24, 0x24, 0xF1, 0x02],
2764 "AF-I Nikkor 300mm f/2.8D IF-ED + TC-14E",
2765 ),
2766 (
2767 [0x27, 0x48, 0x8E, 0x8E, 0x24, 0x24, 0xE1, 0x02],
2768 "AF-I Nikkor 300mm f/2.8D IF-ED + TC-17E",
2769 ),
2770 (
2771 [0x27, 0x48, 0x8E, 0x8E, 0x24, 0x24, 0xF2, 0x02],
2772 "AF-I Nikkor 300mm f/2.8D IF-ED + TC-20E",
2773 ),
2774 (
2775 [0x28, 0x3C, 0xA6, 0xA6, 0x30, 0x30, 0x1D, 0x02],
2776 "AF-I Nikkor 600mm f/4D IF-ED",
2777 ),
2778 (
2779 [0x28, 0x3C, 0xA6, 0xA6, 0x30, 0x30, 0xF1, 0x02],
2780 "AF-I Nikkor 600mm f/4D IF-ED + TC-14E",
2781 ),
2782 (
2783 [0x28, 0x3C, 0xA6, 0xA6, 0x30, 0x30, 0xE1, 0x02],
2784 "AF-I Nikkor 600mm f/4D IF-ED + TC-17E",
2785 ),
2786 (
2787 [0x28, 0x3C, 0xA6, 0xA6, 0x30, 0x30, 0xF2, 0x02],
2788 "AF-I Nikkor 600mm f/4D IF-ED + TC-20E",
2789 ),
2790 (
2791 [0x2A, 0x54, 0x3C, 0x3C, 0x0C, 0x0C, 0x26, 0x02],
2792 "AF Nikkor 28mm f/1.4D",
2793 ),
2794 (
2795 [0x2B, 0x3C, 0x44, 0x60, 0x30, 0x3C, 0x1F, 0x02],
2796 "AF Zoom-Nikkor 35-80mm f/4-5.6D",
2797 ),
2798 (
2799 [0x2C, 0x48, 0x6A, 0x6A, 0x18, 0x18, 0x27, 0x02],
2800 "AF DC-Nikkor 105mm f/2D",
2801 ),
2802 (
2803 [0x2D, 0x48, 0x80, 0x80, 0x30, 0x30, 0x21, 0x02],
2804 "AF Micro-Nikkor 200mm f/4D IF-ED",
2805 ),
2806 (
2807 [0x2E, 0x48, 0x5C, 0x82, 0x30, 0x3C, 0x22, 0x02],
2808 "AF Nikkor 70-210mm f/4-5.6D",
2809 ),
2810 (
2811 [0x2E, 0x48, 0x5C, 0x82, 0x30, 0x3C, 0x28, 0x02],
2812 "AF Nikkor 70-210mm f/4-5.6D",
2813 ),
2814 (
2815 [0x30, 0x48, 0x98, 0x98, 0x24, 0x24, 0x24, 0x02],
2816 "AF-I Nikkor 400mm f/2.8D IF-ED",
2817 ),
2818 (
2819 [0x30, 0x48, 0x98, 0x98, 0x24, 0x24, 0xF1, 0x02],
2820 "AF-I Nikkor 400mm f/2.8D IF-ED + TC-14E",
2821 ),
2822 (
2823 [0x30, 0x48, 0x98, 0x98, 0x24, 0x24, 0xE1, 0x02],
2824 "AF-I Nikkor 400mm f/2.8D IF-ED + TC-17E",
2825 ),
2826 (
2827 [0x30, 0x48, 0x98, 0x98, 0x24, 0x24, 0xF2, 0x02],
2828 "AF-I Nikkor 400mm f/2.8D IF-ED + TC-20E",
2829 ),
2830 (
2831 [0x31, 0x54, 0x56, 0x56, 0x24, 0x24, 0x25, 0x02],
2832 "AF Micro-Nikkor 60mm f/2.8D",
2833 ),
2834 (
2835 [0x33, 0x48, 0x2D, 0x2D, 0x24, 0x24, 0x31, 0x02],
2836 "AF Nikkor 18mm f/2.8D",
2837 ),
2838 (
2839 [0x34, 0x48, 0x29, 0x29, 0x24, 0x24, 0x32, 0x02],
2840 "AF Fisheye Nikkor 16mm f/2.8D",
2841 ),
2842 (
2843 [0x35, 0x3C, 0xA0, 0xA0, 0x30, 0x30, 0x33, 0x02],
2844 "AF-I Nikkor 500mm f/4D IF-ED",
2845 ),
2846 (
2847 [0x35, 0x3C, 0xA0, 0xA0, 0x30, 0x30, 0xF1, 0x02],
2848 "AF-I Nikkor 500mm f/4D IF-ED + TC-14E",
2849 ),
2850 (
2851 [0x35, 0x3C, 0xA0, 0xA0, 0x30, 0x30, 0xE1, 0x02],
2852 "AF-I Nikkor 500mm f/4D IF-ED + TC-17E",
2853 ),
2854 (
2855 [0x35, 0x3C, 0xA0, 0xA0, 0x30, 0x30, 0xF2, 0x02],
2856 "AF-I Nikkor 500mm f/4D IF-ED + TC-20E",
2857 ),
2858 (
2859 [0x36, 0x48, 0x37, 0x37, 0x24, 0x24, 0x34, 0x02],
2860 "AF Nikkor 24mm f/2.8D",
2861 ),
2862 (
2863 [0x37, 0x48, 0x30, 0x30, 0x24, 0x24, 0x36, 0x02],
2864 "AF Nikkor 20mm f/2.8D",
2865 ),
2866 (
2867 [0x38, 0x4C, 0x62, 0x62, 0x14, 0x14, 0x37, 0x02],
2868 "AF Nikkor 85mm f/1.8D",
2869 ),
2870 (
2871 [0x3A, 0x40, 0x3C, 0x5C, 0x2C, 0x34, 0x39, 0x02],
2872 "AF Zoom-Nikkor 28-70mm f/3.5-4.5D",
2873 ),
2874 (
2875 [0x3B, 0x48, 0x44, 0x5C, 0x24, 0x24, 0x3A, 0x02],
2876 "AF Zoom-Nikkor 35-70mm f/2.8D N",
2877 ),
2878 (
2879 [0x3C, 0x48, 0x60, 0x80, 0x24, 0x24, 0x3B, 0x02],
2880 "AF Zoom-Nikkor 80-200mm f/2.8D ED",
2881 ),
2882 (
2883 [0x3D, 0x3C, 0x44, 0x60, 0x30, 0x3C, 0x3E, 0x02],
2884 "AF Zoom-Nikkor 35-80mm f/4-5.6D",
2885 ),
2886 (
2887 [0x3E, 0x48, 0x3C, 0x3C, 0x24, 0x24, 0x3D, 0x02],
2888 "AF Nikkor 28mm f/2.8D",
2889 ),
2890 (
2891 [0x3F, 0x40, 0x44, 0x6A, 0x2C, 0x34, 0x45, 0x02],
2892 "AF Zoom-Nikkor 35-105mm f/3.5-4.5D",
2893 ),
2894 (
2895 [0x41, 0x48, 0x7C, 0x7C, 0x24, 0x24, 0x43, 0x02],
2896 "AF Nikkor 180mm f/2.8D IF-ED",
2897 ),
2898 (
2899 [0x42, 0x54, 0x44, 0x44, 0x18, 0x18, 0x44, 0x02],
2900 "AF Nikkor 35mm f/2D",
2901 ),
2902 (
2903 [0x43, 0x54, 0x50, 0x50, 0x0C, 0x0C, 0x46, 0x02],
2904 "AF Nikkor 50mm f/1.4D",
2905 ),
2906 (
2907 [0x44, 0x44, 0x60, 0x80, 0x34, 0x3C, 0x47, 0x02],
2908 "AF Zoom-Nikkor 80-200mm f/4.5-5.6D",
2909 ),
2910 (
2911 [0x45, 0x40, 0x3C, 0x60, 0x2C, 0x3C, 0x48, 0x02],
2912 "AF Zoom-Nikkor 28-80mm f/3.5-5.6D",
2913 ),
2914 (
2915 [0x46, 0x3C, 0x44, 0x60, 0x30, 0x3C, 0x49, 0x02],
2916 "AF Zoom-Nikkor 35-80mm f/4-5.6D N",
2917 ),
2918 (
2919 [0x47, 0x42, 0x37, 0x50, 0x2A, 0x34, 0x4A, 0x02],
2920 "AF Zoom-Nikkor 24-50mm f/3.3-4.5D",
2921 ),
2922 (
2923 [0x48, 0x48, 0x8E, 0x8E, 0x24, 0x24, 0x4B, 0x02],
2924 "AF-S Nikkor 300mm f/2.8D IF-ED",
2925 ),
2926 (
2927 [0x48, 0x48, 0x8E, 0x8E, 0x24, 0x24, 0xF1, 0x02],
2928 "AF-S Nikkor 300mm f/2.8D IF-ED + TC-14E",
2929 ),
2930 (
2931 [0x48, 0x48, 0x8E, 0x8E, 0x24, 0x24, 0xE1, 0x02],
2932 "AF-S Nikkor 300mm f/2.8D IF-ED + TC-17E",
2933 ),
2934 (
2935 [0x48, 0x48, 0x8E, 0x8E, 0x24, 0x24, 0xF2, 0x02],
2936 "AF-S Nikkor 300mm f/2.8D IF-ED + TC-20E",
2937 ),
2938 (
2939 [0x49, 0x3C, 0xA6, 0xA6, 0x30, 0x30, 0x4C, 0x02],
2940 "AF-S Nikkor 600mm f/4D IF-ED",
2941 ),
2942 (
2943 [0x49, 0x3C, 0xA6, 0xA6, 0x30, 0x30, 0xF1, 0x02],
2944 "AF-S Nikkor 600mm f/4D IF-ED + TC-14E",
2945 ),
2946 (
2947 [0x49, 0x3C, 0xA6, 0xA6, 0x30, 0x30, 0xE1, 0x02],
2948 "AF-S Nikkor 600mm f/4D IF-ED + TC-17E",
2949 ),
2950 (
2951 [0x49, 0x3C, 0xA6, 0xA6, 0x30, 0x30, 0xF2, 0x02],
2952 "AF-S Nikkor 600mm f/4D IF-ED + TC-20E",
2953 ),
2954 (
2955 [0x4A, 0x54, 0x62, 0x62, 0x0C, 0x0C, 0x4D, 0x02],
2956 "AF Nikkor 85mm f/1.4D IF",
2957 ),
2958 (
2959 [0x4B, 0x3C, 0xA0, 0xA0, 0x30, 0x30, 0x4E, 0x02],
2960 "AF-S Nikkor 500mm f/4D IF-ED",
2961 ),
2962 (
2963 [0x4B, 0x3C, 0xA0, 0xA0, 0x30, 0x30, 0xF1, 0x02],
2964 "AF-S Nikkor 500mm f/4D IF-ED + TC-14E",
2965 ),
2966 (
2967 [0x4B, 0x3C, 0xA0, 0xA0, 0x30, 0x30, 0xE1, 0x02],
2968 "AF-S Nikkor 500mm f/4D IF-ED + TC-17E",
2969 ),
2970 (
2971 [0x4B, 0x3C, 0xA0, 0xA0, 0x30, 0x30, 0xF2, 0x02],
2972 "AF-S Nikkor 500mm f/4D IF-ED + TC-20E",
2973 ),
2974 (
2975 [0x4C, 0x40, 0x37, 0x6E, 0x2C, 0x3C, 0x4F, 0x02],
2976 "AF Zoom-Nikkor 24-120mm f/3.5-5.6D IF",
2977 ),
2978 (
2979 [0x4D, 0x40, 0x3C, 0x80, 0x2C, 0x3C, 0x62, 0x02],
2980 "AF Zoom-Nikkor 28-200mm f/3.5-5.6D IF",
2981 ),
2982 (
2983 [0x4E, 0x48, 0x72, 0x72, 0x18, 0x18, 0x51, 0x02],
2984 "AF DC-Nikkor 135mm f/2D",
2985 ),
2986 (
2987 [0x4F, 0x40, 0x37, 0x5C, 0x2C, 0x3C, 0x53, 0x06],
2988 "IX-Nikkor 24-70mm f/3.5-5.6",
2989 ),
2990 (
2991 [0x50, 0x48, 0x56, 0x7C, 0x30, 0x3C, 0x54, 0x06],
2992 "IX-Nikkor 60-180mm f/4-5.6",
2993 ),
2994 (
2995 [0x53, 0x48, 0x60, 0x80, 0x24, 0x24, 0x57, 0x02],
2996 "AF Zoom-Nikkor 80-200mm f/2.8D ED",
2997 ),
2998 (
2999 [0x53, 0x48, 0x60, 0x80, 0x24, 0x24, 0x60, 0x02],
3000 "AF Zoom-Nikkor 80-200mm f/2.8D ED",
3001 ),
3002 (
3003 [0x54, 0x44, 0x5C, 0x7C, 0x34, 0x3C, 0x58, 0x02],
3004 "AF Zoom-Micro Nikkor 70-180mm f/4.5-5.6D ED",
3005 ),
3006 (
3007 [0x54, 0x44, 0x5C, 0x7C, 0x34, 0x3C, 0x61, 0x02],
3008 "AF Zoom-Micro Nikkor 70-180mm f/4.5-5.6D ED",
3009 ),
3010 (
3011 [0x56, 0x48, 0x5C, 0x8E, 0x30, 0x3C, 0x5A, 0x02],
3012 "AF Zoom-Nikkor 70-300mm f/4-5.6D ED",
3013 ),
3014 (
3015 [0x59, 0x48, 0x98, 0x98, 0x24, 0x24, 0x5D, 0x02],
3016 "AF-S Nikkor 400mm f/2.8D IF-ED",
3017 ),
3018 (
3019 [0x59, 0x48, 0x98, 0x98, 0x24, 0x24, 0xF1, 0x02],
3020 "AF-S Nikkor 400mm f/2.8D IF-ED + TC-14E",
3021 ),
3022 (
3023 [0x59, 0x48, 0x98, 0x98, 0x24, 0x24, 0xE1, 0x02],
3024 "AF-S Nikkor 400mm f/2.8D IF-ED + TC-17E",
3025 ),
3026 (
3027 [0x59, 0x48, 0x98, 0x98, 0x24, 0x24, 0xF2, 0x02],
3028 "AF-S Nikkor 400mm f/2.8D IF-ED + TC-20E",
3029 ),
3030 (
3031 [0x5A, 0x3C, 0x3E, 0x56, 0x30, 0x3C, 0x5E, 0x06],
3032 "IX-Nikkor 30-60mm f/4-5.6",
3033 ),
3034 (
3035 [0x5B, 0x44, 0x56, 0x7C, 0x34, 0x3C, 0x5F, 0x06],
3036 "IX-Nikkor 60-180mm f/4.5-5.6",
3037 ),
3038 (
3039 [0x5D, 0x48, 0x3C, 0x5C, 0x24, 0x24, 0x63, 0x02],
3040 "AF-S Zoom-Nikkor 28-70mm f/2.8D IF-ED",
3041 ),
3042 (
3043 [0x5E, 0x48, 0x60, 0x80, 0x24, 0x24, 0x64, 0x02],
3044 "AF-S Zoom-Nikkor 80-200mm f/2.8D IF-ED",
3045 ),
3046 (
3047 [0x5F, 0x40, 0x3C, 0x6A, 0x2C, 0x34, 0x65, 0x02],
3048 "AF Zoom-Nikkor 28-105mm f/3.5-4.5D IF",
3049 ),
3050 (
3051 [0x60, 0x40, 0x3C, 0x60, 0x2C, 0x3C, 0x66, 0x02],
3052 "AF Zoom-Nikkor 28-80mm f/3.5-5.6D",
3053 ),
3054 (
3055 [0x61, 0x44, 0x5E, 0x86, 0x34, 0x3C, 0x67, 0x02],
3056 "AF Zoom-Nikkor 75-240mm f/4.5-5.6D",
3057 ),
3058 (
3059 [0x63, 0x48, 0x2B, 0x44, 0x24, 0x24, 0x68, 0x02],
3060 "AF-S Nikkor 17-35mm f/2.8D IF-ED",
3061 ),
3062 (
3063 [0x64, 0x00, 0x62, 0x62, 0x24, 0x24, 0x6A, 0x02],
3064 "PC Micro-Nikkor 85mm f/2.8D",
3065 ),
3066 (
3067 [0x65, 0x44, 0x60, 0x98, 0x34, 0x3C, 0x6B, 0x0A],
3068 "AF VR Zoom-Nikkor 80-400mm f/4.5-5.6D ED",
3069 ),
3070 (
3071 [0x66, 0x40, 0x2D, 0x44, 0x2C, 0x34, 0x6C, 0x02],
3072 "AF Zoom-Nikkor 18-35mm f/3.5-4.5D IF-ED",
3073 ),
3074 (
3075 [0x67, 0x48, 0x37, 0x62, 0x24, 0x30, 0x6D, 0x02],
3076 "AF Zoom-Nikkor 24-85mm f/2.8-4D IF",
3077 ),
3078 (
3079 [0x68, 0x42, 0x3C, 0x60, 0x2A, 0x3C, 0x6E, 0x06],
3080 "AF Zoom-Nikkor 28-80mm f/3.3-5.6G",
3081 ),
3082 (
3083 [0x69, 0x48, 0x5C, 0x8E, 0x30, 0x3C, 0x6F, 0x06],
3084 "AF Zoom-Nikkor 70-300mm f/4-5.6G",
3085 ),
3086 (
3087 [0x6A, 0x48, 0x8E, 0x8E, 0x30, 0x30, 0x70, 0x02],
3088 "AF-S Nikkor 300mm f/4D IF-ED",
3089 ),
3090 (
3091 [0x6B, 0x48, 0x24, 0x24, 0x24, 0x24, 0x71, 0x02],
3092 "AF Nikkor ED 14mm f/2.8D",
3093 ),
3094 (
3095 [0x6D, 0x48, 0x8E, 0x8E, 0x24, 0x24, 0x73, 0x02],
3096 "AF-S Nikkor 300mm f/2.8D IF-ED II",
3097 ),
3098 (
3099 [0x6E, 0x48, 0x98, 0x98, 0x24, 0x24, 0x74, 0x02],
3100 "AF-S Nikkor 400mm f/2.8D IF-ED II",
3101 ),
3102 (
3103 [0x6F, 0x3C, 0xA0, 0xA0, 0x30, 0x30, 0x75, 0x02],
3104 "AF-S Nikkor 500mm f/4D IF-ED II",
3105 ),
3106 (
3107 [0x70, 0x3C, 0xA6, 0xA6, 0x30, 0x30, 0x76, 0x02],
3108 "AF-S Nikkor 600mm f/4D IF-ED II",
3109 ),
3110 (
3111 [0x72, 0x48, 0x4C, 0x4C, 0x24, 0x24, 0x77, 0x00],
3112 "Nikkor 45mm f/2.8 P",
3113 ),
3114 (
3115 [0x74, 0x40, 0x37, 0x62, 0x2C, 0x34, 0x78, 0x06],
3116 "AF-S Zoom-Nikkor 24-85mm f/3.5-4.5G IF-ED",
3117 ),
3118 (
3119 [0x75, 0x40, 0x3C, 0x68, 0x2C, 0x3C, 0x79, 0x06],
3120 "AF Zoom-Nikkor 28-100mm f/3.5-5.6G",
3121 ),
3122 (
3123 [0x76, 0x58, 0x50, 0x50, 0x14, 0x14, 0x7A, 0x02],
3124 "AF Nikkor 50mm f/1.8D",
3125 ),
3126 (
3127 [0x77, 0x48, 0x5C, 0x80, 0x24, 0x24, 0x7B, 0x0E],
3128 "AF-S VR Zoom-Nikkor 70-200mm f/2.8G IF-ED",
3129 ),
3130 (
3131 [0x78, 0x40, 0x37, 0x6E, 0x2C, 0x3C, 0x7C, 0x0E],
3132 "AF-S VR Zoom-Nikkor 24-120mm f/3.5-5.6G IF-ED",
3133 ),
3134 (
3135 [0x79, 0x40, 0x3C, 0x80, 0x2C, 0x3C, 0x7F, 0x06],
3136 "AF Zoom-Nikkor 28-200mm f/3.5-5.6G IF-ED",
3137 ),
3138 (
3139 [0x7B, 0x48, 0x80, 0x98, 0x30, 0x30, 0x80, 0x0E],
3140 "AF-S VR Zoom-Nikkor 200-400mm f/4G IF-ED",
3141 ),
3142 (
3143 [0x7D, 0x48, 0x2B, 0x53, 0x24, 0x24, 0x82, 0x06],
3144 "AF-S DX Zoom-Nikkor 17-55mm f/2.8G IF-ED",
3145 ),
3146 (
3147 [0x7F, 0x40, 0x2D, 0x5C, 0x2C, 0x34, 0x84, 0x06],
3148 "AF-S DX Zoom-Nikkor 18-70mm f/3.5-4.5G IF-ED",
3149 ),
3150 (
3151 [0x80, 0x48, 0x1A, 0x1A, 0x24, 0x24, 0x85, 0x06],
3152 "AF DX Fisheye-Nikkor 10.5mm f/2.8G ED",
3153 ),
3154 (
3155 [0x81, 0x54, 0x80, 0x80, 0x18, 0x18, 0x86, 0x0E],
3156 "AF-S VR Nikkor 200mm f/2G IF-ED",
3157 ),
3158 (
3159 [0x82, 0x48, 0x8E, 0x8E, 0x24, 0x24, 0x87, 0x0E],
3160 "AF-S VR Nikkor 300mm f/2.8G IF-ED",
3161 ),
3162 (
3163 [0x83, 0x00, 0xB0, 0xB0, 0x5A, 0x5A, 0x88, 0x04],
3164 "FSA-L2, EDG 65, 800mm F13 G",
3165 ),
3166 (
3167 [0x89, 0x3C, 0x53, 0x80, 0x30, 0x3C, 0x8B, 0x06],
3168 "AF-S DX Zoom-Nikkor 55-200mm f/4-5.6G ED",
3169 ),
3170 (
3171 [0x8A, 0x54, 0x6A, 0x6A, 0x24, 0x24, 0x8C, 0x0E],
3172 "AF-S VR Micro-Nikkor 105mm f/2.8G IF-ED",
3173 ),
3174 (
3175 [0x8B, 0x40, 0x2D, 0x80, 0x2C, 0x3C, 0x8D, 0x0E],
3176 "AF-S DX VR Zoom-Nikkor 18-200mm f/3.5-5.6G IF-ED",
3177 ),
3178 (
3179 [0x8B, 0x40, 0x2D, 0x80, 0x2C, 0x3C, 0xFD, 0x0E],
3180 "AF-S DX VR Zoom-Nikkor 18-200mm f/3.5-5.6G IF-ED [II]",
3181 ),
3182 (
3183 [0x8C, 0x40, 0x2D, 0x53, 0x2C, 0x3C, 0x8E, 0x06],
3184 "AF-S DX Zoom-Nikkor 18-55mm f/3.5-5.6G ED",
3185 ),
3186 (
3187 [0x8D, 0x44, 0x5C, 0x8E, 0x34, 0x3C, 0x8F, 0x0E],
3188 "AF-S VR Zoom-Nikkor 70-300mm f/4.5-5.6G IF-ED",
3189 ),
3190 (
3191 [0x8F, 0x40, 0x2D, 0x72, 0x2C, 0x3C, 0x91, 0x06],
3192 "AF-S DX Zoom-Nikkor 18-135mm f/3.5-5.6G IF-ED",
3193 ),
3194 (
3195 [0x90, 0x3B, 0x53, 0x80, 0x30, 0x3C, 0x92, 0x0E],
3196 "AF-S DX VR Zoom-Nikkor 55-200mm f/4-5.6G IF-ED",
3197 ),
3198 (
3199 [0x92, 0x48, 0x24, 0x37, 0x24, 0x24, 0x94, 0x06],
3200 "AF-S Zoom-Nikkor 14-24mm f/2.8G ED",
3201 ),
3202 (
3203 [0x93, 0x48, 0x37, 0x5C, 0x24, 0x24, 0x95, 0x06],
3204 "AF-S Zoom-Nikkor 24-70mm f/2.8G ED",
3205 ),
3206 (
3207 [0x94, 0x40, 0x2D, 0x53, 0x2C, 0x3C, 0x96, 0x06],
3208 "AF-S DX Zoom-Nikkor 18-55mm f/3.5-5.6G ED II",
3209 ),
3210 (
3211 [0x95, 0x4C, 0x37, 0x37, 0x2C, 0x2C, 0x97, 0x02],
3212 "PC-E Nikkor 24mm f/3.5D ED",
3213 ),
3214 (
3215 [0x95, 0x00, 0x37, 0x37, 0x2C, 0x2C, 0x97, 0x06],
3216 "PC-E Nikkor 24mm f/3.5D ED",
3217 ),
3218 (
3219 [0x96, 0x48, 0x98, 0x98, 0x24, 0x24, 0x98, 0x0E],
3220 "AF-S VR Nikkor 400mm f/2.8G ED",
3221 ),
3222 (
3223 [0x97, 0x3C, 0xA0, 0xA0, 0x30, 0x30, 0x99, 0x0E],
3224 "AF-S VR Nikkor 500mm f/4G ED",
3225 ),
3226 (
3227 [0x98, 0x3C, 0xA6, 0xA6, 0x30, 0x30, 0x9A, 0x0E],
3228 "AF-S VR Nikkor 600mm f/4G ED",
3229 ),
3230 (
3231 [0x99, 0x40, 0x29, 0x62, 0x2C, 0x3C, 0x9B, 0x0E],
3232 "AF-S DX VR Zoom-Nikkor 16-85mm f/3.5-5.6G ED",
3233 ),
3234 (
3235 [0x9A, 0x40, 0x2D, 0x53, 0x2C, 0x3C, 0x9C, 0x0E],
3236 "AF-S DX VR Zoom-Nikkor 18-55mm f/3.5-5.6G",
3237 ),
3238 (
3239 [0x9B, 0x54, 0x4C, 0x4C, 0x24, 0x24, 0x9D, 0x02],
3240 "PC-E Micro Nikkor 45mm f/2.8D ED",
3241 ),
3242 (
3243 [0x9B, 0x00, 0x4C, 0x4C, 0x24, 0x24, 0x9D, 0x06],
3244 "PC-E Micro Nikkor 45mm f/2.8D ED",
3245 ),
3246 (
3247 [0x9C, 0x54, 0x56, 0x56, 0x24, 0x24, 0x9E, 0x06],
3248 "AF-S Micro Nikkor 60mm f/2.8G ED",
3249 ),
3250 (
3251 [0x9D, 0x54, 0x62, 0x62, 0x24, 0x24, 0x9F, 0x02],
3252 "PC-E Micro Nikkor 85mm f/2.8D",
3253 ),
3254 (
3255 [0x9D, 0x00, 0x62, 0x62, 0x24, 0x24, 0x9F, 0x06],
3256 "PC-E Micro Nikkor 85mm f/2.8D",
3257 ),
3258 (
3259 [0x9E, 0x40, 0x2D, 0x6A, 0x2C, 0x3C, 0xA0, 0x0E],
3260 "AF-S DX VR Zoom-Nikkor 18-105mm f/3.5-5.6G ED",
3261 ),
3262 (
3263 [0x9F, 0x58, 0x44, 0x44, 0x14, 0x14, 0xA1, 0x06],
3264 "AF-S DX Nikkor 35mm f/1.8G",
3265 ),
3266 (
3267 [0xA0, 0x54, 0x50, 0x50, 0x0C, 0x0C, 0xA2, 0x06],
3268 "AF-S Nikkor 50mm f/1.4G",
3269 ),
3270 (
3271 [0xA1, 0x40, 0x18, 0x37, 0x2C, 0x34, 0xA3, 0x06],
3272 "AF-S DX Nikkor 10-24mm f/3.5-4.5G ED",
3273 ),
3274 (
3275 [0xA1, 0x40, 0x2D, 0x53, 0x2C, 0x3C, 0xCB, 0x86],
3276 "AF-P DX Nikkor 18-55mm f/3.5-5.6G",
3277 ),
3278 (
3279 [0xA2, 0x48, 0x5C, 0x80, 0x24, 0x24, 0xA4, 0x0E],
3280 "AF-S Nikkor 70-200mm f/2.8G ED VR II",
3281 ),
3282 (
3283 [0xA3, 0x3C, 0x29, 0x44, 0x30, 0x30, 0xA5, 0x0E],
3284 "AF-S Nikkor 16-35mm f/4G ED VR",
3285 ),
3286 (
3287 [0xA4, 0x54, 0x37, 0x37, 0x0C, 0x0C, 0xA6, 0x06],
3288 "AF-S Nikkor 24mm f/1.4G ED",
3289 ),
3290 (
3291 [0xA5, 0x40, 0x3C, 0x8E, 0x2C, 0x3C, 0xA7, 0x0E],
3292 "AF-S Nikkor 28-300mm f/3.5-5.6G ED VR",
3293 ),
3294 (
3295 [0xA6, 0x48, 0x8E, 0x8E, 0x24, 0x24, 0xA8, 0x0E],
3296 "AF-S Nikkor 300mm f/2.8G IF-ED VR II",
3297 ),
3298 (
3299 [0xA7, 0x4B, 0x62, 0x62, 0x2C, 0x2C, 0xA9, 0x0E],
3300 "AF-S DX Micro Nikkor 85mm f/3.5G ED VR",
3301 ),
3302 (
3303 [0xA8, 0x48, 0x80, 0x98, 0x30, 0x30, 0xAA, 0x0E],
3304 "AF-S Zoom-Nikkor 200-400mm f/4G IF-ED VR II",
3305 ),
3306 (
3307 [0xA9, 0x54, 0x80, 0x80, 0x18, 0x18, 0xAB, 0x0E],
3308 "AF-S Nikkor 200mm f/2G ED VR II",
3309 ),
3310 (
3311 [0xAA, 0x3C, 0x37, 0x6E, 0x30, 0x30, 0xAC, 0x0E],
3312 "AF-S Nikkor 24-120mm f/4G ED VR",
3313 ),
3314 (
3315 [0xAC, 0x38, 0x53, 0x8E, 0x34, 0x3C, 0xAE, 0x0E],
3316 "AF-S DX Nikkor 55-300mm f/4.5-5.6G ED VR",
3317 ),
3318 (
3319 [0xAD, 0x3C, 0x2D, 0x8E, 0x2C, 0x3C, 0xAF, 0x0E],
3320 "AF-S DX Nikkor 18-300mm f/3.5-5.6G ED VR",
3321 ),
3322 (
3323 [0xAE, 0x54, 0x62, 0x62, 0x0C, 0x0C, 0xB0, 0x06],
3324 "AF-S Nikkor 85mm f/1.4G",
3325 ),
3326 (
3327 [0xAF, 0x54, 0x44, 0x44, 0x0C, 0x0C, 0xB1, 0x06],
3328 "AF-S Nikkor 35mm f/1.4G",
3329 ),
3330 (
3331 [0xB0, 0x4C, 0x50, 0x50, 0x14, 0x14, 0xB2, 0x06],
3332 "AF-S Nikkor 50mm f/1.8G",
3333 ),
3334 (
3335 [0xB1, 0x48, 0x48, 0x48, 0x24, 0x24, 0xB3, 0x06],
3336 "AF-S DX Micro Nikkor 40mm f/2.8G",
3337 ),
3338 (
3339 [0xB2, 0x48, 0x5C, 0x80, 0x30, 0x30, 0xB4, 0x0E],
3340 "AF-S Nikkor 70-200mm f/4G ED VR",
3341 ),
3342 (
3343 [0xB3, 0x4C, 0x62, 0x62, 0x14, 0x14, 0xB5, 0x06],
3344 "AF-S Nikkor 85mm f/1.8G",
3345 ),
3346 (
3347 [0xB4, 0x40, 0x37, 0x62, 0x2C, 0x34, 0xB6, 0x0E],
3348 "AF-S Zoom-Nikkor 24-85mm f/3.5-4.5G IF-ED VR",
3349 ),
3350 (
3351 [0xB5, 0x4C, 0x3C, 0x3C, 0x14, 0x14, 0xB7, 0x06],
3352 "AF-S Nikkor 28mm f/1.8G",
3353 ),
3354 (
3355 [0xB6, 0x3C, 0xB0, 0xB0, 0x3C, 0x3C, 0xB8, 0x0E],
3356 "AF-S VR Nikkor 800mm f/5.6E FL ED",
3357 ),
3358 (
3359 [0xB6, 0x3C, 0xB0, 0xB0, 0x3C, 0x3C, 0xB8, 0x4E],
3360 "AF-S VR Nikkor 800mm f/5.6E FL ED",
3361 ),
3362 (
3363 [0xB7, 0x44, 0x60, 0x98, 0x34, 0x3C, 0xB9, 0x0E],
3364 "AF-S Nikkor 80-400mm f/4.5-5.6G ED VR",
3365 ),
3366 (
3367 [0xB8, 0x40, 0x2D, 0x44, 0x2C, 0x34, 0xBA, 0x06],
3368 "AF-S Nikkor 18-35mm f/3.5-4.5G ED",
3369 ),
3370 (
3371 [0xA0, 0x40, 0x2D, 0x74, 0x2C, 0x3C, 0xBB, 0x0E],
3372 "AF-S DX Nikkor 18-140mm f/3.5-5.6G ED VR",
3373 ),
3374 (
3375 [0xA1, 0x54, 0x55, 0x55, 0x0C, 0x0C, 0xBC, 0x06],
3376 "AF-S Nikkor 58mm f/1.4G",
3377 ),
3378 (
3379 [0xA1, 0x48, 0x6E, 0x8E, 0x24, 0x24, 0xDB, 0x4E],
3380 "AF-S Nikkor 120-300mm f/2.8E FL ED SR VR",
3381 ),
3382 (
3383 [0xA2, 0x40, 0x2D, 0x53, 0x2C, 0x3C, 0xBD, 0x0E],
3384 "AF-S DX Nikkor 18-55mm f/3.5-5.6G VR II",
3385 ),
3386 (
3387 [0xA4, 0x40, 0x2D, 0x8E, 0x2C, 0x40, 0xBF, 0x0E],
3388 "AF-S DX Nikkor 18-300mm f/3.5-6.3G ED VR",
3389 ),
3390 (
3391 [0xA5, 0x4C, 0x44, 0x44, 0x14, 0x14, 0xC0, 0x06],
3392 "AF-S Nikkor 35mm f/1.8G ED",
3393 ),
3394 (
3395 [0xA6, 0x48, 0x98, 0x98, 0x24, 0x24, 0xC1, 0x0E],
3396 "AF-S Nikkor 400mm f/2.8E FL ED VR",
3397 ),
3398 (
3399 [0xA7, 0x3C, 0x53, 0x80, 0x30, 0x3C, 0xC2, 0x0E],
3400 "AF-S DX Nikkor 55-200mm f/4-5.6G ED VR II",
3401 ),
3402 (
3403 [0xA8, 0x48, 0x8E, 0x8E, 0x30, 0x30, 0xC3, 0x4E],
3404 "AF-S Nikkor 300mm f/4E PF ED VR",
3405 ),
3406 (
3407 [0xA8, 0x48, 0x8E, 0x8E, 0x30, 0x30, 0xC3, 0x0E],
3408 "AF-S Nikkor 300mm f/4E PF ED VR",
3409 ),
3410 (
3411 [0xA9, 0x4C, 0x31, 0x31, 0x14, 0x14, 0xC4, 0x06],
3412 "AF-S Nikkor 20mm f/1.8G ED",
3413 ),
3414 (
3415 [0xAA, 0x48, 0x37, 0x5C, 0x24, 0x24, 0xC5, 0x4E],
3416 "AF-S Nikkor 24-70mm f/2.8E ED VR",
3417 ),
3418 (
3419 [0xAA, 0x48, 0x37, 0x5C, 0x24, 0x24, 0xC5, 0x0E],
3420 "AF-S Nikkor 24-70mm f/2.8E ED VR",
3421 ),
3422 (
3423 [0xAB, 0x3C, 0xA0, 0xA0, 0x30, 0x30, 0xC6, 0x4E],
3424 "AF-S Nikkor 500mm f/4E FL ED VR",
3425 ),
3426 (
3427 [0xAC, 0x3C, 0xA6, 0xA6, 0x30, 0x30, 0xC7, 0x4E],
3428 "AF-S Nikkor 600mm f/4E FL ED VR",
3429 ),
3430 (
3431 [0xAD, 0x48, 0x28, 0x60, 0x24, 0x30, 0xC8, 0x4E],
3432 "AF-S DX Nikkor 16-80mm f/2.8-4E ED VR",
3433 ),
3434 (
3435 [0xAD, 0x48, 0x28, 0x60, 0x24, 0x30, 0xC8, 0x0E],
3436 "AF-S DX Nikkor 16-80mm f/2.8-4E ED VR",
3437 ),
3438 (
3439 [0xAE, 0x3C, 0x80, 0xA0, 0x3C, 0x3C, 0xC9, 0x4E],
3440 "AF-S Nikkor 200-500mm f/5.6E ED VR",
3441 ),
3442 (
3443 [0xAE, 0x3C, 0x80, 0xA0, 0x3C, 0x3C, 0xC9, 0x0E],
3444 "AF-S Nikkor 200-500mm f/5.6E ED VR",
3445 ),
3446 (
3447 [0xA0, 0x40, 0x2D, 0x53, 0x2C, 0x3C, 0xCA, 0x8E],
3448 "AF-P DX Nikkor 18-55mm f/3.5-5.6G",
3449 ),
3450 (
3451 [0xA0, 0x40, 0x2D, 0x53, 0x2C, 0x3C, 0xCA, 0x0E],
3452 "AF-P DX Nikkor 18-55mm f/3.5-5.6G VR",
3453 ),
3454 (
3455 [0xAF, 0x4C, 0x37, 0x37, 0x14, 0x14, 0xCC, 0x06],
3456 "AF-S Nikkor 24mm f/1.8G ED",
3457 ),
3458 (
3459 [0xA2, 0x38, 0x5C, 0x8E, 0x34, 0x40, 0xCD, 0x86],
3460 "AF-P DX Nikkor 70-300mm f/4.5-6.3G VR",
3461 ),
3462 (
3463 [0xA3, 0x38, 0x5C, 0x8E, 0x34, 0x40, 0xCE, 0x8E],
3464 "AF-P DX Nikkor 70-300mm f/4.5-6.3G ED VR",
3465 ),
3466 (
3467 [0xA3, 0x38, 0x5C, 0x8E, 0x34, 0x40, 0xCE, 0x0E],
3468 "AF-P DX Nikkor 70-300mm f/4.5-6.3G ED",
3469 ),
3470 (
3471 [0xA4, 0x48, 0x5C, 0x80, 0x24, 0x24, 0xCF, 0x4E],
3472 "AF-S Nikkor 70-200mm f/2.8E FL ED VR",
3473 ),
3474 (
3475 [0xA4, 0x48, 0x5C, 0x80, 0x24, 0x24, 0xCF, 0x0E],
3476 "AF-S Nikkor 70-200mm f/2.8E FL ED VR",
3477 ),
3478 (
3479 [0xA5, 0x54, 0x6A, 0x6A, 0x0C, 0x0C, 0xD0, 0x46],
3480 "AF-S Nikkor 105mm f/1.4E ED",
3481 ),
3482 (
3483 [0xA5, 0x54, 0x6A, 0x6A, 0x0C, 0x0C, 0xD0, 0x06],
3484 "AF-S Nikkor 105mm f/1.4E ED",
3485 ),
3486 (
3487 [0xA6, 0x48, 0x2F, 0x2F, 0x30, 0x30, 0xD1, 0x46],
3488 "PC Nikkor 19mm f/4E ED",
3489 ),
3490 (
3491 [0xA6, 0x48, 0x2F, 0x2F, 0x30, 0x30, 0xD1, 0x06],
3492 "PC Nikkor 19mm f/4E ED",
3493 ),
3494 (
3495 [0xA7, 0x40, 0x11, 0x26, 0x2C, 0x34, 0xD2, 0x46],
3496 "AF-S Fisheye Nikkor 8-15mm f/3.5-4.5E ED",
3497 ),
3498 (
3499 [0xA7, 0x40, 0x11, 0x26, 0x2C, 0x34, 0xD2, 0x06],
3500 "AF-S Fisheye Nikkor 8-15mm f/3.5-4.5E ED",
3501 ),
3502 (
3503 [0xA8, 0x38, 0x18, 0x30, 0x34, 0x3C, 0xD3, 0x8E],
3504 "AF-P DX Nikkor 10-20mm f/4.5-5.6G VR",
3505 ),
3506 (
3507 [0xA8, 0x38, 0x18, 0x30, 0x34, 0x3C, 0xD3, 0x0E],
3508 "AF-P DX Nikkor 10-20mm f/4.5-5.6G VR",
3509 ),
3510 (
3511 [0xA9, 0x48, 0x7C, 0x98, 0x30, 0x30, 0xD4, 0x4E],
3512 "AF-S Nikkor 180-400mm f/4E TC1.4 FL ED VR",
3513 ),
3514 (
3515 [0xA9, 0x48, 0x7C, 0x98, 0x30, 0x30, 0xD4, 0x0E],
3516 "AF-S Nikkor 180-400mm f/4E TC1.4 FL ED VR",
3517 ),
3518 (
3519 [0xAA, 0x48, 0x88, 0xA4, 0x3C, 0x3C, 0xD5, 0x4E],
3520 "AF-S Nikkor 180-400mm f/4E TC1.4 FL ED VR + 1.4x TC",
3521 ),
3522 (
3523 [0xAA, 0x48, 0x88, 0xA4, 0x3C, 0x3C, 0xD5, 0x0E],
3524 "AF-S Nikkor 180-400mm f/4E TC1.4 FL ED VR + 1.4x TC",
3525 ),
3526 (
3527 [0xAB, 0x44, 0x5C, 0x8E, 0x34, 0x3C, 0xD6, 0xCE],
3528 "AF-P Nikkor 70-300mm f/4.5-5.6E ED VR",
3529 ),
3530 (
3531 [0xAB, 0x44, 0x5C, 0x8E, 0x34, 0x3C, 0xD6, 0x0E],
3532 "AF-P Nikkor 70-300mm f/4.5-5.6E ED VR",
3533 ),
3534 (
3535 [0xAB, 0x44, 0x5C, 0x8E, 0x34, 0x3C, 0xD6, 0x4E],
3536 "AF-P Nikkor 70-300mm f/4.5-5.6E ED VR",
3537 ),
3538 (
3539 [0xAC, 0x54, 0x3C, 0x3C, 0x0C, 0x0C, 0xD7, 0x46],
3540 "AF-S Nikkor 28mm f/1.4E ED",
3541 ),
3542 (
3543 [0xAC, 0x54, 0x3C, 0x3C, 0x0C, 0x0C, 0xD7, 0x06],
3544 "AF-S Nikkor 28mm f/1.4E ED",
3545 ),
3546 (
3547 [0xAD, 0x3C, 0xA0, 0xA0, 0x3C, 0x3C, 0xD8, 0x0E],
3548 "AF-S Nikkor 500mm f/5.6E PF ED VR",
3549 ),
3550 (
3551 [0xAD, 0x3C, 0xA0, 0xA0, 0x3C, 0x3C, 0xD8, 0x4E],
3552 "AF-S Nikkor 500mm f/5.6E PF ED VR",
3553 ),
3554 ([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00], "TC-16A"),
3555 ([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00], "TC-16A"),
3556 (
3557 [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0x0C],
3558 "TC-14E [II] or Sigma APO Tele Converter 1.4x EX DG or Kenko Teleplus PRO 300 DG 1.4x",
3559 ),
3560 (
3561 [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0x18],
3562 "TC-20E [II] or Sigma APO Tele Converter 2x EX DG or Kenko Teleplus PRO 300 DG 2.0x",
3563 ),
3564 (
3565 [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0x12],
3566 "TC-17E II",
3567 ),
3568 (
3569 [0xFE, 0x47, 0x00, 0x00, 0x24, 0x24, 0x4B, 0x06],
3570 "Sigma 4.5mm F2.8 EX DC HSM Circular Fisheye",
3571 ),
3572 (
3573 [0x26, 0x48, 0x11, 0x11, 0x30, 0x30, 0x1C, 0x02],
3574 "Sigma 8mm F4 EX Circular Fisheye",
3575 ),
3576 (
3577 [0x79, 0x40, 0x11, 0x11, 0x2C, 0x2C, 0x1C, 0x06],
3578 "Sigma 8mm F3.5 EX Circular Fisheye",
3579 ),
3580 (
3581 [0xDB, 0x40, 0x11, 0x11, 0x2C, 0x2C, 0x1C, 0x06],
3582 "Sigma 8mm F3.5 EX DG Circular Fisheye",
3583 ),
3584 (
3585 [0xDC, 0x48, 0x19, 0x19, 0x24, 0x24, 0x4B, 0x06],
3586 "Sigma 10mm F2.8 EX DC HSM Fisheye",
3587 ),
3588 (
3589 [0xC2, 0x4C, 0x24, 0x24, 0x14, 0x14, 0x4B, 0x06],
3590 "Sigma 14mm F1.8 DG HSM | A",
3591 ),
3592 (
3593 [0x48, 0x48, 0x24, 0x24, 0x24, 0x24, 0x4B, 0x02],
3594 "Sigma 14mm F2.8 EX Aspherical HSM",
3595 ),
3596 (
3597 [0x02, 0x3F, 0x24, 0x24, 0x2C, 0x2C, 0x02, 0x00],
3598 "Sigma 14mm F3.5",
3599 ),
3600 (
3601 [0x26, 0x48, 0x27, 0x27, 0x24, 0x24, 0x1C, 0x02],
3602 "Sigma 15mm F2.8 EX Diagonal Fisheye",
3603 ),
3604 (
3605 [0xEA, 0x48, 0x27, 0x27, 0x24, 0x24, 0x1C, 0x02],
3606 "Sigma 15mm F2.8 EX Diagonal Fisheye",
3607 ),
3608 (
3609 [0x26, 0x58, 0x31, 0x31, 0x14, 0x14, 0x1C, 0x02],
3610 "Sigma 20mm F1.8 EX DG Aspherical RF",
3611 ),
3612 (
3613 [0x79, 0x54, 0x31, 0x31, 0x0C, 0x0C, 0x4B, 0x06],
3614 "Sigma 20mm F1.4 DG HSM | A",
3615 ),
3616 (
3617 [0x26, 0x58, 0x37, 0x37, 0x14, 0x14, 0x1C, 0x02],
3618 "Sigma 24mm F1.8 EX DG Aspherical Macro",
3619 ),
3620 (
3621 [0xE1, 0x58, 0x37, 0x37, 0x14, 0x14, 0x1C, 0x02],
3622 "Sigma 24mm F1.8 EX DG Aspherical Macro",
3623 ),
3624 (
3625 [0x02, 0x46, 0x37, 0x37, 0x25, 0x25, 0x02, 0x00],
3626 "Sigma 24mm F2.8 Super Wide II Macro",
3627 ),
3628 (
3629 [0x7E, 0x54, 0x37, 0x37, 0x0C, 0x0C, 0x4B, 0x06],
3630 "Sigma 24mm F1.4 DG HSM | A",
3631 ),
3632 (
3633 [0x26, 0x58, 0x3C, 0x3C, 0x14, 0x14, 0x1C, 0x02],
3634 "Sigma 28mm F1.8 EX DG Aspherical Macro",
3635 ),
3636 (
3637 [0xBC, 0x54, 0x3C, 0x3C, 0x0C, 0x0C, 0x4B, 0x46],
3638 "Sigma 28mm F1.4 DG HSM | A",
3639 ),
3640 (
3641 [0x48, 0x54, 0x3E, 0x3E, 0x0C, 0x0C, 0x4B, 0x06],
3642 "Sigma 30mm F1.4 EX DC HSM",
3643 ),
3644 (
3645 [0xF8, 0x54, 0x3E, 0x3E, 0x0C, 0x0C, 0x4B, 0x06],
3646 "Sigma 30mm F1.4 EX DC HSM",
3647 ),
3648 (
3649 [0x91, 0x54, 0x44, 0x44, 0x0C, 0x0C, 0x4B, 0x06],
3650 "Sigma 35mm F1.4 DG HSM",
3651 ),
3652 (
3653 [0xBD, 0x54, 0x48, 0x48, 0x0C, 0x0C, 0x4B, 0x46],
3654 "Sigma 40mm F1.4 DG HSM | A",
3655 ),
3656 (
3657 [0xDE, 0x54, 0x50, 0x50, 0x0C, 0x0C, 0x4B, 0x06],
3658 "Sigma 50mm F1.4 EX DG HSM",
3659 ),
3660 (
3661 [0x88, 0x54, 0x50, 0x50, 0x0C, 0x0C, 0x4B, 0x06],
3662 "Sigma 50mm F1.4 DG HSM | A",
3663 ),
3664 (
3665 [0x02, 0x48, 0x50, 0x50, 0x24, 0x24, 0x02, 0x00],
3666 "Sigma Macro 50mm F2.8",
3667 ),
3668 (
3669 [0x32, 0x54, 0x50, 0x50, 0x24, 0x24, 0x35, 0x02],
3670 "Sigma Macro 50mm F2.8 EX DG",
3671 ),
3672 (
3673 [0xE3, 0x54, 0x50, 0x50, 0x24, 0x24, 0x35, 0x02],
3674 "Sigma Macro 50mm F2.8 EX DG",
3675 ),
3676 (
3677 [0x79, 0x48, 0x5C, 0x5C, 0x24, 0x24, 0x1C, 0x06],
3678 "Sigma Macro 70mm F2.8 EX DG",
3679 ),
3680 (
3681 [0x9B, 0x54, 0x62, 0x62, 0x0C, 0x0C, 0x4B, 0x06],
3682 "Sigma 85mm F1.4 EX DG HSM",
3683 ),
3684 (
3685 [0xC8, 0x54, 0x62, 0x62, 0x0C, 0x0C, 0x4B, 0x46],
3686 "Sigma 85mm F1.4 DG HSM | A",
3687 ),
3688 (
3689 [0xC8, 0x54, 0x62, 0x62, 0x0C, 0x0C, 0x4B, 0x06],
3690 "Sigma 85mm F1.4 DG HSM | A",
3691 ),
3692 (
3693 [0x02, 0x48, 0x65, 0x65, 0x24, 0x24, 0x02, 0x00],
3694 "Sigma Macro 90mm F2.8",
3695 ),
3696 (
3697 [0xE5, 0x54, 0x6A, 0x6A, 0x24, 0x24, 0x35, 0x02],
3698 "Sigma Macro 105mm F2.8 EX DG",
3699 ),
3700 (
3701 [0x97, 0x48, 0x6A, 0x6A, 0x24, 0x24, 0x4B, 0x0E],
3702 "Sigma Macro 105mm F2.8 EX DG OS HSM",
3703 ),
3704 (
3705 [0xBE, 0x54, 0x6A, 0x6A, 0x0C, 0x0C, 0x4B, 0x46],
3706 "Sigma 105mm F1.4 DG HSM | A",
3707 ),
3708 (
3709 [0x48, 0x48, 0x76, 0x76, 0x24, 0x24, 0x4B, 0x06],
3710 "Sigma APO Macro 150mm F2.8 EX DG HSM",
3711 ),
3712 (
3713 [0xF5, 0x48, 0x76, 0x76, 0x24, 0x24, 0x4B, 0x06],
3714 "Sigma APO Macro 150mm F2.8 EX DG HSM",
3715 ),
3716 (
3717 [0x99, 0x48, 0x76, 0x76, 0x24, 0x24, 0x4B, 0x0E],
3718 "Sigma APO Macro 150mm F2.8 EX DG OS HSM",
3719 ),
3720 (
3721 [0x48, 0x4C, 0x7C, 0x7C, 0x2C, 0x2C, 0x4B, 0x02],
3722 "Sigma APO Macro 180mm F3.5 EX DG HSM",
3723 ),
3724 (
3725 [0x48, 0x4C, 0x7D, 0x7D, 0x2C, 0x2C, 0x4B, 0x02],
3726 "Sigma APO Macro 180mm F3.5 EX DG HSM",
3727 ),
3728 (
3729 [0xF4, 0x4C, 0x7C, 0x7C, 0x2C, 0x2C, 0x4B, 0x02],
3730 "Sigma APO Macro 180mm F3.5 EX DG HSM",
3731 ),
3732 (
3733 [0x94, 0x48, 0x7C, 0x7C, 0x24, 0x24, 0x4B, 0x0E],
3734 "Sigma APO Macro 180mm F2.8 EX DG OS HSM",
3735 ),
3736 (
3737 [0x48, 0x54, 0x8E, 0x8E, 0x24, 0x24, 0x4B, 0x02],
3738 "Sigma APO 300mm F2.8 EX DG HSM",
3739 ),
3740 (
3741 [0xFB, 0x54, 0x8E, 0x8E, 0x24, 0x24, 0x4B, 0x02],
3742 "Sigma APO 300mm F2.8 EX DG HSM",
3743 ),
3744 (
3745 [0x26, 0x48, 0x8E, 0x8E, 0x30, 0x30, 0x1C, 0x02],
3746 "Sigma APO Tele Macro 300mm F4",
3747 ),
3748 (
3749 [0x02, 0x2F, 0x98, 0x98, 0x3D, 0x3D, 0x02, 0x00],
3750 "Sigma APO 400mm F5.6",
3751 ),
3752 (
3753 [0x26, 0x3C, 0x98, 0x98, 0x3C, 0x3C, 0x1C, 0x02],
3754 "Sigma APO Tele Macro 400mm F5.6",
3755 ),
3756 (
3757 [0x02, 0x37, 0xA0, 0xA0, 0x34, 0x34, 0x02, 0x00],
3758 "Sigma APO 500mm F4.5",
3759 ),
3760 (
3761 [0x48, 0x44, 0xA0, 0xA0, 0x34, 0x34, 0x4B, 0x02],
3762 "Sigma APO 500mm F4.5 EX HSM",
3763 ),
3764 (
3765 [0xF1, 0x44, 0xA0, 0xA0, 0x34, 0x34, 0x4B, 0x02],
3766 "Sigma APO 500mm F4.5 EX DG HSM",
3767 ),
3768 (
3769 [0x02, 0x34, 0xA0, 0xA0, 0x44, 0x44, 0x02, 0x00],
3770 "Sigma APO 500mm F7.2",
3771 ),
3772 (
3773 [0x02, 0x3C, 0xB0, 0xB0, 0x3C, 0x3C, 0x02, 0x00],
3774 "Sigma APO 800mm F5.6",
3775 ),
3776 (
3777 [0x48, 0x3C, 0xB0, 0xB0, 0x3C, 0x3C, 0x4B, 0x02],
3778 "Sigma APO 800mm F5.6 EX HSM",
3779 ),
3780 (
3781 [0x9E, 0x38, 0x11, 0x29, 0x34, 0x3C, 0x4B, 0x06],
3782 "Sigma 8-16mm F4.5-5.6 DC HSM",
3783 ),
3784 (
3785 [0xA1, 0x41, 0x19, 0x31, 0x2C, 0x2C, 0x4B, 0x06],
3786 "Sigma 10-20mm F3.5 EX DC HSM",
3787 ),
3788 (
3789 [0x48, 0x3C, 0x19, 0x31, 0x30, 0x3C, 0x4B, 0x06],
3790 "Sigma 10-20mm F4-5.6 EX DC HSM",
3791 ),
3792 (
3793 [0xF9, 0x3C, 0x19, 0x31, 0x30, 0x3C, 0x4B, 0x06],
3794 "Sigma 10-20mm F4-5.6 EX DC HSM",
3795 ),
3796 (
3797 [0x48, 0x38, 0x1F, 0x37, 0x34, 0x3C, 0x4B, 0x06],
3798 "Sigma 12-24mm F4.5-5.6 EX DG Aspherical HSM",
3799 ),
3800 (
3801 [0xF0, 0x38, 0x1F, 0x37, 0x34, 0x3C, 0x4B, 0x06],
3802 "Sigma 12-24mm F4.5-5.6 EX DG Aspherical HSM",
3803 ),
3804 (
3805 [0x96, 0x38, 0x1F, 0x37, 0x34, 0x3C, 0x4B, 0x06],
3806 "Sigma 12-24mm F4.5-5.6 II DG HSM",
3807 ),
3808 (
3809 [0xCA, 0x3C, 0x1F, 0x37, 0x30, 0x30, 0x4B, 0x46],
3810 "Sigma 12-24mm F4 DG HSM | A",
3811 ),
3812 (
3813 [0xC1, 0x48, 0x24, 0x37, 0x24, 0x24, 0x4B, 0x46],
3814 "Sigma 14-24mm F2.8 DG HSM | A",
3815 ),
3816 (
3817 [0x26, 0x40, 0x27, 0x3F, 0x2C, 0x34, 0x1C, 0x02],
3818 "Sigma 15-30mm F3.5-4.5 EX DG Aspherical DF",
3819 ),
3820 (
3821 [0x48, 0x48, 0x2B, 0x44, 0x24, 0x30, 0x4B, 0x06],
3822 "Sigma 17-35mm F2.8-4 EX DG Aspherical HSM",
3823 ),
3824 (
3825 [0x26, 0x54, 0x2B, 0x44, 0x24, 0x30, 0x1C, 0x02],
3826 "Sigma 17-35mm F2.8-4 EX Aspherical",
3827 ),
3828 (
3829 [0x9D, 0x48, 0x2B, 0x50, 0x24, 0x24, 0x4B, 0x0E],
3830 "Sigma 17-50mm F2.8 EX DC OS HSM",
3831 ),
3832 (
3833 [0x8F, 0x48, 0x2B, 0x50, 0x24, 0x24, 0x4B, 0x0E],
3834 "Sigma 17-50mm F2.8 EX DC OS HSM",
3835 ),
3836 (
3837 [0x7A, 0x47, 0x2B, 0x5C, 0x24, 0x34, 0x4B, 0x06],
3838 "Sigma 17-70mm F2.8-4.5 DC Macro Asp. IF HSM",
3839 ),
3840 (
3841 [0x7A, 0x48, 0x2B, 0x5C, 0x24, 0x34, 0x4B, 0x06],
3842 "Sigma 17-70mm F2.8-4.5 DC Macro Asp. IF HSM",
3843 ),
3844 (
3845 [0x7F, 0x48, 0x2B, 0x5C, 0x24, 0x34, 0x1C, 0x06],
3846 "Sigma 17-70mm F2.8-4.5 DC Macro Asp. IF",
3847 ),
3848 (
3849 [0x8E, 0x3C, 0x2B, 0x5C, 0x24, 0x30, 0x4B, 0x0E],
3850 "Sigma 17-70mm F2.8-4 DC Macro OS HSM | C",
3851 ),
3852 (
3853 [0xA0, 0x48, 0x2A, 0x5C, 0x24, 0x30, 0x4B, 0x0E],
3854 "Sigma 17-70mm F2.8-4 DC Macro OS HSM",
3855 ),
3856 (
3857 [0x8B, 0x4C, 0x2D, 0x44, 0x14, 0x14, 0x4B, 0x06],
3858 "Sigma 18-35mm F1.8 DC HSM",
3859 ),
3860 (
3861 [0x26, 0x40, 0x2D, 0x44, 0x2B, 0x34, 0x1C, 0x02],
3862 "Sigma 18-35mm F3.5-4.5 Aspherical",
3863 ),
3864 (
3865 [0x26, 0x48, 0x2D, 0x50, 0x24, 0x24, 0x1C, 0x06],
3866 "Sigma 18-50mm F2.8 EX DC",
3867 ),
3868 (
3869 [0x7F, 0x48, 0x2D, 0x50, 0x24, 0x24, 0x1C, 0x06],
3870 "Sigma 18-50mm F2.8 EX DC Macro",
3871 ),
3872 (
3873 [0x7A, 0x48, 0x2D, 0x50, 0x24, 0x24, 0x4B, 0x06],
3874 "Sigma 18-50mm F2.8 EX DC Macro",
3875 ),
3876 (
3877 [0xF6, 0x48, 0x2D, 0x50, 0x24, 0x24, 0x4B, 0x06],
3878 "Sigma 18-50mm F2.8 EX DC Macro",
3879 ),
3880 (
3881 [0xA4, 0x47, 0x2D, 0x50, 0x24, 0x34, 0x4B, 0x0E],
3882 "Sigma 18-50mm F2.8-4.5 DC OS HSM",
3883 ),
3884 (
3885 [0x26, 0x40, 0x2D, 0x50, 0x2C, 0x3C, 0x1C, 0x06],
3886 "Sigma 18-50mm F3.5-5.6 DC",
3887 ),
3888 (
3889 [0x7A, 0x40, 0x2D, 0x50, 0x2C, 0x3C, 0x4B, 0x06],
3890 "Sigma 18-50mm F3.5-5.6 DC HSM",
3891 ),
3892 (
3893 [0x26, 0x40, 0x2D, 0x70, 0x2B, 0x3C, 0x1C, 0x06],
3894 "Sigma 18-125mm F3.5-5.6 DC",
3895 ),
3896 (
3897 [0xCD, 0x3D, 0x2D, 0x70, 0x2E, 0x3C, 0x4B, 0x0E],
3898 "Sigma 18-125mm F3.8-5.6 DC OS HSM",
3899 ),
3900 (
3901 [0x26, 0x40, 0x2D, 0x80, 0x2C, 0x40, 0x1C, 0x06],
3902 "Sigma 18-200mm F3.5-6.3 DC",
3903 ),
3904 (
3905 [0xFF, 0x40, 0x2D, 0x80, 0x2C, 0x40, 0x4B, 0x06],
3906 "Sigma 18-200mm F3.5-6.3 DC",
3907 ),
3908 (
3909 [0x7A, 0x40, 0x2D, 0x80, 0x2C, 0x40, 0x4B, 0x0E],
3910 "Sigma 18-200mm F3.5-6.3 DC OS HSM",
3911 ),
3912 (
3913 [0xED, 0x40, 0x2D, 0x80, 0x2C, 0x40, 0x4B, 0x0E],
3914 "Sigma 18-200mm F3.5-6.3 DC OS HSM",
3915 ),
3916 (
3917 [0x90, 0x40, 0x2D, 0x80, 0x2C, 0x40, 0x4B, 0x0E],
3918 "Sigma 18-200mm F3.5-6.3 II DC OS HSM",
3919 ),
3920 (
3921 [0x89, 0x30, 0x2D, 0x80, 0x2C, 0x40, 0x4B, 0x0E],
3922 "Sigma 18-200mm F3.5-6.3 DC Macro OS HS | C",
3923 ),
3924 (
3925 [0xA5, 0x40, 0x2D, 0x88, 0x2C, 0x40, 0x4B, 0x0E],
3926 "Sigma 18-250mm F3.5-6.3 DC OS HSM",
3927 ),
3928 (
3929 [0x92, 0x2C, 0x2D, 0x88, 0x2C, 0x40, 0x4B, 0x0E],
3930 "Sigma 18-250mm F3.5-6.3 DC Macro OS HSM",
3931 ),
3932 (
3933 [0x87, 0x2C, 0x2D, 0x8E, 0x2C, 0x40, 0x4B, 0x0E],
3934 "Sigma 18-300mm F3.5-6.3 DC Macro HSM",
3935 ),
3936 (
3937 [0x26, 0x48, 0x31, 0x49, 0x24, 0x24, 0x1C, 0x02],
3938 "Sigma 20-40mm F2.8",
3939 ),
3940 (
3941 [0x7B, 0x48, 0x37, 0x44, 0x18, 0x18, 0x4B, 0x06],
3942 "Sigma 24-35mm F2.0 DG HSM | A",
3943 ),
3944 (
3945 [0x02, 0x3A, 0x37, 0x50, 0x31, 0x3D, 0x02, 0x00],
3946 "Sigma 24-50mm F4-5.6 UC",
3947 ),
3948 (
3949 [0x26, 0x48, 0x37, 0x56, 0x24, 0x24, 0x1C, 0x02],
3950 "Sigma 24-60mm F2.8 EX DG",
3951 ),
3952 (
3953 [0xB6, 0x48, 0x37, 0x56, 0x24, 0x24, 0x1C, 0x02],
3954 "Sigma 24-60mm F2.8 EX DG",
3955 ),
3956 (
3957 [0xA6, 0x48, 0x37, 0x5C, 0x24, 0x24, 0x4B, 0x06],
3958 "Sigma 24-70mm F2.8 IF EX DG HSM",
3959 ),
3960 (
3961 [0xC9, 0x48, 0x37, 0x5C, 0x24, 0x24, 0x4B, 0x4E],
3962 "Sigma 24-70mm F2.8 DG OS HSM | A",
3963 ),
3964 (
3965 [0x26, 0x54, 0x37, 0x5C, 0x24, 0x24, 0x1C, 0x02],
3966 "Sigma 24-70mm F2.8 EX DG Macro",
3967 ),
3968 (
3969 [0x67, 0x54, 0x37, 0x5C, 0x24, 0x24, 0x1C, 0x02],
3970 "Sigma 24-70mm F2.8 EX DG Macro",
3971 ),
3972 (
3973 [0xE9, 0x54, 0x37, 0x5C, 0x24, 0x24, 0x1C, 0x02],
3974 "Sigma 24-70mm F2.8 EX DG Macro",
3975 ),
3976 (
3977 [0x26, 0x40, 0x37, 0x5C, 0x2C, 0x3C, 0x1C, 0x02],
3978 "Sigma 24-70mm F3.5-5.6 Aspherical HF",
3979 ),
3980 (
3981 [0x8A, 0x3C, 0x37, 0x6A, 0x30, 0x30, 0x4B, 0x0E],
3982 "Sigma 24-105mm F4 DG OS HSM",
3983 ),
3984 (
3985 [0x26, 0x54, 0x37, 0x73, 0x24, 0x34, 0x1C, 0x02],
3986 "Sigma 24-135mm F2.8-4.5",
3987 ),
3988 (
3989 [0x02, 0x46, 0x3C, 0x5C, 0x25, 0x25, 0x02, 0x00],
3990 "Sigma 28-70mm F2.8",
3991 ),
3992 (
3993 [0x26, 0x54, 0x3C, 0x5C, 0x24, 0x24, 0x1C, 0x02],
3994 "Sigma 28-70mm F2.8 EX",
3995 ),
3996 (
3997 [0x26, 0x48, 0x3C, 0x5C, 0x24, 0x24, 0x1C, 0x06],
3998 "Sigma 28-70mm F2.8 EX DG",
3999 ),
4000 (
4001 [0x79, 0x48, 0x3C, 0x5C, 0x24, 0x24, 0x1C, 0x06],
4002 "Sigma 28-70mm F2.8 EX DG",
4003 ),
4004 (
4005 [0x26, 0x48, 0x3C, 0x5C, 0x24, 0x30, 0x1C, 0x02],
4006 "Sigma 28-70mm F2.8-4 DG",
4007 ),
4008 (
4009 [0x02, 0x3F, 0x3C, 0x5C, 0x2D, 0x35, 0x02, 0x00],
4010 "Sigma 28-70mm F3.5-4.5 UC",
4011 ),
4012 (
4013 [0x26, 0x40, 0x3C, 0x60, 0x2C, 0x3C, 0x1C, 0x02],
4014 "Sigma 28-80mm F3.5-5.6 Mini Zoom Macro II Aspherical",
4015 ),
4016 (
4017 [0x26, 0x40, 0x3C, 0x65, 0x2C, 0x3C, 0x1C, 0x02],
4018 "Sigma 28-90mm F3.5-5.6 Macro",
4019 ),
4020 (
4021 [0x26, 0x48, 0x3C, 0x6A, 0x24, 0x30, 0x1C, 0x02],
4022 "Sigma 28-105mm F2.8-4 Aspherical",
4023 ),
4024 (
4025 [0x26, 0x3E, 0x3C, 0x6A, 0x2E, 0x3C, 0x1C, 0x02],
4026 "Sigma 28-105mm F3.8-5.6 UC-III Aspherical IF",
4027 ),
4028 (
4029 [0x26, 0x40, 0x3C, 0x80, 0x2C, 0x3C, 0x1C, 0x02],
4030 "Sigma 28-200mm F3.5-5.6 Compact Aspherical Hyperzoom Macro",
4031 ),
4032 (
4033 [0x26, 0x40, 0x3C, 0x80, 0x2B, 0x3C, 0x1C, 0x02],
4034 "Sigma 28-200mm F3.5-5.6 Compact Aspherical Hyperzoom Macro",
4035 ),
4036 (
4037 [0x26, 0x3D, 0x3C, 0x80, 0x2F, 0x3D, 0x1C, 0x02],
4038 "Sigma 28-300mm F3.8-5.6 Aspherical",
4039 ),
4040 (
4041 [0x26, 0x41, 0x3C, 0x8E, 0x2C, 0x40, 0x1C, 0x02],
4042 "Sigma 28-300mm F3.5-6.3 DG Macro",
4043 ),
4044 (
4045 [0xE6, 0x41, 0x3C, 0x8E, 0x2C, 0x40, 0x1C, 0x02],
4046 "Sigma 28-300mm F3.5-6.3 DG Macro",
4047 ),
4048 (
4049 [0x26, 0x40, 0x3C, 0x8E, 0x2C, 0x40, 0x1C, 0x02],
4050 "Sigma 28-300mm F3.5-6.3 Macro",
4051 ),
4052 (
4053 [0x02, 0x3B, 0x44, 0x61, 0x30, 0x3D, 0x02, 0x00],
4054 "Sigma 35-80mm F4-5.6",
4055 ),
4056 (
4057 [0x02, 0x40, 0x44, 0x73, 0x2B, 0x36, 0x02, 0x00],
4058 "Sigma 35-135mm F3.5-4.5 a",
4059 ),
4060 (
4061 [0xCC, 0x4C, 0x50, 0x68, 0x14, 0x14, 0x4B, 0x06],
4062 "Sigma 50-100mm F1.8 DC HSM | A",
4063 ),
4064 (
4065 [0x7A, 0x47, 0x50, 0x76, 0x24, 0x24, 0x4B, 0x06],
4066 "Sigma 50-150mm F2.8 EX APO DC HSM",
4067 ),
4068 (
4069 [0xFD, 0x47, 0x50, 0x76, 0x24, 0x24, 0x4B, 0x06],
4070 "Sigma 50-150mm F2.8 EX APO DC HSM II",
4071 ),
4072 (
4073 [0x98, 0x48, 0x50, 0x76, 0x24, 0x24, 0x4B, 0x0E],
4074 "Sigma 50-150mm F2.8 EX APO DC OS HSM",
4075 ),
4076 (
4077 [0x48, 0x3C, 0x50, 0xA0, 0x30, 0x40, 0x4B, 0x02],
4078 "Sigma 50-500mm F4-6.3 EX APO RF HSM",
4079 ),
4080 (
4081 [0x9F, 0x37, 0x50, 0xA0, 0x34, 0x40, 0x4B, 0x0E],
4082 "Sigma 50-500mm F4.5-6.3 DG OS HSM",
4083 ),
4084 (
4085 [0x26, 0x3C, 0x54, 0x80, 0x30, 0x3C, 0x1C, 0x06],
4086 "Sigma 55-200mm F4-5.6 DC",
4087 ),
4088 (
4089 [0x7A, 0x3B, 0x53, 0x80, 0x30, 0x3C, 0x4B, 0x06],
4090 "Sigma 55-200mm F4-5.6 DC HSM",
4091 ),
4092 (
4093 [0x48, 0x54, 0x5C, 0x80, 0x24, 0x24, 0x4B, 0x02],
4094 "Sigma 70-200mm F2.8 EX APO IF HSM",
4095 ),
4096 (
4097 [0x7A, 0x48, 0x5C, 0x80, 0x24, 0x24, 0x4B, 0x06],
4098 "Sigma 70-200mm F2.8 EX APO DG Macro HSM II",
4099 ),
4100 (
4101 [0xEE, 0x48, 0x5C, 0x80, 0x24, 0x24, 0x4B, 0x06],
4102 "Sigma 70-200mm F2.8 EX APO DG Macro HSM II",
4103 ),
4104 (
4105 [0x9C, 0x48, 0x5C, 0x80, 0x24, 0x24, 0x4B, 0x0E],
4106 "Sigma 70-200mm F2.8 EX DG OS HSM",
4107 ),
4108 (
4109 [0xBB, 0x48, 0x5C, 0x80, 0x24, 0x24, 0x4B, 0x4E],
4110 "Sigma 70-200mm F2.8 DG OS HSM | S",
4111 ),
4112 (
4113 [0x02, 0x46, 0x5C, 0x82, 0x25, 0x25, 0x02, 0x00],
4114 "Sigma 70-210mm F2.8 APO",
4115 ),
4116 (
4117 [0x02, 0x40, 0x5C, 0x82, 0x2C, 0x35, 0x02, 0x00],
4118 "Sigma APO 70-210mm F3.5-4.5",
4119 ),
4120 (
4121 [0x26, 0x3C, 0x5C, 0x82, 0x30, 0x3C, 0x1C, 0x02],
4122 "Sigma 70-210mm F4-5.6 UC-II",
4123 ),
4124 (
4125 [0x02, 0x3B, 0x5C, 0x82, 0x30, 0x3C, 0x02, 0x00],
4126 "Sigma Zoom-K 70-210mm F4-5.6",
4127 ),
4128 (
4129 [0x26, 0x3C, 0x5C, 0x8E, 0x30, 0x3C, 0x1C, 0x02],
4130 "Sigma 70-300mm F4-5.6 DG Macro",
4131 ),
4132 (
4133 [0x56, 0x3C, 0x5C, 0x8E, 0x30, 0x3C, 0x1C, 0x02],
4134 "Sigma 70-300mm F4-5.6 APO Macro Super II",
4135 ),
4136 (
4137 [0xE0, 0x3C, 0x5C, 0x8E, 0x30, 0x3C, 0x4B, 0x06],
4138 "Sigma 70-300mm F4-5.6 APO DG Macro HSM",
4139 ),
4140 (
4141 [0xA3, 0x3C, 0x5C, 0x8E, 0x30, 0x3C, 0x4B, 0x0E],
4142 "Sigma 70-300mm F4-5.6 DG OS",
4143 ),
4144 (
4145 [0x02, 0x37, 0x5E, 0x8E, 0x35, 0x3D, 0x02, 0x00],
4146 "Sigma 75-300mm F4.5-5.6 APO",
4147 ),
4148 (
4149 [0x02, 0x3A, 0x5E, 0x8E, 0x32, 0x3D, 0x02, 0x00],
4150 "Sigma 75-300mm F4.0-5.6",
4151 ),
4152 (
4153 [0x77, 0x44, 0x61, 0x98, 0x34, 0x3C, 0x7B, 0x0E],
4154 "Sigma 80-400mm F4.5-5.6 EX OS",
4155 ),
4156 (
4157 [0x77, 0x44, 0x60, 0x98, 0x34, 0x3C, 0x7B, 0x0E],
4158 "Sigma 80-400mm F4.5-5.6 APO DG D OS",
4159 ),
4160 (
4161 [0x48, 0x48, 0x68, 0x8E, 0x30, 0x30, 0x4B, 0x02],
4162 "Sigma APO 100-300mm F4 EX IF HSM",
4163 ),
4164 (
4165 [0xF3, 0x48, 0x68, 0x8E, 0x30, 0x30, 0x4B, 0x02],
4166 "Sigma APO 100-300mm F4 EX IF HSM",
4167 ),
4168 (
4169 [0x26, 0x45, 0x68, 0x8E, 0x34, 0x42, 0x1C, 0x02],
4170 "Sigma 100-300mm F4.5-6.7 DL",
4171 ),
4172 (
4173 [0x48, 0x54, 0x6F, 0x8E, 0x24, 0x24, 0x4B, 0x02],
4174 "Sigma APO 120-300mm F2.8 EX DG HSM",
4175 ),
4176 (
4177 [0x7A, 0x54, 0x6E, 0x8E, 0x24, 0x24, 0x4B, 0x02],
4178 "Sigma APO 120-300mm F2.8 EX DG HSM",
4179 ),
4180 (
4181 [0xFA, 0x54, 0x6E, 0x8E, 0x24, 0x24, 0x4B, 0x02],
4182 "Sigma APO 120-300mm F2.8 EX DG HSM",
4183 ),
4184 (
4185 [0xCF, 0x38, 0x6E, 0x98, 0x34, 0x3C, 0x4B, 0x0E],
4186 "Sigma APO 120-400mm F4.5-5.6 DG OS HSM",
4187 ),
4188 (
4189 [0xC3, 0x34, 0x68, 0x98, 0x38, 0x40, 0x4B, 0x4E],
4190 "Sigma 100-400mm F5-6.3 DG OS HSM | C",
4191 ),
4192 (
4193 [0x8D, 0x48, 0x6E, 0x8E, 0x24, 0x24, 0x4B, 0x0E],
4194 "Sigma 120-300mm F2.8 DG OS HSM Sports",
4195 ),
4196 (
4197 [0x26, 0x44, 0x73, 0x98, 0x34, 0x3C, 0x1C, 0x02],
4198 "Sigma 135-400mm F4.5-5.6 APO Aspherical",
4199 ),
4200 (
4201 [0xCE, 0x34, 0x76, 0xA0, 0x38, 0x40, 0x4B, 0x0E],
4202 "Sigma 150-500mm F5-6.3 DG OS APO HSM",
4203 ),
4204 (
4205 [0x81, 0x34, 0x76, 0xA6, 0x38, 0x40, 0x4B, 0x0E],
4206 "Sigma 150-600mm F5-6.3 DG OS HSM | S",
4207 ),
4208 (
4209 [0x82, 0x34, 0x76, 0xA6, 0x38, 0x40, 0x4B, 0x0E],
4210 "Sigma 150-600mm F5-6.3 DG OS HSM | C",
4211 ),
4212 (
4213 [0xC4, 0x4C, 0x73, 0x73, 0x14, 0x14, 0x4B, 0x46],
4214 "Sigma 135mm F1.8 DG HSM | A",
4215 ),
4216 (
4217 [0x26, 0x40, 0x7B, 0xA0, 0x34, 0x40, 0x1C, 0x02],
4218 "Sigma APO 170-500mm F5-6.3 Aspherical RF",
4219 ),
4220 (
4221 [0xA7, 0x49, 0x80, 0xA0, 0x24, 0x24, 0x4B, 0x06],
4222 "Sigma APO 200-500mm F2.8 EX DG",
4223 ),
4224 (
4225 [0x48, 0x3C, 0x8E, 0xB0, 0x3C, 0x3C, 0x4B, 0x02],
4226 "Sigma APO 300-800mm F5.6 EX DG HSM",
4227 ),
4228 (
4229 [0xD2, 0x3C, 0x8E, 0xB0, 0x3C, 0x3C, 0x4B, 0x02],
4230 "Sigma APO 300-800mm F5.6 EX DG HSM",
4231 ),
4232 (
4233 [0x00, 0x47, 0x25, 0x25, 0x24, 0x24, 0x00, 0x02],
4234 "Tamron SP AF 14mm f/2.8 Aspherical (IF) (69E)",
4235 ),
4236 (
4237 [0xC8, 0x54, 0x44, 0x44, 0x0D, 0x0D, 0xDF, 0x46],
4238 "Tamron SP 35mm f/1.4 Di USD (F045)",
4239 ),
4240 (
4241 [0xE8, 0x4C, 0x44, 0x44, 0x14, 0x14, 0xDF, 0x0E],
4242 "Tamron SP 35mm f/1.8 Di VC USD (F012)",
4243 ),
4244 (
4245 [0xE7, 0x4C, 0x4C, 0x4C, 0x14, 0x14, 0xDF, 0x0E],
4246 "Tamron SP 45mm f/1.8 Di VC USD (F013)",
4247 ),
4248 (
4249 [0xF4, 0x54, 0x56, 0x56, 0x18, 0x18, 0x84, 0x06],
4250 "Tamron SP AF 60mm f/2.0 Di II Macro 1:1 (G005)",
4251 ),
4252 (
4253 [0xE5, 0x4C, 0x62, 0x62, 0x14, 0x14, 0xC9, 0x4E],
4254 "Tamron SP 85mm f/1.8 Di VC USD (F016)",
4255 ),
4256 (
4257 [0x1E, 0x5D, 0x64, 0x64, 0x20, 0x20, 0x13, 0x00],
4258 "Tamron SP AF 90mm f/2.5 (52E)",
4259 ),
4260 (
4261 [0x20, 0x5A, 0x64, 0x64, 0x20, 0x20, 0x14, 0x00],
4262 "Tamron SP AF 90mm f/2.5 Macro (152E)",
4263 ),
4264 (
4265 [0x22, 0x53, 0x64, 0x64, 0x24, 0x24, 0xE0, 0x02],
4266 "Tamron SP AF 90mm f/2.8 Macro 1:1 (72E)",
4267 ),
4268 (
4269 [0x32, 0x53, 0x64, 0x64, 0x24, 0x24, 0x35, 0x02],
4270 "Tamron SP AF 90mm f/2.8 [Di] Macro 1:1 (172E/272E)",
4271 ),
4272 (
4273 [0xF8, 0x55, 0x64, 0x64, 0x24, 0x24, 0x84, 0x06],
4274 "Tamron SP AF 90mm f/2.8 Di Macro 1:1 (272NII)",
4275 ),
4276 (
4277 [0xF8, 0x54, 0x64, 0x64, 0x24, 0x24, 0xDF, 0x06],
4278 "Tamron SP AF 90mm f/2.8 Di Macro 1:1 (272NII)",
4279 ),
4280 (
4281 [0xFE, 0x54, 0x64, 0x64, 0x24, 0x24, 0xDF, 0x0E],
4282 "Tamron SP 90mm f/2.8 Di VC USD Macro 1:1 (F004)",
4283 ),
4284 (
4285 [0xE4, 0x54, 0x64, 0x64, 0x24, 0x24, 0xDF, 0x0E],
4286 "Tamron SP 90mm f/2.8 Di VC USD Macro 1:1 (F017)",
4287 ),
4288 (
4289 [0x00, 0x4C, 0x7C, 0x7C, 0x2C, 0x2C, 0x00, 0x02],
4290 "Tamron SP AF 180mm f/3.5 Di Model (B01)",
4291 ),
4292 (
4293 [0x21, 0x56, 0x8E, 0x8E, 0x24, 0x24, 0x14, 0x00],
4294 "Tamron SP AF 300mm f/2.8 LD-IF (60E)",
4295 ),
4296 (
4297 [0x27, 0x54, 0x8E, 0x8E, 0x24, 0x24, 0x1D, 0x02],
4298 "Tamron SP AF 300mm f/2.8 LD-IF (360E)",
4299 ),
4300 (
4301 [0xE1, 0x40, 0x19, 0x36, 0x2C, 0x35, 0xDF, 0x4E],
4302 "Tamron 10-24mm f/3.5-4.5 Di II VC HLD (B023)",
4303 ),
4304 (
4305 [0xE1, 0x40, 0x19, 0x36, 0x2C, 0x35, 0xDF, 0x0E],
4306 "Tamron 10-24mm f/3.5-4.5 Di II VC HLD (B023)",
4307 ),
4308 (
4309 [0xF6, 0x3F, 0x18, 0x37, 0x2C, 0x34, 0x84, 0x06],
4310 "Tamron SP AF 10-24mm f/3.5-4.5 Di II LD Aspherical (IF) (B001)",
4311 ),
4312 (
4313 [0xF6, 0x3F, 0x18, 0x37, 0x2C, 0x34, 0xDF, 0x06],
4314 "Tamron SP AF 10-24mm f/3.5-4.5 Di II LD Aspherical (IF) (B001)",
4315 ),
4316 (
4317 [0x00, 0x36, 0x1C, 0x2D, 0x34, 0x3C, 0x00, 0x06],
4318 "Tamron SP AF 11-18mm f/4.5-5.6 Di II LD Aspherical (IF) (A13)",
4319 ),
4320 (
4321 [0xE9, 0x48, 0x27, 0x3E, 0x24, 0x24, 0xDF, 0x0E],
4322 "Tamron SP 15-30mm f/2.8 Di VC USD (A012)",
4323 ),
4324 (
4325 [0xCA, 0x48, 0x27, 0x3E, 0x24, 0x24, 0xDF, 0x4E],
4326 "Tamron SP 15-30mm f/2.8 Di VC USD G2 (A041)",
4327 ),
4328 (
4329 [0xEA, 0x40, 0x29, 0x8E, 0x2C, 0x40, 0xDF, 0x0E],
4330 "Tamron 16-300mm f/3.5-6.3 Di II VC PZD (B016)",
4331 ),
4332 (
4333 [0x07, 0x46, 0x2B, 0x44, 0x24, 0x30, 0x03, 0x02],
4334 "Tamron SP AF 17-35mm f/2.8-4 Di LD Aspherical (IF) (A05)",
4335 ),
4336 (
4337 [0xCB, 0x3C, 0x2B, 0x44, 0x24, 0x31, 0xDF, 0x46],
4338 "Tamron 17-35mm f/2.8-4 Di OSD (A037)",
4339 ),
4340 (
4341 [0x00, 0x53, 0x2B, 0x50, 0x24, 0x24, 0x00, 0x06],
4342 "Tamron SP AF 17-50mm f/2.8 XR Di II LD Aspherical (IF) (A16)",
4343 ),
4344 (
4345 [0x7C, 0x54, 0x2B, 0x50, 0x24, 0x24, 0x00, 0x06],
4346 "Tamron SP AF 17-50mm f/2.8 XR Di II LD Aspherical (IF) (A16)",
4347 ),
4348 (
4349 [0x00, 0x54, 0x2B, 0x50, 0x24, 0x24, 0x00, 0x06],
4350 "Tamron SP AF 17-50mm f/2.8 XR Di II LD Aspherical (IF) (A16NII)",
4351 ),
4352 (
4353 [0xFB, 0x54, 0x2B, 0x50, 0x24, 0x24, 0x84, 0x06],
4354 "Tamron SP AF 17-50mm f/2.8 XR Di II LD Aspherical (IF) (A16NII)",
4355 ),
4356 (
4357 [0xF3, 0x54, 0x2B, 0x50, 0x24, 0x24, 0x84, 0x0E],
4358 "Tamron SP AF 17-50mm f/2.8 XR Di II VC LD Aspherical (IF) (B005)",
4359 ),
4360 (
4361 [0x00, 0x3F, 0x2D, 0x80, 0x2B, 0x40, 0x00, 0x06],
4362 "Tamron AF 18-200mm f/3.5-6.3 XR Di II LD Aspherical (IF) (A14)",
4363 ),
4364 (
4365 [0x00, 0x3F, 0x2D, 0x80, 0x2C, 0x40, 0x00, 0x06],
4366 "Tamron AF 18-200mm f/3.5-6.3 XR Di II LD Aspherical (IF) Macro (A14)",
4367 ),
4368 (
4369 [0xEC, 0x3E, 0x3C, 0x8E, 0x2C, 0x40, 0xDF, 0x0E],
4370 "Tamron 28-300mm f/3.5-6.3 Di VC PZD A010",
4371 ),
4372 (
4373 [0x00, 0x40, 0x2D, 0x80, 0x2C, 0x40, 0x00, 0x06],
4374 "Tamron AF 18-200mm f/3.5-6.3 XR Di II LD Aspherical (IF) Macro (A14NII)",
4375 ),
4376 (
4377 [0xFC, 0x40, 0x2D, 0x80, 0x2C, 0x40, 0xDF, 0x06],
4378 "Tamron AF 18-200mm f/3.5-6.3 XR Di II LD Aspherical (IF) Macro (A14NII)",
4379 ),
4380 (
4381 [0xE6, 0x40, 0x2D, 0x80, 0x2C, 0x40, 0xDF, 0x0E],
4382 "Tamron 18-200mm f/3.5-6.3 Di II VC (B018)",
4383 ),
4384 (
4385 [0x00, 0x40, 0x2D, 0x88, 0x2C, 0x40, 0x62, 0x06],
4386 "Tamron AF 18-250mm f/3.5-6.3 Di II LD Aspherical (IF) Macro (A18)",
4387 ),
4388 (
4389 [0x00, 0x40, 0x2D, 0x88, 0x2C, 0x40, 0x00, 0x06],
4390 "Tamron AF 18-250mm f/3.5-6.3 Di II LD Aspherical (IF) Macro (A18NII)",
4391 ),
4392 (
4393 [0xF5, 0x40, 0x2C, 0x8A, 0x2C, 0x40, 0x40, 0x0E],
4394 "Tamron AF 18-270mm f/3.5-6.3 Di II VC LD Aspherical (IF) Macro (B003)",
4395 ),
4396 (
4397 [0xF0, 0x3F, 0x2D, 0x8A, 0x2C, 0x40, 0xDF, 0x0E],
4398 "Tamron AF 18-270mm f/3.5-6.3 Di II VC PZD (B008)",
4399 ),
4400 (
4401 [0xE0, 0x40, 0x2D, 0x98, 0x2C, 0x41, 0xDF, 0x0E],
4402 "Tamron 18-400mm f/3.5-6.3 Di II VC HLD (B028)",
4403 ),
4404 (
4405 [0xE0, 0x40, 0x2D, 0x98, 0x2C, 0x41, 0xDF, 0x4E],
4406 "Tamron 18-400mm f/3.5-6.3 Di II VC HLD (B028)",
4407 ),
4408 (
4409 [0x07, 0x40, 0x2F, 0x44, 0x2C, 0x34, 0x03, 0x02],
4410 "Tamron AF 19-35mm f/3.5-4.5 (A10)",
4411 ),
4412 (
4413 [0x00, 0x49, 0x30, 0x48, 0x22, 0x2B, 0x00, 0x02],
4414 "Tamron SP AF 20-40mm f/2.7-3.5 (166D)",
4415 ),
4416 (
4417 [0x0E, 0x4A, 0x31, 0x48, 0x23, 0x2D, 0x0E, 0x02],
4418 "Tamron SP AF 20-40mm f/2.7-3.5 (166D)",
4419 ),
4420 (
4421 [0xFE, 0x48, 0x37, 0x5C, 0x24, 0x24, 0xDF, 0x0E],
4422 "Tamron SP 24-70mm f/2.8 Di VC USD (A007)",
4423 ),
4424 (
4425 [0xCE, 0x47, 0x37, 0x5C, 0x25, 0x25, 0xDF, 0x4E],
4426 "Tamron SP 24-70mm f/2.8 Di VC USD G2 (A032)",
4427 ),
4428 (
4429 [0xCE, 0x00, 0x37, 0x5C, 0x25, 0x25, 0xDF, 0x4E],
4430 "Tamron SP 24-70mm f/2.8 Di VC USD G2 (A032)",
4431 ),
4432 (
4433 [0x45, 0x41, 0x37, 0x72, 0x2C, 0x3C, 0x48, 0x02],
4434 "Tamron SP AF 24-135mm f/3.5-5.6 AD Aspherical (IF) Macro (190D)",
4435 ),
4436 (
4437 [0x33, 0x54, 0x3C, 0x5E, 0x24, 0x24, 0x62, 0x02],
4438 "Tamron SP AF 28-75mm f/2.8 XR Di LD Aspherical (IF) Macro (A09)",
4439 ),
4440 (
4441 [0xFA, 0x54, 0x3C, 0x5E, 0x24, 0x24, 0x84, 0x06],
4442 "Tamron SP AF 28-75mm f/2.8 XR Di LD Aspherical (IF) Macro (A09NII)",
4443 ),
4444 (
4445 [0xFA, 0x54, 0x3C, 0x5E, 0x24, 0x24, 0xDF, 0x06],
4446 "Tamron SP AF 28-75mm f/2.8 XR Di LD Aspherical (IF) Macro (A09NII)",
4447 ),
4448 (
4449 [0x10, 0x3D, 0x3C, 0x60, 0x2C, 0x3C, 0xD2, 0x02],
4450 "Tamron AF 28-80mm f/3.5-5.6 Aspherical (177D)",
4451 ),
4452 (
4453 [0x45, 0x3D, 0x3C, 0x60, 0x2C, 0x3C, 0x48, 0x02],
4454 "Tamron AF 28-80mm f/3.5-5.6 Aspherical (177D)",
4455 ),
4456 (
4457 [0x00, 0x48, 0x3C, 0x6A, 0x24, 0x24, 0x00, 0x02],
4458 "Tamron SP AF 28-105mm f/2.8 LD Aspherical IF (176D)",
4459 ),
4460 (
4461 [0x4D, 0x3E, 0x3C, 0x80, 0x2E, 0x3C, 0x62, 0x02],
4462 "Tamron AF 28-200mm f/3.8-5.6 XR Aspherical (IF) Macro (A03N)",
4463 ),
4464 (
4465 [0x0B, 0x3E, 0x3D, 0x7F, 0x2F, 0x3D, 0x0E, 0x00],
4466 "Tamron AF 28-200mm f/3.8-5.6 (71D)",
4467 ),
4468 (
4469 [0x0B, 0x3E, 0x3D, 0x7F, 0x2F, 0x3D, 0x0E, 0x02],
4470 "Tamron AF 28-200mm f/3.8-5.6D (171D)",
4471 ),
4472 (
4473 [0x12, 0x3D, 0x3C, 0x80, 0x2E, 0x3C, 0xDF, 0x02],
4474 "Tamron AF 28-200mm f/3.8-5.6 AF Aspherical LD (IF) (271D)",
4475 ),
4476 (
4477 [0x4D, 0x41, 0x3C, 0x8E, 0x2B, 0x40, 0x62, 0x02],
4478 "Tamron AF 28-300mm f/3.5-6.3 XR Di LD Aspherical (IF) (A061)",
4479 ),
4480 (
4481 [0x4D, 0x41, 0x3C, 0x8E, 0x2C, 0x40, 0x62, 0x02],
4482 "Tamron AF 28-300mm f/3.5-6.3 XR LD Aspherical (IF) (185D)",
4483 ),
4484 (
4485 [0xF9, 0x40, 0x3C, 0x8E, 0x2C, 0x40, 0x40, 0x0E],
4486 "Tamron AF 28-300mm f/3.5-6.3 XR Di VC LD Aspherical (IF) Macro (A20)",
4487 ),
4488 (
4489 [0xC9, 0x3C, 0x44, 0x76, 0x25, 0x31, 0xDF, 0x4E],
4490 "Tamron 35-150mm f/2.8-4 Di VC OSD (A043)",
4491 ),
4492 (
4493 [0x00, 0x47, 0x53, 0x80, 0x30, 0x3C, 0x00, 0x06],
4494 "Tamron AF 55-200mm f/4-5.6 Di II LD (A15)",
4495 ),
4496 (
4497 [0xF7, 0x53, 0x5C, 0x80, 0x24, 0x24, 0x84, 0x06],
4498 "Tamron SP AF 70-200mm f/2.8 Di LD (IF) Macro (A001)",
4499 ),
4500 (
4501 [0xFE, 0x53, 0x5C, 0x80, 0x24, 0x24, 0x84, 0x06],
4502 "Tamron SP AF 70-200mm f/2.8 Di LD (IF) Macro (A001)",
4503 ),
4504 (
4505 [0xF7, 0x53, 0x5C, 0x80, 0x24, 0x24, 0x40, 0x06],
4506 "Tamron SP AF 70-200mm f/2.8 Di LD (IF) Macro (A001)",
4507 ),
4508 (
4509 [0xFE, 0x54, 0x5C, 0x80, 0x24, 0x24, 0xDF, 0x0E],
4510 "Tamron SP 70-200mm f/2.8 Di VC USD (A009)",
4511 ),
4512 (
4513 [0xE2, 0x47, 0x5C, 0x80, 0x24, 0x24, 0xDF, 0x4E],
4514 "Tamron SP 70-200mm f/2.8 Di VC USD G2 (A025)",
4515 ),
4516 (
4517 [0x69, 0x48, 0x5C, 0x8E, 0x30, 0x3C, 0x6F, 0x02],
4518 "Tamron AF 70-300mm f/4-5.6 LD Macro 1:2 (572D/772D)",
4519 ),
4520 (
4521 [0x69, 0x47, 0x5C, 0x8E, 0x30, 0x3C, 0x00, 0x02],
4522 "Tamron AF 70-300mm f/4-5.6 Di LD Macro 1:2 (A17N)",
4523 ),
4524 (
4525 [0x00, 0x48, 0x5C, 0x8E, 0x30, 0x3C, 0x00, 0x06],
4526 "Tamron AF 70-300mm f/4-5.6 Di LD Macro 1:2 (A17NII)",
4527 ),
4528 (
4529 [0xF1, 0x47, 0x5C, 0x8E, 0x30, 0x3C, 0xDF, 0x0E],
4530 "Tamron SP 70-300mm f/4-5.6 Di VC USD (A005)",
4531 ),
4532 (
4533 [0xCF, 0x47, 0x5C, 0x8E, 0x31, 0x3D, 0xDF, 0x0E],
4534 "Tamron SP 70-300mm f/4-5.6 Di VC USD (A030)",
4535 ),
4536 (
4537 [0xCC, 0x44, 0x68, 0x98, 0x34, 0x41, 0xDF, 0x0E],
4538 "Tamron 100-400mm f/4.5-6.3 Di VC USD",
4539 ),
4540 (
4541 [0xEB, 0x40, 0x76, 0xA6, 0x38, 0x40, 0xDF, 0x0E],
4542 "Tamron SP AF 150-600mm f/5-6.3 VC USD (A011)",
4543 ),
4544 (
4545 [0xE3, 0x40, 0x76, 0xA6, 0x38, 0x40, 0xDF, 0x4E],
4546 "Tamron SP 150-600mm f/5-6.3 Di VC USD G2",
4547 ),
4548 (
4549 [0xE3, 0x40, 0x76, 0xA6, 0x38, 0x40, 0xDF, 0x0E],
4550 "Tamron SP 150-600mm f/5-6.3 Di VC USD G2 (A022)",
4551 ),
4552 (
4553 [0x20, 0x3C, 0x80, 0x98, 0x3D, 0x3D, 0x1E, 0x02],
4554 "Tamron AF 200-400mm f/5.6 LD IF (75D)",
4555 ),
4556 (
4557 [0x00, 0x3E, 0x80, 0xA0, 0x38, 0x3F, 0x00, 0x02],
4558 "Tamron SP AF 200-500mm f/5-6.3 Di LD (IF) (A08)",
4559 ),
4560 (
4561 [0x00, 0x3F, 0x80, 0xA0, 0x38, 0x3F, 0x00, 0x02],
4562 "Tamron SP AF 200-500mm f/5-6.3 Di (A08)",
4563 ),
4564 (
4565 [0x00, 0x40, 0x2B, 0x2B, 0x2C, 0x2C, 0x00, 0x02],
4566 "Tokina AT-X 17 AF PRO (AF 17mm f/3.5)",
4567 ),
4568 (
4569 [0x00, 0x47, 0x44, 0x44, 0x24, 0x24, 0x00, 0x06],
4570 "Tokina AT-X M35 PRO DX (AF 35mm f/2.8 Macro)",
4571 ),
4572 (
4573 [0x8D, 0x54, 0x68, 0x68, 0x24, 0x24, 0x87, 0x02],
4574 "Tokina AT-X PRO 100mm F2.8 D Macro",
4575 ),
4576 (
4577 [0x00, 0x54, 0x68, 0x68, 0x24, 0x24, 0x00, 0x02],
4578 "Tokina AT-X M100 AF PRO D (AF 100mm f/2.8 Macro)",
4579 ),
4580 (
4581 [0x27, 0x48, 0x8E, 0x8E, 0x30, 0x30, 0x1D, 0x02],
4582 "Tokina AT-X 304 AF (AF 300mm f/4.0)",
4583 ),
4584 (
4585 [0x00, 0x54, 0x8E, 0x8E, 0x24, 0x24, 0x00, 0x02],
4586 "Tokina AT-X 300 AF PRO (AF 300mm f/2.8)",
4587 ),
4588 (
4589 [0x12, 0x3B, 0x98, 0x98, 0x3D, 0x3D, 0x09, 0x00],
4590 "Tokina AT-X 400 AF SD (AF 400mm f/5.6)",
4591 ),
4592 (
4593 [0x00, 0x40, 0x18, 0x2B, 0x2C, 0x34, 0x00, 0x06],
4594 "Tokina AT-X 107 AF DX Fisheye (AF 10-17mm f/3.5-4.5)",
4595 ),
4596 (
4597 [0x00, 0x48, 0x1C, 0x29, 0x24, 0x24, 0x00, 0x06],
4598 "Tokina AT-X 116 PRO DX (AF 11-16mm f/2.8)",
4599 ),
4600 (
4601 [0x7A, 0x48, 0x1C, 0x29, 0x24, 0x24, 0x7E, 0x06],
4602 "Tokina AT-X 116 PRO DX II (AF 11-16mm f/2.8)",
4603 ),
4604 (
4605 [0x80, 0x48, 0x1C, 0x29, 0x24, 0x24, 0x7A, 0x06],
4606 "Tokina atx-i 11-16mm F2.8 CF",
4607 ),
4608 (
4609 [0x7A, 0x48, 0x1C, 0x30, 0x24, 0x24, 0x7E, 0x06],
4610 "Tokina AT-X 11-20 F2.8 PRO DX (AF 11-20mm f/2.8)",
4611 ),
4612 (
4613 [0x8B, 0x48, 0x1C, 0x30, 0x24, 0x24, 0x85, 0x06],
4614 "Tokina AT-X 11-20 F2.8 PRO DX (AF 11-20mm f/2.8)",
4615 ),
4616 (
4617 [0x00, 0x3C, 0x1F, 0x37, 0x30, 0x30, 0x00, 0x06],
4618 "Tokina AT-X 124 AF PRO DX (AF 12-24mm f/4)",
4619 ),
4620 (
4621 [0x7A, 0x3C, 0x1F, 0x3C, 0x30, 0x30, 0x7E, 0x06],
4622 "Tokina AT-X 12-28 PRO DX (AF 12-28mm f/4)",
4623 ),
4624 (
4625 [0x00, 0x48, 0x29, 0x3C, 0x24, 0x24, 0x00, 0x06],
4626 "Tokina AT-X 16-28 AF PRO FX (AF 16-28mm f/2.8)",
4627 ),
4628 (
4629 [0x00, 0x48, 0x29, 0x50, 0x24, 0x24, 0x00, 0x06],
4630 "Tokina AT-X 165 PRO DX (AF 16-50mm f/2.8)",
4631 ),
4632 (
4633 [0x00, 0x40, 0x2A, 0x72, 0x2C, 0x3C, 0x00, 0x06],
4634 "Tokina AT-X 16.5-135 DX (AF 16.5-135mm F3.5-5.6)",
4635 ),
4636 (
4637 [0x00, 0x3C, 0x2B, 0x44, 0x30, 0x30, 0x00, 0x06],
4638 "Tokina AT-X 17-35 F4 PRO FX (AF 17-35mm f/4)",
4639 ),
4640 (
4641 [0x00, 0x48, 0x37, 0x5C, 0x24, 0x24, 0x00, 0x06],
4642 "Tokina AT-X 24-70 F2.8 PRO FX (AF 24-70mm f/2.8)",
4643 ),
4644 (
4645 [0x00, 0x40, 0x37, 0x80, 0x2C, 0x3C, 0x00, 0x02],
4646 "Tokina AT-X 242 AF (AF 24-200mm f/3.5-5.6)",
4647 ),
4648 (
4649 [0x07, 0x48, 0x3C, 0x5C, 0x24, 0x24, 0x03, 0x00],
4650 "Tokina AT-X 287 AF (AF 28-70mm f/2.8)",
4651 ),
4652 (
4653 [0x07, 0x47, 0x3C, 0x5C, 0x25, 0x35, 0x03, 0x00],
4654 "Tokina AF 287 SD (AF 28-70mm f/2.8-4.5)",
4655 ),
4656 (
4657 [0x07, 0x40, 0x3C, 0x5C, 0x2C, 0x35, 0x03, 0x00],
4658 "Tokina AF 270 II (AF 28-70mm f/3.5-4.5)",
4659 ),
4660 (
4661 [0x00, 0x48, 0x3C, 0x60, 0x24, 0x24, 0x00, 0x02],
4662 "Tokina AT-X 280 AF PRO (AF 28-80mm f/2.8)",
4663 ),
4664 (
4665 [0x25, 0x44, 0x44, 0x8E, 0x34, 0x42, 0x1B, 0x02],
4666 "Tokina AF 353 (AF 35-300mm f/4.5-6.7)",
4667 ),
4668 (
4669 [0x00, 0x48, 0x50, 0x72, 0x24, 0x24, 0x00, 0x06],
4670 "Tokina AT-X 535 PRO DX (AF 50-135mm f/2.8)",
4671 ),
4672 (
4673 [0x00, 0x3C, 0x5C, 0x80, 0x30, 0x30, 0x00, 0x0E],
4674 "Tokina AT-X 70-200 F4 FX VCM-S (AF 70-200mm f/4)",
4675 ),
4676 (
4677 [0x00, 0x48, 0x5C, 0x80, 0x30, 0x30, 0x00, 0x0E],
4678 "Tokina AT-X 70-200 F4 FX VCM-S (AF 70-200mm f/4)",
4679 ),
4680 (
4681 [0x12, 0x44, 0x5E, 0x8E, 0x34, 0x3C, 0x09, 0x00],
4682 "Tokina AF 730 (AF 75-300mm F4.5-5.6)",
4683 ),
4684 (
4685 [0x14, 0x54, 0x60, 0x80, 0x24, 0x24, 0x0B, 0x00],
4686 "Tokina AT-X 828 AF (AF 80-200mm f/2.8)",
4687 ),
4688 (
4689 [0x24, 0x54, 0x60, 0x80, 0x24, 0x24, 0x1A, 0x02],
4690 "Tokina AT-X 828 AF PRO (AF 80-200mm f/2.8)",
4691 ),
4692 (
4693 [0x24, 0x44, 0x60, 0x98, 0x34, 0x3C, 0x1A, 0x02],
4694 "Tokina AT-X 840 AF-II (AF 80-400mm f/4.5-5.6)",
4695 ),
4696 (
4697 [0x00, 0x44, 0x60, 0x98, 0x34, 0x3C, 0x00, 0x02],
4698 "Tokina AT-X 840 D (AF 80-400mm f/4.5-5.6)",
4699 ),
4700 (
4701 [0x14, 0x48, 0x68, 0x8E, 0x30, 0x30, 0x0B, 0x00],
4702 "Tokina AT-X 340 AF (AF 100-300mm f/4)",
4703 ),
4704 (
4705 [0x8C, 0x48, 0x29, 0x3C, 0x24, 0x24, 0x86, 0x06],
4706 "Tokina opera 16-28mm F2.8 FF",
4707 ),
4708 (
4709 [0x06, 0x3F, 0x68, 0x68, 0x2C, 0x2C, 0x06, 0x00],
4710 "Cosina AF 100mm F3.5 Macro",
4711 ),
4712 (
4713 [0x07, 0x36, 0x3D, 0x5F, 0x2C, 0x3C, 0x03, 0x00],
4714 "Cosina AF Zoom 28-80mm F3.5-5.6 MC Macro",
4715 ),
4716 (
4717 [0x07, 0x46, 0x3D, 0x6A, 0x25, 0x2F, 0x03, 0x00],
4718 "Cosina AF Zoom 28-105mm F2.8-3.8 MC",
4719 ),
4720 (
4721 [0x12, 0x36, 0x5C, 0x81, 0x35, 0x3D, 0x09, 0x00],
4722 "Cosina AF Zoom 70-210mm F4.5-5.6 MC Macro",
4723 ),
4724 (
4725 [0x12, 0x39, 0x5C, 0x8E, 0x34, 0x3D, 0x08, 0x02],
4726 "Cosina AF Zoom 70-300mm F4.5-5.6 MC Macro",
4727 ),
4728 (
4729 [0x12, 0x3B, 0x68, 0x8D, 0x3D, 0x43, 0x09, 0x02],
4730 "Cosina AF Zoom 100-300mm F5.6-6.7 MC Macro",
4731 ),
4732 (
4733 [0x12, 0x38, 0x69, 0x97, 0x35, 0x42, 0x09, 0x02],
4734 "Promaster Spectrum 7 100-400mm F4.5-6.7",
4735 ),
4736 (
4737 [0x00, 0x40, 0x31, 0x31, 0x2C, 0x2C, 0x00, 0x00],
4738 "Voigtlander Color Skopar 20mm F3.5 SLII Aspherical",
4739 ),
4740 (
4741 [0x00, 0x48, 0x3C, 0x3C, 0x24, 0x24, 0x00, 0x00],
4742 "Voigtlander Color Skopar 28mm F2.8 SL II",
4743 ),
4744 (
4745 [0x00, 0x54, 0x48, 0x48, 0x18, 0x18, 0x00, 0x00],
4746 "Voigtlander Ultron 40mm F2 SLII Aspherical",
4747 ),
4748 (
4749 [0x00, 0x54, 0x55, 0x55, 0x0C, 0x0C, 0x00, 0x00],
4750 "Voigtlander Nokton 58mm F1.4 SLII",
4751 ),
4752 (
4753 [0x00, 0x40, 0x64, 0x64, 0x2C, 0x2C, 0x00, 0x00],
4754 "Voigtlander APO-Lanthar 90mm F3.5 SLII Close Focus",
4755 ),
4756 (
4757 [0x71, 0x48, 0x64, 0x64, 0x24, 0x24, 0x00, 0x00],
4758 "Voigtlander APO-Skopar 90mm F2.8 SL IIs",
4759 ),
4760 (
4761 [0xFD, 0x00, 0x50, 0x50, 0x18, 0x18, 0xDF, 0x00],
4762 "Voigtlander APO-Lanthar 50mm F2 Aspherical",
4763 ),
4764 (
4765 [0xFD, 0x00, 0x44, 0x44, 0x18, 0x18, 0xDF, 0x00],
4766 "Voigtlander APO-Lanthar 35mm F2",
4767 ),
4768 (
4769 [0xFD, 0x00, 0x59, 0x59, 0x18, 0x18, 0xDF, 0x00],
4770 "Voigtlander Macro APO-Lanthar 65mm F2",
4771 ),
4772 (
4773 [0xFD, 0x00, 0x48, 0x48, 0x07, 0x07, 0xDF, 0x00],
4774 "Voigtlander Nokton 40mm F1.2 Aspherical",
4775 ),
4776 (
4777 [0xFD, 0x00, 0x3C, 0x3C, 0x18, 0x18, 0xDF, 0x00],
4778 "Voigtlander APO-Lanthar 28mm F2 Aspherical",
4779 ),
4780 (
4781 [0x00, 0x40, 0x2D, 0x2D, 0x2C, 0x2C, 0x00, 0x00],
4782 "Carl Zeiss Distagon T* 3.5/18 ZF.2",
4783 ),
4784 (
4785 [0x00, 0x48, 0x27, 0x27, 0x24, 0x24, 0x00, 0x00],
4786 "Carl Zeiss Distagon T* 2.8/15 ZF.2",
4787 ),
4788 (
4789 [0x00, 0x48, 0x32, 0x32, 0x24, 0x24, 0x00, 0x00],
4790 "Carl Zeiss Distagon T* 2.8/21 ZF.2",
4791 ),
4792 (
4793 [0x00, 0x54, 0x38, 0x38, 0x18, 0x18, 0x00, 0x00],
4794 "Carl Zeiss Distagon T* 2/25 ZF.2",
4795 ),
4796 (
4797 [0x00, 0x54, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x00],
4798 "Carl Zeiss Distagon T* 2/28 ZF.2",
4799 ),
4800 (
4801 [0x00, 0x54, 0x44, 0x44, 0x0C, 0x0C, 0x00, 0x00],
4802 "Carl Zeiss Distagon T* 1.4/35 ZF.2",
4803 ),
4804 (
4805 [0x00, 0x54, 0x44, 0x44, 0x18, 0x18, 0x00, 0x00],
4806 "Carl Zeiss Distagon T* 2/35 ZF.2",
4807 ),
4808 (
4809 [0x00, 0x54, 0x50, 0x50, 0x0C, 0x0C, 0x00, 0x00],
4810 "Carl Zeiss Planar T* 1.4/50 ZF.2",
4811 ),
4812 (
4813 [0x00, 0x54, 0x50, 0x50, 0x18, 0x18, 0x00, 0x00],
4814 "Carl Zeiss Makro-Planar T* 2/50 ZF.2",
4815 ),
4816 (
4817 [0x00, 0x54, 0x62, 0x62, 0x0C, 0x0C, 0x00, 0x00],
4818 "Carl Zeiss Planar T* 1.4/85 ZF.2",
4819 ),
4820 (
4821 [0x00, 0x54, 0x68, 0x68, 0x18, 0x18, 0x00, 0x00],
4822 "Carl Zeiss Makro-Planar T* 2/100 ZF.2",
4823 ),
4824 (
4825 [0x00, 0x54, 0x72, 0x72, 0x18, 0x18, 0x00, 0x00],
4826 "Carl Zeiss Apo Sonnar T* 2/135 ZF.2",
4827 ),
4828 (
4829 [0x02, 0x54, 0x3C, 0x3C, 0x0C, 0x0C, 0x00, 0x00],
4830 "Zeiss Otus 1.4/28 ZF.2",
4831 ),
4832 (
4833 [0x00, 0x54, 0x53, 0x53, 0x0C, 0x0C, 0x00, 0x00],
4834 "Zeiss Otus 1.4/55",
4835 ),
4836 (
4837 [0x01, 0x54, 0x62, 0x62, 0x0C, 0x0C, 0x00, 0x00],
4838 "Zeiss Otus 1.4/85",
4839 ),
4840 (
4841 [0x03, 0x54, 0x68, 0x68, 0x0C, 0x0C, 0x00, 0x00],
4842 "Zeiss Otus 1.4/100",
4843 ),
4844 (
4845 [0x52, 0x54, 0x44, 0x44, 0x18, 0x18, 0x00, 0x00],
4846 "Zeiss Milvus 35mm f/2",
4847 ),
4848 (
4849 [0x53, 0x54, 0x50, 0x50, 0x0C, 0x0C, 0x00, 0x00],
4850 "Zeiss Milvus 50mm f/1.4",
4851 ),
4852 (
4853 [0x54, 0x54, 0x50, 0x50, 0x18, 0x18, 0x00, 0x00],
4854 "Zeiss Milvus 50mm f/2 Macro",
4855 ),
4856 (
4857 [0x55, 0x54, 0x62, 0x62, 0x0C, 0x0C, 0x00, 0x00],
4858 "Zeiss Milvus 85mm f/1.4",
4859 ),
4860 (
4861 [0x56, 0x54, 0x68, 0x68, 0x18, 0x18, 0x00, 0x00],
4862 "Zeiss Milvus 100mm f/2 Macro",
4863 ),
4864 (
4865 [0x00, 0x54, 0x56, 0x56, 0x30, 0x30, 0x00, 0x00],
4866 "Coastal Optical Systems 60mm 1:4 UV-VIS-IR Macro Apo",
4867 ),
4868 (
4869 [0xBF, 0x4E, 0x26, 0x26, 0x1E, 0x1E, 0x01, 0x04],
4870 "Irix 15mm f/2.4 Firefly",
4871 ),
4872 (
4873 [0xBF, 0x3C, 0x1B, 0x1B, 0x30, 0x30, 0x01, 0x04],
4874 "Irix 11mm f/4 Firefly",
4875 ),
4876 (
4877 [0x4A, 0x40, 0x11, 0x11, 0x2C, 0x0C, 0x4D, 0x02],
4878 "Samyang 8mm f/3.5 Fish-Eye CS",
4879 ),
4880 (
4881 [0x4A, 0x48, 0x1E, 0x1E, 0x24, 0x0C, 0x4D, 0x02],
4882 "Samyang 12mm f/2.8 ED AS NCS Fish-Eye",
4883 ),
4884 (
4885 [0x4A, 0x4C, 0x24, 0x24, 0x1E, 0x6C, 0x4D, 0x06],
4886 "Samyang 14mm f/2.4 Premium",
4887 ),
4888 (
4889 [0x4A, 0x54, 0x29, 0x29, 0x18, 0x0C, 0x4D, 0x02],
4890 "Samyang 16mm f/2.0 ED AS UMC CS",
4891 ),
4892 (
4893 [0x4A, 0x60, 0x36, 0x36, 0x0C, 0x0C, 0x4D, 0x02],
4894 "Samyang 24mm f/1.4 ED AS UMC",
4895 ),
4896 (
4897 [0x4A, 0x60, 0x44, 0x44, 0x0C, 0x0C, 0x4D, 0x02],
4898 "Samyang 35mm f/1.4 AS UMC",
4899 ),
4900 (
4901 [0x4A, 0x60, 0x62, 0x62, 0x0C, 0x0C, 0x4D, 0x02],
4902 "Samyang AE 85mm f/1.4 AS IF UMC",
4903 ),
4904 (
4905 [0x9A, 0x4C, 0x50, 0x50, 0x14, 0x14, 0x9C, 0x06],
4906 "Yongnuo YN50mm F1.8N",
4907 ),
4908 (
4909 [0x9F, 0x48, 0x48, 0x48, 0x24, 0x24, 0xA1, 0x06],
4910 "Yongnuo YN40mm F2.8N",
4911 ),
4912 (
4913 [0x9F, 0x54, 0x68, 0x68, 0x18, 0x18, 0xA2, 0x06],
4914 "Yongnuo YN100mm F2N",
4915 ),
4916 (
4917 [0x9F, 0x4C, 0x44, 0x44, 0x18, 0x18, 0xA1, 0x06],
4918 "Yongnuo YN35mm F2",
4919 ),
4920 (
4921 [0x9F, 0x4D, 0x50, 0x50, 0x14, 0x14, 0xA0, 0x06],
4922 "Yongnuo YN50mm F1.8N",
4923 ),
4924 (
4925 [0x02, 0x40, 0x44, 0x5C, 0x2C, 0x34, 0x02, 0x00],
4926 "Exakta AF 35-70mm 1:3.5-4.5 MC",
4927 ),
4928 (
4929 [0x07, 0x3E, 0x30, 0x43, 0x2D, 0x35, 0x03, 0x00],
4930 "Soligor AF Zoom 19-35mm 1:3.5-4.5 MC",
4931 ),
4932 (
4933 [0x03, 0x43, 0x5C, 0x81, 0x35, 0x35, 0x02, 0x00],
4934 "Soligor AF C/D Zoom UMCS 70-210mm 1:4.5",
4935 ),
4936 (
4937 [0x12, 0x4A, 0x5C, 0x81, 0x31, 0x3D, 0x09, 0x00],
4938 "Soligor AF C/D Auto Zoom+Macro 70-210mm 1:4-5.6 UMCS",
4939 ),
4940 (
4941 [0x12, 0x36, 0x69, 0x97, 0x35, 0x42, 0x09, 0x00],
4942 "Soligor AF Zoom 100-400mm 1:4.5-6.7 MC",
4943 ),
4944 (
4945 [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
4946 "Manual Lens No CPU",
4947 ),
4948 (
4949 [0x00, 0x00, 0x48, 0x48, 0x53, 0x53, 0x00, 0x01],
4950 "Loreo 40mm F11-22 3D Lens in a Cap 9005",
4951 ),
4952 (
4953 [0x00, 0x47, 0x10, 0x10, 0x24, 0x24, 0x00, 0x00],
4954 "Fisheye Nikkor 8mm f/2.8 AiS",
4955 ),
4956 (
4957 [0x00, 0x47, 0x3C, 0x3C, 0x24, 0x24, 0x00, 0x00],
4958 "Nikkor 28mm f/2.8 AiS",
4959 ),
4960 (
4961 [0x00, 0x57, 0x50, 0x50, 0x14, 0x14, 0x00, 0x00],
4962 "Nikkor 50mm f/1.8 AI",
4963 ),
4964 (
4965 [0x00, 0x48, 0x50, 0x50, 0x18, 0x18, 0x00, 0x00],
4966 "Nikkor H 50mm f/2",
4967 ),
4968 (
4969 [0x00, 0x48, 0x68, 0x68, 0x24, 0x24, 0x00, 0x00],
4970 "Series E 100mm f/2.8",
4971 ),
4972 (
4973 [0x00, 0x4C, 0x6A, 0x6A, 0x20, 0x20, 0x00, 0x00],
4974 "Nikkor 105mm f/2.5 AiS",
4975 ),
4976 (
4977 [0x00, 0x48, 0x80, 0x80, 0x30, 0x30, 0x00, 0x00],
4978 "Nikkor 200mm f/4 AiS",
4979 ),
4980 (
4981 [0x00, 0x40, 0x11, 0x11, 0x2C, 0x2C, 0x00, 0x00],
4982 "Samyang 8mm f/3.5 Fish-Eye",
4983 ),
4984 (
4985 [0x00, 0x58, 0x64, 0x64, 0x20, 0x20, 0x00, 0x00],
4986 "Soligor C/D Macro MC 90mm f/2.5",
4987 ),
4988 (
4989 [0x4A, 0x58, 0x30, 0x30, 0x14, 0x0C, 0x4D, 0x02],
4990 "Rokinon 20mm f/1.8 ED AS UMC",
4991 ),
4992 (
4993 [0xA0, 0x56, 0x44, 0x44, 0x14, 0x14, 0xA2, 0x06],
4994 "Sony FE 35mm F1.8",
4995 ),
4996 (
4997 [0xA0, 0x37, 0x5C, 0x8E, 0x34, 0x3C, 0xA2, 0x06],
4998 "Sony FE 70-300mm F4.5-5.6 G OSS",
4999 ),
5000];
5001
5002#[allow(clippy::type_complexity)]
5010const MWG_MAPPINGS: &[(&str, &str, &[(&str, &str)])] = &[
5011 (
5012 "Description",
5013 "Description",
5014 &[
5015 ("XMP", "Description"),
5016 ("EXIF", "ImageDescription"),
5017 ("IPTC", "Caption-Abstract"),
5018 ],
5019 ),
5020 (
5021 "DateTimeOriginal",
5022 "Date/Time Original",
5023 &[
5024 ("EXIF", "DateTimeOriginal"),
5025 ("XMP", "DateCreated"),
5026 ("IPTC", "DateCreated"),
5027 ],
5028 ),
5029 (
5030 "CreateDate",
5031 "Create Date",
5032 &[
5033 ("XMP", "CreateDate"),
5034 ("EXIF", "CreateDate"),
5035 ("IPTC", "DigitalCreationDate"),
5036 ],
5037 ),
5038 (
5039 "Copyright",
5040 "Copyright",
5041 &[
5042 ("XMP", "Rights"),
5043 ("EXIF", "Copyright"),
5044 ("IPTC", "CopyrightNotice"),
5045 ],
5046 ),
5047 (
5048 "Creator",
5049 "Creator",
5050 &[("XMP", "Creator"), ("EXIF", "Artist"), ("IPTC", "By-line")],
5051 ),
5052 (
5053 "Keywords",
5054 "Keywords",
5055 &[("XMP", "Subject"), ("IPTC", "Keywords")],
5056 ),
5057 ("City", "City", &[("XMP", "City"), ("IPTC", "City")]),
5058 (
5059 "State",
5060 "State",
5061 &[("XMP", "State"), ("IPTC", "Province-State")],
5062 ),
5063 (
5064 "Country",
5065 "Country",
5066 &[("XMP", "Country"), ("IPTC", "Country-PrimaryLocationName")],
5067 ),
5068 (
5069 "Location",
5070 "Location",
5071 &[("XMP", "Location"), ("IPTC", "Sub-location")],
5072 ),
5073 ("Rating", "Rating", &[("XMP", "Rating")]),
5074];
5075
5076pub fn compute_mwg_composites(tags: &[Tag]) -> Vec<Tag> {
5081 let mut mwg_tags = Vec::new();
5082
5083 for &(mwg_name, description, sources) in MWG_MAPPINGS {
5084 let found = sources
5086 .iter()
5087 .find_map(|&(group, tag_name)| find_tag_with_group(tags, tag_name, group));
5088
5089 if let Some(source_tag) = found {
5090 mwg_tags.push(Tag {
5091 id: TagId::Text(format!("MWG:{}", mwg_name)),
5092 name: mwg_name.to_string(),
5093 description: description.to_string(),
5094 group: TagGroup {
5095 family0: "Composite".to_string(),
5096 family1: "MWG".to_string(),
5097 family2: "Other".to_string(),
5098 family3: "Main".into(),
5099 },
5100 raw_value: source_tag.raw_value.clone(),
5101 print_value: source_tag.print_value.clone(),
5102 priority: 10, });
5104 }
5105 }
5106
5107 mwg_tags
5108}
5109
5110fn find_tag_with_group<'a>(tags: &'a [Tag], name: &str, group: &str) -> Option<&'a Tag> {
5112 let name_lower = name.to_lowercase();
5113 let group_lower = group.to_lowercase();
5114 tags.iter().find(|t| {
5115 t.name.to_lowercase() == name_lower
5116 && (t.group.family0.to_lowercase().contains(&group_lower)
5117 || t.group.family1.to_lowercase().contains(&group_lower))
5118 })
5119}
5120
5121const MWG_WRITE_MAPPINGS: &[(&str, &[&str])] = &[
5124 (
5125 "Description",
5126 &[
5127 "XMP-dc:Description",
5128 "EXIF:ImageDescription",
5129 "IPTC:Caption-Abstract",
5130 ],
5131 ),
5132 (
5133 "DateTimeOriginal",
5134 &[
5135 "EXIF:DateTimeOriginal",
5136 "XMP-photoshop:DateCreated",
5137 "IPTC:DateCreated",
5138 ],
5139 ),
5140 (
5141 "CreateDate",
5142 &[
5143 "XMP-xmp:CreateDate",
5144 "EXIF:CreateDate",
5145 "IPTC:DigitalCreationDate",
5146 ],
5147 ),
5148 (
5149 "Copyright",
5150 &["XMP-dc:Rights", "EXIF:Copyright", "IPTC:CopyrightNotice"],
5151 ),
5152 (
5153 "Creator",
5154 &["XMP-dc:Creator", "EXIF:Artist", "IPTC:By-line"],
5155 ),
5156 ("Keywords", &["XMP-dc:Subject", "IPTC:Keywords"]),
5157 ("City", &["XMP-photoshop:City", "IPTC:City"]),
5158 ("State", &["XMP-photoshop:State", "IPTC:Province-State"]),
5159 (
5160 "Country",
5161 &["XMP-photoshop:Country", "IPTC:Country-PrimaryLocationName"],
5162 ),
5163 ("Location", &["XMP-iptcCore:Location", "IPTC:Sub-location"]),
5164 ("Rating", &["XMP-xmp:Rating"]),
5165];
5166
5167pub fn expand_mwg_write_tag(tag: &str) -> Vec<String> {
5172 let bare = if let Some(stripped) = tag.strip_prefix("MWG:") {
5174 stripped
5175 } else {
5176 tag
5177 };
5178
5179 for &(mwg_name, targets) in MWG_WRITE_MAPPINGS {
5180 if bare.eq_ignore_ascii_case(mwg_name) {
5181 return targets.iter().map(|t| t.to_string()).collect();
5182 }
5183 }
5184
5185 vec![tag.to_string()]
5187}
5188
5189#[cfg(test)]
5190mod tests {
5191 use super::*;
5192
5193 fn make_tag(name: &str, family0: &str, family1: &str, value: &str) -> Tag {
5195 Tag {
5196 id: TagId::Text(format!("{}:{}", family0, name)),
5197 name: name.to_string(),
5198 description: name.to_string(),
5199 group: TagGroup {
5200 family0: family0.to_string(),
5201 family1: family1.to_string(),
5202 family2: "Other".to_string(),
5203 family3: "Main".into(),
5204 },
5205 raw_value: Value::String(value.to_string()),
5206 print_value: value.to_string(),
5207 priority: 0,
5208 }
5209 }
5210
5211 #[test]
5212 fn test_mwg_description_priority() {
5213 let tags = vec![
5215 make_tag("ImageDescription", "EXIF", "IFD0", "EXIF description"),
5216 make_tag("Description", "XMP", "XMP-dc", "XMP description"),
5217 ];
5218 let mwg = compute_mwg_composites(&tags);
5219 let desc = mwg.iter().find(|t| t.name == "Description");
5220 assert!(desc.is_some(), "MWG Description should be present");
5221 assert_eq!(desc.unwrap().print_value, "XMP description");
5222 }
5223
5224 #[test]
5225 fn test_mwg_no_source_tags() {
5226 let mwg = compute_mwg_composites(&[]);
5228 assert!(mwg.is_empty(), "No MWG composites from empty tags");
5229 }
5230
5231 #[test]
5232 fn test_mwg_fallback_to_exif() {
5233 let tags = vec![make_tag("ImageDescription", "EXIF", "IFD0", "EXIF only")];
5235 let mwg = compute_mwg_composites(&tags);
5236 let desc = mwg.iter().find(|t| t.name == "Description");
5237 assert!(desc.is_some(), "MWG Description should fall back to EXIF");
5238 assert_eq!(desc.unwrap().print_value, "EXIF only");
5239 }
5240
5241 #[test]
5242 fn test_expand_mwg_write_tag() {
5243 let targets = expand_mwg_write_tag("Description");
5245 assert_eq!(targets.len(), 3);
5246 assert!(targets.contains(&"XMP-dc:Description".to_string()));
5247 assert!(targets.contains(&"EXIF:ImageDescription".to_string()));
5248 assert!(targets.contains(&"IPTC:Caption-Abstract".to_string()));
5249 }
5250
5251 #[test]
5252 fn test_expand_mwg_write_tag_with_prefix() {
5253 let targets = expand_mwg_write_tag("MWG:Description");
5255 assert_eq!(targets.len(), 3);
5256 }
5257
5258 #[test]
5259 fn test_expand_mwg_write_tag_non_mwg() {
5260 let targets = expand_mwg_write_tag("Artist");
5262 assert_eq!(targets, vec!["Artist".to_string()]);
5263 }
5264
5265 #[test]
5266 fn test_expand_mwg_write_tag_case_insensitive() {
5267 let targets = expand_mwg_write_tag("description");
5268 assert_eq!(targets.len(), 3);
5269 }
5270
5271 fn make_exif_tag(name: &str, value: Value, print: &str) -> Tag {
5275 Tag {
5276 id: TagId::Numeric(0),
5277 name: name.to_string(),
5278 description: name.to_string(),
5279 group: TagGroup {
5280 family0: "EXIF".to_string(),
5281 family1: "ExifIFD".to_string(),
5282 family2: "Image".to_string(),
5283 family3: "Main".into(),
5284 },
5285 raw_value: value,
5286 print_value: print.to_string(),
5287 priority: 0,
5288 }
5289 }
5290
5291 fn make_gps_tag(name: &str, value: Value, print: &str) -> Tag {
5292 Tag {
5293 id: TagId::Numeric(0),
5294 name: name.to_string(),
5295 description: name.to_string(),
5296 group: TagGroup {
5297 family0: "EXIF".to_string(),
5298 family1: "GPS".to_string(),
5299 family2: "Location".to_string(),
5300 family3: "Main".into(),
5301 },
5302 raw_value: value,
5303 print_value: print.to_string(),
5304 priority: 0,
5305 }
5306 }
5307
5308 #[test]
5309 fn empty_tags_produce_no_composites() {
5310 let result = compute_composite_tags(&[]);
5311 assert!(result.is_empty());
5312 }
5313
5314 #[test]
5315 fn image_size_from_width_and_height() {
5316 let tags = vec![
5317 make_exif_tag("ImageWidth", Value::U32(1920), "1920"),
5318 make_exif_tag("ImageHeight", Value::U32(1080), "1080"),
5319 ];
5320 let composites = compute_composite_tags(&tags);
5321 let size = composites.iter().find(|t| t.name == "ImageSize");
5322 assert!(size.is_some(), "ImageSize composite not found");
5323 assert_eq!(size.unwrap().print_value, "1920x1080");
5324 }
5325
5326 #[test]
5327 fn megapixels_from_image_size() {
5328 let tags = vec![
5329 make_exif_tag("ImageWidth", Value::U32(4000), "4000"),
5330 make_exif_tag("ImageHeight", Value::U32(3000), "3000"),
5331 ];
5332 let composites = compute_composite_tags(&tags);
5333 let mp = composites.iter().find(|t| t.name == "Megapixels");
5334 assert!(mp.is_some(), "Megapixels composite not found");
5335 assert_eq!(mp.unwrap().print_value, "12.0");
5336 }
5337
5338 #[test]
5339 fn gps_position_from_lat_lon() {
5340 let tags = vec![
5341 make_gps_tag(
5342 "GPSLatitude",
5343 Value::List(vec![
5344 Value::URational(48, 1),
5345 Value::URational(51, 1),
5346 Value::URational(24, 1),
5347 ]),
5348 "48 deg 51' 24\"",
5349 ),
5350 make_gps_tag("GPSLatitudeRef", Value::String("N".into()), "N"),
5351 make_gps_tag(
5352 "GPSLongitude",
5353 Value::List(vec![
5354 Value::URational(2, 1),
5355 Value::URational(21, 1),
5356 Value::URational(7, 1),
5357 ]),
5358 "2 deg 21' 7\"",
5359 ),
5360 make_gps_tag("GPSLongitudeRef", Value::String("E".into()), "E"),
5361 ];
5362 let composites = compute_composite_tags(&tags);
5363 let pos = composites.iter().find(|t| t.name == "GPSPosition");
5364 assert!(pos.is_some(), "GPSPosition composite not found");
5365 let pv = &pos.unwrap().print_value;
5366 assert!(pv.contains("deg"), "expected degrees in: {}", pv);
5367 assert!(pv.contains(", "), "expected comma separator in: {}", pv);
5368 }
5369
5370 #[test]
5371 fn gps_position_south_west_negative() {
5372 let tags = vec![
5373 make_gps_tag(
5374 "GPSLatitude",
5375 Value::List(vec![
5376 Value::URational(33, 1),
5377 Value::URational(52, 1),
5378 Value::URational(0, 1),
5379 ]),
5380 "33 deg 52' 0\" S",
5381 ),
5382 make_gps_tag("GPSLatitudeRef", Value::String("S".into()), "S"),
5383 make_gps_tag(
5384 "GPSLongitude",
5385 Value::List(vec![
5386 Value::URational(151, 1),
5387 Value::URational(12, 1),
5388 Value::URational(0, 1),
5389 ]),
5390 "151 deg 12' 0\" W",
5391 ),
5392 make_gps_tag("GPSLongitudeRef", Value::String("W".into()), "W"),
5393 ];
5394 let composites = compute_composite_tags(&tags);
5395 let pos = composites.iter().find(|t| t.name == "GPSPosition");
5396 assert!(pos.is_some(), "GPSPosition composite not found");
5397 let pv = &pos.unwrap().print_value;
5398 let parts: Vec<&str> = pv.split(", ").collect();
5401 assert!(
5402 parts.len() == 2 && parts[0].ends_with(" S") && parts[1].ends_with(" W"),
5403 "expected S/W hemisphere suffixes in: {}",
5404 pv
5405 );
5406 }
5407
5408 #[test]
5409 fn shutter_speed_from_exposure_time() {
5410 let tags = vec![make_exif_tag(
5411 "ExposureTime",
5412 Value::URational(1, 125),
5413 "1/125",
5414 )];
5415 let composites = compute_composite_tags(&tags);
5416 let ss = composites.iter().find(|t| t.name == "ShutterSpeed");
5417 assert!(ss.is_some(), "ShutterSpeed composite not found");
5418 }
5419
5420 #[test]
5421 fn aperture_from_fnumber() {
5422 let tags = vec![make_exif_tag("FNumber", Value::URational(28, 10), "2.8")];
5423 let composites = compute_composite_tags(&tags);
5424 let ap = composites.iter().find(|t| t.name == "Aperture");
5425 assert!(ap.is_some(), "Aperture composite not found");
5426 assert_eq!(ap.unwrap().print_value, "2.8");
5427 }
5428
5429 #[test]
5430 fn light_value_from_aperture_shutter_iso() {
5431 let tags = vec![
5432 make_exif_tag("FNumber", Value::URational(4, 1), "4.0"),
5433 make_exif_tag("ExposureTime", Value::URational(1, 125), "1/125"),
5434 make_exif_tag("ISO", Value::U16(100), "100"),
5435 ];
5436 let composites = compute_composite_tags(&tags);
5437 let lv = composites.iter().find(|t| t.name == "LightValue");
5438 assert!(lv.is_some(), "LightValue composite not found");
5439 let val: f64 = lv
5440 .unwrap()
5441 .print_value
5442 .parse()
5443 .expect("LV should be numeric");
5444 assert!((val - 11.0).abs() < 1.0, "LV expected ~11, got: {}", val);
5445 }
5446
5447 #[test]
5448 fn no_gps_without_longitude() {
5449 let tags = vec![make_gps_tag(
5450 "GPSLatitude",
5451 Value::List(vec![
5452 Value::URational(48, 1),
5453 Value::URational(51, 1),
5454 Value::URational(24, 1),
5455 ]),
5456 "48 deg 51' 24\"",
5457 )];
5458 let composites = compute_composite_tags(&tags);
5459 assert!(
5460 composites.iter().all(|t| t.name != "GPSPosition"),
5461 "GPSPosition should not be generated without GPSLongitude"
5462 );
5463 }
5464
5465 #[test]
5466 fn no_image_size_without_height() {
5467 let tags = vec![make_exif_tag("ImageWidth", Value::U32(800), "800")];
5468 let composites = compute_composite_tags(&tags);
5469 assert!(
5470 composites.iter().all(|t| t.name != "ImageSize"),
5471 "ImageSize should not be generated without ImageHeight"
5472 );
5473 }
5474
5475 #[test]
5476 fn composite_tags_in_composite_group() {
5477 let tags = vec![
5478 make_exif_tag("ImageWidth", Value::U32(640), "640"),
5479 make_exif_tag("ImageHeight", Value::U32(480), "480"),
5480 ];
5481 let composites = compute_composite_tags(&tags);
5482 for tag in &composites {
5483 assert_eq!(tag.group.family0, "Composite");
5484 assert_eq!(tag.group.family1, "Composite");
5485 }
5486 }
5487}