Skip to content

Commit 5a1e605

Browse files
committed
Add bounded BMFF foreign-meta merge, docs, tests
Implement bounded BMFF foreign top-level `meta` merge/replace/strip and ICC property merge support. Adds new BMFF parsing/merge logic and rewrite policy in metadata_transfer.cc, updates Nikon MakerNote flash decoding, and extends tests to cover BMFF XMP/ICC transfer paths. Add CMake cache options (OPENMETA_FFMPEG_EXECUTABLE, OPENMETA_BMFF_HEIF/AVIF/CR3_TEST_TARGET) and wire them into the external image-usability and transfer release gates (with optional ffmpeg fallback). Update documentation (README, multiple docs/sphinx/md files) to describe the new bounded contract and test behavior, add CHANGES.md, and bump VERSION to 0.4.7.
1 parent f4ce348 commit 5a1e605

16 files changed

Lines changed: 4037 additions & 331 deletions

CHANGES.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# OpenMeta Changes
2+
3+
## 0.4.7 - 2026-04-27
4+
5+
Changes compared with `0.4.6`.
6+
7+
### Added
8+
9+
- Added bounded foreign top-level BMFF `meta` merge support for parseable
10+
HEIF/AVIF/CR3-style item graphs. OpenMeta can now merge prepared
11+
Exif/XMP/JUMBF/C2PA item metadata into an existing foreign `meta` graph
12+
instead of appending a second competing `meta` box.
13+
- Added BMFF XMP replacement and strip support for foreign item graphs that
14+
satisfy the bounded primary-item contract: a single parseable `iinf`,
15+
`iloc` version 0/1/2, `pitm`, and at most one `idat`.
16+
- Added bounded BMFF ICC property merge support for foreign `meta` graphs.
17+
Existing ICC `colr/prof` and `colr/rICC` properties are removed from
18+
`iprp/ipco`, existing `ipma` associations are compacted/remapped, and the
19+
transferred `colr/prof` property is associated with the primary item.
20+
- Added public CMake cache options for external BMFF usability checks:
21+
`OPENMETA_BMFF_HEIF_TEST_TARGET`, `OPENMETA_BMFF_AVIF_TEST_TARGET`,
22+
`OPENMETA_BMFF_CR3_TEST_TARGET`, and `OPENMETA_FFMPEG_EXECUTABLE`.
23+
- Added external image-usability gate coverage for BMFF ICC and XMP transfer
24+
on configured HEIF/AVIF/CR3 targets when local tools can validate them.
25+
26+
### Changed
27+
28+
- BMFF edit/apply now preserves non-`meta` top-level boxes while rebuilding
29+
supported foreign `meta` item/property graphs in place.
30+
- BMFF embedded-XMP strip mode no longer requires an OpenMeta-authored
31+
metadata `meta` box when the foreign graph is parseable and has a valid
32+
primary-item relationship.
33+
- BMFF summary output and gate checks now cover both `bmff_item mime/xmp` and
34+
`bmff_property colr/prof` transfer results.
35+
- Public transfer docs now describe the updated BMFF preserve/replace/strip
36+
contract and the configured-target validation limits.
37+
38+
### Fixed
39+
40+
- Fixed duplicate or stale BMFF ICC associations by replacing prior ICC
41+
properties and remapping `ipma` instead of adding competing property
42+
entries.
43+
- Fixed supported foreign BMFF XMP strip/replacement paths that previously
44+
failed even when the target graph had enough structure for a safe bounded
45+
rewrite.
46+
- Fixed Nikon MakerNote FlashInfo decoding for `0107`/`0108` and
47+
`0300`/`0301` layouts by emitting Flash Group A/B/C control-mode fields
48+
with ExifTool-compatible contextual names.
49+
50+
### Tests And Validation
51+
52+
- Added focused BMFF API tests for foreign Exif/XMP item replacement, XMP
53+
strip, ICC property replacement, existing `ipma` merge/remap, `iloc`
54+
rebasing, and fail-safe rejection of unsupported foreign graphs.
55+
- Extended Nikon MakerNote tests for Flash Group A/B/C control-mode decoding.
56+
- Extended the public transfer release and image-usability gates to accept
57+
configured BMFF target files and optional ffmpeg decode fallback.
58+
- Verified the public release with the unit test suite, transfer release gate,
59+
external image-usability gate, documentation build, and whitespace checks.

CMakeLists.txt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,26 @@ if(NOT OPENMETA_EXIFTOOL_EXECUTABLE)
8686
endif()
8787
endif()
8888

89+
set(OPENMETA_FFMPEG_EXECUTABLE "" CACHE FILEPATH "Optional ffmpeg executable for external image decode checks")
90+
if(NOT OPENMETA_FFMPEG_EXECUTABLE)
91+
find_program(_openmeta_ffmpeg_executable
92+
NAMES ffmpeg
93+
HINTS ${CMAKE_PREFIX_PATH}
94+
PATH_SUFFIXES bin)
95+
if(_openmeta_ffmpeg_executable)
96+
set(OPENMETA_FFMPEG_EXECUTABLE "${_openmeta_ffmpeg_executable}"
97+
CACHE FILEPATH "Optional ffmpeg executable for external image decode checks" FORCE)
98+
endif()
99+
endif()
100+
101+
set(OPENMETA_BMFF_HEIF_TEST_TARGET "" CACHE FILEPATH "Optional existing HEIF/HEIC target image for external transfer usability checks")
102+
set(OPENMETA_BMFF_AVIF_TEST_TARGET "" CACHE FILEPATH "Optional existing AVIF target image for external transfer usability checks")
103+
set(OPENMETA_BMFF_CR3_TEST_TARGET "" CACHE FILEPATH "Optional existing CR3 target image for external transfer usability checks")
104+
set(_openmeta_bmff_test_target_args
105+
"-DBMFF_HEIF_TEST_TARGET=${OPENMETA_BMFF_HEIF_TEST_TARGET}"
106+
"-DBMFF_AVIF_TEST_TARGET=${OPENMETA_BMFF_AVIF_TEST_TARGET}"
107+
"-DBMFF_CR3_TEST_TARGET=${OPENMETA_BMFF_CR3_TEST_TARGET}")
108+
89109
# Optional path to local dependency repos (used for the FuzzTest wrapper).
90110
# Expected layout:
91111
# <root>/fuzztest
@@ -431,7 +451,9 @@ if(OPENMETA_BUILD_TOOLS)
431451
"-DMETATRANSFER_BIN=$<TARGET_FILE:metatransfer>"
432452
"-DOIIOTOOL_BIN=${OPENMETA_OIIOTOOL_EXECUTABLE}"
433453
"-DEXIFTOOL_BIN=${OPENMETA_EXIFTOOL_EXECUTABLE}"
454+
"-DFFMPEG_BIN=${OPENMETA_FFMPEG_EXECUTABLE}"
434455
"-DWORK_DIR=${CMAKE_CURRENT_BINARY_DIR}/_metatransfer_image_usability"
456+
${_openmeta_bmff_test_target_args}
435457
-P "${CMAKE_CURRENT_SOURCE_DIR}/tests/metatransfer_image_usability_test.cmake"
436458
COMMENT "Running metatransfer external image usability gate"
437459
VERBATIM
@@ -699,6 +721,8 @@ if(OPENMETA_BUILD_TESTS)
699721
"-DOPENMETA_PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/python"
700722
"-DOIIOTOOL_BIN=${OPENMETA_OIIOTOOL_EXECUTABLE}"
701723
"-DEXIFTOOL_BIN=${OPENMETA_EXIFTOOL_EXECUTABLE}"
724+
"-DFFMPEG_BIN=${OPENMETA_FFMPEG_EXECUTABLE}"
725+
${_openmeta_bmff_test_target_args}
702726
-P "${CMAKE_CURRENT_SOURCE_DIR}/tests/transfer_release_gate.cmake"
703727
COMMENT "Running transfer release gate"
704728
VERBATIM
@@ -713,6 +737,8 @@ if(OPENMETA_BUILD_TESTS)
713737
"-DOPENMETA_PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/python"
714738
"-DOIIOTOOL_BIN=${OPENMETA_OIIOTOOL_EXECUTABLE}"
715739
"-DEXIFTOOL_BIN=${OPENMETA_EXIFTOOL_EXECUTABLE}"
740+
"-DFFMPEG_BIN=${OPENMETA_FFMPEG_EXECUTABLE}"
741+
${_openmeta_bmff_test_target_args}
716742
-P "${CMAKE_CURRENT_SOURCE_DIR}/tests/transfer_release_gate.cmake"
717743
)
718744
elseif(TARGET metatransfer)
@@ -724,6 +750,8 @@ if(OPENMETA_BUILD_TESTS)
724750
"-DWORK_DIR=${CMAKE_CURRENT_BINARY_DIR}/_transfer_release_gate"
725751
"-DOIIOTOOL_BIN=${OPENMETA_OIIOTOOL_EXECUTABLE}"
726752
"-DEXIFTOOL_BIN=${OPENMETA_EXIFTOOL_EXECUTABLE}"
753+
"-DFFMPEG_BIN=${OPENMETA_FFMPEG_EXECUTABLE}"
754+
${_openmeta_bmff_test_target_args}
727755
-P "${CMAKE_CURRENT_SOURCE_DIR}/tests/transfer_release_gate.cmake"
728756
COMMENT "Running transfer release gate"
729757
VERBATIM
@@ -736,6 +764,8 @@ if(OPENMETA_BUILD_TESTS)
736764
"-DWORK_DIR=${CMAKE_CURRENT_BINARY_DIR}/_transfer_release_gate"
737765
"-DOIIOTOOL_BIN=${OPENMETA_OIIOTOOL_EXECUTABLE}"
738766
"-DEXIFTOOL_BIN=${OPENMETA_EXIFTOOL_EXECUTABLE}"
767+
"-DFFMPEG_BIN=${OPENMETA_FFMPEG_EXECUTABLE}"
768+
${_openmeta_bmff_test_target_args}
739769
-P "${CMAKE_CURRENT_SOURCE_DIR}/tests/transfer_release_gate.cmake"
740770
)
741771
endif()
@@ -780,7 +810,9 @@ if(OPENMETA_BUILD_TESTS)
780810
"-DMETATRANSFER_BIN=$<TARGET_FILE:metatransfer>"
781811
"-DOIIOTOOL_BIN=${OPENMETA_OIIOTOOL_EXECUTABLE}"
782812
"-DEXIFTOOL_BIN=${OPENMETA_EXIFTOOL_EXECUTABLE}"
813+
"-DFFMPEG_BIN=${OPENMETA_FFMPEG_EXECUTABLE}"
783814
"-DWORK_DIR=${CMAKE_CURRENT_BINARY_DIR}/_metatransfer_image_usability"
815+
${_openmeta_bmff_test_target_args}
784816
-P "${CMAKE_CURRENT_SOURCE_DIR}/tests/metatransfer_image_usability_test.cmake"
785817
)
786818
endif()

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,12 @@ 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. 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.
145+
transfer entry points. BMFF file edits can replace OpenMeta-authored
146+
metadata-only `meta` boxes and can merge, replace, or strip bounded
147+
Exif/XMP/JUMBF/C2PA metadata items plus bounded ICC `colr/prof` properties
148+
in a parseable foreign top-level `meta` graph. Arbitrary BMFF
149+
scene/property-graph rewrite remains out of scope for the bounded writer
150+
path.
148151
- EXR is still narrower than the container-edit targets: it emits safe string
149152
header attributes through the transfer core, can materialize a prepared
150153
`ExrAdapterBatch` for host exporters, and Python can inspect that prepared

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.4.6
1+
0.4.7

docs/development.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,8 +317,15 @@ Current v1 behavior is:
317317
metadata-only `meta` box from the same bounded contract. It preserves
318318
non-`meta` top-level BMFF boxes and replaces the OpenMeta-authored
319319
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.
320+
- For targets with a parseable foreign top-level `meta` box, OpenMeta can
321+
merge, replace, or strip bounded Exif/XMP/JUMBF/C2PA items by extending
322+
`iinf`, `iloc`, `idat`, and `iref` with `cdsc` references to the primary
323+
item. This path requires a single parseable `iinf`, `iloc` version
324+
0/1/2, `pitm`, and at most one `idat`. Bounded ICC transfer removes
325+
prior ICC `colr/prof` and `colr/rICC` properties from `iprp/ipco`,
326+
compacts/remaps existing `ipma` associations, appends the transferred
327+
`colr/prof` property, and associates it with the primary item.
328+
Arbitrary BMFF scene/property graph rewrite remains unsupported.
322329
- CLI/Python `metatransfer` wrappers expose both BMFF summaries and this
323330
bounded edit path; `--target-heif`, `--target-avif`, and `--target-cr3`
324331
now accept `--source-meta PATH` plus `--output PATH` for metadata
@@ -816,6 +823,19 @@ cmake --build build-tests --target openmeta_gate_transfer_release
816823
ctest --test-dir build-tests -R openmeta_transfer_release_gate --output-on-failure
817824
```
818825

826+
The external image-usability gate can also use existing BMFF target files when
827+
local tools cannot create them:
828+
`OPENMETA_BMFF_HEIF_TEST_TARGET`, `OPENMETA_BMFF_AVIF_TEST_TARGET`, and
829+
`OPENMETA_BMFF_CR3_TEST_TARGET`. Arbitrary configured targets exercise the ICC
830+
property and XMP item transfer/read-back paths. The EXIF image-property
831+
transfer path only runs for configured targets that match the 64x32,
832+
3-channel fixture shape, so the gate does not intentionally write mismatched
833+
image geometry. The configured XMP assertion is based on OpenMeta's BMFF
834+
summary; ExifTool title validation is also applied for formats where ExifTool
835+
exposes the generic BMFF XMP item. If the local `oiiotool` build cannot decode
836+
a configured BMFF target after rewrite, `OPENMETA_FFMPEG_EXECUTABLE` can
837+
provide the decode fallback.
838+
819839
The public GitHub Actions workflow `.github/workflows/ci.yml` runs two Linux
820840
variants of these public release gates:
821841
- self-contained non-Python, non-DNG-SDK

docs/metadata_backend_matrix.md

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,7 @@ For the public per-target preserve/replace guarantees, see
115115
### ISO-BMFF metadata items (HEIF / AVIF / CR3)
116116

117117
- 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.
118+
supports a bounded BMFF metadata edit path over an existing BMFF target file.
120119
- Current prepared item routes:
121120
- `bmff:item-exif`
122121
- `bmff:item-xmp`
@@ -145,16 +144,26 @@ For the public per-target preserve/replace guarantees, see
145144
- The shared package-batch persistence/replay layer can own and hand off
146145
stable BMFF item and property payload bytes.
147146
- `metatransfer` / `openmeta.transfer_probe(...)` expose BMFF summaries,
148-
including `bmff_property colr/prof ...`.
147+
including `bmff_item mime/xmp ...` and `bmff_property colr/prof ...`.
149148
- `metatransfer --target-heif|--target-avif|--target-cr3 --source-meta ... -o ...`
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.
154-
- Embedded-XMP strip mode removes only OpenMeta-authored metadata `meta` boxes.
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.
149+
performs bounded metadata edits for targets with no foreign top-level `meta`
150+
box, with a prior OpenMeta-authored metadata-only `meta` box from the same
151+
bounded contract, or with a parseable foreign top-level `meta` item graph.
152+
The foreign-`meta` path merges, replaces, or strips bounded
153+
Exif/XMP/JUMBF/C2PA metadata items by extending `iinf`, `iloc`, `idat`, and
154+
`iref` with `cdsc` references to the primary item. It requires a single
155+
parseable `iinf`, `iloc` version 0/1/2, `pitm`, and at most one `idat`.
156+
- Foreign-`meta` ICC property merge is bounded to
157+
`bmff:property-colr-icc`. OpenMeta removes prior ICC `colr/prof` and
158+
`colr/rICC` properties from `iprp/ipco`, compacts/remaps existing `ipma`
159+
associations, appends the transferred `colr/prof` property, and associates
160+
it with the primary item. Broader non-ICC property replacement and arbitrary
161+
scene/property-graph rewrites remain out of scope for the current contract.
162+
- Embedded-XMP strip mode removes XMP from OpenMeta-authored metadata `meta`
163+
boxes and from parseable foreign top-level `meta` item graphs that satisfy
164+
the same bounded merge contract. Foreign graphs without a primary item
165+
relationship (`pitm`) or outside the supported `iinf`/`iloc`/`idat`/`iref`
166+
shape still fail explicitly instead of silently claiming removal.
158167
- The same bounded BMFF edit contract now also participates in the core /
159168
file-helper C2PA signer path:
160169
- sign-request derivation
@@ -163,7 +172,7 @@ For the public per-target preserve/replace guarantees, see
163172
- staged bmff:item-c2pa apply before bounded metadata-only edit
164173
- Out of scope for the current BMFF contract:
165174
- thin CLI/Python signer-input exposure for BMFF
166-
- merging or rewriting foreign top-level BMFF meta item/property graphs
175+
- arbitrary rewrite of foreign top-level BMFF scene/property graphs
167176
- full BMFF signed rewrite/re-sign beyond the bounded metadata-only edit path
168177

169178
### EXR (OpenEXR)

docs/metadata_transfer_plan.md

Lines changed: 30 additions & 11 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 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 |
58+
| HEIF / AVIF / CR3 | Bounded but real | Prepared bundle, compiled emit, OpenMeta-managed BMFF item/property edit, constrained foreign-`meta` item merge/replacement/strip plus bounded ICC property merge, file-helper roundtrip | Not broad BMFF writer parity; arbitrary foreign `meta` scene/property-graph rewrite is still unsupported |
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,17 +104,23 @@ 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 -> 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
107+
- source JPEG -> bounded HEIF edit/apply -> read-back
108+
- source JPEG -> bounded AVIF edit/apply -> read-back
109+
- source JPEG -> bounded CR3 edit/apply -> read-back
110+
- source ICC -> bounded BMFF ICC property edit/apply -> read-back and
111+
external validation when local HEIF/AVIF/CR3 encoders can create usable
112+
targets
113+
- source XMP -> bounded BMFF XMP item edit/apply -> read-back and external
114+
validation on configured HEIF/AVIF/CR3 targets where local tools expose the
115+
transferred XMP payload
110116

111117
That does not make all targets equally mature, but it does mean the transfer
112118
core has real roundtrip regression gates across the primary supported export
113119
families.
114120

115121
The primary writer family is also covered by a deterministic compatibility-dump
116-
gate for JPEG, TIFF, DNG, PNG, WebP, JP2, JXL, and OpenMeta-managed HEIF,
117-
AVIF, and CR3. That gate
122+
gate for JPEG, TIFF, DNG, PNG, WebP, JP2, JXL, and bounded HEIF, AVIF, and
123+
CR3 item metadata edits. That gate
118124
checks the prepared EXIF/XMP routes, edit/apply status, dual-write XMP
119125
writeback summary, and decoded metadata dump for the managed source fields.
120126
Additional compatibility-dump gates cover sidecar-only writeback with explicit
@@ -136,6 +142,13 @@ In a Python-enabled test tree it also runs:
136142
- `openmeta_python_transfer_probe_smoke`
137143
- `openmeta_python_metatransfer_edit_smoke`
138144

145+
The external image-usability gate can use optional configured HEIF, AVIF, and
146+
CR3 target files via CMake cache paths when local tools cannot create those
147+
formats. Arbitrary configured BMFF targets exercise the ICC property and XMP
148+
item transfer/read-back routes; the EXIF image-property route remains limited
149+
to the 64x32, 3-channel fixture shape to avoid intentionally mismatched
150+
geometry.
151+
139152
## Per-Target Notes
140153

141154
### JPEG
@@ -288,10 +301,16 @@ Implemented as a bounded BMFF target family:
288301
- bounded `bmff:item-c2pa`
289302
- bounded `bmff:property-colr-icc`
290303
- bounded OpenMeta-managed metadata-only `meta` rewrite path
291-
- explicit strip-mode rejection for recognized foreign XMP item graphs,
292-
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
304+
- constrained foreign top-level `meta` item merge for parseable `iinf`,
305+
`iloc` version 0/1/2, `pitm`, optional single `idat`, and primary-item `cdsc`
306+
references
307+
- bounded foreign top-level `meta` ICC property merge by replacing prior ICC
308+
`colr/prof` and `colr/rICC` properties, remapping `ipma`, and associating the
309+
transferred `colr/prof` property with the primary item
310+
- bounded foreign top-level `meta` XMP replacement and strip support for
311+
parseable item graphs that satisfy the same primary-item contract
312+
- fail-safe rejection for unsupported foreign top-level `meta` shapes and
313+
broader BMFF item/property graph rewrite shapes outside the bounded contract
295314

296315
### EXR
297316

@@ -787,7 +806,7 @@ embedded/sidecar writeback across the primary writer target family.
787806
#### 3. Compare-Backed Release Validation
788807

789808
- [x] promote the current primary-target roundtrip checks into explicit release-facing compare gates
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
809+
- [x] add compare-backed validation for `TIFF`, `DNG`, `PNG`, `WebP`, `JP2`, `JXL`, and bounded `BMFF` target outputs, including fail-safe rejection for unsupported foreign top-level `meta` BMFF shapes
791810
- [x] cover embedded-only, sidecar-only, and dual-write XMP flows in release-facing compare validation
792811
- [x] add compare-backed validation for explicit sidecar-base overrides and destination-sidecar cleanup behavior
793812
- [x] gate the primary writer family on deterministic read-back of managed metadata after edit/apply

0 commit comments

Comments
 (0)