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

40
Cargo.lock generated
View file

@ -2382,7 +2382,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -3042,7 +3042,7 @@ dependencies = [
"gobject-sys", "gobject-sys",
"libc", "libc",
"system-deps", "system-deps",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -4535,7 +4535,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi",
"libc", "libc",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -4839,7 +4839,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"windows-targets 0.52.6", "windows-targets 0.48.5",
] ]
[[package]] [[package]]
@ -7004,7 +7004,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.4.15", "linux-raw-sys 0.4.15",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -7017,7 +7017,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.9.4", "linux-raw-sys 0.9.4",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -7354,7 +7354,7 @@ dependencies = [
[[package]] [[package]]
name = "selectors" name = "selectors"
version = "0.31.0" version = "0.31.0"
source = "git+https://github.com/servo/stylo?branch=2025-08-01#d2519c05c9d7db31c91552d3337578270645d797" source = "git+https://github.com/servo/stylo?branch=2025-08-01#8a6763eb2a66d3c93860313fba37fc3f09c7f70f"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.1",
"cssparser", "cssparser",
@ -7660,7 +7660,7 @@ dependencies = [
[[package]] [[package]]
name = "servo_arc" name = "servo_arc"
version = "0.4.1" version = "0.4.1"
source = "git+https://github.com/servo/stylo?branch=2025-08-01#d2519c05c9d7db31c91552d3337578270645d797" source = "git+https://github.com/servo/stylo?branch=2025-08-01#8a6763eb2a66d3c93860313fba37fc3f09c7f70f"
dependencies = [ dependencies = [
"serde", "serde",
"stable_deref_trait", "stable_deref_trait",
@ -8123,7 +8123,7 @@ dependencies = [
[[package]] [[package]]
name = "stylo" name = "stylo"
version = "0.6.0" version = "0.6.0"
source = "git+https://github.com/servo/stylo?branch=2025-08-01#d2519c05c9d7db31c91552d3337578270645d797" source = "git+https://github.com/servo/stylo?branch=2025-08-01#8a6763eb2a66d3c93860313fba37fc3f09c7f70f"
dependencies = [ dependencies = [
"app_units", "app_units",
"arrayvec", "arrayvec",
@ -8180,7 +8180,7 @@ dependencies = [
[[package]] [[package]]
name = "stylo_atoms" name = "stylo_atoms"
version = "0.6.0" version = "0.6.0"
source = "git+https://github.com/servo/stylo?branch=2025-08-01#d2519c05c9d7db31c91552d3337578270645d797" source = "git+https://github.com/servo/stylo?branch=2025-08-01#8a6763eb2a66d3c93860313fba37fc3f09c7f70f"
dependencies = [ dependencies = [
"string_cache", "string_cache",
"string_cache_codegen", "string_cache_codegen",
@ -8189,12 +8189,12 @@ dependencies = [
[[package]] [[package]]
name = "stylo_config" name = "stylo_config"
version = "0.6.0" version = "0.6.0"
source = "git+https://github.com/servo/stylo?branch=2025-08-01#d2519c05c9d7db31c91552d3337578270645d797" source = "git+https://github.com/servo/stylo?branch=2025-08-01#8a6763eb2a66d3c93860313fba37fc3f09c7f70f"
[[package]] [[package]]
name = "stylo_derive" name = "stylo_derive"
version = "0.6.0" version = "0.6.0"
source = "git+https://github.com/servo/stylo?branch=2025-08-01#d2519c05c9d7db31c91552d3337578270645d797" source = "git+https://github.com/servo/stylo?branch=2025-08-01#8a6763eb2a66d3c93860313fba37fc3f09c7f70f"
dependencies = [ dependencies = [
"darling", "darling",
"proc-macro2", "proc-macro2",
@ -8206,7 +8206,7 @@ dependencies = [
[[package]] [[package]]
name = "stylo_dom" name = "stylo_dom"
version = "0.6.0" version = "0.6.0"
source = "git+https://github.com/servo/stylo?branch=2025-08-01#d2519c05c9d7db31c91552d3337578270645d797" source = "git+https://github.com/servo/stylo?branch=2025-08-01#8a6763eb2a66d3c93860313fba37fc3f09c7f70f"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.1",
"stylo_malloc_size_of", "stylo_malloc_size_of",
@ -8215,7 +8215,7 @@ dependencies = [
[[package]] [[package]]
name = "stylo_malloc_size_of" name = "stylo_malloc_size_of"
version = "0.6.0" version = "0.6.0"
source = "git+https://github.com/servo/stylo?branch=2025-08-01#d2519c05c9d7db31c91552d3337578270645d797" source = "git+https://github.com/servo/stylo?branch=2025-08-01#8a6763eb2a66d3c93860313fba37fc3f09c7f70f"
dependencies = [ dependencies = [
"app_units", "app_units",
"cssparser", "cssparser",
@ -8232,12 +8232,12 @@ dependencies = [
[[package]] [[package]]
name = "stylo_static_prefs" name = "stylo_static_prefs"
version = "0.6.0" version = "0.6.0"
source = "git+https://github.com/servo/stylo?branch=2025-08-01#d2519c05c9d7db31c91552d3337578270645d797" source = "git+https://github.com/servo/stylo?branch=2025-08-01#8a6763eb2a66d3c93860313fba37fc3f09c7f70f"
[[package]] [[package]]
name = "stylo_traits" name = "stylo_traits"
version = "0.6.0" version = "0.6.0"
source = "git+https://github.com/servo/stylo?branch=2025-08-01#d2519c05c9d7db31c91552d3337578270645d797" source = "git+https://github.com/servo/stylo?branch=2025-08-01#8a6763eb2a66d3c93860313fba37fc3f09c7f70f"
dependencies = [ dependencies = [
"app_units", "app_units",
"bitflags 2.9.1", "bitflags 2.9.1",
@ -8411,7 +8411,7 @@ dependencies = [
"getrandom 0.3.3", "getrandom 0.3.3",
"once_cell", "once_cell",
"rustix 1.0.8", "rustix 1.0.8",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -8643,7 +8643,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "to_shmem" name = "to_shmem"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/servo/stylo?branch=2025-08-01#d2519c05c9d7db31c91552d3337578270645d797" source = "git+https://github.com/servo/stylo?branch=2025-08-01#8a6763eb2a66d3c93860313fba37fc3f09c7f70f"
dependencies = [ dependencies = [
"cssparser", "cssparser",
"servo_arc", "servo_arc",
@ -8656,7 +8656,7 @@ dependencies = [
[[package]] [[package]]
name = "to_shmem_derive" name = "to_shmem_derive"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/servo/stylo?branch=2025-08-01#d2519c05c9d7db31c91552d3337578270645d797" source = "git+https://github.com/servo/stylo?branch=2025-08-01#8a6763eb2a66d3c93860313fba37fc3f09c7f70f"
dependencies = [ dependencies = [
"darling", "darling",
"proc-macro2", "proc-macro2",
@ -9946,7 +9946,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.48.0",
] ]
[[package]] [[package]]

View file

@ -43,9 +43,9 @@ use webrender_api::units::{
use webrender_api::{ use webrender_api::{
self, BuiltDisplayList, DirtyRect, DisplayListPayload, DocumentId, Epoch as WebRenderEpoch, self, BuiltDisplayList, DirtyRect, DisplayListPayload, DocumentId, Epoch as WebRenderEpoch,
ExternalScrollId, FontInstanceFlags, FontInstanceKey, FontInstanceOptions, FontKey, ExternalScrollId, FontInstanceFlags, FontInstanceKey, FontInstanceOptions, FontKey,
HitTestFlags, PipelineId as WebRenderPipelineId, PropertyBinding, ReferenceFrameKind, FontVariation, HitTestFlags, PipelineId as WebRenderPipelineId, PropertyBinding,
RenderReasons, SampledScrollOffset, ScrollLocation, SpaceAndClipInfo, SpatialId, ReferenceFrameKind, RenderReasons, SampledScrollOffset, ScrollLocation, SpaceAndClipInfo,
SpatialTreeItemKey, TransformStyle, SpatialId, SpatialTreeItemKey, TransformStyle,
}; };
use crate::InitialCompositorState; use crate::InitialCompositorState;
@ -713,8 +713,14 @@ impl IOCompositor {
self.global.borrow_mut().send_transaction(transaction); self.global.borrow_mut().send_transaction(transaction);
}, },
CompositorMsg::AddFontInstance(font_instance_key, font_key, size, flags) => { CompositorMsg::AddFontInstance(
self.add_font_instance(font_instance_key, font_key, size, flags); font_instance_key,
font_key,
size,
flags,
variations,
) => {
self.add_font_instance(font_instance_key, font_key, size, flags, variations);
}, },
CompositorMsg::RemoveFonts(keys, instance_keys) => { CompositorMsg::RemoveFonts(keys, instance_keys) => {
@ -1506,7 +1512,14 @@ impl IOCompositor {
font_key: FontKey, font_key: FontKey,
size: f32, size: f32,
flags: FontInstanceFlags, flags: FontInstanceFlags,
variations: Vec<FontVariation>,
) { ) {
let variations = if pref!(layout_variable_fonts_enabled) {
variations
} else {
vec![]
};
let mut transaction = Transaction::new(); let mut transaction = Transaction::new();
let font_instance_options = FontInstanceOptions { let font_instance_options = FontInstanceOptions {
@ -1519,7 +1532,7 @@ impl IOCompositor {
size, size,
Some(font_instance_options), Some(font_instance_options),
None, None,
Vec::new(), variations,
); );
self.global.borrow_mut().send_transaction(transaction); self.global.borrow_mut().send_transaction(transaction);

View file

@ -48,6 +48,10 @@ pub fn set(preferences: Preferences) {
"layout.container-queries.enabled", "layout.container-queries.enabled",
preferences.layout_container_queries_enabled, preferences.layout_container_queries_enabled,
); );
stylo_config::set_bool(
"layout.variable_fonts.enabled",
preferences.layout_variable_fonts_enabled,
);
let changed = preferences.diff(&PREFERENCES.read().unwrap()); let changed = preferences.diff(&PREFERENCES.read().unwrap());
@ -229,6 +233,7 @@ pub struct Preferences {
pub layout_flexbox_enabled: bool, pub layout_flexbox_enabled: bool,
pub layout_threads: i64, pub layout_threads: i64,
pub layout_unimplemented: bool, pub layout_unimplemented: bool,
pub layout_variable_fonts_enabled: bool,
pub layout_writing_mode_enabled: bool, pub layout_writing_mode_enabled: bool,
/// Enable hardware acceleration for video playback. /// Enable hardware acceleration for video playback.
pub media_glvideo_enabled: bool, pub media_glvideo_enabled: bool,
@ -407,6 +412,7 @@ impl Preferences {
// TODO(mrobinson): This should likely be based on the number of processors. // TODO(mrobinson): This should likely be based on the number of processors.
layout_threads: 3, layout_threads: 3,
layout_unimplemented: false, layout_unimplemented: false,
layout_variable_fonts_enabled: false,
layout_writing_mode_enabled: false, layout_writing_mode_enabled: false,
media_glvideo_enabled: false, media_glvideo_enabled: false,
media_testing_enabled: false, media_testing_enabled: false,

View file

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

View file

@ -32,7 +32,7 @@ use style::shared_lock::SharedRwLockReadGuard;
use style::stylesheets::{CssRule, DocumentStyleSheet, FontFaceRule, StylesheetInDocument}; use style::stylesheets::{CssRule, DocumentStyleSheet, FontFaceRule, StylesheetInDocument};
use style::values::computed::font::{FamilyName, FontFamilyNameSyntax, SingleFontFamily}; use style::values::computed::font::{FamilyName, FontFamilyNameSyntax, SingleFontFamily};
use url::Url; use url::Url;
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey}; use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation};
use crate::font::{ use crate::font::{
Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope, 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) static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
pub type FontParameters = (FontKey, Au, Vec<FontVariation>);
#[derive(MallocSizeOf)] #[derive(MallocSizeOf)]
struct FontGroupRef(#[conditional_malloc_size_of] Arc<RwLock<FontGroup>>); 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 /// A collection of WebRender [`FontInstanceKey`]s generated for the web fonts that
/// this [`FontContext`] controls. /// 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 /// The data for each web font [`FontIdentifier`]. This data might be used by more than one
/// [`FontTemplate`] as each identifier refers to a URL. /// [`FontTemplate`] as each identifier refers to a URL.
@ -270,7 +272,7 @@ impl FontContext {
) -> Result<FontRef, &'static str> { ) -> Result<FontRef, &'static str> {
Ok(FontRef(Arc::new(Font::new( Ok(FontRef(Arc::new(Font::new(
font_template.clone(), font_template.clone(),
font_descriptor.clone(), font_descriptor,
self.get_font_data(&font_template.identifier()), self.get_font_data(&font_template.identifier()),
synthesized_small_caps, synthesized_small_caps,
)?))) )?)))
@ -282,11 +284,13 @@ impl FontContext {
font.template.identifier(), font.template.identifier(),
font.descriptor.pt_size, font.descriptor.pt_size,
font.webrender_font_instance_flags(), font.webrender_font_instance_flags(),
font.variations().to_owned(),
), ),
FontIdentifier::Web(_) => self.create_web_font_instance( FontIdentifier::Web(_) => self.create_web_font_instance(
font.template.clone(), font.template.clone(),
font.descriptor.pt_size, font.descriptor.pt_size,
font.webrender_font_instance_flags(), font.webrender_font_instance_flags(),
font.variations().to_owned(),
), ),
} }
} }
@ -296,6 +300,7 @@ impl FontContext {
font_template: FontTemplateRef, font_template: FontTemplateRef,
pt_size: Au, pt_size: Au,
flags: FontInstanceFlags, flags: FontInstanceFlags,
variations: Vec<FontVariation>,
) -> FontInstanceKey { ) -> FontInstanceKey {
let identifier = font_template.identifier().clone(); let identifier = font_template.identifier().clone();
let font_data = self let font_data = self
@ -318,7 +323,7 @@ impl FontContext {
let key = *self let key = *self
.webrender_font_instance_keys .webrender_font_instance_keys
.write() .write()
.entry((font_key, pt_size)) .entry((font_key, pt_size, variations.clone()))
.or_insert_with(|| { .or_insert_with(|| {
let font_instance_key = self.system_font_service_proxy.generate_font_instance_key(); let font_instance_key = self.system_font_service_proxy.generate_font_instance_key();
self.compositor_api.lock().add_font_instance( self.compositor_api.lock().add_font_instance(
@ -326,6 +331,7 @@ impl FontContext {
font_key, font_key,
pt_size.to_f32_px(), pt_size.to_f32_px(),
flags, flags,
variations,
); );
font_instance_key font_instance_key
}); });
@ -612,7 +618,7 @@ impl FontContextWebFontMethods for Arc<FontContext> {
}); });
let mut removed_instance_keys: HashSet<FontInstanceKey> = HashSet::new(); 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) { if removed_keys.contains(font_key) {
removed_instance_keys.insert(*instance_key); removed_instance_keys.insert(*instance_key);
false false
@ -857,11 +863,11 @@ impl RemoteWebFontDownloader {
let url: ServoUrl = self.url.clone().into(); let url: ServoUrl = self.url.clone().into();
let identifier = FontIdentifier::Web(url.clone()); 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; return false;
}; };
let state = self.take_state(); let state = self.take_state();
let mut descriptor = handle.descriptor(); let mut descriptor = handle.descriptor();
descriptor descriptor
.override_values_with_css_font_template_descriptors(&state.css_font_face_descriptors); .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. // 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. // It should be allocated on the stack and passed by reference to GlyphStore.
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
pub struct GlyphData { pub struct GlyphData {
id: GlyphId, id: GlyphId,
advance: Au, 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_stretch::T as FontStretch;
use style::computed_values::font_weight::T as FontWeight; use style::computed_values::font_weight::T as FontWeight;
use style::values::computed::font::FontStyle; use style::values::computed::font::FontStyle;
use webrender_api::FontInstanceFlags; use webrender_api::{FontInstanceFlags, FontVariation};
use super::LocalFontIdentifier; use super::LocalFontIdentifier;
use super::library_handle::FreeTypeLibraryHandle; use super::library_handle::FreeTypeLibraryHandle;
@ -63,6 +63,7 @@ pub struct PlatformFont {
face: ReentrantMutex<FreeTypeFace>, face: ReentrantMutex<FreeTypeFace>,
requested_face_size: Au, requested_face_size: Au,
actual_face_size: Au, actual_face_size: Au,
variations: Vec<FontVariation>,
/// A member that allows using `skrifa` to read values from this font. /// A member that allows using `skrifa` to read values from this font.
table_provider_data: FreeTypeFaceTableProviderData, table_provider_data: FreeTypeFaceTableProviderData,
@ -73,11 +74,14 @@ impl PlatformFontMethods for PlatformFont {
_font_identifier: FontIdentifier, _font_identifier: FontIdentifier,
font_data: &FontData, font_data: &FontData,
requested_size: Option<Au>, requested_size: Option<Au>,
variations: &[FontVariation],
) -> Result<PlatformFont, &'static str> { ) -> Result<PlatformFont, &'static str> {
let library = FreeTypeLibraryHandle::get().lock(); let library = FreeTypeLibraryHandle::get().lock();
let data: &[u8] = font_data.as_ref(); let data: &[u8] = font_data.as_ref();
let face = FreeTypeFace::new_from_memory(&library, data)?; 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 { let (requested_face_size, actual_face_size) = match requested_size {
Some(requested_size) => (requested_size, face.set_size(requested_size)?), Some(requested_size) => (requested_size, face.set_size(requested_size)?),
None => (Au::zero(), Au::zero()), None => (Au::zero(), Au::zero()),
@ -88,18 +92,22 @@ impl PlatformFontMethods for PlatformFont {
requested_face_size, requested_face_size,
actual_face_size, actual_face_size,
table_provider_data: FreeTypeFaceTableProviderData::Web(font_data.clone()), table_provider_data: FreeTypeFaceTableProviderData::Web(font_data.clone()),
variations: normalized_variations,
}) })
} }
fn new_from_local_font_identifier( fn new_from_local_font_identifier(
font_identifier: LocalFontIdentifier, font_identifier: LocalFontIdentifier,
requested_size: Option<Au>, requested_size: Option<Au>,
variations: &[FontVariation],
) -> Result<PlatformFont, &'static str> { ) -> Result<PlatformFont, &'static str> {
let library = FreeTypeLibraryHandle::get().lock(); let library = FreeTypeLibraryHandle::get().lock();
let filename = CString::new(&*font_identifier.path).expect("filename contains NUL byte!"); let filename = CString::new(&*font_identifier.path).expect("filename contains NUL byte!");
let face = FreeTypeFace::new_from_file(&library, &filename, font_identifier.index())?; 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 { let (requested_face_size, actual_face_size) = match requested_size {
Some(requested_size) => (requested_size, face.set_size(requested_size)?), Some(requested_size) => (requested_size, face.set_size(requested_size)?),
None => (Au::zero(), Au::zero()), None => (Au::zero(), Au::zero()),
@ -119,6 +127,7 @@ impl PlatformFontMethods for PlatformFont {
Arc::new(memory_mapped_font_data), Arc::new(memory_mapped_font_data),
font_identifier.index(), font_identifier.index(),
), ),
variations: normalized_variations,
}) })
} }
@ -377,6 +386,10 @@ impl PlatformFontMethods for PlatformFont {
// loading bitmaps. There's no harm to always passing it. // loading bitmaps. There's no harm to always passing it.
FontInstanceFlags::EMBEDDED_BITMAPS FontInstanceFlags::EMBEDDED_BITMAPS
} }
fn variations(&self) -> &[FontVariation] {
&self.variations
}
} }
impl PlatformFont { impl PlatformFont {

View file

@ -7,10 +7,13 @@ use std::ptr;
use app_units::Au; use app_units::Au;
use freetype_sys::{ use freetype_sys::{
FT_Done_Face, FT_F26Dot6, FT_FACE_FLAG_COLOR, FT_FACE_FLAG_FIXED_SIZES, FT_FACE_FLAG_SCALABLE, FT_Done_Face, FT_Done_MM_Var, FT_F26Dot6, FT_FACE_FLAG_COLOR, FT_FACE_FLAG_FIXED_SIZES,
FT_Face, FT_FaceRec, FT_Int32, FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_Long, FT_New_Face, FT_FACE_FLAG_SCALABLE, FT_Face, FT_FaceRec, FT_Fixed, FT_Get_MM_Var, FT_HAS_MULTIPLE_MASTERS,
FT_New_Memory_Face, FT_Pos, FT_Select_Size, FT_Set_Char_Size, FT_UInt, 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; use crate::platform::freetype::library_handle::FreeTypeLibraryHandle;
@ -164,6 +167,74 @@ impl FreeTypeFace {
load_flags as FT_Int32 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. /// 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 euclid::default::{Point2D, Rect, Size2D};
use log::debug; use log::debug;
use style::values::computed::font::{FontStretch, FontStyle, FontWeight}; 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::core_text_font_cache::CoreTextFontCache;
use super::font_list::LocalFontIdentifier; use super::font_list::LocalFontIdentifier;
@ -201,6 +201,7 @@ impl PlatformFontMethods for PlatformFont {
font_identifier: FontIdentifier, font_identifier: FontIdentifier,
data: &FontData, data: &FontData,
requested_size: Option<Au>, requested_size: Option<Au>,
_variations: &[FontVariation],
) -> Result<PlatformFont, &'static str> { ) -> Result<PlatformFont, &'static str> {
Self::new(font_identifier, Some(data), requested_size) Self::new(font_identifier, Some(data), requested_size)
} }
@ -208,6 +209,7 @@ impl PlatformFontMethods for PlatformFont {
fn new_from_local_font_identifier( fn new_from_local_font_identifier(
font_identifier: LocalFontIdentifier, font_identifier: LocalFontIdentifier,
requested_size: Option<Au>, requested_size: Option<Au>,
_variations: &[FontVariation],
) -> Result<PlatformFont, &'static str> { ) -> Result<PlatformFont, &'static str> {
Self::new(FontIdentifier::Local(font_identifier), None, requested_size) 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), 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 { 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 style::values::computed::font::FontStyle as StyleFontStyle;
use truetype::tables::WindowsMetrics; use truetype::tables::WindowsMetrics;
use truetype::value::Read; use truetype::value::Read;
use webrender_api::FontInstanceFlags; use webrender_api::{FontInstanceFlags, FontVariation};
use super::font_list::LocalFontIdentifier; use super::font_list::LocalFontIdentifier;
use crate::{ use crate::{
@ -116,6 +116,7 @@ impl PlatformFontMethods for PlatformFont {
_font_identifier: FontIdentifier, _font_identifier: FontIdentifier,
data: &FontData, data: &FontData,
pt_size: Option<Au>, pt_size: Option<Au>,
_variations: &[FontVariation],
) -> Result<Self, &'static str> { ) -> Result<Self, &'static str> {
let font_face = FontFile::new_from_buffer(Arc::new(data.clone())) let font_face = FontFile::new_from_buffer(Arc::new(data.clone()))
.ok_or("Could not create FontFile")? .ok_or("Could not create FontFile")?
@ -130,6 +131,7 @@ impl PlatformFontMethods for PlatformFont {
fn new_from_local_font_identifier( fn new_from_local_font_identifier(
font_identifier: LocalFontIdentifier, font_identifier: LocalFontIdentifier,
pt_size: Option<Au>, pt_size: Option<Au>,
_variations: &[FontVariation],
) -> Result<PlatformFont, &'static str> { ) -> Result<PlatformFont, &'static str> {
let font_face = FontCollection::system() let font_face = FontCollection::system()
.font_from_descriptor(&font_identifier.font_descriptor) .font_from_descriptor(&font_identifier.font_descriptor)
@ -320,4 +322,9 @@ impl PlatformFontMethods for PlatformFont {
Size2D::new(width, height), 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_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_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_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_font_set_scale, hb_font_set_variations, hb_font_t, hb_glyph_info_t, hb_glyph_position_t,
hb_position_t, hb_script_from_iso15924_tag, hb_shape, hb_tag_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 log::debug;
use num_traits::Zero; use num_traits::Zero;
@ -32,13 +33,14 @@ use crate::font::advance_for_shaped_glyph;
use crate::platform::font::FontTable; use crate::platform::font::FontTable;
use crate::{ use crate::{
BASE, ByteIndex, Font, FontBaseline, FontTableMethods, FontTableTag, GlyphData, GlyphId, 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 NO_GLYPH: i32 = -1;
const LIGA: u32 = ot_tag!('l', 'i', 'g', 'a'); const LIGA: OpenTypeTableTag = ot_tag!('l', 'i', 'g', 'a');
const HB_OT_TAG_DEFAULT_SCRIPT: u32 = ot_tag!('D', 'F', 'L', 'T'); const HB_OT_TAG_DEFAULT_SCRIPT: OpenTypeTableTag = ot_tag!('D', 'F', 'L', 'T');
const HB_OT_TAG_DEFAULT_LANGUAGE: u32 = ot_tag!('d', 'f', 'l', 't'); const HB_OT_TAG_DEFAULT_LANGUAGE: OpenTypeTableTag = ot_tag!('d', 'f', 'l', 't');
pub struct ShapedGlyphData { pub struct ShapedGlyphData {
count: usize, count: usize,
@ -155,18 +157,17 @@ impl Drop for Shaper {
} }
impl Shaper { impl Shaper {
#[allow(clippy::not_unsafe_ptr_arg_deref)] // Has an unsafe block inside pub fn new(font: &Font) -> Shaper {
pub fn new(font: *const Font) -> Shaper {
unsafe { unsafe {
let hb_face: *mut hb_face_t = hb_face_create_for_tables( let hb_face: *mut hb_face_t = hb_face_create_for_tables(
Some(font_table_func), Some(font_table_func),
font as *const c_void as *mut c_void, font as *const Font as *mut c_void,
None, None,
); );
let hb_font: *mut hb_font_t = hb_font_create(hb_face); let hb_font: *mut hb_font_t = hb_font_create(hb_face);
// Set points-per-em. if zero, performs no hinting in that direction. // 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); 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. // Set scaling. Note that this takes 16.16 fixed point.
@ -180,10 +181,26 @@ impl Shaper {
hb_font_set_funcs( hb_font_set_funcs(
hb_font, hb_font,
HB_FONT_FUNCS.0, HB_FONT_FUNCS.0,
font as *mut Font as *mut c_void, font as *const Font as *mut c_void,
None, 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 { Shaper {
hb_face, hb_face,
hb_font, hb_font,
@ -270,6 +287,7 @@ impl Shaper {
features.as_mut_ptr(), features.as_mut_ptr(),
features.len() as u32, features.len() as u32,
); );
self.save_glyph_results(text, options, glyphs, hb_buffer); self.save_glyph_results(text, options, glyphs, hb_buffer);
hb_buffer_destroy(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::computed::{FontStretch, FontWeight};
use style::values::specified::FontStretch as SpecifiedFontStretch; 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::FontDescriptor;
use crate::font_store::FontStore; use crate::font_store::FontStore;
@ -65,6 +65,7 @@ pub enum SystemFontServiceMessage {
FontIdentifier, FontIdentifier,
Au, Au,
FontInstanceFlags, FontInstanceFlags,
Vec<FontVariation>,
IpcSender<FontInstanceKey>, IpcSender<FontInstanceKey>,
), ),
GetFontKey(IpcSender<FontKey>), GetFontKey(IpcSender<FontKey>),
@ -94,7 +95,7 @@ pub struct SystemFontService {
local_families: FontStore, local_families: FontStore,
compositor_api: CrossProcessCompositorApi, compositor_api: CrossProcessCompositorApi,
webrender_fonts: HashMap<FontIdentifier, FontKey>, webrender_fonts: HashMap<FontIdentifier, FontKey>,
font_instances: HashMap<(FontKey, Au), FontInstanceKey>, font_instances: HashMap<(FontKey, Au, Vec<FontVariation>), FontInstanceKey>,
generic_fonts: ResolvedGenericFontFamilies, generic_fonts: ResolvedGenericFontFamilies,
/// This is an optimization that allows the [`SystemFontService`] to send font data to /// This is an optimization that allows the [`SystemFontService`] to send font data to
@ -176,8 +177,15 @@ impl SystemFontService {
let _ = let _ =
result_sender.send(self.get_font_templates(font_descriptor, font_family)); result_sender.send(self.get_font_templates(font_descriptor, font_family));
}, },
SystemFontServiceMessage::GetFontInstance(identifier, pt_size, flags, result) => { SystemFontServiceMessage::GetFontInstance(
let _ = result.send(self.get_font_instance(identifier, pt_size, flags)); identifier,
pt_size,
flags,
variations,
result,
) => {
let _ =
result.send(self.get_font_instance(identifier, pt_size, flags, variations));
}, },
SystemFontServiceMessage::GetFontKey(result_sender) => { SystemFontServiceMessage::GetFontKey(result_sender) => {
self.fetch_new_keys(); self.fetch_new_keys();
@ -281,6 +289,7 @@ impl SystemFontService {
identifier: FontIdentifier, identifier: FontIdentifier,
pt_size: Au, pt_size: Au,
flags: FontInstanceFlags, flags: FontInstanceFlags,
variations: Vec<FontVariation>,
) -> FontInstanceKey { ) -> FontInstanceKey {
self.fetch_new_keys(); self.fetch_new_keys();
@ -301,7 +310,7 @@ impl SystemFontService {
*self *self
.font_instances .font_instances
.entry((font_key, pt_size)) .entry((font_key, pt_size, variations.clone()))
.or_insert_with(|| { .or_insert_with(|| {
let font_instance_key = self.free_font_instance_keys.pop().unwrap(); let font_instance_key = self.free_font_instance_keys.pop().unwrap();
compositor_api.add_font_instance( compositor_api.add_font_instance(
@ -309,6 +318,7 @@ impl SystemFontService {
font_key, font_key,
pt_size.to_f32_px(), pt_size.to_f32_px(),
flags, flags,
variations,
); );
font_instance_key font_instance_key
}) })
@ -473,6 +483,7 @@ impl SystemFontServiceProxy {
identifier: FontIdentifier, identifier: FontIdentifier,
size: Au, size: Au,
flags: FontInstanceFlags, flags: FontInstanceFlags,
variations: Vec<FontVariation>,
) -> FontInstanceKey { ) -> FontInstanceKey {
let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel"); let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
self.sender self.sender
@ -481,6 +492,7 @@ impl SystemFontServiceProxy {
identifier, identifier,
size, size,
flags, flags,
variations,
response_chan, response_chan,
)) ))
.expect("failed to send message to system font service"); .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 data = FontData::from_bytes(&bytes);
let identifier = FontIdentifier::Web(ServoUrl::from_file_path(path).unwrap()); 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 { let template = FontTemplate {
identifier, identifier,
@ -40,6 +40,7 @@ fn make_font(path: PathBuf) -> Font {
style: FontStyle::normal(), style: FontStyle::normal(),
variant: FontVariantCaps::Normal, variant: FontVariantCaps::Normal,
pt_size: Au::from_px(24), pt_size: Au::from_px(24),
variation_settings: vec![],
}; };
Font::new(FontTemplateRef::new(template), descriptor, Some(data), None).unwrap() Font::new(FontTemplateRef::new(template), descriptor, Some(data), None).unwrap()
} }

View file

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

View file

@ -36,7 +36,7 @@ fn test_font_template_descriptor() {
.unwrap(); .unwrap();
let data = FontData::from_bytes(&bytes); 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() handle.descriptor()
} }

View file

@ -432,6 +432,7 @@ impl TextRun {
&shaping_options, &shaping_options,
font, font,
); );
segment segment
}) })
.collect(); .collect();

View file

@ -837,6 +837,7 @@ malloc_size_of_is_webrender_malloc_size_of!(webrender_api::LineStyle);
malloc_size_of_is_webrender_malloc_size_of!(webrender_api::MixBlendMode); malloc_size_of_is_webrender_malloc_size_of!(webrender_api::MixBlendMode);
malloc_size_of_is_webrender_malloc_size_of!(webrender_api::NormalBorder); malloc_size_of_is_webrender_malloc_size_of!(webrender_api::NormalBorder);
malloc_size_of_is_webrender_malloc_size_of!(webrender_api::RepeatMode); malloc_size_of_is_webrender_malloc_size_of!(webrender_api::RepeatMode);
malloc_size_of_is_webrender_malloc_size_of!(webrender_api::FontVariation);
macro_rules! malloc_size_of_is_stylo_malloc_size_of( macro_rules! malloc_size_of_is_stylo_malloc_size_of(
($($ty:ty),+) => ( ($($ty:ty),+) => (

View file

@ -122,6 +122,7 @@ def add_css_properties_attributes(css_properties_json: str, parser: Parser) -> N
["layout.css.transition-behavior.enabled", "layout_css_transition_behavior_enabled"], ["layout.css.transition-behavior.enabled", "layout_css_transition_behavior_enabled"],
["layout.writing-mode.enabled", "layout_writing_mode_enabled"], ["layout.writing-mode.enabled", "layout_writing_mode_enabled"],
["layout.container-queries.enabled", "layout_container_queries_enabled"], ["layout.container-queries.enabled", "layout_container_queries_enabled"],
["layout.variable_fonts.enabled", "layout_variable_fonts_enabled"]
] ]
for mapping in MAPPING: for mapping in MAPPING:
if mapping[0] == preference_name: if mapping[0] == preference_name:

View file

@ -14,7 +14,7 @@ use log::warn;
use malloc_size_of_derive::MallocSizeOf; use malloc_size_of_derive::MallocSizeOf;
use smallvec::SmallVec; use smallvec::SmallVec;
use strum_macros::IntoStaticStr; use strum_macros::IntoStaticStr;
use webrender_api::DocumentId; use webrender_api::{DocumentId, FontVariation};
pub mod display_list; pub mod display_list;
pub mod rendering_context; pub mod rendering_context;
@ -132,7 +132,13 @@ pub enum CompositorMsg {
/// Add a system font with the given font key and handle. /// Add a system font with the given font key and handle.
AddSystemFont(FontKey, NativeFontHandle), AddSystemFont(FontKey, NativeFontHandle),
/// Add an instance of a font with the given instance key. /// Add an instance of a font with the given instance key.
AddFontInstance(FontInstanceKey, FontKey, f32, FontInstanceFlags), AddFontInstance(
FontInstanceKey,
FontKey,
f32,
FontInstanceFlags,
Vec<FontVariation>,
),
/// Remove the given font resources from our WebRender instance. /// Remove the given font resources from our WebRender instance.
RemoveFonts(Vec<FontKey>, Vec<FontInstanceKey>), RemoveFonts(Vec<FontKey>, Vec<FontInstanceKey>),
/// Measure the current memory usage associated with the compositor. /// Measure the current memory usage associated with the compositor.
@ -303,12 +309,14 @@ impl CrossProcessCompositorApi {
font_key: FontKey, font_key: FontKey,
size: f32, size: f32,
flags: FontInstanceFlags, flags: FontInstanceFlags,
variations: Vec<FontVariation>,
) { ) {
let _x = self.0.send(CompositorMsg::AddFontInstance( let _x = self.0.send(CompositorMsg::AddFontInstance(
font_instance_key, font_instance_key,
font_key, font_key,
size, size,
flags, flags,
variations,
)); ));
} }