feat(render): parse presentational attributes and font sizes for beautiful layout

This commit is contained in:
SGavrl committed 2026-07-02 20:20:26 +00:00
1 parent 9c8d62e13b
commit 42874989eb
4 files changed
+81 -17

No files matched your search

+56 -10
View File
@@ -54,10 +54,29 @@ pub fn layout_dom(tree: &DomTree, viewport: (f32, f32)) -> DomLayout {
for nid in tree.descendants(tree.document()) {
if let Some(node) = tree.get_node(nid) {
if let Some(inline) = node.get_attribute("style") {
if node.is_element() {
if let Some(style) = styles.get_mut(&nid) {
if let Some(inline) = node.get_attribute("style") {
crate::style::apply_inline(style, inline);
}
if let Some(bgcolor) = node.get_attribute("bgcolor") {
crate::style::apply_inline(style, &format!("background-color: {}", bgcolor));
}
if let Some(width) = node.get_attribute("width") {
if width.chars().all(|c| c.is_ascii_digit()) {
crate::style::apply_inline(style, &format!("width: {}px", width));
} else {
crate::style::apply_inline(style, &format!("width: {}", width));
}
}
if let Some(height) = node.get_attribute("height") {
if height.chars().all(|c| c.is_ascii_digit()) {
crate::style::apply_inline(style, &format!("height: {}px", height));
} else {
crate::style::apply_inline(style, &format!("height: {}", height));
}
}
}
}
}
}
@@ -82,22 +101,42 @@ pub fn layout_dom(tree: &DomTree, viewport: (f32, f32)) -> DomLayout {
height: taffy::AvailableSpace::Definite(viewport.1),
},
);
for (taffy_id, dom_id) in &id_map {
if let Ok(layout) = taffy_tree.layout(*taffy_id) {
compute_absolute_rects(&taffy_tree, taffy_root, 0.0, 0.0, &id_map, &mut rects);
}
}
DomLayout { rects, styles }
}
fn compute_absolute_rects(
taffy_tree: &TaffyTree,
taffy_id: taffy::NodeId,
abs_x: f32,
abs_y: f32,
id_map: &HashMap<taffy::NodeId, NodeId>,
rects: &mut HashMap<NodeId, Rect>,
) {
if let Ok(layout) = taffy_tree.layout(taffy_id) {
let x = abs_x + layout.location.x;
let y = abs_y + layout.location.y;
if let Some(dom_id) = id_map.get(&taffy_id) {
rects.insert(
*dom_id,
Rect {
x: layout.location.x,
y: layout.location.y,
x,
y,
width: layout.size.width,
height: layout.size.height,
},
);
}
if let Ok(children) = taffy_tree.children(taffy_id) {
for child_id in children {
compute_absolute_rects(taffy_tree, child_id, x, y, id_map, rects);
}
}
}
DomLayout { rects, styles }
}
fn parse_simple_css(css: &str) -> Vec<(String, String)> {
@@ -146,15 +185,22 @@ fn build(
if text.is_empty() {
return None;
}
let width = text.chars().count() as f32 * 9.0;
let style = taffy::Style {
let parent = node.parent?;
let style = styles.get(&parent)?;
let fsize = style.font_size.unwrap_or(16.0);
let width = text.chars().count() as f32 * (fsize * 0.55); // rough width estimate
let height = fsize * 1.2;
let taffy_style = taffy::Style {
size: taffy::Size {
width: taffy::Dimension::Length(width),
height: taffy::Dimension::Length(19.0),
height: taffy::Dimension::Length(height),
},
..Default::default()
};
let taffy_id = taffy_tree.new_leaf(style).ok()?;
let taffy_id = taffy_tree.new_leaf(taffy_style).ok()?;
id_map.insert(taffy_id, id);
return Some(taffy_id);
}
+1 -1
View File
@@ -68,8 +68,8 @@ pub struct LayoutStyle {
pub background_color: Option<[u8; 4]>,
/// Foreground (text) color for the paint step.
pub color: Option<[u8; 4]>,
/// Border color for the paint step.
pub border_color: Option<[u8; 4]>,
pub font_size: Option<f32>,
}
/// A node in the input layout tree. `text` is carried for the paint phase; it
+4 -3
View File
@@ -127,6 +127,7 @@ fn paint_text_node(
let parent = node.parent?;
let style = laid.styles.get(&parent)?;
let color = style.color.unwrap_or([0, 0, 0, 255]);
let fsize = style.font_size.unwrap_or(16.0);
// Get geometry of the text node itself!
let rect = match laid.rects.get(&nid) {
@@ -135,13 +136,13 @@ fn paint_text_node(
};
// Paint text at rect.x, rect.y
draw_text(pixmap, text, rect.x, rect.y, color);
draw_text(pixmap, text, rect.x, rect.y, color, fsize);
Some(())
}
fn draw_text(pixmap: &mut Pixmap, text: &str, x: f32, y: f32, color: [u8; 4]) {
fn draw_text(pixmap: &mut Pixmap, text: &str, x: f32, y: f32, color: [u8; 4], size: f32) {
let font = FontRef::try_from_slice(FONT_BYTES).unwrap();
let scale = PxScale::from(16.0); // Default font size
let scale = PxScale::from(size);
let scaled_font = font.as_scaled(scale);
let mut caret = ab_glyph::point(x, y + scaled_font.ascent());
+20 -3
View File
@@ -78,6 +78,7 @@ fn apply_value(style: &mut LayoutStyle, name: &str, value: &str) {
"background-color" | "background" => style.background_color = parse_color(value),
"color" => style.color = parse_color(value),
"border-color" => style.border_color = parse_color(value),
"font-size" => style.font_size = px(value),
_ => {}
}
}
@@ -156,12 +157,28 @@ fn edges(value: &str) -> Option<Edges> {
}
fn px_value(tok: &str) -> Option<f32> {
let n = tok.strip_suffix("px").unwrap_or(tok);
// Reject percentages and other units for phase 1.
let mut n = tok;
let mut scale = 1.0;
if n.ends_with("px") {
n = &n[..n.len() - 2];
} else if n.ends_with("pt") {
n = &n[..n.len() - 2];
scale = 1.333; // 1pt ≈ 1.333px
} else if n.ends_with("em") || n.ends_with("rem") {
n = n.trim_end_matches(|c: char| c.is_ascii_alphabetic());
scale = 16.0; // 1em = 16px
} else if n.ends_with('%') {
n = &n[..n.len() - 1];
scale = 16.0 / 100.0;
} else {
n = n.trim_end_matches(|c: char| c.is_ascii_alphabetic());
}
if n.chars().any(|c| !(c.is_ascii_digit() || c == '.' || c == '-')) {
return None;
}
n.parse::<f32>().ok()
n.parse::<f32>().ok().map(|v| v * scale)
}
fn token(value: &str) -> Option<&str> {