Merge pull request #89 from geieredgar/font-system

Make `FontSystem` not self-referencing and update `fontdb` and `rustybuzz`
This commit is contained in:
Jeremy Soller 2023-03-02 18:15:34 -07:00 committed by GitHub
commit b6398a2d57
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 235 additions and 189 deletions

View file

@ -9,11 +9,10 @@ documentation = "https://docs.rs/cosmic-text/latest/cosmic_text/"
repository = "https://github.com/pop-os/cosmic-text" repository = "https://github.com/pop-os/cosmic-text"
[dependencies] [dependencies]
fontdb = { version = "0.10.0", default-features = false } fontdb = { version = "0.13.0", default-features = false }
libm = "0.2.6" libm = "0.2.6"
log = "0.4.17" log = "0.4.17"
ouroboros = "0.15.5" rustybuzz = { version = "0.7.0", default-features = false, features = ["libm"] }
rustybuzz = { version = "0.6.0", default-features = false, features = ["libm"] }
swash = { version = "0.1.6", optional = true } swash = { version = "0.1.6", optional = true }
syntect = { version = "5.0.0", optional = true } syntect = { version = "5.0.0", optional = true }
sys-locale = { version = "0.2.3", optional = true } sys-locale = { version = "0.2.3", optional = true }

View file

@ -112,6 +112,7 @@ allow = [
"Apache-2.0", "Apache-2.0",
"Unicode-DFS-2016", "Unicode-DFS-2016",
"BSD-2-Clause", "BSD-2-Clause",
"Zlib",
#"Apache-2.0 WITH LLVM-exception", #"Apache-2.0 WITH LLVM-exception",
] ]
# List of explicitly disallowed licenses # List of explicitly disallowed licenses
@ -163,8 +164,8 @@ exceptions = [
# and the crate will be checked normally, which may produce warnings or errors # and the crate will be checked normally, which may produce warnings or errors
# depending on the rest of your configuration # depending on the rest of your configuration
#license-files = [ #license-files = [
# Each entry is a crate relative path, and the (opaque) hash of its contents # Each entry is a crate relative path, and the (opaque) hash of its contents
#{ path = "LICENSE", hash = 0xbd0eed23 } #{ path = "LICENSE", hash = 0xbd0eed23 }
#] #]
[licenses.private] [licenses.private]

View file

@ -1,10 +1,12 @@
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
use crate::FontKey;
/// Key for building a glyph cache /// Key for building a glyph cache
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct CacheKey { pub struct CacheKey {
/// Font ID /// Font key
pub font_id: fontdb::ID, pub font_key: FontKey,
/// Glyph ID /// Glyph ID
pub glyph_id: u16, pub glyph_id: u16,
/// `f32` bits of font size /// `f32` bits of font size
@ -17,7 +19,7 @@ pub struct CacheKey {
impl CacheKey { impl CacheKey {
pub fn new( pub fn new(
font_id: fontdb::ID, font_key: FontKey,
glyph_id: u16, glyph_id: u16,
font_size: f32, font_size: f32,
pos: (f32, f32), pos: (f32, f32),
@ -26,7 +28,7 @@ impl CacheKey {
let (y, y_bin) = SubpixelBin::new(pos.1); let (y, y_bin) = SubpixelBin::new(pos.1);
( (
Self { Self {
font_id, font_key,
glyph_id, glyph_id,
font_size_bits: font_size.to_bits(), font_size_bits: font_size.to_bits(),
x_bin, x_bin,

View file

@ -1,11 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
use alloc::sync::Arc;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use alloc::vec::Vec; use alloc::vec::Vec;
use unicode_script::Script; use unicode_script::Script;
use crate::Font; use crate::{Font, FontKey};
use self::platform::*; use self::platform::*;
@ -26,7 +25,8 @@ mod platform;
mod platform; mod platform;
pub struct FontFallbackIter<'a> { pub struct FontFallbackIter<'a> {
fonts: &'a [Arc<Font<'a>>], db: &'a fontdb::Database,
font_keys: &'a [FontKey],
default_families: &'a [&'a str], default_families: &'a [&'a str],
default_i: usize, default_i: usize,
scripts: Vec<Script>, scripts: Vec<Script>,
@ -39,13 +39,15 @@ pub struct FontFallbackIter<'a> {
impl<'a> FontFallbackIter<'a> { impl<'a> FontFallbackIter<'a> {
pub fn new( pub fn new(
fonts: &'a [Arc<Font<'a>>], db: &'a fontdb::Database,
font_keys: &'a [FontKey],
default_families: &'a [&'a str], default_families: &'a [&'a str],
scripts: Vec<Script>, scripts: Vec<Script>,
locale: &'a str, locale: &'a str,
) -> Self { ) -> Self {
Self { Self {
fonts, db,
font_keys,
default_families, default_families,
default_i: 0, default_i: 0,
scripts, scripts,
@ -66,12 +68,13 @@ impl<'a> FontFallbackIter<'a> {
word word
); );
} else if self.other_i > 0 { } else if self.other_i > 0 {
let font = &self.fonts[self.other_i - 1]; let font_key = self.font_keys[self.other_i - 1];
let font = Font::from_key(self.db, font_key).expect("invalid font key");
log::debug!( log::debug!(
"Failed to find preset fallback for {:?} locale '{}', used '{}': '{}'", "Failed to find preset fallback for {:?} locale '{}', used '{}': '{}'",
self.scripts, self.scripts,
self.locale, self.locale,
font.info.family, font.name(),
word word
); );
} else if !self.scripts.is_empty() && self.common_i > 0 { } else if !self.scripts.is_empty() && self.common_i > 0 {
@ -88,14 +91,15 @@ impl<'a> FontFallbackIter<'a> {
} }
impl<'a> Iterator for FontFallbackIter<'a> { impl<'a> Iterator for FontFallbackIter<'a> {
type Item = &'a Arc<Font<'a>>; type Item = Font<'a>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
while self.default_i < self.default_families.len() { while self.default_i < self.default_families.len() {
let default_family = self.default_families[self.default_i]; let default_family = self.default_families[self.default_i];
self.default_i += 1; self.default_i += 1;
for font in self.fonts.iter() { for font_key in self.font_keys.iter().copied() {
if font.info.family == default_family { let font = Font::from_key(self.db, font_key).expect("invalid font key");
if font.contains_family(default_family) {
return Some(font); return Some(font);
} }
} }
@ -108,8 +112,9 @@ impl<'a> Iterator for FontFallbackIter<'a> {
while self.script_i.1 < script_families.len() { while self.script_i.1 < script_families.len() {
let script_family = script_families[self.script_i.1]; let script_family = script_families[self.script_i.1];
self.script_i.1 += 1; self.script_i.1 += 1;
for font in self.fonts.iter() { for font_key in self.font_keys.iter().copied() {
if font.info.family == script_family { let font = Font::from_key(self.db, font_key).expect("invalid font key");
if font.contains_family(script_family) {
return Some(font); return Some(font);
} }
} }
@ -129,8 +134,9 @@ impl<'a> Iterator for FontFallbackIter<'a> {
while self.common_i < common_families.len() { while self.common_i < common_families.len() {
let common_family = common_families[self.common_i]; let common_family = common_families[self.common_i];
self.common_i += 1; self.common_i += 1;
for font in self.fonts.iter() { for font_key in self.font_keys.iter().copied() {
if font.info.family == common_family { let font = Font::from_key(self.db, font_key).expect("invalid font key");
if font.contains_family(common_family) {
return Some(font); return Some(font);
} }
} }
@ -140,10 +146,14 @@ impl<'a> Iterator for FontFallbackIter<'a> {
//TODO: do we need to do this? //TODO: do we need to do this?
//TODO: do not evaluate fonts more than once! //TODO: do not evaluate fonts more than once!
let forbidden_families = forbidden_fallback(); let forbidden_families = forbidden_fallback();
while self.other_i < self.fonts.len() { while self.other_i < self.font_keys.len() {
let font = &self.fonts[self.other_i]; let font_key = self.font_keys[self.other_i];
let font = Font::from_key(self.db, font_key).expect("invalid font key");
self.other_i += 1; self.other_i += 1;
if !forbidden_families.contains(&font.info.family.as_str()) { if forbidden_families
.iter()
.all(|family| !font.contains_family(family))
{
return Some(font); return Some(font);
} }
} }

View file

@ -1,14 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use alloc::sync::Arc;
#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};
use crate::Font;
/// Fonts that match a pattern
pub struct FontMatches<'a> {
pub locale: &'a str,
pub default_family: String,
pub fonts: Vec<Arc<Font<'a>>>,
}

View file

@ -4,12 +4,18 @@ use core::ops::Deref;
pub(crate) mod fallback; pub(crate) mod fallback;
pub use self::matches::*;
mod matches;
pub use self::system::*; pub use self::system::*;
mod system; mod system;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(not(feature = "swash"), repr(transparent))]
/// Identifies a [`Font`] in a [`FontSystem`]
pub struct FontKey {
pub id: fontdb::ID,
#[cfg(feature = "swash")]
pub swash: (u32, swash::CacheKey),
}
/// A font /// A font
pub struct Font<'a> { pub struct Font<'a> {
pub info: &'a fontdb::FaceInfo, pub info: &'a fontdb::FaceInfo,
@ -44,6 +50,48 @@ impl<'a> Font<'a> {
}) })
} }
pub fn from_key(db: &'a fontdb::Database, key: FontKey) -> Option<Self> {
let info = db.face(key.id)?;
let data = match &info.source {
fontdb::Source::Binary(data) => data.deref().as_ref(),
#[cfg(feature = "std")]
fontdb::Source::File(path) => {
log::warn!("Unsupported fontdb Source::File('{}')", path.display());
return None;
}
#[cfg(feature = "std")]
fontdb::Source::SharedFile(_path, data) => data.deref().as_ref(),
};
Some(Self {
info,
data,
rustybuzz: rustybuzz::Face::from_slice(data, info.index)?,
#[cfg(feature = "swash")]
swash: key.swash,
})
}
pub fn key(&self) -> FontKey {
FontKey {
id: self.info.id,
#[cfg(feature = "swash")]
swash: self.swash,
}
}
pub fn name(&self) -> &str {
if let Some((name, _)) = self.info.families.first() {
name
} else {
&self.info.post_script_name
}
}
pub fn contains_family(&self, family: &str) -> bool {
self.info.families.iter().any(|(name, _)| name == family)
}
#[cfg(feature = "swash")] #[cfg(feature = "swash")]
pub fn as_swash(&self) -> swash::FontRef { pub fn as_swash(&self) -> swash::FontRef {
swash::FontRef { swash::FontRef {

View file

@ -6,7 +6,7 @@ use alloc::{
vec::Vec, vec::Vec,
}; };
use crate::{Attrs, Font, FontMatches}; use crate::{Attrs, Font, FontKey};
/// Access system fonts /// Access system fonts
pub struct FontSystem { pub struct FontSystem {
@ -45,33 +45,38 @@ impl FontSystem {
// Clippy false positive // Clippy false positive
#[allow(clippy::needless_lifetimes)] #[allow(clippy::needless_lifetimes)]
pub fn get_font<'a>(&'a self, id: fontdb::ID) -> Option<Arc<Font<'a>>> { pub fn get_font<'a>(&'a self, key: FontKey) -> Option<Font<'a>> {
let face = self.db.face(id)?; match Font::from_key(&self.db, key) {
match Font::new(face) { Some(font) => Some(font),
Some(font) => Some(Arc::new(font)),
None => { None => {
let face = self.db.face(key.id)?;
log::warn!("failed to load font '{}'", face.post_script_name); log::warn!("failed to load font '{}'", face.post_script_name);
None None
} }
} }
} }
pub fn get_font_matches<'a>(&'a self, attrs: Attrs) -> Arc<FontMatches<'a>> { pub fn get_font_key(&self, id: fontdb::ID) -> Option<FontKey> {
let mut fonts = Vec::new(); Some(Font::new(self.db.face(id)?)?.key())
}
pub fn get_font_matches(&self, attrs: Attrs) -> Arc<Vec<FontKey>> {
let mut font_keys = Vec::new();
for face in self.db.faces() { for face in self.db.faces() {
if !attrs.matches(face) { if !attrs.matches(face) {
continue; continue;
} }
if let Some(font) = self.get_font(face.id) { let font_key = match self.get_font_key(face.id) {
fonts.push(font); Some(font_key) => font_key,
None => continue,
};
if self.get_font(font_key).is_some() {
font_keys.push(font_key);
} }
} }
Arc::new(FontMatches { Arc::new(font_keys)
locale: &self.locale,
default_family: self.db.family_name(&attrs.family).to_string(),
fonts,
})
} }
} }

View file

@ -6,7 +6,7 @@ use alloc::{
vec::Vec, vec::Vec,
}; };
use crate::{Attrs, Font, FontMatches}; use crate::{Attrs, Font, FontKey};
/// Access system fonts /// Access system fonts
pub struct FontSystem { pub struct FontSystem {
@ -44,8 +44,7 @@ impl FontSystem {
let now = std::time::Instant::now(); let now = std::time::Instant::now();
//TODO only do this on demand! //TODO only do this on demand!
for i in 0..db.faces().len() { for id in db.faces().map(|face| face.id).collect::<Vec<_>>() {
let id = db.faces()[i].id;
unsafe { unsafe {
db.make_shared_face_data(id); db.make_shared_face_data(id);
} }
@ -71,33 +70,38 @@ impl FontSystem {
// Clippy false positive // Clippy false positive
#[allow(clippy::needless_lifetimes)] #[allow(clippy::needless_lifetimes)]
pub fn get_font<'a>(&'a self, id: fontdb::ID) -> Option<Arc<Font<'a>>> { pub fn get_font<'a>(&'a self, key: FontKey) -> Option<Font<'a>> {
let face = self.db.face(id)?; match Font::from_key(&self.db, key) {
match Font::new(face) { Some(font) => Some(font),
Some(font) => Some(Arc::new(font)),
None => { None => {
let face = self.db.face(key.id)?;
log::warn!("failed to load font '{}'", face.post_script_name); log::warn!("failed to load font '{}'", face.post_script_name);
None None
} }
} }
} }
pub fn get_font_matches<'a>(&'a self, attrs: Attrs) -> Arc<FontMatches<'a>> { pub fn get_font_key(&self, id: fontdb::ID) -> Option<FontKey> {
let mut fonts = Vec::new(); Some(Font::new(self.db.face(id)?)?.key())
}
pub fn get_font_matches<'a>(&'a self, attrs: Attrs) -> Arc<Vec<FontKey>> {
let mut font_keys = Vec::new();
for face in self.db.faces() { for face in self.db.faces() {
if !attrs.matches(face) { if !attrs.matches(face) {
continue; continue;
} }
if let Some(font) = self.get_font(face.id) { let font_key = match self.get_font_key(face.id) {
fonts.push(font); Some(font_key) => font_key,
None => continue,
};
if self.get_font(font_key).is_some() {
font_keys.push(font_key);
} }
} }
Arc::new(FontMatches { Arc::new(font_keys)
locale: &self.locale,
default_family: self.db.family_name(&attrs.family).to_string(),
fonts,
})
} }
} }

View file

@ -1,26 +1,22 @@
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
#[cfg(feature = "swash")]
use std::collections::hash_map::Entry;
use std::{ use std::{
collections::HashMap, collections::HashMap,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use crate::{Attrs, AttrsOwned, Font, FontMatches}; use crate::{Attrs, AttrsOwned, Font, FontKey};
#[ouroboros::self_referencing]
struct FontSystemInner {
locale: String,
db: fontdb::Database,
#[borrows(db)]
#[not_covariant]
font_cache: Mutex<HashMap<fontdb::ID, Option<Arc<Font<'this>>>>>,
#[borrows(locale, db)]
#[not_covariant]
font_matches_cache: Mutex<HashMap<AttrsOwned, Arc<FontMatches<'this>>>>,
}
/// Access system fonts /// Access system fonts
pub struct FontSystem(FontSystemInner); pub struct FontSystem {
locale: String,
db: fontdb::Database,
font_matches_cache: Mutex<HashMap<AttrsOwned, Arc<Vec<FontKey>>>>,
#[cfg(feature = "swash")]
font_key_cache: Mutex<HashMap<fontdb::ID, Option<FontKey>>>,
}
impl FontSystem { impl FontSystem {
/// Create a new [`FontSystem`], that allows access to any installed system fonts /// Create a new [`FontSystem`], that allows access to any installed system fonts
@ -75,8 +71,7 @@ impl FontSystem {
let now = std::time::Instant::now(); let now = std::time::Instant::now();
//TODO only do this on demand! //TODO only do this on demand!
for i in 0..db.faces().len() { for id in db.faces().map(|face| face.id).collect::<Vec<_>>() {
let id = db.faces()[i].id;
unsafe { unsafe {
db.make_shared_face_data(id); db.make_shared_face_data(id);
} }
@ -90,97 +85,92 @@ impl FontSystem {
); );
} }
Self( Self {
FontSystemInnerBuilder { locale,
locale, db,
db, font_matches_cache: Mutex::new(HashMap::new()),
font_cache_builder: |_| Mutex::new(HashMap::new()), #[cfg(feature = "swash")]
font_matches_cache_builder: |_, _| Mutex::new(HashMap::new()), font_key_cache: Mutex::new(HashMap::new()),
} }
.build(),
)
} }
pub fn locale(&self) -> &str { pub fn locale(&self) -> &str {
self.0.borrow_locale() &self.locale
} }
pub fn db(&self) -> &fontdb::Database { pub fn db(&self) -> &fontdb::Database {
self.0.borrow_db() &self.db
} }
pub fn into_locale_and_db(self) -> (String, fontdb::Database) { pub fn into_locale_and_db(self) -> (String, fontdb::Database) {
let heads = self.0.into_heads(); (self.locale, self.db)
(heads.locale, heads.db)
} }
// Clippy false positive // Clippy false positive
#[allow(clippy::needless_lifetimes)] #[allow(clippy::needless_lifetimes)]
pub fn get_font<'a>(&'a self, id: fontdb::ID) -> Option<Arc<Font<'a>>> { pub fn get_font<'a>(&'a self, key: FontKey) -> Option<Font<'a>> {
self.0.with(|fields| get_font(&fields, id)) match Font::from_key(&self.db, key) {
} Some(font) => Some(font),
None => {
pub fn get_font_matches<'a>(&'a self, attrs: Attrs) -> Arc<FontMatches<'a>> { let face = self.db.face(key.id)?;
self.0.with(|fields| { log::warn!("failed to load font '{}'", face.post_script_name);
let mut font_matches_cache = fields None
.font_matches_cache
.lock()
.expect("failed to lock font matches cache");
//TODO: do not create AttrsOwned unless entry does not already exist
font_matches_cache
.entry(AttrsOwned::new(attrs))
.or_insert_with(|| {
#[cfg(not(target_arch = "wasm32"))]
let now = std::time::Instant::now();
let mut fonts = Vec::new();
for face in fields.db.faces() {
if !attrs.matches(face) {
continue;
}
if let Some(font) = get_font(&fields, face.id) {
fonts.push(font);
}
}
let font_matches = Arc::new(FontMatches {
locale: fields.locale,
default_family: fields.db.family_name(&attrs.family).to_string(),
fonts,
});
#[cfg(not(target_arch = "wasm32"))]
{
let elapsed = now.elapsed();
log::debug!("font matches for {:?} in {:?}", attrs, elapsed);
}
font_matches
})
.clone()
})
}
}
fn get_font<'b>(
fields: &ouroboros_impl_font_system_inner::BorrowedFields<'_, 'b>,
id: fontdb::ID,
) -> Option<Arc<Font<'b>>> {
fields
.font_cache
.lock()
.expect("failed to lock font cache")
.entry(id)
.or_insert_with(|| {
let face = fields.db.face(id)?;
match Font::new(face) {
Some(font) => Some(Arc::new(font)),
None => {
log::warn!("failed to load font '{}'", face.post_script_name);
None
}
} }
}) }
.clone() }
#[cfg(feature = "swash")]
pub fn get_font_key(&self, id: fontdb::ID) -> Option<FontKey> {
let mut font_key_cache = self
.font_key_cache
.lock()
.expect("failed to lock font matches cache");
match font_key_cache.entry(id) {
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
let key = self.db.face(id).and_then(Font::new).as_ref().map(Font::key);
entry.insert(key);
key
}
}
}
#[cfg(not(feature = "swash"))]
pub fn get_font_key(&self, id: fontdb::ID) -> Option<FontKey> {
Some(Font::new(self.db.face(id)?)?.key())
}
pub fn get_font_matches(&self, attrs: Attrs) -> Arc<Vec<FontKey>> {
let mut font_matches_cache = self
.font_matches_cache
.lock()
.expect("failed to lock font matches cache");
//TODO: do not create AttrsOwned unless entry does not already exist
font_matches_cache
.entry(AttrsOwned::new(attrs))
.or_insert_with(|| {
#[cfg(not(target_arch = "wasm32"))]
let now = std::time::Instant::now();
let mut font_keys = Vec::new();
for face in self.db.faces() {
if !attrs.matches(face) {
continue;
}
if let Some(key) = self.get_font_key(face.id) {
font_keys.push(key);
}
}
#[cfg(not(target_arch = "wasm32"))]
{
let elapsed = now.elapsed();
log::debug!("font matches for {:?} in {:?}", attrs, elapsed);
}
Arc::new(font_keys)
})
.clone()
}
} }

View file

@ -9,7 +9,9 @@ use unicode_script::{Script, UnicodeScript};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use crate::fallback::FontFallbackIter; use crate::fallback::FontFallbackIter;
use crate::{Align, AttrsList, CacheKey, Color, Font, FontSystem, LayoutGlyph, LayoutLine, Wrap}; use crate::{
Align, AttrsList, CacheKey, Color, Font, FontKey, FontSystem, LayoutGlyph, LayoutLine, Wrap,
};
fn shape_fallback( fn shape_fallback(
font: &Font, font: &Font,
@ -62,7 +64,7 @@ fn shape_fallback(
y_advance, y_advance,
x_offset, x_offset,
y_offset, y_offset,
font_id: font.info.id, font_key: font.key(),
glyph_id: info.glyph_id.try_into().expect("failed to cast glyph ID"), glyph_id: info.glyph_id.try_into().expect("failed to cast glyph ID"),
//TODO: color should not be related to shaping //TODO: color should not be related to shaping
color_opt: attrs.color_opt, color_opt: attrs.color_opt,
@ -125,22 +127,21 @@ fn shape_run(
let font_matches = font_system.get_font_matches(attrs); let font_matches = font_system.get_font_matches(attrs);
let default_families = [font_matches.default_family.as_str()]; let db = font_system.db();
let default_families = [db.family_name(&attrs.family)];
let mut font_iter = FontFallbackIter::new( let mut font_iter = FontFallbackIter::new(
&font_matches.fonts, db,
&font_matches,
&default_families, &default_families,
scripts, scripts,
font_matches.locale, font_system.locale(),
); );
let (mut glyphs, mut missing) = shape_fallback( let font = font_iter.next().expect("no default font found");
font_iter.next().expect("no default font found"),
line, let (mut glyphs, mut missing) =
attrs_list, shape_fallback(&font, line, attrs_list, start_run, end_run, span_rtl);
start_run,
end_run,
span_rtl,
);
//TODO: improve performance! //TODO: improve performance!
while !missing.is_empty() { while !missing.is_empty() {
@ -149,9 +150,9 @@ fn shape_run(
None => break, None => break,
}; };
log::trace!("Evaluating fallback with font '{}'", font.info.family); log::trace!("Evaluating fallback with font '{}'", font.name());
let (mut fb_glyphs, fb_missing) = let (mut fb_glyphs, fb_missing) =
shape_fallback(font, line, attrs_list, start_run, end_run, span_rtl); shape_fallback(&font, line, attrs_list, start_run, end_run, span_rtl);
// Insert all matching glyphs // Insert all matching glyphs
let mut fb_i = 0; let mut fb_i = 0;
@ -228,7 +229,7 @@ pub struct ShapeGlyph {
pub y_advance: f32, pub y_advance: f32,
pub x_offset: f32, pub x_offset: f32,
pub y_offset: f32, pub y_offset: f32,
pub font_id: fontdb::ID, pub font_key: FontKey,
pub glyph_id: u16, pub glyph_id: u16,
pub color_opt: Option<Color>, pub color_opt: Option<Color>,
pub metadata: usize, pub metadata: usize,
@ -247,7 +248,7 @@ impl ShapeGlyph {
let y_offset = font_size * self.y_offset; let y_offset = font_size * self.y_offset;
let (cache_key, x_int, y_int) = CacheKey::new( let (cache_key, x_int, y_int) = CacheKey::new(
self.font_id, self.font_key,
self.glyph_id, self.glyph_id,
font_size, font_size,
(x + x_offset, y - y_offset), (x + x_offset, y - y_offset),

View file

@ -20,10 +20,10 @@ fn swash_image(
context: &mut ScaleContext, context: &mut ScaleContext,
cache_key: CacheKey, cache_key: CacheKey,
) -> Option<SwashImage> { ) -> Option<SwashImage> {
let font = match font_system.get_font(cache_key.font_id) { let font = match font_system.get_font(cache_key.font_key) {
Some(some) => some, Some(some) => some,
None => { None => {
log::warn!("did not find font {:?}", cache_key.font_id); log::warn!("did not find font {:?}", cache_key.font_key.id);
return None; return None;
} }
}; };
@ -63,10 +63,10 @@ fn swash_outline_commands(
) -> Option<Vec<swash::zeno::Command>> { ) -> Option<Vec<swash::zeno::Command>> {
use swash::zeno::PathData as _; use swash::zeno::PathData as _;
let font = match font_system.get_font(cache_key.font_id) { let font = match font_system.get_font(cache_key.font_key) {
Some(some) => some, Some(some) => some,
None => { None => {
log::warn!("did not find font {:?}", cache_key.font_id); log::warn!("did not find font {:?}", cache_key.font_key.id);
return None; return None;
} }
}; };