clone

a browser-first diagram editor with live svg previews, local drafts and encrypted, accountless sharing

main
diagramzip / docs / svg-normalization.md
svg-normalization.md 27.6K rendered
1# SVG normalization contract23## Purpose45SVG is the rendering contract between a diagram engine and its consumers. An6engine can use any language, runtime, renderer, layout algorithm, or deployment7topology. Its accepted output becomes safe, deterministic SVG. Consumers can8present it with one of seven Diagram.zip appearance presets:910```text11raw12auto-transparent13light-transparent14dark-transparent15auto-framed16light-framed17dark-framed18```1920Normalization gives these outputs a coherent Diagram.zip visual language21without requiring engines to produce identical markup or use identical native22themes. The contract is defined in terms of output capabilities and semantic23roles rather than a fixed inventory of engines. A new engine or a new release24of an existing engine may use a different normalization pattern when its output25traits change.2627This contract does not require pixel compatibility between different engines,28replace an engine's layout algorithm, or make arbitrary SVG artwork fully29themeable. It does define what an engine must provide before each normalized30appearance can be advertised as supported.3132## Terminology3334- **Engine SVG** is the SVG returned by a renderer before shared processing.35- **Sanitized SVG** is engine SVG with unsafe content and disallowed resources36 removed.37- **Normalization profile** is a versioned set of configuration, recognition,38 and annotation rules for a particular renderer output contract.39- **Canonical SVG** is safe SVG whose geometry is preserved and whose relevant40 visual elements have Diagram.zip semantic roles.41- **Appearance preset** is a versioned mapping from semantic roles to concrete42 paint and presentation values.43- **Materialized SVG** is a self-contained SVG with one appearance preset44 selected. It is the public render artifact.45- **Canvas** is the backdrop around the diagram. It is distinct from the fills46 of nodes, notes, groups, labels, and other diagram objects.4748## Pipeline contract4950Every render follows the same conceptual pipeline:5152```text53source and options54 -> renderer configuration55 -> engine render56 -> SVG sanitization57 -> structural normalization58 -> semantic annotation59 -> canonical SVG60 -> appearance materialization61 -> final validation62 -> artifact63```6465Stages may execute in the browser, at the edge, in a service, or in the same66process as the engine. Their location does not change their contracts.6768The engine render is the only stage allowed to change diagram geometry as part69of layout. Later stages may normalize document bounds and add presentation70padding or a frame. They must not reroute connectors, reflow labels, or change71font metrics after layout. They must not reinterpret the source language.7273Sanitization occurs before semantic processing. Normalization rules must never74depend on active content, external stylesheets, remote resources, or other75content that the sanitizer will remove. A final validation pass verifies that76normalization did not introduce invalid or unsafe SVG.7778## Appearance presets7980The appearance name is a closed product-level enum, not a bag of independent81flags. This prevents combinations whose contrast and presentation have not82been tested.8384| Appearance | Canvas | Frame | Diagram palette |85| --- | --- | --- | --- |86| `raw` | Renderer-defined | Renderer-defined | Renderer-defined |87| `auto-transparent` | Transparent | None | Matches the viewing color scheme |88| `light-transparent` | Transparent | None | Light |89| `dark-transparent` | Transparent | None | Dark |90| `auto-framed` | Matching surface | Standard | Matches the viewing color scheme |91| `light-framed` | Light surface | Standard | Light |92| `dark-framed` | Dark surface | Standard | Dark |9394Transparent means that the canvas is transparent. It does not mean that all95object fills are transparent. A light-transparent node may use an opaque light96surface so that its dark label remains readable. A dark-transparent node may97use an opaque dark surface for the same reason.9899The transparent variants are intended for a surrounding surface of the same100lightness. Free-standing text and connectors in a light-transparent diagram may101not be readable on an arbitrary dark page. The reverse is true for a102dark-transparent diagram. Framed variants are self-contained and must remain103readable regardless of the embedding page.104105The automatic variants include both palettes and select between them with an106internal `prefers-color-scheme` media query. They do not depend on CSS from the107embedding page, which cannot style an SVG displayed through an image element.108109The standard frame owns:110111- a canvas surface;112- deterministic padding derived from the normalized bounds;113- a palette-defined border;114- any standard corner treatment;115- any frame-only shadow or elevation treatment.116117Frame strokes should use non-scaling behavior when necessary. Frame geometry118must be included in the final view box, and rounded corners must not expose an119unintended opaque rectangular canvas behind them.120121`raw` means visually faithful, safe renderer output. It does not mean122byte-for-byte preservation, unsanitized output, or exemption from deterministic123serialization. Removal of unsafe content may necessarily change the result.124125## Canonical SVG contract126127A canonical SVG preserves the engine's geometry while making presentation128intent explicit. It should identify its normalization schema, renderer129identity, normalization profile, and palette compatibility in machine-readable130metadata or root attributes.131132Schema 1 uses the following root attributes:133134```xml135<svg136 data-dz-schema="1"137 data-dz-normalizer="svg-normalizer-4"138 data-dz-engine="example"139 data-dz-profile="semantic-markup-2"140 data-dz-palette="renderer"141 data-dz-appearance="raw">142 <g data-dz-role="node">143 <rect data-dz-fill="surface-1" data-dz-stroke="line" />144 <text data-dz-fill="ink">Service</text>145 </g>146</svg>147```148149Appearance materialization adds the self-contained tokens, owned canvas, and150owned frame after the canonical SVG is stored. The canonical object remains151unchanged.152153These attributes are namespaced to Diagram.zip. The generic `data-theme`154attribute is not part of the contract because it can collide with renderer and155embedding conventions. Geometry and semantic paint roles remain independent156of the selected appearance.157158Canonical SVG must be:159160- valid, namespace-correct SVG;161- safe to display as an image under the product's content security policy;162- free of unapproved network dependencies;163- deterministic for identical renderer output, profile, and normalizer builds;164- idempotent under the same normalization build;165- self-contained after materialization;166- explicit about document bounds;167- stable under a change of appearance, except for presentation bounds added by168 the framed variants.169170The normalizer may preserve original IDs when safe. Any IDs it creates must be171deterministically namespaced to avoid collisions with engine IDs, paint172servers, masks, markers, filters, or embedded SVG fragments.173174Schema 1 separates fill and stroke roles. `data-dz-fill` controls paint inside175an element, while `data-dz-stroke` controls its boundary or connector. This176keeps arrowheads, unfilled nodes, and line geometry from inheriting the wrong177property during materialization.178179## Editable SVG document contract180181An editable file materializes the requested appearance, then adds a separate182versioned Diagram.zip document without changing the visible SVG geometry. The183root carries `data-dz-document="1"`, and exactly one direct `<metadata>` child184with `data-dz-kind="document"` and `data-dz-schema="1"` contains canonical185JSON. That JSON preserves the diagram type, source, options, presentation,186title, and description.187188This contract deliberately uses SVG 2 `data-*` attributes for element and root189bindings. Application data that does not belong to one element lives in190`<metadata>`. It does not depend on a foreign XML namespace whose behavior191would differ when the same SVG is parsed as inline HTML.192193Serialization is deterministic. Keys are sorted, and no timestamp or random194value is added. The same canonical render and normalized state produce the same195bytes. Import requires the supported marker, exactly one supported document, a196known diagram type, complete state, and a bounded 5 MiB input.197Ordinary, ambiguous, unsupported, or lossy SVG is rejected rather than reverse198engineered from its visible shapes.199200## Current profile coverage201202The shared sanitizer, canonical serializer, and appearance materializer apply203to browser and Worker renderers. Every catalog entry reports its active204normalization capability. An unrecognized renderer contract remains on205`safe-raw-1` and supports only `raw`.206207The current catalog uses these profile families:208209| Profile | Conformance | Engines | Supported appearances |210| --- | --- | --- | --- |211| `graphviz-15-semantic-2` | Semantic | Graphviz, DBML, ERD, WireViz | All |212| `d2-0.7-semantic-2` | Semantic | D2 | All |213| `plantuml-2026-semantic-2` | Semantic | PlantUML, C4 PlantUML, Structurizr | All |214| `svgbob-0.7-semantic-2` | Semantic | Svgbob, Ditaa | All |215| `neutral-svg-semantic-2` | Adaptive | BlockDiag, SeqDiag, ActDiag, NwDiag, PacketDiag, RackDiag, Bytefield, Mermaid, BPMN, Nomnoml, Pikchr, Symbolator, UMLet | All |216| `structured-svg-semantic-2` | Adaptive | GoAT, Vega, Vega-Lite, WaveDrom | All |217| `authored-neutral-semantic-1` | Adaptive | Excalidraw, TikZ | All |218| `authored-svg-presentation-1` | Presentation only | Diagrams.net | Raw and framed |219220Semantic profiles rely on pinned renderer structures and stable class or group221meaning. Adaptive profiles recognize neutral paint and selected structural222roles. They preserve authored data colors and other non-neutral paint.223Presentation-only profiles do not claim that the authored drawing adapts. They224add only the shared outer canvas and frame.225226Profile selection also checks the pinned renderer version. An engine upgrade227does not inherit a profile until its output passes the catalog audit. Unknown228builds fall back to `safe-raw-1`.229230## Semantic visual roles231232The normalized palette is expressed through semantic roles rather than literal233colors. The minimum role vocabulary is:234235| Role | Meaning |236| --- | --- |237| `canvas` | Framed diagram backdrop |238| `none` | Deliberately hollow or unpainted interior |239| `surface-1` | Ordinary node or object surface |240| `surface-2` | Container, group, pool, cluster, or section surface |241| `surface-3` | Nested, elevated, note, header, or callout surface |242| `ink` | Primary text and high-emphasis symbols |243| `ink-muted` | Secondary labels and annotations |244| `line` | Primary boundaries and connectors |245| `line-muted` | Grids, separators, guides, and secondary boundaries |246| `accent-N` | Stable categorical or semantic hue slot |247| `on-accent` | Content placed on a strong accent surface |248249Profiles may define more precise structural roles, but public appearance250presets should resolve them through this small shared vocabulary. This keeps251the palette bounded and prevents engine-specific colors from becoming part of252the public contract.253254Four surface levels are enough to express canvas, ordinary objects,255containers, and elevated or nested objects. Profiles need not use every level.256Surface numbers describe hierarchy, not fixed lightness: the appearance preset257chooses appropriate values in light and dark modes.258259Accent slots preserve distinctions, not literal shades. An authored blue and260an authored orange can map to stable blue-like and orange-like accent slots.261The light and dark presets select different shades for contrast. Color must not262be the sole carrier of meaning when the source format provides shape, label,263line, or pattern distinctions.264265## Palette contract266267The palette is a small, pinned data set embedded into materialized SVG. It must268not depend on a remote CSS resource or on the embedding page. A versioned subset269of the [Pico color palette](https://picocss.com/docs/colors) can supply neutral270surfaces and accent ramps. Diagram.zip role assignments and contrast pairs have271their own versioned contract.272273The palette contains only the families and shades used by the role table. At a274minimum it defines:275276- four light surfaces and four dark surfaces;277- primary and muted ink for each scheme;278- primary and muted line colors for each scheme;279- paired light and dark values for every supported accent slot;280- safe foreground values for strong accent surfaces;281- frame border and optional shadow values.282283Selected foreground/background pairs must be measured rather than assumed to284be accessible because they came from a color library. Text contrast is checked285against the surface that owns the text. Strong accent fills should use a known286`on-accent` value. Subtle categorical fills should use a tinted surface and a287stronger accent border when that combination provides more reliable contrast.288289Palette changes create a new palette build. Existing artifacts remain tied to290the palette build with which they were produced.291292## Renderer configuration and normalization293294The renderer and the normalizer share responsibility, but the normalizer owns295the final contract.296297Configure a renderer when configuration affects output before layout or makes298semantic intent more reliable. Examples include:299300- selecting deterministic output;301- selecting a known font before text measurement;302- suppressing an unnecessary canvas;303- enabling stable semantic groups or classes;304- disabling renderer-owned shadows or decorations that cannot be adapted;305- asking for neutral rather than independently themed output;306- preventing network or filesystem resource loading.307308Post-process renderer output when the change is presentation-only and does not309need source-language knowledge. Examples include:310311- sanitization;312- canvas recognition and removal;313- deterministic bounds and serialization;314- role annotations based on stable renderer structure;315- semantic paint overrides;316- adding the Diagram.zip frame;317- embedding metadata and the selected appearance.318319Do not render separate native light and dark layouts only to obtain theme320support. The renderer must first prove that both modes have identical geometry321and semantic structure. Native themes can change font selection, layout,322filters, markup, and measurements. Prefer one canonical geometry.323324Do not replace fonts after layout unless the profile proves metric325compatibility. A color-only post-processing step is generally safe; a font326change can overflow labels and move perceived alignment even when SVG geometry327is unchanged.328329## Normalization profiles330331A normalization profile is selected for a renderer output contract, not merely332for an engine name. Selection may consider renderer identity, build, declared333capabilities, output schema, and tested structural signatures. A renderer334upgrade does not inherit a profile automatically.335336Profiles are built from composable patterns. Common patterns include:337338### Native semantic output339340The renderer emits stable semantic groups, classes, or tokens for nodes, edges,341containers, labels, and accents. The profile validates and maps those semantics342onto Diagram.zip roles. This is the strongest and least heuristic pattern.343344### Configurable neutral output345346The renderer can be configured to emit a predictable neutral palette and no347canvas. The profile recognizes a small set of known paints and structural348elements, then assigns roles. Configuration and post-processing work together.349350### Stylesheet-driven output351352Visual properties are controlled by embedded CSS rather than presentation353attributes. The profile must account for cascade and inheritance. It may append354appearance rules with sufficient specificity, rewrite a bounded known355stylesheet, or resolve selected properties before annotation. Blind string356replacement inside CSS is not acceptable.357358### Structural geometry output359360The renderer emits groups and geometry with little or no semantic styling. The361profile identifies object boundaries from stable structure and supplies362surfaces, ink, and lines. This pattern may synthesize a missing object fill only363when it has positively identified the element's semantic role.364365### Flat paint output366367The renderer emits literal paints with weak structural semantics. The profile368maps neutral paints to shared roles and hue-bearing paints to accent slots.369It must preserve distinctions and avoid treating every equal literal color as370the same semantic role when context says otherwise.371372### Authored-visual output373374Colors, gradients, patterns, or opacity are significant user-authored content.375The profile preserves the raw appearance and uses semantic or hue-preserving376adaptation only where it can distinguish structure from authored artwork. Full377theme support requires stronger renderer metadata; a frame alone must not be378advertised as full dark-mode normalization.379380### Composite output381382The SVG contains embedded raster images, nested SVG, complex filters, or other383opaque visual subtrees. The profile normalizes the parts it understands and384declares the remainder. Opaque content cannot be claimed as theme-adaptive385without a renderer-provided alternate representation.386387### Translated pipeline output388389One stage translates source into another renderer's input. Semantic annotations390may be carried through the translation, applied by the final renderer's391profile, or composed by both. Artifact identity records the ordered pipeline392and every normalization profile that affected the result.393394These patterns are neither exclusive nor permanent categories. One renderer395build may combine semantic markup with stylesheet-driven labels and composite396icons. A later build may expose native tokens and stop using the flat-paint397pattern. The profile documents and tests the combination actually used.398399## Paint assignment rules400401Paint assignment operates on parsed SVG and known style structure, not regular402expressions over serialized markup.403404The profile distinguishes at least:405406- canvas paint;407- object surface paint;408- text paint;409- connector and boundary paint;410- markers and arrowheads;411- categorical or semantic accents;412- authored artwork;413- paint servers such as gradients and patterns;414- filters and shadows.415416SVG defaults and inheritance matter. An absent `fill` does not necessarily mean417transparent. An explicit `fill="none"` does not identify a shape as a node418boundary or an open decorative path. The normalizer must inspect computed intent419within the bounded styling model that the profile supports.420421A profile may turn an unfilled boundary into an opaque themed surface only422when it has identified the element as an object surface. It must not infer that423every closed rectangle, polygon, or path is a node. Arrowheads, icons, chart424marks, separators, clipping geometry, masks, hit targets, and invisible layout425elements are common counterexamples.426427Text is assigned an ink role relative to its owning surface. Text without an428identifiable owner uses the scheme's canvas ink. A profile must account for429ordinary SVG text, paths, and approved embedded markup. Support for one form430does not imply support for the others.431432Markers should inherit or be assigned the connector's role. If the renderer433reuses one marker definition across differently colored connectors, the434profile may need deterministic marker cloning or a supported contextual paint435mechanism.436437Gradients and patterns require explicit profile rules. Mapping only a gradient438reference while leaving incompatible stops unchanged is not normalization.439When safe adaptation is unavailable, preserve the authored paint in `raw`.440Report the themed appearance as unsupported or incomplete. Do not silently441produce misleading output.442443## Raw preservation and themed overrides444445Raw values remain available independently of Diagram.zip role values. A446canonical representation can preserve renderer paint and apply scoped447appearance overrides. It can instead store normalized raw fallbacks in448deterministic custom properties. The exact mechanism is an implementation449choice when these invariants hold:450451- selecting `raw` restores the safe renderer appearance;452- selecting a themed appearance does not require rerendering geometry;453- appearance rules affect only elements positively assigned a role;454- engine styles cannot accidentally override required themed contrast;455- themed rules do not destroy the information needed for `raw`.456457Literal byte equality is not an invariant. Visual equivalence is tested with a458documented tolerance, alongside structural assertions for content that is459difficult to compare by pixels.460461## Materialization462463Canonical SVG is the reusable geometry and role representation. Materialization464embeds the palette tokens and role rules for raw, light, dark, and automatic465selection. The root `data-dz-appearance` is the single CSS selector for those466palettes. Materialization also applies canvas or frame geometry and serializes a467self-contained SVG.468469Consumers may inline canonical SVG and change its root appearance in a trusted470environment. Consumers displaying SVG through an image element cannot assume471that page CSS will style the SVG document. Public image endpoints and downloaded472artifacts therefore return a materialized SVG with an explicit appearance.473474Materialization must not invoke the diagram engine. Switching between light475and dark or transparent and framed variants reuses canonical geometry. Framed476and transparent variants may have different outer view boxes because padding477and frame geometry are presentation, not diagram layout.478479Changing the root data value is sufficient to switch palette rules within480prepared geometry. Changing between transparent and framed bounds still481requires materialization because CSS cannot rewrite the root `viewBox`.482483## Conformance and capability reporting484485An appearance is supported only when the active profile satisfies its486invariants. Capability reporting should distinguish:487488- `raw`: safe, visually faithful output;489- `semantic`: roles are derived from renderer semantics or proven stable490 structure;491- `adaptive`: paints are mapped with sufficient context to meet contrast and492 distinction requirements;493- `presentation-only`: only the canvas and frame are normalized;494- `unsupported`: the appearance cannot be produced honestly.495496Palette adaptation requires `semantic` or `adaptive` conformance.497`presentation-only` output may support framed appearances, but only the outer498canvas and frame adapt. The authored drawing remains unchanged. A production499engine upgrade should remain on its last conforming renderer/profile pair500until the new output contract passes normalization tests.501502If a profile selector does not recognize a renderer build or structural503signature, it fails closed. It must not silently apply rules written for a504different output contract. A generic safe fallback may still provide `raw`,505but it does not inherit themed conformance.506507Materialized SVG should expose enough metadata for diagnostics, including:508509- renderer identity and build;510- ordered renderer or translator pipeline;511- normalizer schema and build;512- normalization profile and build;513- palette build;514- selected appearance;515- conformance level and declared limitations.516517## Artifact identity and caching518519Renderer, normalization, and appearance versions are independent inputs to520artifact identity. A cache key for a final SVG includes at least:521522```text523source identity524renderer options525renderer pipeline and builds526normalizer schema and build527normalization profile builds528palette build529appearance530render-affecting metadata531```532533Caching may be split into layers:534535```text536engine render cache537 = source + renderer options + renderer pipeline/builds538539canonical SVG cache540 = engine SVG identity + sanitizer/normalizer/profile builds541542materialized SVG cache543 = canonical identity + palette build + appearance + metadata544```545546This split is an optimization, not part of the public API. The correctness rule547is that changing a renderer, sanitizer, profile, normalizer, palette, or548appearance cannot accidentally reuse an artifact created by an incompatible549build.550551## Validation552553Each renderer/profile pair has a representative golden corpus. The corpus554covers the output traits used by its patterns rather than only a minimal smoke555diagram. Where applicable it includes:556557- unfilled and filled objects with labels;558- nested containers and repeated surface levels;559- free-standing text;560- multiple connector types and markers;561- explicit authored colors and categorical accents;562- gradients, patterns, opacity, and filters;563- embedded markup, nested SVG, or raster content;564- unusual view boxes, dimensions, and coordinate origins;565- international text and the renderer's supported font behaviors;566- empty, very small, and bounded large diagrams;567- hostile SVG and resource-loading attempts.568569Conformance tests verify:5705711. The engine SVG is sanitized and the result remains valid SVG.5722. `raw` remains visually faithful within the declared tolerance.5733. Repeated normalization is byte-deterministic.5744. Normalization is idempotent for the same build.5755. All appearances preserve canonical diagram geometry.5766. Transparent appearances contain no canvas backdrop.5777. Transparent appearances retain required object surfaces.5788. Framed appearances include the complete frame within their bounds.5799. Text and meaningful marks meet the palette's contrast requirements against580 their owning surfaces.58110. Accent distinctions remain stable across light and dark appearances.58211. Unknown or decorative geometry is not assigned an invented surface.58312. External resources and active content do not survive or reappear.58413. The SVG renders consistently through supported image-element, inline, and585 rasterization paths.586587Visual tests render transparent appearances over both expected and adversarial588checkerboard surfaces. This makes accidental canvas fills and unreadable free589text visible. Framed variants are tested over unrelated embedding colors to590verify that they are self-contained.591592## Versioning and change management593594The following are separately versioned contracts:595596- renderer identity and build;597- renderer pipeline;598- sanitizer behavior;599- canonical SVG schema;600- normalization profile;601- palette;602- appearance preset geometry and rules.603604A renderer upgrade, even under the same engine name, is treated as a new output605contract until its existing profile passes the full corpus. A profile may then606declare compatibility with the new renderer build, or a new profile may be607introduced. Pattern membership is updated based on observed capabilities, not608historical engine classification.609610Changes that alter materialized pixels or bounds require a new relevant build611identifier. Schema migrations should preserve the ability to serve or612regenerate artifacts whose identities refer to an older supported contract.613614## Anti-patterns615616The following approaches violate this contract:617618- global search-and-replace of color strings;619- treating every `fill="none"` shape as an object that needs a surface;620- using the presence of a rectangle alone to identify the canvas;621- applying a profile solely by engine name across untested renderer upgrades;622- claiming dark support after changing only the outer canvas;623- changing fonts after layout without metric-compatibility proof;624- allowing embedding-page CSS to be required for a public SVG;625- loading palette styles or fonts from the network at display time;626- mutating geometry separately for light and dark appearances;627- silently falling back from an unrecognized profile to heuristic theming;628- omitting normalizer, profile, or palette builds from artifact identity;629- calling unsafe renderer output `raw` and bypassing sanitization.630631## Contract summary632633The engine owns source interpretation and layout. The sanitizer owns safety.634The normalization profile owns the mapping from a tested renderer output635contract to semantic roles. The appearance preset owns palette, canvas, and636frame. Materialization combines one canonical geometry with one explicit637appearance to produce a deterministic, self-contained SVG.638639Engines and their versions may change normalization patterns freely. Consumers640continue to receive the same seven appearance names and the same semantic641visual contract.