# craft-metadata Photo metadata: EXIF, IPTC-IIM, XMP packets and GPX track logs, the blocks that carry them in image files, and one `Metadata` model they all read into. From LightCraft (`lightcraft-meta`). TIFF structure comes from `craft-tiff`. One module per format; they share `Metadata`, `DateTime`, `Gps` and `Orientation`, which is why they are one crate. | Function | Purpose | |---|---| | `read_exif(&[u8]) -> Metadata` | A TIFF-structured Exif block: a whole TIFF/DNG/raw file, or a JPEG APP1 payload with or without `Exif\0\0`. `try_read_exif` reports header errors, `try_read_exif_with` takes `craft_tiff::ParseOptions`. | | `from_tiff(&Tiff) -> Metadata` | The same from an already parsed stream. | | `write_exif(&Metadata) -> Vec` | An Exif block for an exported file. | | `tags::tag_rows`, `tags::file_tag_rows` | Every tag with its name and value as text, maker notes included. | | `focus::read`, `focus::read_header` | Where the camera focused, from the maker note (Sony), on the upright photo. `read_header` reads only the first kilobytes of a file through a callback. | | `recipe_from_exif`, `recipe_from_bytes`, `read_recipe_file` | The Fujifilm in-camera recipe (film simulation, grain, colour chrome, tone, white balance shift and the rest) from the maker note of an Exif block, a whole TIFF, JPEG or RAF, or a file read only as far as its metadata. | | `parse_iptc(&[u8]) -> Metadata` | IIM record 2: title (2:5), keywords (2:25), by-line (2:80), copyright (2:116), caption (2:120), date and time created (2:55, 2:60). | | `parse_xmp(&str) -> Result` | `Metadata`, every property (`prefix:name`, structs flattened as `a:b/c:d`) and the structured values. `parse_xmp_with` also canonicalises the caller's own namespaces. | | `write_xmp(&Metadata) -> String` | A complete `` packet. `write_xmp_with` sets the toolkit name and adds simple properties in the caller's namespace. | | `merge_xmp(existing, fresh, MergeRules)` | Updates a packet another program wrote: owned properties are replaced, everything else stays byte for byte. | | `parse_gpx(&str) -> Result` | GPX track points; `Tracklog::locate` gives the position at a capture time. | | `embedded(&[u8]) -> Embedded` | Sniffs JPEG, PNG, WebP, TIFF-based files, Fujifilm RAF, Canon CR3 and JPEG XL containers and returns the Exif, XMP (and extended XMP), ICC and IPTC blocks. `jpeg_segments`, `png_chunks`, `webp_chunks` are the walkers. | | `cr3::parse_cr3` | The Canon CR3 container: `CMT1`…`CMT4`, the XMP `uuid` box, the thumbnail, the tracks. | | `jxl::metadata` | The `Exif` and `xml ` boxes of a JPEG XL container, plain or Brotli-wrapped in `brob`. | | `mpf::images`, `mpf::write` | The Multi-Picture Format index (CIPA DC-007) of a JPEG: the images stored after the primary one (a gain map, a preview), and writing an index plus appending the images. `mpf::markers` lists the marker segments before the first scan. | | `extract(&[u8]) -> Metadata` | All of it for a whole file. Precedence: EXIF, then IPTC fills gaps, then XMP overrides the user fields (rating, label, title, caption, artist, copyright, keywords, GPS) and fills the rest. `from_embedded` does the same for blocks already located. | HEIF, PSD and MRW files are read by `craft-heif`, `craft-psd` and `craft-raw`. Put the blocks they return into an `Embedded` and call `from_embedded`. ## Capture time EXIF candidates are tried in order: DateTimeOriginal, DateTimeDigitized, then IFD0 DateTime. A clock with more than three colon-separated components is rejected as a whole, so the next candidate or the XMP supplies the time. ISO 8601 times in XMP need a complete clock. Calendar checks go through `chrono`. Invalid GPX point times count as untimed. ## XMP namespaces Written: `dc`, `xmp`, `xmpRights`, `photoshop`, `exif`, `exifEX`, `tiff`, `aux`, `lr`, `Iptc4xmpCore`, and the one namespace the caller passes. Read as well: `crs`, `mwg-rs` regions (`stArea`, `stDim`). Reading canonicalises prefixes by namespace URI and handles attribute and element forms, `rdf:Seq`/`Bag`/`Alt` (x-default first), `rdf:parseType="Resource"` and nested-description structs, predefined and numeric entities. A foreign namespace that uses a recognised prefix cannot supply recognised properties. Its keys are `Q{hex-encoded namespace URI}local`. Merging keeps the original foreign XML. ## Limits XMP: 64 levels, 200 000 nodes, 1 MiB of flattened keys and 8 MiB of opaque names per packet (`XmpError::TooComplex`). GPX: 64 levels, 5 000 000 points. MPF: 16 index entries. CR3 and JPEG XL: 4096 boxes; a `brob` box inflates to at most 32 MiB. JPEG ICC is returned only when its APP2 chunks form a complete, consistent, uniquely numbered sequence. TIFF limits are `craft-tiff`'s. No input panics: proptests feed random bytes to every container walker and random text to the XMP parser, and a sample JPEG is cut at every byte. ## Sources CIPA DC-008 (Exif 2.32), TIFF 6.0, Adobe DNG 1.7 (tag numbers), ISO 16684-1 and the Adobe XMP Specification Parts 1 to 3, IPTC-IIM 4.2 and the IPTC Photo Metadata Standard, the MWG Region Guidelines, ITU-T T.81 (JPEG markers), ICC.1 (APP2 embedding), the PNG Specification 3rd ed. (`eXIf`, `iTXt`), the WebP RIFF container specification, ISO/IEC 14496-12 (box structure), GPX 1.1. XML parsing: `quick-xml` (MIT). No third-party metadata code was consulted.