render: preserve inline descendant font metrics
This commit is contained in:
1 parent
35c54936d5
commit
085c1458c2
1 file changed
+56
@@ -559,6 +559,8 @@ impl TextEngine {
|
|||||||
);
|
);
|
||||||
let context = base_span_ctx(style, family, weight, &mut collector);
|
let context = base_span_ctx(style, family, weight, &mut collector);
|
||||||
let attrs = SpanAttrs {
|
let attrs = SpanAttrs {
|
||||||
|
font_size: context.font_size,
|
||||||
|
line_height: context.line_height,
|
||||||
weight: context.weight,
|
weight: context.weight,
|
||||||
italic: context.italic,
|
italic: context.italic,
|
||||||
underline: context.underline,
|
underline: context.underline,
|
||||||
@@ -730,6 +732,8 @@ impl TextEngine {
|
|||||||
/// A run of same-styled inline text.
|
/// A run of same-styled inline text.
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
struct SpanAttrs {
|
struct SpanAttrs {
|
||||||
|
font_size: f32,
|
||||||
|
line_height: f32,
|
||||||
weight: u16,
|
weight: u16,
|
||||||
italic: bool,
|
italic: bool,
|
||||||
underline: bool,
|
underline: bool,
|
||||||
@@ -741,6 +745,14 @@ struct SpanAttrs {
|
|||||||
impl SpanAttrs {
|
impl SpanAttrs {
|
||||||
fn to_attrs(&self) -> Attrs<'_> {
|
fn to_attrs(&self) -> Attrs<'_> {
|
||||||
let mut a = Attrs::new().family(Family::Name(self.family.as_ref()));
|
let mut a = Attrs::new().family(Family::Name(self.family.as_ref()));
|
||||||
|
// Inline descendants keep their own computed font metrics inside the
|
||||||
|
// enclosing line box. Without per-span metrics, an `<a>`/`<span>`
|
||||||
|
// with a relative font-size was shaped at the block container's size
|
||||||
|
// even though cascade had resolved the descendant correctly.
|
||||||
|
a = a.metrics(Metrics::new(
|
||||||
|
self.font_size.max(1.0),
|
||||||
|
self.line_height.max(1.0),
|
||||||
|
));
|
||||||
a = a.weight(Weight(self.weight));
|
a = a.weight(Weight(self.weight));
|
||||||
a = a.style(if self.italic { Style::Italic } else { Style::Normal });
|
a = a.style(if self.italic { Style::Italic } else { Style::Normal });
|
||||||
// Clip-text glyphs must be shaped with an opaque fill so their coverage
|
// Clip-text glyphs must be shaped with an opaque fill so their coverage
|
||||||
@@ -757,6 +769,8 @@ impl SpanAttrs {
|
|||||||
/// Inherited inline context threaded down the subtree while collecting spans.
|
/// Inherited inline context threaded down the subtree while collecting spans.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct SpanCtx {
|
struct SpanCtx {
|
||||||
|
font_size: f32,
|
||||||
|
line_height: f32,
|
||||||
color: [u8; 4],
|
color: [u8; 4],
|
||||||
weight: u16,
|
weight: u16,
|
||||||
italic: bool,
|
italic: bool,
|
||||||
@@ -796,6 +810,8 @@ fn base_span_ctx(
|
|||||||
index
|
index
|
||||||
});
|
});
|
||||||
SpanCtx {
|
SpanCtx {
|
||||||
|
font_size: base.font_size.unwrap_or(16.0),
|
||||||
|
line_height: used_line_height(base),
|
||||||
color: base.color.unwrap_or([0, 0, 0, 255]),
|
color: base.color.unwrap_or([0, 0, 0, 255]),
|
||||||
weight,
|
weight,
|
||||||
italic: base.font_style_italic.unwrap_or(false),
|
italic: base.font_style_italic.unwrap_or(false),
|
||||||
@@ -845,6 +861,8 @@ fn collect_node_spans(
|
|||||||
match &node.data {
|
match &node.data {
|
||||||
obscura_dom::tree::NodeData::Text { contents } => {
|
obscura_dom::tree::NodeData::Text { contents } => {
|
||||||
let attrs = SpanAttrs {
|
let attrs = SpanAttrs {
|
||||||
|
font_size: ctx.font_size,
|
||||||
|
line_height: ctx.line_height,
|
||||||
weight: ctx.weight,
|
weight: ctx.weight,
|
||||||
italic: ctx.italic,
|
italic: ctx.italic,
|
||||||
underline: ctx.underline,
|
underline: ctx.underline,
|
||||||
@@ -862,6 +880,8 @@ fn collect_node_spans(
|
|||||||
}
|
}
|
||||||
if elem.local.as_ref() == "br" {
|
if elem.local.as_ref() == "br" {
|
||||||
out.push(("\n".to_string(), SpanAttrs {
|
out.push(("\n".to_string(), SpanAttrs {
|
||||||
|
font_size: ctx.font_size,
|
||||||
|
line_height: ctx.line_height,
|
||||||
weight: ctx.weight,
|
weight: ctx.weight,
|
||||||
italic: ctx.italic,
|
italic: ctx.italic,
|
||||||
underline: ctx.underline,
|
underline: ctx.underline,
|
||||||
@@ -901,6 +921,12 @@ fn collect_node_spans(
|
|||||||
})
|
})
|
||||||
.unwrap_or_else(|| (Arc::clone(&ctx.family), requested_weight));
|
.unwrap_or_else(|| (Arc::clone(&ctx.family), requested_weight));
|
||||||
let child = SpanCtx {
|
let child = SpanCtx {
|
||||||
|
font_size: style
|
||||||
|
.and_then(|style| style.font_size)
|
||||||
|
.unwrap_or(ctx.font_size),
|
||||||
|
line_height: style
|
||||||
|
.map(used_line_height)
|
||||||
|
.unwrap_or(ctx.line_height),
|
||||||
color,
|
color,
|
||||||
weight,
|
weight,
|
||||||
italic: ctx.italic || style.and_then(|s| s.font_style_italic).unwrap_or(false),
|
italic: ctx.italic || style.and_then(|s| s.font_style_italic).unwrap_or(false),
|
||||||
@@ -1403,6 +1429,36 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inline_descendant_keeps_its_computed_font_metrics() {
|
||||||
|
let tree = obscura_dom::parse_html(
|
||||||
|
r#"<style>
|
||||||
|
#copy { font-size:16px; line-height:20px }
|
||||||
|
#big { font-size:2em; line-height:1.5 }
|
||||||
|
</style>
|
||||||
|
<p id="copy">small <a id="big">large</a></p>"#,
|
||||||
|
);
|
||||||
|
let copy = tree.get_element_by_id("copy").unwrap();
|
||||||
|
let big = tree.get_element_by_id("big").unwrap();
|
||||||
|
let laid = crate::dom::layout_dom(&tree, (500.0, 200.0));
|
||||||
|
|
||||||
|
assert_eq!(laid.styles[&big].font_size, Some(32.0));
|
||||||
|
let item = laid.ifc_items[©];
|
||||||
|
let glyph_sizes = laid.text_engine.items[item]
|
||||||
|
.buffer
|
||||||
|
.layout_runs()
|
||||||
|
.flat_map(|run| run.glyphs.iter().map(|glyph| glyph.font_size))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
assert!(
|
||||||
|
glyph_sizes.iter().any(|size| (*size - 16.0).abs() < 0.01),
|
||||||
|
"base text should shape at 16px: {glyph_sizes:?}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
glyph_sizes.iter().any(|size| (*size - 32.0).abs() < 0.01),
|
||||||
|
"inline descendant should shape at 32px: {glyph_sizes:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn clip_fill_only_for_transparent_clip_text() {
|
fn clip_fill_only_for_transparent_clip_text() {
|
||||||
let mut s = LayoutStyle::default();
|
let mut s = LayoutStyle::default();
|
||||||
|
|||||||
Reference in new issue
Block a user