|
|
|
@ -1,28 +1,32 @@
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
|
|
use log::trace;
|
|
|
|
|
use lru::LruCache;
|
|
|
|
|
use skia_safe::{font::Edging, Data, Font, FontHinting, FontMgr, FontStyle, Typeface};
|
|
|
|
|
|
|
|
|
|
use crate::renderer::fonts::{font_options::FontOptions, swash_font::SwashFont};
|
|
|
|
|
use crate::renderer::fonts::swash_font::SwashFont;
|
|
|
|
|
|
|
|
|
|
static DEFAULT_FONT: &[u8] = include_bytes!("../../../assets/fonts/FiraCode-Regular.ttf");
|
|
|
|
|
static LAST_RESORT_FONT: &[u8] = include_bytes!("../../../assets/fonts/LastResort-Regular.ttf");
|
|
|
|
|
|
|
|
|
|
pub struct FontPair {
|
|
|
|
|
pub key: FontKey,
|
|
|
|
|
pub skia_font: Font,
|
|
|
|
|
pub swash_font: SwashFont,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FontPair {
|
|
|
|
|
fn new(mut skia_font: Font) -> Option<FontPair> {
|
|
|
|
|
fn new(key: FontKey, mut skia_font: Font) -> Option<FontPair> {
|
|
|
|
|
skia_font.set_subpixel(true);
|
|
|
|
|
skia_font.set_hinting(FontHinting::Full);
|
|
|
|
|
skia_font.set_edging(Edging::AntiAlias);
|
|
|
|
|
|
|
|
|
|
let (font_data, index) = skia_font.typeface().unwrap().to_font_data().unwrap();
|
|
|
|
|
let typeface = skia_font.typeface().unwrap();
|
|
|
|
|
let (font_data, index) = typeface.to_font_data().unwrap();
|
|
|
|
|
let swash_font = SwashFont::from_data(font_data, index)?;
|
|
|
|
|
|
|
|
|
|
Some(Self {
|
|
|
|
|
key,
|
|
|
|
|
skia_font,
|
|
|
|
|
swash_font,
|
|
|
|
|
})
|
|
|
|
@ -35,116 +39,43 @@ impl PartialEq for FontPair {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct FontLoader {
|
|
|
|
|
font_mgr: FontMgr,
|
|
|
|
|
cache: LruCache<FontKey, Arc<FontPair>>,
|
|
|
|
|
font_size: f32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
|
|
|
|
|
#[derive(Debug, Default, Hash, PartialEq, Eq, Clone)]
|
|
|
|
|
pub struct FontKey {
|
|
|
|
|
// TODO(smolck): Could make these private and add constructor method(s)?
|
|
|
|
|
// Would theoretically make things safer I guess, but not sure . . .
|
|
|
|
|
pub bold: bool,
|
|
|
|
|
pub italic: bool,
|
|
|
|
|
pub font_selection: FontSelection,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for FontKey {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
FontKey {
|
|
|
|
|
italic: false,
|
|
|
|
|
bold: false,
|
|
|
|
|
font_selection: FontSelection::Default,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<&FontOptions> for FontKey {
|
|
|
|
|
fn from(options: &FontOptions) -> FontKey {
|
|
|
|
|
FontKey {
|
|
|
|
|
italic: options.italic,
|
|
|
|
|
bold: options.bold,
|
|
|
|
|
font_selection: options.primary_font(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
|
|
|
|
|
pub enum FontSelection {
|
|
|
|
|
Name(String),
|
|
|
|
|
Character(char),
|
|
|
|
|
Default,
|
|
|
|
|
LastResort,
|
|
|
|
|
pub family_name: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<&str> for FontSelection {
|
|
|
|
|
fn from(string: &str) -> FontSelection {
|
|
|
|
|
let string = string.to_string();
|
|
|
|
|
FontSelection::Name(string)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<&String> for FontSelection {
|
|
|
|
|
fn from(string: &String) -> FontSelection {
|
|
|
|
|
let string = string.to_owned();
|
|
|
|
|
FontSelection::Name(string)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<String> for FontSelection {
|
|
|
|
|
fn from(string: String) -> FontSelection {
|
|
|
|
|
FontSelection::Name(string)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<char> for FontSelection {
|
|
|
|
|
fn from(character: char) -> FontSelection {
|
|
|
|
|
FontSelection::Character(character)
|
|
|
|
|
}
|
|
|
|
|
pub struct FontLoader {
|
|
|
|
|
font_mgr: FontMgr,
|
|
|
|
|
cache: LruCache<FontKey, Arc<FontPair>>,
|
|
|
|
|
font_size: f32,
|
|
|
|
|
last_resort: Option<Arc<FontPair>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FontLoader {
|
|
|
|
|
pub fn new(font_size: f32) -> FontLoader {
|
|
|
|
|
FontLoader {
|
|
|
|
|
font_mgr: FontMgr::new(),
|
|
|
|
|
cache: LruCache::new(10),
|
|
|
|
|
cache: LruCache::new(20),
|
|
|
|
|
font_size,
|
|
|
|
|
last_resort: None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn load(&mut self, font_key: FontKey) -> Option<FontPair> {
|
|
|
|
|
let font_style = match (font_key.bold, font_key.italic) {
|
|
|
|
|
(true, true) => FontStyle::bold_italic(),
|
|
|
|
|
(false, true) => FontStyle::italic(),
|
|
|
|
|
(true, false) => FontStyle::bold(),
|
|
|
|
|
(false, false) => FontStyle::normal(),
|
|
|
|
|
};
|
|
|
|
|
let font_style = font_style(font_key.bold, font_key.italic);
|
|
|
|
|
|
|
|
|
|
match font_key.font_selection {
|
|
|
|
|
FontSelection::Name(name) => {
|
|
|
|
|
let typeface = self.font_mgr.match_family_style(name, font_style)?;
|
|
|
|
|
FontPair::new(Font::from_typeface(typeface, self.font_size))
|
|
|
|
|
}
|
|
|
|
|
FontSelection::Character(character) => {
|
|
|
|
|
let typeface = self.font_mgr.match_family_style_character(
|
|
|
|
|
"",
|
|
|
|
|
font_style,
|
|
|
|
|
&[],
|
|
|
|
|
character as i32,
|
|
|
|
|
)?;
|
|
|
|
|
FontPair::new(Font::from_typeface(typeface, self.font_size))
|
|
|
|
|
}
|
|
|
|
|
FontSelection::Default => {
|
|
|
|
|
trace!("Loading font {:?}", font_key);
|
|
|
|
|
if let Some(family_name) = &font_key.family_name {
|
|
|
|
|
let typeface = self.font_mgr.match_family_style(family_name, font_style)?;
|
|
|
|
|
FontPair::new(font_key, Font::from_typeface(typeface, self.font_size))
|
|
|
|
|
} else {
|
|
|
|
|
let data = Data::new_copy(DEFAULT_FONT);
|
|
|
|
|
let typeface = Typeface::from_data(data, 0).unwrap();
|
|
|
|
|
FontPair::new(Font::from_typeface(typeface, self.font_size))
|
|
|
|
|
}
|
|
|
|
|
FontSelection::LastResort => {
|
|
|
|
|
let data = Data::new_copy(LAST_RESORT_FONT);
|
|
|
|
|
let typeface = Typeface::from_data(data, 0).unwrap();
|
|
|
|
|
FontPair::new(Font::from_typeface(typeface, self.font_size))
|
|
|
|
|
}
|
|
|
|
|
FontPair::new(font_key, Font::from_typeface(typeface, self.font_size))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -162,7 +93,68 @@ impl FontLoader {
|
|
|
|
|
Some(font_arc)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn load_font_for_character(
|
|
|
|
|
&mut self,
|
|
|
|
|
bold: bool,
|
|
|
|
|
italic: bool,
|
|
|
|
|
character: char,
|
|
|
|
|
) -> Option<Arc<FontPair>> {
|
|
|
|
|
let font_style = font_style(bold, italic);
|
|
|
|
|
let typeface =
|
|
|
|
|
self.font_mgr
|
|
|
|
|
.match_family_style_character("", font_style, &[], character as i32)?;
|
|
|
|
|
|
|
|
|
|
let font_key = FontKey {
|
|
|
|
|
bold,
|
|
|
|
|
italic,
|
|
|
|
|
family_name: Some(typeface.family_name()),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let font_pair = Arc::new(FontPair::new(
|
|
|
|
|
font_key.clone(),
|
|
|
|
|
Font::from_typeface(typeface, self.font_size),
|
|
|
|
|
)?);
|
|
|
|
|
|
|
|
|
|
self.cache.put(font_key, font_pair.clone());
|
|
|
|
|
|
|
|
|
|
Some(font_pair)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_or_load_last_resort(&mut self) -> Arc<FontPair> {
|
|
|
|
|
if let Some(last_resort) = self.last_resort.clone() {
|
|
|
|
|
last_resort
|
|
|
|
|
} else {
|
|
|
|
|
let font_key = FontKey::default();
|
|
|
|
|
let data = Data::new_copy(LAST_RESORT_FONT);
|
|
|
|
|
let typeface = Typeface::from_data(data, 0).unwrap();
|
|
|
|
|
|
|
|
|
|
let font_pair =
|
|
|
|
|
FontPair::new(font_key, Font::from_typeface(typeface, self.font_size)).unwrap();
|
|
|
|
|
let font_pair = Arc::new(font_pair);
|
|
|
|
|
|
|
|
|
|
self.last_resort = Some(font_pair.clone());
|
|
|
|
|
font_pair
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn loaded_fonts(&self) -> Vec<Arc<FontPair>> {
|
|
|
|
|
self.cache.iter().map(|(_, v)| v.clone()).collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn refresh(&mut self, font_pair: &FontPair) {
|
|
|
|
|
self.cache.get(&font_pair.key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn font_names(&self) -> Vec<String> {
|
|
|
|
|
self.font_mgr.family_names().collect()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn font_style(bold: bool, italic: bool) -> FontStyle {
|
|
|
|
|
match (bold, italic) {
|
|
|
|
|
(true, true) => FontStyle::bold_italic(),
|
|
|
|
|
(false, true) => FontStyle::italic(),
|
|
|
|
|
(true, false) => FontStyle::bold(),
|
|
|
|
|
(false, false) => FontStyle::normal(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|