Migrating: OpenRaster (.ora)
OpenRaster import/export scope, the best-effort/source-preservation policy, the generated blend-mode fidelity table, and a live import demo.
Generated from code
The import-ora package has landed. The blend-mode fidelity table below is
generated at build time from @faceless-photolib/import-pipeline's real
mapping table (task 5.1) — a mapping change updates this page on the next
site rebuild, with no manual doc edit. The import demo at the bottom runs
the real worker pipeline on a bundled sample.
Why OpenRaster first
ORA is the closest prior art to this format (see Overview → Positioning) and, per the design, the only export target in v0 — it's trivially producible from the reference renderer and buys immediate GIMP/Krita interop for demos. Import and export are two separate, asymmetric operations; see the scopes below.
Policy
Import here follows the same policy every importer in this format follows — never a fidelity promise:
- Best-effort, raster-safe subset. An importer maps what it confidently
can onto the native document model and is honest about the rest — see
History-DAG model for how every import lands as a
first-class
intent.kind: "external-tool"history node, not a side channel. - A fidelity report is data, not prose. Every layer/feature gets a
mapped | degraded | droppedverdict plus a human-readabledetail— shown to the user in a pre-commit dialog before the import is accepted, per thetasker-ui-site-and-format-docsspec's live-demo requirement. Adegradedverdict always fires awarnDegradedbeacon in the same call that constructs it — it can never enter a report silently. - Source preservation. The original
.orabytes are stored as a content-addressed blob (role: "master") and referenced from the import's history node — you can always get the exact original back and re-import later with a better importer, because nothing about the original is discarded even when the mapped result is lossy. - Detection is content-based, never filename-based. ORA is detected by
its zip
mimetypemember (image/openraster), not the.oraextension — a renamed file still imports correctly, and a file with an.oraextension that isn't actually one is never silently mis-imported.
Import scope
Per design D4: stack.xml's layer tree maps to groups and raster layers with
name/visibility/opacity/x,y offsets; mergedimage.png becomes a preview-role
blob; SVG layers rasterize through the existing image-decode path with a
degraded verdict (the source SVG is preserved as its own blob, not
discarded).
Export scope
Per design D5, ORA export is a raster-safe projection, not a lossless
round-trip: every layer flattens to PNG at document resolution through the
reference renderer, stack.xml is emitted with the mappable subset
(name/opacity/visibility/offset/blend mode), and mergedimage.png plus a
thumbnail come from packages/backend-cpu. The export's fidelity report uses
the identical verdict shape as import — it shows what got dropped on the way
out, symmetrically.
Blend-mode mapping (generated from import-pipeline)
The ORA↔native blend-mode table lives in
@faceless-photolib/import-pipeline (mapOraCompositeOp) — shared by both
the importer and exporter, so the same ORA composite-op value always
produces the same verdict regardless of which direction it's crossed. This
table is rendered from that exact code at build time:
| ORA composite-op | Native mode | Verdict |
|---|---|---|
svg:src-over | normal | mapped |
svg:multiply | multiply | mapped |
svg:screen | screen | mapped |
svg:overlay | overlay | mapped |
svg:darken | darken | mapped |
svg:lighten | lighten | mapped |
svg:color-dodge | colorDodge | mapped |
svg:color-burn | colorBurn | mapped |
svg:hard-light | hardLight | mapped |
svg:soft-light | softLight | mapped |
svg:difference | difference | mapped |
svg:color | color | mapped |
svg:luminosity | luminosity | mapped |
svg:hue | hue | mapped |
svg:saturation | saturation | mapped |
svg:plus, svg:dst-in, svg:dst-out, svg:src-atop, svg:dst-atop | normal | degraded |
The degraded row isn't a gap to be filled later — those five ORA operators
are true Porter-Duff compositing operators (they change which pixels show
at all), not per-pixel color-blend functions, so mapping them onto a blend
mode would silently change the image rather than just its color math. They
degrade to normal by design, never a nearest-match guess.
Try it: run the real import pipeline
The demo below fetches the corpus's ora/import-sample entry (a group, a
svg:multiply layer, and one svg:plus layer) via the server proxy and runs
it through the same format-worker pipeline the editor uses — the fidelity
report you see is the pipeline's actual output.
Run the bundled import-sample.ora (a group, a multiply layer, and one Porter-Duff svg:plus layer) through the real worker pipeline and see exactly what survives. Nothing is committed anywhere.
Corpus samples
Every ORA fixture in the sample corpus — group/isolation cases and the
svg:* composite-op matrix — is served through the same /api/samples/<id>
proxy every other demo on this site uses; no direct storage URL ever appears
in this page. "Open in editor" runs the real import pipeline end to end,
fidelity dialog included.
Preparing sample list…