Try harder to succeed at fall-backing to a Monospace font

A combination of some ideas:

 * Try all Monospace fonts before giving up.
 * Relax exact weight restriction on font matching when trying Monospace
   fall-back. Try smaller weights if needed.
 * Make the fall-back try order weight-offset aware, AND script-aware.
 * And finally, add the option to adjust the font size of glyphs using
   fall-back Monospace fonts, so the width of them matches the default
   font width.

   For my use-case, the current fall-back attempt always fails with
   Arabic script. And none of the Arabic-supporting Monospace fonts in
   my system also support medium weight. So, if my default font is set
   to medium weight, script-aware fall-back alone will still not work.

Signed-off-by: Mohammad AlSaleh <CE.Mohammad.AlSaleh@gmail.com>
This commit is contained in:
Mohammad AlSaleh 2024-01-17 12:32:33 +03:00 committed by Jeremy Soller
parent 054b7da828
commit 329941c4a6
8 changed files with 177 additions and 39 deletions

View file

@ -9,6 +9,12 @@ use core::ops::{Deref, DerefMut};
pub use fontdb;
pub use rustybuzz;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct FontMatchKey {
pub(crate) weight_offset: u16,
pub(crate) id: fontdb::ID,
}
/// Access to the system fonts.
pub struct FontSystem {
/// The locale of the system.
@ -21,7 +27,7 @@ pub struct FontSystem {
font_cache: HashMap<fontdb::ID, Option<Arc<Font>>>,
/// Cache for font matches.
font_matches_cache: HashMap<AttrsOwned, Arc<Vec<fontdb::ID>>>,
font_matches_cache: HashMap<AttrsOwned, Arc<Vec<FontMatchKey>>>,
/// Cache for rustybuzz shape plans.
shape_plan_cache: ShapePlanCache,
@ -111,11 +117,10 @@ impl FontSystem {
unsafe {
self.db.make_shared_face_data(id);
}
let face = self.db.face(id)?;
match Font::new(face) {
match Font::new(&self.db, id) {
Some(font) => Some(Arc::new(font)),
None => {
log::warn!("failed to load font '{}'", face.post_script_name);
log::warn!("failed to load font '{}'", self.db.face(id)?.post_script_name);
None
}
}
@ -123,7 +128,7 @@ impl FontSystem {
.clone()
}
pub fn get_font_matches(&mut self, attrs: Attrs<'_>) -> Arc<Vec<fontdb::ID>> {
pub fn get_font_matches(&mut self, attrs: Attrs<'_>) -> Arc<Vec<FontMatchKey>> {
self.font_matches_cache
//TODO: do not create AttrsOwned unless entry does not already exist
.entry(AttrsOwned::new(attrs))
@ -131,20 +136,23 @@ impl FontSystem {
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
let now = std::time::Instant::now();
let ids = self
let mut font_match_keys = self
.db
.faces()
.filter(|face| attrs.matches(face))
.map(|face| face.id)
.map(|face| FontMatchKey{ weight_offset: attrs.weight.0 - face.weight.0, id: face.id })
.collect::<Vec<_>>();
// Sort so we get the keys with weight_offset=0 first
font_match_keys.sort();
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
{
let elapsed = now.elapsed();
log::debug!("font matches for {:?} in {:?}", attrs, elapsed);
}
Arc::new(ids)
Arc::new(font_match_keys)
})
.clone()
}