Call get_font lazily
This commit is contained in:
parent
f86acd325c
commit
d297a6a48a
5 changed files with 97 additions and 99 deletions
|
|
@ -3,9 +3,10 @@
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
use fontdb::Family;
|
||||||
use unicode_script::Script;
|
use unicode_script::Script;
|
||||||
|
|
||||||
use crate::Font;
|
use crate::{Font, FontSystem};
|
||||||
|
|
||||||
use self::platform::*;
|
use self::platform::*;
|
||||||
|
|
||||||
|
|
@ -26,11 +27,11 @@ mod platform;
|
||||||
mod platform;
|
mod platform;
|
||||||
|
|
||||||
pub struct FontFallbackIter<'a> {
|
pub struct FontFallbackIter<'a> {
|
||||||
fonts: &'a [Arc<Font>],
|
font_system: &'a mut FontSystem,
|
||||||
default_families: &'a [&'a str],
|
font_ids: &'a [fontdb::ID],
|
||||||
|
default_families: &'a [&'a Family<'a>],
|
||||||
default_i: usize,
|
default_i: usize,
|
||||||
scripts: Vec<Script>,
|
scripts: Vec<Script>,
|
||||||
locale: &'a str,
|
|
||||||
script_i: (usize, usize),
|
script_i: (usize, usize),
|
||||||
common_i: usize,
|
common_i: usize,
|
||||||
other_i: usize,
|
other_i: usize,
|
||||||
|
|
@ -39,17 +40,17 @@ pub struct FontFallbackIter<'a> {
|
||||||
|
|
||||||
impl<'a> FontFallbackIter<'a> {
|
impl<'a> FontFallbackIter<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
fonts: &'a [Arc<Font>],
|
font_system: &'a mut FontSystem,
|
||||||
default_families: &'a [&'a str],
|
font_ids: &'a [fontdb::ID],
|
||||||
|
default_families: &'a [&'a Family<'a>],
|
||||||
scripts: Vec<Script>,
|
scripts: Vec<Script>,
|
||||||
locale: &'a str,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
fonts,
|
font_system,
|
||||||
|
font_ids,
|
||||||
default_families,
|
default_families,
|
||||||
default_i: 0,
|
default_i: 0,
|
||||||
scripts,
|
scripts,
|
||||||
locale,
|
|
||||||
script_i: (0, 0),
|
script_i: (0, 0),
|
||||||
common_i: 0,
|
common_i: 0,
|
||||||
other_i: 0,
|
other_i: 0,
|
||||||
|
|
@ -57,21 +58,20 @@ impl<'a> FontFallbackIter<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_missing(&self, word: &str) {
|
pub fn check_missing(&mut self, word: &str) {
|
||||||
if self.end {
|
if self.end {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Failed to find any fallback for {:?} locale '{}': '{}'",
|
"Failed to find any fallback for {:?} locale '{}': '{}'",
|
||||||
self.scripts,
|
self.scripts,
|
||||||
self.locale,
|
self.font_system.locale(),
|
||||||
word
|
word
|
||||||
);
|
);
|
||||||
} else if self.other_i > 0 {
|
} else if self.other_i > 0 {
|
||||||
let font = &self.fonts[self.other_i - 1];
|
|
||||||
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.font_system.locale(),
|
||||||
font.name(),
|
self.face_name(self.font_ids[self.other_i - 1]),
|
||||||
word
|
word
|
||||||
);
|
);
|
||||||
} else if !self.scripts.is_empty() && self.common_i > 0 {
|
} else if !self.scripts.is_empty() && self.common_i > 0 {
|
||||||
|
|
@ -79,24 +79,48 @@ impl<'a> FontFallbackIter<'a> {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Failed to find script fallback for {:?} locale '{}', used '{}': '{}'",
|
"Failed to find script fallback for {:?} locale '{}', used '{}': '{}'",
|
||||||
self.scripts,
|
self.scripts,
|
||||||
self.locale,
|
self.font_system.locale(),
|
||||||
family,
|
family,
|
||||||
word
|
word
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn face_name(&self, id: fontdb::ID) -> &str {
|
||||||
|
if let Some(face) = self.font_system.db().face(id) {
|
||||||
|
if let Some((name, _)) = face.families.first() {
|
||||||
|
name
|
||||||
|
} else {
|
||||||
|
&face.post_script_name
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
"invalid font id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn face_contains_family(&self, id: fontdb::ID, family_name: &str) -> bool {
|
||||||
|
if let Some(face) = self.font_system.db().face(id) {
|
||||||
|
face.families.iter().any(|(name, _)| name == family_name)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for FontFallbackIter<'a> {
|
impl<'a> Iterator for FontFallbackIter<'a> {
|
||||||
type Item = &'a Arc<Font>;
|
type Item = Arc<Font>;
|
||||||
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];
|
|
||||||
self.default_i += 1;
|
self.default_i += 1;
|
||||||
|
for id in self.font_ids.iter() {
|
||||||
for font in self.fonts.iter() {
|
let default_family = self
|
||||||
if font.contains_family(default_family) {
|
.font_system
|
||||||
return Some(font);
|
.db()
|
||||||
|
.family_name(self.default_families[self.default_i - 1]);
|
||||||
|
if self.face_contains_family(*id, default_family) {
|
||||||
|
if let Some(font) = self.font_system.get_font(*id) {
|
||||||
|
return Some(font);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -104,20 +128,22 @@ impl<'a> Iterator for FontFallbackIter<'a> {
|
||||||
while self.script_i.0 < self.scripts.len() {
|
while self.script_i.0 < self.scripts.len() {
|
||||||
let script = self.scripts[self.script_i.0];
|
let script = self.scripts[self.script_i.0];
|
||||||
|
|
||||||
let script_families = script_fallback(script, self.locale);
|
let script_families = script_fallback(script, self.font_system.locale());
|
||||||
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 id in self.font_ids.iter() {
|
||||||
if font.contains_family(script_family) {
|
if self.face_contains_family(*id, script_family) {
|
||||||
return Some(font);
|
if let Some(font) = self.font_system.get_font(*id) {
|
||||||
|
return Some(font);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"failed to find family '{}' for script {:?} and locale '{}'",
|
"failed to find family '{}' for script {:?} and locale '{}'",
|
||||||
script_family,
|
script_family,
|
||||||
script,
|
script,
|
||||||
self.locale
|
self.font_system.locale(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,9 +155,11 @@ 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 id in self.font_ids.iter() {
|
||||||
if font.contains_family(common_family) {
|
if self.face_contains_family(*id, common_family) {
|
||||||
return Some(font);
|
if let Some(font) = self.font_system.get_font(*id) {
|
||||||
|
return Some(font);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log::debug!("failed to find family '{}'", common_family);
|
log::debug!("failed to find family '{}'", common_family);
|
||||||
|
|
@ -140,14 +168,16 @@ 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_ids.len() {
|
||||||
let font = &self.fonts[self.other_i];
|
let id = self.font_ids[self.other_i];
|
||||||
self.other_i += 1;
|
self.other_i += 1;
|
||||||
if forbidden_families
|
if forbidden_families
|
||||||
.iter()
|
.iter()
|
||||||
.all(|family| !font.contains_family(family))
|
.all(|family_name| !self.face_contains_family(id, family_name))
|
||||||
{
|
{
|
||||||
return Some(font);
|
if let Some(font) = self.font_system.get_font(id) {
|
||||||
|
return Some(font);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
pub(crate) mod fallback;
|
pub(crate) mod fallback;
|
||||||
|
|
||||||
use fontdb::FaceInfo;
|
use alloc::sync::Arc;
|
||||||
|
|
||||||
pub use self::system::*;
|
pub use self::system::*;
|
||||||
mod system;
|
mod system;
|
||||||
|
|
@ -12,8 +12,9 @@ pub struct Font(FontInner);
|
||||||
#[ouroboros::self_referencing]
|
#[ouroboros::self_referencing]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
struct FontInner {
|
struct FontInner {
|
||||||
info: fontdb::FaceInfo,
|
id: fontdb::ID,
|
||||||
#[borrows(info)]
|
data: Arc<dyn AsRef<[u8]> + Send + Sync>,
|
||||||
|
#[borrows(data)]
|
||||||
#[covariant]
|
#[covariant]
|
||||||
rustybuzz: rustybuzz::Face<'this>,
|
rustybuzz: rustybuzz::Face<'this>,
|
||||||
// workaround, since ouroboros does not work with #[cfg(feature = "swash")]
|
// workaround, since ouroboros does not work with #[cfg(feature = "swash")]
|
||||||
|
|
@ -30,18 +31,18 @@ impl Font {
|
||||||
pub fn new(info: &fontdb::FaceInfo) -> Option<Self> {
|
pub fn new(info: &fontdb::FaceInfo) -> Option<Self> {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
let data = match &info.source {
|
let data = match &info.source {
|
||||||
fontdb::Source::Binary(data) => (**data).as_ref(),
|
fontdb::Source::Binary(data) => Arc::clone(data),
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
fontdb::Source::File(path) => {
|
fontdb::Source::File(path) => {
|
||||||
log::warn!("Unsupported fontdb Source::File('{}')", path.display());
|
log::warn!("Unsupported fontdb Source::File('{}')", path.display());
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
fontdb::Source::SharedFile(_path, data) => (**data).as_ref(),
|
fontdb::Source::SharedFile(_path, data) => Arc::clone(data),
|
||||||
};
|
};
|
||||||
Some(Self(
|
Some(Self(
|
||||||
FontInnerTryBuilder {
|
FontInnerTryBuilder {
|
||||||
info: info.clone(),
|
id: info.id,
|
||||||
swash: {
|
swash: {
|
||||||
#[cfg(feature = "swash")]
|
#[cfg(feature = "swash")]
|
||||||
let swash = {
|
let swash = {
|
||||||
|
|
@ -53,8 +54,9 @@ impl Font {
|
||||||
let swash = ();
|
let swash = ();
|
||||||
swash
|
swash
|
||||||
},
|
},
|
||||||
rustybuzz_builder: |info| {
|
data,
|
||||||
rustybuzz::Face::from_slice(get_data(info), info.index).ok_or(())
|
rustybuzz_builder: |data| {
|
||||||
|
rustybuzz::Face::from_slice((**data).as_ref(), info.index).ok_or(())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
.try_build()
|
.try_build()
|
||||||
|
|
@ -62,36 +64,23 @@ impl Font {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn info(&self) -> &FaceInfo {
|
pub fn id(&self) -> fontdb::ID {
|
||||||
self.0.borrow_info()
|
*self.0.borrow_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data(&self) -> &[u8] {
|
pub fn data(&self) -> &[u8] {
|
||||||
get_data(self.0.borrow_info())
|
(**self.0.borrow_data()).as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rustybuzz(&self) -> &rustybuzz::Face {
|
pub fn rustybuzz(&self) -> &rustybuzz::Face {
|
||||||
self.0.borrow_rustybuzz()
|
self.0.borrow_rustybuzz()
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
let info = self.0.borrow_info();
|
|
||||||
let swash = self.0.borrow_swash();
|
let swash = self.0.borrow_swash();
|
||||||
swash::FontRef {
|
swash::FontRef {
|
||||||
data: get_data(info),
|
data: self.data(),
|
||||||
offset: swash.0,
|
offset: swash.0,
|
||||||
key: swash.1,
|
key: swash.1,
|
||||||
}
|
}
|
||||||
|
|
@ -104,16 +93,3 @@ impl Font {
|
||||||
self.0.borrow_swash();
|
self.0.borrow_swash();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_data(info: &FaceInfo) -> &[u8] {
|
|
||||||
match &info.source {
|
|
||||||
fontdb::Source::Binary(data) => (**data).as_ref(),
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
fontdb::Source::File(path) => {
|
|
||||||
// This should never happen, because `Font::new` verified the source isn't a file
|
|
||||||
panic!("Unsupported fontdb Source::File('{}')", path.display());
|
|
||||||
}
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
fontdb::Source::SharedFile(_path, data) => (**data).as_ref(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -51,19 +51,15 @@ impl FontSystem {
|
||||||
get_font(&self.db, id)
|
get_font(&self.db, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_font_matches(&mut self, attrs: Attrs) -> Arc<Vec<Arc<Font>>> {
|
pub fn get_font_matches(&mut self, attrs: Attrs) -> Arc<Vec<fontdb::ID>> {
|
||||||
let mut fonts = Vec::new();
|
let ids = self
|
||||||
for face in self.db.faces() {
|
.db
|
||||||
if !attrs.matches(face) {
|
.faces()
|
||||||
continue;
|
.filter(|face| attrs.matches(face))
|
||||||
}
|
.map(|face| face.id)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if let Some(font) = get_font(&self.db, face.id) {
|
Arc::new(ids)
|
||||||
fonts.push(font);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Arc::new(fonts)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ pub struct FontSystem {
|
||||||
locale: String,
|
locale: String,
|
||||||
db: fontdb::Database,
|
db: fontdb::Database,
|
||||||
font_cache: HashMap<fontdb::ID, Option<Arc<Font>>>,
|
font_cache: HashMap<fontdb::ID, Option<Arc<Font>>>,
|
||||||
font_matches_cache: HashMap<AttrsOwned, Arc<Vec<Arc<Font>>>>,
|
font_matches_cache: HashMap<AttrsOwned, Arc<Vec<fontdb::ID>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontSystem {
|
impl FontSystem {
|
||||||
|
|
@ -92,7 +92,7 @@ impl FontSystem {
|
||||||
get_font(&mut self.font_cache, &mut self.db, id)
|
get_font(&mut self.font_cache, &mut self.db, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_font_matches(&mut self, attrs: Attrs) -> Arc<Vec<Arc<Font>>> {
|
pub fn get_font_matches(&mut self, attrs: Attrs) -> Arc<Vec<fontdb::ID>> {
|
||||||
self.font_matches_cache
|
self.font_matches_cache
|
||||||
//TODO: do not create AttrsOwned unless entry does not already exist
|
//TODO: do not create AttrsOwned unless entry does not already exist
|
||||||
.entry(AttrsOwned::new(attrs))
|
.entry(AttrsOwned::new(attrs))
|
||||||
|
|
@ -106,10 +106,6 @@ impl FontSystem {
|
||||||
.filter(|face| attrs.matches(face))
|
.filter(|face| attrs.matches(face))
|
||||||
.map(|face| face.id)
|
.map(|face| face.id)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let fonts = ids
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|id| get_font(&mut self.font_cache, &mut self.db, id))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
{
|
{
|
||||||
|
|
@ -117,7 +113,7 @@ impl FontSystem {
|
||||||
log::debug!("font matches for {:?} in {:?}", attrs, elapsed);
|
log::debug!("font matches for {:?} in {:?}", attrs, elapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
Arc::new(fonts)
|
Arc::new(ids)
|
||||||
})
|
})
|
||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
src/shape.rs
18
src/shape.rs
|
|
@ -62,7 +62,7 @@ fn shape_fallback(
|
||||||
y_advance,
|
y_advance,
|
||||||
x_offset,
|
x_offset,
|
||||||
y_offset,
|
y_offset,
|
||||||
font_id: font.info().id,
|
font_id: font.id(),
|
||||||
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,16 +125,13 @@ fn shape_run(
|
||||||
|
|
||||||
let fonts = font_system.get_font_matches(attrs);
|
let fonts = font_system.get_font_matches(attrs);
|
||||||
|
|
||||||
let db = font_system.db();
|
let default_families = [&attrs.family];
|
||||||
|
let mut font_iter = FontFallbackIter::new(font_system, &fonts, &default_families, scripts);
|
||||||
let default_families = [db.family_name(&attrs.family)];
|
|
||||||
let mut font_iter =
|
|
||||||
FontFallbackIter::new(&fonts, &default_families, scripts, font_system.locale());
|
|
||||||
|
|
||||||
let font = font_iter.next().expect("no default font found");
|
let font = font_iter.next().expect("no default font found");
|
||||||
|
|
||||||
let (mut glyphs, mut missing) =
|
let (mut glyphs, mut 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);
|
||||||
|
|
||||||
//TODO: improve performance!
|
//TODO: improve performance!
|
||||||
while !missing.is_empty() {
|
while !missing.is_empty() {
|
||||||
|
|
@ -143,9 +140,12 @@ fn shape_run(
|
||||||
None => break,
|
None => break,
|
||||||
};
|
};
|
||||||
|
|
||||||
log::trace!("Evaluating fallback with font '{}'", font.name());
|
log::trace!(
|
||||||
|
"Evaluating fallback with font '{}'",
|
||||||
|
font_iter.face_name(font.id())
|
||||||
|
);
|
||||||
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;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue