mirror of
https://github.com/servo/servo.git
synced 2025-08-07 14:35:33 +01:00
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:
commit
77dcc678fe
83 changed files with 1379 additions and 462 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[font-family-invalid-characters-001.xht]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[font-family-invalid-characters-003.xht]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[font-family-rule-001.xht]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
[css3-text-line-break-baspglwj-015.html]
|
||||||
|
type: testharness
|
||||||
|
[ ]
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,5 @@
|
||||||
|
[css3-text-line-break-baspglwj-033.html]
|
||||||
|
type: testharness
|
||||||
|
[ ]
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,5 @@
|
||||||
|
[css3-text-line-break-baspglwj-034.html]
|
||||||
|
type: testharness
|
||||||
|
[ ]
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,5 @@
|
||||||
|
[css3-text-line-break-baspglwj-035.html]
|
||||||
|
type: testharness
|
||||||
|
[ ]
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,5 @@
|
||||||
|
[css3-text-line-break-baspglwj-037.html]
|
||||||
|
type: testharness
|
||||||
|
[ ]
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-025.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-026.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-027.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-028.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-029.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-030.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-031.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-032.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-033.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-034.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-035.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-036.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-037.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-130.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-131.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-132.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-133.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-134.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-135.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-136.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-137.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-138.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-139.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-140.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-141.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -0,0 +1,4 @@
|
||||||
|
[css3-text-line-break-opclns-142.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "mac": FAIL
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
4
tests/wpt/mozilla/meta/css/font_fallback_01.html.ini
Normal file
4
tests/wpt/mozilla/meta/css/font_fallback_01.html.ini
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[font_fallback_01.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "linux": FAIL
|
4
tests/wpt/mozilla/meta/css/font_fallback_02.html.ini
Normal file
4
tests/wpt/mozilla/meta/css/font_fallback_02.html.ini
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[font_fallback_02.html]
|
||||||
|
type: reftest
|
||||||
|
expected:
|
||||||
|
if os == "linux": FAIL
|
4
tests/wpt/mozilla/tests/css/font_fallback_01.html
Normal file
4
tests/wpt/mozilla/tests/css/font_fallback_01.html
Normal 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">
|
||||||
|
の
|
4
tests/wpt/mozilla/tests/css/font_fallback_02.html
Normal file
4
tests/wpt/mozilla/tests/css/font_fallback_02.html
Normal 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">
|
||||||
|
コ
|
4
tests/wpt/mozilla/tests/css/font_fallback_03.html
Normal file
4
tests/wpt/mozilla/tests/css/font_fallback_03.html
Normal 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">
|
||||||
|
✂
|
|
@ -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. -->
|
||||||
|

|
Loading…
Add table
Add a link
Reference in a new issue