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:
Martin Robinson 2024-04-29 19:02:07 +02:00 committed by GitHub
parent 628e33bfa9
commit 4732da3477
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
56 changed files with 613 additions and 593 deletions

View file

@ -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(
Self::url_for_font_name(name),
handle.descriptor(),
data,
));
family.add_template(
FontTemplate::new_web_font(
Self::url_for_font_name(name),
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(),
},
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,

View file

@ -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,
)
);
}