This directory contains the exif-oxide project, which ports ExifTool's metadata extraction functionality to Rust using a hybrid approach of code generation and manual implementations.
While ExifTool is the gold standard for metadata extraction, the Perl runtime that it relies on can grind against Windows and macOS security systems.
By translating to rust, applications using exif-oxide can be a single, "normal"
binary application that can "play nicely" with Defender and Gatekeeper, as well
as providing substantive performance gains over an approach that fork
s
exiftool
as a child process.
- Leverage ExifTool's invaluable camera-specific quirks and edge cases, and not introduce any novel parsing or heuristics--ExifTool has figured everything out already.
- Create a Rust library with behavioral compatibility with ExifTool for reading metadata
- Use code generation for static tag definitions while manually implementing complex logic
- Maintain the ability to track ExifTool's monthly updates through regeneration
- Provide streaming I/O to handle large files efficiently without loading them into memory
- Support mainstream metadata tags (>80% frequency) for the most common formats and manufacturers
- We will not provide 100% tag compatibility for reading nor writing. We focus on mainstream tags (frequency >80% in TagMetadata.json), translating approximately 500-1000 tags out of ExifTool's 15,000+.
- We will not port over the
geolocation
functionality - We will not port over ExifTool's custom tag definitions and user-defined tags
- No filesystem recursion: we will not support batch-update operations via the CLI
- We will not support tag value updates that are not static values (no pattern-match replacements)
- We will not provide verbatim exiftool logging results
- We will not seek to reproduce exiftool warnings or error verbiage verbatim
- We aren't doing any date or time parsing.
See ARCHITECTURE.md for architectural details.
For engineers starting on the project, see ENGINEER-GUIDE.md for practical guidance on understanding ExifTool and implementing features.
-j
(JSON output) is default behavior by the tool. There is no non-JSON output.
This program and code is offered under a commercial and under the GNU Affero General Public License. See LICENSE for details.
This project includes a branch of exiftool
as a submodule: use git clone --recursive
. This branch has a number of documents to bolster comprehension of
ExifTool, which has a ton of surprising bits going on.
The production code paths are mostly produced via automatic code generation that reads from the ExifTool source directly.
- Clone with submodules:
git clone --recursive https://github.com/yourusername/exif-oxide
- Read ENGINEER-GUIDE.md to understand ExifTool concepts
$ exif-oxide --help
Extracts EXIF data from image files
Usage: exif-oxide [OPTIONS] [ARGS]...
Arguments:
[ARGS]... All remaining arguments (files and -TagName patterns)
Options:
-G, --groups Include group names in output
-n, --numeric Show numeric/raw values (no PrintConv)
-b, --binary Extract raw binary data (requires single tag and single file)
--api API mode: show both raw and formatted values with full type information
-h, --help Show help information
EXAMPLES:
exif-oxide photo.jpg # All tags as JSON
exif-oxide photo1.jpg photo2.jpg # Multiple files
exif-oxide -G photo.jpg # All tags with groups
exif-oxide -Make -Model photo.jpg # Only return Make and Model tag values
exif-oxide -b -ThumbnailImage photo.jpg > thumb.jpg # Save thumbnail
use exif_oxide::ExifReader;
use std::fs::File;
let mut reader = ExifReader::new();
let file = File::open("photo.jpg")?;
// Extract all mainstream metadata
let metadata = reader.read_metadata_stream(file, Default::default())?;
println!("Camera: {} {}", metadata.tags["Make"], metadata.tags["Model"]);
// Stream large binary data (thumbnails) without loading into memory
if let Some(thumbnail_ref) = metadata.get_binary_ref("ThumbnailImage") {
let file = File::open("photo.jpg")?;
let mut thumbnail_reader = reader.stream_binary_tag(file, thumbnail_ref)?;
let mut thumbnail_file = File::create("thumbnail.jpg")?;
std::io::copy(&mut thumbnail_reader, &mut thumbnail_file)?;
}
- Hybrid Approach: Generate static tag tables at compile time, manually implement complex logic
- Minimal Perl: Use Perl only to extract tag definitions to JSON, then process with Rust
- Implementation Library: Manual Rust implementations indexed by Perl snippet signatures
- Always Compilable: Codegen produces working code even with missing implementations
- Incremental Scope: Start with basic JPEG/EXIF, then one manufacturer at a time
- Streaming First: Handle large files efficiently without requiring full file loads
ExifTool tag names are preserved when possible, but tags with invalid Rust identifier characters are modified:
- ExifTool:
"NikonActiveD-Lighting"
- exif-oxide:
"NikonActiveD_Lighting"
Hyphens (and any other invalid characters) are converted to underscores to ensure that they are valid Rust identifiers, while mostly preserving semantic meaning.
- Phil Harvey for creating and maintaining ExifTool for 25 years