feat(render): parse presentational attributes and font sizes for beautiful layout
This commit is contained in:
1 parent
9c8d62e13b
commit
42874989eb
4 files changed
+90
-26
No files matched your search
@@ -54,9 +54,28 @@ 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) {
|
||||
crate::style::apply_inline(style, inline);
|
||||
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,24 +101,44 @@ 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) {
|
||||
rects.insert(
|
||||
*dom_id,
|
||||
Rect {
|
||||
x: layout.location.x,
|
||||
y: layout.location.y,
|
||||
width: layout.size.width,
|
||||
height: layout.size.height,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
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,
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_simple_css(css: &str) -> Vec<(String, String)> {
|
||||
let mut rules = Vec::new();
|
||||
let mut current_selector = String::new();
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
Reference in new issue
Block a user