fonts: Implement CSS font-variation-settings property for FreeType platforms (#38642)

This change adds support for variable fonts via the
[`font-variation-settings`](https://developer.mozilla.org/en-US/docs/Web/CSS/font-variation-settings)
property.

There are three areas where we need to set the variation values:
* Webrender (`compositor.rs`), for drawing the glyphs
* Harfbuzz (`shaper.rs`), for most shaping tasks
* PlatformFont (`fonts/platform/`), for horizontal advances and kerning

For now, freetype is the only platform shaper that supports variable
fonts. I can't easily test the fonts with non-freetype shapers. Thats
why variable fonts are behind the `layout_variable_fonts_enabled` pref,
which is disabled by default.

<img width="1250" height="710" alt="image"
src="https://github.com/user-attachments/assets/1aee1407-f3a2-42f6-a106-af0443fcd588"
/>

<details><summary>HTML test file</summary>

```html
<style>
@font-face {
  font-family: "Amstelvar VF";
  src: url("https://mdn.github.io/shared-assets/fonts/variable-fonts/AmstelvarAlpha-VF.woff2")
    format("woff2-variations");
  font-weight: 300 900;
  font-stretch: 35% 100%;
  font-style: normal;
  font-display: swap;
}

p {
  font:
    1.2em "Amstelvar VF",
    Georgia,
    serif;
  font-size: 4rem;
  margin: 1rem;
  display: inline-block;
}

.p1 {
  font-variation-settings: "wght" 300;
}

.p2 {
  font-variation-settings: "wght" 625;
}

.p3 {
  font-variation-settings: "wght" 900;
}

</style>
<div>
  <p class="p1">Weight</p>
  <span>(font-variation-settings: "wght" 300)</span>
</div>
<div>
  <p class="p2">Weight</p>
  <span>(font-variation-settings: "wght" 625)</span>
</div>
<div>
  <p class="p3">Weight</p>
  <span>(font-variation-settings: "wght" 900)</span>
</div>
</div>
```
</details>



https://github.com/user-attachments/assets/9e21101a-796a-49fe-b82c-8999d8fa9ee1


Testing: Needs decision on whether we want to enable the pref in CI
Works towards https://github.com/servo/servo/issues/37236

Depends on https://github.com/servo/stylo/pull/230

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
Simon Wülker 2025-08-18 18:30:14 +02:00 committed by GitHub
parent ce16fbce75
commit 7471ad7730
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 274 additions and 79 deletions

View file

@ -4,6 +4,7 @@
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::hash::Hash;
use std::ops::Deref;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, OnceLock};
@ -26,7 +27,7 @@ use style::values::computed::font::{
};
use style::values::computed::{FontStretch, FontStyle, FontWeight};
use unicode_script::Script;
use webrender_api::{FontInstanceFlags, FontInstanceKey};
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontVariation};
use crate::platform::font::{FontTable, PlatformFont};
pub use crate::platform::font_list::fallback_font_families;
@ -43,13 +44,14 @@ macro_rules! ot_tag {
};
}
pub const GPOS: u32 = ot_tag!('G', 'P', 'O', 'S');
pub const GSUB: u32 = ot_tag!('G', 'S', 'U', 'B');
pub const KERN: u32 = ot_tag!('k', 'e', 'r', 'n');
pub const SBIX: u32 = ot_tag!('s', 'b', 'i', 'x');
pub const CBDT: u32 = ot_tag!('C', 'B', 'D', 'T');
pub const COLR: u32 = ot_tag!('C', 'O', 'L', 'R');
pub const BASE: u32 = ot_tag!('B', 'A', 'S', 'E');
pub type OpenTypeTableTag = u32;
pub const GPOS: OpenTypeTableTag = ot_tag!('G', 'P', 'O', 'S');
pub const GSUB: OpenTypeTableTag = ot_tag!('G', 'S', 'U', 'B');
pub const KERN: OpenTypeTableTag = ot_tag!('k', 'e', 'r', 'n');
pub const SBIX: OpenTypeTableTag = ot_tag!('s', 'b', 'i', 'x');
pub const CBDT: OpenTypeTableTag = ot_tag!('C', 'B', 'D', 'T');
pub const COLR: OpenTypeTableTag = ot_tag!('C', 'O', 'L', 'R');
pub const BASE: OpenTypeTableTag = ot_tag!('B', 'A', 'S', 'E');
pub const LAST_RESORT_GLYPH_ADVANCE: FractionalPixel = 10.0;
@ -66,6 +68,7 @@ pub trait PlatformFontMethods: Sized {
fn new_from_template(
template: FontTemplateRef,
pt_size: Option<Au>,
variations: &[FontVariation],
data: &Option<FontData>,
) -> Result<PlatformFont, &'static str> {
let template = template.borrow();
@ -73,13 +76,14 @@ pub trait PlatformFontMethods: Sized {
match font_identifier {
FontIdentifier::Local(font_identifier) => {
Self::new_from_local_font_identifier(font_identifier, pt_size)
Self::new_from_local_font_identifier(font_identifier, pt_size, variations)
},
FontIdentifier::Web(_) => Self::new_from_data(
font_identifier,
data.as_ref()
.expect("Should never create a web font without data."),
pt_size,
variations,
),
}
}
@ -87,12 +91,14 @@ pub trait PlatformFontMethods: Sized {
fn new_from_local_font_identifier(
font_identifier: LocalFontIdentifier,
pt_size: Option<Au>,
variations: &[FontVariation],
) -> Result<PlatformFont, &'static str>;
fn new_from_data(
font_identifier: FontIdentifier,
data: &FontData,
pt_size: Option<Au>,
variations: &[FontVariation],
) -> Result<PlatformFont, &'static str>;
/// Get a [`FontTemplateDescriptor`] from a [`PlatformFont`]. This is used to get
@ -109,6 +115,9 @@ pub trait PlatformFontMethods: Sized {
/// Get the necessary [`FontInstanceFlags`]` for this font.
fn webrender_font_instance_flags(&self) -> FontInstanceFlags;
/// Return all the variation values that the font was instantiated with.
fn variations(&self) -> &[FontVariation];
}
// Used to abstract over the shaper's choice of fixed int representation.
@ -192,18 +201,29 @@ pub struct FontDescriptor {
pub style: FontStyle,
pub variant: font_variant_caps::T,
pub pt_size: Au,
pub variation_settings: Vec<FontVariation>,
}
impl Eq for FontDescriptor {}
impl<'a> From<&'a FontStyleStruct> for FontDescriptor {
fn from(style: &'a FontStyleStruct) -> Self {
let variation_settings = style
.clone_font_variation_settings()
.0
.into_iter()
.map(|setting| FontVariation {
tag: setting.tag.0,
value: setting.value,
})
.collect();
FontDescriptor {
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()),
variation_settings,
}
}
}
@ -280,8 +300,12 @@ impl Font {
data: Option<FontData>,
synthesized_small_caps: Option<FontRef>,
) -> Result<Font, &'static str> {
let handle =
PlatformFont::new_from_template(template.clone(), Some(descriptor.pt_size), &data)?;
let handle = PlatformFont::new_from_template(
template.clone(),
Some(descriptor.pt_size),
&descriptor.variation_settings,
&data,
)?;
let metrics = handle.metrics();
Ok(Font {
@ -336,6 +360,10 @@ impl Font {
)
})
}
pub fn variations(&self) -> &[FontVariation] {
self.handle.variations()
}
}
bitflags! {
@ -426,9 +454,8 @@ impl Font {
}
fn shape_text_harfbuzz(&self, text: &str, options: &ShapingOptions, glyphs: &mut GlyphStore) {
let this = self as *const Font;
self.shaper
.get_or_init(|| Shaper::new(this))
.get_or_init(|| Shaper::new(self))
.shape_text(text, options, glyphs);
}
@ -544,8 +571,7 @@ impl Font {
/// Get the [`FontBaseline`] for this font.
pub fn baseline(&self) -> Option<FontBaseline> {
let this = self as *const Font;
self.shaper.get_or_init(|| Shaper::new(this)).baseline()
self.shaper.get_or_init(|| Shaper::new(self)).baseline()
}
}

View file

@ -32,7 +32,7 @@ use style::shared_lock::SharedRwLockReadGuard;
use style::stylesheets::{CssRule, DocumentStyleSheet, FontFaceRule, StylesheetInDocument};
use style::values::computed::font::{FamilyName, FontFamilyNameSyntax, SingleFontFamily};
use url::Url;
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey};
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation};
use crate::font::{
Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope,
@ -45,6 +45,8 @@ use crate::{FontData, LowercaseFontFamilyName, PlatformFontMethods, SystemFontSe
static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
pub type FontParameters = (FontKey, Au, Vec<FontVariation>);
#[derive(MallocSizeOf)]
struct FontGroupRef(#[conditional_malloc_size_of] Arc<RwLock<FontGroup>>);
@ -87,7 +89,7 @@ pub struct FontContext {
/// A collection of WebRender [`FontInstanceKey`]s generated for the web fonts that
/// this [`FontContext`] controls.
webrender_font_instance_keys: RwLock<HashMap<(FontKey, Au), FontInstanceKey>>,
webrender_font_instance_keys: RwLock<HashMap<FontParameters, FontInstanceKey>>,
/// The data for each web font [`FontIdentifier`]. This data might be used by more than one
/// [`FontTemplate`] as each identifier refers to a URL.
@ -270,7 +272,7 @@ impl FontContext {
) -> Result<FontRef, &'static str> {
Ok(FontRef(Arc::new(Font::new(
font_template.clone(),
font_descriptor.clone(),
font_descriptor,
self.get_font_data(&font_template.identifier()),
synthesized_small_caps,
)?)))
@ -282,11 +284,13 @@ impl FontContext {
font.template.identifier(),
font.descriptor.pt_size,
font.webrender_font_instance_flags(),
font.variations().to_owned(),
),
FontIdentifier::Web(_) => self.create_web_font_instance(
font.template.clone(),
font.descriptor.pt_size,
font.webrender_font_instance_flags(),
font.variations().to_owned(),
),
}
}
@ -296,6 +300,7 @@ impl FontContext {
font_template: FontTemplateRef,
pt_size: Au,
flags: FontInstanceFlags,
variations: Vec<FontVariation>,
) -> FontInstanceKey {
let identifier = font_template.identifier().clone();
let font_data = self
@ -318,7 +323,7 @@ impl FontContext {
let key = *self
.webrender_font_instance_keys
.write()
.entry((font_key, pt_size))
.entry((font_key, pt_size, variations.clone()))
.or_insert_with(|| {
let font_instance_key = self.system_font_service_proxy.generate_font_instance_key();
self.compositor_api.lock().add_font_instance(
@ -326,6 +331,7 @@ impl FontContext {
font_key,
pt_size.to_f32_px(),
flags,
variations,
);
font_instance_key
});
@ -612,7 +618,7 @@ impl FontContextWebFontMethods for Arc<FontContext> {
});
let mut removed_instance_keys: HashSet<FontInstanceKey> = HashSet::new();
webrender_font_instance_keys.retain(|(font_key, _), instance_key| {
webrender_font_instance_keys.retain(|(font_key, _, _), instance_key| {
if removed_keys.contains(font_key) {
removed_instance_keys.insert(*instance_key);
false
@ -857,11 +863,11 @@ impl RemoteWebFontDownloader {
let url: ServoUrl = self.url.clone().into();
let identifier = FontIdentifier::Web(url.clone());
let Ok(handle) = PlatformFont::new_from_data(identifier, &font_data, None) else {
let Ok(handle) = PlatformFont::new_from_data(identifier, &font_data, None, &[]) else {
return false;
};
let state = self.take_state();
let mut descriptor = handle.descriptor();
descriptor
.override_values_with_css_font_template_descriptors(&state.css_font_face_descriptors);

View file

@ -315,7 +315,7 @@ impl<'a> DetailedGlyphStore {
// This struct is used by GlyphStore clients to provide new glyph data.
// It should be allocated on the stack and passed by reference to GlyphStore.
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Debug)]
pub struct GlyphData {
id: GlyphId,
advance: Au,

View file

@ -23,7 +23,7 @@ use style::Zero;
use style::computed_values::font_stretch::T as FontStretch;
use style::computed_values::font_weight::T as FontWeight;
use style::values::computed::font::FontStyle;
use webrender_api::FontInstanceFlags;
use webrender_api::{FontInstanceFlags, FontVariation};
use super::LocalFontIdentifier;
use super::library_handle::FreeTypeLibraryHandle;
@ -63,6 +63,7 @@ pub struct PlatformFont {
face: ReentrantMutex<FreeTypeFace>,
requested_face_size: Au,
actual_face_size: Au,
variations: Vec<FontVariation>,
/// A member that allows using `skrifa` to read values from this font.
table_provider_data: FreeTypeFaceTableProviderData,
@ -73,11 +74,14 @@ impl PlatformFontMethods for PlatformFont {
_font_identifier: FontIdentifier,
font_data: &FontData,
requested_size: Option<Au>,
variations: &[FontVariation],
) -> Result<PlatformFont, &'static str> {
let library = FreeTypeLibraryHandle::get().lock();
let data: &[u8] = font_data.as_ref();
let face = FreeTypeFace::new_from_memory(&library, data)?;
let normalized_variations = face.set_variations_for_font(variations, &library)?;
let (requested_face_size, actual_face_size) = match requested_size {
Some(requested_size) => (requested_size, face.set_size(requested_size)?),
None => (Au::zero(), Au::zero()),
@ -88,18 +92,22 @@ impl PlatformFontMethods for PlatformFont {
requested_face_size,
actual_face_size,
table_provider_data: FreeTypeFaceTableProviderData::Web(font_data.clone()),
variations: normalized_variations,
})
}
fn new_from_local_font_identifier(
font_identifier: LocalFontIdentifier,
requested_size: Option<Au>,
variations: &[FontVariation],
) -> Result<PlatformFont, &'static str> {
let library = FreeTypeLibraryHandle::get().lock();
let filename = CString::new(&*font_identifier.path).expect("filename contains NUL byte!");
let face = FreeTypeFace::new_from_file(&library, &filename, font_identifier.index())?;
let normalized_variations = face.set_variations_for_font(variations, &library)?;
let (requested_face_size, actual_face_size) = match requested_size {
Some(requested_size) => (requested_size, face.set_size(requested_size)?),
None => (Au::zero(), Au::zero()),
@ -119,6 +127,7 @@ impl PlatformFontMethods for PlatformFont {
Arc::new(memory_mapped_font_data),
font_identifier.index(),
),
variations: normalized_variations,
})
}
@ -377,6 +386,10 @@ impl PlatformFontMethods for PlatformFont {
// loading bitmaps. There's no harm to always passing it.
FontInstanceFlags::EMBEDDED_BITMAPS
}
fn variations(&self) -> &[FontVariation] {
&self.variations
}
}
impl PlatformFont {

View file

@ -7,10 +7,13 @@ use std::ptr;
use app_units::Au;
use freetype_sys::{
FT_Done_Face, FT_F26Dot6, FT_FACE_FLAG_COLOR, FT_FACE_FLAG_FIXED_SIZES, FT_FACE_FLAG_SCALABLE,
FT_Face, FT_FaceRec, FT_Int32, FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_Long, FT_New_Face,
FT_New_Memory_Face, FT_Pos, FT_Select_Size, FT_Set_Char_Size, FT_UInt,
FT_Done_Face, FT_Done_MM_Var, FT_F26Dot6, FT_FACE_FLAG_COLOR, FT_FACE_FLAG_FIXED_SIZES,
FT_FACE_FLAG_SCALABLE, FT_Face, FT_FaceRec, FT_Fixed, FT_Get_MM_Var, FT_HAS_MULTIPLE_MASTERS,
FT_Int32, FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_Long, FT_MM_Var, FT_New_Face, FT_New_Memory_Face,
FT_Pos, FT_Select_Size, FT_Set_Char_Size, FT_Set_Var_Design_Coordinates, FT_UInt,
FTErrorMethods,
};
use webrender_api::FontVariation;
use crate::platform::freetype::library_handle::FreeTypeLibraryHandle;
@ -164,6 +167,74 @@ impl FreeTypeFace {
load_flags as FT_Int32
}
/// Applies to provided variations to the font face.
///
/// Returns the normalized font variations, which are clamped
/// to fit within the range of their respective axis. Variation
/// values for nonexistent axes are not included.
pub(crate) fn set_variations_for_font(
&self,
variations: &[FontVariation],
library: &FreeTypeLibraryHandle,
) -> Result<Vec<FontVariation>, &'static str> {
if !FT_HAS_MULTIPLE_MASTERS(self.as_ptr()) ||
variations.is_empty() ||
!servo_config::pref!(layout_variable_fonts_enabled)
{
// Nothing to do
return Ok(vec![]);
}
// Query variation axis of font
let mut mm_var: *mut FT_MM_Var = ptr::null_mut();
let result = unsafe { FT_Get_MM_Var(self.as_ptr(), &mut mm_var as *mut _) };
if !result.succeeded() {
return Err("Failed to query font variations");
}
// Prepare values for each axis. These are either the provided values (if any) or the default
// ones for the axis.
let num_axis = unsafe { (*mm_var).num_axis } as usize;
let mut normalized_axis_values = Vec::with_capacity(variations.len());
let mut coords = vec![0; num_axis];
for (index, coord) in coords.iter_mut().enumerate() {
let axis_data = unsafe { &*(*mm_var).axis.add(index) };
let Some(variation) = variations
.iter()
.find(|variation| variation.tag == axis_data.tag as u32)
else {
*coord = axis_data.def;
continue;
};
// Freetype uses a 16.16 fixed point format for variation values
let shift_factor = 16.0_f32.exp2();
let min_value = axis_data.minimum as f32 / shift_factor;
let max_value = axis_data.maximum as f32 / shift_factor;
normalized_axis_values.push(FontVariation {
tag: variation.tag,
value: variation.value.min(max_value).max(min_value),
});
*coord = (variation.value * shift_factor) as FT_Fixed;
}
// Free the MM_Var structure
unsafe {
FT_Done_MM_Var(library.freetype_library, mm_var);
}
// Set the values for each variation axis
let result = unsafe {
FT_Set_Var_Design_Coordinates(self.as_ptr(), coords.len() as u32, coords.as_ptr())
};
if !result.succeeded() {
return Err("Could not set variations for font face");
}
Ok(normalized_axis_values)
}
}
/// FT_Face can be used in multiple threads, but from only one thread at a time.

View file

@ -19,7 +19,7 @@ use core_text::font_descriptor::{
use euclid::default::{Point2D, Rect, Size2D};
use log::debug;
use style::values::computed::font::{FontStretch, FontStyle, FontWeight};
use webrender_api::FontInstanceFlags;
use webrender_api::{FontInstanceFlags, FontVariation};
use super::core_text_font_cache::CoreTextFontCache;
use super::font_list::LocalFontIdentifier;
@ -201,6 +201,7 @@ impl PlatformFontMethods for PlatformFont {
font_identifier: FontIdentifier,
data: &FontData,
requested_size: Option<Au>,
_variations: &[FontVariation],
) -> Result<PlatformFont, &'static str> {
Self::new(font_identifier, Some(data), requested_size)
}
@ -208,6 +209,7 @@ impl PlatformFontMethods for PlatformFont {
fn new_from_local_font_identifier(
font_identifier: LocalFontIdentifier,
requested_size: Option<Au>,
_variations: &[FontVariation],
) -> Result<PlatformFont, &'static str> {
Self::new(FontIdentifier::Local(font_identifier), None, requested_size)
}
@ -353,6 +355,11 @@ impl PlatformFontMethods for PlatformFont {
Size2D::new(rect.size.width as f32, rect.size.height as f32),
)
}
fn variations(&self) -> &[FontVariation] {
// FIXME: Implement this for macos
&[]
}
}
pub(super) trait CoreTextFontTraitsMapping {

View file

@ -21,7 +21,7 @@ use style::computed_values::font_weight::T as StyleFontWeight;
use style::values::computed::font::FontStyle as StyleFontStyle;
use truetype::tables::WindowsMetrics;
use truetype::value::Read;
use webrender_api::FontInstanceFlags;
use webrender_api::{FontInstanceFlags, FontVariation};
use super::font_list::LocalFontIdentifier;
use crate::{
@ -116,6 +116,7 @@ impl PlatformFontMethods for PlatformFont {
_font_identifier: FontIdentifier,
data: &FontData,
pt_size: Option<Au>,
_variations: &[FontVariation],
) -> Result<Self, &'static str> {
let font_face = FontFile::new_from_buffer(Arc::new(data.clone()))
.ok_or("Could not create FontFile")?
@ -130,6 +131,7 @@ impl PlatformFontMethods for PlatformFont {
fn new_from_local_font_identifier(
font_identifier: LocalFontIdentifier,
pt_size: Option<Au>,
_variations: &[FontVariation],
) -> Result<PlatformFont, &'static str> {
let font_face = FontCollection::system()
.font_from_descriptor(&font_identifier.font_descriptor)
@ -320,4 +322,9 @@ impl PlatformFontMethods for PlatformFont {
Size2D::new(width, height),
)
}
fn variations(&self) -> &[FontVariation] {
// FIXME: implement this for windows
&[]
}
}

View file

@ -22,8 +22,9 @@ use harfbuzz_sys::{
hb_face_create_for_tables, hb_face_destroy, hb_face_t, hb_feature_t, hb_font_create,
hb_font_destroy, hb_font_funcs_create, hb_font_funcs_set_glyph_h_advance_func,
hb_font_funcs_set_nominal_glyph_func, hb_font_funcs_t, hb_font_set_funcs, hb_font_set_ppem,
hb_font_set_scale, hb_font_t, hb_glyph_info_t, hb_glyph_position_t, hb_ot_layout_get_baseline,
hb_position_t, hb_script_from_iso15924_tag, hb_shape, hb_tag_t,
hb_font_set_scale, hb_font_set_variations, hb_font_t, hb_glyph_info_t, hb_glyph_position_t,
hb_ot_layout_get_baseline, hb_position_t, hb_script_from_iso15924_tag, hb_shape, hb_tag_t,
hb_variation_t,
};
use log::debug;
use num_traits::Zero;
@ -32,13 +33,14 @@ use crate::font::advance_for_shaped_glyph;
use crate::platform::font::FontTable;
use crate::{
BASE, ByteIndex, Font, FontBaseline, FontTableMethods, FontTableTag, GlyphData, GlyphId,
GlyphStore, KERN, ShapingFlags, ShapingOptions, fixed_to_float, float_to_fixed, ot_tag,
GlyphStore, KERN, OpenTypeTableTag, ShapingFlags, ShapingOptions, fixed_to_float,
float_to_fixed, ot_tag,
};
const NO_GLYPH: i32 = -1;
const LIGA: u32 = ot_tag!('l', 'i', 'g', 'a');
const HB_OT_TAG_DEFAULT_SCRIPT: u32 = ot_tag!('D', 'F', 'L', 'T');
const HB_OT_TAG_DEFAULT_LANGUAGE: u32 = ot_tag!('d', 'f', 'l', 't');
const LIGA: OpenTypeTableTag = ot_tag!('l', 'i', 'g', 'a');
const HB_OT_TAG_DEFAULT_SCRIPT: OpenTypeTableTag = ot_tag!('D', 'F', 'L', 'T');
const HB_OT_TAG_DEFAULT_LANGUAGE: OpenTypeTableTag = ot_tag!('d', 'f', 'l', 't');
pub struct ShapedGlyphData {
count: usize,
@ -155,18 +157,17 @@ impl Drop for Shaper {
}
impl Shaper {
#[allow(clippy::not_unsafe_ptr_arg_deref)] // Has an unsafe block inside
pub fn new(font: *const Font) -> Shaper {
pub fn new(font: &Font) -> Shaper {
unsafe {
let hb_face: *mut hb_face_t = hb_face_create_for_tables(
Some(font_table_func),
font as *const c_void as *mut c_void,
font as *const Font as *mut c_void,
None,
);
let hb_font: *mut hb_font_t = hb_font_create(hb_face);
// Set points-per-em. if zero, performs no hinting in that direction.
let pt_size = (*font).descriptor.pt_size.to_f64_px();
let pt_size = font.descriptor.pt_size.to_f64_px();
hb_font_set_ppem(hb_font, pt_size as c_uint, pt_size as c_uint);
// Set scaling. Note that this takes 16.16 fixed point.
@ -180,10 +181,26 @@ impl Shaper {
hb_font_set_funcs(
hb_font,
HB_FONT_FUNCS.0,
font as *mut Font as *mut c_void,
font as *const Font as *mut c_void,
None,
);
if servo_config::pref!(layout_variable_fonts_enabled) {
let variations = &font.variations();
if !variations.is_empty() {
let variations: Vec<_> = variations
.iter()
.map(|variation| hb_variation_t {
tag: variation.tag,
value: variation.value,
})
.collect();
hb_font_set_variations(hb_font, variations.as_ptr(), variations.len() as u32);
}
}
Shaper {
hb_face,
hb_font,
@ -270,6 +287,7 @@ impl Shaper {
features.as_mut_ptr(),
features.len() as u32,
);
self.save_glyph_results(text, options, glyphs, hb_buffer);
hb_buffer_destroy(hb_buffer);
}

View file

@ -28,7 +28,7 @@ use style::values::computed::font::{
};
use style::values::computed::{FontStretch, FontWeight};
use style::values::specified::FontStretch as SpecifiedFontStretch;
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey};
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation};
use crate::font::FontDescriptor;
use crate::font_store::FontStore;
@ -65,6 +65,7 @@ pub enum SystemFontServiceMessage {
FontIdentifier,
Au,
FontInstanceFlags,
Vec<FontVariation>,
IpcSender<FontInstanceKey>,
),
GetFontKey(IpcSender<FontKey>),
@ -94,7 +95,7 @@ pub struct SystemFontService {
local_families: FontStore,
compositor_api: CrossProcessCompositorApi,
webrender_fonts: HashMap<FontIdentifier, FontKey>,
font_instances: HashMap<(FontKey, Au), FontInstanceKey>,
font_instances: HashMap<(FontKey, Au, Vec<FontVariation>), FontInstanceKey>,
generic_fonts: ResolvedGenericFontFamilies,
/// This is an optimization that allows the [`SystemFontService`] to send font data to
@ -176,8 +177,15 @@ impl SystemFontService {
let _ =
result_sender.send(self.get_font_templates(font_descriptor, font_family));
},
SystemFontServiceMessage::GetFontInstance(identifier, pt_size, flags, result) => {
let _ = result.send(self.get_font_instance(identifier, pt_size, flags));
SystemFontServiceMessage::GetFontInstance(
identifier,
pt_size,
flags,
variations,
result,
) => {
let _ =
result.send(self.get_font_instance(identifier, pt_size, flags, variations));
},
SystemFontServiceMessage::GetFontKey(result_sender) => {
self.fetch_new_keys();
@ -281,6 +289,7 @@ impl SystemFontService {
identifier: FontIdentifier,
pt_size: Au,
flags: FontInstanceFlags,
variations: Vec<FontVariation>,
) -> FontInstanceKey {
self.fetch_new_keys();
@ -301,7 +310,7 @@ impl SystemFontService {
*self
.font_instances
.entry((font_key, pt_size))
.entry((font_key, pt_size, variations.clone()))
.or_insert_with(|| {
let font_instance_key = self.free_font_instance_keys.pop().unwrap();
compositor_api.add_font_instance(
@ -309,6 +318,7 @@ impl SystemFontService {
font_key,
pt_size.to_f32_px(),
flags,
variations,
);
font_instance_key
})
@ -473,6 +483,7 @@ impl SystemFontServiceProxy {
identifier: FontIdentifier,
size: Au,
flags: FontInstanceFlags,
variations: Vec<FontVariation>,
) -> FontInstanceKey {
let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
self.sender
@ -481,6 +492,7 @@ impl SystemFontServiceProxy {
identifier,
size,
flags,
variations,
response_chan,
))
.expect("failed to send message to system font service");

View file

@ -27,7 +27,7 @@ fn make_font(path: PathBuf) -> Font {
let data = FontData::from_bytes(&bytes);
let identifier = FontIdentifier::Web(ServoUrl::from_file_path(path).unwrap());
let platform_font = PlatformFont::new_from_data(identifier.clone(), &data, None).unwrap();
let platform_font = PlatformFont::new_from_data(identifier.clone(), &data, None, &[]).unwrap();
let template = FontTemplate {
identifier,
@ -40,6 +40,7 @@ fn make_font(path: PathBuf) -> Font {
style: FontStyle::normal(),
variant: FontVariantCaps::Normal,
pt_size: Au::from_px(24),
variation_settings: vec![],
};
Font::new(FontTemplateRef::new(template), descriptor, Some(data), None).unwrap()
}

View file

@ -133,7 +133,7 @@ mod font_context {
);
},
SystemFontServiceMessage::GetFontInstanceKey(result_sender) |
SystemFontServiceMessage::GetFontInstance(_, _, _, result_sender) => {
SystemFontServiceMessage::GetFontInstance(_, _, _, _, result_sender) => {
let _ = result_sender.send(FontInstanceKey(IdNamespace(0), 0));
},
SystemFontServiceMessage::GetFontKey(result_sender) => {
@ -187,9 +187,12 @@ mod font_context {
path: path.to_str().expect("Could not load test font").into(),
variation_index: 0,
};
let handle =
PlatformFont::new_from_local_font_identifier(local_font_identifier.clone(), None)
.expect("Could not load test font");
let handle = PlatformFont::new_from_local_font_identifier(
local_font_identifier.clone(),
None,
&[],
)
.expect("Could not load test font");
family.add_template(FontTemplate::new(
FontIdentifier::Local(local_font_identifier),
@ -352,6 +355,7 @@ mod font_context {
style: FontStyle::normal(),
variant: FontVariantCaps::Normal,
pt_size: Au(10),
variation_settings: vec![],
};
let family = SingleFontFamily::FamilyName(FamilyName {

View file

@ -36,7 +36,7 @@ fn test_font_template_descriptor() {
.unwrap();
let data = FontData::from_bytes(&bytes);
let handle = PlatformFont::new_from_data(identifier, &data, None).unwrap();
let handle = PlatformFont::new_from_data(identifier, &data, None, &[]).unwrap();
handle.descriptor()
}