Support non-static list of fallbacks

This commit is contained in:
tigregalis 2025-03-14 14:27:27 +08:00 committed by Jeremy Soller
parent 9a4d80d116
commit ae0fb9b561
2 changed files with 49 additions and 3 deletions

View file

@ -1,5 +1,7 @@
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
use core::mem;
use alloc::sync::Arc; use alloc::sync::Arc;
use alloc::vec::Vec; use alloc::vec::Vec;
use fontdb::Family; use fontdb::Family;
@ -27,13 +29,13 @@ mod platform;
pub trait Fallback { pub trait Fallback {
/// Fallbacks to use after any script specific fallbacks /// Fallbacks to use after any script specific fallbacks
fn common_fallback(&self) -> &'static [&'static str]; fn common_fallback(&self) -> &[&'static str];
/// Fallbacks to never use /// Fallbacks to never use
fn forbidden_fallback(&self) -> &'static [&'static str]; fn forbidden_fallback(&self) -> &[&'static str];
/// Fallbacks to use per script /// Fallbacks to use per script
fn script_fallback(&self, script: Script, locale: &str) -> &'static [&'static str]; fn script_fallback(&self, script: Script, locale: &str) -> &[&'static str];
} }
pub use platform::PlatformFallback; pub use platform::PlatformFallback;
@ -279,12 +281,22 @@ impl Iterator for FontFallbackIter<'_> {
.font_system .font_system
.fallbacks .fallbacks
.script_fallback(script, self.font_system.locale()); .script_fallback(script, self.font_system.locale());
self.font_system.scratch_fallbacks.clear();
self.font_system
.scratch_fallbacks
.extend_from_slice(script_families);
let mut script_families = mem::take(&mut self.font_system.scratch_fallbacks);
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 m_key in font_match_keys_iter(false) { for m_key in font_match_keys_iter(false) {
if self.face_contains_family(m_key.id, script_family) { if self.face_contains_family(m_key.id, script_family) {
if let Some(font) = self.font_system.get_font(m_key.id) { if let Some(font) = self.font_system.get_font(m_key.id) {
mem::swap(
&mut script_families,
&mut self.font_system.scratch_fallbacks,
);
return Some(font); return Some(font);
} }
} }
@ -296,28 +308,50 @@ impl Iterator for FontFallbackIter<'_> {
self.font_system.locale(), self.font_system.locale(),
); );
} }
mem::swap(
&mut script_families,
&mut self.font_system.scratch_fallbacks,
);
self.script_i.0 += 1; self.script_i.0 += 1;
self.script_i.1 = 0; self.script_i.1 = 0;
} }
let common_families = self.font_system.fallbacks.common_fallback(); let common_families = self.font_system.fallbacks.common_fallback();
self.font_system.scratch_fallbacks.clear();
self.font_system
.scratch_fallbacks
.extend_from_slice(common_families);
let mut common_families = mem::take(&mut self.font_system.scratch_fallbacks);
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 m_key in font_match_keys_iter(false) { for m_key in font_match_keys_iter(false) {
if self.face_contains_family(m_key.id, common_family) { if self.face_contains_family(m_key.id, common_family) {
if let Some(font) = self.font_system.get_font(m_key.id) { if let Some(font) = self.font_system.get_font(m_key.id) {
mem::swap(
&mut common_families,
&mut self.font_system.scratch_fallbacks,
);
return Some(font); return Some(font);
} }
} }
} }
log::debug!("failed to find family '{}'", common_family); log::debug!("failed to find family '{}'", common_family);
} }
mem::swap(
&mut common_families,
&mut self.font_system.scratch_fallbacks,
);
//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 = self.font_system.fallbacks.forbidden_fallback(); let forbidden_families = self.font_system.fallbacks.forbidden_fallback();
self.font_system.scratch_fallbacks.clear();
self.font_system
.scratch_fallbacks
.extend_from_slice(forbidden_families);
let mut forbidden_families = mem::take(&mut self.font_system.scratch_fallbacks);
while self.other_i < self.font_match_keys.len() { while self.other_i < self.font_match_keys.len() {
let id = self.font_match_keys[self.other_i].id; let id = self.font_match_keys[self.other_i].id;
self.other_i += 1; self.other_i += 1;
@ -326,10 +360,18 @@ impl Iterator for FontFallbackIter<'_> {
.all(|family_name| !self.face_contains_family(id, family_name)) .all(|family_name| !self.face_contains_family(id, family_name))
{ {
if let Some(font) = self.font_system.get_font(id) { if let Some(font) = self.font_system.get_font(id) {
mem::swap(
&mut forbidden_families,
&mut self.font_system.scratch_fallbacks,
);
return Some(font); return Some(font);
} }
} }
} }
mem::swap(
&mut forbidden_families,
&mut self.font_system.scratch_fallbacks,
);
self.end = true; self.end = true;
None None

View file

@ -116,6 +116,9 @@ pub struct FontSystem {
/// List of fallbacks /// List of fallbacks
pub(crate) fallbacks: Box<dyn Fallback>, pub(crate) fallbacks: Box<dyn Fallback>,
/// Scratch buffer for fallback list
pub(crate) scratch_fallbacks: Vec<&'static str>,
} }
impl fmt::Debug for FontSystem { impl fmt::Debug for FontSystem {
@ -213,6 +216,7 @@ impl FontSystem {
shape_run_cache: crate::ShapeRunCache::default(), shape_run_cache: crate::ShapeRunCache::default(),
shape_buffer: ShapeBuffer::default(), shape_buffer: ShapeBuffer::default(),
fallbacks: Box::new(fallbacks), fallbacks: Box::new(fallbacks),
scratch_fallbacks: Vec::new(),
} }
} }