Use supported codepoints instead of scripts in Monospace fallback

This should/could improve fallback order.

 This could also probably be utilized for non-Monospace fallback too.
 But I didn't want to touch that code to avoid accidentally breaking
 anything.

Signed-off-by: Mohammad AlSaleh <CE.Mohammad.AlSaleh@gmail.com>
This commit is contained in:
Mohammad AlSaleh 2024-01-18 23:00:54 +03:00 committed by Jeremy Soller
parent 845a66ceff
commit db1530c4ec
3 changed files with 45 additions and 37 deletions

View file

@ -38,7 +38,7 @@ use log::warn as missing_warn;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct MonospaceFallbackInfo { struct MonospaceFallbackInfo {
weight_offset: Option<u16>, weight_offset: Option<u16>,
script_non_matches: Option<usize>, codepoint_non_matches: Option<usize>,
id: fontdb::ID, id: fontdb::ID,
} }
@ -49,6 +49,7 @@ pub struct FontFallbackIter<'a> {
monospace_fallbacks: BTreeSet<MonospaceFallbackInfo>, monospace_fallbacks: BTreeSet<MonospaceFallbackInfo>,
default_i: usize, default_i: usize,
scripts: &'a [Script], scripts: &'a [Script],
word: &'a str,
script_i: (usize, usize), script_i: (usize, usize),
common_i: usize, common_i: usize,
other_i: usize, other_i: usize,
@ -61,6 +62,7 @@ impl<'a> FontFallbackIter<'a> {
font_match_keys: &'a [FontMatchKey], font_match_keys: &'a [FontMatchKey],
default_families: &'a [&'a Family<'a>], default_families: &'a [&'a Family<'a>],
scripts: &'a [Script], scripts: &'a [Script],
word: &'a str,
) -> Self { ) -> Self {
Self { Self {
font_system, font_system,
@ -69,6 +71,7 @@ impl<'a> FontFallbackIter<'a> {
monospace_fallbacks: BTreeSet::new(), monospace_fallbacks: BTreeSet::new(),
default_i: 0, default_i: 0,
scripts, scripts,
word,
script_i: (0, 0), script_i: (0, 0),
common_i: 0, common_i: 0,
other_i: 0, other_i: 0,
@ -161,44 +164,34 @@ impl<'a> Iterator for FontFallbackIter<'a> {
// Default font // Default font
let fallback_info = MonospaceFallbackInfo { let fallback_info = MonospaceFallbackInfo {
weight_offset: None, weight_offset: None,
script_non_matches: None, codepoint_non_matches: None,
id: m_key.id, 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 // Set a monospace fallback if Monospace family is not found
if is_mono { 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::<Vec<_>>();
if let Some(face_info) = self.font_system.db().face(m_key.id) { if let Some(face_info) = self.font_system.db().face(m_key.id) {
// Don't use emoji fonts as Monospace // Don't use emoji fonts as Monospace
if face_info.monospaced && !face_info.post_script_name.contains("Emoji") { if face_info.monospaced && !face_info.post_script_name.contains("Emoji") {
if let Some(font) = self.font_system.get_font(m_key.id) { if let Some(font) = self.font_system.get_font(m_key.id) {
let script_non_matches = self.scripts.len() let codepoint_non_matches = self.word.chars().count()
- script_tags - self
.iter() .word
.filter(|&&script_tag| { .chars()
font.scripts() .filter(|ch| {
.iter() font.unicode_codepoints().contains(&u32::from(*ch))
.any(|&tag_bytes| tag_bytes == script_tag)
}) })
.count(); .count();
let fallback_info = MonospaceFallbackInfo { let fallback_info = MonospaceFallbackInfo {
weight_offset: m_key.weight_offset, weight_offset: m_key.weight_offset,
script_non_matches: Some(script_non_matches), codepoint_non_matches: Some(codepoint_non_matches),
id: m_key.id, id: m_key.id,
}; };
assert_eq!(self.monospace_fallbacks.insert(fallback_info), true); assert!(self.monospace_fallbacks.insert(fallback_info));
} }
} }
} }

View file

@ -30,7 +30,7 @@ pub struct Font {
data: Arc<dyn AsRef<[u8]> + Send + Sync>, data: Arc<dyn AsRef<[u8]> + Send + Sync>,
id: fontdb::ID, id: fontdb::ID,
monospace_em_width: Option<f32>, monospace_em_width: Option<f32>,
scripts: Vec<[u8; 4]>, unicode_codepoints: Vec<u32>,
} }
impl fmt::Debug for Font { impl fmt::Debug for Font {
@ -50,8 +50,8 @@ impl Font {
self.monospace_em_width self.monospace_em_width
} }
pub fn scripts(&self) -> &[[u8; 4]] { pub fn unicode_codepoints(&self) -> &[u32] {
&self.scripts &self.unicode_codepoints
} }
pub fn data(&self) -> &[u8] { pub fn data(&self) -> &[u8] {
@ -77,7 +77,7 @@ impl Font {
pub fn new(db: &fontdb::Database, id: fontdb::ID) -> Option<Self> { pub fn new(db: &fontdb::Database, id: fontdb::ID) -> Option<Self> {
let info = db.face(id)?; 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| { db.with_face_data(id, |font_data, face_index| {
let face = ttf_parser::Face::parse(font_data, face_index).ok()?; let face = ttf_parser::Face::parse(font_data, face_index).ok()?;
let monospace_em_width = info let monospace_em_width = info
@ -93,16 +93,25 @@ impl Font {
None?; None?;
} }
let scripts = face let mut unicode_codepoints = Vec::new();
.tables()
.gpos face.tables()
.cmap?
.subtables
.into_iter() .into_iter()
.chain(face.tables().gsub) .filter(|subtable| subtable.is_unicode())
.map(|table| table.scripts) .for_each(|subtable| {
.flatten() unicode_codepoints.reserve(1024);
.map(|script| script.tag.to_bytes()) subtable.codepoints(|code_point| {
.collect(); if subtable.glyph_index(code_point).is_some() {
Some((monospace_em_width, scripts)) unicode_codepoints.push(code_point);
}
});
});
unicode_codepoints.shrink_to_fit();
Some((monospace_em_width, unicode_codepoints))
})? })?
}?; }?;
@ -120,7 +129,7 @@ impl Font {
Some(Self { Some(Self {
id: info.id, id: info.id,
monospace_em_width, monospace_em_width,
scripts, unicode_codepoints,
#[cfg(feature = "swash")] #[cfg(feature = "swash")]
swash: { swash: {
let swash = swash::FontRef::from_index((*data).as_ref(), info.index as usize)?; let swash = swash::FontRef::from_index((*data).as_ref(), info.index as usize)?;

View file

@ -219,7 +219,13 @@ fn shape_run(
let fonts = font_system.get_font_matches(attrs); let fonts = font_system.get_font_matches(attrs);
let default_families = [&attrs.family]; 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"); 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 fonts = font_system.get_font_matches(attrs);
let default_families = [&attrs.family]; 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 = font_iter.next().expect("no default font found");
let font_id = font.id(); let font_id = font.id();