exif-rs

A pure-Rust library that parses Exif metadata directly out of JPEG, TIFF, PNG, WebP, and HEIF/HEIC image files.

Library
Cargo
v0.6.1
256stars
BSD-2-Clause

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
37/100Needs Attention
Development Activity0
Maintenance0
Community68
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
72/100Good
Architecture75
Code Quality78
Innovation55
Learning Curve80

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 Reader type that auto-detects and extracts Exif data from TIFF, JPEG, HEIF/HEIC/AVIF, PNG, and WebP containers via read_from_container
  • A typed Value enum 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_error mode that returns a best-effort partial result plus the list of ignored errors instead of failing hard on malformed data
  • An experimental Writer for encoding Field values back into Exif/TIFF byte streams
  • A documented Tag enum 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 dumpexif example 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.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search