Use fallback list via dyn trait in FontSystem

This commit is contained in:
tigregalis 2025-03-03 16:14:45 +08:00 committed by Jeremy Soller
parent 4e6d5d731b
commit a67dded054
6 changed files with 142 additions and 20 deletions

View file

@ -2,8 +2,31 @@
use unicode_script::Script;
use super::Fallback;
/// A platform-specific font fallback list, for MacOS.
pub struct PlatformFallback;
impl Fallback for PlatformFallback {
fn common_fallback(&self) -> &'static [&'static str] {
common_fallback()
}
fn forbidden_fallback(&self) -> &'static [&'static str] {
forbidden_fallback()
}
fn script_fallback(
&self,
script: unicode_script::Script,
locale: &str,
) -> &'static [&'static str] {
script_fallback(script, locale)
}
}
// Fallbacks to use after any script specific fallbacks
pub fn common_fallback() -> &'static [&'static str] {
fn common_fallback() -> &'static [&'static str] {
&[
".SF NS",
"Menlo",
@ -14,7 +37,7 @@ pub fn common_fallback() -> &'static [&'static str] {
}
// Fallbacks to never use
pub fn forbidden_fallback() -> &'static [&'static str] {
fn forbidden_fallback() -> &'static [&'static str] {
&[".LastResort"]
}
@ -34,7 +57,7 @@ fn han_unification(locale: &str) -> &'static [&'static str] {
}
// Fallbacks to use per script
pub fn script_fallback(script: Script, locale: &str) -> &'static [&'static str] {
fn script_fallback(script: Script, locale: &str) -> &'static [&'static str] {
//TODO: abstract style (sans/serif/monospaced)
//TODO: pull more data from about:config font.name-list.sans-serif in Firefox
match script {

View file

@ -25,6 +25,19 @@ mod platform;
#[path = "windows.rs"]
mod platform;
pub trait Fallback {
/// Fallbacks to use after any script specific fallbacks
fn common_fallback(&self) -> &'static [&'static str];
/// Fallbacks to never use
fn forbidden_fallback(&self) -> &'static [&'static str];
/// Fallbacks to use per script
fn script_fallback(&self, script: Script, locale: &str) -> &'static [&'static str];
}
pub use platform::PlatformFallback;
#[cfg(not(feature = "warn_on_missing_glyphs"))]
use log::debug as missing_warn;
#[cfg(feature = "warn_on_missing_glyphs")]
@ -94,7 +107,7 @@ impl<'a> FontFallbackIter<'a> {
word
);
} else if !self.scripts.is_empty() && self.common_i > 0 {
let family = common_fallback()[self.common_i - 1];
let family = self.font_system.fallbacks.common_fallback()[self.common_i - 1];
missing_warn!(
"Failed to find script fallback for {:?} locale '{}', used '{}': '{}'",
self.scripts,
@ -262,7 +275,10 @@ impl Iterator for FontFallbackIter<'_> {
while self.script_i.0 < self.scripts.len() {
let script = self.scripts[self.script_i.0];
let script_families = script_fallback(script, self.font_system.locale());
let script_families = self
.font_system
.fallbacks
.script_fallback(script, self.font_system.locale());
while self.script_i.1 < script_families.len() {
let script_family = script_families[self.script_i.1];
self.script_i.1 += 1;
@ -285,7 +301,7 @@ impl Iterator for FontFallbackIter<'_> {
self.script_i.1 = 0;
}
let common_families = common_fallback();
let common_families = self.font_system.fallbacks.common_fallback();
while self.common_i < common_families.len() {
let common_family = common_families[self.common_i];
self.common_i += 1;
@ -301,7 +317,7 @@ impl Iterator for FontFallbackIter<'_> {
//TODO: do we need to do this?
//TODO: do not evaluate fonts more than once!
let forbidden_families = forbidden_fallback();
let forbidden_families = self.font_system.fallbacks.forbidden_fallback();
while self.other_i < self.font_match_keys.len() {
let id = self.font_match_keys[self.other_i].id;
self.other_i += 1;

View file

@ -2,17 +2,40 @@
use unicode_script::Script;
use super::Fallback;
/// An empty platform-specific font fallback list.
pub struct PlatformFallback;
impl Fallback for PlatformFallback {
fn common_fallback(&self) -> &'static [&'static str] {
common_fallback()
}
fn forbidden_fallback(&self) -> &'static [&'static str] {
forbidden_fallback()
}
fn script_fallback(
&self,
script: unicode_script::Script,
locale: &str,
) -> &'static [&'static str] {
script_fallback(script, locale)
}
}
// Fallbacks to use after any script specific fallbacks
pub fn common_fallback() -> &'static [&'static str] {
fn common_fallback() -> &'static [&'static str] {
&[]
}
// Fallbacks to never use
pub fn forbidden_fallback() -> &'static [&'static str] {
fn forbidden_fallback() -> &'static [&'static str] {
&[]
}
// Fallbacks to use per script
pub fn script_fallback(_script: Script, _locale: &str) -> &'static [&'static str] {
fn script_fallback(_script: Script, _locale: &str) -> &'static [&'static str] {
&[]
}

View file

@ -2,8 +2,31 @@
use unicode_script::Script;
use super::Fallback;
/// A platform-specific font fallback list, for Unix.
pub struct PlatformFallback;
impl Fallback for PlatformFallback {
fn common_fallback(&self) -> &'static [&'static str] {
common_fallback()
}
fn forbidden_fallback(&self) -> &'static [&'static str] {
forbidden_fallback()
}
fn script_fallback(
&self,
script: unicode_script::Script,
locale: &str,
) -> &'static [&'static str] {
script_fallback(script, locale)
}
}
// Fallbacks to use after any script specific fallbacks
pub fn common_fallback() -> &'static [&'static str] {
fn common_fallback() -> &'static [&'static str] {
//TODO: abstract style (sans/serif/monospaced)
&[
/* Sans-serif fallbacks */
@ -25,7 +48,7 @@ pub fn common_fallback() -> &'static [&'static str] {
}
// Fallbacks to never use
pub fn forbidden_fallback() -> &'static [&'static str] {
fn forbidden_fallback() -> &'static [&'static str] {
&[]
}
@ -45,7 +68,7 @@ fn han_unification(locale: &str) -> &'static [&'static str] {
}
// Fallbacks to use per script
pub fn script_fallback(script: Script, locale: &str) -> &'static [&'static str] {
fn script_fallback(script: Script, locale: &str) -> &'static [&'static str] {
//TODO: abstract style (sans/serif/monospaced)
match script {
Script::Adlam => &["Noto Sans Adlam", "Noto Sans Adlam Unjoined"],

View file

@ -2,8 +2,31 @@
use unicode_script::Script;
use super::Fallback;
/// A platform-specific font fallback list, for Windows.
pub struct PlatformFallback;
impl Fallback for PlatformFallback {
fn common_fallback(&self) -> &'static [&'static str] {
common_fallback()
}
fn forbidden_fallback(&self) -> &'static [&'static str] {
forbidden_fallback()
}
fn script_fallback(
&self,
script: unicode_script::Script,
locale: &str,
) -> &'static [&'static str] {
script_fallback(script, locale)
}
}
// Fallbacks to use after any script specific fallbacks
pub fn common_fallback() -> &'static [&'static str] {
fn common_fallback() -> &'static [&'static str] {
//TODO: abstract style (sans/serif/monospaced)
&[
"Segoe UI",
@ -15,7 +38,7 @@ pub fn common_fallback() -> &'static [&'static str] {
}
// Fallbacks to never use
pub fn forbidden_fallback() -> &'static [&'static str] {
fn forbidden_fallback() -> &'static [&'static str] {
&[]
}
@ -36,7 +59,7 @@ fn han_unification(locale: &str) -> &'static [&'static str] {
}
// Fallbacks to use per script
pub fn script_fallback(script: Script, locale: &str) -> &'static [&'static str] {
fn script_fallback(script: Script, locale: &str) -> &'static [&'static str] {
//TODO: better match https://github.com/chromium/chromium/blob/master/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc#L99
match script {
Script::Adlam => &["Ebrima"],

View file

@ -1,4 +1,5 @@
use crate::{Attrs, Font, FontMatchAttrs, HashMap, ShapeBuffer};
use alloc::boxed::Box;
use alloc::collections::BTreeSet;
use alloc::string::String;
use alloc::sync::Arc;
@ -10,7 +11,7 @@ use core::ops::{Deref, DerefMut};
pub use fontdb;
pub use rustybuzz;
use super::fallback::MonospaceFallbackInfo;
use super::fallback::{Fallback, MonospaceFallbackInfo, PlatformFallback};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct FontMatchKey {
@ -112,6 +113,9 @@ pub struct FontSystem {
/// Cache for shaped runs
#[cfg(feature = "shape-run-cache")]
pub shape_run_cache: crate::ShapeRunCache,
/// List of fallbacks
pub(crate) fallbacks: Box<dyn Fallback>,
}
impl fmt::Debug for FontSystem {
@ -150,11 +154,15 @@ impl FontSystem {
db.set_sans_serif_family("Open Sans");
db.set_serif_family("DejaVu Serif");
Self::new_with_locale_and_db(locale, db)
Self::new_with_locale_and_db_and_fallback(locale, db, PlatformFallback)
}
/// Create a new [`FontSystem`] with a pre-specified locale and font database.
pub fn new_with_locale_and_db(locale: String, db: fontdb::Database) -> Self {
/// Create a new [`FontSystem`] with a pre-specified locale, font database and font fallback list.
pub fn new_with_locale_and_db_and_fallback(
locale: String,
db: fontdb::Database,
fallbacks: impl Fallback + 'static,
) -> Self {
let mut monospace_font_ids = db
.faces()
.filter(|face_info| {
@ -204,9 +212,15 @@ impl FontSystem {
#[cfg(feature = "shape-run-cache")]
shape_run_cache: crate::ShapeRunCache::default(),
shape_buffer: ShapeBuffer::default(),
fallbacks: Box::new(fallbacks),
}
}
/// Create a new [`FontSystem`] with a pre-specified locale and font database.
pub fn new_with_locale_and_db(locale: String, db: fontdb::Database) -> Self {
Self::new_with_locale_and_db_and_fallback(locale, db, PlatformFallback)
}
/// Get the locale.
pub fn locale(&self) -> &str {
&self.locale