Skip to content

Commit 3a06dd9

Browse files
feat(websem,textlayout): render <text> — the text rung
The Web family gains a real shaped-text producer, and <text> leaves the refusal corpus for six Chromium-baked cells. The document says, the oracle resolves: - websem's <text> arm owns source semantics only — character data with XML whitespace collapsing (measured: a tab, a newline, and a run of spaces each collapse to one advance), x/y, text-anchor, and the cascaded family and size. It shapes nothing. - textlayout resolves the run once against a declared font environment and returns the immutable artifact; glyph outlines lower to rframe's existing path facts, so no font identity crosses the contract and the frame stays honestly glyphless. - the admitted numeric domain is enforced, not assumed: integer position, font-size a multiple of 5, integer anchor-resolved start. Chromium snaps everything else by a rasterizer-internal rule; this refuses by name rather than codifying it. Hermetic by default: a run whose family the host never declared refuses by name — no tofu, no ambient face, no machine-local pixel. A generic family (including the initial value) names no declared font and says so. fixtures/web-first/text/ is the suite, on the animation/ precedent and under the corpus-growth law: the fixture is the document, the font is the environment, and the baker declares the pinned identity to Chromium exactly as the host declares it to the engine — verified against its digest before any capture. All six cells are byte-exact with no tolerance admissible. svg-text graduates; what remains refused holds its own rows (svg-text-undeclared-font, svg-text-tspan). Two legacy expectations move with the behavior: basic-shapes.svg's stylesheet declares text-anchor and its labels ask for a generic family — both were silent drops before this rung and are named holes after it.
1 parent 5e07b1d commit 3a06dd9

31 files changed

Lines changed: 1297 additions & 15 deletions

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/csscascade/tests/svg_presentation_hints.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,10 @@ fn standalone_svg_presentation_hints_enter_below_author_rules() {
121121
// Measured in Chromium: the attribute alone selects the face, an author
122122
// rule beats it, `font-family=""` drops to the default family, and the
123123
// property inherits.
124-
assert_eq!(property(root, "family-hint", LonghandId::FontFamily), "Ahem");
124+
assert_eq!(
125+
property(root, "family-hint", LonghandId::FontFamily),
126+
"Ahem"
127+
);
125128
assert_eq!(
126129
property(root, "family-rule-beats-hint", LonghandId::FontFamily),
127130
"monospace"

crates/n0_cli/src/main.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -435,13 +435,15 @@ mod tests {
435435
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
436436
for (relative, kind, size, named) in [
437437
(
438-
// The strokes rung consumed this fixture's stroked shapes and
439-
// its two `<line>`s, so strict now reaches the labels: the
440-
// first refusal is `<text>`, the next rung on the ladder.
438+
// The text rung admitted `<text>` itself, so strict now
439+
// reaches this fixture's *stylesheet* first: `.label`
440+
// declares `text-anchor`, which Chromium consumes from the
441+
// cascade and the pinned servo build cannot represent. It
442+
// was a silent drop before that row existed.
441443
"fixtures/test-svg/L0/basic-shapes.svg",
442444
SourceKind::Svg,
443445
(500, 500),
444-
"unsupported element <text>",
446+
"a stylesheet declares text-anchor",
445447
),
446448
(
447449
// The probe's `.mark` class CSS-sizes the inline <svg>:
@@ -485,6 +487,12 @@ mod tests {
485487
// because Chromium paints nothing for them either. After the points
486488
// rung the polygons and the polyline render too, so what remains is
487489
// the labels and one rounded rect (`rx` is not consumed).
490+
//
491+
// After the text rung the labels are still declared, for reasons
492+
// that moved one layer up: the sheet's `text-anchor` cannot be
493+
// represented (declared once at `svg/style[1]`), and each label asks
494+
// for the generic `monospace`, which names no font in a declared
495+
// environment. Both are named holes where a silent drop used to be.
488496
let source = std::fs::read_to_string(root.join("fixtures/test-svg/L0/basic-shapes.svg"))
489497
.expect("read basic-shapes");
490498
let (png, degradations) = render_source_to_png(
@@ -506,6 +514,7 @@ mod tests {
506514
assert_eq!(
507515
skipped,
508516
vec![
517+
"svg/style[1]",
509518
"svg/g[1]/text[1]",
510519
"svg/g[2]/rect[1]",
511520
"svg/g[2]/text[1]",
@@ -521,8 +530,15 @@ mod tests {
521530
assert!(
522531
degradations
523532
.iter()
524-
.any(|d| d.reason() == "unsupported element <text>"),
525-
"reasons name the construct"
533+
.any(|d| d.reason().contains("a stylesheet declares text-anchor")),
534+
"the sheet's unrepresentable declaration is named once"
535+
);
536+
assert!(
537+
degradations
538+
.iter()
539+
.any(|d| d.reason().contains("generic font family")),
540+
"the labels' `font-family: monospace` names no declared font, and the run says so \
541+
instead of falling back to an ambient face"
526542
);
527543
assert!(
528544
!degradations.iter().any(|d| d.path() == "svg/g[1]"),

crates/websem/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ cg = { path = "../cg" }
1616
# The source-neutral resolved render contract this crate produces.
1717
# Contract-only and backend-free; the n0 engine owns the one downstream.
1818
rframe = { path = "../rframe" }
19+
# The Web family's text resolution oracle (the text rung): websem states what
20+
# the document says, textlayout states what the glyphs are. No font byte and
21+
# no ambient face enters through this edge — the environment is the host's.
22+
textlayout = { path = "../textlayout" }
1923
# Neutral geometry.
2024
math2 = { path = "../math2" }
2125
# The one namespace-aware document + the one (Stylo) browser-grade cascade.

crates/websem/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub mod svg;
2727
mod svg_animation;
2828
mod svg_paint_server;
2929
mod svg_path;
30+
mod svg_text;
3031
mod svg_transform;
3132

3233
pub use svg::{

0 commit comments

Comments
 (0)