Skip to content

blitz-dom: an inline element's client rect includes the whitespace hanging at the end of a wrapped line

Generated from repros/blitz-dom-inline-rect-trailing-space/README.md when the site was built at 7aa23e2.

Found by Acid1/Acid2 comparisons (link boxes one space too wide). crates/niso-core/src/geometry.rs: Dom::inline_rect measures non-atomic inlines for Element.getBoundingClientRect() itself and clips each fragment to the line’s content, leaving out LineMetrics::hanging_advance. Inline boxes have no native view of their own, so nothing else uses blitz’s rect.

blitz-dom Node::inline_fragment_boxes (packages/blitz-dom/src/node/node.rs:1852-1853): each fragment spans glyph_run.offset() .. offset + glyph_run.advance(), and on a soft-wrapped line the run’s advance includes the collapsible space that hangs past the line end. Chrome leaves the hanging space out of the inline box’s rect. Parley already reports it per line (LineMetrics::hanging_advance, “the portion of advance that hangs past the line’s end edge”), so this is a blitz fix: clip the last fragment on the line by hanging_advance.

<p style="width:120px;font:20px sans-serif">aaaa <a href=#>bbbb cccc</a></p> wraps after “bbbb “. The link’s widest fragment should end where “aaaa bbbb” ends; the repro measures that with a non-wrapping reference span aaaa bbbb, and prints the line’s hanging_advance from parley:

<a> wraps after "bbbb"; its widest fragment ends at the end of "aaaa bbbb"
expected: width 99.28125 (Chrome with its sans-serif: 99.3)
actual: width 104.484375 (+5.203125, the hanging space is 5.2)

Measured with Noto Sans as sans-serif (fc-match sans-serif), which gives the same 99.3 as Chrome 156; the extra width equals the space’s advance with any font.

Terminal window
CARGO_TARGET_DIR=~/.capy/work/repro-target cargo run

Exit status 1 while the bug reproduces, 0 once fixed. Needs libfontconfig-dev pkg-config on Ubuntu. Built with blitz-dom features system-fonts, accessibility, floats (as niso), viewport 800x600 at scale 1.

Reproduces. Blitz main was still at be60597f67b3cece0456d26e8d7ff5a88600bbc5 (niso’s pin) when tested on 2026-11-08, so the pinned result is the main result.

None for the rects. Related: https://github.com/DioxusLabs/blitz/pull/1009 (open, “Don’t paint inline backgrounds behind collapsed trailing whitespace”) fixes the same thing for painting only (it touches blitz-paint/src/text.rs and render.rs, not inline_fragment_boxes).

Title: get_client_bounding_rect of an inline includes the hanging space at the end of a wrapped line

Body:

On a soft-wrapped line, collapsible trailing whitespace hangs past the end edge and is not part of the inline box’s rect in browsers. Node::inline_fragment_boxes uses the glyph run’s full advance, so an inline element that wraps after a word is one space wider than in Chrome. Parley exposes the amount as LineMetrics::hanging_advance. (#1009 fixes the painted background for the same case.)

use blitz_dom::DocumentConfig;
use blitz_html::HtmlDocument;
use blitz_traits::shell::{ColorScheme, Viewport};
const HTML: &str = r##"<body style="margin:0">
<p style="width:120px;font:20px sans-serif;margin:0">aaaa <a id="a" href="#">bbbb cccc</a></p></body>"##;
fn main() {
let config = DocumentConfig { viewport: Some(Viewport::new(800, 600, 1.0, ColorScheme::Light)), ..Default::default() };
let mut doc = HtmlDocument::from_html(HTML, config);
doc.resolve(0.0);
let r = doc.get_client_bounding_rect(doc.get_element_by_id("a").unwrap()).unwrap();
println!("{}", r.width); // with Noto Sans: Chrome 99.3, blitz 104.48 (one space wider)
}

Tested at be60597. Suggested fix: in inline_fragment_boxes, subtract line.metrics().hanging_advance from the end of the fragment that ends the line (the start in RTL).