From 2ac8665e03e84b3cc74ae8612cbadc8f52359026 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Fri, 22 Aug 2025 04:10:17 -0700 Subject: [PATCH] fonts: Add font variations support for Windows (#38831) Unlike other platforms where we read the default axis values and combine it with variations from style to make the font face, we set the variations from the style when creating the font face and then read the final variations from the face. It seems that DirectWrite does the normalization of variation values internally. This depends on servo/dwrote-rs#68. Testing: We currently don't have tests for Windows, but variation support is covered by the WPT tests. Fixes: This is part of #38800. Signed-off-by: Martin Robinson --- Cargo.lock | 8 +-- components/fonts/Cargo.toml | 2 +- components/fonts/platform/windows/font.rs | 70 +++++++++++++++++++---- 3 files changed, 63 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e40ebd0722e..0edef019254 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2174,9 +2174,9 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "dwrote" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe1f192fcce01590bd8d839aca53ce0d11d803bf291b2a6c4ad925a8f0024be" +checksum = "20c93d234bac0cdd0e2ac08bc8a5133f8df2169e95b262dfcea5e5cb7855672f" dependencies = [ "lazy_static", "libc", @@ -4903,7 +4903,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -10079,7 +10079,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/components/fonts/Cargo.toml b/components/fonts/Cargo.toml index e4c27ca6c6f..7cb96dab891 100644 --- a/components/fonts/Cargo.toml +++ b/components/fonts/Cargo.toml @@ -73,7 +73,7 @@ fontconfig_sys = { package = "yeslogic-fontconfig-sys", version = "6" } xml-rs = "0.8" [target.'cfg(target_os = "windows")'.dependencies] -dwrote = "0.11.2" +dwrote = "0.11.4" truetype = { version = "0.47.3", features = ["ignore-invalid-language-ids"] } [lints.rust] diff --git a/components/fonts/platform/windows/font.rs b/components/fonts/platform/windows/font.rs index 55d90886225..09e0e8f1642 100644 --- a/components/fonts/platform/windows/font.rs +++ b/components/fonts/platform/windows/font.rs @@ -12,7 +12,9 @@ use std::ops::Deref; use std::sync::Arc; use app_units::Au; -use dwrote::{FontCollection, FontFace, FontFile}; +use dwrote::{ + DWRITE_FONT_AXIS_VALUE, DWRITE_FONT_SIMULATIONS_NONE, FontCollection, FontFace, FontFile, +}; use euclid::default::{Point2D, Rect, Size2D}; use log::{debug, warn}; use style::Zero; @@ -67,6 +69,7 @@ pub struct PlatformFont { em_size: f32, du_to_px: f32, scaled_du_to_px: f32, + variations: Vec, } // Based on information from the Skia codebase, it seems that DirectWrite APIs from @@ -92,7 +95,11 @@ impl Deref for Nondebug { } impl PlatformFont { - fn new(font_face: FontFace, pt_size: Option) -> Result { + fn new( + font_face: FontFace, + pt_size: Option, + variations: Vec, + ) -> Result { let pt_size = pt_size.unwrap_or(au_from_pt(12.)); let du_per_em = font_face.metrics().metrics0().designUnitsPerEm as f32; @@ -107,8 +114,51 @@ impl PlatformFont { em_size, du_to_px: design_units_to_pixels, scaled_du_to_px: scaled_design_units_to_pixels, + variations, }) } + + fn new_with_variations( + font_face: FontFace, + pt_size: Option, + variations: &[FontVariation], + ) -> Result { + if variations.is_empty() { + return Self::new(font_face, pt_size, vec![]); + } + + // On FreeType and CoreText platforms, the platform layer is able to read the minimum, maxmimum, + // and default values of each axis. This doesn't seem possible here and it seems that Gecko + // also just sets the value of the axis based on the values from the style as well. + // + // dwrote (and presumably the Windows APIs) accept a reversed version of the table + // tag bytes, which means that `u32::swap_bytes` must be called here in order to + // use a byte order compatible with the rest of Servo. + let variations: Vec<_> = variations + .into_iter() + .map(|variation| DWRITE_FONT_AXIS_VALUE { + axisTag: variation.tag.swap_bytes(), + value: variation.value, + }) + .collect(); + + let Some(font_face) = + font_face.create_font_face_with_variations(DWRITE_FONT_SIMULATIONS_NONE, &variations) + else { + return Err("Could not adapt FontFace to given variations"); + }; + + let variations = font_face.variations().unwrap_or_default(); + let variations = variations + .iter() + .map(|dwrote_variation| FontVariation { + tag: dwrote_variation.axisTag.swap_bytes(), + value: dwrote_variation.value, + }) + .collect(); + + Self::new(font_face, pt_size, variations) + } } impl PlatformFontMethods for PlatformFont { @@ -116,22 +166,19 @@ impl PlatformFontMethods for PlatformFont { _font_identifier: FontIdentifier, data: &FontData, pt_size: Option, - _variations: &[FontVariation], + variations: &[FontVariation], ) -> Result { let font_face = FontFile::new_from_buffer(Arc::new(data.clone())) .ok_or("Could not create FontFile")? - .create_face( - 0, /* face_index */ - dwrote::DWRITE_FONT_SIMULATIONS_NONE, - ) + .create_face(0 /* face_index */, DWRITE_FONT_SIMULATIONS_NONE) .map_err(|_| "Could not create FontFace")?; - Self::new(font_face, pt_size) + Self::new_with_variations(font_face, pt_size, variations) } fn new_from_local_font_identifier( font_identifier: LocalFontIdentifier, pt_size: Option, - _variations: &[FontVariation], + variations: &[FontVariation], ) -> Result { let font_face = FontCollection::system() .font_from_descriptor(&font_identifier.font_descriptor) @@ -139,7 +186,7 @@ impl PlatformFontMethods for PlatformFont { .flatten() .ok_or("Could not create Font from descriptor")? .create_font_face(); - Self::new(font_face, pt_size) + Self::new_with_variations(font_face, pt_size, variations) } fn descriptor(&self) -> FontTemplateDescriptor { @@ -324,7 +371,6 @@ impl PlatformFontMethods for PlatformFont { } fn variations(&self) -> &[FontVariation] { - // FIXME: implement this for windows - &[] + &self.variations } }