Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inline SVG WIP #106

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/assets/google.html
Original file line number Diff line number Diff line change
Expand Up @@ -3090,7 +3090,7 @@
alt=""
height="24"
width="24"
style="border:none;display:none \9"
style="border:none;display:none"
></image>
</svg>
</a>
Expand Down
7 changes: 6 additions & 1 deletion packages/blitz/src/renderer/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,12 @@ impl<'dom> VelloSceneGenerator<'dom> {
cx.stroke_border(scene);
cx.stroke_devtools(scene);
cx.draw_image(scene);
cx.draw_svg(scene);

// Render inline SVG elements
if element.local_name() == "svg" {
cx.draw_svg(scene);
return;
}

// Render the text in text inputs
if let Some(input_data) = cx.text_input {
Expand Down
20 changes: 19 additions & 1 deletion packages/dom/src/document.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::events::{EventData, HitResult, RendererEvent};
use crate::node::TextBrush;
use crate::node::{NodeSpecificData, TextBrush};
use crate::util::parse_svg;
use crate::{Node, NodeData, TextNodeData, Viewport};
use app_units::Au;
use peniko::kurbo;
Expand Down Expand Up @@ -433,6 +434,23 @@ impl Document {
self.add_stylesheet(&css);
}

pub fn process_svg_element(&mut self, target_id: usize) {
let outer_html = self.nodes[target_id].outer_html();
println!("{}", outer_html);
match parse_svg(outer_html.as_bytes()) {
Ok(svg) => {
println!("SVG parsed successfully");
self.nodes[target_id]
.element_data_mut()
.unwrap()
.node_specific_data = NodeSpecificData::Svg(svg);
}
Err(err) => {
dbg!(err);
}
};
}

pub fn remove_stylehsheet(&mut self, contents: &str) {
if let Some(sheet) = self.stylesheets.remove(contents) {
self.stylist.remove_stylesheet(sheet, &self.guard.read());
Expand Down
8 changes: 8 additions & 0 deletions packages/dom/src/htmlsink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub struct DocumentHtmlParser<'a> {
doc: &'a mut Document,

style_nodes: Vec<usize>,
svg_nodes: Vec<usize>,

/// Errors that occurred during parsing.
pub errors: Vec<Cow<'static, str>>,
Expand All @@ -38,6 +39,7 @@ impl<'a> DocumentHtmlParser<'a> {
DocumentHtmlParser {
doc,
style_nodes: Vec::new(),
svg_nodes: Vec::new(),
errors: Vec::new(),
quirks_mode: QuirksMode::NoQuirks,
}
Expand Down Expand Up @@ -168,6 +170,11 @@ impl<'b> TreeSink for DocumentHtmlParser<'b> {
self.doc.process_style_element(*id);
}

// Parse inline SVGs (<svg> elements)
for id in &self.svg_nodes {
self.doc.process_svg_element(*id);
}

// Compute child_idx fields.
self.doc.flush_child_indexes(0, 0, 0);

Expand Down Expand Up @@ -221,6 +228,7 @@ impl<'b> TreeSink for DocumentHtmlParser<'b> {
"img" => self.load_image(id),
"input" => self.process_button_input(id),
"style" => self.style_nodes.push(id),
"svg" => self.svg_nodes.push(id),
_ => {}
}

Expand Down
59 changes: 59 additions & 0 deletions packages/dom/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use style::{
shared_lock::{Locked, SharedRwLock},
stylesheets::CssRuleType,
};
use style_traits::values::ToCss;
use taffy::{
prelude::{Layout, Style},
Cache,
Expand Down Expand Up @@ -697,6 +698,64 @@ impl Node {
s
}

pub fn outer_html(&self) -> String {
let mut output = String::new();
self.write_outer_html(&mut output);
output
}

pub fn write_outer_html(&self, writer: &mut String) {
let has_children = !self.children.is_empty();
let current_color = self
.primary_styles()
.map(|style| style.clone_color())
.map(|color| color.to_css_string());

match &self.raw_dom_data {
NodeData::Document => {}
NodeData::Comment => {}
NodeData::AnonymousBlock(_) => {}
// NodeData::Doctype { name, .. } => write!(s, "DOCTYPE {name}"),
NodeData::Text(data) => {
writer.push_str(data.content.as_str());
}
NodeData::Element(data) => {
writer.push_str("<");
writer.push_str(&data.name.local);

for attr in data.attrs() {
writer.push_str(" ");
writer.push_str(&attr.name.local);
writer.push_str("=\"");
if current_color.is_some() && attr.value.contains("currentColor") {
writer.push_str(
&attr
.value
.replace("currentColor", current_color.as_ref().unwrap()),
);
} else {
writer.push_str(&attr.value);
}
writer.push_str("\"");
}
if !has_children {
writer.push_str(" /");
}
writer.push_str(">");

if has_children {
for &child_id in &self.children {
self.tree()[child_id].write_outer_html(writer);
}

writer.push_str("</");
writer.push_str(&data.name.local);
writer.push_str(">");
}
}
}
}

pub fn attrs(&self) -> Option<&[Attribute]> {
Some(&self.element_data()?.attrs)
}
Expand Down
32 changes: 18 additions & 14 deletions packages/dom/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,23 @@ pub(crate) fn fetch_blob(url: &str) -> Result<Vec<u8>, FetchErr> {
Ok(bytes)
}

pub(crate) fn parse_svg(source: &[u8]) -> Result<usvg::Tree, usvg::Error> {
// TODO: Use fontique
let fontdb = FONT_DB.get_or_init(|| {
let mut fontdb = usvg::fontdb::Database::new();
fontdb.load_system_fonts();
Arc::new(fontdb)
});

let options = usvg::Options {
fontdb: fontdb.clone(),
..Default::default()
};

let tree = usvg::Tree::from_data(&source, &options)?;
Ok(tree)
}

pub(crate) fn fetch_string(url: &str) -> Result<String, FetchErr> {
fetch_blob(url).map(|vec| String::from_utf8(vec).expect("Invalid UTF8"))
}
Expand Down Expand Up @@ -124,20 +141,7 @@ pub(crate) fn fetch_image(url: &str) -> Result<ImageOrSvg, ImageFetchErr> {
};

// Try parse SVG

// TODO: Use fontique
let fontdb = FONT_DB.get_or_init(|| {
let mut fontdb = usvg::fontdb::Database::new();
fontdb.load_system_fonts();
Arc::new(fontdb)
});

let options = usvg::Options {
fontdb: fontdb.clone(),
..Default::default()
};

let tree = usvg::Tree::from_data(&blob, &options)?;
let tree = parse_svg(&blob)?;
Ok(ImageOrSvg::Svg(tree))
}

Expand Down
Loading