openmeta/metadata_editing.h provides a bounded transactional editing contract
for the same logical fields accepted by the high-level Creation API. It edits
canonical portable-XMP entries in a finalized MetaStore without requiring a
host to work with namespace paths or entry IDs.
The API is experimental and versioned by
kMetadataEditingContractVersion == 1.
#include "openmeta/metadata_editing.h"
#include <array>
const std::array operations = {
openmeta::make_metadata_edit_set(
openmeta::make_metadata_creation_text(
openmeta::MetadataCreationFieldKind::Title, "Edited title")),
openmeta::make_metadata_edit_add(
openmeta::make_metadata_creation_text(
openmeta::MetadataCreationFieldKind::Keyword, "approved")),
openmeta::make_metadata_edit_remove(
openmeta::MetadataCreationFieldKind::Creator, 0),
};
openmeta::MetadataEditingRequest request;
request.operations = operations;
openmeta::MetaStore edited;
const openmeta::MetadataEditingResult result =
openmeta::edit_metadata(source, request, &edited);source must be finalized. The output is replaced only after every operation
has passed validation and the entire edit has committed. On failure, edited
is unchanged and failed_operation_index identifies the rejected operation
when available.
| Operation | Behavior |
|---|---|
Add |
Creates an absent singleton or appends a creator/keyword value. Adding an existing singleton is an explicit conflict. |
Set |
Replaces one active value. Existing key, origin, block, wire provenance, and flags are preserved; Dirty is added. |
Remove |
Marks one active value as `Deleted |
RemoveAll |
Tombstones every active occurrence. This can repair malformed duplicate singleton fields before a new value is added in the same transaction. |
Operations observe earlier operations in request order. For repeated creators
and keywords, occurrence is a zero-based index into the current active
logical values. Removing an occurrence shifts later values for subsequent
operations. New repeated values receive the next unused canonical property
index; existing index gaps are not renumbered.
Singleton fields accept only occurrence zero. If a malformed store contains
multiple active copies, single-value Set and Remove return
AmbiguousTarget; use RemoveAll followed by Add when that repair is
intended. Missing targets are errors rather than silent no-ops.
Set changes only the value and dirty flag. Remove changes only entry flags.
Both therefore retain the original block and wire provenance.
Add emits a new dirty canonical portable-XMP entry. When the store already
contains an XMP entry with a valid block, the new entry uses that block and a
later deterministic order. A finalized empty store can also be edited; its new
entry has no source block because no original carrier exists. Portable XMP and
transfer preparation can serialize that entry normally.
Editing does not compact tombstones automatically. Call the lower-level
compact(...) helper only when losing deleted-entry identity is appropriate
for the host workflow.
Add and Set use exactly the Creation field mapping and value validation.
Text must be non-empty valid UTF-8 and XML 1.0 character data. Orientation,
rating, dimensions, color space, ISO, and rational values use the constraints
documented in creation.md.
The hard maxima are:
1024operations1 MiBof UTF-8 text perAddorSetoperation8 MiBof UTF-8 text per request
MetadataEditingRequest::limits may lower but not raise these bounds. The
implementation keeps no global state. Concurrent calls are safe when callers
use distinct output stores and do not mutate the finalized source.
Python operation objects own their text and pass the same request to C++:
import openmeta
K = openmeta.MetadataCreationFieldKind
edited = document.edit_metadata([
openmeta.metadata_edit_set(
openmeta.metadata_creation_text(K.Title, "Edited title")),
openmeta.metadata_edit_add(
openmeta.metadata_creation_text(K.Keyword, "approved")),
openmeta.metadata_edit_remove(K.Creator, 0),
])The method returns a detached edited Document; the original document is not
mutated. The result can be queried, dumped as XMP, or converted into a transfer
snapshot. Invalid requests raise ValueError with the C++ status and rejected
operation index.
This milestone edits the 24 logical fields listed in
creation.md. It does not yet provide high-level arbitrary
EXIF/IPTC/XMP/custom-key operations, language-alternative selection beyond
x-default, structural block editing, a full EXIF/IPTC/XMP synchronization
engine, or direct in-place file patching. Lower-level MetaEdit remains
available for entry-ID-based host code, while transfer and writer APIs handle
container persistence.