Skip to content

Commit

Permalink
Fix XML parsing, update rust-fontconfig (preparation for WASM)
Browse files Browse the repository at this point in the history
  • Loading branch information
fschutt committed Oct 31, 2024
1 parent 423260d commit 9078ae5
Show file tree
Hide file tree
Showing 9 changed files with 404 additions and 358 deletions.
620 changes: 306 additions & 314 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion azul-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ azul-css-parser = { path = "../azul-css-parser", version = "0.0.1", opti
rayon = { version = "1.5.3", default-features = false, optional = true }
gl-context-loader = { version ="0.1.8", default-features = false }
highway = { version = "0.8.0", default-features = false }
rust-fontconfig = { version = "0.1.5", default-features = false }
rust-fontconfig = { version = "0.1.11", default-features = false }

[features]
default = ["std"]
Expand Down
20 changes: 15 additions & 5 deletions azul-core/src/xml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1138,20 +1138,30 @@ pub fn normalize_casing(input: &str) -> String {

/// Given a root node, traverses along the hierarchy, and returns a
/// mutable reference to the last child node of the root node
#[allow(trivial_casts)]
pub fn get_item<'a>(hierarchy: &[usize], root_node: &'a mut XmlNode) -> Option<&'a mut XmlNode> {
let mut hierarchy = hierarchy.to_vec();
hierarchy.reverse();
let item = hierarchy.pop()?;
let node = root_node.children.as_mut_slice_extended().get_mut(item)?;
let item = match hierarchy.pop() {
Some(s) => s,
None => return Some(root_node),
};
let node = root_node.children.as_mut().get_mut(item)?;
get_item_internal(&mut hierarchy, node)
}

fn get_item_internal<'a>(hierarchy: &mut Vec<usize>, root_node: &'a mut XmlNode) -> Option<&'a mut XmlNode> {
fn get_item_internal<'a>(
hierarchy: &mut Vec<usize>,
root_node: &'a mut XmlNode,
) -> Option<&'a mut XmlNode> {
if hierarchy.is_empty() {
return Some(root_node);
}
let cur_item = hierarchy.pop()?;
let node = root_node.children.as_mut_slice_extended().get_mut(cur_item)?;
let cur_item = match hierarchy.pop() {
Some(s) => s,
None => return Some(root_node),
};
let node = root_node.children.as_mut().get_mut(cur_item)?;
get_item_internal(hierarchy, node)
}

Expand Down
2 changes: 1 addition & 1 deletion azul-desktop/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ azul-css-parser = { path = "../azul-css-parser", version = "0.0.1", def
log = { version = "0.4.17", default-features = false, optional = true }
fern = { version = "0.6.1", default-features = false, optional = true }
backtrace = { version = "0.3.66" }
rust-fontconfig = { version = "0.1.5", default-features = false, features = ["std"] }
rust-fontconfig = { version = "0.1.11", default-features = false, features = ["std", "parsing"] }
strfmt = { version = "0.1.6", default-features = false }
libm = { version = "0.2.2", default-features = false }
gl-context-loader = { version ="0.1.8", default-features = false }
Expand Down
2 changes: 1 addition & 1 deletion azul-layout/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ azul-css = { path = "../azul-css", version = "0.0.1", default-feature
azul-core = { path = "../azul-core", version = "0.0.2", default-features = false, features = ["multithreading", "css_parser"] }
azul-text-layout = { path = "../azul-text-layout", version = "0.0.5", default-features = false, optional = true }
rayon = { version = "1.5.3", default-features = false }
rust-fontconfig = { version = "0.1.5", default-features = false }
rust-fontconfig = { version = "0.1.11", default-features = false }

[dev-dependencies]
azul-css-parser = { path = "../azul-css-parser", version = "0.0.1" }
Expand Down
3 changes: 2 additions & 1 deletion azulc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ azul-css = { path = "../azul-css", version = "0.0.1",
azul-css-parser = { path = "../azul-css-parser", version = "0.0.1", default-features = false }
azul-layout = { path = "../azul-layout", default-features = false }
image = { version = "0.24.3", default-features = false, optional = true }
rust-fontconfig = { version = "0.1.5", default-features = false, optional = true }
rust-fontconfig = { version = "0.1.11", default-features = false, optional = true }
lyon = { version = "0.17.10", default-features = false, optional = true }
rayon = { version = "1.5.3", default-features = false, optional = true }
usvg = { version = "0.22.0", default-features = false, optional = true, features = ["export"] }
Expand All @@ -43,6 +43,7 @@ tiny-skia = { version = "0.6.5", default-features = false, opti
xmlwriter = { version = "0.1.0", default-features = false }
geo-booleanop = { version = "0.2.1", default-features = false }
geo = { version = "0.26.0", default-features = false }
base64 = "0.22.1"

[features]
default = ["std", "text_layout"]
Expand Down
17 changes: 13 additions & 4 deletions azulc/src/font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,19 @@ pub fn load_system_font(id: &str, fc_cache: &FcFontCache) -> Option<(U8Vec, i32)

for pattern in patterns {
if let Some(FcFontPath { path, font_index }) = fc_cache.query(&pattern) {
use std::fs;
use std::path::Path;
if let Ok(bytes) = fs::read(Path::new(path)) {
return Some((bytes.into(), *font_index as i32));
if path.starts_with("base64:") {
use base64::{engine::general_purpose::URL_SAFE, Engine as _};
let base64_str = &path[7..];
let decoded = URL_SAFE.decode(base64_str).ok().map(|s| (s.into(), *font_index as i32));
if let Some(s) = decoded {
return Some(s);
}
} else {
use std::fs;
use std::path::Path;
if let Ok(bytes) = fs::read(Path::new(path)) {
return Some((bytes.into(), *font_index as i32));
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions azulc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use azul_core::{
IdNamespace, LoadFontFn,
Epoch, RendererResources,
ImageCache, GlTextureCache,
DpiScaleFactor,
},
display_list::{
SolvedLayout,
Expand Down Expand Up @@ -287,6 +288,7 @@ fn solve_layout(
&fc_cache,
&callbacks,
renderer_resources,
DpiScaleFactor { inner: azul_css::FloatValue::new(fake_window_state.size.dpi as f32 / 96.0) }
);

solved_layout.layout_results.remove(0)
Expand Down
94 changes: 63 additions & 31 deletions azulc/src/xml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,34 @@ pub fn domxml_from_file<I: AsRef<Path>>(file_path: I, component_map: &mut XmlCom
/// )
/// ```
#[cfg(feature = "xml")]
pub fn parse_xml_string(xml: &str) -> Result<XmlNodeVec, XmlError> {

use xmlparser::Token::*;
use xmlparser::ElementEnd::*;
pub fn parse_xml_string(xml: &str) -> Result<Vec<XmlNode>, XmlError> {
use self::XmlParseError::*;
use xmlparser::{
ElementEnd::*,
Token::*,
Tokenizer,
};

let mut root_node = XmlNode::default();

// Search for "<?xml" and "?>" tags and delete them from the XML
let mut xml = xml.trim();
if xml.starts_with("<?") {
let pos = xml
.find("?>")
.ok_or(XmlError::MalformedHierarchy("<?xml".into(), "?>".into()))?;
xml = &xml[(pos + 2)..];
}

// Delete <!doctype if necessary
let mut xml = xml.trim();
if xml.starts_with("<!") {
let pos = xml
.find(">")
.ok_or(XmlError::MalformedHierarchy("<!doctype".into(), ">".into()))?;
xml = &xml[(pos + 1)..];
}

let tokenizer = Tokenizer::from_fragment(xml, 0..xml.len());

// In order to insert where the item is, let's say
Expand All @@ -104,63 +125,74 @@ pub fn parse_xml_string(xml: &str) -> Result<XmlNodeVec, XmlError> {
let mut current_hierarchy: Vec<usize> = Vec::new();

for token in tokenizer {

let token = token
.map_err(|e| XmlError::ParserError(translate_xmlparser_error(e)))?;

let token = token.map_err(|e| XmlError::ParserError(translate_xmlparser_error(e)))?;
match token {
ElementStart { local, .. } => {
if let Some(current_parent) = get_item(&current_hierarchy, &mut root_node) {
let children_len = current_parent.children.as_ref().len();
let children_len = current_parent.children.len();
current_parent.children.push(XmlNode {
node_type: normalize_casing(local.as_str()).into(),
attributes: Vec::new().into(),
node_type: local.to_string().into(),
attributes: StringPairVec::new(),
children: Vec::new().into(),
text: None.into(),
});
current_hierarchy.push(children_len);
}
},
}
ElementEnd { end: Empty, .. } => {
current_hierarchy.pop();
},
ElementEnd { end: Close(_, close_value), .. } => {
let close_value = normalize_casing(close_value.as_str());
if let Some(last) = get_item(&current_hierarchy, &mut root_node) {
}
ElementEnd {
end: Close(_, close_value),
..
} => {
let i = get_item(&current_hierarchy, &mut root_node);
if let Some(last) = i {
if last.node_type.as_str() != close_value.as_str() {
return Err(XmlError::MalformedHierarchy(close_value.into(), last.node_type.clone().into()));
return Err(XmlError::MalformedHierarchy(
close_value.to_string().into(),
last.node_type.clone(),
));
}
}
current_hierarchy.pop();
},
}
Attribute { local, value, .. } => {
if let Some(last) = get_item(&current_hierarchy, &mut root_node) {
// NOTE: Only lowercase the key ("local"), not the value!
last.attributes.insert_kv(normalize_casing(local.as_str()), value.as_str().to_string());
last.attributes
.push(azul_core::window::AzStringPair {
key: local.to_string().into(),
value: value.as_str().to_string().into(),
});
}
},
}
Text { text } => {
if let Some(last) = get_item(&current_hierarchy, &mut root_node) {
if let Some(s) = last.text.as_mut() {
let mut s_copy = s.as_str().to_owned();
s_copy.push_str(text.as_str());
*s = s_copy.into();
}
if last.text.is_none() {
last.text = Some(AzString::from(text.as_str())).into();
let text = text.trim();
if !text.is_empty() {
if let Some(last) = get_item(&current_hierarchy, &mut root_node) {
if let Some(s) = last.text.as_mut() {
let mut newstr = s.as_str().to_string();
newstr.push_str(text);
*s = newstr.into();
}
if last.text.is_none() {
last.text = Some(text.to_string().into()).into();
}
}
}
}
_ => { },
_ => {}
}
}

Ok(root_node.children)
Ok(root_node.children.into())
}


#[cfg(feature = "xml")]
pub fn parse_xml(s: &str) -> Result<Xml, XmlError> {
Ok(Xml { root: parse_xml_string(s)? })
Ok(Xml { root: parse_xml_string(s)?.into() })
}

#[cfg(not(feature = "xml"))]
Expand Down

0 comments on commit 9078ae5

Please sign in to comment.