mirror of
https://github.com/servo/servo.git
synced 2025-08-17 03:15:34 +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
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
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 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
|
||||
}
|
||||
|
||||
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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue