diff --git a/Cargo.lock b/Cargo.lock index 11e85bdafd8..f2081c3fd34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2382,7 +2382,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -3042,7 +3042,7 @@ dependencies = [ "gobject-sys", "libc", "system-deps", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4535,7 +4535,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4839,7 +4839,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -7004,7 +7004,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7017,7 +7017,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7354,7 +7354,7 @@ dependencies = [ [[package]] name = "selectors" 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 = [ "bitflags 2.9.1", "cssparser", @@ -7660,7 +7660,7 @@ dependencies = [ [[package]] name = "servo_arc" 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 = [ "serde", "stable_deref_trait", @@ -8123,7 +8123,7 @@ dependencies = [ [[package]] name = "stylo" 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 = [ "app_units", "arrayvec", @@ -8180,7 +8180,7 @@ dependencies = [ [[package]] name = "stylo_atoms" 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 = [ "string_cache", "string_cache_codegen", @@ -8189,12 +8189,12 @@ dependencies = [ [[package]] name = "stylo_config" 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]] name = "stylo_derive" 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 = [ "darling", "proc-macro2", @@ -8206,7 +8206,7 @@ dependencies = [ [[package]] name = "stylo_dom" 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 = [ "bitflags 2.9.1", "stylo_malloc_size_of", @@ -8215,7 +8215,7 @@ dependencies = [ [[package]] name = "stylo_malloc_size_of" 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 = [ "app_units", "cssparser", @@ -8232,12 +8232,12 @@ dependencies = [ [[package]] name = "stylo_static_prefs" 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]] name = "stylo_traits" 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 = [ "app_units", "bitflags 2.9.1", @@ -8411,7 +8411,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix 1.0.8", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -8643,7 +8643,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "to_shmem" 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 = [ "cssparser", "servo_arc", @@ -8656,7 +8656,7 @@ dependencies = [ [[package]] name = "to_shmem_derive" 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 = [ "darling", "proc-macro2", @@ -9946,7 +9946,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index eb3f4432a3c..a8065bd667c 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -43,9 +43,9 @@ use webrender_api::units::{ use webrender_api::{ self, BuiltDisplayList, DirtyRect, DisplayListPayload, DocumentId, Epoch as WebRenderEpoch, ExternalScrollId, FontInstanceFlags, FontInstanceKey, FontInstanceOptions, FontKey, - HitTestFlags, PipelineId as WebRenderPipelineId, PropertyBinding, ReferenceFrameKind, - RenderReasons, SampledScrollOffset, ScrollLocation, SpaceAndClipInfo, SpatialId, - SpatialTreeItemKey, TransformStyle, + FontVariation, HitTestFlags, PipelineId as WebRenderPipelineId, PropertyBinding, + ReferenceFrameKind, RenderReasons, SampledScrollOffset, ScrollLocation, SpaceAndClipInfo, + SpatialId, SpatialTreeItemKey, TransformStyle, }; use crate::InitialCompositorState; @@ -713,8 +713,14 @@ impl IOCompositor { self.global.borrow_mut().send_transaction(transaction); }, - CompositorMsg::AddFontInstance(font_instance_key, font_key, size, flags) => { - self.add_font_instance(font_instance_key, font_key, size, flags); + CompositorMsg::AddFontInstance( + 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) => { @@ -1506,7 +1512,14 @@ impl IOCompositor { font_key: FontKey, size: f32, flags: FontInstanceFlags, + variations: Vec, ) { + let variations = if pref!(layout_variable_fonts_enabled) { + variations + } else { + vec![] + }; + let mut transaction = Transaction::new(); let font_instance_options = FontInstanceOptions { @@ -1519,7 +1532,7 @@ impl IOCompositor { size, Some(font_instance_options), None, - Vec::new(), + variations, ); self.global.borrow_mut().send_transaction(transaction); diff --git a/components/config/prefs.rs b/components/config/prefs.rs index b7449ac1b07..c21679995d7 100644 --- a/components/config/prefs.rs +++ b/components/config/prefs.rs @@ -48,6 +48,10 @@ pub fn set(preferences: 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()); @@ -229,6 +233,7 @@ pub struct Preferences { pub layout_flexbox_enabled: bool, pub layout_threads: i64, pub layout_unimplemented: bool, + pub layout_variable_fonts_enabled: bool, pub layout_writing_mode_enabled: bool, /// Enable hardware acceleration for video playback. pub media_glvideo_enabled: bool, @@ -407,6 +412,7 @@ impl Preferences { // TODO(mrobinson): This should likely be based on the number of processors. layout_threads: 3, layout_unimplemented: false, + layout_variable_fonts_enabled: false, layout_writing_mode_enabled: false, media_glvideo_enabled: false, media_testing_enabled: false, diff --git a/components/fonts/font.rs b/components/fonts/font.rs index 1a323d6b36a..751aeecdba7 100644 --- a/components/fonts/font.rs +++ b/components/fonts/font.rs @@ -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, + variations: &[FontVariation], data: &Option, ) -> Result { 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, + variations: &[FontVariation], ) -> Result; fn new_from_data( font_identifier: FontIdentifier, data: &FontData, pt_size: Option, + variations: &[FontVariation], ) -> Result; /// 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, } 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, synthesized_small_caps: Option, ) -> Result { - 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 { - let this = self as *const Font; - self.shaper.get_or_init(|| Shaper::new(this)).baseline() + self.shaper.get_or_init(|| Shaper::new(self)).baseline() } } diff --git a/components/fonts/font_context.rs b/components/fonts/font_context.rs index ff080cebae8..a7df1ae3068 100644 --- a/components/fonts/font_context.rs +++ b/components/fonts/font_context.rs @@ -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); + #[derive(MallocSizeOf)] struct FontGroupRef(#[conditional_malloc_size_of] Arc>); @@ -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>, + webrender_font_instance_keys: RwLock>, /// 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 { 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, ) -> 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 { }); let mut removed_instance_keys: HashSet = 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); diff --git a/components/fonts/glyph.rs b/components/fonts/glyph.rs index db97ee0ebee..954d30da560 100644 --- a/components/fonts/glyph.rs +++ b/components/fonts/glyph.rs @@ -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, diff --git a/components/fonts/platform/freetype/font.rs b/components/fonts/platform/freetype/font.rs index 2d721183ab6..617c488df33 100644 --- a/components/fonts/platform/freetype/font.rs +++ b/components/fonts/platform/freetype/font.rs @@ -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, requested_face_size: Au, actual_face_size: Au, + variations: Vec, /// 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, + variations: &[FontVariation], ) -> Result { 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, + variations: &[FontVariation], ) -> Result { 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 { diff --git a/components/fonts/platform/freetype/freetype_face.rs b/components/fonts/platform/freetype/freetype_face.rs index 3683a062ba0..2a92108885a 100644 --- a/components/fonts/platform/freetype/freetype_face.rs +++ b/components/fonts/platform/freetype/freetype_face.rs @@ -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, &'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. diff --git a/components/fonts/platform/macos/font.rs b/components/fonts/platform/macos/font.rs index af3841141a8..e776337b550 100644 --- a/components/fonts/platform/macos/font.rs +++ b/components/fonts/platform/macos/font.rs @@ -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, + _variations: &[FontVariation], ) -> Result { 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, + _variations: &[FontVariation], ) -> Result { 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 { diff --git a/components/fonts/platform/windows/font.rs b/components/fonts/platform/windows/font.rs index 74f592e63b9..55d90886225 100644 --- a/components/fonts/platform/windows/font.rs +++ b/components/fonts/platform/windows/font.rs @@ -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, + _variations: &[FontVariation], ) -> Result { 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, + _variations: &[FontVariation], ) -> Result { 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 + &[] + } } diff --git a/components/fonts/shaper.rs b/components/fonts/shaper.rs index 92703463764..3a8c2bbd99a 100644 --- a/components/fonts/shaper.rs +++ b/components/fonts/shaper.rs @@ -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); } diff --git a/components/fonts/system_font_service.rs b/components/fonts/system_font_service.rs index c41a6b799e5..dd2de815596 100644 --- a/components/fonts/system_font_service.rs +++ b/components/fonts/system_font_service.rs @@ -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, IpcSender, ), GetFontKey(IpcSender), @@ -94,7 +95,7 @@ pub struct SystemFontService { local_families: FontStore, compositor_api: CrossProcessCompositorApi, webrender_fonts: HashMap, - font_instances: HashMap<(FontKey, Au), FontInstanceKey>, + font_instances: HashMap<(FontKey, Au, Vec), 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, ) -> 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, ) -> 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"); diff --git a/components/fonts/tests/font.rs b/components/fonts/tests/font.rs index a473be9222b..6b102b79981 100644 --- a/components/fonts/tests/font.rs +++ b/components/fonts/tests/font.rs @@ -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() } diff --git a/components/fonts/tests/font_context.rs b/components/fonts/tests/font_context.rs index f03e4bf861e..e551b8aed38 100644 --- a/components/fonts/tests/font_context.rs +++ b/components/fonts/tests/font_context.rs @@ -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 { diff --git a/components/fonts/tests/font_template.rs b/components/fonts/tests/font_template.rs index 031e57342db..3fa32222931 100644 --- a/components/fonts/tests/font_template.rs +++ b/components/fonts/tests/font_template.rs @@ -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() } diff --git a/components/layout/flow/inline/text_run.rs b/components/layout/flow/inline/text_run.rs index 591c7b9b5e2..e37be9a5687 100644 --- a/components/layout/flow/inline/text_run.rs +++ b/components/layout/flow/inline/text_run.rs @@ -432,6 +432,7 @@ impl TextRun { &shaping_options, font, ); + segment }) .collect(); diff --git a/components/malloc_size_of/lib.rs b/components/malloc_size_of/lib.rs index ff769a6af05..7897a85dc9b 100644 --- a/components/malloc_size_of/lib.rs +++ b/components/malloc_size_of/lib.rs @@ -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::NormalBorder); 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( ($($ty:ty),+) => ( diff --git a/components/script_bindings/codegen/run.py b/components/script_bindings/codegen/run.py index ede354e2a02..6c23b93e722 100644 --- a/components/script_bindings/codegen/run.py +++ b/components/script_bindings/codegen/run.py @@ -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.writing-mode.enabled", "layout_writing_mode_enabled"], ["layout.container-queries.enabled", "layout_container_queries_enabled"], + ["layout.variable_fonts.enabled", "layout_variable_fonts_enabled"] ] for mapping in MAPPING: if mapping[0] == preference_name: diff --git a/components/shared/compositing/lib.rs b/components/shared/compositing/lib.rs index b083e0060a4..6592276f74c 100644 --- a/components/shared/compositing/lib.rs +++ b/components/shared/compositing/lib.rs @@ -14,7 +14,7 @@ use log::warn; use malloc_size_of_derive::MallocSizeOf; use smallvec::SmallVec; use strum_macros::IntoStaticStr; -use webrender_api::DocumentId; +use webrender_api::{DocumentId, FontVariation}; pub mod display_list; pub mod rendering_context; @@ -132,7 +132,13 @@ pub enum CompositorMsg { /// Add a system font with the given font key and handle. AddSystemFont(FontKey, NativeFontHandle), /// Add an instance of a font with the given instance key. - AddFontInstance(FontInstanceKey, FontKey, f32, FontInstanceFlags), + AddFontInstance( + FontInstanceKey, + FontKey, + f32, + FontInstanceFlags, + Vec, + ), /// Remove the given font resources from our WebRender instance. RemoveFonts(Vec, Vec), /// Measure the current memory usage associated with the compositor. @@ -303,12 +309,14 @@ impl CrossProcessCompositorApi { font_key: FontKey, size: f32, flags: FontInstanceFlags, + variations: Vec, ) { let _x = self.0.send(CompositorMsg::AddFontInstance( font_instance_key, font_key, size, flags, + variations, )); }