diff --git a/wezterm-font/src/db.rs b/wezterm-font/src/db.rs index afca3efed..ef4c6eb5d 100644 --- a/wezterm-font/src/db.rs +++ b/wezterm-font/src/db.rs @@ -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> { diff --git a/wezterm-font/src/lib.rs b/wezterm-font/src/lib.rs index 96fe3d20a..9524fd310 100644 --- a/wezterm-font/src/lib.rs +++ b/wezterm-font/src/lib.rs @@ -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::(); 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::(); + if config.warn_about_missing_glyphs { mux::connui::show_configuration_error_message(&format!( "No fonts contain glyphs for these codepoints: {}.\n\