aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorImanol Fernandez <mortimergoro@gmail.com>2017-06-02 18:40:08 +0200
committerImanol Fernandez <mortimergoro@gmail.com>2017-06-02 18:40:08 +0200
commit19fbec9d546a31af072b9552eecc0541dc7b36a8 (patch)
tree7ff2f560daebafc768be94939ed52d69b54bc992
parentfa158a78b6f976156a93bcccae3fdd27046faf50 (diff)
downloadservo-19fbec9d546a31af072b9552eecc0541dc7b36a8.tar.gz
servo-19fbec9d546a31af072b9552eecc0541dc7b36a8.zip
Ged rid of libfontconfig in Android. Query available fonts from Android system font configuration files.
-rw-r--r--Cargo.lock1
-rw-r--r--components/gfx/Cargo.toml5
-rw-r--r--components/gfx/lib.rs4
-rw-r--r--components/gfx/platform/freetype/android/font_list.rs481
-rw-r--r--components/gfx/platform/freetype/font_list.rs8
-rw-r--r--components/gfx/platform/mod.rs8
-rw-r--r--ports/servo/main.rs2
7 files changed, 498 insertions, 11 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 4fb92f7ec9f..fce24203be9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1055,6 +1055,7 @@ dependencies = [
"unicode-script 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"webrender_traits 0.39.0 (git+https://github.com/servo/webrender)",
"xi-unicode 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "xml5ever 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
diff --git a/components/gfx/Cargo.toml b/components/gfx/Cargo.toml
index dfbe6ab582b..78b7f958650 100644
--- a/components/gfx/Cargo.toml
+++ b/components/gfx/Cargo.toml
@@ -50,8 +50,13 @@ core-text = "4.0"
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
freetype = "0.2"
+
+[target.'cfg(target_os = "linux")'.dependencies]
servo-fontconfig = "0.2.1"
+[target.'cfg(target_os = "android")'.dependencies]
+xml5ever = {version = "0.7", features = ["unstable"]}
+
[target.'cfg(any(target_feature = "sse2", target_feature = "neon"))'.dependencies]
simd = "0.2.0"
diff --git a/components/gfx/lib.rs b/components/gfx/lib.rs
index b7e23df0d83..3f356bb0cea 100644
--- a/components/gfx/lib.rs
+++ b/components/gfx/lib.rs
@@ -33,7 +33,7 @@ extern crate bitflags;
extern crate euclid;
extern crate fnv;
-#[cfg(any(target_os = "linux", target_os = "android"))]
+#[cfg(target_os = "linux")]
extern crate fontconfig;
extern crate fontsan;
#[cfg(any(target_os = "linux", target_os = "android"))]
@@ -73,6 +73,8 @@ extern crate unicode_bidi;
extern crate unicode_script;
extern crate webrender_traits;
extern crate xi_unicode;
+#[cfg(target_os = "android")]
+extern crate xml5ever;
#[deny(unsafe_code)]
pub mod display_list;
diff --git a/components/gfx/platform/freetype/android/font_list.rs b/components/gfx/platform/freetype/android/font_list.rs
new file mode 100644
index 00000000000..358b9a2104b
--- /dev/null
+++ b/components/gfx/platform/freetype/android/font_list.rs
@@ -0,0 +1,481 @@
+/* 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 http://mozilla.org/MPL/2.0/. */
+
+use std::cell::RefCell;
+use std::fs::File;
+use std::io::{self, Read};
+use std::path::Path;
+use xml5ever::Attribute;
+use xml5ever::driver::parse_document;
+use xml5ever::rcdom::*;
+use xml5ever::rcdom::{Node, RcDom};
+use xml5ever::tendril::TendrilSink;
+
+lazy_static! {
+ static ref FONT_LIST: FontList = FontList::new();
+}
+
+// Android doesn't provide an API to query system fonts until Android O:
+// https://developer.android.com/reference/android/text/FontConfig.html
+// System font configuration files must be parsed until Android O version is set as the minimum target.
+// Android uses XML files to handle font mapping configurations.
+// On Android API 21+ font mappings are loaded from /etc/fonts.xml.
+// Each entry consists of a family with various font names, or a font alias.
+// Example:
+// <familyset>
+// <!-- first font is default -->
+// <family name="sans-serif">
+// <font weight="100" style="normal">Roboto-Thin.ttf</font>
+// <font weight="100" style="italic">Roboto-ThinItalic.ttf</font>
+// <font weight="300" style="normal">Roboto-Light.ttf</font>
+// <font weight="300" style="italic">Roboto-LightItalic.ttf</font>
+// <font weight="400" style="normal">Roboto-Regular.ttf</font>
+// <font weight="400" style="italic">Roboto-Italic.ttf</font>
+// <font weight="500" style="normal">Roboto-Medium.ttf</font>
+// <font weight="500" style="italic">Roboto-MediumItalic.ttf</font>
+// <font weight="900" style="normal">Roboto-Black.ttf</font>
+// <font weight="900" style="italic">Roboto-BlackItalic.ttf</font>
+// <font weight="700" style="normal">Roboto-Bold.ttf</font>
+// <font weight="700" style="italic">Roboto-BoldItalic.ttf</font>
+// </family>//
+
+// <!-- Note that aliases must come after the fonts they reference. -->
+// <alias name="sans-serif-thin" to="sans-serif" weight="100" />
+// <alias name="sans-serif-light" to="sans-serif" weight="300" />
+// <alias name="sans-serif-medium" to="sans-serif" weight="500" />
+// <alias name="sans-serif-black" to="sans-serif" weight="900" />
+// <alias name="arial" to="sans-serif" />
+// <alias name="helvetica" to="sans-serif" />
+// <alias name="tahoma" to="sans-serif" />
+// <alias name="verdana" to="sans-serif" />
+
+// <family name="sans-serif-condensed">
+// <font weight="300" style="normal">RobotoCondensed-Light.ttf</font>
+// <font weight="300" style="italic">RobotoCondensed-LightItalic.ttf</font>
+// <font weight="400" style="normal">RobotoCondensed-Regular.ttf</font>
+// <font weight="400" style="italic">RobotoCondensed-Italic.ttf</font>
+// <font weight="700" style="normal">RobotoCondensed-Bold.ttf</font>
+// <font weight="700" style="italic">RobotoCondensed-BoldItalic.ttf</font>
+// </family>
+// <alias name="sans-serif-condensed-light" to="sans-serif-condensed" weight="300" />
+// </familyset>
+// On Android API 17-20 font mappings are loaded from /system/etc/system_fonts.xml
+// Each entry consists of a family with a nameset and a fileset.
+// Example:
+// <familyset>
+// <family>
+// <nameset>
+// <name>sans-serif</name>
+// <name>arial</name>
+// <name>helvetica</name>
+// <name>tahoma</name>
+// <name>verdana</name>
+// </nameset>
+// <fileset>
+// <file>Roboto-Regular.ttf</file>
+// <file>Roboto-Bold.ttf</file>
+// <file>Roboto-Italic.ttf</file>
+// <file>Roboto-BoldItalic.ttf</file>
+// </fileset>
+// </family>//
+
+// <family>
+// <nameset>
+// <name>sans-serif-light</name>
+// </nameset>
+// <fileset>
+// <file>Roboto-Light.ttf</file>
+// <file>Roboto-LightItalic.ttf</file>
+// </fileset>
+// </family>//
+
+// <family>
+// <nameset>
+// <name>sans-serif-thin</name>
+// </nameset>
+// <fileset>
+// <file>Roboto-Thin.ttf</file>
+// <file>Roboto-ThinItalic.ttf</file>
+// </fileset>
+// </family>
+// </familyset>
+
+struct Font {
+ filename: String,
+ weight: Option<i32>,
+}
+
+struct FontFamily {
+ name: String,
+ fonts: Vec<Font>,
+}
+
+struct FontAlias {
+ from: String,
+ to: String,
+ weight: Option<i32>
+}
+
+struct FontList {
+ families: Vec<FontFamily>,
+ aliases: Vec<FontAlias>
+}
+
+impl FontList {
+ fn new() -> FontList {
+ // Possible paths containing the font mapping xml file.
+ let paths = [
+ "/etc/fonts.xml",
+ "/system/etc/system_fonts.xml"
+ ];
+
+ // Try to load and parse paths until one of them success.
+ let mut result = None;
+ paths.iter().all(|path| {
+ result = Self::from_path(path);
+ !result.is_some()
+ });
+
+ match result {
+ Some(result) => result,
+ // If no xml mapping file is found fallback to some default
+ // fonts expected to be on all Android devices.
+ None => FontList {
+ families: Self::fallback_font_families(),
+ aliases: Vec::new(),
+ }
+ }
+ }
+
+ // 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;
+
+ // 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; }
+ };
+
+ // 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);
+ }
+ }
+ },
+ _=> {}
+ }
+ }
+
+ Some(FontList {
+ families: families,
+ aliases: aliases
+ })
+ }
+
+ // Fonts expected to exist in Android devices.
+ // Only used in the unlikely case where no font xml mapping files are found.
+ fn fallback_font_families() -> Vec<FontFamily> {
+ let alternatives = [
+ ("san-serif", "Roboto-Regular.ttf"),
+ ("Droid Sans", "DroidSans.ttf"),
+ ];
+
+ alternatives.iter().filter(|item| {
+ Path::new(&Self::font_absolute_path(item.1)).exists()
+ }).map(|item| {
+ FontFamily {
+ name: item.0.into(),
+ fonts: vec![Font {
+ filename: item.1.into(),
+ weight: None,
+ }]
+ }
+ }). collect()
+ }
+
+ // All Android fonts are located in /system/fonts
+ fn font_absolute_path(filename: &str) -> String {
+ format!("/system/fonts/{}", filename)
+ }
+
+ fn find_family(&self, name: &str) -> Option<&FontFamily>{
+ self.families.iter().find(|f| f.name == name)
+ }
+
+ fn find_alias(&self, name: &str) -> Option<&FontAlias>{
+ self.aliases.iter().find(|f| f.from == name)
+ }
+
+
+ fn load_file(path: &str) -> Result<String, io::Error> {
+ let mut file = try!(File::open(path));
+ let mut content = String::new();
+ try!(file.read_to_string(&mut content));
+
+ Ok(content)
+ }
+
+ // Parse family and font file names
+ // Example:
+ // <family name="sans-serif">
+ // <font weight="100" style="normal">Roboto-Thin.ttf</font>
+ // <font weight="100" style="italic">Roboto-ThinItalic.ttf</font>
+ // <font weight="300" style="normal">Roboto-Light.ttf</font>
+ // <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>) {
+ // 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,
+ }
+ });
+ 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 mut fonts = Vec::new();
+ // Parse font variants
+ for node in familyset.children.borrow().iter() {
+ match node.data {
+ NodeData::Element { ref name, ref attrs, .. } => {
+ if &*name.local == "font" {
+ FontList::parse_font(&node, attrs, &mut fonts);
+ }
+ },
+ _=> {}
+ }
+ }
+
+ out.push(FontFamily {
+ name: name,
+ fonts: fonts
+ });
+ }
+
+ // Parse family and font file names for Androi API < 21
+ // Example:
+ // <family>
+ // <nameset>
+ // <name>sans-serif</name>
+ // <name>arial</name>
+ // <name>helvetica</name>
+ // <name>tahoma</name>
+ // <name>verdana</name>
+ // </nameset>
+ // <fileset>
+ // <file>Roboto-Regular.ttf</file>
+ // <file>Roboto-Bold.ttf</file>
+ // <file>Roboto-Italic.ttf</file>
+ // <file>Roboto-BoldItalic.ttf</file>
+ // </fileset>
+ // </family>
+ 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);
+ }
+ },
+ _=> {}
+ }
+ }
+
+ // Create a families for each variation
+ for name in nameset {
+ let fonts: Vec<Font> = fileset.iter().map(|f| Font {
+ filename: f.clone(),
+ weight: None,
+ }).collect();
+
+ if !fonts.is_empty() {
+ out.push(FontFamily {
+ name: name,
+ fonts: fonts
+ })
+ }
+ }
+ }
+
+ // Example:
+ // <font weight="100" style="normal">Roboto-Thin.ttf</font>
+ fn parse_font(node: &Node, attrs: &RefCell<Vec<Attribute>>, out:&mut Vec<Font>) {
+ // Parse font filename
+ let filename = match Self::text_content(node) {
+ Some(filename) => filename,
+ _ => { return; }
+ };
+
+ // Parse font weight
+ let weight = Self::find_attrib("weight", attrs).and_then(|w| w.parse().ok());
+
+ out.push(Font {
+ filename: filename,
+ weight: weight,
+ })
+ }
+
+ // Example:
+ // <alias name="sans-serif-thin" to="sans-serif" weight="100" />
+ // <alias name="sans-serif-light" to="sans-serif" weight="300" />
+ // <alias name="sans-serif-medium" to="sans-serif" weight="500" />
+ // <alias name="sans-serif-black" to="sans-serif" weight="900" />
+ // <alias name="arial" to="sans-serif" />
+ // <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>) {
+ // Parse alias name and referenced font
+ let from = match Self::find_attrib("name", attrs) {
+ Some(from) => from,
+ _ => { return; },
+ };
+
+ // Parse referenced font
+ let to = match Self::find_attrib("to", attrs) {
+ Some(to) => to,
+ _ => { return; },
+ };
+
+ // Parse optional weight filter
+ let weight = Self::find_attrib("weight", attrs).and_then(|w| w.parse().ok());
+
+ out.push(FontAlias {
+ from: from,
+ to: to,
+ weight: weight,
+ })
+ }
+
+ fn find_attrib(name: &str, attrs: &RefCell<Vec<Attribute>>) -> Option<String> {
+ attrs.borrow().iter().find(|attr| &*attr.name.local == name).map(|s| String::from(&s.value))
+ }
+
+ 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 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);
+ }
+ }
+ },
+ _=> {}
+ }
+ }
+ }
+}
+
+// Functions used by FontCacheThread
+pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) {
+ for family in &FONT_LIST.families {
+ callback(family.name.clone());
+ }
+ for alias in &FONT_LIST.aliases {
+ callback(alias.from.clone());
+ }
+}
+
+pub fn for_each_variation<F>(family_name: &str, mut callback: F)
+ where F: FnMut(String)
+{
+ println!("Variatioooon {:?}", family_name);
+ if let Some(family) = FONT_LIST.find_family(family_name) {
+ for font in &family.fonts {
+ callback(FontList::font_absolute_path(&font.filename));
+ }
+ return;
+ }
+
+ if let Some(alias) = FONT_LIST.find_alias(family_name) {
+ if let Some(family) = FONT_LIST.find_family(&alias.to) {
+ for font in &family.fonts {
+ match (alias.weight, font.weight) {
+ (None, _) => callback(FontList::font_absolute_path(&font.filename)),
+ (Some(w1), Some(w2)) => {
+ if w1 == w2 {
+ callback(FontList::font_absolute_path(&font.filename))
+ }
+ },
+ _ => {}
+ }
+ }
+ }
+ }
+}
+
+pub fn system_default_family(generic_name: &str) -> Option<String> {
+ if let Some(family) = FONT_LIST.find_family(&generic_name) {
+ Some(family.name.clone())
+ } else if let Some(alias) = FONT_LIST.find_alias(&generic_name) {
+ Some(alias.from.clone())
+ } else {
+ // First font defined in the fonts.xml is the default on Android.
+ FONT_LIST.families.get(0).map(|family| family.name.clone())
+ }
+}
+
+pub fn last_resort_font_families() -> Vec<String> {
+ vec!(
+ "sans-serif".to_owned(),
+ "Droid Sans".to_owned(),
+ "serif".to_owned(),
+ )
+}
+
+pub static SANS_SERIF_FONT_FAMILY: &'static str = "sans-serif";
diff --git a/components/gfx/platform/freetype/font_list.rs b/components/gfx/platform/freetype/font_list.rs
index 1b87a18221d..8f2898ae680 100644
--- a/components/gfx/platform/freetype/font_list.rs
+++ b/components/gfx/platform/freetype/font_list.rs
@@ -141,11 +141,6 @@ pub fn last_resort_font_families() -> Vec<String> {
)
}
-#[cfg(target_os = "android")]
-pub fn last_resort_font_families() -> Vec<String> {
- vec!("Roboto".to_owned())
-}
-
#[cfg(target_os = "windows")]
pub fn last_resort_font_families() -> Vec<String> {
vec!(
@@ -153,9 +148,6 @@ pub fn last_resort_font_families() -> Vec<String> {
)
}
-#[cfg(target_os = "android")]
-pub static SANS_SERIF_FONT_FAMILY: &'static str = "Roboto";
-
#[cfg(target_os = "linux")]
pub static SANS_SERIF_FONT_FAMILY: &'static str = "DejaVu Sans";
diff --git a/components/gfx/platform/mod.rs b/components/gfx/platform/mod.rs
index 9a505c14284..897fbe9e8d3 100644
--- a/components/gfx/platform/mod.rs
+++ b/components/gfx/platform/mod.rs
@@ -29,8 +29,14 @@ mod freetype {
pub mod font;
pub mod font_context;
- #[cfg(any(target_os = "linux", target_os = "android"))]
+ #[cfg(target_os = "linux")]
pub mod font_list;
+ #[cfg(target_os = "android")]
+ mod android {
+ pub mod font_list;
+ }
+ #[cfg(target_os = "android")]
+ pub use self::android::font_list;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod font_template;
diff --git a/ports/servo/main.rs b/ports/servo/main.rs
index bec8ffa8089..7d390988be0 100644
--- a/ports/servo/main.rs
+++ b/ports/servo/main.rs
@@ -204,7 +204,7 @@ impl app::NestedEventLoopListener for BrowserWrapper {
#[cfg(target_os = "android")]
fn setup_logging() {
// Piping logs from stdout/stderr to logcat happens in android_injected_glue.
- ::std::env::set_var("RUST_LOG", "debug");
+ ::std::env::set_var("RUST_LOG", "error");
unsafe { android_injected_glue::ffi::app_dummy() };
}