1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-25 22:33:52 +03:00

fonts: refine fallback some more

* Log which codepoints we're about to perform fallback resolution for
* Rank the fallback fonts by decreasing amount of coverage
* If a fallback covers the desired codepoints, remove those codepoints
  from the set and reduce, so that we only add the minimal set of
  fallback fonts for the set of codepoints

You can see what triggered fallback processing using:

```
; WEZTERM_LOG=wezterm_font=trace wezterm -n 2> /tmp/font.txt
; grep 'fallback font' /tmp/font.txt
 2021-04-11T21:41:09.653Z TRACE wezterm_font > Looking for \u{d604}\u{c7ac}\u{be0c}\u{b79c}\u{ce58} in fallback fonts
 2021-04-11T21:41:12.132Z TRACE wezterm_font > Looking for \u{f4e9} in fallback fonts
```

refs: https://github.com/wez/wezterm/issues/559#issuecomment-817260512
This commit is contained in:
Wez Furlong 2021-04-11 14:38:06 -07:00
parent 0ab6382bb4
commit 903c7a47b6
2 changed files with 43 additions and 19 deletions

View File

@ -5,7 +5,6 @@ use crate::parser::{load_built_in_fonts, parse_and_collect_font_info, ParsedFont
use anyhow::Context;
use config::{Config, FontAttributes};
use rangeset::RangeSet;
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
pub struct FontDatabase {
@ -109,26 +108,12 @@ impl FontDatabase {
let covered = parsed
.coverage_intersection(&wanted_range)
.with_context(|| format!("coverage_interaction for {:?}", parsed))?;
let len = covered.len();
if len > 0 {
matches.push((len, parsed.clone()));
if !covered.is_empty() {
matches.push(parsed.clone());
}
}
// Add the handles in order of descending coverage; the idea being
// that if a font has a large coverage then it is probably a better
// candidate and more likely to result in other glyphs matching
// in future shaping calls.
matches.sort_by(|(a_len, a), (b_len, b)| {
let primary = a_len.cmp(&b_len).reverse();
if primary == Ordering::Equal {
a.cmp(b)
} else {
primary
}
});
Ok(matches.into_iter().map(|(_len, handle)| handle).collect())
Ok(matches)
}
pub fn resolve(&self, font_attr: &FontAttributes) -> Option<&ParsedFont> {

View File

@ -7,6 +7,7 @@ use anyhow::{Context, Error};
use config::{
configuration, ConfigHandle, FontRasterizerSelection, FontStretch, FontWeight, TextStyle,
};
use rangeset::RangeSet;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::rc::{Rc, Weak};
@ -214,6 +215,11 @@ impl FontConfigInner {
let fallback_str = no_glyphs.iter().collect::<String>();
let mut extra_handles = vec![];
log::trace!(
"Looking for {} in fallback fonts",
fallback_str.escape_unicode()
);
match font_dirs.locate_fallback_for_codepoints(&no_glyphs) {
Ok(ref mut handles) => extra_handles.append(handles),
Err(err) => log::error!(
@ -241,11 +247,44 @@ impl FontConfigInner {
),
}
let mut wanted = RangeSet::new();
for c in no_glyphs {
wanted.add(c as u32);
}
// Sort by ascending coverage
extra_handles.sort_by_cached_key(|p| {
p.coverage_intersection(&wanted)
.map(|r| r.len())
.unwrap_or(0)
});
// Re-arrange to descending coverage
extra_handles.reverse();
// iteratively reduce to just the fonts that we need
extra_handles.retain(|p| match p.coverage_intersection(&wanted) {
Ok(cov) if cov.is_empty() => false,
Ok(cov) => {
// Remove the matches from the set, so that we avoid
// picking up multiple fonts for the same glyphs
wanted = wanted.difference(&cov);
true
}
Err(_) => false,
});
if !extra_handles.is_empty() {
let mut pending = pending.lock().unwrap();
pending.append(&mut extra_handles);
completion();
} else {
}
if !wanted.is_empty() {
// There were some glyphs we couldn't resolve!
let fallback_str = wanted
.iter_values()
.map(|c| std::char::from_u32(c).unwrap_or(' '))
.collect::<String>();
if config.warn_about_missing_glyphs {
mux::connui::show_configuration_error_message(&format!(
"No fonts contain glyphs for these codepoints: {}.\n\