Auto merge of #20506 - jonleighton:font-fallback, r=emilio,mbrubeck

Font fallback

This implements more complete support for font fallback, see #17267.

r? @glennw @mbrubeck

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/20506)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-05-19 09:53:29 -04:00 committed by GitHub
commit 77dcc678fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
83 changed files with 1379 additions and 462 deletions

7
Cargo.lock generated
View file

@ -1014,6 +1014,7 @@ dependencies = [
"style 0.0.1", "style 0.0.1",
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
"truetype 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", "truetype 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ucd 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-bidi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-bidi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-script 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-script 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"webrender_api 0.57.2 (git+https://github.com/servo/webrender)", "webrender_api 0.57.2 (git+https://github.com/servo/webrender)",
@ -3252,6 +3253,11 @@ name = "typeable"
version = "0.1.2" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ucd"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "uluru" name = "uluru"
version = "0.2.0" version = "0.2.0"
@ -4002,6 +4008,7 @@ dependencies = [
"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
"checksum truetype 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec30350633d6dac9dc1a625786b6cbe9150664be941aac2c35ad7199eab877" "checksum truetype 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec30350633d6dac9dc1a625786b6cbe9150664be941aac2c35ad7199eab877"
"checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"
"checksum ucd 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fe4fa6e588762366f1eb4991ce59ad1b93651d0b769dfb4e4d1c5c4b943d1159"
"checksum uluru 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "519130f0ea964ba540a9d8af1373738c2226f1d465eda07e61db29feb5479db9" "checksum uluru 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "519130f0ea964ba540a9d8af1373738c2226f1d465eda07e61db29feb5479db9"
"checksum unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a5906ca2b98c799f4b1ab4557b76367ebd6ae5ef14930ec841c74aed5f3764" "checksum unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a5906ca2b98c799f4b1ab4557b76367ebd6ae5ef14930ec841c74aed5f3764"
"checksum unicode-bidi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a6a2c4e3710edd365cd7e78383153ed739fa31af19f9172f72d3575060f5a43a" "checksum unicode-bidi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a6a2c4e3710edd365cd7e78383153ed739fa31af19f9172f72d3575060f5a43a"

View file

@ -42,6 +42,7 @@ unicode-bidi = {version = "0.3", features = ["with_serde"]}
unicode-script = {version = "0.2", features = ["harfbuzz"]} unicode-script = {version = "0.2", features = ["harfbuzz"]}
webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]} webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}
xi-unicode = "0.1.0" xi-unicode = "0.1.0"
ucd = "0.1.1"
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
byteorder = "1.0" byteorder = "1.0"

View file

@ -9,12 +9,14 @@ use font_template::FontTemplateDescriptor;
use ordered_float::NotNaN; use ordered_float::NotNaN;
use platform::font::{FontHandle, FontTable}; use platform::font::{FontHandle, FontTable};
use platform::font_context::FontContextHandle; use platform::font_context::FontContextHandle;
pub use platform::font_list::fallback_font_families;
use platform::font_template::FontTemplateData; use platform::font_template::FontTemplateData;
use servo_atoms::Atom; use servo_atoms::Atom;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::iter;
use std::rc::Rc; use std::rc::Rc;
use std::str; use std::str;
use std::sync::Arc; use std::sync::Arc;
@ -117,7 +119,7 @@ pub struct FontMetrics {
/// template at a particular size, with a particular font-variant-caps applied, etc. This contrasts /// template at a particular size, with a particular font-variant-caps applied, etc. This contrasts
/// with `FontTemplateDescriptor` in that the latter represents only the parameters inherent in the /// with `FontTemplateDescriptor` in that the latter represents only the parameters inherent in the
/// font data (weight, stretch, etc.). /// font data (weight, stretch, etc.).
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct FontDescriptor { pub struct FontDescriptor {
pub template_descriptor: FontTemplateDescriptor, pub template_descriptor: FontTemplateDescriptor,
pub variant: font_variant_caps::T, pub variant: font_variant_caps::T,
@ -329,6 +331,7 @@ pub type FontRef = Rc<RefCell<Font>>;
pub struct FontGroup { pub struct FontGroup {
descriptor: FontDescriptor, descriptor: FontDescriptor,
families: SmallVec<[FontGroupFamily; 8]>, families: SmallVec<[FontGroupFamily; 8]>,
last_matching_fallback: Option<FontRef>,
} }
impl FontGroup { impl FontGroup {
@ -337,10 +340,14 @@ impl FontGroup {
let families = let families =
style.font_family.0.iter() style.font_family.0.iter()
.map(|family| FontGroupFamily::new(descriptor.clone(), family.clone())) .map(|family| FontGroupFamily::new(descriptor.clone(), &family))
.collect(); .collect();
FontGroup { descriptor, families } FontGroup {
descriptor,
families,
last_matching_fallback: None,
}
} }
/// Finds the first font, or else the first fallback font, which contains a glyph for /// Finds the first font, or else the first fallback font, which contains a glyph for
@ -352,15 +359,35 @@ impl FontGroup {
mut font_context: &mut FontContext<S>, mut font_context: &mut FontContext<S>,
codepoint: char codepoint: char
) -> Option<FontRef> { ) -> Option<FontRef> {
self.find(&mut font_context, |font| font.borrow().has_glyph_for(codepoint)) let has_glyph = |font: &FontRef| font.borrow().has_glyph_for(codepoint);
.or_else(|| self.first(&mut font_context))
let font = self.find(&mut font_context, |font| has_glyph(font));
if font.is_some() {
return font
} }
if let Some(ref fallback) = self.last_matching_fallback {
if has_glyph(&fallback) {
return self.last_matching_fallback.clone()
}
}
let font = self.find_fallback(&mut font_context, Some(codepoint), has_glyph);
if font.is_some() {
self.last_matching_fallback = font.clone();
return font
}
self.first(&mut font_context)
}
/// Find the first available font in the group, or the first available fallback font.
pub fn first<S: FontSource>( pub fn first<S: FontSource>(
&mut self, &mut self,
mut font_context: &mut FontContext<S> mut font_context: &mut FontContext<S>
) -> Option<FontRef> { ) -> Option<FontRef> {
self.find(&mut font_context, |_| true) self.find(&mut font_context, |_| true)
.or_else(|| self.find_fallback(&mut font_context, None, |_| true))
} }
/// Find a font which returns true for `predicate`. This method mutates because we may need to /// Find a font which returns true for `predicate`. This method mutates because we may need to
@ -368,19 +395,42 @@ impl FontGroup {
fn find<S, P>( fn find<S, P>(
&mut self, &mut self,
mut font_context: &mut FontContext<S>, mut font_context: &mut FontContext<S>,
mut predicate: P predicate: P,
) -> Option<FontRef> ) -> Option<FontRef>
where where
S: FontSource, S: FontSource,
P: FnMut(&FontRef) -> bool P: FnMut(&FontRef) -> bool,
{ {
self.families.iter_mut() self.families.iter_mut()
.filter_map(|family| family.font(&mut font_context)) .filter_map(|family| family.font(&mut font_context))
.find(|f| predicate(f)) .find(predicate)
.or_else(|| { }
font_context.fallback_font(&self.descriptor)
.into_iter().find(predicate) /// Attempts to find a suitable fallback font which matches the `predicate`. The default
/// family (i.e. "serif") will be tried first, followed by platform-specific family names.
/// If a `codepoint` is provided, then its Unicode block may be used to refine the list of
/// family names which will be tried.
fn find_fallback<S, P>(
&mut self,
font_context: &mut FontContext<S>,
codepoint: Option<char>,
predicate: P,
) -> Option<FontRef>
where
S: FontSource,
P: FnMut(&FontRef) -> bool,
{
iter::once(FontFamilyDescriptor::default())
.chain(
fallback_font_families(codepoint).into_iter().map(|family| {
FontFamilyDescriptor::new(
FontFamilyName::from(family),
FontSearchScope::Local,
)
}) })
)
.filter_map(|family| font_context.font(&self.descriptor, &family))
.find(predicate)
} }
} }
@ -389,17 +439,22 @@ impl FontGroup {
/// only if actually needed. /// only if actually needed.
#[derive(Debug)] #[derive(Debug)]
struct FontGroupFamily { struct FontGroupFamily {
descriptor: FontDescriptor, font_descriptor: FontDescriptor,
family: SingleFontFamily, family_descriptor: FontFamilyDescriptor,
loaded: bool, loaded: bool,
font: Option<FontRef>, font: Option<FontRef>,
} }
impl FontGroupFamily { impl FontGroupFamily {
fn new(descriptor: FontDescriptor, family: SingleFontFamily) -> FontGroupFamily { fn new(font_descriptor: FontDescriptor, family: &SingleFontFamily) -> FontGroupFamily {
let family_descriptor = FontFamilyDescriptor::new(
FontFamilyName::from(family),
FontSearchScope::Any
);
FontGroupFamily { FontGroupFamily {
descriptor, font_descriptor,
family, family_descriptor,
loaded: false, loaded: false,
font: None, font: None,
} }
@ -410,7 +465,7 @@ impl FontGroupFamily {
/// subsequent calls. /// subsequent calls.
fn font<S: FontSource>(&mut self, font_context: &mut FontContext<S>) -> Option<FontRef> { fn font<S: FontSource>(&mut self, font_context: &mut FontContext<S>) -> Option<FontRef> {
if !self.loaded { if !self.loaded {
self.font = font_context.font(&self.descriptor, &self.family); self.font = font_context.font(&self.font_descriptor, &self.family_descriptor);
self.loaded = true; self.loaded = true;
} }
@ -451,3 +506,74 @@ pub fn get_and_reset_text_shaping_performance_counter() -> usize {
TEXT_SHAPING_PERFORMANCE_COUNTER.store(0, Ordering::SeqCst); TEXT_SHAPING_PERFORMANCE_COUNTER.store(0, Ordering::SeqCst);
value value
} }
/// The scope within which we will look for a font.
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum FontSearchScope {
/// All fonts will be searched, including those specified via `@font-face` rules.
Any,
/// Only local system fonts will be searched.
Local,
}
/// A font family name used in font selection.
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum FontFamilyName {
/// A specific name such as `"Arial"`
Specific(Atom),
/// A generic name such as `sans-serif`
Generic(Atom),
}
impl FontFamilyName {
pub fn name(&self) -> &str {
match *self {
FontFamilyName::Specific(ref name) => name,
FontFamilyName::Generic(ref name) => name,
}
}
}
impl<'a> From<&'a SingleFontFamily> for FontFamilyName {
fn from(other: &'a SingleFontFamily) -> FontFamilyName {
match *other {
SingleFontFamily::FamilyName(ref family_name) =>
FontFamilyName::Specific(family_name.name.clone()),
SingleFontFamily::Generic(ref generic_name) =>
FontFamilyName::Generic(generic_name.clone()),
}
}
}
impl<'a> From<&'a str> for FontFamilyName {
fn from(other: &'a str) -> FontFamilyName {
FontFamilyName::Specific(Atom::from(other))
}
}
/// The font family parameters for font selection.
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct FontFamilyDescriptor {
pub name: FontFamilyName,
pub scope: FontSearchScope,
}
impl FontFamilyDescriptor {
pub fn new(name: FontFamilyName, scope: FontSearchScope) -> FontFamilyDescriptor {
FontFamilyDescriptor { name, scope }
}
fn default() -> FontFamilyDescriptor {
FontFamilyDescriptor {
name: FontFamilyName::Generic(atom!("serif")),
scope: FontSearchScope::Local,
}
}
pub fn name(&self) -> &str {
self.name.name()
}
}

View file

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use app_units::Au; use app_units::Au;
use font::{FontFamilyDescriptor, FontFamilyName, FontSearchScope};
use font_context::FontSource; use font_context::FontSource;
use font_template::{FontTemplate, FontTemplateDescriptor}; use font_template::{FontTemplate, FontTemplateDescriptor};
use fontsan; use fontsan;
@ -13,7 +14,6 @@ use platform::font_context::FontContextHandle;
use platform::font_list::SANS_SERIF_FONT_FAMILY; use platform::font_list::SANS_SERIF_FONT_FAMILY;
use platform::font_list::for_each_available_family; use platform::font_list::for_each_available_family;
use platform::font_list::for_each_variation; use platform::font_list::for_each_variation;
use platform::font_list::last_resort_font_families;
use platform::font_list::system_default_family; use platform::font_list::system_default_family;
use platform::font_template::FontTemplateData; use platform::font_template::FontTemplateData;
use servo_atoms::Atom; use servo_atoms::Atom;
@ -24,7 +24,7 @@ use std::collections::HashMap;
use std::ops::Deref; use std::ops::Deref;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use style::font_face::{EffectiveSources, Source}; use style::font_face::{EffectiveSources, Source};
use style::values::computed::font::{SingleFontFamily, FamilyName}; use style::values::computed::font::FamilyName;
use webrender_api; use webrender_api;
/// A list of font templates that make up a given font family. /// A list of font templates that make up a given font family.
@ -32,7 +32,7 @@ pub struct FontTemplates {
templates: Vec<FontTemplate>, templates: Vec<FontTemplate>,
} }
#[derive(Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub struct FontTemplateInfo { pub struct FontTemplateInfo {
pub font_template: Arc<FontTemplateData>, pub font_template: Arc<FontTemplateData>,
pub font_key: webrender_api::FontKey, pub font_key: webrender_api::FontKey,
@ -103,8 +103,7 @@ impl FontTemplates {
/// Commands that the FontContext sends to the font cache thread. /// Commands that the FontContext sends to the font cache thread.
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub enum Command { pub enum Command {
GetFontTemplate(SingleFontFamily, FontTemplateDescriptor, IpcSender<Reply>), GetFontTemplate(FontTemplateDescriptor, FontFamilyDescriptor, IpcSender<Reply>),
GetLastResortFontTemplate(FontTemplateDescriptor, IpcSender<Reply>),
GetFontInstance(webrender_api::FontKey, Au, IpcSender<webrender_api::FontInstanceKey>), GetFontInstance(webrender_api::FontKey, Au, IpcSender<webrender_api::FontInstanceKey>),
AddWebFont(LowercaseString, EffectiveSources, IpcSender<()>), AddWebFont(LowercaseString, EffectiveSources, IpcSender<()>),
AddDownloadedWebFont(LowercaseString, ServoUrl, Vec<u8>, IpcSender<()>), AddDownloadedWebFont(LowercaseString, ServoUrl, Vec<u8>, IpcSender<()>),
@ -123,7 +122,7 @@ pub enum Reply {
struct FontCache { struct FontCache {
port: IpcReceiver<Command>, port: IpcReceiver<Command>,
channel_to_self: IpcSender<Command>, channel_to_self: IpcSender<Command>,
generic_fonts: HashMap<SingleFontFamily, LowercaseString>, generic_fonts: HashMap<FontFamilyName, LowercaseString>,
local_families: HashMap<LowercaseString, FontTemplates>, local_families: HashMap<LowercaseString, FontTemplates>,
web_families: HashMap<LowercaseString, FontTemplates>, web_families: HashMap<LowercaseString, FontTemplates>,
font_context: FontContextHandle, font_context: FontContextHandle,
@ -133,27 +132,28 @@ struct FontCache {
font_instances: HashMap<(webrender_api::FontKey, Au), webrender_api::FontInstanceKey>, font_instances: HashMap<(webrender_api::FontKey, Au), webrender_api::FontInstanceKey>,
} }
fn populate_generic_fonts() -> HashMap<SingleFontFamily, LowercaseString> { fn populate_generic_fonts() -> HashMap<FontFamilyName, LowercaseString> {
let mut generic_fonts = HashMap::with_capacity(5); let mut generic_fonts = HashMap::with_capacity(5);
append_map(&mut generic_fonts, SingleFontFamily::Generic(atom!("serif")), "Times New Roman"); append_map(&mut generic_fonts, "serif", "Times New Roman");
append_map(&mut generic_fonts, SingleFontFamily::Generic(atom!("sans-serif")), SANS_SERIF_FONT_FAMILY); append_map(&mut generic_fonts, "sans-serif", SANS_SERIF_FONT_FAMILY);
append_map(&mut generic_fonts, SingleFontFamily::Generic(atom!("cursive")), "Apple Chancery"); append_map(&mut generic_fonts, "cursive", "Apple Chancery");
append_map(&mut generic_fonts, SingleFontFamily::Generic(atom!("fantasy")), "Papyrus"); append_map(&mut generic_fonts, "fantasy", "Papyrus");
append_map(&mut generic_fonts, SingleFontFamily::Generic(atom!("monospace")), "Menlo"); append_map(&mut generic_fonts, "monospace", "Menlo");
fn append_map(generic_fonts: &mut HashMap<SingleFontFamily, LowercaseString>, fn append_map(
font_family: SingleFontFamily, generic_fonts: &mut HashMap<FontFamilyName, LowercaseString>,
mapped_name: &str) { generic_name: &str,
let family_name = { mapped_name: &str,
let opt_system_default = system_default_family(font_family.name()); ) {
match opt_system_default { let family_name = match system_default_family(generic_name) {
Some(system_default) => LowercaseString::new(&system_default), Some(system_default) => LowercaseString::new(&system_default),
None => LowercaseString::new(mapped_name) None => LowercaseString::new(mapped_name)
}
}; };
generic_fonts.insert(font_family, family_name); let generic_name = FontFamilyName::Generic(Atom::from(generic_name));
generic_fonts.insert(generic_name, family_name);
} }
@ -166,14 +166,10 @@ impl FontCache {
let msg = self.port.recv().unwrap(); let msg = self.port.recv().unwrap();
match msg { match msg {
Command::GetFontTemplate(family, descriptor, result) => { Command::GetFontTemplate(template_descriptor, family_descriptor, result) => {
let maybe_font_template = self.find_font_template(&family, &descriptor); let maybe_font_template = self.find_font_template(&template_descriptor, &family_descriptor);
let _ = result.send(Reply::GetFontTemplateReply(maybe_font_template)); let _ = result.send(Reply::GetFontTemplateReply(maybe_font_template));
} }
Command::GetLastResortFontTemplate(descriptor, result) => {
let font_template = self.last_resort_font_template(&descriptor);
let _ = result.send(Reply::GetFontTemplateReply(Some(font_template)));
}
Command::GetFontInstance(font_key, size, result) => { Command::GetFontInstance(font_key, size, result) => {
let webrender_api = &self.webrender_api; let webrender_api = &self.webrender_api;
@ -320,23 +316,28 @@ impl FontCache {
}); });
} }
fn transform_family(&self, family: &SingleFontFamily) -> LowercaseString { fn transform_family(&self, family_name: &FontFamilyName) -> LowercaseString {
match self.generic_fonts.get(family) { match self.generic_fonts.get(family_name) {
None => LowercaseString::new(family.name()), None => LowercaseString::from(family_name),
Some(mapped_family) => (*mapped_family).clone() Some(mapped_family) => (*mapped_family).clone()
} }
} }
fn find_font_in_local_family(&mut self, family_name: &LowercaseString, desc: &FontTemplateDescriptor) fn find_font_in_local_family(
-> Option<Arc<FontTemplateData>> { &mut self,
template_descriptor: &FontTemplateDescriptor,
family_name: &FontFamilyName,
) -> Option<Arc<FontTemplateData>> {
let family_name = self.transform_family(family_name);
// TODO(Issue #188): look up localized font family names if canonical name not found // TODO(Issue #188): look up localized font family names if canonical name not found
// look up canonical name // look up canonical name
if self.local_families.contains_key(family_name) { if self.local_families.contains_key(&family_name) {
debug!("FontList: Found font family with name={}", &**family_name); debug!("FontList: Found font family with name={}", &*family_name);
let s = self.local_families.get_mut(family_name).unwrap(); let s = self.local_families.get_mut(&family_name).unwrap();
if s.templates.is_empty() { if s.templates.is_empty() {
for_each_variation(family_name, |path| { for_each_variation(&family_name, |path| {
s.add_template(Atom::from(&*path), None); s.add_template(Atom::from(&*path), None);
}); });
} }
@ -344,20 +345,23 @@ impl FontCache {
// TODO(Issue #192: handle generic font families, like 'serif' and 'sans-serif'. // TODO(Issue #192: handle generic font families, like 'serif' and 'sans-serif'.
// if such family exists, try to match style to a font // if such family exists, try to match style to a font
s.find_font_for_style(desc, &self.font_context) s.find_font_for_style(template_descriptor, &self.font_context)
} else { } else {
debug!("FontList: Couldn't find font family with name={}", &**family_name); debug!("FontList: Couldn't find font family with name={}", &*family_name);
None None
} }
} }
fn find_font_in_web_family(&mut self, family: &SingleFontFamily, desc: &FontTemplateDescriptor) fn find_font_in_web_family(
-> Option<Arc<FontTemplateData>> { &mut self,
let family_name = LowercaseString::new(family.name()); template_descriptor: &FontTemplateDescriptor,
family_name: &FontFamilyName,
) -> Option<Arc<FontTemplateData>> {
let family_name = LowercaseString::from(family_name);
if self.web_families.contains_key(&family_name) { if self.web_families.contains_key(&family_name) {
let templates = self.web_families.get_mut(&family_name).unwrap(); let templates = self.web_families.get_mut(&family_name).unwrap();
templates.find_font_for_style(desc, &self.font_context) templates.find_font_for_style(template_descriptor, &self.font_context)
} else { } else {
None None
} }
@ -385,32 +389,21 @@ impl FontCache {
} }
} }
fn find_font_template(&mut self, family: &SingleFontFamily, desc: &FontTemplateDescriptor) fn find_font_template(
-> Option<FontTemplateInfo> { &mut self,
let template = self.find_font_in_web_family(family, desc) template_descriptor: &FontTemplateDescriptor,
.or_else(|| { family_descriptor: &FontFamilyDescriptor,
let transformed_family = self.transform_family(family); ) -> Option<FontTemplateInfo> {
self.find_font_in_local_family(&transformed_family, desc) match family_descriptor.scope {
}); FontSearchScope::Any => {
self.find_font_in_web_family(&template_descriptor, &family_descriptor.name)
template.map(|template| { .or_else(|| self.find_font_in_local_family(&template_descriptor, &family_descriptor.name))
self.get_font_template_info(template)
})
} }
fn last_resort_font_template(&mut self, desc: &FontTemplateDescriptor) FontSearchScope::Local => {
-> FontTemplateInfo { self.find_font_in_local_family(&template_descriptor, &family_descriptor.name)
let last_resort = last_resort_font_families();
for family in &last_resort {
let family = LowercaseString::new(family);
let maybe_font_in_family = self.find_font_in_local_family(&family, desc);
if let Some(family) = maybe_font_in_family {
return self.get_font_template_info(family)
} }
} }.map(|t| self.get_font_template_info(t))
panic!("Unable to find any fonts that match (do you have fallback fonts installed?)");
} }
} }
@ -480,11 +473,14 @@ impl FontSource for FontCacheThread {
instance_key.unwrap() instance_key.unwrap()
} }
fn find_font_template(&mut self, family: SingleFontFamily, desc: FontTemplateDescriptor) fn font_template(
-> Option<FontTemplateInfo> { &mut self,
template_descriptor: FontTemplateDescriptor,
family_descriptor: FontFamilyDescriptor,
) -> Option<FontTemplateInfo> {
let (response_chan, response_port) = let (response_chan, response_port) =
ipc::channel().expect("failed to create IPC channel"); ipc::channel().expect("failed to create IPC channel");
self.chan.send(Command::GetFontTemplate(family, desc, response_chan)) self.chan.send(Command::GetFontTemplate(template_descriptor, family_descriptor, response_chan))
.expect("failed to send message to font cache thread"); .expect("failed to send message to font cache thread");
let reply = response_port.recv(); let reply = response_port.recv();
@ -501,27 +497,6 @@ impl FontSource for FontCacheThread {
} }
} }
} }
fn last_resort_font_template(&mut self, desc: FontTemplateDescriptor)
-> FontTemplateInfo {
let (response_chan, response_port) =
ipc::channel().expect("failed to create IPC channel");
self.chan.send(Command::GetLastResortFontTemplate(desc, response_chan))
.expect("failed to send message to font cache thread");
let reply = response_port.recv();
if reply.is_err() {
let font_thread_has_closed = self.chan.send(Command::Ping).is_err();
assert!(font_thread_has_closed, "Failed to receive a response from live font cache");
panic!("Font cache thread has already exited.");
}
match reply.unwrap() {
Reply::GetFontTemplateReply(data) => {
data.unwrap()
}
}
}
} }
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
@ -537,6 +512,12 @@ impl LowercaseString {
} }
} }
impl<'a> From<&'a FontFamilyName> for LowercaseString {
fn from(family_name: &'a FontFamilyName) -> LowercaseString {
LowercaseString::new(family_name.name())
}
}
impl Deref for LowercaseString { impl Deref for LowercaseString {
type Target = str; type Target = str;

View file

@ -4,14 +4,13 @@
use app_units::Au; use app_units::Au;
use fnv::FnvHasher; use fnv::FnvHasher;
use font::{Font, FontDescriptor, FontGroup, FontHandleMethods, FontRef}; use font::{Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontHandleMethods, FontRef};
use font_cache_thread::FontTemplateInfo; use font_cache_thread::FontTemplateInfo;
use font_template::FontTemplateDescriptor; use font_template::FontTemplateDescriptor;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use platform::font::FontHandle; use platform::font::FontHandle;
pub use platform::font_context::FontContextHandle; pub use platform::font_context::FontContextHandle;
use servo_arc::Arc; use servo_arc::Arc;
use servo_atoms::Atom;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::default::Default; use std::default::Default;
@ -20,42 +19,10 @@ use std::rc::Rc;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use style::computed_values::font_variant_caps::T as FontVariantCaps; use style::computed_values::font_variant_caps::T as FontVariantCaps;
use style::properties::style_structs::Font as FontStyleStruct; use style::properties::style_structs::Font as FontStyleStruct;
use style::values::computed::font::SingleFontFamily;
use webrender_api; use webrender_api;
static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h) static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
#[derive(Debug)]
struct FontCacheEntry {
family: Atom,
font: Option<FontRef>,
}
impl FontCacheEntry {
fn matches(&self, descriptor: &FontDescriptor, family: &SingleFontFamily) -> bool {
if self.family != *family.atom() {
return false
}
if let Some(ref font) = self.font {
(*font).borrow().descriptor == *descriptor
} else {
true
}
}
}
#[derive(Debug)]
struct FallbackFontCacheEntry {
font: FontRef,
}
impl FallbackFontCacheEntry {
fn matches(&self, descriptor: &FontDescriptor) -> bool {
self.font.borrow().descriptor == *descriptor
}
}
/// An epoch for the font context cache. The cache is flushed if the current epoch does not match /// An epoch for the font context cache. The cache is flushed if the current epoch does not match
/// this one. /// this one.
static FONT_CACHE_EPOCH: AtomicUsize = ATOMIC_USIZE_INIT; static FONT_CACHE_EPOCH: AtomicUsize = ATOMIC_USIZE_INIT;
@ -63,13 +30,11 @@ static FONT_CACHE_EPOCH: AtomicUsize = ATOMIC_USIZE_INIT;
pub trait FontSource { pub trait FontSource {
fn get_font_instance(&mut self, key: webrender_api::FontKey, size: Au) -> webrender_api::FontInstanceKey; fn get_font_instance(&mut self, key: webrender_api::FontKey, size: Au) -> webrender_api::FontInstanceKey;
fn find_font_template( fn font_template(
&mut self, &mut self,
family: SingleFontFamily, template_descriptor: FontTemplateDescriptor,
desc: FontTemplateDescriptor family_descriptor: FontFamilyDescriptor,
) -> Option<FontTemplateInfo>; ) -> Option<FontTemplateInfo>;
fn last_resort_font_template(&mut self, desc: FontTemplateDescriptor) -> FontTemplateInfo;
} }
/// The FontContext represents the per-thread/thread state necessary for /// The FontContext represents the per-thread/thread state necessary for
@ -84,10 +49,8 @@ pub struct FontContext<S: FontSource> {
// TODO: The font context holds a strong ref to the cached fonts // TODO: The font context holds a strong ref to the cached fonts
// so they will never be released. Find out a good time to drop them. // so they will never be released. Find out a good time to drop them.
// See bug https://github.com/servo/servo/issues/3300 // See bug https://github.com/servo/servo/issues/3300
// font_cache: HashMap<FontCacheKey, Option<FontRef>>,
// GWTODO: Check on real pages if this is faster as Vec() or HashMap(). font_template_cache: HashMap<FontTemplateCacheKey, Option<FontTemplateInfo>>,
font_cache: Vec<FontCacheEntry>,
fallback_font_cache: Vec<FallbackFontCacheEntry>,
font_group_cache: font_group_cache:
HashMap<FontGroupCacheKey, Rc<RefCell<FontGroup>>, BuildHasherDefault<FnvHasher>>, HashMap<FontGroupCacheKey, Rc<RefCell<FontGroup>>, BuildHasherDefault<FnvHasher>>,
@ -101,33 +64,13 @@ impl<S: FontSource> FontContext<S> {
FontContext { FontContext {
platform_handle: handle, platform_handle: handle,
font_source, font_source,
font_cache: vec!(), font_cache: HashMap::new(),
fallback_font_cache: vec!(), font_template_cache: HashMap::new(),
font_group_cache: HashMap::with_hasher(Default::default()), font_group_cache: HashMap::with_hasher(Default::default()),
epoch: 0, epoch: 0,
} }
} }
/// Create a `Font` for use in layout calculations, from a `FontTemplateInfo` returned by the
/// cache thread (which contains the underlying font data) and a `FontDescriptor` which
/// contains the styling parameters.
fn create_font(&mut self, info: FontTemplateInfo, descriptor: FontDescriptor) -> Result<Font, ()> {
// TODO: (Bug #3463): Currently we only support fake small-caps
// painting. We should also support true small-caps (where the
// font supports it) in the future.
let actual_pt_size = match descriptor.variant {
FontVariantCaps::SmallCaps => descriptor.pt_size.scale_by(SMALL_CAPS_SCALE_FACTOR),
FontVariantCaps::Normal => descriptor.pt_size,
};
let handle = FontHandle::new_from_template(&self.platform_handle,
info.font_template,
Some(actual_pt_size))?;
let font_instance_key = self.font_source.get_font_instance(info.font_key, actual_pt_size);
Ok(Font::new(handle, descriptor.to_owned(), actual_pt_size, font_instance_key))
}
fn expire_font_caches_if_necessary(&mut self) { fn expire_font_caches_if_necessary(&mut self) {
let current_epoch = FONT_CACHE_EPOCH.load(Ordering::SeqCst); let current_epoch = FONT_CACHE_EPOCH.load(Ordering::SeqCst);
if current_epoch == self.epoch { if current_epoch == self.epoch {
@ -135,7 +78,7 @@ impl<S: FontSource> FontContext<S> {
} }
self.font_cache.clear(); self.font_cache.clear();
self.fallback_font_cache.clear(); self.font_template_cache.clear();
self.font_group_cache.clear(); self.font_group_cache.clear();
self.epoch = current_epoch self.epoch = current_epoch
} }
@ -160,76 +103,85 @@ impl<S: FontSource> FontContext<S> {
font_group font_group
} }
/// Returns a reference to an existing font cache entry matching `descriptor` and `family`, if /// Returns a font matching the parameters. Fonts are cached, so repeated calls will return a
/// there is one. /// reference to the same underlying `Font`.
fn font_cache_entry(&self, descriptor: &FontDescriptor, family: &SingleFontFamily) -> Option<&FontCacheEntry> { pub fn font(
self.font_cache.iter() &mut self,
.find(|cache_entry| cache_entry.matches(&descriptor, &family)) font_descriptor: &FontDescriptor,
} family_descriptor: &FontFamilyDescriptor,
) -> Option<FontRef> {
/// Creates a new font cache entry matching `descriptor` and `family`. let cache_key = FontCacheKey {
fn create_font_cache_entry(&mut self, descriptor: &FontDescriptor, family: &SingleFontFamily) -> FontCacheEntry { font_descriptor: font_descriptor.clone(),
let font = family_descriptor: family_descriptor.clone(),
self.font_source.find_font_template(family.clone(), descriptor.template_descriptor.clone())
.and_then(|template_info|
self.create_font(template_info, descriptor.to_owned()).ok()
)
.map(|font| Rc::new(RefCell::new(font)));
FontCacheEntry { family: family.atom().to_owned(), font }
}
/// Returns a font from `family` matching the `descriptor`. Fonts are cached, so repeated calls
/// will return a reference to the same underlying `Font`.
pub fn font(&mut self, descriptor: &FontDescriptor, family: &SingleFontFamily) -> Option<FontRef> {
if let Some(entry) = self.font_cache_entry(descriptor, family) {
return entry.font.clone()
}
let entry = self.create_font_cache_entry(descriptor, family);
let font = entry.font.clone();
self.font_cache.push(entry);
font
}
/// Returns a reference to an existing fallback font cache entry matching `descriptor`, if
/// there is one.
fn fallback_font_cache_entry(&self, descriptor: &FontDescriptor) -> Option<&FallbackFontCacheEntry> {
self.fallback_font_cache.iter()
.find(|cache_entry| cache_entry.matches(descriptor))
}
/// Creates a new fallback font cache entry matching `descriptor`.
fn create_fallback_font_cache_entry(&mut self, descriptor: &FontDescriptor) -> Option<FallbackFontCacheEntry> {
let template_info = self.font_source.last_resort_font_template(descriptor.template_descriptor.clone());
match self.create_font(template_info, descriptor.to_owned()) {
Ok(font) =>
Some(FallbackFontCacheEntry {
font: Rc::new(RefCell::new(font))
}),
Err(_) => {
debug!("Failed to create fallback font!");
None
}
}
}
/// Returns a fallback font matching the `descriptor`. Fonts are cached, so repeated calls will
/// return a reference to the same underlying `Font`.
pub fn fallback_font(&mut self, descriptor: &FontDescriptor) -> Option<FontRef> {
if let Some(cached_entry) = self.fallback_font_cache_entry(descriptor) {
return Some(cached_entry.font.clone())
}; };
if let Some(entry) = self.create_fallback_font_cache_entry(descriptor) { self.font_cache.get(&cache_key).map(|v| v.clone()).unwrap_or_else(|| {
let font = entry.font.clone(); debug!(
self.fallback_font_cache.push(entry); "FontContext::font cache miss for font_descriptor={:?} family_descriptor={:?}",
Some(font) font_descriptor,
} else { family_descriptor
None );
let font =
self.font_template(&font_descriptor.template_descriptor, family_descriptor)
.and_then(|template_info| self.create_font(template_info, font_descriptor.to_owned()).ok())
.map(|font| Rc::new(RefCell::new(font)));
self.font_cache.insert(cache_key, font.clone());
font
})
} }
fn font_template(
&mut self,
template_descriptor: &FontTemplateDescriptor,
family_descriptor: &FontFamilyDescriptor
) -> Option<FontTemplateInfo> {
let cache_key = FontTemplateCacheKey {
template_descriptor: template_descriptor.clone(),
family_descriptor: family_descriptor.clone(),
};
self.font_template_cache.get(&cache_key).map(|v| v.clone()).unwrap_or_else(|| {
debug!(
"FontContext::font_template cache miss for template_descriptor={:?} family_descriptor={:?}",
template_descriptor,
family_descriptor
);
let template_info = self.font_source.font_template(
template_descriptor.clone(),
family_descriptor.clone(),
);
self.font_template_cache.insert(cache_key, template_info.clone());
template_info
})
}
/// Create a `Font` for use in layout calculations, from a `FontTemplateData` returned by the
/// cache thread and a `FontDescriptor` which contains the styling parameters.
fn create_font(
&mut self,
info: FontTemplateInfo,
descriptor: FontDescriptor
) -> Result<Font, ()> {
// TODO: (Bug #3463): Currently we only support fake small-caps
// painting. We should also support true small-caps (where the
// font supports it) in the future.
let actual_pt_size = match descriptor.variant {
FontVariantCaps::SmallCaps => descriptor.pt_size.scale_by(SMALL_CAPS_SCALE_FACTOR),
FontVariantCaps::Normal => descriptor.pt_size,
};
let handle = FontHandle::new_from_template(
&self.platform_handle,
info.font_template,
Some(actual_pt_size)
)?;
let font_instance_key = self.font_source.get_font_instance(info.font_key, actual_pt_size);
Ok(Font::new(handle, descriptor.to_owned(), actual_pt_size, font_instance_key))
} }
} }
@ -240,6 +192,18 @@ impl<S: FontSource> MallocSizeOf for FontContext<S> {
} }
} }
#[derive(Debug, Eq, Hash, PartialEq)]
struct FontCacheKey {
font_descriptor: FontDescriptor,
family_descriptor: FontFamilyDescriptor,
}
#[derive(Debug, Eq, Hash, PartialEq)]
struct FontTemplateCacheKey {
template_descriptor: FontTemplateDescriptor,
family_descriptor: FontFamilyDescriptor,
}
#[derive(Debug)] #[derive(Debug)]
struct FontGroupCacheKey { struct FontGroupCacheKey {
style: Arc<FontStyleStruct>, style: Arc<FontStyleStruct>,

View file

@ -19,13 +19,18 @@ use style::values::computed::font::FontWeight;
/// to be expanded or refactored when we support more of the font styling parameters. /// to be expanded or refactored when we support more of the font styling parameters.
/// ///
/// NB: If you change this, you will need to update `style::properties::compute_font_hash()`. /// NB: If you change this, you will need to update `style::properties::compute_font_hash()`.
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] #[derive(Clone, Copy, Debug, Deserialize, Hash, PartialEq, Serialize)]
pub struct FontTemplateDescriptor { pub struct FontTemplateDescriptor {
pub weight: FontWeight, pub weight: FontWeight,
pub stretch: FontStretch, pub stretch: FontStretch,
pub style: FontStyle, pub style: FontStyle,
} }
/// FontTemplateDescriptor contains floats, which are not Eq because of NaN. However,
/// we know they will never be NaN, so we can manually implement Eq.
impl Eq for FontTemplateDescriptor {}
fn style_to_number(s: &FontStyle) -> f32 { fn style_to_number(s: &FontStyle) -> f32 {
use style::values::generics::font::FontStyle as GenericFontStyle; use style::values::generics::font::FontStyle as GenericFontStyle;
@ -66,7 +71,7 @@ impl FontTemplateDescriptor {
// 0 <= weightPart <= 800 // 0 <= weightPart <= 800
let weight_part = (self.weight.0 - other.weight.0).abs(); let weight_part = (self.weight.0 - other.weight.0).abs();
// 0 <= stretchPart <= 8 // 0 <= stretchPart <= 8
let stretch_part = ((self.stretch.0).0 - (other.stretch.0).0).abs(); let stretch_part = (self.stretch.value() - other.stretch.value()).abs();
style_part + weight_part + stretch_part style_part + weight_part + stretch_part
} }
} }

View file

@ -48,14 +48,15 @@ extern crate ordered_float;
extern crate range; extern crate range;
#[macro_use] extern crate serde; #[macro_use] extern crate serde;
extern crate servo_arc; extern crate servo_arc;
extern crate servo_url;
#[macro_use] extern crate servo_atoms; #[macro_use] extern crate servo_atoms;
extern crate servo_url;
#[cfg(feature = "unstable")] #[cfg(feature = "unstable")]
#[cfg(any(target_feature = "sse2", target_feature = "neon"))] #[cfg(any(target_feature = "sse2", target_feature = "neon"))]
extern crate simd; extern crate simd;
extern crate smallvec; extern crate smallvec;
extern crate style; extern crate style;
extern crate time; extern crate time;
extern crate ucd;
extern crate unicode_bidi; extern crate unicode_bidi;
extern crate unicode_script; extern crate unicode_script;
extern crate webrender_api; extern crate webrender_api;

View file

@ -6,6 +6,8 @@ use std::cell::RefCell;
use std::fs::File; use std::fs::File;
use std::io::{self, Read}; use std::io::{self, Read};
use std::path::Path; use std::path::Path;
use text::util::is_cjk;
use ucd::{Codepoint, UnicodeBlock};
use xml5ever::Attribute; use xml5ever::Attribute;
use xml5ever::driver::parse_document; use xml5ever::driver::parse_document;
use xml5ever::rcdom::*; use xml5ever::rcdom::*;
@ -470,12 +472,61 @@ pub fn system_default_family(generic_name: &str) -> Option<String> {
} }
} }
pub fn last_resort_font_families() -> Vec<String> { // Based on gfxAndroidPlatform::GetCommonFallbackFonts() in Gecko
vec!( pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
"sans-serif".to_owned(), let mut families = vec!();
"Droid Sans".to_owned(),
"serif".to_owned(), if let Some(block) = codepoint.and_then(|c| c.block()) {
) match block {
UnicodeBlock::Armenian => {
families.push("Droid Sans Armenian");
}
UnicodeBlock::Hebrew => {
families.push("Droid Sans Hebrew");
}
UnicodeBlock::Arabic => {
families.push("Droid Sans Arabic");
}
UnicodeBlock::Devanagari => {
families.push("Noto Sans Devanagari");
families.push("Droid Sans Devanagari");
}
UnicodeBlock::Tamil => {
families.push("Noto Sans Tamil");
families.push("Droid Sans Tamil");
}
UnicodeBlock::Thai => {
families.push("Noto Sans Thai");
families.push("Droid Sans Thai");
}
UnicodeBlock::Georgian |
UnicodeBlock::GeorgianSupplement => {
families.push("Droid Sans Georgian");
}
UnicodeBlock::Ethiopic |
UnicodeBlock::EthiopicSupplement => {
families.push("Droid Sans Ethiopic");
}
_ => {
if is_cjk(codepoint.unwrap()) {
families.push("MotoyaLMaru");
families.push("Noto Sans CJK JP");
families.push("Droid Sans Japanese");
}
}
}
}
families.push("Droid Sans Fallback");
families
} }
pub static SANS_SERIF_FONT_FAMILY: &'static str = "sans-serif"; pub static SANS_SERIF_FONT_FAMILY: &'static str = "sans-serif";

View file

@ -5,7 +5,7 @@
use app_units::Au; use app_units::Au;
use font::{FontHandleMethods, FontMetrics, FontTableMethods}; use font::{FontHandleMethods, FontMetrics, FontTableMethods};
use font::{FontTableTag, FractionalPixel, GPOS, GSUB, KERN}; use font::{FontTableTag, FractionalPixel, GPOS, GSUB, KERN};
use freetype::freetype::{FT_Done_Face, FT_New_Memory_Face}; use freetype::freetype::{FT_Done_Face, FT_New_Face, FT_New_Memory_Face};
use freetype::freetype::{FT_F26Dot6, FT_Face, FT_FaceRec}; use freetype::freetype::{FT_F26Dot6, FT_Face, FT_FaceRec};
use freetype::freetype::{FT_Get_Char_Index, FT_Get_Postscript_Name}; use freetype::freetype::{FT_Get_Char_Index, FT_Get_Postscript_Name};
use freetype::freetype::{FT_Get_Kerning, FT_Get_Sfnt_Table, FT_Load_Sfnt_Table}; use freetype::freetype::{FT_Get_Kerning, FT_Get_Sfnt_Table, FT_Load_Sfnt_Table};
@ -20,6 +20,7 @@ use platform::font_context::FontContextHandle;
use platform::font_template::FontTemplateData; use platform::font_template::FontTemplateData;
use servo_atoms::Atom; use servo_atoms::Atom;
use std::{mem, ptr}; use std::{mem, ptr};
use std::ffi::CString;
use std::os::raw::{c_char, c_long}; use std::os::raw::{c_char, c_long};
use std::sync::Arc; use std::sync::Arc;
use style::computed_values::font_stretch::T as FontStretch; use style::computed_values::font_stretch::T as FontStretch;
@ -87,6 +88,39 @@ impl Drop for FontHandle {
} }
} }
fn create_face(
lib: FT_Library,
template: &FontTemplateData,
pt_size: Option<Au>,
) -> Result<FT_Face, ()> {
unsafe {
let mut face: FT_Face = ptr::null_mut();
let face_index = 0 as FT_Long;
let result = if let Some(ref bytes) = template.bytes {
FT_New_Memory_Face(lib, bytes.as_ptr(), bytes.len() as FT_Long, face_index, &mut face)
} else {
// This will trigger a synchronous file read in the layout thread, which we may want to
// revisit at some point. See discussion here:
//
// https://github.com/servo/servo/pull/20506#issuecomment-378838800
let filename = CString::new(&*template.identifier).expect("filename contains NUL byte!");
FT_New_Face(lib, filename.as_ptr(), face_index, &mut face)
};
if !succeeded(result) || face.is_null() {
return Err(());
}
if let Some(s) = pt_size {
FontHandle::set_char_size(face, s).or(Err(()))?
}
Ok(face)
}
}
impl FontHandleMethods for FontHandle { impl FontHandleMethods for FontHandle {
fn new_from_template(fctx: &FontContextHandle, fn new_from_template(fctx: &FontContextHandle,
template: Arc<FontTemplateData>, template: Arc<FontTemplateData>,
@ -95,7 +129,8 @@ impl FontHandleMethods for FontHandle {
let ft_ctx: FT_Library = fctx.ctx.ctx; let ft_ctx: FT_Library = fctx.ctx.ctx;
if ft_ctx.is_null() { return Err(()); } if ft_ctx.is_null() { return Err(()); }
return create_face_from_buffer(ft_ctx, &template.bytes, pt_size).map(|face| { let face = create_face(ft_ctx, &template, pt_size)?;
let mut handle = FontHandle { let mut handle = FontHandle {
face: face, face: face,
font_data: template.clone(), font_data: template.clone(),
@ -106,26 +141,7 @@ impl FontHandleMethods for FontHandle {
handle.can_do_fast_shaping = handle.has_table(KERN) && handle.can_do_fast_shaping = handle.has_table(KERN) &&
!handle.has_table(GPOS) && !handle.has_table(GPOS) &&
!handle.has_table(GSUB); !handle.has_table(GSUB);
handle Ok(handle)
});
fn create_face_from_buffer(lib: FT_Library, buffer: &[u8], pt_size: Option<Au>)
-> Result<FT_Face, ()> {
unsafe {
let mut face: FT_Face = ptr::null_mut();
let face_index = 0 as FT_Long;
let result = FT_New_Memory_Face(lib, buffer.as_ptr(), buffer.len() as FT_Long,
face_index, &mut face);
if !succeeded(result) || face.is_null() {
return Err(());
}
if let Some(s) = pt_size {
FontHandle::set_char_size(face, s).or(Err(()))?
}
Ok(face)
}
}
} }
fn template(&self) -> Arc<FontTemplateData> { fn template(&self) -> Arc<FontTemplateData> {
@ -187,7 +203,7 @@ impl FontHandleMethods for FontHandle {
} else { } else {
FontStretchKeyword::Normal FontStretchKeyword::Normal
}.compute(); }.compute();
NonNegative(percentage) FontStretch(NonNegative(percentage))
} }
fn glyph_index(&self, codepoint: char) -> Option<GlyphId> { fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
@ -197,7 +213,7 @@ impl FontHandleMethods for FontHandle {
if idx != 0 as FT_UInt { if idx != 0 as FT_UInt {
Some(idx as GlyphId) Some(idx as GlyphId)
} else { } else {
debug!("Invalid codepoint: {}", codepoint); debug!("Invalid codepoint: U+{:04X} ('{}')", codepoint as u32, codepoint);
None None
} }
} }

View file

@ -10,10 +10,10 @@ use fontconfig::fontconfig::{FcFontSetList, FcObjectSetCreate, FcObjectSetDestro
use fontconfig::fontconfig::{FcObjectSetAdd, FcPatternGetInteger}; use fontconfig::fontconfig::{FcObjectSetAdd, FcPatternGetInteger};
use libc; use libc;
use libc::{c_char, c_int}; use libc::{c_char, c_int};
use std::borrow::ToOwned;
use std::ffi::CString; use std::ffi::CString;
use std::ptr; use std::ptr;
use super::c_str_to_string; use super::c_str_to_string;
use text::util::is_cjk;
static FC_FAMILY: &'static [u8] = b"family\0"; static FC_FAMILY: &'static [u8] = b"family\0";
static FC_FILE: &'static [u8] = b"file\0"; static FC_FILE: &'static [u8] = b"file\0";
@ -132,12 +132,25 @@ pub fn system_default_family(generic_name: &str) -> Option<String> {
} }
} }
pub fn last_resort_font_families() -> Vec<String> { pub static SANS_SERIF_FONT_FAMILY: &'static str = "DejaVu Sans";
vec!(
"Fira Sans".to_owned(), // Based on gfxPlatformGtk::GetCommonFallbackFonts() in Gecko
"DejaVu Sans".to_owned(), pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
"Arial".to_owned() let mut families = vec!(
) "DejaVu Serif",
"FreeSerif",
"DejaVu Sans",
"FreeSans",
);
if let Some(codepoint) = codepoint {
if is_cjk(codepoint) {
families.push("TakaoPGothic");
families.push("Droid Sans Fallback");
families.push("WenQuanYi Micro Hei");
families.push("NanumGothic");
}
} }
pub static SANS_SERIF_FONT_FAMILY: &'static str = "DejaVu Sans"; families
}

View file

@ -16,34 +16,21 @@ use webrender_api::NativeFontHandle;
pub struct FontTemplateData { pub struct FontTemplateData {
// If you add members here, review the Debug impl below // If you add members here, review the Debug impl below
pub bytes: Vec<u8>, pub bytes: Option<Vec<u8>>,
pub identifier: Atom, pub identifier: Atom,
} }
impl fmt::Debug for FontTemplateData { impl fmt::Debug for FontTemplateData {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("FontTemplateData") fmt.debug_struct("FontTemplateData")
.field("bytes", &format!("[{} bytes]", self.bytes.len())) .field("bytes", &self.bytes.as_ref().map(|b| format!("[{} bytes]", b.len())))
.field("identifier", &self.identifier) .field("identifier", &self.identifier)
.finish() .finish()
} }
} }
impl FontTemplateData { impl FontTemplateData {
pub fn new(identifier: Atom, font_data: Option<Vec<u8>>) -> Result<FontTemplateData, Error> { pub fn new(identifier: Atom, bytes: Option<Vec<u8>>) -> Result<FontTemplateData, Error> {
let bytes = match font_data {
Some(bytes) => {
bytes
},
None => {
// TODO: Handle file load failure!
let mut file = File::open(&*identifier)?;
let mut buffer = vec![];
file.read_to_end(&mut buffer).unwrap();
buffer
},
};
Ok(FontTemplateData { Ok(FontTemplateData {
bytes: bytes, bytes: bytes,
identifier: identifier, identifier: identifier,
@ -54,17 +41,29 @@ impl FontTemplateData {
/// operation (depending on the platform) which performs synchronous disk I/O /// operation (depending on the platform) which performs synchronous disk I/O
/// and should never be done lightly. /// and should never be done lightly.
pub fn bytes(&self) -> Vec<u8> { pub fn bytes(&self) -> Vec<u8> {
self.bytes.clone() self.bytes_if_in_memory().unwrap_or_else(|| {
let mut file = File::open(&*self.identifier).expect("Couldn't open font file!");
let mut buffer = vec![];
file.read_to_end(&mut buffer).unwrap();
buffer
})
} }
/// Returns a clone of the bytes in this font if they are in memory. This function never /// Returns a clone of the bytes in this font if they are in memory. This function never
/// performs disk I/O. /// performs disk I/O.
pub fn bytes_if_in_memory(&self) -> Option<Vec<u8>> { pub fn bytes_if_in_memory(&self) -> Option<Vec<u8>> {
Some(self.bytes()) self.bytes.clone()
} }
/// Returns the native font that underlies this font template, if applicable. /// Returns the native font that underlies this font template, if applicable.
pub fn native_font(&self) -> Option<NativeFontHandle> { pub fn native_font(&self) -> Option<NativeFontHandle> {
if self.bytes.is_none() {
Some(NativeFontHandle {
pathname: String::from(&*self.identifier),
index: 0,
})
} else {
None None
} }
} }
}

View file

@ -229,7 +229,7 @@ impl FontHandleMethods for FontHandle {
use style::values::generics::NonNegative; use style::values::generics::NonNegative;
let normalized = self.ctfont.all_traits().normalized_width(); // [-1.0, 1.0] let normalized = self.ctfont.all_traits().normalized_width(); // [-1.0, 1.0]
NonNegative(Percentage(normalized as f32 + 1.0)) FontStretch(NonNegative(Percentage(normalized as f32 + 1.0)))
} }
fn glyph_index(&self, codepoint: char) -> Option<GlyphId> { fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {

View file

@ -3,7 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use core_text; use core_text;
use std::borrow::ToOwned; use text::util::unicode_plane;
use ucd::{Codepoint, UnicodeBlock};
pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) { pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) {
let family_names = core_text::font_collection::get_family_names(); let family_names = core_text::font_collection::get_family_names();
@ -28,8 +29,162 @@ pub fn system_default_family(_generic_name: &str) -> Option<String> {
None None
} }
pub fn last_resort_font_families() -> Vec<String> { // Based on gfxPlatformMac::GetCommonFallbackFonts() in Gecko
vec!("Arial Unicode MS".to_owned(), "Arial".to_owned()) pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
let mut families = vec!("Lucida Grande");
if let Some(codepoint) = codepoint {
match unicode_plane(codepoint) {
// https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane
0 => {
if let Some(block) = codepoint.block() {
match block {
UnicodeBlock::Arabic |
UnicodeBlock::Syriac |
UnicodeBlock::ArabicSupplement |
UnicodeBlock::Thaana |
UnicodeBlock::NKo => {
families.push("Geeza Pro");
}
UnicodeBlock::Devanagari => {
families.push("Devanagari Sangam MN");
}
UnicodeBlock::Gurmukhi => {
families.push("Gurmukhi MN");
}
UnicodeBlock::Gujarati => {
families.push("Gujarati Sangam MN");
}
UnicodeBlock::Tamil => {
families.push("Tamil MN");
}
UnicodeBlock::Lao => {
families.push("Lao MN");
}
UnicodeBlock::Tibetan => {
families.push("Songti SC");
}
UnicodeBlock::Myanmar => {
families.push("Myanmar MN");
}
UnicodeBlock::Ethiopic |
UnicodeBlock::EthiopicSupplement |
UnicodeBlock::EthiopicExtended |
UnicodeBlock::EthiopicExtendedA => {
families.push("Kefa");
}
UnicodeBlock::Cherokee => {
families.push("Plantagenet Cherokee");
}
UnicodeBlock::UnifiedCanadianAboriginalSyllabics |
UnicodeBlock::UnifiedCanadianAboriginalSyllabicsExtended => {
families.push("Euphemia UCAS");
}
UnicodeBlock::Mongolian |
UnicodeBlock::YiSyllables |
UnicodeBlock::YiRadicals => {
families.push("STHeiti");
}
UnicodeBlock::Khmer |
UnicodeBlock::KhmerSymbols => {
families.push("Khmer MN");
}
UnicodeBlock::TaiLe => {
families.push("Microsoft Tai Le");
}
UnicodeBlock::GeneralPunctuation |
UnicodeBlock::SuperscriptsandSubscripts |
UnicodeBlock::CurrencySymbols |
UnicodeBlock::CombiningDiacriticalMarksforSymbols |
UnicodeBlock::LetterlikeSymbols |
UnicodeBlock::NumberForms |
UnicodeBlock::Arrows |
UnicodeBlock::MathematicalOperators |
UnicodeBlock::MiscellaneousTechnical |
UnicodeBlock::ControlPictures |
UnicodeBlock::OpticalCharacterRecognition |
UnicodeBlock::EnclosedAlphanumerics |
UnicodeBlock::BoxDrawing |
UnicodeBlock::BlockElements |
UnicodeBlock::GeometricShapes |
UnicodeBlock::MiscellaneousSymbols |
UnicodeBlock::Dingbats |
UnicodeBlock::MiscellaneousMathematicalSymbolsA |
UnicodeBlock::SupplementalArrowsA |
UnicodeBlock::SupplementalArrowsB |
UnicodeBlock::MiscellaneousMathematicalSymbolsB |
UnicodeBlock::SupplementalMathematicalOperators |
UnicodeBlock::MiscellaneousSymbolsandArrows |
UnicodeBlock::SupplementalPunctuation => {
families.push("Hiragino Kaku Gothic ProN");
families.push("Apple Symbols");
families.push("Menlo");
families.push("STIXGeneral");
}
UnicodeBlock::BraillePatterns => {
families.push("Apple Braille");
}
UnicodeBlock::Bopomofo |
UnicodeBlock::HangulCompatibilityJamo |
UnicodeBlock::Kanbun |
UnicodeBlock::BopomofoExtended |
UnicodeBlock::CJKStrokes |
UnicodeBlock::KatakanaPhoneticExtensions => {
families.push("Hiragino Sans GB");
}
UnicodeBlock::YijingHexagramSymbols |
UnicodeBlock::CyrillicExtendedB |
UnicodeBlock::Bamum |
UnicodeBlock::ModifierToneLetters |
UnicodeBlock::LatinExtendedD |
UnicodeBlock::ArabicPresentationFormsA |
UnicodeBlock::HalfwidthandFullwidthForms |
UnicodeBlock::Specials => {
families.push("Apple Symbols");
}
_ => {}
}
}
}
// https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Multilingual_Plane
1 => {
families.push("Apple Symbols");
families.push("STIXGeneral");
}
// https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Ideographic_Plane
2 => {
// Systems with MS Office may have these fonts
families.push("MingLiU-ExtB");
families.push("SimSun-ExtB");
}
_ => {}
}
}
families.push("Geneva");
families.push("Arial Unicode MS");
families
} }
pub static SANS_SERIF_FONT_FAMILY: &'static str = "Helvetica"; pub static SANS_SERIF_FONT_FAMILY: &'static str = "Helvetica";

View file

@ -163,7 +163,7 @@ impl FontInfo {
let weight = StyleFontWeight(weight_val as f32); let weight = StyleFontWeight(weight_val as f32);
let stretch = NonNegative(match min(9, max(1, width_val)) { let stretch = StyleFontStretch(NonNegative(match min(9, max(1, width_val)) {
1 => FontStretchKeyword::UltraCondensed, 1 => FontStretchKeyword::UltraCondensed,
2 => FontStretchKeyword::ExtraCondensed, 2 => FontStretchKeyword::ExtraCondensed,
3 => FontStretchKeyword::Condensed, 3 => FontStretchKeyword::Condensed,
@ -174,7 +174,7 @@ impl FontInfo {
8 => FontStretchKeyword::ExtraExpanded, 8 => FontStretchKeyword::ExtraExpanded,
9 => FontStretchKeyword::UltraExpanded, 9 => FontStretchKeyword::UltraExpanded,
_ => return Err(()), _ => return Err(()),
}.compute()); }.compute()));
let style = if italic_bool { let style = if italic_bool {
GenericFontStyle::Italic GenericFontStyle::Italic
@ -212,7 +212,7 @@ impl FontInfo {
// slightly blacker black // slightly blacker black
FontWeight::ExtraBlack => 1000., FontWeight::ExtraBlack => 1000.,
}); });
let stretch = NonNegative(match font.stretch() { let stretch = StyleFontStretch(NonNegative(match font.stretch() {
FontStretch::Undefined => FontStretchKeyword::Normal, FontStretch::Undefined => FontStretchKeyword::Normal,
FontStretch::UltraCondensed => FontStretchKeyword::UltraCondensed, FontStretch::UltraCondensed => FontStretchKeyword::UltraCondensed,
FontStretch::ExtraCondensed => FontStretchKeyword::ExtraCondensed, FontStretch::ExtraCondensed => FontStretchKeyword::ExtraCondensed,
@ -223,7 +223,7 @@ impl FontInfo {
FontStretch::Expanded => FontStretchKeyword::Expanded, FontStretch::Expanded => FontStretchKeyword::Expanded,
FontStretch::ExtraExpanded => FontStretchKeyword::ExtraExpanded, FontStretch::ExtraExpanded => FontStretchKeyword::ExtraExpanded,
FontStretch::UltraExpanded => FontStretchKeyword::UltraExpanded, FontStretch::UltraExpanded => FontStretchKeyword::UltraExpanded,
}.compute()); }.compute()));
Ok(FontInfo { Ok(FontInfo {
family_name: font.family_name(), family_name: font.family_name(),

View file

@ -7,6 +7,8 @@ use servo_atoms::Atom;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Mutex; use std::sync::Mutex;
use std::sync::atomic::{Ordering, AtomicUsize}; use std::sync::atomic::{Ordering, AtomicUsize};
use text::util::unicode_plane;
use ucd::{Codepoint, UnicodeBlock};
lazy_static! { lazy_static! {
static ref FONT_ATOM_COUNTER: AtomicUsize = AtomicUsize::new(1); static ref FONT_ATOM_COUNTER: AtomicUsize = AtomicUsize::new(1);
@ -19,10 +21,6 @@ pub fn system_default_family(_: &str) -> Option<String> {
Some("Verdana".to_owned()) Some("Verdana".to_owned())
} }
pub fn last_resort_font_families() -> Vec<String> {
vec!("Arial".to_owned())
}
pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) { pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) {
let system_fc = FontCollection::system(); let system_fc = FontCollection::system();
for family in system_fc.families_iter() { for family in system_fc.families_iter() {
@ -69,3 +67,270 @@ pub fn font_from_atom(ident: &Atom) -> Font {
let fonts = FONT_ATOM_MAP.lock().unwrap(); let fonts = FONT_ATOM_MAP.lock().unwrap();
FontCollection::system().get_font_from_descriptor(fonts.get(ident).unwrap()).unwrap() FontCollection::system().get_font_from_descriptor(fonts.get(ident).unwrap()).unwrap()
} }
// Based on gfxWindowsPlatform::GetCommonFallbackFonts() in Gecko
pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
let mut families = vec!("Arial");
if let Some(codepoint) = codepoint {
match unicode_plane(codepoint) {
// https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane
0 => {
if let Some(block) = codepoint.block() {
match block {
UnicodeBlock::CyrillicSupplement |
UnicodeBlock::Armenian |
UnicodeBlock::Hebrew => {
families.push("Estrangelo Edessa");
families.push("Cambria");
}
UnicodeBlock::Arabic |
UnicodeBlock::ArabicSupplement => {
families.push("Microsoft Uighur");
}
UnicodeBlock::Syriac => {
families.push("Estrangelo Edessa");
}
UnicodeBlock::Thaana => {
families.push("MV Boli");
}
UnicodeBlock::NKo => {
families.push("Ebrima");
}
UnicodeBlock::Devanagari |
UnicodeBlock::Bengali => {
families.push("Nirmala UI");
families.push("Utsaah");
families.push("Aparajita");
}
UnicodeBlock::Gurmukhi |
UnicodeBlock::Gujarati |
UnicodeBlock::Oriya |
UnicodeBlock::Tamil |
UnicodeBlock::Telugu |
UnicodeBlock::Kannada |
UnicodeBlock::Malayalam |
UnicodeBlock::Sinhala |
UnicodeBlock::Lepcha |
UnicodeBlock::OlChiki |
UnicodeBlock::CyrillicExtendedC |
UnicodeBlock::SundaneseSupplement |
UnicodeBlock::VedicExtensions => {
families.push("Nirmala UI");
}
UnicodeBlock::Thai => {
families.push("Leelawadee UI");
}
UnicodeBlock::Lao => {
families.push("Lao UI");
}
UnicodeBlock::Myanmar |
UnicodeBlock::MyanmarExtendedA |
UnicodeBlock::MyanmarExtendedB => {
families.push("Myanmar Text");
}
UnicodeBlock::HangulJamo |
UnicodeBlock::HangulJamoExtendedA |
UnicodeBlock::HangulSyllables |
UnicodeBlock::HangulJamoExtendedB |
UnicodeBlock::HangulCompatibilityJamo => {
families.push("Malgun Gothic");
}
UnicodeBlock::Ethiopic |
UnicodeBlock::EthiopicSupplement |
UnicodeBlock::EthiopicExtended |
UnicodeBlock::EthiopicExtendedA => {
families.push("Nyala");
}
UnicodeBlock::Cherokee => {
families.push("Plantagenet Cherokee");
}
UnicodeBlock::UnifiedCanadianAboriginalSyllabics |
UnicodeBlock::UnifiedCanadianAboriginalSyllabicsExtended => {
families.push("Euphemia");
families.push("Segoe UI");
}
UnicodeBlock::Khmer |
UnicodeBlock::KhmerSymbols => {
families.push("Khmer UI");
families.push("Leelawadee UI");
}
UnicodeBlock::Mongolian => {
families.push("Mongolian Baiti");
}
UnicodeBlock::TaiLe => {
families.push("Microsoft Tai Le");
}
UnicodeBlock::NewTaiLue => {
families.push("Microsoft New Tai Lue");
}
UnicodeBlock::Buginese |
UnicodeBlock::TaiTham |
UnicodeBlock::CombiningDiacriticalMarksExtended => {
families.push("Leelawadee UI");
}
UnicodeBlock::GeneralPunctuation |
UnicodeBlock::SuperscriptsandSubscripts |
UnicodeBlock::CurrencySymbols |
UnicodeBlock::CombiningDiacriticalMarksforSymbols |
UnicodeBlock::LetterlikeSymbols |
UnicodeBlock::NumberForms |
UnicodeBlock::Arrows |
UnicodeBlock::MathematicalOperators |
UnicodeBlock::MiscellaneousTechnical |
UnicodeBlock::ControlPictures |
UnicodeBlock::OpticalCharacterRecognition |
UnicodeBlock::EnclosedAlphanumerics |
UnicodeBlock::BoxDrawing |
UnicodeBlock::BlockElements |
UnicodeBlock::GeometricShapes |
UnicodeBlock::MiscellaneousSymbols |
UnicodeBlock::Dingbats |
UnicodeBlock::MiscellaneousMathematicalSymbolsA |
UnicodeBlock::SupplementalArrowsA |
UnicodeBlock::SupplementalArrowsB |
UnicodeBlock::MiscellaneousMathematicalSymbolsB |
UnicodeBlock::SupplementalMathematicalOperators |
UnicodeBlock::MiscellaneousSymbolsandArrows |
UnicodeBlock::Glagolitic |
UnicodeBlock::LatinExtendedC |
UnicodeBlock::Coptic => {
families.push("Segoe UI");
families.push("Segoe UI Symbol");
families.push("Cambria");
families.push("Meiryo");
families.push("Lucida Sans Unicode");
families.push("Ebrima");
}
UnicodeBlock::GeorgianSupplement |
UnicodeBlock::Tifinagh |
UnicodeBlock::CyrillicExtendedA |
UnicodeBlock::SupplementalPunctuation |
UnicodeBlock::CJKRadicalsSupplement |
UnicodeBlock::KangxiRadicals |
UnicodeBlock::IdeographicDescriptionCharacters => {
families.push("Segoe UI");
families.push("Segoe UI Symbol");
families.push("Meiryo");
}
UnicodeBlock::BraillePatterns => {
families.push("Segoe UI Symbol");
}
UnicodeBlock::CJKSymbolsandPunctuation |
UnicodeBlock::Hiragana |
UnicodeBlock::Katakana |
UnicodeBlock::Bopomofo |
UnicodeBlock::Kanbun |
UnicodeBlock::BopomofoExtended |
UnicodeBlock::CJKStrokes |
UnicodeBlock::KatakanaPhoneticExtensions |
UnicodeBlock::CJKUnifiedIdeographs => {
families.push("Microsoft YaHei");
families.push("Yu Gothic");
}
UnicodeBlock::EnclosedCJKLettersandMonths => {
families.push("Malgun Gothic");
}
UnicodeBlock::YijingHexagramSymbols => {
families.push("Segoe UI Symbol");
}
UnicodeBlock::YiSyllables |
UnicodeBlock::YiRadicals => {
families.push("Microsoft Yi Baiti");
families.push("Segoe UI");
}
UnicodeBlock::Vai |
UnicodeBlock::CyrillicExtendedB |
UnicodeBlock::Bamum |
UnicodeBlock::ModifierToneLetters |
UnicodeBlock::LatinExtendedD => {
families.push("Ebrima");
families.push("Segoe UI");
families.push("Cambria Math");
}
UnicodeBlock::SylotiNagri |
UnicodeBlock::CommonIndicNumberForms |
UnicodeBlock::Phagspa |
UnicodeBlock::Saurashtra |
UnicodeBlock::DevanagariExtended => {
families.push("Microsoft PhagsPa");
families.push("Nirmala UI");
}
UnicodeBlock::KayahLi |
UnicodeBlock::Rejang |
UnicodeBlock::Javanese => {
families.push("Malgun Gothic");
families.push("Javanese Text");
families.push("Leelawadee UI");
}
UnicodeBlock::AlphabeticPresentationForms => {
families.push("Microsoft Uighur");
families.push("Gabriola");
families.push("Sylfaen");
}
UnicodeBlock::ArabicPresentationFormsA |
UnicodeBlock::ArabicPresentationFormsB => {
families.push("Traditional Arabic");
families.push("Arabic Typesetting");
}
UnicodeBlock::VariationSelectors |
UnicodeBlock::VerticalForms |
UnicodeBlock::CombiningHalfMarks |
UnicodeBlock::CJKCompatibilityForms |
UnicodeBlock::SmallFormVariants |
UnicodeBlock::HalfwidthandFullwidthForms |
UnicodeBlock::Specials => {
families.push("Microsoft JhengHei");
}
_ => {}
}
}
}
// https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Multilingual_Plane
1 => {
families.push("Segoe UI Symbol");
families.push("Ebrima");
families.push("Nirmala UI");
families.push("Cambria Math");
}
_ => {}
}
}
families.push("Arial Unicode MS");
families
}

View file

@ -10,7 +10,7 @@ extern crate style;
extern crate webrender_api; extern crate webrender_api;
use app_units::Au; use app_units::Au;
use gfx::font::FontHandleMethods; use gfx::font::{fallback_font_families, FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontSearchScope};
use gfx::font_cache_thread::{FontTemplates, FontTemplateInfo}; use gfx::font_cache_thread::{FontTemplates, FontTemplateInfo};
use gfx::font_context::{FontContext, FontContextHandle, FontSource}; use gfx::font_context::{FontContext, FontContextHandle, FontSource};
use gfx::font_template::FontTemplateDescriptor; use gfx::font_template::FontTemplateDescriptor;
@ -24,29 +24,31 @@ use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
use style::properties::longhands::font_variant_caps::computed_value::T as FontVariantCaps; use style::properties::longhands::font_variant_caps::computed_value::T as FontVariantCaps;
use style::properties::style_structs::Font as FontStyleStruct; use style::properties::style_structs::Font as FontStyleStruct;
use style::values::computed::Percentage;
use style::values::computed::font::{FamilyName, FamilyNameSyntax, FontFamily, FontFamilyList, FontSize}; use style::values::computed::font::{FamilyName, FamilyNameSyntax, FontFamily, FontFamilyList, FontSize};
use style::values::computed::font::{FontWeight, SingleFontFamily}; use style::values::computed::font::{FontStretch, FontWeight, SingleFontFamily};
use style::values::generics::NonNegative;
use style::values::generics::font::FontStyle; use style::values::generics::font::FontStyle;
struct TestFontSource { struct TestFontSource {
handle: FontContextHandle, handle: FontContextHandle,
families: HashMap<Atom, FontTemplates>, families: HashMap<String, FontTemplates>,
find_font_count: Rc<Cell<isize>>, find_font_count: Rc<Cell<isize>>,
} }
impl TestFontSource { impl TestFontSource {
fn new() -> TestFontSource { fn new() -> TestFontSource {
let mut csstest_ascii = FontTemplates::new(); let mut csstest_ascii = FontTemplates::new();
Self::add_face(&mut csstest_ascii, "csstest-ascii"); Self::add_face(&mut csstest_ascii, "csstest-ascii", None);
let mut csstest_basic = FontTemplates::new(); let mut csstest_basic = FontTemplates::new();
Self::add_face(&mut csstest_basic, "csstest-basic-regular"); Self::add_face(&mut csstest_basic, "csstest-basic-regular", None);
let mut fallback = FontTemplates::new();
Self::add_face(&mut fallback, "csstest-basic-regular", Some("fallback"));
let mut families = HashMap::new(); let mut families = HashMap::new();
families.insert(Atom::from("CSSTest ASCII"), csstest_ascii); families.insert("CSSTest ASCII".to_owned(), csstest_ascii);
families.insert(Atom::from("CSSTest Basic"), csstest_basic); families.insert("CSSTest Basic".to_owned(), csstest_basic);
families.insert(fallback_font_families(None)[0].to_owned(), fallback);
TestFontSource { TestFontSource {
handle: FontContextHandle::new(), handle: FontContextHandle::new(),
@ -55,7 +57,7 @@ impl TestFontSource {
} }
} }
fn add_face(family: &mut FontTemplates, name: &str) { fn add_face(family: &mut FontTemplates, name: &str, identifier: Option<&str>) {
let mut path: PathBuf = [ let mut path: PathBuf = [
env!("CARGO_MANIFEST_DIR"), env!("CARGO_MANIFEST_DIR"),
"tests", "tests",
@ -65,9 +67,10 @@ impl TestFontSource {
path.push(format!("{}.ttf", name)); path.push(format!("{}.ttf", name));
let file = File::open(path).unwrap(); let file = File::open(path).unwrap();
let identifier = Atom::from(identifier.unwrap_or(name));
family.add_template( family.add_template(
Atom::from(name), identifier,
Some(file.bytes().map(|b| b.unwrap()).collect()) Some(file.bytes().map(|b| b.unwrap()).collect())
) )
} }
@ -78,17 +81,17 @@ impl FontSource for TestFontSource {
webrender_api::FontInstanceKey(webrender_api::IdNamespace(0), 0) webrender_api::FontInstanceKey(webrender_api::IdNamespace(0), 0)
} }
fn find_font_template( fn font_template(
&mut self, &mut self,
family: SingleFontFamily, template_descriptor: FontTemplateDescriptor,
desc: FontTemplateDescriptor family_descriptor: FontFamilyDescriptor,
) -> Option<FontTemplateInfo> { ) -> Option<FontTemplateInfo> {
let handle = &self.handle; let handle = &self.handle;
self.find_font_count.set(self.find_font_count.get() + 1); self.find_font_count.set(self.find_font_count.get() + 1);
self.families self.families
.get_mut(family.atom()) .get_mut(family_descriptor.name())
.and_then(|family| family.find_font_for_style(&desc, handle)) .and_then(|family| family.find_font_for_style(&template_descriptor, handle))
.map(|template| { .map(|template| {
FontTemplateInfo { FontTemplateInfo {
font_template: template, font_template: template,
@ -96,10 +99,6 @@ impl FontSource for TestFontSource {
} }
}) })
} }
fn last_resort_font_template(&mut self, _desc: FontTemplateDescriptor) -> FontTemplateInfo {
unimplemented!();
}
} }
fn style() -> FontStyleStruct { fn style() -> FontStyleStruct {
@ -109,7 +108,7 @@ fn style() -> FontStyleStruct {
font_variant_caps: FontVariantCaps::Normal, font_variant_caps: FontVariantCaps::Normal,
font_weight: FontWeight::normal(), font_weight: FontWeight::normal(),
font_size: FontSize::medium(), font_size: FontSize::medium(),
font_stretch: NonNegative(Percentage(1.)), font_stretch: FontStretch::hundred(),
hash: 0, hash: 0,
}; };
style.compute_font_hash(); style.compute_font_hash();
@ -162,14 +161,72 @@ fn test_font_group_find_by_codepoint() {
let group = context.font_group(Arc::new(style)); let group = context.font_group(Arc::new(style));
let font = group.borrow_mut().find_by_codepoint(&mut context, 'a').unwrap(); let font = group.borrow_mut().find_by_codepoint(&mut context, 'a').unwrap();
assert_eq!(font.borrow().handle.family_name(), "CSSTest ASCII"); assert_eq!(&*font.borrow().identifier(), "csstest-ascii");
assert_eq!(count.get(), 1, "only the first font in the list should have been loaded"); assert_eq!(count.get(), 1, "only the first font in the list should have been loaded");
let font = group.borrow_mut().find_by_codepoint(&mut context, 'a').unwrap(); let font = group.borrow_mut().find_by_codepoint(&mut context, 'a').unwrap();
assert_eq!(font.borrow().handle.family_name(), "CSSTest ASCII"); assert_eq!(&*font.borrow().identifier(), "csstest-ascii");
assert_eq!(count.get(), 1, "we shouldn't load the same font a second time"); assert_eq!(count.get(), 1, "we shouldn't load the same font a second time");
let font = group.borrow_mut().find_by_codepoint(&mut context, 'á').unwrap(); let font = group.borrow_mut().find_by_codepoint(&mut context, 'á').unwrap();
assert_eq!(font.borrow().handle.family_name(), "CSSTest Basic"); assert_eq!(&*font.borrow().identifier(), "csstest-basic-regular");
assert_eq!(count.get(), 2, "both fonts should now have been loaded"); assert_eq!(count.get(), 2, "both fonts should now have been loaded");
} }
#[test]
fn test_font_fallback() {
let source = TestFontSource::new();
let mut context = FontContext::new(source);
let mut style = style();
style.set_font_family(font_family(vec!("CSSTest ASCII")));
let group = context.font_group(Arc::new(style));
let font = group.borrow_mut().find_by_codepoint(&mut context, 'a').unwrap();
assert_eq!(
&*font.borrow().identifier(), "csstest-ascii",
"a family in the group should be used if there is a matching glyph"
);
let font = group.borrow_mut().find_by_codepoint(&mut context, 'á').unwrap();
assert_eq!(
&*font.borrow().identifier(), "fallback",
"a fallback font should be used if there is no matching glyph in the group"
);
}
#[test]
fn test_font_template_is_cached() {
let source = TestFontSource::new();
let count = source.find_font_count.clone();
let mut context = FontContext::new(source);
let mut font_descriptor = FontDescriptor {
template_descriptor: FontTemplateDescriptor {
weight: FontWeight::normal(),
stretch: FontStretch::hundred(),
style: FontStyle::Normal,
},
variant: FontVariantCaps::Normal,
pt_size: Au(10),
};
let family_descriptor = FontFamilyDescriptor::new(
FontFamilyName::from("CSSTest Basic"),
FontSearchScope::Any,
);
let font1 = context.font(&font_descriptor, &family_descriptor).unwrap();
font_descriptor.pt_size = Au(20);
let font2 = context.font(&font_descriptor, &family_descriptor).unwrap();
assert_ne!(
font1.borrow().actual_pt_size,
font2.borrow().actual_pt_size,
"the same font should not have been returned"
);
assert_eq!(count.get(), 1, "we should only have fetched the template data from the cache thread once");
}

View file

@ -17,7 +17,7 @@ fn test_font_template_descriptor() {
use std::io::prelude::*; use std::io::prelude::*;
use std::path::PathBuf; use std::path::PathBuf;
use style::values::computed::Percentage; use style::values::computed::Percentage;
use style::values::computed::font::FontWeight; use style::values::computed::font::{FontStretch, FontWeight};
use style::values::generics::NonNegative; use style::values::generics::NonNegative;
use style::values::generics::font::FontStyle; use style::values::generics::font::FontStyle;
@ -45,25 +45,25 @@ fn test_font_template_descriptor() {
assert_eq!(descriptor("DejaVuSans"), FontTemplateDescriptor { assert_eq!(descriptor("DejaVuSans"), FontTemplateDescriptor {
weight: FontWeight::normal(), weight: FontWeight::normal(),
stretch: NonNegative(Percentage(1.)), stretch: FontStretch::hundred(),
style: FontStyle::Normal, style: FontStyle::Normal,
}); });
assert_eq!(descriptor("DejaVuSans-Bold"), FontTemplateDescriptor { assert_eq!(descriptor("DejaVuSans-Bold"), FontTemplateDescriptor {
weight: FontWeight::bold(), weight: FontWeight::bold(),
stretch: NonNegative(Percentage(1.)), stretch: FontStretch::hundred(),
style: FontStyle::Normal, style: FontStyle::Normal,
}); });
assert_eq!(descriptor("DejaVuSans-Oblique"), FontTemplateDescriptor { assert_eq!(descriptor("DejaVuSans-Oblique"), FontTemplateDescriptor {
weight: FontWeight::normal(), weight: FontWeight::normal(),
stretch: NonNegative(Percentage(1.)), stretch: FontStretch::hundred(),
style: FontStyle::Italic, style: FontStyle::Italic,
}); });
assert_eq!(descriptor("DejaVuSansCondensed-BoldOblique"), FontTemplateDescriptor { assert_eq!(descriptor("DejaVuSansCondensed-BoldOblique"), FontTemplateDescriptor {
weight: FontWeight::bold(), weight: FontWeight::bold(),
stretch: NonNegative(Percentage(0.875)), stretch: FontStretch(NonNegative(Percentage(0.875))),
style: FontStyle::Italic, style: FontStyle::Italic,
}); });
} }

View file

@ -2,6 +2,8 @@
* 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 http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use ucd::{Codepoint, UnicodeBlock};
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CompressionMode { pub enum CompressionMode {
CompressNone, CompressNone,
@ -114,3 +116,42 @@ pub fn is_bidi_control(c: char) -> bool {
_ => false _ => false
} }
} }
pub fn unicode_plane(codepoint: char) -> u32 {
(codepoint as u32) >> 16
}
pub fn is_cjk(codepoint: char) -> bool {
if let Some(block) = codepoint.block() {
match block {
UnicodeBlock::CJKRadicalsSupplement |
UnicodeBlock::KangxiRadicals |
UnicodeBlock::IdeographicDescriptionCharacters |
UnicodeBlock::CJKSymbolsandPunctuation |
UnicodeBlock::Hiragana |
UnicodeBlock::Katakana |
UnicodeBlock::Bopomofo |
UnicodeBlock::HangulCompatibilityJamo |
UnicodeBlock::Kanbun |
UnicodeBlock::BopomofoExtended |
UnicodeBlock::CJKStrokes |
UnicodeBlock::KatakanaPhoneticExtensions |
UnicodeBlock::EnclosedCJKLettersandMonths |
UnicodeBlock::CJKCompatibility |
UnicodeBlock::CJKUnifiedIdeographsExtensionA |
UnicodeBlock::YijingHexagramSymbols |
UnicodeBlock::CJKUnifiedIdeographs |
UnicodeBlock::CJKCompatibilityIdeographs |
UnicodeBlock::CJKCompatibilityForms |
UnicodeBlock::HalfwidthandFullwidthForms => {
return true
}
_ => {}
}
}
// https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Ideographic_Plane
unicode_plane(codepoint) == 2
}

View file

@ -215,6 +215,7 @@ impl TextRunScanner {
let (mut start_position, mut end_position) = (0, 0); let (mut start_position, mut end_position) = (0, 0);
for (byte_index, character) in text.char_indices() { for (byte_index, character) in text.char_indices() {
if !character.is_control() {
let font = font_group.borrow_mut().find_by_codepoint(&mut font_context, character); let font = font_group.borrow_mut().find_by_codepoint(&mut font_context, character);
let bidi_level = match bidi_levels { let bidi_level = match bidi_levels {
@ -271,6 +272,7 @@ impl TextRunScanner {
run_info.script = script; run_info.script = script;
mapping.selected = selected; mapping.selected = selected;
} }
}
// Consume this character. // Consume this character.
end_position += character.len_utf8(); end_position += character.len_utf8();

View file

@ -84,7 +84,7 @@ ${helpers.predefined_type("font-synthesis",
${helpers.predefined_type( ${helpers.predefined_type(
"font-stretch", "font-stretch",
"FontStretch", "FontStretch",
initial_value="computed::NonNegativePercentage::hundred()", initial_value="computed::FontStretch::hundred()",
initial_specified_value="specified::FontStretch::normal()", initial_specified_value="specified::FontStretch::normal()",
animation_value_type="Percentage", animation_value_type="Percentage",
flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",

View file

@ -2305,13 +2305,10 @@ pub mod style_structs {
pub fn compute_font_hash(&mut self) { pub fn compute_font_hash(&mut self) {
// Corresponds to the fields in // Corresponds to the fields in
// `gfx::font_template::FontTemplateDescriptor`. // `gfx::font_template::FontTemplateDescriptor`.
//
// FIXME(emilio): Where's font-style?
let mut hasher: FnvHasher = Default::default(); let mut hasher: FnvHasher = Default::default();
// We hash the floating point number with four decimal self.font_weight.hash(&mut hasher);
// places. self.font_stretch.hash(&mut hasher);
hasher.write_u64((self.font_weight.0 * 10000.).trunc() as u64); self.font_style.hash(&mut hasher);
hasher.write_u64(((self.font_stretch.0).0 * 10000.).trunc() as u64);
self.font_family.hash(&mut hasher); self.font_family.hash(&mut hasher);
self.hash = hasher.finish() self.hash = hasher.finish()
} }

View file

@ -15,21 +15,20 @@ use gecko_bindings::sugar::refptr::RefPtr;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use std::fmt::{self, Write}; use std::fmt::{self, Write};
#[cfg(feature = "gecko")]
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
use std::slice; use std::slice;
use style_traits::{CssWriter, ParseError, ToCss}; use style_traits::{CssWriter, ParseError, ToCss};
use values::CSSFloat; use values::CSSFloat;
use values::animated::{ToAnimatedValue, ToAnimatedZero}; use values::animated::{ToAnimatedValue, ToAnimatedZero};
use values::computed::{Angle, Context, Integer, NonNegativeLength, Number, ToComputedValue}; use values::computed::{Angle, Context, Integer, NonNegative, NonNegativeLength, NonNegativePercentage};
use values::computed::{Number, Percentage, ToComputedValue};
use values::generics::font::{self as generics, FeatureTagValue, FontSettings, VariationValue}; use values::generics::font::{self as generics, FeatureTagValue, FontSettings, VariationValue};
use values::specified::font::{self as specified, MIN_FONT_WEIGHT, MAX_FONT_WEIGHT}; use values::specified::font::{self as specified, MIN_FONT_WEIGHT, MAX_FONT_WEIGHT};
use values::specified::length::{FontBaseSize, NoCalcLength}; use values::specified::length::{FontBaseSize, NoCalcLength};
pub use values::computed::Length as MozScriptMinSize; pub use values::computed::Length as MozScriptMinSize;
pub use values::specified::font::{FontSynthesis, MozScriptSizeMultiplier, XLang, XTextZoom}; pub use values::specified::font::{FontSynthesis, MozScriptSizeMultiplier, XLang, XTextZoom};
pub use values::computed::NonNegativePercentage as FontStretch;
/// A value for the font-weight property per: /// A value for the font-weight property per:
/// ///
@ -41,6 +40,12 @@ pub use values::computed::NonNegativePercentage as FontStretch;
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
pub struct FontWeight(pub Number); pub struct FontWeight(pub Number);
impl Hash for FontWeight {
fn hash<H: Hasher>(&self, hasher: &mut H) {
hasher.write_u64((self.0 * 10000.).trunc() as u64);
}
}
impl ToAnimatedValue for FontWeight { impl ToAnimatedValue for FontWeight {
type AnimatedValue = Number; type AnimatedValue = Number;
@ -853,6 +858,12 @@ impl ToAnimatedValue for FontStyleAngle {
} }
} }
impl Hash for FontStyleAngle {
fn hash<H: Hasher>(&self, hasher: &mut H) {
hasher.write_u64((self.0.degrees() * 10000.).trunc() as u64);
}
}
/// The computed value of `font-style`. /// The computed value of `font-style`.
/// ///
/// FIXME(emilio): Angle should be a custom type to handle clamping during /// FIXME(emilio): Angle should be a custom type to handle clamping during
@ -916,3 +927,43 @@ impl ToCss for FontStyle {
} }
} }
} }
/// A value for the font-stretch property per:
///
/// https://drafts.csswg.org/css-fonts-4/#propdef-font-stretch
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
pub struct FontStretch(pub NonNegativePercentage);
impl FontStretch {
/// 100%
pub fn hundred() -> Self {
FontStretch(NonNegativePercentage::hundred())
}
/// The float value of the percentage
#[inline]
pub fn value(&self) -> CSSFloat {
((self.0).0).0
}
}
impl ToAnimatedValue for FontStretch {
type AnimatedValue = Percentage;
#[inline]
fn to_animated_value(self) -> Self::AnimatedValue {
(self.0).0
}
#[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
FontStretch(NonNegative(animated))
}
}
impl Hash for FontStretch {
fn hash<H: Hasher>(&self, hasher: &mut H) {
hasher.write_u64((self.value() * 10000.).trunc() as u64);
}
}

View file

@ -228,7 +228,7 @@ impl Default for KeywordSize {
/// https://drafts.csswg.org/css-fonts-4/#font-style-prop /// https://drafts.csswg.org/css-fonts-4/#font-style-prop
#[allow(missing_docs)] #[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, Hash, MallocSizeOf,
PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero)] PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero)]
pub enum FontStyle<Angle> { pub enum FontStyle<Angle> {
#[animation(error)] #[animation(error)]

View file

@ -156,7 +156,7 @@ impl SpecifiedValueInfo for CounterStyleOrNone {
/// A wrapper of Non-negative values. /// A wrapper of Non-negative values.
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, Hash, MallocSizeOf,
PartialEq, PartialOrd, SpecifiedValueInfo, ToAnimatedZero, PartialEq, PartialOrd, SpecifiedValueInfo, ToAnimatedZero,
ToComputedValue, ToCss)] ToComputedValue, ToCss)]
pub struct NonNegative<T>(pub T); pub struct NonNegative<T>(pub T);

View file

@ -490,22 +490,22 @@ impl Parse for FontStretch {
} }
impl ToComputedValue for FontStretch { impl ToComputedValue for FontStretch {
type ComputedValue = NonNegative<ComputedPercentage>; type ComputedValue = computed::FontStretch;
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
match *self { match *self {
FontStretch::Stretch(ref percentage) => { FontStretch::Stretch(ref percentage) => {
NonNegative(percentage.to_computed_value(context)) computed::FontStretch(NonNegative(percentage.to_computed_value(context)))
}, },
FontStretch::Keyword(ref kw) => { FontStretch::Keyword(ref kw) => {
NonNegative(kw.compute()) computed::FontStretch(NonNegative(kw.compute()))
}, },
FontStretch::System(_) => self.compute_system(context), FontStretch::System(_) => self.compute_system(context),
} }
} }
fn from_computed_value(computed: &Self::ComputedValue) -> Self { fn from_computed_value(computed: &Self::ComputedValue) -> Self {
FontStretch::Stretch(Percentage::from_computed_value(&computed.0)) FontStretch::Stretch(Percentage::from_computed_value(&(computed.0).0))
} }
} }

View file

@ -1,3 +0,0 @@
[font-family-invalid-characters-001.xht]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[font-family-invalid-characters-003.xht]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[font-family-rule-001.xht]
type: reftest
expected: FAIL

View file

@ -0,0 +1,5 @@
[css3-text-line-break-baspglwj-015.html]
type: testharness
[ ]
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,5 @@
[css3-text-line-break-baspglwj-033.html]
type: testharness
[ ]
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,5 @@
[css3-text-line-break-baspglwj-034.html]
type: testharness
[ ]
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,5 @@
[css3-text-line-break-baspglwj-035.html]
type: testharness
[ ]
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,5 @@
[css3-text-line-break-baspglwj-037.html]
type: testharness
[ ]
expected:
if os == "mac": FAIL

View file

@ -1,4 +1,3 @@
[css3-text-line-break-opclns-006.html] [css3-text-line-break-opclns-006.html]
type: reftest type: reftest
expected: expected: FAIL
if os == "linux": FAIL

View file

@ -1,4 +1,3 @@
[css3-text-line-break-opclns-014.html] [css3-text-line-break-opclns-014.html]
type: reftest type: reftest
expected: expected: FAIL
if os == "linux": FAIL

View file

@ -1,4 +1,3 @@
[css3-text-line-break-opclns-015.html] [css3-text-line-break-opclns-015.html]
type: reftest type: reftest
expected: expected: FAIL
if os == "linux": FAIL

View file

@ -1,4 +1,3 @@
[css3-text-line-break-opclns-016.html] [css3-text-line-break-opclns-016.html]
type: reftest type: reftest
expected: expected: FAIL
if os == "linux": FAIL

View file

@ -1,4 +1,3 @@
[css3-text-line-break-opclns-018.html] [css3-text-line-break-opclns-018.html]
type: reftest type: reftest
expected: expected: FAIL
if os == "linux": FAIL

View file

@ -1,4 +1,3 @@
[css3-text-line-break-opclns-019.html] [css3-text-line-break-opclns-019.html]
type: reftest type: reftest
expected: expected: FAIL
if os == "linux": FAIL

View file

@ -1,4 +1,3 @@
[css3-text-line-break-opclns-020.html] [css3-text-line-break-opclns-020.html]
type: reftest type: reftest
expected: expected: FAIL
if os == "linux": FAIL

View file

@ -1,4 +1,3 @@
[css3-text-line-break-opclns-023.html] [css3-text-line-break-opclns-023.html]
type: reftest type: reftest
expected: expected: FAIL
if os == "linux": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-025.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-026.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-027.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-028.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-029.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-030.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-031.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-032.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-033.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-034.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-035.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-036.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-037.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -1,4 +1,3 @@
[css3-text-line-break-opclns-119.html] [css3-text-line-break-opclns-119.html]
type: reftest type: reftest
expected: expected: FAIL
if os == "linux": FAIL

View file

@ -1,4 +1,3 @@
[css3-text-line-break-opclns-120.html] [css3-text-line-break-opclns-120.html]
type: reftest type: reftest
expected: expected: FAIL
if os == "linux": FAIL

View file

@ -1,4 +1,3 @@
[css3-text-line-break-opclns-122.html] [css3-text-line-break-opclns-122.html]
type: reftest type: reftest
expected: expected: FAIL
if os == "linux": FAIL

View file

@ -1,4 +1,3 @@
[css3-text-line-break-opclns-123.html] [css3-text-line-break-opclns-123.html]
type: reftest type: reftest
expected: expected: FAIL
if os == "linux": FAIL

View file

@ -1,4 +1,3 @@
[css3-text-line-break-opclns-124.html] [css3-text-line-break-opclns-124.html]
type: reftest type: reftest
expected: expected: FAIL
if os == "linux": FAIL

View file

@ -1,4 +1,3 @@
[css3-text-line-break-opclns-125.html] [css3-text-line-break-opclns-125.html]
type: reftest type: reftest
expected: expected: FAIL
if os == "linux": FAIL

View file

@ -1,4 +1,3 @@
[css3-text-line-break-opclns-128.html] [css3-text-line-break-opclns-128.html]
type: reftest type: reftest
expected: expected: FAIL
if os == "linux": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-130.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-131.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-132.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-133.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-134.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-135.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-136.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-137.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-138.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-139.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-140.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-141.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -0,0 +1,4 @@
[css3-text-line-break-opclns-142.html]
type: reftest
expected:
if os == "mac": FAIL

View file

@ -1,4 +1,3 @@
[word-break-break-all-007.html] [word-break-break-all-007.html]
type: reftest type: reftest
expected: expected: FAIL
if os == "linux": FAIL

View file

@ -1,3 +1,4 @@
[word-break-normal-bo-000.html] [word-break-normal-bo-000.html]
type: reftest type: reftest
expected: FAIL expected:
if os == "linux": FAIL

View file

@ -2267,6 +2267,42 @@
{} {}
] ]
], ],
"css/font_fallback_01.html": [
[
"/_mozilla/css/font_fallback_01.html",
[
[
"/_mozilla/css/font_fallback_failed_ref.html",
"!="
]
],
{}
]
],
"css/font_fallback_02.html": [
[
"/_mozilla/css/font_fallback_02.html",
[
[
"/_mozilla/css/font_fallback_failed_ref.html",
"!="
]
],
{}
]
],
"css/font_fallback_03.html": [
[
"/_mozilla/css/font_fallback_03.html",
[
[
"/_mozilla/css/font_fallback_failed_ref.html",
"!="
]
],
{}
]
],
"css/font_size.html": [ "css/font_size.html": [
[ [
"/_mozilla/css/font_size.html", "/_mozilla/css/font_size.html",
@ -8248,6 +8284,11 @@
{} {}
] ]
], ],
"css/font_fallback_failed_ref.html": [
[
{}
]
],
"css/font_size_ref.html": [ "css/font_size_ref.html": [
[ [
{} {}
@ -61506,6 +61547,22 @@
"0c8f70a37e6d56370576c7f5ba7f1df359db8f9c", "0c8f70a37e6d56370576c7f5ba7f1df359db8f9c",
"support" "support"
], ],
"css/font_fallback_01.html": [
"1dee8f90d8d1ee1f91e9ad9d1442a3abfec5d596",
"reftest"
],
"css/font_fallback_02.html": [
"60d61d9d51206c8b741ef7914447329722950d67",
"reftest"
],
"css/font_fallback_03.html": [
"4ef0d7e423624e93b3f028d9baa5a11f9e8f28b2",
"reftest"
],
"css/font_fallback_failed_ref.html": [
"ebc502a8b3e0fd4dd5eb79a1dae83ab3e6774178",
"support"
],
"css/font_size.html": [ "css/font_size.html": [
"7c944acb4f7d909083888a355e6d664c23081b99", "7c944acb4f7d909083888a355e6d664c23081b99",
"reftest" "reftest"

View file

@ -0,0 +1,4 @@
[font_fallback_01.html]
type: reftest
expected:
if os == "linux": FAIL

View file

@ -0,0 +1,4 @@
[font_fallback_02.html]
type: reftest
expected:
if os == "linux": FAIL

View file

@ -0,0 +1,4 @@
<!doctype html>
<link rel="mismatch" href="font_fallback_failed_ref.html">
<meta name="assert" content="A fallback font must be found for a hiragana character">

View file

@ -0,0 +1,4 @@
<!doctype html>
<link rel="mismatch" href="font_fallback_failed_ref.html">
<meta name="assert" content="A fallback font must be found for a katakana character">

View file

@ -0,0 +1,4 @@
<!doctype html>
<link rel="mismatch" href="font_fallback_failed_ref.html">
<meta name="assert" content="A fallback font must be found for an emoji with text presentation">

View file

@ -0,0 +1,3 @@
<!doctype html>
<!-- This codepoint is in the Private Use Area, therefore we would expect it to render as a missing glyph. -->
&#xE000;