diff --git a/src/font/fallback/mod.rs b/src/font/fallback/mod.rs index 02d5fcb..d090be5 100644 --- a/src/font/fallback/mod.rs +++ b/src/font/fallback/mod.rs @@ -2,6 +2,7 @@ use alloc::collections::BTreeSet; use alloc::sync::Arc; +use alloc::vec::Vec; use fontdb::Family; use unicode_script::Script; @@ -150,6 +151,16 @@ impl<'a> Iterator for FontFallbackIter<'a> { self.default_i += 1; let is_mono = self.default_families[self.default_i - 1] == &Family::Monospace; + let mono_ids_for_scripts = if is_mono && !self.scripts.is_empty() { + let scripts = self.scripts.iter().filter_map(|script| { + let script_as_lower = script.short_name().to_lowercase(); + <[u8; 4]>::try_from(script_as_lower.as_bytes()).ok() + }); + self.font_system.get_monospace_ids_for_scripts(scripts) + } else { + Vec::new() + }; + for m_key in font_match_keys_iter(is_mono) { let default_family = self .font_system @@ -173,7 +184,13 @@ impl<'a> Iterator for FontFallbackIter<'a> { } // Set a monospace fallback if Monospace family is not found if is_mono { - if self.font_system.is_monospace(m_key.id) { + let include_mono_id = if mono_ids_for_scripts.is_empty() { + self.font_system.is_monospace(m_key.id) + } else { + mono_ids_for_scripts.binary_search(&m_key.id).is_ok() + }; + + if include_mono_id { let supported_cp_count_opt = self .font_system .get_font_supported_codepoints_in_word(m_key.id, self.word); diff --git a/src/font/mod.rs b/src/font/mod.rs index 25dbf9b..a25d8b2 100644 --- a/src/font/mod.rs +++ b/src/font/mod.rs @@ -30,6 +30,7 @@ pub struct Font { data: Arc + Send + Sync>, id: fontdb::ID, monospace_em_width: Option, + scripts: Vec<[u8; 4]>, unicode_codepoints: Vec, } @@ -50,6 +51,10 @@ impl Font { self.monospace_em_width } + pub fn scripts(&self) -> &[[u8; 4]] { + &self.scripts + } + pub fn unicode_codepoints(&self) -> &[u32] { &self.unicode_codepoints } @@ -77,7 +82,7 @@ impl Font { pub fn new(db: &fontdb::Database, id: fontdb::ID) -> Option { let info = db.face(id)?; - let (monospace_em_width, unicode_codepoints) = { + let (monospace_em_width, scripts, unicode_codepoints) = { db.with_face_data(id, |font_data, face_index| { let face = ttf_parser::Face::parse(font_data, face_index).ok()?; let monospace_em_width = info @@ -93,6 +98,15 @@ impl Font { None?; } + let scripts = face + .tables() + .gpos + .into_iter() + .chain(face.tables().gsub) + .flat_map(|table| table.scripts) + .map(|script| script.tag.to_bytes()) + .collect(); + let mut unicode_codepoints = Vec::new(); face.tables() @@ -111,7 +125,7 @@ impl Font { unicode_codepoints.shrink_to_fit(); - Some((monospace_em_width, unicode_codepoints)) + Some((monospace_em_width, scripts, unicode_codepoints)) })? }?; @@ -129,6 +143,7 @@ impl Font { Some(Self { id: info.id, monospace_em_width, + scripts, unicode_codepoints, #[cfg(feature = "swash")] swash: { diff --git a/src/font/system.rs b/src/font/system.rs index 5cdf24a..0872ba4 100644 --- a/src/font/system.rs +++ b/src/font/system.rs @@ -89,6 +89,11 @@ pub struct FontSystem { /// Sorted unique ID's of all Monospace fonts in DB monospace_font_ids: Vec, + /// Sorted unique ID's of all Monospace fonts in DB per script. + /// A font may support multiple scripts of course, so the same ID + /// may appear in multiple map value vecs. + per_script_monospace_font_ids: HashMap<[u8; 4], Vec>, + /// Cache for font codepoint support info font_codepoint_support_info_cache: HashMap, @@ -153,17 +158,32 @@ impl FontSystem { .collect::>(); monospace_font_ids.sort(); - Self { + let cloned_monospace_font_ids = monospace_font_ids.clone(); + + let mut ret = Self { locale, db, monospace_font_ids, + per_script_monospace_font_ids: Default::default(), font_cache: Default::default(), font_matches_cache: Default::default(), font_codepoint_support_info_cache: Default::default(), shape_plan_cache: ShapePlanCache::default(), #[cfg(feature = "shape-run-cache")] shape_run_cache: crate::ShapeRunCache::default(), - } + }; + + cloned_monospace_font_ids.into_iter().for_each(|id| { + if let Some(font) = ret.get_font(id) { + font.scripts().iter().copied().for_each(|script| { + ret.per_script_monospace_font_ids + .entry(script) + .or_default() + .push(font.id); + }); + } + }); + ret } /// Get the locale. @@ -219,6 +239,19 @@ impl FontSystem { self.monospace_font_ids.binary_search(&id).is_ok() } + pub fn get_monospace_ids_for_scripts( + &self, + scripts: impl Iterator, + ) -> Vec { + let mut ret = scripts + .filter_map(|script| self.per_script_monospace_font_ids.get(&script)) + .flat_map(|ids| ids.iter().copied()) + .collect::>(); + ret.sort(); + ret.dedup(); + ret + } + #[inline(always)] pub fn get_font_supported_codepoints_in_word( &mut self,