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()) {
|
for nid in tree.descendants(tree.document()) {
|
||||||
if let Some(node) = tree.get_node(nid) {
|
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(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),
|
height: taffy::AvailableSpace::Definite(viewport.1),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
for (taffy_id, dom_id) in &id_map {
|
compute_absolute_rects(&taffy_tree, taffy_root, 0.0, 0.0, &id_map, &mut rects);
|
||||||
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,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DomLayout { rects, styles }
|
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)> {
|
fn parse_simple_css(css: &str) -> Vec<(String, String)> {
|
||||||
let mut rules = Vec::new();
|
let mut rules = Vec::new();
|
||||||
let mut current_selector = String::new();
|
let mut current_selector = String::new();
|
||||||
@@ -146,15 +185,22 @@ fn build(
|
|||||||
if text.is_empty() {
|
if text.is_empty() {
|
||||||
return None;
|
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 {
|
size: taffy::Size {
|
||||||
width: taffy::Dimension::Length(width),
|
width: taffy::Dimension::Length(width),
|
||||||
height: taffy::Dimension::Length(19.0),
|
height: taffy::Dimension::Length(height),
|
||||||
},
|
},
|
||||||
..Default::default()
|
..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);
|
id_map.insert(taffy_id, id);
|
||||||
return Some(taffy_id);
|
return Some(taffy_id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ pub struct LayoutStyle {
|
|||||||
pub background_color: Option<[u8; 4]>,
|
pub background_color: Option<[u8; 4]>,
|
||||||
/// Foreground (text) color for the paint step.
|
/// Foreground (text) color for the paint step.
|
||||||
pub color: Option<[u8; 4]>,
|
pub color: Option<[u8; 4]>,
|
||||||
/// Border color for the paint step.
|
|
||||||
pub border_color: Option<[u8; 4]>,
|
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
|
/// 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 parent = node.parent?;
|
||||||
let style = laid.styles.get(&parent)?;
|
let style = laid.styles.get(&parent)?;
|
||||||
let color = style.color.unwrap_or([0, 0, 0, 255]);
|
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!
|
// Get geometry of the text node itself!
|
||||||
let rect = match laid.rects.get(&nid) {
|
let rect = match laid.rects.get(&nid) {
|
||||||
@@ -135,13 +136,13 @@ fn paint_text_node(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Paint text at rect.x, rect.y
|
// 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(())
|
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 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 scaled_font = font.as_scaled(scale);
|
||||||
let mut caret = ab_glyph::point(x, y + scaled_font.ascent());
|
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),
|
"background-color" | "background" => style.background_color = parse_color(value),
|
||||||
"color" => style.color = parse_color(value),
|
"color" => style.color = parse_color(value),
|
||||||
"border-color" => style.border_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> {
|
fn px_value(tok: &str) -> Option<f32> {
|
||||||
let n = tok.strip_suffix("px").unwrap_or(tok);
|
let mut n = tok;
|
||||||
// Reject percentages and other units for phase 1.
|
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 == '-')) {
|
if n.chars().any(|c| !(c.is_ascii_digit() || c == '.' || c == '-')) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
n.parse::<f32>().ok()
|
n.parse::<f32>().ok().map(|v| v * scale)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn token(value: &str) -> Option<&str> {
|
fn token(value: &str) -> Option<&str> {
|
||||||
|
|||||||
Reference in new issue
Block a user