mirror of
https://github.com/emilk/egui.git
synced 2026-08-29 04:40:03 -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)]
|
#![allow(clippy::mem_forget)]
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use emath::{GuiRounding as _, OrderedFloat, Vec2, vec2};
|
use emath::{GuiRounding as _, OrderedFloat, Vec2, vec2};
|
||||||
use self_cell::self_cell;
|
use self_cell::self_cell;
|
||||||
use skrifa::{
|
use skrifa::{
|
||||||
MetadataProvider as _,
|
MetadataProvider as _,
|
||||||
raw::{TableProvider as _, tables::kern::SubtableKind},
|
raw::{TableProvider as _, tables::kern::SubtableKind},
|
||||||
};
|
};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use vello_cpu::{color, kurbo};
|
use vello_cpu::{color, kurbo};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -201,6 +200,7 @@ impl FontCell {
|
|||||||
metrics: &ScaledMetrics,
|
metrics: &ScaledMetrics,
|
||||||
glyph_info: &GlyphInfo,
|
glyph_info: &GlyphInfo,
|
||||||
bin: SubpixelBin,
|
bin: SubpixelBin,
|
||||||
|
location: &skrifa::instance::Location,
|
||||||
) -> Option<GlyphAllocation> {
|
) -> Option<GlyphAllocation> {
|
||||||
let glyph_id = glyph_info.id?;
|
let glyph_id = glyph_info.id?;
|
||||||
|
|
||||||
@@ -225,7 +225,7 @@ impl FontCell {
|
|||||||
.reconfigure(
|
.reconfigure(
|
||||||
&font_data.outline_glyphs,
|
&font_data.outline_glyphs,
|
||||||
size,
|
size,
|
||||||
skrifa::instance::LocationRef::default(),
|
location,
|
||||||
skrifa::outline::Target::Smooth {
|
skrifa::outline::Target::Smooth {
|
||||||
mode: skrifa::outline::SmoothMode::Normal,
|
mode: skrifa::outline::SmoothMode::Normal,
|
||||||
symmetric_rendering: true,
|
symmetric_rendering: true,
|
||||||
@@ -239,7 +239,7 @@ impl FontCell {
|
|||||||
} else {
|
} else {
|
||||||
let draw_settings = skrifa::outline::DrawSettings::unhinted(
|
let draw_settings = skrifa::outline::DrawSettings::unhinted(
|
||||||
skrifa::instance::Size::new(metrics.scale),
|
skrifa::instance::Size::new(metrics.scale),
|
||||||
skrifa::instance::LocationRef::default(),
|
location,
|
||||||
);
|
);
|
||||||
outline.draw(draw_settings, &mut pen).ok()?;
|
outline.draw(draw_settings, &mut pen).ok()?;
|
||||||
}
|
}
|
||||||
@@ -336,6 +336,12 @@ pub struct FontFace {
|
|||||||
name: String,
|
name: String,
|
||||||
font: FontCell,
|
font: FontCell,
|
||||||
tweak: FontTweak,
|
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_info_cache: ahash::HashMap<char, GlyphInfo>,
|
||||||
glyph_alloc_cache: ahash::HashMap<GlyphCacheKey, GlyphAllocation>,
|
glyph_alloc_cache: ahash::HashMap<GlyphCacheKey, GlyphAllocation>,
|
||||||
}
|
}
|
||||||
@@ -347,6 +353,7 @@ impl FontFace {
|
|||||||
font_data: Blob,
|
font_data: Blob,
|
||||||
index: u32,
|
index: u32,
|
||||||
tweak: FontTweak,
|
tweak: FontTweak,
|
||||||
|
preferred_weight: Option<u16>,
|
||||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
let font = FontCell::try_new(font_data, |font_data| {
|
let font = FontCell::try_new(font_data, |font_data| {
|
||||||
let skrifa_font =
|
let skrifa_font =
|
||||||
@@ -354,6 +361,10 @@ impl FontFace {
|
|||||||
|
|
||||||
let charmap = skrifa_font.charmap();
|
let charmap = skrifa_font.charmap();
|
||||||
let glyphs = skrifa_font.outline_glyphs();
|
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(
|
let metrics = skrifa_font.metrics(
|
||||||
skrifa::instance::Size::unscaled(),
|
skrifa::instance::Size::unscaled(),
|
||||||
skrifa::instance::LocationRef::default(),
|
skrifa::instance::LocationRef::default(),
|
||||||
@@ -387,15 +398,56 @@ impl FontFace {
|
|||||||
hinting_instance,
|
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 {
|
Ok(Self {
|
||||||
name,
|
name,
|
||||||
font,
|
font,
|
||||||
tweak,
|
tweak,
|
||||||
|
weight,
|
||||||
|
location,
|
||||||
glyph_info_cache: Default::default(),
|
glyph_info_cache: Default::default(),
|
||||||
glyph_alloc_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.
|
/// Code points that will always be replaced by the replacement character.
|
||||||
///
|
///
|
||||||
/// See also [`invisible_char`].
|
/// See also [`invisible_char`].
|
||||||
@@ -585,7 +637,7 @@ impl FontFace {
|
|||||||
|
|
||||||
let allocation = self
|
let allocation = self
|
||||||
.font
|
.font
|
||||||
.allocate_glyph_uncached(atlas, metrics, &glyph_info, bin)
|
.allocate_glyph_uncached(atlas, metrics, &glyph_info, bin, &self.location)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
entry.insert(allocation);
|
entry.insert(allocation);
|
||||||
|
|||||||
@@ -125,6 +125,12 @@ pub struct FontData {
|
|||||||
|
|
||||||
/// Extra scale and vertical tweak to apply to all text of this font.
|
/// Extra scale and vertical tweak to apply to all text of this font.
|
||||||
pub tweak: FontTweak,
|
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 {
|
impl FontData {
|
||||||
@@ -133,6 +139,7 @@ impl FontData {
|
|||||||
font: Cow::Borrowed(font),
|
font: Cow::Borrowed(font),
|
||||||
index: 0,
|
index: 0,
|
||||||
tweak: Default::default(),
|
tweak: Default::default(),
|
||||||
|
weight: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,12 +148,43 @@ impl FontData {
|
|||||||
font: Cow::Owned(font),
|
font: Cow::Owned(font),
|
||||||
index: 0,
|
index: 0,
|
||||||
tweak: Default::default(),
|
tweak: Default::default(),
|
||||||
|
weight: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tweak(self, tweak: FontTweak) -> Self {
|
pub fn tweak(self, tweak: FontTweak) -> Self {
|
||||||
Self { tweak, ..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 {
|
impl AsRef<[u8]> for FontData {
|
||||||
@@ -771,8 +809,15 @@ impl FontsImpl {
|
|||||||
for (name, font_data) in &definitions.font_data {
|
for (name, font_data) in &definitions.font_data {
|
||||||
let tweak = font_data.tweak;
|
let tweak = font_data.tweak;
|
||||||
let blob = blob_from_font_data(font_data);
|
let blob = blob_from_font_data(font_data);
|
||||||
let font_face = FontFace::new(options, name.clone(), blob, font_data.index, tweak)
|
let font_face = FontFace::new(
|
||||||
.unwrap_or_else(|err| panic!("Error parsing {name:?} TTF/OTF font file: {err}"));
|
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();
|
let key = FontFaceKey::new();
|
||||||
fonts_by_id.insert(key, font_face);
|
fonts_by_id.insert(key, font_face);
|
||||||
fonts_by_name.insert(name.clone(), key);
|
fonts_by_name.insert(name.clone(), key);
|
||||||
@@ -816,6 +861,26 @@ impl FontsImpl {
|
|||||||
atlas: &mut self.atlas,
|
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