diff --git a/src/font/fallback/mod.rs b/src/font/fallback/mod.rs index d854624b0c..528cdf816d 100644 --- a/src/font/fallback/mod.rs +++ b/src/font/fallback/mod.rs @@ -38,7 +38,7 @@ use log::warn as missing_warn; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] struct MonospaceFallbackInfo { weight_offset: Option, - script_non_matches: Option, + codepoint_non_matches: Option, id: fontdb::ID, } @@ -49,6 +49,7 @@ pub struct FontFallbackIter<'a> { monospace_fallbacks: BTreeSet, default_i: usize, scripts: &'a [Script], + word: &'a str, script_i: (usize, usize), common_i: usize, other_i: usize, @@ -61,6 +62,7 @@ impl<'a> FontFallbackIter<'a> { font_match_keys: &'a [FontMatchKey], default_families: &'a [&'a Family<'a>], scripts: &'a [Script], + word: &'a str, ) -> Self { Self { font_system, @@ -69,6 +71,7 @@ impl<'a> FontFallbackIter<'a> { monospace_fallbacks: BTreeSet::new(), default_i: 0, scripts, + word, script_i: (0, 0), common_i: 0, other_i: 0, @@ -161,44 +164,34 @@ impl<'a> Iterator for FontFallbackIter<'a> { // Default font let fallback_info = MonospaceFallbackInfo { weight_offset: None, - script_non_matches: None, + codepoint_non_matches: None, id: m_key.id, }; - assert_eq!(self.monospace_fallbacks.insert(fallback_info), true); + assert!(self.monospace_fallbacks.insert(fallback_info)); } } } // Set a monospace fallback if Monospace family is not found if is_mono { - let script_tags = 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() - }) - .collect::>(); - if let Some(face_info) = self.font_system.db().face(m_key.id) { // Don't use emoji fonts as Monospace if face_info.monospaced && !face_info.post_script_name.contains("Emoji") { if let Some(font) = self.font_system.get_font(m_key.id) { - let script_non_matches = self.scripts.len() - - script_tags - .iter() - .filter(|&&script_tag| { - font.scripts() - .iter() - .any(|&tag_bytes| tag_bytes == script_tag) + let codepoint_non_matches = self.word.chars().count() + - self + .word + .chars() + .filter(|ch| { + font.unicode_codepoints().contains(&u32::from(*ch)) }) .count(); let fallback_info = MonospaceFallbackInfo { weight_offset: m_key.weight_offset, - script_non_matches: Some(script_non_matches), + codepoint_non_matches: Some(codepoint_non_matches), id: m_key.id, }; - assert_eq!(self.monospace_fallbacks.insert(fallback_info), true); + assert!(self.monospace_fallbacks.insert(fallback_info)); } } } diff --git a/src/font/mod.rs b/src/font/mod.rs index 12a011d193..25dbf9b7b7 100644 --- a/src/font/mod.rs +++ b/src/font/mod.rs @@ -30,7 +30,7 @@ pub struct Font { data: Arc + Send + Sync>, id: fontdb::ID, monospace_em_width: Option, - scripts: Vec<[u8; 4]>, + unicode_codepoints: Vec, } impl fmt::Debug for Font { @@ -50,8 +50,8 @@ impl Font { self.monospace_em_width } - pub fn scripts(&self) -> &[[u8; 4]] { - &self.scripts + pub fn unicode_codepoints(&self) -> &[u32] { + &self.unicode_codepoints } pub fn data(&self) -> &[u8] { @@ -77,7 +77,7 @@ impl Font { pub fn new(db: &fontdb::Database, id: fontdb::ID) -> Option { let info = db.face(id)?; - let (monospace_em_width, scripts) = { + let (monospace_em_width, 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,16 +93,25 @@ impl Font { None?; } - let scripts = face - .tables() - .gpos + let mut unicode_codepoints = Vec::new(); + + face.tables() + .cmap? + .subtables .into_iter() - .chain(face.tables().gsub) - .map(|table| table.scripts) - .flatten() - .map(|script| script.tag.to_bytes()) - .collect(); - Some((monospace_em_width, scripts)) + .filter(|subtable| subtable.is_unicode()) + .for_each(|subtable| { + unicode_codepoints.reserve(1024); + subtable.codepoints(|code_point| { + if subtable.glyph_index(code_point).is_some() { + unicode_codepoints.push(code_point); + } + }); + }); + + unicode_codepoints.shrink_to_fit(); + + Some((monospace_em_width, unicode_codepoints)) })? }?; @@ -120,7 +129,7 @@ impl Font { Some(Self { id: info.id, monospace_em_width, - scripts, + unicode_codepoints, #[cfg(feature = "swash")] swash: { let swash = swash::FontRef::from_index((*data).as_ref(), info.index as usize)?; diff --git a/src/shape.rs b/src/shape.rs index 581bf6d057..a7ac4f15dc 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -219,7 +219,13 @@ fn shape_run( let fonts = font_system.get_font_matches(attrs); let default_families = [&attrs.family]; - let mut font_iter = FontFallbackIter::new(font_system, &fonts, &default_families, &scripts); + let mut font_iter = FontFallbackIter::new( + font_system, + &fonts, + &default_families, + &scripts, + &line[start_run..end_run], + ); let font = font_iter.next().expect("no default font found"); @@ -341,7 +347,7 @@ fn shape_skip( let fonts = font_system.get_font_matches(attrs); let default_families = [&attrs.family]; - let mut font_iter = FontFallbackIter::new(font_system, &fonts, &default_families, &[]); + let mut font_iter = FontFallbackIter::new(font_system, &fonts, &default_families, &[], ""); let font = font_iter.next().expect("no default font found"); let font_id = font.id();