Use weight absolute difference in monospace fallback matching

When matching on weights smaller than normal, "equal or smaller"
 weight restriction may cause monospace fallback to fail, depending
 on font support at such weights for the text to be shaped.

 So remove that restriction, and calculate weight differences instead
 of offsets.

 In case of no exact weight match, and with all other factors being
 equal, smaller weights will be picked before bigger ones. So, this
 should generally not cause any behavioral changes when matching on
 normal weight or bigger.

 Should fix pop-os/cosmic-term#104.

Signed-off-by: Mohammad AlSaleh <CE.Mohammad.AlSaleh@gmail.com>
This commit is contained in:
Mohammad AlSaleh 2024-01-31 13:45:19 +03:00 committed by Jeremy Soller
parent 1a18296a67
commit 0cea55630c
3 changed files with 14 additions and 12 deletions

View file

@ -176,10 +176,7 @@ impl<'a> Attrs<'a> {
pub fn matches(&self, face: &fontdb::FaceInfo) -> bool {
//TODO: smarter way of including emoji
face.post_script_name.contains("Emoji")
|| (face.style == self.style
// Relax exact weight matching for the Monospace fallback use-case
&& face.weight <= self.weight
&& face.stretch == self.stretch)
|| (face.style == self.style && face.stretch == self.stretch)
}
/// Check if this set of attributes can be shaped with another

View file

@ -32,13 +32,14 @@ use log::debug as missing_warn;
#[cfg(feature = "warn_on_missing_glyphs")]
use log::warn as missing_warn;
// Match on lowest weight_offset, then script_non_matches
// Match on lowest font_weight_diff, then script_non_matches, then font_weight
// Default font gets None for both `weight_offset` and `script_non_matches`, and thus, it is
// always the first to be popped from the set.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct MonospaceFallbackInfo {
weight_offset: Option<u16>,
font_weight_diff: Option<u16>,
codepoint_non_matches: Option<usize>,
font_weight: u16,
id: fontdb::ID,
}
@ -144,7 +145,7 @@ impl<'a> Iterator for FontFallbackIter<'a> {
let font_match_keys_iter = |is_mono| {
self.font_match_keys
.iter()
.filter(move |m_key| m_key.weight_offset == Some(0) || is_mono)
.filter(move |m_key| m_key.font_weight_diff == 0 || is_mono)
};
while self.default_i < self.default_families.len() {
@ -160,11 +161,12 @@ impl<'a> Iterator for FontFallbackIter<'a> {
if let Some(font) = self.font_system.get_font(m_key.id) {
if !is_mono {
return Some(font);
} else if m_key.weight_offset == Some(0) {
} else if m_key.font_weight_diff == 0 {
// Default font
let fallback_info = MonospaceFallbackInfo {
weight_offset: None,
font_weight_diff: None,
codepoint_non_matches: None,
font_weight: m_key.font_weight,
id: m_key.id,
};
assert!(self.monospace_fallbacks.insert(fallback_info));
@ -187,8 +189,9 @@ impl<'a> Iterator for FontFallbackIter<'a> {
.count();
let fallback_info = MonospaceFallbackInfo {
weight_offset: m_key.weight_offset,
font_weight_diff: Some(m_key.font_weight_diff),
codepoint_non_matches: Some(codepoint_non_matches),
font_weight: m_key.font_weight,
id: m_key.id,
};
assert!(self.monospace_fallbacks.insert(fallback_info));

View file

@ -11,7 +11,8 @@ pub use rustybuzz;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct FontMatchKey {
pub(crate) weight_offset: Option<u16>,
pub(crate) font_weight_diff: u16,
pub(crate) font_weight: u16,
pub(crate) id: fontdb::ID,
}
@ -151,7 +152,8 @@ impl FontSystem {
.faces()
.filter(|face| attrs.matches(face))
.map(|face| FontMatchKey {
weight_offset: attrs.weight.0.checked_sub(face.weight.0),
font_weight_diff: attrs.weight.0.abs_diff(face.weight.0),
font_weight: face.weight.0,
id: face.id,
})
.collect::<Vec<_>>();