Conflicts: - apps/lightcraft/src/native_menu.rs: upstream's item swapping (macOS keys given up for real, Help menu not swapped, Settings / Quit tracked) with KEY-GRID's keymap-aware rows. `Item.label` stays the text the row shows (native_item_presentation), so `Item::native_label` reuses it instead of formatting it again; Settings / Quit store theirs through display_item_label, as upstream's native_label did. No early return after a rebuild (upstream). - crates/ui-egui/src/i18n.rs: upstream's tests (#2729: one test per check across languages, gaps reported, never a failure) plus the batch's two new ones (dropped-photos notice, Polish counts); production code as merged. - grid.rs, keyword_list.rs, masking.rs: the translation tests #2729 removed stay removed; the batch's new keyword and segmenter tests stay. - cmd/library.rs, ui-egui/src/lib.rs: both test modules, each with its #[cfg(test)]. - xtask/src/main.rs: #2302's test_selected and upstream's translation_gaps. - docs: tracker / gap rows take both sides' edits, headers list both sides' changes, history rows of both sides newest-first. What's New: one section per date, newest first (the batch had 10-22 above 10-23, and second 10-21 and 10-19 sections); no line lost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lightcraft-meta (L1)
Photo metadata: EXIF, XMP (read + write), IPTC-IIM, and the containers that carry them.
API
| Function | Purpose |
|---|---|
read_exif(&[u8]) -> Metadata |
TIFF-structured Exif block (whole TIFF/DNG/raw file, or JPEG APP1 payload with/without Exif\0\0). try_read_exif reports header errors. |
from_tiff(&Tiff) -> Metadata |
Same, from an already parsed stream (used by lightcraft-raw). |
embedded(&[u8]) -> Embedded |
Sniffs JPEG / PNG / WebP / TIFF / PSD / PSB and returns the Exif, XMP (+ extended XMP), ICC and IPTC blocks. PSD/PSB EXIF resource 1058 and XMP resource 1060 are read from the bounded image-resource section without decoding pixels. jpeg_segments, png_chunks, webp_chunks are the per-container walkers. |
parse_xmp(&str) -> Result<XmpData> |
Metadata + the opaque lc:settings JSON + every property (prefix:name, structs flattened as a:b/c:d). |
write_xmp(&Metadata, Option<&str>) -> String |
Complete <?xpacket?> packet; the second argument is LightCraft's full develop settings JSON. |
parse_iptc(&[u8]) -> Metadata |
IIM record 2: title (2:5), keywords (2:25), by-line (2:80), copyright (2:116), caption (2:120), date/time created (2:55/2:60). |
extract(&[u8]) -> Metadata |
Everything for a whole file. Precedence: EXIF → IPTC fills gaps → XMP overrides user fields (rating, label, title, caption, artist, copyright, keywords, GPS) and fills the rest. |
try_extract(&[u8]) -> Result<Metadata, ExtractError> |
Like extract, but reports malformed PSD/PSB headers, image-resource sections, or embedded EXIF/XMP structure (including duplicate EXIF/XMP resources) so callers can fail closed when location absence cannot be established. Other formats remain best-effort. |
Metadata carries camera (make, model, serial, software), lens (make, model, serial, specification), capture
(date-time with UTC offset and milliseconds, exposure time, f-number, ISO incl. extended ISO, focal length and
35 mm equivalent, flash, exposure bias, program, metering, white balance), orientation, GPS, dimensions and the
user fields (artist, copyright, title, caption, keywords, hierarchical keywords, rating −1…5, colour label).
XMP namespaces
Written: dc, xmp, photoshop, exif, exifEX, tiff, lr (hierarchicalSubject) and ours,
lc = http://ns.lightcraft.app/lc/1.0/ (lc:settings, an opaque JSON string). Reading canonicalises
prefixes by namespace URI, handles attribute and element forms, rdf:Seq/Bag/Alt (x-default first),
rdf:parseType="Resource" and nested-description structs, predefined and numeric entities. Nesting depth
and node count are limited.
An explicitly foreign namespace using a recognized prefix (for example lc or xmp) cannot supply
recognized properties. Its keys use Q{hex-encoded namespace URI}local in properties and values;
hex encoding keeps / reserved for struct paths. Other unknown prefixes keep their existing QName keys.
Merging preserves the original foreign XML rather than writing these opaque keys into the packet.
The parser and merge scanner each cap generated opaque names at 8 MiB per packet, returning TooComplex
if repeated use of a long namespace URI would exceed that budget.
Robustness
No panics on malformed input: proptests feed random bytes to every container walker and random text to the XMP parser; truncation tests cut a sample JPEG at every byte and exercise truncated PSD/PSB headers and image-resource records with hostile declared lengths. PSD/PSB XMP resources require valid UTF-8; trailing NUL padding is trimmed after validation.
Sources
CIPA DC-008 (Exif 2.32), TIFF 6.0, Adobe DNG 1.7 (tag numbers), ISO 16684-1 / Adobe XMP Specification
Parts 1–3 (packet, RDF serialisation, standard schemas, JPEG extended XMP), IPTC-IIM 4.2 and the IPTC Photo
Metadata Standard, ITU-T T.81 (JPEG markers), ICC.1 (APP2 embedding), PNG Specification 3rd ed. (eXIf,
iTXt), the WebP RIFF container specification, Adobe Photoshop File Format Specification (image resource
blocks). XML parsing: quick-xml (MIT). No third-party metadata code was consulted.