exif-rs
A pure-Rust library that parses Exif metadata directly out of JPEG, TIFF, PNG, WebP, and HEIF/HEIC image files.
Repository Health
Technical Analysis
kamadak-exif (published on crates.io as kamadak-exif, imported as exif) is a pure-Rust library for reading Exif metadata. Rather than requiring callers to extract the raw Exif block themselves, its Reader::read_from_container entry point accepts a full image file and handles container detection and extraction internally across five formats: TIFF and TIFF-derived RAW formats, JPEG, HEIF/HEIC/AVIF, PNG, and WebP.
Parsed fields come back as a typed Value enum (Byte, Ascii, Short, Long, Rational, Undefined, and their signed/float variants) rather than opaque bytes, and each field can be rendered through display_value() with unit-aware formatting (e.g. GPS altitude as “0.5 meters below sea level”). An experimental Writer module supports re-encoding Exif data. The crate is dependency-light (a single small crate, mutate_once), has no unsafe blocks in its public surface, and targets Rust 1.60+.
What You Get
- A
Readertype that auto-detects and extracts Exif data from TIFF, JPEG, HEIF/HEIC/AVIF, PNG, and WebP containers viaread_from_container - A typed
Valueenum for field values (Byte, Ascii, Short, Long, Rational, SRational, Undefined, Float, Double) instead of raw bytes - Unit-aware
display_value()formatting that renders values like GPS altitude or resolution with their correct units - A
continue_on_errormode that returns a best-effort partial result plus the list of ignored errors instead of failing hard on malformed data - An experimental
Writerfor encodingFieldvalues back into Exif/TIFF byte streams - A documented
Tagenum covering the full standard Exif/TIFF/GPS tag set, addressable by(Context, u16)for vendor-specific tags
Common Use Cases
- Reading camera make/model, exposure settings, and capture timestamp from uploaded photos in an image-processing pipeline
- Extracting GPS coordinates embedded in JPEG/HEIC photos for geotagging features
- Reading the Orientation tag to auto-rotate images before display or thumbnailing
- Auditing or stripping Exif metadata from images before publishing them for privacy
- Building command-line or GUI Exif viewers/dumpers (the crate ships a
dumpexifexample for exactly this)
Under The Hood
Architecture
The crate is organized around one shared TIFF/Exif decoder fed by several format-specific extraction modules. jpeg.rs, png.rs, webp.rs, and isobmff.rs (HEIF/HEIC/AVIF) each implement a container sniff function (is_jpeg, is_tiff, etc.) and a get_exif_attr extractor that locates and pulls the embedded Exif block out of that container’s own structure. reader.rs’s Reader::read_from_container is the orchestration layer: it reads the first 4KB of the stream, sniffs the format, dispatches to the matching extractor, and hands the resulting raw bytes to the shared tiff::Parser, which walks the TIFF IFD structure into a flat Vec<IfdEntry>. exifimpl.rs then wraps that entry list plus a HashMap<(In, Tag), usize> index into the public Exif struct, giving O(1) get_field lookups by tag and IFD number. This keeps format-specific complexity isolated at the edges while the core Exif/TIFF parsing logic stays single-sourced.
Tech Stack
Pure Rust, edition 2021, targeting Rust 1.60+, with effectively one external dependency (mutate_once 0.1.1, used for deferred field initialization inside the parser). The public Reader API is generic over any R: io::BufRead + io::Seek, so it works uniformly over File, in-memory buffers, or other seekable streams without committing to a specific I/O backend. There is no async runtime, no unsafe code in the crate’s own implementation, and the crate ships two examples/ binaries (dumpexif, reading) alongside the library for direct reference.
Code Quality
Testing is extensive and inline: nearly every source module (value.rs, tiff.rs, endian.rs, writer.rs, webp.rs, isobmff.rs, etc.) carries its own #[cfg(test)] mod tests block, and the top-level tests/ directory adds integration coverage against real fixture images (JPEG, TIFF, PNG, WebP, HEIC) plus round-trip comparison tests. The public Error enum is marked #[non_exhaustive] and implements std::error::Error, favoring forward-compatible, explicit error handling over panics. Rustdoc examples embedded in lib.rs, reader.rs, and exifimpl.rs are executable doctests exercised by cargo test. One gap: the shallow clone contains no .github/workflows or other CI configuration, so automated testing on push isn’t visibly enforced in-repo.
API Design
The public surface is small and consistently shaped: Reader::new() builds a reader, read_from_container/read_raw produce an Exif, and Exif::get_field/fields() retrieve typed Field values — a three-step path from bytes to typed data with no configuration required for the common case. The continue_on_error builder option is opt-in and documented with a worked example showing how to recover a PartialResult. Field values are exposed through a single Value enum rather than per-type getters, which keeps the type surface small at the cost of requiring callers to match on the variant (mitigated by helpers like get_uint for the common case of “any unsigned integer representation”). Documentation is dense and example-driven throughout, with a dedicated doc::upgrade module tracking API-incompatible changes across versions.