From 5b9e2f2588164c54570cf8a51440296a0aaf76b8 Mon Sep 17 00:00:00 2001 From: Andy Terra <152812+airstrike@users.noreply.github.com> Date: Thu, 5 Mar 2026 15:31:16 -0500 Subject: [PATCH] Fix variable font weight matching in FontMatchKey MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add variable_weight_match flag to FontMatchKey: when a face has a wght axis covering the requested weight, let it pass the font_weight_diff == 0 filter without altering sort priority. The check is done inline via db.with_face_data() in FontMatchKey::new() so it participates in the existing font_matches_cache invalidation — no separate caching needed. --- src/font/fallback/mod.rs | 8 ++++---- src/font/system.rs | 18 ++++++++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/font/fallback/mod.rs b/src/font/fallback/mod.rs index 00b7218..3b9aaaf 100644 --- a/src/font/fallback/mod.rs +++ b/src/font/fallback/mod.rs @@ -282,7 +282,7 @@ impl<'a> FontFallbackIter<'a> { self.font_match_keys .iter() - .filter(|m_key| m_key.font_weight_diff == 0) + .filter(|m_key| m_key.font_weight_diff == 0 || m_key.variable_weight_match) .find(|m_key| self.face_contains_family(m_key.id, default_family_name)) } @@ -297,9 +297,9 @@ impl<'a> FontFallbackIter<'a> { } let font_match_keys_iter = |is_mono| { - self.font_match_keys - .iter() - .filter(move |m_key| m_key.font_weight_diff == 0 || is_mono) + self.font_match_keys.iter().filter(move |m_key| { + m_key.font_weight_diff == 0 || m_key.variable_weight_match || is_mono + }) }; 'DEF_FAM: while self.default_i < self.default_families.len() { diff --git a/src/font/system.rs b/src/font/system.rs index 2d19743..6542289 100644 --- a/src/font/system.rs +++ b/src/font/system.rs @@ -8,6 +8,7 @@ use core::fmt; use core::ops::{Deref, DerefMut}; use fontdb::{FaceInfo, Query, Style}; use skrifa::raw::{ReadError, TableProvider as _}; +use skrifa::MetadataProvider; // re-export fontdb and harfrust pub use fontdb; @@ -25,14 +26,22 @@ pub struct FontMatchKey { pub(crate) font_weight: u16, pub(crate) font_stretch: u16, pub(crate) id: fontdb::ID, + pub(crate) variable_weight_match: bool, } impl FontMatchKey { - fn new(attrs: &Attrs, face: &FaceInfo) -> FontMatchKey { + fn new(attrs: &Attrs, face: &FaceInfo, db: &fontdb::Database) -> FontMatchKey { // TODO: smarter way of detecting emoji let not_emoji = !face.post_script_name.contains("Emoji"); - // TODO: correctly take variable axes into account let font_weight_diff = attrs.weight.0.abs_diff(face.weight.0); + + let variable_weight_match = font_weight_diff != 0 + && db.with_face_data(face.id, |font_data, face_index| { + let font_ref = skrifa::FontRef::from_index(font_data, face_index).ok()?; + let axis = font_ref.axes().get_by_tag(skrifa::Tag::new(b"wght"))?; + let w = attrs.weight.0 as f32; + Some(w >= axis.min_value() && w <= axis.max_value()) + }) == Some(Some(true)); let font_weight = face.weight.0; let font_stretch_diff = attrs.stretch.to_number().abs_diff(face.stretch.to_number()); let font_stretch = face.stretch.to_number(); @@ -55,6 +64,7 @@ impl FontMatchKey { font_weight, font_stretch, id, + variable_weight_match, } } } @@ -363,7 +373,7 @@ impl FontSystem { let mut font_match_keys = self .db .faces() - .map(|face| FontMatchKey::new(attrs, face)) + .map(|face| FontMatchKey::new(attrs, face, &self.db)) .collect::>(); // Sort so we get the keys with weight_offset=0 first @@ -389,7 +399,7 @@ impl FontSystem { font_match_keys.insert(0, match_key); } else if let Some(face) = self.db.face(id) { // else insert in front - let match_key = FontMatchKey::new(attrs, face); + let match_key = FontMatchKey::new(attrs, face, &self.db); font_match_keys.insert(0, match_key); } else { log::error!("Could not get face from db, that should've been there.");