Skip to content

Commit f4ce348

Browse files
committed
WebP EXIF as TIFF; reject foreign BMFF meta
Convert prepared EXIF chunk payloads to the raw TIFF byte stream (omit JPEG APP1 Exif\0\0 preamble) and emit WebP EXIF chunks using that TIFF payload. Add adjust_exif_time_patch_slot_for_target to map time-patch offsets for PNG/WebP/JXL/JP2/BMFF targets and rename the PNG helper to build_tiff_exif_payload_from_app1. Implement detection of foreign top-level BMFF meta boxes and fail/decline bounded BMFF edits for such files (reject until OpenMeta can safely merge foreign item/property graphs). Update documentation to reflect the WebP EXIF payload shape and the constrained OpenMeta-managed BMFF edit contract. Add/adjust tests and CMake test helpers (WebP VP8X wrapper, payload checks, and a test asserting rejection of foreign BMFF meta boxes).
1 parent 5aaf1c8 commit f4ce348

9 files changed

Lines changed: 321 additions & 70 deletions

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,9 @@ In practice:
142142
terms are not part of the public CI dependency story; SDK-backed coverage is
143143
treated as maintainer or release validation.
144144
- PNG, WebP, JP2, JXL, bounded BMFF, and EXR all have real first-class
145-
transfer entry points.
145+
transfer entry points. BMFF file edits are currently limited to
146+
OpenMeta-managed metadata-only `meta` boxes; targets with foreign top-level
147+
`meta` boxes are rejected instead of risking an unusable HEIF/AVIF result.
146148
- EXR is still narrower than the container-edit targets: it emits safe string
147149
header attributes through the transfer core, can materialize a prepared
148150
`ExrAdapterBatch` for host exporters, and Python can inspect that prepared

docs/development.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ Current v1 behavior is:
270270
- `prepare_metadata_for_target(..., TransferTargetFormat::Webp, ...)`
271271
currently builds `EXIF`, `XMP `, `ICCP`, and bounded `C2PA` RIFF
272272
metadata chunks from `MetaStore`
273+
- WebP `EXIF` chunk payloads contain direct TIFF bytes and intentionally
274+
omit the JPEG APP1 `Exif\0\0` preamble
273275
- `compile_prepared_bundle_webp(...)` and
274276
`emit_prepared_bundle_webp_compiled(...)` provide the same
275277
`prepare once -> compile once -> emit many` shape as JPEG/TIFF/JXL
@@ -285,8 +287,7 @@ Current v1 behavior is:
285287
bytes from prepared bundles, and
286288
`build_prepared_transfer_package_batch(...)` can materialize those bytes
287289
into one owned replay batch
288-
- full WebP file rewrite/edit and signed C2PA rewrite remain follow-up
289-
work
290+
- full WebP signed C2PA rewrite remains follow-up work
290291
- ISO-BMFF metadata-item transfer now uses the same bounded contract for
291292
`HEIF` / `AVIF` / `CR3` targets:
292293
- `prepare_metadata_for_target(..., TransferTargetFormat::{Heif,Avif,Cr3}, ...)`
@@ -311,14 +312,16 @@ Current v1 behavior is:
311312
the reusable item/property-emitter path
312313
- the shared package-batch persistence/replay layer can own and hand off
313314
those stable BMFF item and property payload bytes
314-
- OpenMeta also supports a bounded append-style BMFF edit path:
315-
it preserves existing top-level BMFF boxes, strips prior
316-
OpenMeta-authored metadata-only `meta` boxes, and appends one new
317-
OpenMeta-authored metadata-only `meta` box carrying the prepared BMFF
318-
items/properties
315+
- OpenMeta also supports a bounded BMFF edit path for targets without a
316+
foreign top-level `meta` box, or with a prior OpenMeta-authored
317+
metadata-only `meta` box from the same bounded contract. It preserves
318+
non-`meta` top-level BMFF boxes and replaces the OpenMeta-authored
319+
metadata-only `meta` box with the prepared BMFF items/properties.
320+
Targets with foreign top-level `meta` boxes are rejected until OpenMeta
321+
can merge real BMFF item/property graphs safely.
319322
- CLI/Python `metatransfer` wrappers expose both BMFF summaries and this
320323
bounded edit path; `--target-heif`, `--target-avif`, and `--target-cr3`
321-
now accept `--source-meta <path>` plus `--output <path>` for metadata
324+
now accept `--source-meta PATH` plus `--output PATH` for metadata
322325
transfer onto an existing BMFF target file
323326
- the same bounded BMFF edit contract now also participates in the core /
324327
file-helper C2PA signer path:

docs/metadata_backend_matrix.md

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ For the public per-target preserve/replace guarantees, see
9393
- `XMP `
9494
- `ICCP`
9595
- bounded `C2PA`
96+
- The prepared `EXIF` chunk payload is the TIFF byte stream only. It does not
97+
include the JPEG APP1 `Exif\0\0` preamble.
9698
- OpenMeta now has a bounded WebP transfer path on the core transfer API:
9799
`prepare_metadata_for_target(..., TransferTargetFormat::Webp, ...)`,
98100
`compile_prepared_bundle_webp(...)`,
@@ -105,14 +107,16 @@ For the public per-target preserve/replace guarantees, see
105107
`write_prepared_transfer_package(...)` can serialize direct WebP chunk bytes
106108
from prepared bundles, and the owned package batch/replay path can persist
107109
or hand off those bytes without keeping the source bundle alive.
108-
- Full WebP file rewrite/edit and signed C2PA rewrite/re-sign are still
109-
outside the current WebP transfer contract.
110+
- The file edit path rewrites managed metadata chunks and patches existing
111+
`VP8X` feature bits. It does not synthesize a missing `VP8X` chunk.
112+
- Full WebP signed C2PA rewrite/re-sign is still outside the current WebP
113+
transfer contract.
110114

111115
### ISO-BMFF metadata items (HEIF / AVIF / CR3)
112116

113-
- This bounded transfer path is metadata-item/property oriented. OpenMeta now
114-
also supports a bounded append-style BMFF metadata edit path over an
115-
existing BMFF target file.
117+
- This bounded transfer path is metadata-item/property oriented. OpenMeta also
118+
supports a bounded OpenMeta-managed BMFF metadata edit path over an existing
119+
BMFF target file.
116120
- Current prepared item routes:
117121
- `bmff:item-exif`
118122
- `bmff:item-xmp`
@@ -143,21 +147,23 @@ For the public per-target preserve/replace guarantees, see
143147
- `metatransfer` / `openmeta.transfer_probe(...)` expose BMFF summaries,
144148
including `bmff_property colr/prof ...`.
145149
- `metatransfer --target-heif|--target-avif|--target-cr3 --source-meta ... -o ...`
146-
now performs bounded metadata-only edit by appending one OpenMeta-authored
147-
metadata-only top-level `meta` box and replacing any prior OpenMeta-authored
148-
metadata-only `meta` box from the same bounded contract.
150+
performs bounded metadata-only edit only for targets with no foreign
151+
top-level `meta` box, or with a prior OpenMeta-authored metadata-only `meta`
152+
box from the same bounded contract. Foreign top-level `meta` boxes are
153+
rejected until OpenMeta can merge real BMFF item/property graphs safely.
149154
- Embedded-XMP strip mode removes only OpenMeta-authored metadata `meta` boxes.
150-
Foreign BMFF XMP item graphs are preserved; recognized foreign XMP `mime`
151-
items, including `iinf` version 0/1/2 tables, make strip mode fail
152-
explicitly instead of silently claiming removal.
155+
Foreign BMFF metadata graphs are not modified; recognized foreign XMP `mime`
156+
items, including `iinf` version 0/1/2 tables, make strip mode fail explicitly
157+
instead of silently claiming removal.
153158
- The same bounded BMFF edit contract now also participates in the core /
154159
file-helper C2PA signer path:
155160
- sign-request derivation
156161
- binding-byte materialization
157162
- signed-payload validation
158-
- staged `bmff:item-c2pa` apply before bounded metadata-only edit
163+
- staged bmff:item-c2pa apply before bounded metadata-only edit
159164
- Out of scope for the current BMFF contract:
160165
- thin CLI/Python signer-input exposure for BMFF
166+
- merging or rewriting foreign top-level BMFF meta item/property graphs
161167
- full BMFF signed rewrite/re-sign beyond the bounded metadata-only edit path
162168

163169
### EXR (OpenEXR)

docs/metadata_transfer_plan.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ The first public write-side sync controls are also in place:
5555
| WebP | Bounded but real | Prepared bundle, compiled emit, bounded chunk rewrite/edit, file-helper roundtrip | Not a general WebP chunk editor |
5656
| JP2 | Bounded but real | Prepared bundle, compiled emit, bounded box rewrite/edit, file-helper roundtrip | `jp2h` synthesis is still out of scope |
5757
| JXL | Bounded but real | Prepared bundle, compiled emit, bounded box rewrite/edit, file-helper roundtrip | Still narrower than JPEG/TIFF |
58-
| HEIF / AVIF / CR3 | Bounded but real | Prepared bundle, compiled emit, bounded BMFF item/property edit, file-helper roundtrip | Not broad BMFF writer parity |
58+
| HEIF / AVIF / CR3 | Bounded but real | Prepared bundle, compiled emit, bounded OpenMeta-managed BMFF item/property edit, file-helper roundtrip | Not broad BMFF writer parity; foreign top-level `meta` graphs are rejected until merge support exists |
5959
| EXR | Bounded but real | Prepared bundle, compiled emit, direct backend attribute emit, prepared-bundle to `ExrAdapterBatch` bridge, CLI/Python transfer surface | No file rewrite/edit path yet; current transfer payload is safe string attributes only |
6060

6161
## What Is Already Implemented
@@ -104,16 +104,17 @@ OpenMeta now has explicit end-to-end read-backed transfer tests for:
104104
- source JPEG -> WebP edit/apply -> read-back
105105
- source JPEG -> JP2 edit/apply -> read-back
106106
- source JPEG -> JXL edit/apply -> read-back
107-
- source JPEG -> HEIF edit/apply -> read-back
108-
- source JPEG -> AVIF edit/apply -> read-back
109-
- source JPEG -> CR3 edit/apply -> read-back
107+
- source JPEG -> OpenMeta-managed HEIF edit/apply -> read-back
108+
- source JPEG -> OpenMeta-managed AVIF edit/apply -> read-back
109+
- source JPEG -> OpenMeta-managed CR3 edit/apply -> read-back
110110

111111
That does not make all targets equally mature, but it does mean the transfer
112112
core has real roundtrip regression gates across the primary supported export
113113
families.
114114

115115
The primary writer family is also covered by a deterministic compatibility-dump
116-
gate for JPEG, TIFF, DNG, PNG, WebP, JP2, JXL, HEIF, AVIF, and CR3. That gate
116+
gate for JPEG, TIFF, DNG, PNG, WebP, JP2, JXL, and OpenMeta-managed HEIF,
117+
AVIF, and CR3. That gate
117118
checks the prepared EXIF/XMP routes, edit/apply status, dual-write XMP
118119
writeback summary, and decoded metadata dump for the managed source fields.
119120
Additional compatibility-dump gates cover sidecar-only writeback with explicit
@@ -256,6 +257,8 @@ Implemented as a bounded RIFF metadata target:
256257
- `ICCP`
257258
- bounded `C2PA`
258259
- bounded rewrite/edit for managed metadata chunks
260+
- `EXIF` chunk payloads use direct TIFF bytes, without the JPEG APP1
261+
`Exif\0\0` preamble
259262

260263
### JP2
261264

@@ -284,9 +287,11 @@ Implemented as a bounded BMFF target family:
284287
- bounded `bmff:item-jumb`
285288
- bounded `bmff:item-c2pa`
286289
- bounded `bmff:property-colr-icc`
287-
- bounded metadata-only `meta` rewrite path
290+
- bounded OpenMeta-managed metadata-only `meta` rewrite path
288291
- explicit strip-mode rejection for recognized foreign XMP item graphs,
289292
including `iinf` version 0/1/2 `mime` entries
293+
- fail-safe rejection for foreign top-level `meta` boxes until real BMFF
294+
item/property merge support exists
290295

291296
### EXR
292297

@@ -782,7 +787,7 @@ embedded/sidecar writeback across the primary writer target family.
782787
#### 3. Compare-Backed Release Validation
783788

784789
- [x] promote the current primary-target roundtrip checks into explicit release-facing compare gates
785-
- [x] add compare-backed validation for `TIFF`, `DNG`, `PNG`, `WebP`, `JP2`, `JXL`, and bounded `BMFF` target outputs
790+
- [x] add compare-backed validation for `TIFF`, `DNG`, `PNG`, `WebP`, `JP2`, `JXL`, and OpenMeta-managed bounded `BMFF` target outputs, plus fail-safe rejection for foreign top-level `meta` BMFF targets
786791
- [x] cover embedded-only, sidecar-only, and dual-write XMP flows in release-facing compare validation
787792
- [x] add compare-backed validation for explicit sidecar-base overrides and destination-sidecar cleanup behavior
788793
- [x] gate the primary writer family on deterministic read-back of managed metadata after edit/apply

docs/sphinx/writer_target_contract.rst

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,10 @@ Target Summary
105105
* - ``HEIF`` / ``AVIF`` / ``CR3``
106106
- one OpenMeta-authored metadata-only top-level ``meta`` box with
107107
Exif/XMP/JUMBF/C2PA items and ``colr`` ICC property
108-
- Preserve all non-OpenMeta top-level boxes; replace prior
109-
OpenMeta-authored metadata ``meta``; append a new OpenMeta metadata
110-
``meta`` when needed.
111-
- Does not rewrite arbitrary existing BMFF scene/item graphs.
108+
- Preserve non-``meta`` top-level boxes; replace prior
109+
OpenMeta-authored metadata ``meta``; reject foreign top-level ``meta``
110+
boxes instead of appending a competing metadata graph.
111+
- Does not rewrite or merge arbitrary existing BMFF scene/item graphs.
112112
* - ``EXR``
113113
- safe string header attributes through the EXR transfer emitter or
114114
adapter batch
@@ -201,6 +201,9 @@ existing chunks from the same managed family. Unrelated chunks are preserved.
201201
When EXIF, XMP, or ICC is present after rewrite, OpenMeta patches the existing
202202
``VP8X`` feature flags to match the final metadata state.
203203

204+
Prepared WebP ``EXIF`` chunks carry the TIFF byte stream directly. They do
205+
not include the JPEG APP1 ``Exif\0\0`` preamble.
206+
204207
The current WebP edit contract requires an existing ``VP8X`` chunk for EXIF,
205208
XMP, or ICC metadata edits. It does not synthesize ``VP8X``.
206209

@@ -237,14 +240,20 @@ encoder-side handoff and is not serialized by the file edit path.
237240
HEIF / AVIF / CR3
238241
-----------------
239242

240-
The bounded BMFF edit path preserves all non-OpenMeta top-level boxes as
241-
source ranges.
243+
The bounded BMFF edit path preserves non-``meta`` top-level boxes as source
244+
ranges.
242245

243246
OpenMeta writes one metadata-only top-level ``meta`` box using the public
244247
bounded contract. That box can contain prepared Exif, XMP, JUMBF, C2PA, and
245248
ICC ``colr`` property payloads. A prior OpenMeta-authored metadata ``meta``
246-
box is removed and replaced by the newly prepared box. Foreign ``meta`` boxes
247-
and normal BMFF scene/item graphs are preserved.
249+
box is removed and replaced by the newly prepared box.
250+
251+
OpenMeta does not append a second top-level ``meta`` box when the target
252+
already has a foreign top-level ``meta`` box. Real HEIF/AVIF scene graphs
253+
commonly use that box for image items and properties; appending a competing
254+
metadata graph can make the result unusable. Until OpenMeta has a real BMFF
255+
merge path for foreign item/property graphs, that case is reported as
256+
unsupported and no edited output is written.
248257

249258
If sidecar-only writeback asks to strip embedded XMP, OpenMeta can remove XMP
250259
from its own OpenMeta-authored metadata ``meta`` box. It does not strip XMP

docs/writer_target_contract.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ integration checks.
5959
| `WebP` | `EXIF`, XMP RIFF chunk, `ICCP`, bounded `C2PA` | Replace matching managed RIFF chunks; preserve unrelated chunks; patch `VP8X` feature bits | EXIF/XMP/ICC edits require an existing `VP8X` chunk |
6060
| `JP2` | top-level `Exif`, top-level XMP `xml` box, `jp2h/colr` ICC | Replace matching top-level metadata boxes; rewrite `jp2h` only to replace/insert `colr`; preserve unrelated boxes and unrelated `jp2h` children | Does not synthesize `jp2h`; requires one existing `jp2h` for ICC |
6161
| `JXL` | top-level `Exif`, XMP `xml` box, `jumb`, `c2pa` | Replace matching top-level boxes; preserve signature and non-managed boxes; classify `jumb` as generic JUMBF or C2PA | ICC is encoder handoff only; file edit emits uncompressed prepared metadata boxes |
62-
| `HEIF` / `AVIF` / `CR3` | one OpenMeta-authored metadata-only top-level `meta` box with Exif/XMP/JUMBF/C2PA items and `colr` ICC property | Preserve all non-OpenMeta top-level boxes; replace prior OpenMeta-authored metadata `meta`; append a new OpenMeta metadata `meta` when needed | Does not rewrite arbitrary existing BMFF scene/item graphs |
62+
| `HEIF` / `AVIF` / `CR3` | one OpenMeta-authored metadata-only top-level `meta` box with Exif/XMP/JUMBF/C2PA items and `colr` ICC property | Preserve non-`meta` top-level boxes; replace prior OpenMeta-authored metadata `meta`; reject foreign top-level `meta` boxes instead of appending a competing metadata graph | Does not rewrite or merge arbitrary existing BMFF scene/item graphs |
6363
| `EXR` | safe string header attributes through the EXR transfer emitter or adapter batch | No file rewrite contract today; host applies prepared attributes through its own EXR writer | Attribute-emitter target, not a file edit path |
6464

6565
## JPEG
@@ -141,6 +141,9 @@ chunks from the same managed family. Unrelated chunks are preserved. When
141141
EXIF, XMP, or ICC is present after rewrite, OpenMeta patches the existing
142142
`VP8X` feature flags to match the final metadata state.
143143

144+
Prepared WebP `EXIF` chunks carry the TIFF byte stream directly. They do not
145+
include the JPEG APP1 `Exif\0\0` preamble.
146+
144147
The current WebP edit contract requires an existing `VP8X` chunk for EXIF,
145148
XMP, or ICC metadata edits. It does not synthesize `VP8X`.
146149

@@ -174,14 +177,20 @@ encoder-side handoff and is not serialized by the file edit path.
174177

175178
## HEIF / AVIF / CR3
176179

177-
The bounded BMFF edit path preserves all non-OpenMeta top-level boxes as
178-
source ranges.
180+
The bounded BMFF edit path preserves non-`meta` top-level boxes as source
181+
ranges.
179182

180183
OpenMeta writes one metadata-only top-level `meta` box using the public
181184
bounded contract. That box can contain prepared Exif, XMP, JUMBF, C2PA, and
182185
ICC `colr` property payloads. A prior OpenMeta-authored metadata `meta` box is
183-
removed and replaced by the newly prepared box. Foreign `meta` boxes and
184-
normal BMFF scene/item graphs are preserved.
186+
removed and replaced by the newly prepared box.
187+
188+
OpenMeta does not append a second top-level `meta` box when the target already
189+
has a foreign top-level `meta` box. Real HEIF/AVIF scene graphs commonly use
190+
that box for image items and properties; appending a competing metadata graph
191+
can make the result unusable. Until OpenMeta has a real BMFF merge path for
192+
foreign item/property graphs, that case is reported as unsupported and no
193+
edited output is written.
185194

186195
If sidecar-only writeback asks to strip embedded XMP, OpenMeta can remove XMP
187196
from its own OpenMeta-authored metadata `meta` box. It does not strip XMP from

0 commit comments

Comments
 (0)