fix rendering when glyph can't be found and add last resort font

macos-click-through
Keith Simmons 3 years ago
parent 2160520720
commit 5c7deeb8b9

@ -4,7 +4,7 @@ use log::{trace, warn};
use lru::LruCache; use lru::LruCache;
use skia_safe::{TextBlob, TextBlobBuilder}; use skia_safe::{TextBlob, TextBlobBuilder};
use swash::shape::ShapeContext; use swash::shape::ShapeContext;
use swash::text::cluster::{CharCluster, Parser, Status, Token}; use swash::text::cluster::{Char, CharCluster, Parser, Status, Token};
use swash::text::Script; use swash::text::Script;
use swash::Metrics; use swash::Metrics;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
@ -46,7 +46,7 @@ impl CachingShaper {
.unwrap_or(FontKey::Default); .unwrap_or(FontKey::Default);
self.font_loader self.font_loader
.get_or_load(font_key) .get_or_load(&font_key)
.expect("Could not load font") .expect("Could not load font")
} }
@ -128,6 +128,8 @@ impl CachingShaper {
let mut results = Vec::new(); let mut results = Vec::new();
'cluster: while parser.next(&mut cluster) { 'cluster: while parser.next(&mut cluster) {
// TODO: Don't redo this work for every cluster. Save it some how
// Create font fallback list
let mut font_fallback_keys = Vec::new(); let mut font_fallback_keys = Vec::new();
// Add guifont fallback list // Add guifont fallback list
@ -145,9 +147,12 @@ impl CachingShaper {
// Add skia fallback // Add skia fallback
font_fallback_keys.push(cluster.chars()[0].ch.into()); font_fallback_keys.push(cluster.chars()[0].ch.into());
// Add last resort
font_fallback_keys.push(FontKey::LastResort);
let mut best = None; let mut best = None;
// Use the cluster.map function to select a viable font from the fallback list // Use the cluster.map function to select a viable font from the fallback list
for fallback_key in font_fallback_keys.into_iter() { for fallback_key in font_fallback_keys.iter() {
if let Some(font_pair) = self.font_loader.get_or_load(fallback_key) { if let Some(font_pair) = self.font_loader.get_or_load(fallback_key) {
let charmap = font_pair.swash_font.as_ref().charmap(); let charmap = font_pair.swash_font.as_ref().charmap();
match cluster.map(|ch| charmap.map(ch)) { match cluster.map(|ch| charmap.map(ch)) {
@ -161,19 +166,9 @@ impl CachingShaper {
} }
} }
// If we find a font with partial coverage of the cluster, select it
if let Some(best) = best { if let Some(best) = best {
// Last Resort covers all of the unicode space so we will always have a fallback
results.push((cluster.to_owned(), best.clone())); results.push((cluster.to_owned(), best.clone()));
continue 'cluster;
} else {
warn!("No valid font for {:?}", cluster.chars());
// No good match. Just render with the default font.
let default_font = self
.font_loader
.get_or_load(FontKey::Default)
.expect("Could not load default font");
results.push((cluster.to_owned(), default_font));
} }
} }
@ -188,7 +183,7 @@ impl CachingShaper {
current_group.push(cluster); current_group.push(cluster);
} else { } else {
grouped_results.push((current_group, current_font)); grouped_results.push((current_group, current_font));
current_group = Vec::new(); current_group = vec![cluster];
current_font_option = Some(font); current_font_option = Some(font);
} }
} else { } else {

@ -10,6 +10,7 @@ use super::swash_font::SwashFont;
pub struct Asset; pub struct Asset;
const DEFAULT_FONT: &str = "FiraCode-Regular.ttf"; const DEFAULT_FONT: &str = "FiraCode-Regular.ttf";
const LAST_RESORT_FONT: &str = "LastResort-Regular.ttf";
pub struct FontPair { pub struct FontPair {
pub skia_font: Font, pub skia_font: Font,
@ -46,9 +47,10 @@ pub struct FontLoader {
#[derive(Debug, Hash, PartialEq, Eq, Clone)] #[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub enum FontKey { pub enum FontKey {
Default,
Name(String), Name(String),
Character(char), Character(char),
Default,
LastResort,
} }
impl From<&str> for FontKey { impl From<&str> for FontKey {
@ -88,12 +90,6 @@ impl FontLoader {
fn load(&mut self, font_key: FontKey) -> Option<FontPair> { fn load(&mut self, font_key: FontKey) -> Option<FontPair> {
match font_key { match font_key {
FontKey::Default => {
let default_font_data = Asset::get(DEFAULT_FONT).unwrap();
let data = Data::new_copy(&default_font_data);
let typeface = Typeface::from_data(data, 0).unwrap();
FontPair::new(Font::from_typeface(typeface, self.font_size))
}
FontKey::Name(name) => { FontKey::Name(name) => {
let font_style = FontStyle::normal(); let font_style = FontStyle::normal();
let typeface = self.font_mgr.match_family_style(name, font_style)?; let typeface = self.font_mgr.match_family_style(name, font_style)?;
@ -109,11 +105,23 @@ impl FontLoader {
)?; )?;
FontPair::new(Font::from_typeface(typeface, self.font_size)) FontPair::new(Font::from_typeface(typeface, self.font_size))
} }
FontKey::Default => {
let default_font_data = Asset::get(DEFAULT_FONT).unwrap();
let data = Data::new_copy(&default_font_data);
let typeface = Typeface::from_data(data, 0).unwrap();
FontPair::new(Font::from_typeface(typeface, self.font_size))
}
FontKey::LastResort => {
let default_font_data = Asset::get(LAST_RESORT_FONT).unwrap();
let data = Data::new_copy(&default_font_data);
let typeface = Typeface::from_data(data, 0).unwrap();
FontPair::new(Font::from_typeface(typeface, self.font_size))
}
} }
} }
pub fn get_or_load(&mut self, font_key: FontKey) -> Option<Arc<FontPair>> { pub fn get_or_load(&mut self, font_key: &FontKey) -> Option<Arc<FontPair>> {
if let Some(cached) = self.cache.get(&font_key) { if let Some(cached) = self.cache.get(font_key) {
return Some(cached.clone()); return Some(cached.clone());
} }
@ -121,7 +129,7 @@ impl FontLoader {
let font_arc = Arc::new(loaded_font); let font_arc = Arc::new(loaded_font);
self.cache.put(font_key, font_arc.clone()); self.cache.put(font_key.clone(), font_arc.clone());
Some(font_arc) Some(font_arc)
} }

Loading…
Cancel
Save