mirror of
https://github.com/emilk/egui.git
synced 2026-06-26 14:49:06 -04:00
Apply preferred font weight when loading variable fonts (#7790)
Previously, when loading a variable font (e.g. via `egui::FontData::from_static`), the font was rendered using the default (often the lightest) weight, ignoring any preferred weight configuration. This change applies the specified weight to skrifa's `Location` for the `wght` axis, ensuring that variable fonts are rendered with the intended font weight. ## Summary Fixes variable font weight not being applied during rendering. The `FontData::weight()` method now properly configures the font variation axis. ## Changes - Add `location: Location` field to `FontFace` to store variation coordinates - Pass `location` parameter through to glyph rendering functions - Apply weight to skrifa's `LocationRef` in `DrawSettings` and `HintingInstance` ## Weight Priority 1. `preferred_weight` from `FontData::weight()` 2. OS/2 table's `us_weight_class` 3. Variable font's fvar default value 4. `Location::default()` ## Related Issue - #3218 : Not follow font id, but goal would be same ## Todo * [x] Apply preferred font weight when loading variable fonts * [ ] Add small size variable fonts for docs and egui (need discussion)
This commit is contained in:
@@ -1,13 +1,12 @@
|
||||
#![allow(clippy::mem_forget)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use emath::{GuiRounding as _, OrderedFloat, Vec2, vec2};
|
||||
use self_cell::self_cell;
|
||||
use skrifa::{
|
||||
MetadataProvider as _,
|
||||
raw::{TableProvider as _, tables::kern::SubtableKind},
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
use vello_cpu::{color, kurbo};
|
||||
|
||||
use crate::{
|
||||
@@ -201,6 +200,7 @@ impl FontCell {
|
||||
metrics: &ScaledMetrics,
|
||||
glyph_info: &GlyphInfo,
|
||||
bin: SubpixelBin,
|
||||
location: &skrifa::instance::Location,
|
||||
) -> Option<GlyphAllocation> {
|
||||
let glyph_id = glyph_info.id?;
|
||||
|
||||
@@ -225,7 +225,7 @@ impl FontCell {
|
||||
.reconfigure(
|
||||
&font_data.outline_glyphs,
|
||||
size,
|
||||
skrifa::instance::LocationRef::default(),
|
||||
location,
|
||||
skrifa::outline::Target::Smooth {
|
||||
mode: skrifa::outline::SmoothMode::Normal,
|
||||
symmetric_rendering: true,
|
||||
@@ -239,7 +239,7 @@ impl FontCell {
|
||||
} else {
|
||||
let draw_settings = skrifa::outline::DrawSettings::unhinted(
|
||||
skrifa::instance::Size::new(metrics.scale),
|
||||
skrifa::instance::LocationRef::default(),
|
||||
location,
|
||||
);
|
||||
outline.draw(draw_settings, &mut pen).ok()?;
|
||||
}
|
||||
@@ -336,6 +336,12 @@ pub struct FontFace {
|
||||
name: String,
|
||||
font: FontCell,
|
||||
tweak: FontTweak,
|
||||
|
||||
/// The font weight (100-900) if available from the font file.
|
||||
weight: Option<u16>,
|
||||
|
||||
/// Variable font location (for weight axis, etc.)
|
||||
location: skrifa::instance::Location,
|
||||
glyph_info_cache: ahash::HashMap<char, GlyphInfo>,
|
||||
glyph_alloc_cache: ahash::HashMap<GlyphCacheKey, GlyphAllocation>,
|
||||
}
|
||||
@@ -347,6 +353,7 @@ impl FontFace {
|
||||
font_data: Blob,
|
||||
index: u32,
|
||||
tweak: FontTweak,
|
||||
preferred_weight: Option<u16>,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let font = FontCell::try_new(font_data, |font_data| {
|
||||
let skrifa_font =
|
||||
@@ -354,6 +361,10 @@ impl FontFace {
|
||||
|
||||
let charmap = skrifa_font.charmap();
|
||||
let glyphs = skrifa_font.outline_glyphs();
|
||||
|
||||
// Note: We use default location here during initialization because
|
||||
// the actual weight will be applied via the stored location during rendering.
|
||||
// The metrics won't be significantly different at this unscaled size.
|
||||
let metrics = skrifa_font.metrics(
|
||||
skrifa::instance::Size::unscaled(),
|
||||
skrifa::instance::LocationRef::default(),
|
||||
@@ -387,15 +398,56 @@ impl FontFace {
|
||||
hinting_instance,
|
||||
})
|
||||
})?;
|
||||
|
||||
// Use preferred_weight if provided, otherwise try to read from the OS/2 table or fvar default
|
||||
let weight = preferred_weight.or_else(|| {
|
||||
// First try OS/2 table
|
||||
if let Some(w) = font
|
||||
.borrow_dependent()
|
||||
.skrifa
|
||||
.os2()
|
||||
.ok()
|
||||
.map(|os2| os2.us_weight_class())
|
||||
{
|
||||
return Some(w);
|
||||
}
|
||||
// If no OS/2 or preferred_weight, try to get default from variable font's fvar table
|
||||
font.borrow_dependent()
|
||||
.skrifa
|
||||
.axes()
|
||||
.iter()
|
||||
.find(|axis| axis.tag() == skrifa::raw::types::Tag::new(b"wght"))
|
||||
.map(|axis| axis.default_value() as u16)
|
||||
});
|
||||
|
||||
// Create location for variable font with weight axis
|
||||
// If weight is provided (either from preferred_weight, OS/2, or fvar default), use it
|
||||
// Otherwise fall back to Location::default() which uses all axis defaults
|
||||
let location = if let Some(w) = weight {
|
||||
font.borrow_dependent()
|
||||
.skrifa
|
||||
.axes()
|
||||
.location([("wght", w as f32)])
|
||||
} else {
|
||||
skrifa::instance::Location::default()
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
name,
|
||||
font,
|
||||
tweak,
|
||||
weight,
|
||||
location,
|
||||
glyph_info_cache: Default::default(),
|
||||
glyph_alloc_cache: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the font weight (100-900) if available from the font file.
|
||||
pub fn weight(&self) -> Option<u16> {
|
||||
self.weight
|
||||
}
|
||||
|
||||
/// Code points that will always be replaced by the replacement character.
|
||||
///
|
||||
/// See also [`invisible_char`].
|
||||
@@ -585,7 +637,7 @@ impl FontFace {
|
||||
|
||||
let allocation = self
|
||||
.font
|
||||
.allocate_glyph_uncached(atlas, metrics, &glyph_info, bin)
|
||||
.allocate_glyph_uncached(atlas, metrics, &glyph_info, bin, &self.location)
|
||||
.unwrap_or_default();
|
||||
|
||||
entry.insert(allocation);
|
||||
|
||||
@@ -125,6 +125,12 @@ pub struct FontData {
|
||||
|
||||
/// Extra scale and vertical tweak to apply to all text of this font.
|
||||
pub tweak: FontTweak,
|
||||
|
||||
/// The font weight (100-900), if available.
|
||||
/// Standard values: 100 (Thin), 200 (Extra Light), 300 (Light), 400 (Regular),
|
||||
/// 500 (Medium), 600 (Semi Bold), 700 (Bold), 800 (Extra Bold), 900 (Black).
|
||||
/// `None` if the weight could not be determined.
|
||||
pub weight: Option<u16>,
|
||||
}
|
||||
|
||||
impl FontData {
|
||||
@@ -133,6 +139,7 @@ impl FontData {
|
||||
font: Cow::Borrowed(font),
|
||||
index: 0,
|
||||
tweak: Default::default(),
|
||||
weight: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,12 +148,43 @@ impl FontData {
|
||||
font: Cow::Owned(font),
|
||||
index: 0,
|
||||
tweak: Default::default(),
|
||||
weight: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tweak(self, tweak: FontTweak) -> Self {
|
||||
Self { tweak, ..self }
|
||||
}
|
||||
|
||||
/// Set the font weight (100-900).
|
||||
///
|
||||
/// This is typically read automatically from the font file when loaded,
|
||||
/// but can be overridden manually if needed.
|
||||
///
|
||||
/// Standard weight values:
|
||||
/// - 100: Thin
|
||||
/// - 200: Extra Light
|
||||
/// - 300: Light
|
||||
/// - 400: Regular/Normal
|
||||
/// - 500: Medium
|
||||
/// - 600: Semi Bold
|
||||
/// - 700: Bold
|
||||
/// - 800: Extra Bold
|
||||
/// - 900: Black
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use epaint::text::FontData;
|
||||
/// let font_data = FontData::from_static(include_bytes!("../../../epaint_default_fonts/fonts/Ubuntu-Light.ttf"))
|
||||
/// .weight(300); // Override to Light weight
|
||||
/// assert_eq!(font_data.weight, Some(300));
|
||||
/// ```
|
||||
pub fn weight(self, weight: u16) -> Self {
|
||||
Self {
|
||||
weight: Some(weight),
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for FontData {
|
||||
@@ -771,8 +809,15 @@ impl FontsImpl {
|
||||
for (name, font_data) in &definitions.font_data {
|
||||
let tweak = font_data.tweak;
|
||||
let blob = blob_from_font_data(font_data);
|
||||
let font_face = FontFace::new(options, name.clone(), blob, font_data.index, tweak)
|
||||
.unwrap_or_else(|err| panic!("Error parsing {name:?} TTF/OTF font file: {err}"));
|
||||
let font_face = FontFace::new(
|
||||
options,
|
||||
name.clone(),
|
||||
blob,
|
||||
font_data.index,
|
||||
tweak,
|
||||
font_data.weight,
|
||||
)
|
||||
.unwrap_or_else(|err| panic!("Error parsing {name:?} TTF/OTF font file: {err}"));
|
||||
let key = FontFaceKey::new();
|
||||
fonts_by_id.insert(key, font_face);
|
||||
fonts_by_name.insert(name.clone(), key);
|
||||
@@ -816,6 +861,26 @@ impl FontsImpl {
|
||||
atlas: &mut self.atlas,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the weight of a font by name, if available.
|
||||
///
|
||||
/// Returns the weight value (100-900) read from the font file's OS/2 table,
|
||||
/// or `None` if the font is not found or doesn't contain weight information.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use epaint::text::{FontDefinitions, FontsImpl};
|
||||
/// # use epaint::TextOptions;
|
||||
/// let fonts_impl = FontsImpl::new(TextOptions::default(), FontDefinitions::default());
|
||||
/// if let Some(weight) = fonts_impl.font_weight("Hack") {
|
||||
/// println!("Hack font weight: {}", weight);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn font_weight(&self, font_name: &str) -> Option<u16> {
|
||||
let key = self.fonts_by_name.get(font_name)?;
|
||||
let font_face = self.fonts_by_id.get(key)?;
|
||||
font_face.weight()
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user