Merge branch 'refs/heads/main' into stewart-add-kerning-ligatures
# Conflicts: # src/attrs.rs
This commit is contained in:
commit
53763c157b
9 changed files with 350 additions and 34 deletions
45
src/attrs.rs
45
src/attrs.rs
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use alloc::vec::Vec;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::ops::Range;
|
||||
use rangemap::RangeMap;
|
||||
use smol_str::SmolStr;
|
||||
|
|
@ -189,6 +190,37 @@ impl FontFeatures {
|
|||
}
|
||||
}
|
||||
|
||||
/// A wrapper for letter spacing to get around that f32 doesn't implement Eq and Hash
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct LetterSpacing(pub f32);
|
||||
|
||||
impl PartialEq for LetterSpacing {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if self.0.is_nan() {
|
||||
other.0.is_nan()
|
||||
} else {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for LetterSpacing {}
|
||||
|
||||
impl Hash for LetterSpacing {
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
const CANONICAL_NAN_BITS: u32 = 0x7fc0_0000;
|
||||
|
||||
let bits = if self.0.is_nan() {
|
||||
CANONICAL_NAN_BITS
|
||||
} else {
|
||||
// Add +0.0 to canonicalize -0.0 to +0.0
|
||||
(self.0 + 0.0).to_bits()
|
||||
};
|
||||
|
||||
bits.hash(hasher);
|
||||
}
|
||||
}
|
||||
|
||||
/// Text attributes
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct Attrs<'a> {
|
||||
|
|
@ -201,6 +233,8 @@ pub struct Attrs<'a> {
|
|||
pub metadata: usize,
|
||||
pub cache_key_flags: CacheKeyFlags,
|
||||
pub metrics_opt: Option<CacheMetrics>,
|
||||
/// Letter spacing (tracking) in EM
|
||||
pub letter_spacing_opt: Option<LetterSpacing>,
|
||||
pub font_features: FontFeatures,
|
||||
}
|
||||
|
||||
|
|
@ -218,6 +252,7 @@ impl<'a> Attrs<'a> {
|
|||
metadata: 0,
|
||||
cache_key_flags: CacheKeyFlags::empty(),
|
||||
metrics_opt: None,
|
||||
letter_spacing_opt: None,
|
||||
font_features: FontFeatures::new(),
|
||||
}
|
||||
}
|
||||
|
|
@ -270,6 +305,12 @@ impl<'a> Attrs<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Set letter spacing (tracking) in EM
|
||||
pub fn letter_spacing(mut self, letter_spacing: f32) -> Self {
|
||||
self.letter_spacing_opt = Some(LetterSpacing(letter_spacing));
|
||||
self
|
||||
}
|
||||
|
||||
/// Set [FontFeatures]
|
||||
pub fn font_features(mut self, font_features: FontFeatures) -> Self {
|
||||
self.font_features = font_features;
|
||||
|
|
@ -324,6 +365,8 @@ pub struct AttrsOwned {
|
|||
pub metadata: usize,
|
||||
pub cache_key_flags: CacheKeyFlags,
|
||||
pub metrics_opt: Option<CacheMetrics>,
|
||||
/// Letter spacing (tracking) in EM
|
||||
pub letter_spacing_opt: Option<LetterSpacing>,
|
||||
pub font_features: FontFeatures,
|
||||
}
|
||||
|
||||
|
|
@ -338,6 +381,7 @@ impl AttrsOwned {
|
|||
metadata: attrs.metadata,
|
||||
cache_key_flags: attrs.cache_key_flags,
|
||||
metrics_opt: attrs.metrics_opt,
|
||||
letter_spacing_opt: attrs.letter_spacing_opt,
|
||||
font_features: attrs.font_features.clone(),
|
||||
}
|
||||
}
|
||||
|
|
@ -352,6 +396,7 @@ impl AttrsOwned {
|
|||
metadata: self.metadata,
|
||||
cache_key_flags: self.cache_key_flags,
|
||||
metrics_opt: self.metrics_opt,
|
||||
letter_spacing_opt: self.letter_spacing_opt,
|
||||
font_features: self.font_features.clone(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,32 @@
|
|||
|
||||
use unicode_script::Script;
|
||||
|
||||
use super::Fallback;
|
||||
|
||||
/// A platform-specific font fallback list, for MacOS.
|
||||
#[derive(Debug)]
|
||||
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 +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] {
|
||||
&[".LastResort"]
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +58,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 {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::string::String;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
use core::{mem, ops::Range};
|
||||
use fontdb::Family;
|
||||
use unicode_script::Script;
|
||||
|
||||
use crate::{Font, FontMatchKey, FontSystem, ShapeBuffer};
|
||||
|
||||
use self::platform::*;
|
||||
use crate::{BuildHasher, Font, FontMatchKey, FontSystem, HashMap, ShapeBuffer};
|
||||
|
||||
#[cfg(not(any(all(unix, not(target_os = "android")), target_os = "windows")))]
|
||||
#[path = "other.rs"]
|
||||
|
|
@ -25,6 +26,145 @@ mod platform;
|
|||
#[path = "windows.rs"]
|
||||
mod platform;
|
||||
|
||||
/// The `Fallback` trait allows for configurable font fallback lists to be set during construction of the [`FontSystem`].
|
||||
///
|
||||
/// A custom fallback list can be added via the [`FontSystem::new_with_locale_and_db_and_fallback`] constructor.
|
||||
///
|
||||
/// A default implementation is provided by the [`PlatformFallback`] struct, which encapsulates the target platform's pre-configured fallback lists.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use unicode_script::Script;
|
||||
/// # use cosmic_text::{Fallback, FontSystem};
|
||||
/// struct MyFallback;
|
||||
/// impl Fallback for MyFallback {
|
||||
/// fn common_fallback(&self) -> &[&'static str] {
|
||||
/// &[
|
||||
/// "Segoe UI",
|
||||
/// "Segoe UI Emoji",
|
||||
/// "Segoe UI Symbol",
|
||||
/// "Segoe UI Historic",
|
||||
/// ]
|
||||
/// }
|
||||
///
|
||||
/// fn forbidden_fallback(&self) -> &[&'static str] {
|
||||
/// &[]
|
||||
/// }
|
||||
///
|
||||
/// fn script_fallback(&self, script: Script, locale: &str) -> &[&'static str] {
|
||||
/// match script {
|
||||
/// Script::Adlam => &["Ebrima"],
|
||||
/// Script::Bengali => &["Nirmala UI"],
|
||||
/// Script::Canadian_Aboriginal => &["Gadugi"],
|
||||
/// // ...
|
||||
/// _ => &[],
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let locale = "en-US".to_string();
|
||||
/// let db = fontdb::Database::new();
|
||||
/// let font_system = FontSystem::new_with_locale_and_db_and_fallback(locale, db, MyFallback);
|
||||
/// ```
|
||||
pub trait Fallback: Send + Sync {
|
||||
/// Fallbacks to use after any script specific fallbacks
|
||||
fn common_fallback(&self) -> &[&'static str];
|
||||
|
||||
/// Fallbacks to never use
|
||||
fn forbidden_fallback(&self) -> &[&'static str];
|
||||
|
||||
/// Fallbacks to use per script
|
||||
fn script_fallback(&self, script: Script, locale: &str) -> &[&'static str];
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct Fallbacks {
|
||||
lists: Vec<&'static str>,
|
||||
common_fallback_range: Range<usize>,
|
||||
forbidden_fallback_range: Range<usize>,
|
||||
// PERF: Consider using NoHashHasher since Script is just an integer
|
||||
script_fallback_ranges: HashMap<Script, Range<usize>>,
|
||||
locale: String,
|
||||
}
|
||||
|
||||
impl Fallbacks {
|
||||
pub(crate) fn new(fallbacks: &dyn Fallback, scripts: &[Script], locale: &str) -> Self {
|
||||
let common_fallback = fallbacks.common_fallback();
|
||||
|
||||
let forbidden_fallback = fallbacks.forbidden_fallback();
|
||||
|
||||
let mut lists =
|
||||
Vec::with_capacity(common_fallback.len() + forbidden_fallback.len() + scripts.len());
|
||||
|
||||
let mut index = lists.len();
|
||||
let mut new_range = |lists: &Vec<&str>| {
|
||||
let old_index = index;
|
||||
index = lists.len();
|
||||
old_index..index
|
||||
};
|
||||
|
||||
lists.extend_from_slice(common_fallback);
|
||||
let common_fallback_range = new_range(&lists);
|
||||
|
||||
lists.extend_from_slice(forbidden_fallback);
|
||||
let forbidden_fallback_range = new_range(&lists);
|
||||
|
||||
let mut script_fallback_ranges =
|
||||
HashMap::with_capacity_and_hasher(scripts.len(), BuildHasher::new());
|
||||
for &script in scripts {
|
||||
let script_fallback = fallbacks.script_fallback(script, locale);
|
||||
lists.extend_from_slice(script_fallback);
|
||||
let script_fallback_range = new_range(&lists);
|
||||
script_fallback_ranges.insert(script, script_fallback_range);
|
||||
}
|
||||
|
||||
let locale = locale.to_owned();
|
||||
Self {
|
||||
lists,
|
||||
common_fallback_range,
|
||||
forbidden_fallback_range,
|
||||
script_fallback_ranges,
|
||||
locale,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn extend(&mut self, fallbacks: &dyn Fallback, scripts: &[Script]) {
|
||||
self.lists.reserve(scripts.len());
|
||||
|
||||
let mut index = self.lists.len();
|
||||
let mut new_range = |lists: &Vec<&str>| {
|
||||
let old_index = index;
|
||||
index = lists.len();
|
||||
old_index..index
|
||||
};
|
||||
|
||||
for &script in scripts {
|
||||
self.script_fallback_ranges
|
||||
.entry(script)
|
||||
.or_insert_with_key(|&script| {
|
||||
let script_fallback = fallbacks.script_fallback(script, &self.locale);
|
||||
self.lists.extend_from_slice(script_fallback);
|
||||
new_range(&self.lists)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn common_fallback(&self) -> &[&'static str] {
|
||||
&self.lists[self.common_fallback_range.clone()]
|
||||
}
|
||||
|
||||
pub(crate) fn forbidden_fallback(&self) -> &[&'static str] {
|
||||
&self.lists[self.forbidden_fallback_range.clone()]
|
||||
}
|
||||
|
||||
pub(crate) fn script_fallback(&self, script: Script) -> &[&'static str] {
|
||||
self.script_fallback_ranges
|
||||
.get(&script)
|
||||
.map_or(&[], |range| &self.lists[range.clone()])
|
||||
}
|
||||
}
|
||||
|
||||
pub use platform::PlatformFallback;
|
||||
|
||||
#[cfg(not(feature = "warn_on_missing_glyphs"))]
|
||||
use log::debug as missing_warn;
|
||||
#[cfg(feature = "warn_on_missing_glyphs")]
|
||||
|
|
@ -62,6 +202,9 @@ impl<'a> FontFallbackIter<'a> {
|
|||
scripts: &'a [Script],
|
||||
word: &'a str,
|
||||
) -> Self {
|
||||
font_system
|
||||
.fallbacks
|
||||
.extend(font_system.dyn_fallback.as_ref(), scripts);
|
||||
font_system.monospace_fallbacks_buffer.clear();
|
||||
Self {
|
||||
font_system,
|
||||
|
|
@ -94,7 +237,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,
|
||||
|
|
@ -138,11 +281,8 @@ impl<'a> FontFallbackIter<'a> {
|
|||
.filter(|m_key| m_key.font_weight_diff == 0)
|
||||
.find(|m_key| self.face_contains_family(m_key.id, default_family_name))
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for FontFallbackIter<'_> {
|
||||
type Item = Arc<Font>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
fn next_item(&mut self, fallbacks: &Fallbacks) -> Option<<Self as Iterator>::Item> {
|
||||
if let Some(fallback_info) = self.font_system.monospace_fallbacks_buffer.pop_first() {
|
||||
if let Some(font) = self.font_system.get_font(fallback_info.id) {
|
||||
return Some(font);
|
||||
|
|
@ -262,7 +402,8 @@ 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 = fallbacks.script_fallback(script);
|
||||
|
||||
while self.script_i.1 < script_families.len() {
|
||||
let script_family = script_families[self.script_i.1];
|
||||
self.script_i.1 += 1;
|
||||
|
|
@ -285,7 +426,7 @@ impl Iterator for FontFallbackIter<'_> {
|
|||
self.script_i.1 = 0;
|
||||
}
|
||||
|
||||
let common_families = common_fallback();
|
||||
let common_families = fallbacks.common_fallback();
|
||||
while self.common_i < common_families.len() {
|
||||
let common_family = common_families[self.common_i];
|
||||
self.common_i += 1;
|
||||
|
|
@ -301,7 +442,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 = 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;
|
||||
|
|
@ -319,3 +460,13 @@ impl Iterator for FontFallbackIter<'_> {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for FontFallbackIter<'_> {
|
||||
type Item = Arc<Font>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut fallbacks = mem::take(&mut self.font_system.fallbacks);
|
||||
let item = self.next_item(&fallbacks);
|
||||
mem::swap(&mut fallbacks, &mut self.font_system.fallbacks);
|
||||
item
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,41 @@
|
|||
|
||||
use unicode_script::Script;
|
||||
|
||||
use super::Fallback;
|
||||
|
||||
/// An empty platform-specific font fallback list.
|
||||
#[derive(Debug)]
|
||||
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] {
|
||||
&[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,32 @@
|
|||
|
||||
use unicode_script::Script;
|
||||
|
||||
use super::Fallback;
|
||||
|
||||
/// A platform-specific font fallback list, for Unix.
|
||||
#[derive(Debug)]
|
||||
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 +49,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 +69,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"],
|
||||
|
|
|
|||
|
|
@ -2,8 +2,32 @@
|
|||
|
||||
use unicode_script::Script;
|
||||
|
||||
use super::Fallback;
|
||||
|
||||
/// A platform-specific font fallback list, for Windows.
|
||||
#[derive(Debug)]
|
||||
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 +39,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 +60,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"],
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
pub(crate) mod fallback;
|
||||
|
||||
// re-export ttf_parser
|
||||
pub use ttf_parser;
|
||||
|
|
@ -13,6 +12,9 @@ use alloc::vec::Vec;
|
|||
use rustybuzz::Face as RustybuzzFace;
|
||||
use self_cell::self_cell;
|
||||
|
||||
pub(crate) mod fallback;
|
||||
pub use fallback::{Fallback, PlatformFallback};
|
||||
|
||||
pub use self::system::*;
|
||||
mod system;
|
||||
|
||||
|
|
|
|||
|
|
@ -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, Fallbacks, MonospaceFallbackInfo, PlatformFallback};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct FontMatchKey {
|
||||
|
|
@ -112,6 +113,12 @@ pub struct FontSystem {
|
|||
/// Cache for shaped runs
|
||||
#[cfg(feature = "shape-run-cache")]
|
||||
pub shape_run_cache: crate::ShapeRunCache,
|
||||
|
||||
/// List of fallbacks
|
||||
pub(crate) dyn_fallback: Box<dyn Fallback>,
|
||||
|
||||
/// List of fallbacks
|
||||
pub(crate) fallbacks: Fallbacks,
|
||||
}
|
||||
|
||||
impl fmt::Debug for FontSystem {
|
||||
|
|
@ -150,11 +157,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,
|
||||
impl_fallback: impl Fallback + 'static,
|
||||
) -> Self {
|
||||
let mut monospace_font_ids = db
|
||||
.faces()
|
||||
.filter(|face_info| {
|
||||
|
|
@ -192,6 +203,8 @@ impl FontSystem {
|
|||
.map(|(k, v)| (k, Vec::from_iter(v)))
|
||||
.collect();
|
||||
|
||||
let fallbacks = Fallbacks::new(&impl_fallback, &[], &locale);
|
||||
|
||||
Self {
|
||||
locale,
|
||||
db,
|
||||
|
|
@ -204,9 +217,16 @@ impl FontSystem {
|
|||
#[cfg(feature = "shape-run-cache")]
|
||||
shape_run_cache: crate::ShapeRunCache::default(),
|
||||
shape_buffer: ShapeBuffer::default(),
|
||||
dyn_fallback: Box::new(impl_fallback),
|
||||
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
|
||||
|
|
|
|||
14
src/shape.rs
14
src/shape.rs
|
|
@ -167,11 +167,6 @@ fn shape_fallback(
|
|||
glyphs.reserve(glyph_infos.len());
|
||||
let glyph_start = glyphs.len();
|
||||
for (info, pos) in glyph_infos.iter().zip(glyph_positions.iter()) {
|
||||
let x_advance = pos.x_advance as f32 / font_scale;
|
||||
let y_advance = pos.y_advance as f32 / font_scale;
|
||||
let x_offset = pos.x_offset as f32 / font_scale;
|
||||
let y_offset = pos.y_offset as f32 / font_scale;
|
||||
|
||||
let start_glyph = start_run + info.cluster as usize;
|
||||
|
||||
if info.glyph_id == 0 {
|
||||
|
|
@ -179,6 +174,12 @@ fn shape_fallback(
|
|||
}
|
||||
|
||||
let attrs = attrs_list.get_span(start_glyph);
|
||||
let x_advance = pos.x_advance as f32 / font_scale
|
||||
+ attrs.letter_spacing_opt.map_or(0.0, |spacing| spacing.0);
|
||||
let y_advance = pos.y_advance as f32 / font_scale;
|
||||
let x_offset = pos.x_offset as f32 / font_scale;
|
||||
let y_offset = pos.y_offset as f32 / font_scale;
|
||||
|
||||
glyphs.push(ShapeGlyph {
|
||||
start: start_glyph,
|
||||
end: end_run, // Set later
|
||||
|
|
@ -465,7 +466,8 @@ fn shape_skip(
|
|||
.char_indices()
|
||||
.map(|(chr_idx, codepoint)| {
|
||||
let glyph_id = charmap.map(codepoint);
|
||||
let x_advance = glyph_metrics.advance_width(glyph_id);
|
||||
let x_advance = glyph_metrics.advance_width(glyph_id)
|
||||
+ attrs.letter_spacing_opt.map_or(0.0, |spacing| spacing.0);
|
||||
let attrs = attrs_list.get_span(start_run + chr_idx);
|
||||
|
||||
ShapeGlyph {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue