05 — Geometry, paint and text
Document status: draft. Canonical source.
Geometry follows established 2D vector mathematics: affine transforms, rectangles/rounded rectangles, ellipses, lines and Bézier paths. Path semantics SHOULD align with SVG where possible.
Paint supports solid colors, gradients, images, strokes, opacity, clipping/masks and compositing/blend modes. Color values MUST declare a color space; conversions are evaluator responsibilities.
Asset and resource boundary
An asset is a semantic entity with stable AssetId. A resource is an immutable
byte sequence identified by ResourceDigest. Package paths and external
locators are resolution hints and MUST NOT be used as either semantic or byte
identity.
Replacing the bytes bound to an asset MUST be expressed as a semantic operation
that preserves AssetId and changes its ResourceDigest. Every resource
descriptor declares media type and byte length. An implementation MUST verify
the declared size and digest before media-specific decoding.
Resource roles are:
source— exact bytes received from an origin;authoring— bytes required to evaluate or edit the semantic document;derived— bytes produced from named inputs by a declared transformation;cache— deletable acceleration data that cannot affect semantic hashes.
Derived resources MUST identify all source digests and the transformation
artifact/profile. A crop or trace created from a screenshot is derived; it
MUST NOT be presented as the original source asset.
Image assets
ImageAsset records AssetId, current encoded-resource digest, intrinsic pixel
dimensions and decoder profile. ImagePaint refers to the asset and records
fit, crop, affine transform, sampling, opacity and color-conversion policy.
Decoded pixels and GPU textures are caches keyed by encoded digest plus decoder
profile.
Renderer-independent scenes MUST store a unique decoded surface once per resource-digest/decoder-profile pair and reference it from image commands. The reference scene budget is 64 MiB of decoded RGBA surfaces and MUST be checked from bounded metadata before allocating the next decode.
The affine fields use [a c tx; b d ty; 0 0 1] and map the selected crop’s
normalized source coordinates forward into normalized coordinates of the
fitted paint rectangle. Fit is calculated first. Rasterizers inverse-map
destination pixel centers, apply crop selection after that inverse, and clip to
the entity rectangle. The origin is the fitted rectangle’s top-left; callers
encode any center-origin adjustment in the translation. Executable matrices
MUST be finite and invertible. The reference bound rejects components or
inverse components above 1,000,000 in magnitude and determinant magnitudes
below 1e-12 as unsupported fidelity.
The first executable image profile, nuif-png-rgba8-0, is PNG-only and
deliberately narrow. It accepts non-interlaced RGBA8 with no ancillary chunk or
one valid pre-image sRGB chunk, interprets the encoded samples as sRGB and
keeps alpha straight through decoding. It rejects palette, grayscale, RGB-only,
16-bit, CICP, ICC, gamma/chromaticity, Exif, animation, arbitrary ancillary
chunks and trailing bytes. Its exact chunk sequence, dimensions, pixel/byte
budgets, fit/crop, bounded affine transform, nearest/fixed-bilinear sampling, opacity
and encoded-sRGB integer composition contract is in
crates/nuif-media/PROFILE.md. JPEG, WebP, AVIF, animation, video and SVG are
unsupported by that profile rather than silently decoded through host defaults.
The separately named nuif-png-basic-rgba8-1 profile accepts the
non-interlaced PNG colour/depth combinations that normalize to RGBA8 without
sample-precision loss, including required palettes and valid tRNS
transparency. It does not change profile zero and still rejects 16-bit,
interlaced and colour-managed inputs. Its exact matrix is also in
crates/nuif-media/PROFILE.md.
An animation or video adapter MAY create a derived still resource when it records source digest, selected frame/time, decoder profile and item-level loss. SVG is evaluated only through a declared safe adapter profile; otherwise its bytes remain inert and preserved.
Font assets and portability
Text stores Unicode scalar content, style runs, paragraph attributes,
direction/language hints and a requested content-addressed font identity. A
font SHA-256 reference MUST contain 64 lowercase hexadecimal digits. Font size
and line height MUST be finite and positive. An optional font_asset binds the
text item to a stable font asset; a family or PostScript name is never a
substitute for that identity.
A font asset additionally records media type, an explicit decoder profile,
face or collection index, names used for matching, variation axes, feature
selections, coverage and portability policy. The policy is portable,
private_authoring, linked, substituted or unavailable. Decoder profile
selection is executable semantics and MUST NOT be inferred from the policy
evidence map. OpenType embedding flags and explicit license metadata are policy
evidence; the format does not claim to make a complete legal decision.
A portable package MUST NOT embed a font whose effective export policy forbids that embedding. Linked fonts retain expected digest and explicit resolver hint; resolution is opt-in and digest-checked. Substitution and unavailability MUST produce item-level fidelity. Family/PostScript names alone MUST NOT satisfy an exact-font profile.
For an exact binding, the asset resource SHA-256 MUST equal the requested text
hash. For a substituted binding, the text retains the requested hash and the
asset resource identifies the exact replacement bytes. For an unavailable
binding, the asset MUST carry no resource. Layout and rendering MUST use an
available declared replacement with approximated fidelity; if replacement
bytes are absent, or the asset is unavailable, rendering MUST emit no text
command and MUST report item-level unsupported fidelity. Resolution MUST NOT
query a platform font database or perform I/O.
The first executable resource subset, nuif-opentype-static-single-0, accepts
only one canonically packed, checksummed TrueType-outline sfnt face at index
zero. It requires exact font/ttf bytes, matching family names and Unicode
coverage, no variation axes, matching fsType evidence, a non-empty license
expression and an explicit embedding review. It rejects TTC, CFF/CFF2,
variable, color, bitmap, SVG and WOFF/WOFF2 sources. Exact limits and non-claims
are versioned in crates/nuif-font/PROFILE.md. In the experimental reference
composition, a digest-verified embedded face is registered locally, its global
feature map is applied by the pinned shaper, its exact advances and ascent drive
layout/baselines, and its unhinted outlines drive CPU rasterization. Missing
bytes fail at item level without platform discovery. This proves deterministic
behavior for the gated fixture, not foreign-shaper or cross-platform raster
equivalence.
RFC 0013’s separately negotiated candidate
nuif-opentype-variable-truetype-single-0 requires a complete finite axis tuple
and the same identifier in the package capability set. The reference runtime
normalizes once in fvar order, records the selected user 16.16 and final 2.14
coordinates in each resolved run, and uses that vector for shaping, horizontal
metrics, global metrics and unhinted outlines. It never falls back to the
static decoder or a host default instance. RFC 0013 and
crates/nuif-font/VARIABLE-PROFILE.md record the limits, evidence, and
remaining cross-platform and format non-claims. The local cross-surface gate
requires exact direct API, CLI, generated Node/browser WASM, stdio MCP and
linked POSIX C snapshot reports for the same capability-authorized package.
A conformance profile that compares resolved text MUST declare the exact font bytes and hash, shaper and Unicode-data versions, direction, language, script-selection rule, feature set, cluster level, cluster coordinate unit, positioning unit and resource limits. Resolved runs contain source text plus ordered glyph identifiers, clusters, advances and offsets; they MUST NOT depend on system font discovery. Profile 0 uses Unicode-scalar indices for cluster coordinates and unscaled font units for advances and offsets.
Shaping and rasterization are distinct conformance stages. A shaping pass does not imply raster conformance. A raster profile MUST additionally declare outline extraction, hinting, stem darkening, anti-aliasing, subpixel quantization, color/blend space and compositing rules. Until those parameters and their foreign/cross-platform trials exist, an implementation MUST classify a glyph-ID bitmap proxy as approximated rather than exact text rendering.
Implementations MUST preserve source text even when resolved glyph information is present.
CPU render profile 0
Profile 0 is deliberately narrower than the complete model. Its supported visual operations are encoded-sRGB solid fills of rectangles and ellipses, plus the text subset below. Color channels are finite numbers in the inclusive range 0 through 1. The reference raster starts as opaque white RGBA8.
Logical geometry is multiplied by the target scale factor before rasterization. A rectangle covers every pixel in floor(x)..ceil(x + width) and floor(y)..ceil(y + height), clipped to the target; it does not compute fractional edge coverage. Float color channels become bytes with round(clamp(channel, 0, 1) × 255). For mask coverage coverage, effective source alpha is (source_alpha × coverage + 127) / 255 using integer division. Each encoded-sRGB destination color channel becomes (source_channel × alpha + destination_channel × (255 - alpha) + 127) / 255; output alpha remains 255.
An ellipse is the closed four-cubic path inscribed in its bounds using control coefficient 0.551915024494. It is rasterized with nonzero fill into an 8-bit grayscale mask by Zeno 0.3.3, crates.io checksum 6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524, then composited by the integer rule above. conformance/render/profile-zero-v1.json fixes rectangle and ellipse scene/PNG hashes on the recorded platform matrix.
Profile-0 text uses Ahem 1.50, HarfRust 0.13.3 with Unicode 17.0.0, unhinted Skrifa 0.46.2 outlines in signed 26.6 font units, and Zeno 0.3.3 grayscale masks. CRLF is one hard break; CR, LF, NEL, LINE SEPARATOR and PARAGRAPH SEPARATOR are individual hard breaks. Each hard line is shaped independently. Intrinsic width is the greatest shaped line advance; intrinsic height is the number of hard lines times line_height. The first baseline is 800 Ahem font units below the line top, subsequent baselines differ by line_height, LTR starts at the left edge, RTL starts at the right edge, and output is clipped to the text box. Profile 0 performs no automatic soft wrapping. Because wrapping is not an authored property in this profile, absence of soft wrapping is exact profile behavior rather than an approximation.
Path geometry, image assets, component-instance materialization and extension-defined paint/effects are not supported by CPU render profile 0. Lowering MUST emit unsupported or preserved_unrenderable fidelity with the originating entity and property pointer; it MUST NOT substitute bounds rectangles or silently omit the data. nuif-png-rgba8-0 is an orthogonal experimental image segment and does not change profile-0 results. Future profiles may compose accepted segments explicitly.
The asset and broad-font requirements above remain draft inputs for broader profiles. The narrow executable image and static-font resource segments compose with the reference renderer only under their separately declared experimental profiles; they do not alter the Ahem-specific CPU render profile-0 golden.