Use xml-rs instead of rcdom for Android font list parsing

This commit is contained in:
Simon Sapin 2019-10-23 14:55:23 +02:00 committed by Anthony Ramine
parent e26530341b
commit 4f8cfd9b48
5 changed files with 125 additions and 128 deletions

2
Cargo.lock generated
View file

@ -1532,7 +1532,7 @@ dependencies = [
"unicode-script", "unicode-script",
"webrender_api", "webrender_api",
"xi-unicode", "xi-unicode",
"xml5ever", "xml-rs",
] ]
[[package]] [[package]]

View file

@ -56,7 +56,7 @@ servo_allocator = {path = "../allocator"}
servo-fontconfig = "0.4" servo-fontconfig = "0.4"
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(target_os = "android")'.dependencies]
xml5ever = {version = "0.15"} xml-rs = "0.8"
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.9" dwrote = "0.9"

View file

@ -2,17 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use super::xml::{Attribute, Node};
use crate::text::util::is_cjk; use crate::text::util::is_cjk;
use std::cell::RefCell;
use std::fs::File;
use std::io::{self, Read};
use std::path::Path; use std::path::Path;
use ucd::{Codepoint, UnicodeBlock}; use ucd::{Codepoint, UnicodeBlock};
use xml5ever::driver::parse_document;
use xml5ever::rcdom::*;
use xml5ever::rcdom::{Node, RcDom};
use xml5ever::tendril::TendrilSink;
use xml5ever::Attribute;
lazy_static! { lazy_static! {
static ref FONT_LIST: FontList = FontList::new(); static ref FONT_LIST: FontList = FontList::new();
@ -157,51 +150,36 @@ impl FontList {
// Creates a new FontList from a path to the font mapping xml file. // Creates a new FontList from a path to the font mapping xml file.
fn from_path(path: &str) -> Option<FontList> { fn from_path(path: &str) -> Option<FontList> {
let xml = match Self::load_file(path) { let bytes = std::fs::read(path).ok()?;
Ok(xml) => xml, let nodes = super::xml::parse(&bytes).ok()?;
_ => {
return None;
},
};
let dom: RcDom = parse_document(RcDom::default(), Default::default()).one(xml);
let doc = &dom.document;
// find familyset root node // find familyset root node
let children = doc.children.borrow(); let familyset = nodes.iter().find_map(|e| match e {
let familyset = children.iter().find(|child| match child.data { Node::Element { name, children, .. } if name.local_name == "familyset" => {
NodeData::Element { ref name, .. } => &*name.local == "familyset", Some(children)
_ => false,
});
let familyset = match familyset {
Some(node) => node,
_ => {
return None;
}, },
}; _ => None,
})?;
// Parse familyset node // Parse familyset node
let mut families = Vec::new(); let mut families = Vec::new();
let mut aliases = Vec::new(); let mut aliases = Vec::new();
for node in familyset.children.borrow().iter() { for node in familyset {
match node.data { if let Node::Element {
NodeData::Element { name,
ref name, attributes,
ref attrs, children,
.. } = node
} => { {
if &*name.local == "family" { if name.local_name == "family" {
Self::parse_family(&node, attrs, &mut families); Self::parse_family(children, attributes, &mut families);
} else if &*name.local == "alias" { } else if name.local_name == "alias" {
// aliases come after the fonts they reference. --> // aliases come after the fonts they reference. -->
if !families.is_empty() { if !families.is_empty() {
Self::parse_alias(attrs, &mut aliases); Self::parse_alias(attributes, &mut aliases);
}
} }
}, }
_ => {},
} }
} }
@ -253,14 +231,6 @@ impl FontList {
self.aliases.iter().find(|f| f.from == name) self.aliases.iter().find(|f| f.from == name)
} }
fn load_file(path: &str) -> Result<String, io::Error> {
let mut file = File::open(path)?;
let mut content = String::new();
file.read_to_string(&mut content)?;
Ok(content)
}
// Parse family and font file names // Parse family and font file names
// Example: // Example:
// <family name="sans-serif"> // <family name="sans-serif">
@ -270,40 +240,35 @@ impl FontList {
// <font weight="300" style="italic">Roboto-LightItalic.ttf</font> // <font weight="300" style="italic">Roboto-LightItalic.ttf</font>
// <font weight="400" style="normal">Roboto-Regular.ttf</font> // <font weight="400" style="normal">Roboto-Regular.ttf</font>
// </family> // </family>
fn parse_family(familyset: &Node, attrs: &RefCell<Vec<Attribute>>, out: &mut Vec<FontFamily>) { fn parse_family(familyset: &[Node], attrs: &[Attribute], out: &mut Vec<FontFamily>) {
// Fallback to old Android API v17 xml format if required // Fallback to old Android API v17 xml format if required
let using_api_17 = familyset let using_api_17 = familyset.iter().any(|node| match node {
.children Node::Element { name, .. } => name.local_name == "nameset",
.borrow() _ => false,
.iter() });
.any(|node| match node.data {
NodeData::Element { ref name, .. } => &*name.local == "nameset",
_ => false,
});
if using_api_17 { if using_api_17 {
Self::parse_family_v17(familyset, out); Self::parse_family_v17(familyset, out);
return; return;
} }
// Parse family name // Parse family name
let name = match Self::find_attrib("name", attrs) { let name = if let Some(name) = Self::find_attrib("name", attrs) {
Some(name) => name, name
_ => { } else {
return; return;
},
}; };
let mut fonts = Vec::new(); let mut fonts = Vec::new();
// Parse font variants // Parse font variants
for node in familyset.children.borrow().iter() { for node in familyset {
match node.data { match node {
NodeData::Element { Node::Element {
ref name, name,
ref attrs, attributes,
.. children,
} => { } => {
if &*name.local == "font" { if name.local_name == "font" {
FontList::parse_font(&node, attrs, &mut fonts); FontList::parse_font(&children, attributes, &mut fonts);
} }
}, },
_ => {}, _ => {},
@ -333,19 +298,16 @@ impl FontList {
// <file>Roboto-BoldItalic.ttf</file> // <file>Roboto-BoldItalic.ttf</file>
// </fileset> // </fileset>
// </family> // </family>
fn parse_family_v17(familyset: &Node, out: &mut Vec<FontFamily>) { fn parse_family_v17(familyset: &[Node], out: &mut Vec<FontFamily>) {
let mut nameset = Vec::new(); let mut nameset = Vec::new();
let mut fileset = Vec::new(); let mut fileset = Vec::new();
for node in familyset.children.borrow().iter() { for node in familyset {
match node.data { if let Node::Element { name, children, .. } = node {
NodeData::Element { ref name, .. } => { if name.local_name == "nameset" {
if &*name.local == "nameset" { Self::collect_contents_with_tag(children, "name", &mut nameset);
Self::collect_contents_with_tag(node, "name", &mut nameset); } else if name.local_name == "fileset" {
} else if &*name.local == "fileset" { Self::collect_contents_with_tag(children, "file", &mut fileset);
Self::collect_contents_with_tag(node, "file", &mut fileset); }
}
},
_ => {},
} }
} }
@ -370,22 +332,17 @@ impl FontList {
// Example: // Example:
// <font weight="100" style="normal">Roboto-Thin.ttf</font> // <font weight="100" style="normal">Roboto-Thin.ttf</font>
fn parse_font(node: &Node, attrs: &RefCell<Vec<Attribute>>, out: &mut Vec<Font>) { fn parse_font(nodes: &[Node], attrs: &[Attribute], out: &mut Vec<Font>) {
// Parse font filename // Parse font filename
let filename = match Self::text_content(node) { if let Some(filename) = Self::text_content(nodes) {
Some(filename) => filename, // Parse font weight
_ => { let weight = Self::find_attrib("weight", attrs).and_then(|w| w.parse().ok());
return;
},
};
// Parse font weight out.push(Font {
let weight = Self::find_attrib("weight", attrs).and_then(|w| w.parse().ok()); filename: filename,
weight: weight,
out.push(Font { })
filename: filename, }
weight: weight,
})
} }
// Example: // Example:
@ -397,7 +354,7 @@ impl FontList {
// <alias name="helvetica" to="sans-serif" /> // <alias name="helvetica" to="sans-serif" />
// <alias name="tahoma" to="sans-serif" /> // <alias name="tahoma" to="sans-serif" />
// <alias name="verdana" to="sans-serif" /> // <alias name="verdana" to="sans-serif" />
fn parse_alias(attrs: &RefCell<Vec<Attribute>>, out: &mut Vec<FontAlias>) { fn parse_alias(attrs: &[Attribute], out: &mut Vec<FontAlias>) {
// Parse alias name and referenced font // Parse alias name and referenced font
let from = match Self::find_attrib("name", attrs) { let from = match Self::find_attrib("name", attrs) {
Some(from) => from, Some(from) => from,
@ -424,39 +381,28 @@ impl FontList {
}) })
} }
fn find_attrib(name: &str, attrs: &RefCell<Vec<Attribute>>) -> Option<String> { fn find_attrib(name: &str, attrs: &[Attribute]) -> Option<String> {
attrs attrs
.borrow()
.iter() .iter()
.find(|attr| &*attr.name.local == name) .find(|attr| attr.name.local_name == name)
.map(|s| String::from(&s.value)) .map(|attr| attr.value.clone())
} }
fn text_content(node: &Node) -> Option<String> { fn text_content(nodes: &[Node]) -> Option<String> {
node.children nodes.get(0).and_then(|child| match child {
.borrow() Node::Text(contents) => Some(contents.clone()),
.get(0) Node::Element { .. } => None,
.and_then(|child| match child.data { })
NodeData::Text { ref contents } => {
let mut result = String::new();
result.push_str(&contents.borrow());
Some(result)
},
_ => None,
})
} }
fn collect_contents_with_tag(node: &Node, tag: &str, out: &mut Vec<String>) { fn collect_contents_with_tag(nodes: &[Node], tag: &str, out: &mut Vec<String>) {
for child in node.children.borrow().iter() { for node in nodes {
match child.data { if let Node::Element { name, children, .. } = node {
NodeData::Element { ref name, .. } => { if name.local_name == tag {
if &*name.local == tag { if let Some(content) = Self::text_content(children) {
if let Some(content) = Self::text_content(child) { out.push(content);
out.push(content);
}
} }
}, }
_ => {},
} }
} }
} }

View file

@ -0,0 +1,50 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use xml::reader::XmlEvent::*;
pub(super) use xml::attribute::OwnedAttribute as Attribute;
pub(super) enum Node {
Element {
name: xml::name::OwnedName,
attributes: Vec<xml::attribute::OwnedAttribute>,
children: Vec<Node>,
},
Text(String),
}
pub(super) fn parse(bytes: &[u8]) -> xml::reader::Result<Vec<Node>> {
let mut stack = Vec::new();
let mut nodes = Vec::new();
for result in xml::EventReader::new(&*bytes) {
match result? {
StartElement {
name, attributes, ..
} => {
stack.push((name, attributes, nodes));
nodes = Vec::new();
},
EndElement { .. } => {
let (name, attributes, mut parent_nodes) = stack.pop().unwrap();
parent_nodes.push(Node::Element {
name,
attributes,
children: nodes,
});
nodes = parent_nodes;
},
CData(s) | Characters(s) | Whitespace(s) => {
if let Some(Node::Text(text)) = nodes.last_mut() {
text.push_str(&s)
} else {
nodes.push(Node::Text(s))
}
},
StartDocument { .. } | EndDocument | ProcessingInstruction { .. } | Comment(..) => {},
}
}
assert!(stack.is_empty());
Ok(nodes)
}

View file

@ -36,6 +36,7 @@ mod freetype {
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
mod android { mod android {
pub mod font_list; pub mod font_list;
mod xml;
} }
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
pub use self::android::font_list; pub use self::android::font_list;