Variable font support (#400)
* Variable font support Here's a pretty naïve solution for variable fonts. The iterator doesn't use the match keys' weight, but instead tries to get the requested ideal weight, if the font is variable, otherwise it is ignored and the actual (non-variable) weight is used. This is because I didn't implement finding variable weight support for match keys; doing so would be impossible without parsing TTF files when matching and I didn't want to add that potentially expensive infrastructure if not entirely necessary. This is a breaking change, and I'm open for ideas on how to fix that if it's an issue. * cargo fmt * Add variable font example to rich-text example
This commit is contained in:
parent
d15011fba5
commit
a03faa654d
10 changed files with 119 additions and 34 deletions
|
|
@ -192,6 +192,7 @@ pub struct FontFallbackIter<'a> {
|
|||
common_i: usize,
|
||||
other_i: usize,
|
||||
end: bool,
|
||||
ideal_weight: fontdb::Weight,
|
||||
}
|
||||
|
||||
impl<'a> FontFallbackIter<'a> {
|
||||
|
|
@ -201,6 +202,7 @@ impl<'a> FontFallbackIter<'a> {
|
|||
default_families: &'a [&'a Family<'a>],
|
||||
scripts: &'a [Script],
|
||||
word: &'a str,
|
||||
ideal_weight: fontdb::Weight,
|
||||
) -> Self {
|
||||
font_system
|
||||
.fallbacks
|
||||
|
|
@ -217,6 +219,7 @@ impl<'a> FontFallbackIter<'a> {
|
|||
common_i: 0,
|
||||
other_i: 0,
|
||||
end: false,
|
||||
ideal_weight,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -284,7 +287,10 @@ impl<'a> FontFallbackIter<'a> {
|
|||
|
||||
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) {
|
||||
if let Some(font) = self
|
||||
.font_system
|
||||
.get_font(fallback_info.id, self.ideal_weight)
|
||||
{
|
||||
return Some(font);
|
||||
}
|
||||
}
|
||||
|
|
@ -303,9 +309,12 @@ impl<'a> FontFallbackIter<'a> {
|
|||
|
||||
macro_rules! mk_mono_fallback_info {
|
||||
($m_key:expr) => {{
|
||||
let supported_cp_count_opt = self
|
||||
.font_system
|
||||
.get_font_supported_codepoints_in_word($m_key.id, self.word);
|
||||
let supported_cp_count_opt =
|
||||
self.font_system.get_font_supported_codepoints_in_word(
|
||||
$m_key.id,
|
||||
self.ideal_weight,
|
||||
self.word,
|
||||
);
|
||||
|
||||
supported_cp_count_opt.map(|supported_cp_count| {
|
||||
let codepoint_non_matches = word_chars_count - supported_cp_count;
|
||||
|
|
@ -323,7 +332,7 @@ impl<'a> FontFallbackIter<'a> {
|
|||
match (is_mono, default_font_match_key.as_ref()) {
|
||||
(false, None) => break 'DEF_FAM,
|
||||
(false, Some(m_key)) => {
|
||||
if let Some(font) = self.font_system.get_font(m_key.id) {
|
||||
if let Some(font) = self.font_system.get_font(m_key.id, self.ideal_weight) {
|
||||
return Some(font);
|
||||
} else {
|
||||
break 'DEF_FAM;
|
||||
|
|
@ -338,7 +347,9 @@ impl<'a> FontFallbackIter<'a> {
|
|||
// Return early if default Monospace font supports all word codepoints.
|
||||
// Otherewise, add to fallbacks set
|
||||
if fallback_info.codepoint_non_matches == Some(0) {
|
||||
if let Some(font) = self.font_system.get_font(m_key.id) {
|
||||
if let Some(font) =
|
||||
self.font_system.get_font(m_key.id, self.ideal_weight)
|
||||
{
|
||||
return Some(font);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -370,9 +381,12 @@ impl<'a> FontFallbackIter<'a> {
|
|||
};
|
||||
|
||||
if is_mono_id {
|
||||
let supported_cp_count_opt = self
|
||||
.font_system
|
||||
.get_font_supported_codepoints_in_word(m_key.id, self.word);
|
||||
let supported_cp_count_opt =
|
||||
self.font_system.get_font_supported_codepoints_in_word(
|
||||
m_key.id,
|
||||
self.ideal_weight,
|
||||
self.word,
|
||||
);
|
||||
if let Some(supported_cp_count) = supported_cp_count_opt {
|
||||
let codepoint_non_matches =
|
||||
self.word.chars().count() - supported_cp_count;
|
||||
|
|
@ -393,7 +407,10 @@ impl<'a> FontFallbackIter<'a> {
|
|||
}
|
||||
// If default family is Monospace fallback to first monospaced font
|
||||
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) {
|
||||
if let Some(font) = self
|
||||
.font_system
|
||||
.get_font(fallback_info.id, self.ideal_weight)
|
||||
{
|
||||
return Some(font);
|
||||
}
|
||||
}
|
||||
|
|
@ -409,7 +426,7 @@ impl<'a> FontFallbackIter<'a> {
|
|||
self.script_i.1 += 1;
|
||||
for m_key in font_match_keys_iter(false) {
|
||||
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, self.ideal_weight) {
|
||||
return Some(font);
|
||||
}
|
||||
}
|
||||
|
|
@ -432,7 +449,7 @@ impl<'a> FontFallbackIter<'a> {
|
|||
self.common_i += 1;
|
||||
for m_key in font_match_keys_iter(false) {
|
||||
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, self.ideal_weight) {
|
||||
return Some(font);
|
||||
}
|
||||
}
|
||||
|
|
@ -450,7 +467,7 @@ impl<'a> FontFallbackIter<'a> {
|
|||
.iter()
|
||||
.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, self.ideal_weight) {
|
||||
return Some(font);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ impl Font {
|
|||
}
|
||||
|
||||
impl Font {
|
||||
pub fn new(db: &fontdb::Database, id: fontdb::ID) -> Option<Self> {
|
||||
pub fn new(db: &fontdb::Database, id: fontdb::ID, weight: fontdb::Weight) -> Option<Self> {
|
||||
let info = db.face(id)?;
|
||||
|
||||
let monospace_fallback = if cfg!(feature = "monospace_fallback") {
|
||||
|
|
@ -186,7 +186,19 @@ impl Font {
|
|||
(swash.offset, swash.key)
|
||||
},
|
||||
rustybuzz: OwnedFace::try_new(Arc::clone(&data), |data| {
|
||||
RustybuzzFace::from_slice((**data).as_ref(), info.index).ok_or(())
|
||||
RustybuzzFace::from_slice((**data).as_ref(), info.index)
|
||||
.ok_or(())
|
||||
.map(|mut face| {
|
||||
if let Some(axis) = face
|
||||
.variation_axes()
|
||||
.into_iter()
|
||||
.find(|axis| axis.tag == ttf_parser::Tag::from_bytes(b"wght"))
|
||||
{
|
||||
let wght = (weight.0 as f32).clamp(axis.min_value, axis.max_value);
|
||||
let _ = face.set_variation(ttf_parser::Tag::from_bytes(b"wght"), wght);
|
||||
}
|
||||
face
|
||||
})
|
||||
})
|
||||
.ok()?,
|
||||
#[cfg(not(feature = "peniko"))]
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ pub struct FontSystem {
|
|||
db: fontdb::Database,
|
||||
|
||||
/// Cache for loaded fonts from the database.
|
||||
font_cache: HashMap<fontdb::ID, Option<Arc<Font>>>,
|
||||
font_cache: HashMap<(fontdb::ID, fontdb::Weight), Option<Arc<Font>>>,
|
||||
|
||||
/// Sorted unique ID's of all Monospace fonts in DB
|
||||
monospace_font_ids: Vec<fontdb::ID>,
|
||||
|
|
@ -249,16 +249,16 @@ impl FontSystem {
|
|||
(self.locale, self.db)
|
||||
}
|
||||
|
||||
/// Get a font by its ID.
|
||||
pub fn get_font(&mut self, id: fontdb::ID) -> Option<Arc<Font>> {
|
||||
/// Get a font by its ID and weight.
|
||||
pub fn get_font(&mut self, id: fontdb::ID, weight: fontdb::Weight) -> Option<Arc<Font>> {
|
||||
self.font_cache
|
||||
.entry(id)
|
||||
.entry((id, weight))
|
||||
.or_insert_with(|| {
|
||||
#[cfg(feature = "std")]
|
||||
unsafe {
|
||||
self.db.make_shared_face_data(id);
|
||||
}
|
||||
match Font::new(&self.db, id) {
|
||||
match Font::new(&self.db, id, weight) {
|
||||
Some(font) => Some(Arc::new(font)),
|
||||
None => {
|
||||
log::warn!(
|
||||
|
|
@ -293,9 +293,10 @@ impl FontSystem {
|
|||
pub fn get_font_supported_codepoints_in_word(
|
||||
&mut self,
|
||||
id: fontdb::ID,
|
||||
weight: fontdb::Weight,
|
||||
word: &str,
|
||||
) -> Option<usize> {
|
||||
self.get_font(id).map(|font| {
|
||||
self.get_font(id, weight).map(|font| {
|
||||
let code_points = font.unicode_codepoints();
|
||||
let cache = self
|
||||
.font_codepoint_support_info_cache
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue