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",
"webrender_api",
"xi-unicode",
"xml5ever",
"xml-rs",
]
[[package]]

View file

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

View file

@ -2,17 +2,10 @@
* 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 super::xml::{Attribute, Node};
use crate::text::util::is_cjk;
use std::cell::RefCell;
use std::fs::File;
use std::io::{self, Read};
use std::path::Path;
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! {
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.
fn from_path(path: &str) -> Option<FontList> {
let xml = match Self::load_file(path) {
Ok(xml) => xml,
_ => {
return None;
},
};
let dom: RcDom = parse_document(RcDom::default(), Default::default()).one(xml);
let doc = &dom.document;
let bytes = std::fs::read(path).ok()?;
let nodes = super::xml::parse(&bytes).ok()?;
// find familyset root node
let children = doc.children.borrow();
let familyset = children.iter().find(|child| match child.data {
NodeData::Element { ref name, .. } => &*name.local == "familyset",
_ => false,
});
let familyset = match familyset {
Some(node) => node,
_ => {
return None;
let familyset = nodes.iter().find_map(|e| match e {
Node::Element { name, children, .. } if name.local_name == "familyset" => {
Some(children)
},
};
_ => None,
})?;
// Parse familyset node
let mut families = Vec::new();
let mut aliases = Vec::new();
for node in familyset.children.borrow().iter() {
match node.data {
NodeData::Element {
ref name,
ref attrs,
..
} => {
if &*name.local == "family" {
Self::parse_family(&node, attrs, &mut families);
} else if &*name.local == "alias" {
// aliases come after the fonts they reference. -->
if !families.is_empty() {
Self::parse_alias(attrs, &mut aliases);
}
for node in familyset {
if let Node::Element {
name,
attributes,
children,
} = node
{
if name.local_name == "family" {
Self::parse_family(children, attributes, &mut families);
} else if name.local_name == "alias" {
// aliases come after the fonts they reference. -->
if !families.is_empty() {
Self::parse_alias(attributes, &mut aliases);
}
},
_ => {},
}
}
}
@ -253,14 +231,6 @@ impl FontList {
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
// Example:
// <family name="sans-serif">
@ -270,40 +240,35 @@ impl FontList {
// <font weight="300" style="italic">Roboto-LightItalic.ttf</font>
// <font weight="400" style="normal">Roboto-Regular.ttf</font>
// </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
let using_api_17 = familyset
.children
.borrow()
.iter()
.any(|node| match node.data {
NodeData::Element { ref name, .. } => &*name.local == "nameset",
_ => false,
});
let using_api_17 = familyset.iter().any(|node| match node {
Node::Element { name, .. } => name.local_name == "nameset",
_ => false,
});
if using_api_17 {
Self::parse_family_v17(familyset, out);
return;
}
// Parse family name
let name = match Self::find_attrib("name", attrs) {
Some(name) => name,
_ => {
return;
},
let name = if let Some(name) = Self::find_attrib("name", attrs) {
name
} else {
return;
};
let mut fonts = Vec::new();
// Parse font variants
for node in familyset.children.borrow().iter() {
match node.data {
NodeData::Element {
ref name,
ref attrs,
..
for node in familyset {
match node {
Node::Element {
name,
attributes,
children,
} => {
if &*name.local == "font" {
FontList::parse_font(&node, attrs, &mut fonts);
if name.local_name == "font" {
FontList::parse_font(&children, attributes, &mut fonts);
}
},
_ => {},
@ -333,19 +298,16 @@ impl FontList {
// <file>Roboto-BoldItalic.ttf</file>
// </fileset>
// </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 fileset = Vec::new();
for node in familyset.children.borrow().iter() {
match node.data {
NodeData::Element { ref name, .. } => {
if &*name.local == "nameset" {
Self::collect_contents_with_tag(node, "name", &mut nameset);
} else if &*name.local == "fileset" {
Self::collect_contents_with_tag(node, "file", &mut fileset);
}
},
_ => {},
for node in familyset {
if let Node::Element { name, children, .. } = node {
if name.local_name == "nameset" {
Self::collect_contents_with_tag(children, "name", &mut nameset);
} else if name.local_name == "fileset" {
Self::collect_contents_with_tag(children, "file", &mut fileset);
}
}
}
@ -370,22 +332,17 @@ impl FontList {
// Example:
// <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
let filename = match Self::text_content(node) {
Some(filename) => filename,
_ => {
return;
},
};
if let Some(filename) = Self::text_content(nodes) {
// Parse font weight
let weight = Self::find_attrib("weight", attrs).and_then(|w| w.parse().ok());
// Parse font weight
let weight = Self::find_attrib("weight", attrs).and_then(|w| w.parse().ok());
out.push(Font {
filename: filename,
weight: weight,
})
out.push(Font {
filename: filename,
weight: weight,
})
}
}
// Example:
@ -397,7 +354,7 @@ impl FontList {
// <alias name="helvetica" to="sans-serif" />
// <alias name="tahoma" 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
let from = match Self::find_attrib("name", attrs) {
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
.borrow()
.iter()
.find(|attr| &*attr.name.local == name)
.map(|s| String::from(&s.value))
.find(|attr| attr.name.local_name == name)
.map(|attr| attr.value.clone())
}
fn text_content(node: &Node) -> Option<String> {
node.children
.borrow()
.get(0)
.and_then(|child| match child.data {
NodeData::Text { ref contents } => {
let mut result = String::new();
result.push_str(&contents.borrow());
Some(result)
},
_ => None,
})
fn text_content(nodes: &[Node]) -> Option<String> {
nodes.get(0).and_then(|child| match child {
Node::Text(contents) => Some(contents.clone()),
Node::Element { .. } => None,
})
}
fn collect_contents_with_tag(node: &Node, tag: &str, out: &mut Vec<String>) {
for child in node.children.borrow().iter() {
match child.data {
NodeData::Element { ref name, .. } => {
if &*name.local == tag {
if let Some(content) = Self::text_content(child) {
out.push(content);
}
fn collect_contents_with_tag(nodes: &[Node], tag: &str, out: &mut Vec<String>) {
for node in nodes {
if let Node::Element { name, children, .. } = node {
if name.local_name == tag {
if let Some(content) = Self::text_content(children) {
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")]
mod android {
pub mod font_list;
mod xml;
}
#[cfg(target_os = "android")]
pub use self::android::font_list;