mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
fonts: Add support for more @font-face features (#32164)
There are a couple major changes here: 1. Support is added for the `weight`, `style`, `stretch` and `unicode-range` declarations in `@font-face`. 2. Font matching in the font cache can return templates and `FontGroupFamily` can own mulitple templates. This is due to needing support for "composite fonts". These are `@font-face` declarations that only differ in their `unicode-range` definition. This fixes a lot of non-determinism in font selection especially when dealing with pages that define "composite faces." A notable example of such a page is servo.org, which now consistently displays the correct web font. One test starts to fail due to an uncovered bug, but this will be fixed in a followup change. Fixes #20686. Fixes #20684. Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
This commit is contained in:
parent
628e33bfa9
commit
4732da3477
56 changed files with 613 additions and 593 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1957,6 +1957,7 @@ dependencies = [
|
|||
"core-foundation",
|
||||
"core-graphics 0.22.3",
|
||||
"core-text 19.2.0",
|
||||
"cssparser",
|
||||
"dwrote",
|
||||
"euclid",
|
||||
"fnv",
|
||||
|
|
|
@ -16,6 +16,7 @@ doctest = false
|
|||
[dependencies]
|
||||
app_units = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
cssparser = { workspace = true }
|
||||
euclid = { workspace = true }
|
||||
fnv = { workspace = true }
|
||||
fontsan = { git = "https://github.com/servo/fontsan" }
|
||||
|
|
|
@ -21,6 +21,7 @@ use smallvec::SmallVec;
|
|||
use style::computed_values::font_variant_caps;
|
||||
use style::properties::style_structs::Font as FontStyleStruct;
|
||||
use style::values::computed::font::{GenericFontFamily, SingleFontFamily};
|
||||
use style::values::computed::{FontStretch, FontStyle, FontWeight};
|
||||
use unicode_script::Script;
|
||||
use webrender_api::FontInstanceKey;
|
||||
|
||||
|
@ -58,7 +59,7 @@ pub trait PlatformFontMethods: Sized {
|
|||
pt_size: Option<Au>,
|
||||
) -> Result<PlatformFont, &'static str> {
|
||||
let data = template.data();
|
||||
let face_index = template.borrow().identifier().index();
|
||||
let face_index = template.identifier().index();
|
||||
let font_identifier = template.borrow().identifier.clone();
|
||||
Self::new_from_data(font_identifier, data, face_index, pt_size)
|
||||
}
|
||||
|
@ -150,17 +151,23 @@ impl FontMetrics {
|
|||
/// 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
|
||||
/// font data (weight, stretch, etc.).
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
|
||||
pub struct FontDescriptor {
|
||||
pub template_descriptor: FontTemplateDescriptor,
|
||||
pub weight: FontWeight,
|
||||
pub stretch: FontStretch,
|
||||
pub style: FontStyle,
|
||||
pub variant: font_variant_caps::T,
|
||||
pub pt_size: Au,
|
||||
}
|
||||
|
||||
impl Eq for FontDescriptor {}
|
||||
|
||||
impl<'a> From<&'a FontStyleStruct> for FontDescriptor {
|
||||
fn from(style: &'a FontStyleStruct) -> Self {
|
||||
FontDescriptor {
|
||||
template_descriptor: FontTemplateDescriptor::from(style),
|
||||
weight: style.font_weight,
|
||||
stretch: style.font_stretch,
|
||||
style: style.font_style,
|
||||
variant: style.font_variant_caps,
|
||||
pt_size: Au::from_f32_px(style.font_size.computed_size().px()),
|
||||
}
|
||||
|
@ -209,7 +216,7 @@ impl Font {
|
|||
|
||||
/// A unique identifier for the font, allowing comparison.
|
||||
pub fn identifier(&self) -> FontIdentifier {
|
||||
self.template.borrow().identifier.clone()
|
||||
self.template.identifier()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -405,7 +412,7 @@ impl FontGroup {
|
|||
.font_family
|
||||
.families
|
||||
.iter()
|
||||
.map(|family| FontGroupFamily::new(descriptor.clone(), family))
|
||||
.map(|family| FontGroupFamily::new(family))
|
||||
.collect();
|
||||
|
||||
FontGroup {
|
||||
|
@ -436,19 +443,28 @@ impl FontGroup {
|
|||
Some(font)
|
||||
};
|
||||
|
||||
let has_glyph = |font: &FontRef| font.borrow().has_glyph_for(codepoint);
|
||||
let glyph_in_font = |font: &FontRef| font.borrow().has_glyph_for(codepoint);
|
||||
let char_in_template =
|
||||
|template: FontTemplateRef| template.char_in_unicode_range(codepoint);
|
||||
|
||||
if let Some(font) = self.find(font_context, has_glyph) {
|
||||
if let Some(font) = self.find(font_context, char_in_template, glyph_in_font) {
|
||||
return font_or_synthesized_small_caps(font);
|
||||
}
|
||||
|
||||
if let Some(ref last_matching_fallback) = self.last_matching_fallback {
|
||||
if has_glyph(last_matching_fallback) {
|
||||
if char_in_template(last_matching_fallback.borrow().template.clone()) &&
|
||||
glyph_in_font(last_matching_fallback)
|
||||
{
|
||||
return font_or_synthesized_small_caps(last_matching_fallback.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(font) = self.find_fallback(font_context, Some(codepoint), has_glyph) {
|
||||
if let Some(font) = self.find_fallback(
|
||||
font_context,
|
||||
Some(codepoint),
|
||||
char_in_template,
|
||||
glyph_in_font,
|
||||
) {
|
||||
self.last_matching_fallback = Some(font.clone());
|
||||
return font_or_synthesized_small_caps(font);
|
||||
}
|
||||
|
@ -458,80 +474,167 @@ impl FontGroup {
|
|||
|
||||
/// Find the first available font in the group, or the first available fallback font.
|
||||
pub fn first<S: FontSource>(&mut self, font_context: &mut FontContext<S>) -> Option<FontRef> {
|
||||
self.find(font_context, |_| true)
|
||||
.or_else(|| self.find_fallback(font_context, None, |_| true))
|
||||
// From https://drafts.csswg.org/css-fonts/#first-available-font:
|
||||
// > The first available font, used for example in the definition of font-relative lengths
|
||||
// > such as ex or in the definition of the line-height property, is defined to be the first
|
||||
// > font for which the character U+0020 (space) is not excluded by a unicode-range, given the
|
||||
// > font families in the font-family list (or a user agent’s default font if none are
|
||||
// > available).
|
||||
// > Note: it does not matter whether that font actually has a glyph for the space character.
|
||||
let space_in_template = |template: FontTemplateRef| template.char_in_unicode_range(' ');
|
||||
let font_predicate = |_: &FontRef| true;
|
||||
self.find(font_context, space_in_template, font_predicate)
|
||||
.or_else(|| self.find_fallback(font_context, None, space_in_template, font_predicate))
|
||||
}
|
||||
|
||||
/// Find a font which returns true for `predicate`. This method mutates because we may need to
|
||||
/// load new font data in the process of finding a suitable font.
|
||||
fn find<S, P>(&mut self, font_context: &mut FontContext<S>, predicate: P) -> Option<FontRef>
|
||||
where
|
||||
S: FontSource,
|
||||
P: FnMut(&FontRef) -> bool,
|
||||
{
|
||||
self.families
|
||||
.iter_mut()
|
||||
.filter_map(|family| family.font(font_context))
|
||||
.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>(
|
||||
/// Attempts to find a font which matches the given `template_predicate` and `font_predicate`.
|
||||
/// This method mutates because we may need to load new font data in the process of finding
|
||||
/// a suitable font.
|
||||
fn find<S, TemplatePredicate, FontPredicate>(
|
||||
&mut self,
|
||||
font_context: &mut FontContext<S>,
|
||||
codepoint: Option<char>,
|
||||
predicate: P,
|
||||
template_predicate: TemplatePredicate,
|
||||
font_predicate: FontPredicate,
|
||||
) -> Option<FontRef>
|
||||
where
|
||||
S: FontSource,
|
||||
P: FnMut(&FontRef) -> bool,
|
||||
TemplatePredicate: Fn(FontTemplateRef) -> bool,
|
||||
FontPredicate: Fn(&FontRef) -> bool,
|
||||
{
|
||||
iter::once(FontFamilyDescriptor::default())
|
||||
let font_descriptor = self.descriptor.clone();
|
||||
self.families
|
||||
.iter_mut()
|
||||
.filter_map(|font_group_family| {
|
||||
font_group_family.find(
|
||||
&font_descriptor,
|
||||
font_context,
|
||||
&template_predicate,
|
||||
&font_predicate,
|
||||
)
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
/// Attempts to find a suitable fallback font which matches the given `template_predicate` and
|
||||
/// `font_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, TemplatePredicate, FontPredicate>(
|
||||
&mut self,
|
||||
font_context: &mut FontContext<S>,
|
||||
codepoint: Option<char>,
|
||||
template_predicate: TemplatePredicate,
|
||||
font_predicate: FontPredicate,
|
||||
) -> Option<FontRef>
|
||||
where
|
||||
S: FontSource,
|
||||
TemplatePredicate: Fn(FontTemplateRef) -> bool,
|
||||
FontPredicate: Fn(&FontRef) -> bool,
|
||||
{
|
||||
iter::once(FontFamilyDescriptor::serif())
|
||||
.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)
|
||||
.into_iter()
|
||||
.filter_map(|family_descriptor| {
|
||||
FontGroupFamily {
|
||||
family_descriptor,
|
||||
members: None,
|
||||
}
|
||||
.find(
|
||||
&self.descriptor,
|
||||
font_context,
|
||||
&template_predicate,
|
||||
&font_predicate,
|
||||
)
|
||||
})
|
||||
.next()
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`FontGroupFamily`] can have multiple members if it is a "composite face", meaning
|
||||
/// that it is defined by multiple `@font-face` declarations which vary only by their
|
||||
/// `unicode-range` descriptors. In this case, font selection will select a single member
|
||||
/// that contains the necessary unicode character. Unicode ranges are specified by the
|
||||
/// [`FontGroupFamilyMember::template`] member.
|
||||
#[derive(Debug)]
|
||||
struct FontGroupFamilyMember {
|
||||
template: FontTemplateRef,
|
||||
font: Option<FontRef>,
|
||||
loaded: bool,
|
||||
}
|
||||
|
||||
/// A `FontGroupFamily` is a single font family in a `FontGroup`. It corresponds to one of the
|
||||
/// families listed in the `font-family` CSS property. The corresponding font data is lazy-loaded,
|
||||
/// only if actually needed.
|
||||
/// only if actually needed. A single `FontGroupFamily` can have multiple fonts, in the case that
|
||||
/// individual fonts only cover part of the Unicode range.
|
||||
#[derive(Debug)]
|
||||
struct FontGroupFamily {
|
||||
font_descriptor: FontDescriptor,
|
||||
family_descriptor: FontFamilyDescriptor,
|
||||
loaded: bool,
|
||||
font: Option<FontRef>,
|
||||
members: Option<Vec<FontGroupFamilyMember>>,
|
||||
}
|
||||
|
||||
impl FontGroupFamily {
|
||||
fn new(font_descriptor: FontDescriptor, family: &SingleFontFamily) -> FontGroupFamily {
|
||||
fn new(family: &SingleFontFamily) -> FontGroupFamily {
|
||||
let family_descriptor =
|
||||
FontFamilyDescriptor::new(FontFamilyName::from(family), FontSearchScope::Any);
|
||||
|
||||
FontGroupFamily {
|
||||
font_descriptor,
|
||||
family_descriptor,
|
||||
members: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn find<S, TemplatePredicate, FontPredicate>(
|
||||
&mut self,
|
||||
font_descriptor: &FontDescriptor,
|
||||
font_context: &mut FontContext<S>,
|
||||
template_predicate: &TemplatePredicate,
|
||||
font_predicate: &FontPredicate,
|
||||
) -> Option<FontRef>
|
||||
where
|
||||
S: FontSource,
|
||||
TemplatePredicate: Fn(FontTemplateRef) -> bool,
|
||||
FontPredicate: Fn(&FontRef) -> bool,
|
||||
{
|
||||
self.members(font_descriptor, font_context)
|
||||
.filter_map(|member| {
|
||||
if !template_predicate(member.template.clone()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if !member.loaded {
|
||||
member.font = font_context.font(member.template.clone(), font_descriptor);
|
||||
member.loaded = true;
|
||||
}
|
||||
if matches!(&member.font, Some(font) if font_predicate(font)) {
|
||||
return member.font.clone();
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
fn members<'a, S: FontSource>(
|
||||
&'a mut self,
|
||||
font_descriptor: &FontDescriptor,
|
||||
font_context: &mut FontContext<S>,
|
||||
) -> impl Iterator<Item = &mut FontGroupFamilyMember> + 'a {
|
||||
let family_descriptor = &self.family_descriptor;
|
||||
let members = self.members.get_or_insert_with(|| {
|
||||
font_context
|
||||
.matching_templates(font_descriptor, family_descriptor)
|
||||
.into_iter()
|
||||
.map(|template| FontGroupFamilyMember {
|
||||
template,
|
||||
loaded: false,
|
||||
font: None,
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
|
||||
/// Returns the font within this family which matches the style. We'll fetch the data from the
|
||||
/// `FontContext` the first time this method is called, and return a cached reference on
|
||||
/// subsequent calls.
|
||||
fn font<S: FontSource>(&mut self, font_context: &mut FontContext<S>) -> Option<FontRef> {
|
||||
if !self.loaded {
|
||||
self.font = font_context.font(&self.font_descriptor, &self.family_descriptor);
|
||||
self.loaded = true;
|
||||
}
|
||||
|
||||
self.font.clone()
|
||||
members.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -636,7 +739,7 @@ impl FontFamilyDescriptor {
|
|||
FontFamilyDescriptor { name, scope }
|
||||
}
|
||||
|
||||
fn default() -> FontFamilyDescriptor {
|
||||
fn serif() -> FontFamilyDescriptor {
|
||||
FontFamilyDescriptor {
|
||||
name: FontFamilyName::Generic(atom!("serif")),
|
||||
scope: FontSearchScope::Local,
|
||||
|
|
|
@ -5,32 +5,37 @@
|
|||
use std::borrow::ToOwned;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
use std::ops::{Deref, RangeInclusive};
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{f32, fmt, mem, thread};
|
||||
|
||||
use app_units::Au;
|
||||
use gfx_traits::WebrenderApi;
|
||||
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
|
||||
use ipc_channel::ipc::{self, IpcBytesSender, IpcReceiver, IpcSender};
|
||||
use log::{debug, trace};
|
||||
use net_traits::request::{Destination, Referrer, RequestBuilder};
|
||||
use net_traits::{fetch_async, CoreResourceThread, FetchResponseMsg};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use servo_atoms::Atom;
|
||||
use servo_url::ServoUrl;
|
||||
use style::font_face::{FontFaceSourceFormat, FontFaceSourceFormatKeyword, Source};
|
||||
use style::font_face::{
|
||||
FontFaceRuleData, FontFaceSourceFormat, FontFaceSourceFormatKeyword,
|
||||
FontStyle as FontFaceStyle, Source,
|
||||
};
|
||||
use style::media_queries::Device;
|
||||
use style::shared_lock::SharedRwLockReadGuard;
|
||||
use style::stylesheets::{Stylesheet, StylesheetInDocument};
|
||||
use style::values::computed::font::{FixedPoint, FontStyleFixedPoint};
|
||||
use style::values::computed::{FontStretch, FontWeight};
|
||||
use style::values::specified::FontStretch as SpecifiedFontStretch;
|
||||
use webrender_api::{FontInstanceKey, FontKey};
|
||||
|
||||
use crate::font::{FontFamilyDescriptor, FontFamilyName, FontSearchScope, PlatformFontMethods};
|
||||
use crate::font::{FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontSearchScope};
|
||||
use crate::font_context::FontSource;
|
||||
use crate::font_template::{
|
||||
FontTemplate, FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods,
|
||||
};
|
||||
use crate::platform::font::PlatformFont;
|
||||
use crate::platform::font_list::{
|
||||
for_each_available_family, for_each_variation, system_default_family, LocalFontIdentifier,
|
||||
SANS_SERIF_FONT_FAMILY,
|
||||
|
@ -42,18 +47,6 @@ pub struct FontTemplates {
|
|||
templates: Vec<FontTemplateRef>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FontTemplateAndWebRenderFontKey {
|
||||
pub font_template: FontTemplateRef,
|
||||
pub font_key: FontKey,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct SerializedFontTemplateAndWebRenderFontKey {
|
||||
pub serialized_font_template: SerializedFontTemplate,
|
||||
pub font_key: FontKey,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub enum FontIdentifier {
|
||||
Local(LocalFontIdentifier),
|
||||
|
@ -76,54 +69,51 @@ pub struct SerializedFontTemplate {
|
|||
bytes_receiver: ipc_channel::ipc::IpcBytesReceiver,
|
||||
}
|
||||
|
||||
impl SerializedFontTemplate {
|
||||
pub fn to_font_template(&self) -> FontTemplate {
|
||||
let font_data = self.bytes_receiver.recv().ok();
|
||||
FontTemplate {
|
||||
identifier: self.identifier.clone(),
|
||||
descriptor: self.descriptor,
|
||||
data: font_data.map(Arc::new),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FontTemplates {
|
||||
/// Find a font in this family that matches a given descriptor.
|
||||
pub fn find_font_for_style(
|
||||
pub fn find_for_descriptor(
|
||||
&mut self,
|
||||
desc: &FontTemplateDescriptor,
|
||||
) -> Option<FontTemplateRef> {
|
||||
descriptor_to_match: &FontDescriptor,
|
||||
) -> Vec<FontTemplateRef> {
|
||||
// TODO(Issue #189): optimize lookup for
|
||||
// regular/bold/italic/bolditalic with fixed offsets and a
|
||||
// static decision table for fallback between these values.
|
||||
for template in &mut self.templates {
|
||||
if template.descriptor_matches(desc) {
|
||||
return Some(template.clone());
|
||||
}
|
||||
let matching_templates: Vec<FontTemplateRef> = self
|
||||
.templates
|
||||
.iter()
|
||||
.filter(|template| template.matches_font_descriptor(descriptor_to_match))
|
||||
.cloned()
|
||||
.collect();
|
||||
if !matching_templates.is_empty() {
|
||||
return matching_templates;
|
||||
}
|
||||
|
||||
// We didn't find an exact match. Do more expensive fuzzy matching.
|
||||
// TODO(#190): Do a better job.
|
||||
let (mut best_template, mut best_distance) = (None, f32::MAX);
|
||||
let mut best_templates = Vec::new();
|
||||
let mut best_distance = f32::MAX;
|
||||
for template in self.templates.iter() {
|
||||
let distance = template.descriptor_distance(desc);
|
||||
let distance = template.descriptor_distance(descriptor_to_match);
|
||||
if distance < best_distance {
|
||||
best_template = Some(template);
|
||||
best_templates = vec![template.clone()];
|
||||
best_distance = distance
|
||||
} else if distance == best_distance {
|
||||
best_templates.push(template.clone());
|
||||
}
|
||||
}
|
||||
if best_template.is_some() {
|
||||
return best_template.cloned();
|
||||
|
||||
if !best_templates.is_empty() {
|
||||
return best_templates;
|
||||
}
|
||||
|
||||
// If a request is made for a font family that exists,
|
||||
// pick the first valid font in the family if we failed
|
||||
// to find an exact match for the descriptor.
|
||||
for template in &mut self.templates.iter() {
|
||||
return Some(template.clone());
|
||||
return vec![template.clone()];
|
||||
}
|
||||
|
||||
None
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
pub fn add_template(&mut self, new_template: FontTemplate) {
|
||||
|
@ -142,30 +132,25 @@ impl FontTemplates {
|
|||
/// Commands that the FontContext sends to the font cache thread.
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub enum Command {
|
||||
GetFontTemplate(
|
||||
FontTemplateDescriptor,
|
||||
GetFontTemplates(
|
||||
FontDescriptor,
|
||||
FontFamilyDescriptor,
|
||||
IpcSender<Reply>,
|
||||
IpcSender<Vec<SerializedFontTemplate>>,
|
||||
),
|
||||
GetFontInstance(FontKey, Au, IpcSender<FontInstanceKey>),
|
||||
AddWebFont(LowercaseString, Vec<Source>, IpcSender<()>),
|
||||
AddDownloadedWebFont(LowercaseString, ServoUrl, Vec<u8>, IpcSender<()>),
|
||||
GetFontInstance(FontIdentifier, Au, IpcSender<FontInstanceKey>),
|
||||
AddWebFont(CSSFontFaceDescriptors, Vec<Source>, IpcSender<()>),
|
||||
AddDownloadedWebFont(CSSFontFaceDescriptors, ServoUrl, Vec<u8>, IpcSender<()>),
|
||||
Exit(IpcSender<()>),
|
||||
Ping,
|
||||
}
|
||||
|
||||
/// Reply messages sent from the font cache thread to the FontContext caller.
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub enum Reply {
|
||||
GetFontTemplateReply(Option<SerializedFontTemplateAndWebRenderFontKey>),
|
||||
}
|
||||
|
||||
/// The font cache thread itself. It maintains a list of reference counted
|
||||
/// font templates that are currently in use.
|
||||
struct FontCache {
|
||||
port: IpcReceiver<Command>,
|
||||
channel_to_self: IpcSender<Command>,
|
||||
generic_fonts: HashMap<FontFamilyName, LowercaseString>,
|
||||
font_data: HashMap<FontIdentifier, Arc<Vec<u8>>>,
|
||||
local_families: HashMap<LowercaseString, FontTemplates>,
|
||||
web_families: HashMap<LowercaseString, FontTemplates>,
|
||||
core_resource_thread: CoreResourceThread,
|
||||
|
@ -207,61 +192,57 @@ impl FontCache {
|
|||
let msg = self.port.recv().unwrap();
|
||||
|
||||
match msg {
|
||||
Command::GetFontTemplate(template_descriptor, family_descriptor, result) => {
|
||||
let Some(font_template_info) =
|
||||
self.find_font_template(&template_descriptor, &family_descriptor)
|
||||
else {
|
||||
let _ = result.send(Reply::GetFontTemplateReply(None));
|
||||
continue;
|
||||
};
|
||||
Command::GetFontTemplates(descriptor_to_match, family_descriptor, result) => {
|
||||
let templates =
|
||||
self.find_font_templates(&descriptor_to_match, &family_descriptor);
|
||||
debug!("Found templates for descriptor {descriptor_to_match:?}: ");
|
||||
debug!(" {templates:?}");
|
||||
|
||||
let (serialized_templates, senders): (
|
||||
Vec<SerializedFontTemplate>,
|
||||
Vec<(FontTemplateRef, IpcBytesSender)>,
|
||||
) = templates
|
||||
.into_iter()
|
||||
.map(|template| {
|
||||
let (bytes_sender, bytes_receiver) =
|
||||
ipc::bytes_channel().expect("failed to create IPC channel");
|
||||
let serialized_font_template = SerializedFontTemplate {
|
||||
identifier: font_template_info.font_template.borrow().identifier.clone(),
|
||||
descriptor: font_template_info.font_template.borrow().descriptor,
|
||||
(
|
||||
SerializedFontTemplate {
|
||||
identifier: template.identifier().clone(),
|
||||
descriptor: template.descriptor().clone(),
|
||||
bytes_receiver,
|
||||
};
|
||||
|
||||
let _ = result.send(Reply::GetFontTemplateReply(Some(
|
||||
SerializedFontTemplateAndWebRenderFontKey {
|
||||
serialized_font_template,
|
||||
font_key: font_template_info.font_key,
|
||||
},
|
||||
)));
|
||||
(template.clone(), bytes_sender),
|
||||
)
|
||||
})
|
||||
.unzip();
|
||||
|
||||
let _ = result.send(serialized_templates);
|
||||
|
||||
// NB: This will load the font into memory if it hasn't been loaded already.
|
||||
let _ = bytes_sender.send(&font_template_info.font_template.data());
|
||||
for (font_template, bytes_sender) in senders.iter() {
|
||||
let identifier = font_template.identifier();
|
||||
let data = self
|
||||
.font_data
|
||||
.entry(identifier)
|
||||
.or_insert_with(|| font_template.data());
|
||||
let _ = bytes_sender.send(&data);
|
||||
}
|
||||
},
|
||||
Command::GetFontInstance(font_key, size, result) => {
|
||||
let webrender_api = &self.webrender_api;
|
||||
|
||||
let instance_key =
|
||||
*self
|
||||
.font_instances
|
||||
.entry((font_key, size))
|
||||
.or_insert_with(|| {
|
||||
webrender_api.add_font_instance(font_key, size.to_f32_px())
|
||||
});
|
||||
|
||||
let _ = result.send(instance_key);
|
||||
Command::GetFontInstance(identifier, pt_size, result) => {
|
||||
let _ = result.send(self.get_font_instance(identifier, pt_size));
|
||||
},
|
||||
Command::AddWebFont(family_name, sources, result) => {
|
||||
self.handle_add_web_font(family_name, sources, result);
|
||||
Command::AddWebFont(css_font_face_descriptors, sources, result) => {
|
||||
self.handle_add_web_font(css_font_face_descriptors, sources, result);
|
||||
},
|
||||
Command::AddDownloadedWebFont(family_name, url, bytes, result) => {
|
||||
Command::AddDownloadedWebFont(css_font_face_descriptors, url, data, result) => {
|
||||
let family_name = css_font_face_descriptors.family_name.clone();
|
||||
let templates = &mut self.web_families.get_mut(&family_name).unwrap();
|
||||
|
||||
let data = Arc::new(bytes);
|
||||
let identifier = FontIdentifier::Web(url.clone());
|
||||
let Ok(handle) = PlatformFont::new_from_data(identifier, data.clone(), 0, None)
|
||||
else {
|
||||
drop(result.send(()));
|
||||
return;
|
||||
};
|
||||
|
||||
let descriptor = handle.descriptor();
|
||||
templates.add_template(FontTemplate::new_web_font(url, descriptor, data));
|
||||
if let Ok(template) =
|
||||
FontTemplate::new_web_font(url, Arc::new(data), css_font_face_descriptors)
|
||||
{
|
||||
templates.add_template(template);
|
||||
}
|
||||
drop(result.send(()));
|
||||
},
|
||||
Command::Ping => (),
|
||||
|
@ -275,10 +256,11 @@ impl FontCache {
|
|||
|
||||
fn handle_add_web_font(
|
||||
&mut self,
|
||||
family_name: LowercaseString,
|
||||
css_font_face_descriptors: CSSFontFaceDescriptors,
|
||||
mut sources: Vec<Source>,
|
||||
sender: IpcSender<()>,
|
||||
) {
|
||||
let family_name = css_font_face_descriptors.family_name.clone();
|
||||
let src = if let Some(src) = sources.pop() {
|
||||
src
|
||||
} else {
|
||||
|
@ -330,7 +312,7 @@ impl FontCache {
|
|||
trace!("@font-face {} EOF={:?}", family_name, response);
|
||||
if response.is_err() || !*response_valid.lock().unwrap() {
|
||||
let msg = Command::AddWebFont(
|
||||
family_name.clone(),
|
||||
css_font_face_descriptors.clone(),
|
||||
sources.clone(),
|
||||
sender.clone(),
|
||||
);
|
||||
|
@ -349,7 +331,7 @@ impl FontCache {
|
|||
family_name, url
|
||||
);
|
||||
let msg = Command::AddWebFont(
|
||||
family_name.clone(),
|
||||
css_font_face_descriptors.clone(),
|
||||
sources.clone(),
|
||||
sender.clone(),
|
||||
);
|
||||
|
@ -358,7 +340,7 @@ impl FontCache {
|
|||
},
|
||||
};
|
||||
let command = Command::AddDownloadedWebFont(
|
||||
family_name.clone(),
|
||||
css_font_face_descriptors.clone(),
|
||||
url.clone().into(),
|
||||
bytes,
|
||||
sender.clone(),
|
||||
|
@ -379,7 +361,8 @@ impl FontCache {
|
|||
if found {
|
||||
sender.send(()).unwrap();
|
||||
} else {
|
||||
let msg = Command::AddWebFont(family_name, sources, sender);
|
||||
let msg =
|
||||
Command::AddWebFont(css_font_face_descriptors.clone(), sources, sender);
|
||||
self.channel_to_self.send(msg).unwrap();
|
||||
}
|
||||
},
|
||||
|
@ -401,58 +384,68 @@ impl FontCache {
|
|||
}
|
||||
}
|
||||
|
||||
fn find_font_in_local_family(
|
||||
fn find_templates_in_local_family(
|
||||
&mut self,
|
||||
template_descriptor: &FontTemplateDescriptor,
|
||||
descriptor_to_match: &FontDescriptor,
|
||||
family_name: &FontFamilyName,
|
||||
) -> Option<FontTemplateRef> {
|
||||
let family_name = self.transform_family(family_name);
|
||||
|
||||
) -> Vec<FontTemplateRef> {
|
||||
// TODO(Issue #188): look up localized font family names if canonical name not found
|
||||
// look up canonical name
|
||||
if self.local_families.contains_key(&family_name) {
|
||||
debug!("FontList: Found font family with name={}", &*family_name);
|
||||
let font_templates = self.local_families.get_mut(&family_name).unwrap();
|
||||
|
||||
// TODO(Issue #192: handle generic font families, like 'serif' and 'sans-serif'.
|
||||
// if such family exists, try to match style to a font
|
||||
let family_name = self.transform_family(family_name);
|
||||
self.local_families
|
||||
.get_mut(&family_name)
|
||||
.map(|font_templates| {
|
||||
if font_templates.templates.is_empty() {
|
||||
for_each_variation(&family_name, |font_template| {
|
||||
font_templates.add_template(font_template);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO(Issue #192: handle generic font families, like 'serif' and 'sans-serif'.
|
||||
// if such family exists, try to match style to a font
|
||||
|
||||
font_templates.find_font_for_style(template_descriptor)
|
||||
} else {
|
||||
debug!(
|
||||
"FontList: Couldn't find font family with name={}",
|
||||
&*family_name
|
||||
);
|
||||
None
|
||||
}
|
||||
font_templates.find_for_descriptor(descriptor_to_match)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn find_font_in_web_family(
|
||||
fn find_templates_in_web_family(
|
||||
&mut self,
|
||||
template_descriptor: &FontTemplateDescriptor,
|
||||
descriptor_to_match: &FontDescriptor,
|
||||
family_name: &FontFamilyName,
|
||||
) -> Option<FontTemplateRef> {
|
||||
) -> Vec<FontTemplateRef> {
|
||||
let family_name = LowercaseString::from(family_name);
|
||||
self.web_families
|
||||
.get_mut(&family_name)
|
||||
.map(|templates| templates.find_for_descriptor(descriptor_to_match))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
if self.web_families.contains_key(&family_name) {
|
||||
let templates = self.web_families.get_mut(&family_name).unwrap();
|
||||
templates.find_font_for_style(template_descriptor)
|
||||
} else {
|
||||
None
|
||||
fn find_font_templates(
|
||||
&mut self,
|
||||
descriptor_to_match: &FontDescriptor,
|
||||
family_descriptor: &FontFamilyDescriptor,
|
||||
) -> Vec<FontTemplateRef> {
|
||||
if family_descriptor.scope == FontSearchScope::Any {
|
||||
let templates =
|
||||
self.find_templates_in_web_family(descriptor_to_match, &family_descriptor.name);
|
||||
if !templates.is_empty() {
|
||||
return templates;
|
||||
}
|
||||
}
|
||||
|
||||
fn get_font_key_for_template(&mut self, template: &FontTemplateRef) -> FontKey {
|
||||
self.find_templates_in_local_family(descriptor_to_match, &family_descriptor.name)
|
||||
}
|
||||
|
||||
fn get_font_instance(&mut self, identifier: FontIdentifier, pt_size: Au) -> FontInstanceKey {
|
||||
let webrender_api = &self.webrender_api;
|
||||
let webrender_fonts = &mut self.webrender_fonts;
|
||||
let identifier = template.borrow().identifier.clone();
|
||||
*webrender_fonts
|
||||
let font_data = self
|
||||
.font_data
|
||||
.get(&identifier)
|
||||
.expect("Got unexpected FontIdentifier")
|
||||
.clone();
|
||||
|
||||
let font_key = *webrender_fonts
|
||||
.entry(identifier.clone())
|
||||
.or_insert_with(|| {
|
||||
// CoreText cannot reliably create CoreTextFonts for system fonts stored
|
||||
|
@ -466,31 +459,13 @@ impl FontCache {
|
|||
.add_system_font(local_font_identifier.native_font_handle());
|
||||
}
|
||||
|
||||
let bytes = template.data();
|
||||
webrender_api.add_font(bytes, identifier.index())
|
||||
})
|
||||
}
|
||||
webrender_api.add_font(font_data, identifier.index())
|
||||
});
|
||||
|
||||
fn find_font_template(
|
||||
&mut self,
|
||||
template_descriptor: &FontTemplateDescriptor,
|
||||
family_descriptor: &FontFamilyDescriptor,
|
||||
) -> Option<FontTemplateAndWebRenderFontKey> {
|
||||
match family_descriptor.scope {
|
||||
FontSearchScope::Any => self
|
||||
.find_font_in_web_family(template_descriptor, &family_descriptor.name)
|
||||
.or_else(|| {
|
||||
self.find_font_in_local_family(template_descriptor, &family_descriptor.name)
|
||||
}),
|
||||
|
||||
FontSearchScope::Local => {
|
||||
self.find_font_in_local_family(template_descriptor, &family_descriptor.name)
|
||||
},
|
||||
}
|
||||
.map(|font_template| FontTemplateAndWebRenderFontKey {
|
||||
font_key: self.get_font_key_for_template(&font_template),
|
||||
font_template,
|
||||
})
|
||||
*self
|
||||
.font_instances
|
||||
.entry((font_key, pt_size))
|
||||
.or_insert_with(|| webrender_api.add_font_instance(font_key, pt_size.to_f32_px()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -501,6 +476,94 @@ pub struct FontCacheThread {
|
|||
chan: IpcSender<Command>,
|
||||
}
|
||||
|
||||
/// A version of `FontStyle` from Stylo that is serializable. Normally this is not
|
||||
/// because the specified version of `FontStyle` contains floats.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub enum ComputedFontStyleDescriptor {
|
||||
Normal,
|
||||
Italic,
|
||||
Oblique(FontStyleFixedPoint, FontStyleFixedPoint),
|
||||
}
|
||||
|
||||
/// This data structure represents the various optional descriptors that can be
|
||||
/// applied to a `@font-face` rule in CSS. These are used to create a [`FontTemplate`]
|
||||
/// from the given font data used as the source of the `@font-face` rule. If values
|
||||
/// like weight, stretch, and style are not specified they are initialized based
|
||||
/// on the contents of the font itself.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct CSSFontFaceDescriptors {
|
||||
pub family_name: LowercaseString,
|
||||
pub weight: Option<(FontWeight, FontWeight)>,
|
||||
pub stretch: Option<(FontStretch, FontStretch)>,
|
||||
pub style: Option<ComputedFontStyleDescriptor>,
|
||||
pub unicode_range: Option<Vec<RangeInclusive<u32>>>,
|
||||
}
|
||||
|
||||
impl CSSFontFaceDescriptors {
|
||||
pub fn new(family_name: &str) -> Self {
|
||||
CSSFontFaceDescriptors {
|
||||
family_name: LowercaseString::new(family_name),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&FontFaceRuleData> for CSSFontFaceDescriptors {
|
||||
fn from(rule_data: &FontFaceRuleData) -> Self {
|
||||
let family_name = rule_data
|
||||
.family
|
||||
.as_ref()
|
||||
.expect("Expected rule to contain a font family.")
|
||||
.name
|
||||
.clone();
|
||||
let weight = rule_data
|
||||
.weight
|
||||
.as_ref()
|
||||
.map(|weight_range| (weight_range.0.compute(), weight_range.1.compute()));
|
||||
|
||||
let stretch_to_computed = |specified: SpecifiedFontStretch| match specified {
|
||||
SpecifiedFontStretch::Stretch(percentage) => {
|
||||
FontStretch::from_percentage(percentage.compute().0)
|
||||
},
|
||||
SpecifiedFontStretch::Keyword(keyword) => keyword.compute(),
|
||||
SpecifiedFontStretch::System(_) => FontStretch::NORMAL,
|
||||
};
|
||||
let stretch = rule_data.stretch.as_ref().map(|stretch_range| {
|
||||
(
|
||||
stretch_to_computed(stretch_range.0),
|
||||
stretch_to_computed(stretch_range.1),
|
||||
)
|
||||
});
|
||||
|
||||
fn style_to_computed(specified: &FontFaceStyle) -> ComputedFontStyleDescriptor {
|
||||
match specified {
|
||||
FontFaceStyle::Normal => ComputedFontStyleDescriptor::Normal,
|
||||
FontFaceStyle::Italic => ComputedFontStyleDescriptor::Italic,
|
||||
FontFaceStyle::Oblique(angle_a, angle_b) => ComputedFontStyleDescriptor::Oblique(
|
||||
FixedPoint::from_float(angle_a.degrees()),
|
||||
FixedPoint::from_float(angle_b.degrees()),
|
||||
),
|
||||
}
|
||||
}
|
||||
let style = rule_data
|
||||
.style
|
||||
.as_ref()
|
||||
.map(|style| style_to_computed(style));
|
||||
let unicode_range = rule_data
|
||||
.unicode_range
|
||||
.as_ref()
|
||||
.map(|ranges| ranges.iter().map(|range| range.start..=range.end).collect());
|
||||
|
||||
CSSFontFaceDescriptors {
|
||||
family_name: LowercaseString::new(&family_name),
|
||||
weight,
|
||||
stretch,
|
||||
style,
|
||||
unicode_range,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FontCacheThread {
|
||||
pub fn new(
|
||||
core_resource_thread: CoreResourceThread,
|
||||
|
@ -520,6 +583,7 @@ impl FontCacheThread {
|
|||
port,
|
||||
channel_to_self,
|
||||
generic_fonts,
|
||||
font_data: HashMap::new(),
|
||||
local_families: HashMap::new(),
|
||||
web_families: HashMap::new(),
|
||||
core_resource_thread,
|
||||
|
@ -572,11 +636,7 @@ impl FontCacheThread {
|
|||
|
||||
let sender = sender.as_ref().unwrap_or(font_cache_sender).clone();
|
||||
self.chan
|
||||
.send(Command::AddWebFont(
|
||||
LowercaseString::new(&font_face.family().name),
|
||||
sources,
|
||||
sender,
|
||||
))
|
||||
.send(Command::AddWebFont(rule.into(), sources, sender))
|
||||
.unwrap();
|
||||
|
||||
// Either increment the count of loading web fonts, or wait for a synchronous load.
|
||||
|
@ -601,10 +661,10 @@ impl FontCacheThread {
|
|||
}
|
||||
|
||||
impl FontSource for FontCacheThread {
|
||||
fn get_font_instance(&mut self, key: FontKey, size: Au) -> FontInstanceKey {
|
||||
fn get_font_instance(&mut self, identifier: FontIdentifier, size: Au) -> FontInstanceKey {
|
||||
let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
|
||||
self.chan
|
||||
.send(Command::GetFontInstance(key, size, response_chan))
|
||||
.send(Command::GetFontInstance(identifier, size, response_chan))
|
||||
.expect("failed to send message to font cache thread");
|
||||
|
||||
let instance_key = response_port.recv();
|
||||
|
@ -619,22 +679,21 @@ impl FontSource for FontCacheThread {
|
|||
instance_key.unwrap()
|
||||
}
|
||||
|
||||
fn font_template(
|
||||
fn find_matching_font_templates(
|
||||
&mut self,
|
||||
template_descriptor: FontTemplateDescriptor,
|
||||
descriptor_to_match: &FontDescriptor,
|
||||
family_descriptor: FontFamilyDescriptor,
|
||||
) -> Option<FontTemplateAndWebRenderFontKey> {
|
||||
) -> Vec<FontTemplateRef> {
|
||||
let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
|
||||
self.chan
|
||||
.send(Command::GetFontTemplate(
|
||||
template_descriptor,
|
||||
.send(Command::GetFontTemplates(
|
||||
descriptor_to_match.clone(),
|
||||
family_descriptor,
|
||||
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!(
|
||||
|
@ -644,25 +703,22 @@ impl FontSource for FontCacheThread {
|
|||
panic!("Font cache thread has already exited.");
|
||||
}
|
||||
|
||||
match reply.unwrap() {
|
||||
Reply::GetFontTemplateReply(maybe_serialized_font_template_info) => {
|
||||
maybe_serialized_font_template_info.map(|serialized_font_template_info| {
|
||||
let font_template = Rc::new(RefCell::new(
|
||||
serialized_font_template_info
|
||||
.serialized_font_template
|
||||
.to_font_template(),
|
||||
));
|
||||
FontTemplateAndWebRenderFontKey {
|
||||
font_template,
|
||||
font_key: serialized_font_template_info.font_key,
|
||||
}
|
||||
reply
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|serialized_font_template| {
|
||||
let font_data = serialized_font_template.bytes_receiver.recv().ok();
|
||||
Rc::new(RefCell::new(FontTemplate {
|
||||
identifier: serialized_font_template.identifier,
|
||||
descriptor: serialized_font_template.descriptor.clone(),
|
||||
data: font_data.map(Arc::new),
|
||||
}))
|
||||
})
|
||||
},
|
||||
}
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub struct LowercaseString {
|
||||
inner: String,
|
||||
}
|
||||
|
|
|
@ -15,11 +15,11 @@ use log::debug;
|
|||
use servo_arc::Arc;
|
||||
use style::computed_values::font_variant_caps::T as FontVariantCaps;
|
||||
use style::properties::style_structs::Font as FontStyleStruct;
|
||||
use webrender_api::{FontInstanceKey, FontKey};
|
||||
use webrender_api::FontInstanceKey;
|
||||
|
||||
use crate::font::{Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef};
|
||||
use crate::font_cache_thread::FontTemplateAndWebRenderFontKey;
|
||||
use crate::font_template::FontTemplateDescriptor;
|
||||
use crate::font_cache_thread::FontIdentifier;
|
||||
use crate::font_template::{FontTemplateRef, FontTemplateRefMethods};
|
||||
#[cfg(target_os = "macos")]
|
||||
use crate::platform::core_text_font_cache::CoreTextFontCache;
|
||||
|
||||
|
@ -30,13 +30,12 @@ static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
|
|||
static FONT_CACHE_EPOCH: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
pub trait FontSource {
|
||||
fn get_font_instance(&mut self, key: FontKey, size: Au) -> FontInstanceKey;
|
||||
|
||||
fn font_template(
|
||||
fn get_font_instance(&mut self, font_identifier: FontIdentifier, size: Au) -> FontInstanceKey;
|
||||
fn find_matching_font_templates(
|
||||
&mut self,
|
||||
template_descriptor: FontTemplateDescriptor,
|
||||
descriptor_to_match: &FontDescriptor,
|
||||
family_descriptor: FontFamilyDescriptor,
|
||||
) -> Option<FontTemplateAndWebRenderFontKey>;
|
||||
) -> Vec<FontTemplateRef>;
|
||||
}
|
||||
|
||||
/// The FontContext represents the per-thread/thread state necessary for
|
||||
|
@ -51,7 +50,7 @@ pub struct FontContext<S: FontSource> {
|
|||
// so they will never be released. Find out a good time to drop them.
|
||||
// See bug https://github.com/servo/servo/issues/3300
|
||||
font_cache: HashMap<FontCacheKey, Option<FontRef>>,
|
||||
font_template_cache: HashMap<FontTemplateCacheKey, Option<FontTemplateAndWebRenderFontKey>>,
|
||||
font_template_cache: HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>,
|
||||
|
||||
font_group_cache:
|
||||
HashMap<FontGroupCacheKey, Rc<RefCell<FontGroup>>, BuildHasherDefault<FnvHasher>>,
|
||||
|
@ -115,20 +114,20 @@ impl<S: FontSource> FontContext<S> {
|
|||
/// reference to the same underlying `Font`.
|
||||
pub fn font(
|
||||
&mut self,
|
||||
font_template: FontTemplateRef,
|
||||
font_descriptor: &FontDescriptor,
|
||||
family_descriptor: &FontFamilyDescriptor,
|
||||
) -> Option<FontRef> {
|
||||
self.get_font_maybe_synthesizing_small_caps(
|
||||
font_template,
|
||||
font_descriptor,
|
||||
family_descriptor,
|
||||
true, /* synthesize_small_caps */
|
||||
)
|
||||
}
|
||||
|
||||
fn get_font_maybe_synthesizing_small_caps(
|
||||
&mut self,
|
||||
font_template: FontTemplateRef,
|
||||
font_descriptor: &FontDescriptor,
|
||||
family_descriptor: &FontFamilyDescriptor,
|
||||
synthesize_small_caps: bool,
|
||||
) -> Option<FontRef> {
|
||||
// TODO: (Bug #3463): Currently we only support fake small-caps
|
||||
|
@ -140,8 +139,8 @@ impl<S: FontSource> FontContext<S> {
|
|||
small_caps_descriptor.pt_size =
|
||||
font_descriptor.pt_size.scale_by(SMALL_CAPS_SCALE_FACTOR);
|
||||
self.get_font_maybe_synthesizing_small_caps(
|
||||
font_template.clone(),
|
||||
&small_caps_descriptor,
|
||||
family_descriptor,
|
||||
false, /* synthesize_small_caps */
|
||||
)
|
||||
} else {
|
||||
|
@ -149,52 +148,48 @@ impl<S: FontSource> FontContext<S> {
|
|||
};
|
||||
|
||||
let cache_key = FontCacheKey {
|
||||
font_identifier: font_template.identifier(),
|
||||
font_descriptor: font_descriptor.clone(),
|
||||
family_descriptor: family_descriptor.clone(),
|
||||
};
|
||||
|
||||
self.font_cache.get(&cache_key).cloned().unwrap_or_else(|| {
|
||||
debug!(
|
||||
"FontContext::font cache miss for font_descriptor={:?} family_descriptor={:?}",
|
||||
font_descriptor, family_descriptor
|
||||
"FontContext::font cache miss for font_template={:?} font_descriptor={:?}",
|
||||
font_template, font_descriptor
|
||||
);
|
||||
|
||||
let font = self
|
||||
.font_template(&font_descriptor.template_descriptor, family_descriptor)
|
||||
.and_then(|template_info| {
|
||||
self.create_font(
|
||||
template_info,
|
||||
.create_font(
|
||||
font_template,
|
||||
font_descriptor.to_owned(),
|
||||
synthesized_small_caps_font,
|
||||
)
|
||||
.ok()
|
||||
})
|
||||
.map(|font| Rc::new(RefCell::new(font)));
|
||||
|
||||
.ok();
|
||||
self.font_cache.insert(cache_key, font.clone());
|
||||
|
||||
font
|
||||
})
|
||||
}
|
||||
|
||||
fn font_template(
|
||||
pub fn matching_templates(
|
||||
&mut self,
|
||||
template_descriptor: &FontTemplateDescriptor,
|
||||
descriptor_to_match: &FontDescriptor,
|
||||
family_descriptor: &FontFamilyDescriptor,
|
||||
) -> Option<FontTemplateAndWebRenderFontKey> {
|
||||
) -> Vec<FontTemplateRef> {
|
||||
let cache_key = FontTemplateCacheKey {
|
||||
template_descriptor: *template_descriptor,
|
||||
font_descriptor: descriptor_to_match.clone(),
|
||||
family_descriptor: family_descriptor.clone(),
|
||||
};
|
||||
|
||||
self.font_template_cache.get(&cache_key).cloned().unwrap_or_else(|| {
|
||||
debug!(
|
||||
"FontContext::font_template cache miss for template_descriptor={:?} family_descriptor={:?}",
|
||||
template_descriptor,
|
||||
descriptor_to_match,
|
||||
family_descriptor
|
||||
);
|
||||
|
||||
let template_info = self.font_source.font_template(
|
||||
*template_descriptor,
|
||||
let template_info = self.font_source.find_matching_font_templates(
|
||||
descriptor_to_match,
|
||||
family_descriptor.clone(),
|
||||
);
|
||||
|
||||
|
@ -207,31 +202,32 @@ impl<S: FontSource> FontContext<S> {
|
|||
/// cache thread and a `FontDescriptor` which contains the styling parameters.
|
||||
fn create_font(
|
||||
&mut self,
|
||||
info: FontTemplateAndWebRenderFontKey,
|
||||
descriptor: FontDescriptor,
|
||||
font_template: FontTemplateRef,
|
||||
font_descriptor: FontDescriptor,
|
||||
synthesized_small_caps: Option<FontRef>,
|
||||
) -> Result<Font, &'static str> {
|
||||
) -> Result<FontRef, &'static str> {
|
||||
let font_instance_key = self
|
||||
.font_source
|
||||
.get_font_instance(info.font_key, descriptor.pt_size);
|
||||
Font::new(
|
||||
info.font_template,
|
||||
descriptor,
|
||||
.get_font_instance(font_template.identifier(), font_descriptor.pt_size);
|
||||
|
||||
Ok(Rc::new(RefCell::new(Font::new(
|
||||
font_template,
|
||||
font_descriptor,
|
||||
font_instance_key,
|
||||
synthesized_small_caps,
|
||||
)
|
||||
)?)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, Hash, PartialEq)]
|
||||
struct FontCacheKey {
|
||||
font_identifier: FontIdentifier,
|
||||
font_descriptor: FontDescriptor,
|
||||
family_descriptor: FontFamilyDescriptor,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, Hash, PartialEq)]
|
||||
struct FontTemplateCacheKey {
|
||||
template_descriptor: FontTemplateDescriptor,
|
||||
font_descriptor: FontDescriptor,
|
||||
family_descriptor: FontFamilyDescriptor,
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::{Debug, Error, Formatter};
|
||||
use std::ops::RangeInclusive;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -11,33 +12,33 @@ use serde::{Deserialize, Serialize};
|
|||
use servo_url::ServoUrl;
|
||||
use style::computed_values::font_stretch::T as FontStretch;
|
||||
use style::computed_values::font_style::T as FontStyle;
|
||||
use style::properties::style_structs::Font as FontStyleStruct;
|
||||
use style::values::computed::font::FontWeight;
|
||||
|
||||
use crate::font_cache_thread::FontIdentifier;
|
||||
use crate::font::{FontDescriptor, PlatformFontMethods};
|
||||
use crate::font_cache_thread::{
|
||||
CSSFontFaceDescriptors, ComputedFontStyleDescriptor, FontIdentifier,
|
||||
};
|
||||
use crate::platform::font::PlatformFont;
|
||||
use crate::platform::font_list::LocalFontIdentifier;
|
||||
|
||||
/// A reference to a [`FontTemplate`] with shared ownership and mutability.
|
||||
pub(crate) type FontTemplateRef = Rc<RefCell<FontTemplate>>;
|
||||
pub type FontTemplateRef = Rc<RefCell<FontTemplate>>;
|
||||
|
||||
/// Describes how to select a font from a given family. This is very basic at the moment and needs
|
||||
/// 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()`.
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Hash, PartialEq, Serialize)]
|
||||
#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
|
||||
pub struct FontTemplateDescriptor {
|
||||
pub weight: FontWeight,
|
||||
pub stretch: FontStretch,
|
||||
pub style: FontStyle,
|
||||
pub weight: (FontWeight, FontWeight),
|
||||
pub stretch: (FontStretch, FontStretch),
|
||||
pub style: (FontStyle, FontStyle),
|
||||
pub unicode_range: Option<Vec<RangeInclusive<u32>>>,
|
||||
}
|
||||
|
||||
impl Default for FontTemplateDescriptor {
|
||||
fn default() -> Self {
|
||||
FontTemplateDescriptor {
|
||||
weight: FontWeight::normal(),
|
||||
stretch: FontStretch::NORMAL,
|
||||
style: FontStyle::NORMAL,
|
||||
}
|
||||
Self::new(FontWeight::normal(), FontStretch::NORMAL, FontStyle::NORMAL)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,9 +58,10 @@ impl FontTemplateDescriptor {
|
|||
#[inline]
|
||||
pub fn new(weight: FontWeight, stretch: FontStretch, style: FontStyle) -> Self {
|
||||
Self {
|
||||
weight,
|
||||
stretch,
|
||||
style,
|
||||
weight: (weight, weight),
|
||||
stretch: (stretch, stretch),
|
||||
style: (style, style),
|
||||
unicode_range: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,24 +73,51 @@ impl FontTemplateDescriptor {
|
|||
///
|
||||
/// The policy is to care most about differences in italicness, then weight, then stretch
|
||||
#[inline]
|
||||
fn distance_from(&self, other: &FontTemplateDescriptor) -> f32 {
|
||||
fn distance_from(&self, other: &FontDescriptor) -> f32 {
|
||||
let weight = self.weight.0;
|
||||
let style = self.style.0;
|
||||
let stretch = self.stretch.0;
|
||||
|
||||
// 0 <= style_part <= 180, since font-style obliqueness should be
|
||||
// between -90 and +90deg.
|
||||
let style_part = (style_to_number(&self.style) - style_to_number(&other.style)).abs();
|
||||
let style_part = (style_to_number(&style) - style_to_number(&other.style)).abs();
|
||||
// 0 <= weightPart <= 800
|
||||
let weight_part = (self.weight.value() - other.weight.value()).abs();
|
||||
let weight_part = (weight.value() - other.weight.value()).abs();
|
||||
// 0 <= stretchPart <= 8
|
||||
let stretch_part = (self.stretch.to_percentage().0 - other.stretch.to_percentage().0).abs();
|
||||
let stretch_part = (stretch.to_percentage().0 - other.stretch.to_percentage().0).abs();
|
||||
style_part + weight_part + stretch_part
|
||||
}
|
||||
|
||||
fn matches(&self, descriptor_to_match: &FontDescriptor) -> bool {
|
||||
self.weight.0 <= descriptor_to_match.weight &&
|
||||
self.weight.1 >= descriptor_to_match.weight &&
|
||||
self.style.0 <= descriptor_to_match.style &&
|
||||
self.style.1 >= descriptor_to_match.style &&
|
||||
self.stretch.0 <= descriptor_to_match.stretch &&
|
||||
self.stretch.1 >= descriptor_to_match.stretch
|
||||
}
|
||||
|
||||
impl<'a> From<&'a FontStyleStruct> for FontTemplateDescriptor {
|
||||
fn from(style: &'a FontStyleStruct) -> Self {
|
||||
FontTemplateDescriptor {
|
||||
weight: style.font_weight,
|
||||
stretch: style.font_stretch,
|
||||
style: style.font_style,
|
||||
fn override_values_with_css_font_template_descriptors(
|
||||
&mut self,
|
||||
css_font_template_descriptors: CSSFontFaceDescriptors,
|
||||
) {
|
||||
if let Some(weight) = css_font_template_descriptors.weight {
|
||||
self.weight = weight;
|
||||
}
|
||||
self.style = match css_font_template_descriptors.style {
|
||||
Some(ComputedFontStyleDescriptor::Italic) => (FontStyle::ITALIC, FontStyle::ITALIC),
|
||||
Some(ComputedFontStyleDescriptor::Normal) => (FontStyle::NORMAL, FontStyle::NORMAL),
|
||||
Some(ComputedFontStyleDescriptor::Oblique(angle_1, angle_2)) => (
|
||||
FontStyle::oblique(angle_1.to_float()),
|
||||
FontStyle::oblique(angle_2.to_float()),
|
||||
),
|
||||
None => self.style,
|
||||
};
|
||||
if let Some(stretch) = css_font_template_descriptors.stretch {
|
||||
self.stretch = stretch;
|
||||
}
|
||||
if let Some(unicode_range) = css_font_template_descriptors.unicode_range {
|
||||
self.unicode_range = Some(unicode_range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,14 +158,22 @@ impl FontTemplate {
|
|||
|
||||
pub fn new_web_font(
|
||||
url: ServoUrl,
|
||||
descriptor: FontTemplateDescriptor,
|
||||
data: Arc<Vec<u8>>,
|
||||
) -> FontTemplate {
|
||||
FontTemplate {
|
||||
css_font_template_descriptors: CSSFontFaceDescriptors,
|
||||
) -> Result<FontTemplate, &'static str> {
|
||||
let identifier = FontIdentifier::Web(url.clone());
|
||||
let Ok(handle) = PlatformFont::new_from_data(identifier, data.clone(), 0, None) else {
|
||||
return Err("Could not initialize platform font data for: {url:?}");
|
||||
};
|
||||
|
||||
let mut descriptor = handle.descriptor();
|
||||
descriptor
|
||||
.override_values_with_css_font_template_descriptors(css_font_template_descriptors);
|
||||
Ok(FontTemplate {
|
||||
identifier: FontIdentifier::Web(url),
|
||||
descriptor,
|
||||
data: Some(data),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn identifier(&self) -> &FontIdentifier {
|
||||
|
@ -155,26 +192,35 @@ pub trait FontTemplateRefMethods {
|
|||
/// operation (depending on the platform) which performs synchronous disk I/O
|
||||
/// and should never be done lightly.
|
||||
fn data(&self) -> Arc<Vec<u8>>;
|
||||
/// Get the descriptor. Returns `None` when instantiating the data fails.
|
||||
/// Get the descriptor.
|
||||
fn descriptor(&self) -> FontTemplateDescriptor;
|
||||
/// Get the [`FontIdentifier`] for this template.
|
||||
fn identifier(&self) -> FontIdentifier;
|
||||
/// Returns true if the given descriptor matches the one in this [`FontTemplate`].
|
||||
fn descriptor_matches(&self, requested_desc: &FontTemplateDescriptor) -> bool;
|
||||
fn matches_font_descriptor(&self, descriptor_to_match: &FontDescriptor) -> bool;
|
||||
/// Calculate the distance from this [`FontTemplate`]s descriptor and return it
|
||||
/// or None if this is not a valid [`FontTemplate`].
|
||||
fn descriptor_distance(&self, requested_descriptor: &FontTemplateDescriptor) -> f32;
|
||||
fn descriptor_distance(&self, descriptor_to_match: &FontDescriptor) -> f32;
|
||||
/// Whether or not this character is in the unicode ranges specified in
|
||||
/// this temlates `@font-face` definition, if any.
|
||||
fn char_in_unicode_range(&self, character: char) -> bool;
|
||||
}
|
||||
|
||||
impl FontTemplateRefMethods for FontTemplateRef {
|
||||
fn descriptor(&self) -> FontTemplateDescriptor {
|
||||
self.borrow().descriptor
|
||||
self.borrow().descriptor.clone()
|
||||
}
|
||||
|
||||
fn descriptor_matches(&self, requested_descriptor: &FontTemplateDescriptor) -> bool {
|
||||
self.descriptor() == *requested_descriptor
|
||||
fn identifier(&self) -> FontIdentifier {
|
||||
self.borrow().identifier.clone()
|
||||
}
|
||||
|
||||
fn descriptor_distance(&self, requested_descriptor: &FontTemplateDescriptor) -> f32 {
|
||||
self.descriptor().distance_from(requested_descriptor)
|
||||
fn matches_font_descriptor(&self, descriptor_to_match: &FontDescriptor) -> bool {
|
||||
self.descriptor().matches(descriptor_to_match)
|
||||
}
|
||||
|
||||
fn descriptor_distance(&self, descriptor_to_match: &FontDescriptor) -> f32 {
|
||||
self.descriptor().distance_from(descriptor_to_match)
|
||||
}
|
||||
|
||||
fn data(&self) -> Arc<Vec<u8>> {
|
||||
|
@ -190,4 +236,15 @@ impl FontTemplateRefMethods for FontTemplateRef {
|
|||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn char_in_unicode_range(&self, character: char) -> bool {
|
||||
let character = character as u32;
|
||||
self.borrow()
|
||||
.descriptor
|
||||
.unicode_range
|
||||
.as_ref()
|
||||
.map_or(true, |ranges| {
|
||||
ranges.iter().any(|range| range.contains(&character))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -485,11 +485,7 @@ where
|
|||
},
|
||||
None => StyleFontStyle::NORMAL,
|
||||
};
|
||||
let descriptor = FontTemplateDescriptor {
|
||||
weight,
|
||||
stretch,
|
||||
style,
|
||||
};
|
||||
let descriptor = FontTemplateDescriptor::new(weight, stretch, style);
|
||||
callback(FontTemplate::new_local(local_font_identifier, descriptor));
|
||||
};
|
||||
|
||||
|
|
|
@ -173,11 +173,7 @@ impl PlatformFontMethods for PlatformFont {
|
|||
})
|
||||
.unwrap_or(FontStretch::NORMAL);
|
||||
|
||||
FontTemplateDescriptor {
|
||||
weight,
|
||||
stretch,
|
||||
style,
|
||||
}
|
||||
FontTemplateDescriptor::new(weight, stretch, style)
|
||||
}
|
||||
|
||||
fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
|
||||
|
|
|
@ -148,11 +148,7 @@ where
|
|||
path: Atom::from(c_str_to_string(path as *const c_char)),
|
||||
variation_index: index as i32,
|
||||
};
|
||||
let descriptor = FontTemplateDescriptor {
|
||||
weight,
|
||||
stretch,
|
||||
style,
|
||||
};
|
||||
let descriptor = FontTemplateDescriptor::new(weight, stretch, style);
|
||||
|
||||
callback(FontTemplate::new_local(local_font_identifier, descriptor))
|
||||
}
|
||||
|
|
|
@ -190,11 +190,7 @@ impl PlatformFontMethods for PlatformFont {
|
|||
|
||||
fn descriptor(&self) -> FontTemplateDescriptor {
|
||||
let traits = self.ctfont.all_traits();
|
||||
FontTemplateDescriptor {
|
||||
weight: traits.weight(),
|
||||
stretch: traits.stretch(),
|
||||
style: traits.style(),
|
||||
}
|
||||
FontTemplateDescriptor::new(traits.weight(), traits.stretch(), traits.style())
|
||||
}
|
||||
|
||||
fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
|
||||
|
|
|
@ -74,11 +74,8 @@ where
|
|||
};
|
||||
|
||||
let traits = family_descriptor.traits();
|
||||
let descriptor = FontTemplateDescriptor {
|
||||
weight: traits.weight(),
|
||||
stretch: traits.stretch(),
|
||||
style: traits.style(),
|
||||
};
|
||||
let descriptor =
|
||||
FontTemplateDescriptor::new(traits.weight(), traits.stretch(), traits.style());
|
||||
let identifier = LocalFontIdentifier {
|
||||
postscript_name: Atom::from(family_descriptor.font_name()),
|
||||
path: Atom::from(path),
|
||||
|
|
|
@ -186,11 +186,7 @@ impl PlatformFontMethods for PlatformFont {
|
|||
StyleFontStyle::NORMAL
|
||||
};
|
||||
|
||||
FontTemplateDescriptor {
|
||||
weight,
|
||||
stretch,
|
||||
style,
|
||||
}
|
||||
FontTemplateDescriptor::new(weight, stretch, style)
|
||||
}
|
||||
|
||||
fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
|
||||
|
|
|
@ -12,12 +12,10 @@ use std::rc::Rc;
|
|||
use app_units::Au;
|
||||
use gfx::font::{
|
||||
fallback_font_families, FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontSearchScope,
|
||||
PlatformFontMethods,
|
||||
};
|
||||
use gfx::font_cache_thread::{FontIdentifier, FontTemplateAndWebRenderFontKey, FontTemplates};
|
||||
use gfx::font_cache_thread::{CSSFontFaceDescriptors, FontIdentifier, FontTemplates};
|
||||
use gfx::font_context::{FontContext, FontSource};
|
||||
use gfx::font_template::{FontTemplate, FontTemplateDescriptor};
|
||||
use gfx::platform::font::PlatformFont;
|
||||
use gfx::font_template::{FontTemplate, FontTemplateRef};
|
||||
use servo_arc::Arc;
|
||||
use servo_atoms::Atom;
|
||||
use servo_url::ServoUrl;
|
||||
|
@ -29,7 +27,7 @@ use style::values::computed::font::{
|
|||
};
|
||||
use style::values::computed::{FontLanguageOverride, XLang};
|
||||
use style::values::generics::font::LineHeight;
|
||||
use webrender_api::{FontInstanceKey, FontKey, IdNamespace};
|
||||
use webrender_api::{FontInstanceKey, IdNamespace};
|
||||
|
||||
struct TestFontSource {
|
||||
families: HashMap<String, FontTemplates>,
|
||||
|
@ -78,40 +76,36 @@ impl TestFontSource {
|
|||
|
||||
let file = File::open(path).unwrap();
|
||||
let data: Vec<u8> = file.bytes().map(|b| b.unwrap()).collect();
|
||||
let data = std::sync::Arc::new(data);
|
||||
let handle = PlatformFont::new_from_data(
|
||||
Self::identifier_for_font_name(name),
|
||||
data.clone(),
|
||||
0,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
family.add_template(FontTemplate::new_web_font(
|
||||
family.add_template(
|
||||
FontTemplate::new_web_font(
|
||||
Self::url_for_font_name(name),
|
||||
handle.descriptor(),
|
||||
data,
|
||||
));
|
||||
std::sync::Arc::new(data),
|
||||
CSSFontFaceDescriptors::new(name),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl FontSource for TestFontSource {
|
||||
fn get_font_instance(&mut self, _key: FontKey, _size: Au) -> FontInstanceKey {
|
||||
fn get_font_instance(
|
||||
&mut self,
|
||||
_font_identifier: FontIdentifier,
|
||||
_size: Au,
|
||||
) -> FontInstanceKey {
|
||||
FontInstanceKey(IdNamespace(0), 0)
|
||||
}
|
||||
|
||||
fn font_template(
|
||||
fn find_matching_font_templates(
|
||||
&mut self,
|
||||
template_descriptor: FontTemplateDescriptor,
|
||||
descriptor_to_match: &FontDescriptor,
|
||||
family_descriptor: FontFamilyDescriptor,
|
||||
) -> Option<FontTemplateAndWebRenderFontKey> {
|
||||
) -> Vec<FontTemplateRef> {
|
||||
self.find_font_count.set(self.find_font_count.get() + 1);
|
||||
self.families
|
||||
.get_mut(family_descriptor.name())
|
||||
.and_then(|family| family.find_font_for_style(&template_descriptor))
|
||||
.map(|template| FontTemplateAndWebRenderFontKey {
|
||||
font_template: template,
|
||||
font_key: FontKey(IdNamespace(0), 0),
|
||||
})
|
||||
.map(|family| family.find_for_descriptor(descriptor_to_match))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,7 +185,7 @@ fn test_font_group_find_by_codepoint() {
|
|||
.find_by_codepoint(&mut context, 'a')
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
font.borrow().template.borrow().identifier,
|
||||
font.borrow().identifier(),
|
||||
TestFontSource::identifier_for_font_name("csstest-ascii")
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -205,7 +199,7 @@ fn test_font_group_find_by_codepoint() {
|
|||
.find_by_codepoint(&mut context, 'a')
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
font.borrow().template.borrow().identifier,
|
||||
font.borrow().identifier(),
|
||||
TestFontSource::identifier_for_font_name("csstest-ascii")
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -219,7 +213,7 @@ fn test_font_group_find_by_codepoint() {
|
|||
.find_by_codepoint(&mut context, 'á')
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
font.borrow().template.borrow().identifier,
|
||||
font.borrow().identifier(),
|
||||
TestFontSource::identifier_for_font_name("csstest-basic-regular")
|
||||
);
|
||||
assert_eq!(count.get(), 2, "both fonts should now have been loaded");
|
||||
|
@ -240,7 +234,7 @@ fn test_font_fallback() {
|
|||
.find_by_codepoint(&mut context, 'a')
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
font.borrow().template.borrow().identifier,
|
||||
font.borrow().identifier(),
|
||||
TestFontSource::identifier_for_font_name("csstest-ascii"),
|
||||
"a family in the group should be used if there is a matching glyph"
|
||||
);
|
||||
|
@ -250,7 +244,7 @@ fn test_font_fallback() {
|
|||
.find_by_codepoint(&mut context, 'á')
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
font.borrow().template.borrow().identifier,
|
||||
font.borrow().identifier(),
|
||||
TestFontSource::identifier_for_font_name("csstest-basic-regular"),
|
||||
"a fallback font should be used if there is no matching glyph in the group"
|
||||
);
|
||||
|
@ -263,11 +257,9 @@ fn test_font_template_is_cached() {
|
|||
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),
|
||||
};
|
||||
|
@ -275,10 +267,16 @@ fn test_font_template_is_cached() {
|
|||
let family_descriptor =
|
||||
FontFamilyDescriptor::new(FontFamilyName::from("CSSTest Basic"), FontSearchScope::Any);
|
||||
|
||||
let font1 = context.font(&font_descriptor, &family_descriptor).unwrap();
|
||||
let font_template = context.matching_templates(&font_descriptor, &family_descriptor)[0].clone();
|
||||
|
||||
let font1 = context
|
||||
.font(font_template.clone(), &font_descriptor)
|
||||
.unwrap();
|
||||
|
||||
font_descriptor.pt_size = Au(20);
|
||||
let font2 = context.font(&font_descriptor, &family_descriptor).unwrap();
|
||||
let font2 = context
|
||||
.font(font_template.clone(), &font_descriptor)
|
||||
.unwrap();
|
||||
|
||||
assert_ne!(
|
||||
font1.borrow().descriptor.pt_size,
|
||||
|
|
|
@ -39,37 +39,33 @@ fn test_font_template_descriptor() {
|
|||
|
||||
assert_eq!(
|
||||
descriptor("DejaVuSans"),
|
||||
FontTemplateDescriptor {
|
||||
weight: FontWeight::NORMAL,
|
||||
stretch: FontStretch::hundred(),
|
||||
style: FontStyle::NORMAL,
|
||||
}
|
||||
FontTemplateDescriptor::new(
|
||||
FontWeight::NORMAL,
|
||||
FontStretch::hundred(),
|
||||
FontStyle::NORMAL,
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
descriptor("DejaVuSans-Bold"),
|
||||
FontTemplateDescriptor {
|
||||
weight: FontWeight::BOLD,
|
||||
stretch: FontStretch::hundred(),
|
||||
style: FontStyle::NORMAL,
|
||||
}
|
||||
FontTemplateDescriptor::new(FontWeight::BOLD, FontStretch::hundred(), FontStyle::NORMAL,)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
descriptor("DejaVuSans-Oblique"),
|
||||
FontTemplateDescriptor {
|
||||
weight: FontWeight::NORMAL,
|
||||
stretch: FontStretch::hundred(),
|
||||
style: FontStyle::ITALIC,
|
||||
}
|
||||
FontTemplateDescriptor::new(
|
||||
FontWeight::NORMAL,
|
||||
FontStretch::hundred(),
|
||||
FontStyle::ITALIC,
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
descriptor("DejaVuSansCondensed-BoldOblique"),
|
||||
FontTemplateDescriptor {
|
||||
weight: FontWeight::BOLD,
|
||||
stretch: FontStretch::from_percentage(0.875),
|
||||
style: FontStyle::ITALIC,
|
||||
}
|
||||
FontTemplateDescriptor::new(
|
||||
FontWeight::BOLD,
|
||||
FontStretch::from_percentage(0.875),
|
||||
FontStyle::ITALIC,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
[content-height-004.html]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[line-height-201.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[first-available-font-003.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[first-available-font-004.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[first-available-font-005.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-face-unicode-range-nbsp.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-size-adjust-014.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-01.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-02.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-03.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-04.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-05.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-06.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-07.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-08.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-09.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-10.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-11.html]
|
||||
expected: FAIL
|
|
@ -2,41 +2,17 @@
|
|||
[Test @font-face matching for weight 600]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 750]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 751]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 900]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 1000]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 399]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 470]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 99]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 249]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 500]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 250]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 400]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 420]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 100]
|
||||
expected: FAIL
|
||||
|
|
2
tests/wpt/meta/css/CSS2/visudet/line-height-201.html.ini
Normal file
2
tests/wpt/meta/css/CSS2/visudet/line-height-201.html.ini
Normal file
|
@ -0,0 +1,2 @@
|
|||
[line-height-201.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[line-height-205.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[first-available-font-001.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[first-available-font-003.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[first-available-font-004.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[first-available-font-005.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[first-available-font-007.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-size-adjust-014.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-01.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-02.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-03.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-04.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-05.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-06.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-07.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-08.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-09.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-10.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[font-stretch-11.html]
|
||||
expected: FAIL
|
|
@ -8,27 +8,12 @@
|
|||
[Descriptor matching priority: Style has higher priority than weight]
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '400' should prefer '450 460' over '500']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '400' should prefer '350 399' over '351 398']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '500' should prefer '351 398' over '501 550']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '500' should prefer '501 550' over '502 560']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '501' should prefer '390 410' over '300 350']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '399' should prefer '340 360' over '200 300']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '399' should prefer '450 460' over '500 501']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-style: 'normal' should prefer 'oblique 0deg' over 'oblique 10deg 40deg']
|
||||
expected: FAIL
|
||||
|
||||
|
@ -47,12 +32,6 @@
|
|||
[Matching font-style: 'oblique 21deg' should prefer 'oblique 21deg' over 'oblique 30deg 60deg']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '400' should prefer '400' over '450 460']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '399' should prefer '500 501' over '502 510']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-style: 'oblique 21deg' should prefer 'oblique 30deg 60deg' over 'oblique 40deg 50deg']
|
||||
expected: FAIL
|
||||
|
||||
|
@ -74,9 +53,6 @@
|
|||
[Matching font-style: 'oblique 21deg' should prefer 'oblique 0deg' over 'oblique -50deg -20deg']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '399' should prefer '350 399' over '340 360']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-stretch: '110%' should prefer '100%' over '50% 80%']
|
||||
expected: FAIL
|
||||
|
||||
|
@ -95,9 +71,6 @@
|
|||
[Matching font-style: 'oblique 10deg' should prefer 'oblique 40deg 50deg' over 'italic']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '400' should prefer '501 550' over '502 560']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-style: 'normal' should prefer 'normal' over 'oblique 0deg']
|
||||
expected: FAIL
|
||||
|
||||
|
@ -125,9 +98,6 @@
|
|||
[Matching font-style: 'oblique -20deg' should prefer 'oblique -60deg -40deg' over 'oblique -10deg']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '501' should prefer '450 460' over '390 410']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-stretch: '110%' should prefer '110% 120%' over '115% 116%']
|
||||
expected: FAIL
|
||||
|
||||
|
@ -185,18 +155,9 @@
|
|||
[Matching font-style: 'oblique -10deg' should prefer 'oblique 0deg 10deg' over 'oblique 40deg 50deg']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '430' should prefer '400 425' over '350 399']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '501' should prefer '501' over '502 510']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '501' should prefer '503 520' over '500']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '501' should prefer '500' over '450 460']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-stretch: '110%' should prefer '115% 116%' over '105%']
|
||||
expected: FAIL
|
||||
|
||||
|
@ -221,9 +182,6 @@
|
|||
[Matching font-style: 'oblique -20deg' should prefer 'oblique -10deg' over 'italic']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '430' should prefer '350 399' over '340 398']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-stretch: '90%' should prefer '60% 70%' over '110% 140%']
|
||||
expected: FAIL
|
||||
|
||||
|
@ -248,9 +206,6 @@
|
|||
[Matching font-style: 'oblique -20deg' should prefer 'oblique -20deg' over 'oblique -60deg -40deg']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '430' should prefer '420 440' over '450 460']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-stretch: '100%' should prefer '110% 120%' over '115% 116%']
|
||||
expected: FAIL
|
||||
|
||||
|
@ -263,9 +218,6 @@
|
|||
[Matching font-weight: '430' should prefer '340 398' over '501 550']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '501' should prefer '502 510' over '503 520']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-style: 'oblique 20deg' should prefer 'oblique 30deg 60deg' over 'oblique 40deg 50deg']
|
||||
expected: FAIL
|
||||
|
||||
|
@ -275,21 +227,6 @@
|
|||
[Matching font-style: 'oblique -20deg' should prefer 'oblique 0deg' over 'oblique 30deg 60deg']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '400' should prefer '351 398' over '501 550']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '430' should prefer '501 550' over '502 560']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '500' should prefer '500' over '450 460']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '500' should prefer '450 460' over '400']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '500' should prefer '400' over '350 399']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-stretch: '100%' should prefer '100%' over '110% 120%']
|
||||
expected: FAIL
|
||||
|
||||
|
@ -314,12 +251,6 @@
|
|||
[Matching font-style: 'oblique -10deg' should prefer 'oblique -50deg -40deg' over 'italic']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '430' should prefer '450 460' over '500']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-weight: '399' should prefer '400' over '450 460']
|
||||
expected: FAIL
|
||||
|
||||
[Matching font-style: 'oblique 10deg' should prefer 'oblique 10deg' over 'oblique 5deg']
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -14,15 +14,9 @@
|
|||
[Test @font-face matching for weight 900]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 100]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 400]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 249]
|
||||
expected: FAIL
|
||||
|
||||
[Test @font-face matching for weight 750]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[ex-unit-001.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[ex-unit-004.html]
|
||||
expected: FAIL
|
Loading…
Add table
Add a link
Reference in a new issue