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.
What niso does
Section titled “What niso does”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.
Where the bug is
Section titled “Where the bug is”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.
Expected vs actual
Section titled “Expected vs actual”<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.
CARGO_TARGET_DIR=~/.capy/work/repro-target cargo runExit 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.
Upstream main
Section titled “Upstream main”Reproduces. Blitz main was still at be60597f67b3cece0456d26e8d7ff5a88600bbc5 (niso’s pin) when tested on 2026-11-08, so the pinned result is the main result.
Existing issue or PR
Section titled “Existing issue or PR”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).
Issue text
Section titled “Issue text”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).