Fix variable font weight matching in FontMatchKey

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.
This commit is contained in:
Andy Terra 2026-03-05 15:31:16 -05:00 committed by Jeremy Soller
parent 29034e56b4
commit 5b9e2f2588
2 changed files with 18 additions and 8 deletions

View file

@ -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() {

View file

@ -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::<Vec<_>>();
// 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.");