OpenMeta is a metadata processing library for image files.
The current focus is safe, format-agnostic reads: find metadata blocks in common containers, decode them into a normalized in-memory model, and expose stable transfer/edit building blocks for export workflows.
Read-path coverage is broad and regression-gated. Write/edit support is real for the main transfer targets, but parts of that API surface are still draft and may change as the transfer contract stabilizes.
Current planning estimate:
| Milestone | Status |
|---|---|
| Read parity on tracked still-image corpora | About 99-100% |
| Transfer / export milestone | About 80-85% |
| Overall product milestone | About 97-98% |
Current baseline-gated snapshot on tracked corpora:
- HEIC/HEIF, CR3, and mixed RAW EXIF tag-id compare gates are passing.
- EXR header metadata compare is passing for name/type/value-class checks.
- Portable and lossless sidecar export paths are covered by baseline and smoke gates.
- MakerNote decode is baseline-gated with broad vendor support; unknown tags are preserved losslessly when no structured mapping exists.
- Scan containers to locate metadata blocks in
jpeg,png,webp,gif,tiff/dng,crw/ciff,raf,x3f,jp2,jxl, andheif/avif/cr3(ISO-BMFF). - Reassemble chunked payloads and optionally decompress supported carriers.
- Decode metadata into a normalized
MetaStore. - Export sidecars and previews.
- Prepare, compile, emit, and edit metadata transfers for bounded target families.
OpenMeta currently covers these major families:
- EXIF, including pointer IFDs and broad MakerNote support.
- Legacy Canon CRW/CIFF bridge with bounded native CIFF naming and projection.
- XMP as RDF/XML properties.
- ICC profile header and tag table.
- Photoshop IRB, with raw preservation plus a bounded interpreted subset.
- IPTC-IIM datasets.
- JPEG comments, GIF comments, and PNG text chunks.
- ISO-BMFF derived fields for primary-item, relation, and auxiliary semantics.
- JUMBF / C2PA draft structural and semantic projection.
- EXR header attributes.
For the detailed support matrix, see docs/metadata_support.md.
EXIF and MakerNote display names have two layers:
- Canonical names from
exif_tag_name(...) - ExifTool-compatible display names from
exif_entry_name(..., ExifTagNamePolicy::ExifToolCompat)
That split lets OpenMeta keep stable internal naming while still matching common external tooling where compatibility matters.
OpenMeta now has a real transfer core built around:
prepare_metadata_for_target(...)compile_prepared_transfer_execution(...)execute_prepared_transfer(...)execute_prepared_transfer_file(...)
Current target status:
| Target | Status |
|---|---|
| JPEG | First-class |
| TIFF | First-class |
| PNG | Bounded but real |
| WebP | Bounded but real |
| JP2 | Bounded but real |
| JXL | Bounded but real |
| HEIF / AVIF / CR3 | Bounded but real |
| EXR | Bounded but real |
In practice:
- JPEG and TIFF are the strongest transfer targets today.
- TIFF edit support now covers classic TIFF, BigTIFF, bounded preview-page
chain rewrite (
ifd1,ifd2, and preserved downstream tails), and bounded TIFF/DNG-style SubIFD rewrite with preserved downstream auxiliary tails and preserved trailing existing children when only the front subset is replaced. ReplacedExifIFDblocks can also preserve an existing targetInteropIFDwhen the source does not supply its own interop child. - For DNG-like TIFF sources, the current bounded merge policy is: replace the source-supplied front preview-page/aux front structures, preserve existing target page tails and trailing auxiliary children.
- PNG, WebP, JP2, JXL, bounded BMFF, and EXR all have real first-class transfer entry points.
- EXR is still narrower than the container-edit targets: it emits safe string
header attributes through the transfer core, can materialize a prepared
ExrAdapterBatchfor host exporters, and Python can inspect that prepared EXR attribute batch through the directbuild_exr_attribute_batch_from_filebinding or the helper-layeropenmeta.python.get_exr_attribute_batch(...), but OpenMeta does not rewrite full EXR files yet. - Writer-side sync behavior is now partially explicit instead of implicit: generated XMP can independently keep or suppress EXIF-derived and IPTC-derived projection during transfer preparation.
- Generated portable XMP also has an explicit conflict policy for existing
decoded XMP versus generated EXIF/IPTC mappings:
current behavior,
existing_wins, orgenerated_wins. - Transfer preparation can also fold an existing sibling
.xmpsidecar from the destination path into generated portable XMP when that bounded mode is requested, with explicitsidecar_winsorsource_winsprecedence against source-embedded existing XMP. - Transfer preparation and file-helper execution can also fold existing
embedded XMP from the destination file into generated portable XMP when
that bounded mode is requested, with explicit
destination_winsorsource_winsprecedence against source-embedded existing XMP. - File-helper execution,
metatransfer, and the Python transfer wrapper now share a bounded XMP carrier choice: embedded XMP only, sidecar-only writeback to a sibling.xmp, or dual embedded-plus-sidecar writeback when a generated XMP packet exists for the prepared transfer. metatransferand the Python transfer wrapper also expose the bounded destination-embedded merge controls directly instead of hiding them behind lower-level bindings.- Sidecar-only writeback also has an explicit destination embedded-XMP policy:
preserve existing embedded XMP by default, or strip it for
jpeg,tiff,png,webp,jp2, andjxl. - Embedded-only writeback can also strip an existing sibling
.xmpdestination sidecar explicitly, so exports can move back to embedded-only XMP without leaving stale sidecar state behind. - C++ hosts now also have a bounded persistence helper for file-helper results, so edited output bytes, generated sidecars, and stale-sidecar cleanup can be applied without copying wrapper logic.
- Python hosts also have matching
transfer_file(...)andunsafe_transfer_file(...)bindings, and the public Python transfer wrapper now uses that same core-backed persistence path for real writes. - Prepared bundles record resolved policy decisions for MakerNote, JUMBF, C2PA, EXIF-to-XMP projection, and IPTC-to-XMP projection.
- This is still not a full MWG-style sync engine. OpenMeta does not yet try to solve all EXIF/IPTC/XMP conflict resolution or canonical writeback policy.
For transfer details, see docs/metadata_transfer_plan.md.
OpenMeta ships a small set of CLI tools:
| Tool | Purpose |
|---|---|
metaread |
Human-readable metadata dump |
metavalidate |
Metadata validation and issue reporting |
metadump |
Sidecar and preview dump tool |
metatransfer |
Transfer/edit smoke tool over the core transfer APIs |
thumdump |
Preview extractor |
The Python bindings expose the same read and transfer core through thin wrapper
helpers in src/python/.
src/include/openmeta/: public headerssrc/openmeta/: library implementationsrc/tools/: CLI toolssrc/python/: Python bindings and helper scriptstests/: unit tests and fuzz targetsdocs/: design notes and developer documentation
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build buildUseful options:
-DOPENMETA_BUILD_TOOLS=ON|OFF-DOPENMETA_BUILD_TESTS=ONfor GoogleTest-based unit tests-DOPENMETA_BUILD_FUZZERS=ONfor Clang + libFuzzer targets-DOPENMETA_USE_LIBCXX=ONwhen linking against dependencies built withlibc++-DOPENMETA_BUILD_DOCS=ONfor Doxygen HTML docs-DOPENMETA_BUILD_SPHINX_DOCS=ONfor Sphinx + Breathe HTML docs
Developer notes live in docs/development.md.
simple_meta_read(...) performs scan_auto(...), payload extraction, and
decode in one call.
- Input: whole-file bytes
- Output:
MetaStoreplus discoveredContainerBlockRef[] - Scratch: caller-provided block list, IFD list, payload buffer, and part-index buffer
- https://ssh4net.github.io/OpenMeta/: published documentation site
- docs/metadata_support.md: metadata support matrix
- docs/metadata_transfer_plan.md: transfer status and roadmap
- docs/doxygen.md: API reference
- SECURITY.md: security model and reporting
- NOTICE.md: notices and third-party dependency information