mirror of
https://github.com/bevyengine/bevy.git
synced 2026-05-06 06:06:42 -04:00
Warn, not panic, on invalid fonts (#22491)
# Objective Fix panics due to invalid legacy fonts that we can't generate glyph images from. Fixes #22475 ## Solution * In `text_system` and `update_text2d_layout`, instead of panicking on `TextError::FailedToGetGlyphImage`, `warn_once` and clear the text layout. * Given an invalid font, Cosmic Text returns an invalid text layout with infinite text bounds. If Cosmic Text returns non-finite bounds, return a zero size from the `buffer_dimensions` function. * Added a `get_face_details` method to `CosmicFontSystem` to provide better info for the warning messages. `buffer_dimensions` probably should be reworked to return a `Result`, with the text systems emitting a warning on invalid layout bounds. Not so important though, left for a follow up. ## Testing The `system_fonts` example should no longer panic now. Instead the text with the invalid font is not rendered: <img width="869" height="166" alt="system-fonts-list-fixed" src="https://github.com/user-attachments/assets/a05cab45-68d2-453b-8fa6-16ec5b6bba76" /> One of the problematic fonts: https://github.com/justrajdeep/fonts/blob/master/NISC18030.ttf --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
@@ -259,9 +259,15 @@ pub fn update_text2d_layout(
|
||||
reprocess_queue.insert(entity);
|
||||
continue;
|
||||
}
|
||||
Err(e @ TextError::FailedToGetGlyphImage(key)) => {
|
||||
bevy_log::warn_once!(
|
||||
"{e}. Face: {:?}",
|
||||
font_system.get_face_details(key.font_id)
|
||||
);
|
||||
text_layout_info.clear();
|
||||
}
|
||||
Err(
|
||||
e @ (TextError::FailedToAddGlyph(_)
|
||||
| TextError::FailedToGetGlyphImage(_)
|
||||
| TextError::MissingAtlasLayout
|
||||
| TextError::MissingAtlasTexture
|
||||
| TextError::InconsistentAtlasState),
|
||||
|
||||
@@ -12,8 +12,8 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
||||
|
||||
use crate::{
|
||||
add_glyph_to_atlas, error::TextError, get_glyph_atlas_info, ComputedTextBlock, Font,
|
||||
FontAtlasKey, FontAtlasSet, FontHinting, FontSmoothing, FontSource, Justify, LineBreak,
|
||||
LineHeight, PositionedGlyph, TextBounds, TextEntity, TextFont, TextLayout,
|
||||
FontAtlasKey, FontAtlasSet, FontHinting, FontSmoothing, FontSource, FontStyle, FontWeight,
|
||||
Justify, LineBreak, LineHeight, PositionedGlyph, TextBounds, TextEntity, TextFont, TextLayout,
|
||||
};
|
||||
use cosmic_text::{Attrs, Buffer, Family, Metrics, Shaping, Wrap};
|
||||
|
||||
@@ -34,6 +34,83 @@ impl Default for CosmicFontSystem {
|
||||
}
|
||||
}
|
||||
|
||||
impl CosmicFontSystem {
|
||||
/// Get information about a font face, if it exists
|
||||
pub fn get_face_details(&self, id: cosmic_text::fontdb::ID) -> Option<FontFaceDetails> {
|
||||
self.0.db().face(id).map(FontFaceDetails::from)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Details about a Font Face
|
||||
pub struct FontFaceDetails {
|
||||
/// The path of the source file, if the font was loaded from a file.
|
||||
pub path: Option<std::path::PathBuf>,
|
||||
|
||||
/// The face's index in the font data.
|
||||
pub index: u32,
|
||||
|
||||
/// A list of family names.
|
||||
///
|
||||
/// Contains pairs of Name + Language. Where the first family is always English US,
|
||||
/// unless it's missing from the font.
|
||||
///
|
||||
/// Corresponds to a *Typographic Family* (ID 16) or a *Font Family* (ID 1) [name ID]
|
||||
/// in a TrueType font.
|
||||
///
|
||||
/// This is not an *Extended Typographic Family* or a *Full Name*.
|
||||
/// Meaning it will contain _Arial_ and not _Arial Bold_.
|
||||
///
|
||||
/// [name ID]: https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-ids
|
||||
pub families: Vec<(String, String)>,
|
||||
|
||||
/// A PostScript name.
|
||||
///
|
||||
/// Corresponds to a *PostScript name* (6) [name ID] in a TrueType font.
|
||||
///
|
||||
/// [name ID]: https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-ids
|
||||
pub post_script_name: String,
|
||||
|
||||
/// A font face style.
|
||||
pub style: FontStyle,
|
||||
|
||||
/// A font face weight.
|
||||
pub weight: FontWeight,
|
||||
|
||||
/// A font face stretch.
|
||||
pub stretch: u16,
|
||||
|
||||
/// Indicates that the font face is monospaced.
|
||||
pub monospaced: bool,
|
||||
}
|
||||
|
||||
impl From<&cosmic_text::fontdb::FaceInfo> for FontFaceDetails {
|
||||
fn from(face: &cosmic_text::fontdb::FaceInfo) -> Self {
|
||||
FontFaceDetails {
|
||||
path: match face.source {
|
||||
cosmic_text::fontdb::Source::Binary(_) => None,
|
||||
cosmic_text::fontdb::Source::File(ref path)
|
||||
| cosmic_text::fontdb::Source::SharedFile(ref path, _) => Some(path.clone()),
|
||||
},
|
||||
index: face.index,
|
||||
families: face
|
||||
.families
|
||||
.iter()
|
||||
.map(|(name, language)| (name.clone(), language.to_string()))
|
||||
.collect(),
|
||||
post_script_name: face.post_script_name.clone(),
|
||||
style: match face.style {
|
||||
cosmic_text::Style::Normal => FontStyle::Normal,
|
||||
cosmic_text::Style::Italic => FontStyle::Italic,
|
||||
cosmic_text::Style::Oblique => FontStyle::Oblique,
|
||||
},
|
||||
weight: FontWeight(face.weight.0),
|
||||
stretch: face.stretch.to_number(),
|
||||
monospaced: face.monospaced,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper resource around a [`cosmic_text::SwashCache`]
|
||||
///
|
||||
/// The swash cache rasterizer is used to rasterize glyphs
|
||||
@@ -511,7 +588,12 @@ fn buffer_dimensions(buffer: &Buffer) -> Vec2 {
|
||||
size.x = size.x.max(run.line_w);
|
||||
size.y += run.line_height;
|
||||
}
|
||||
size.ceil()
|
||||
|
||||
if size.is_finite() {
|
||||
size.ceil()
|
||||
} else {
|
||||
Vec2::ZERO
|
||||
}
|
||||
}
|
||||
|
||||
/// Discards stale data cached in `FontSystem`.
|
||||
|
||||
@@ -21,6 +21,7 @@ bevy_image = { path = "../bevy_image", version = "0.18.0-dev" }
|
||||
bevy_input = { path = "../bevy_input", version = "0.18.0-dev" }
|
||||
bevy_input_focus = { path = "../bevy_input_focus", version = "0.18.0-dev" }
|
||||
bevy_math = { path = "../bevy_math", version = "0.18.0-dev" }
|
||||
bevy_log = { path = "../bevy_log", version = "0.18.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.18.0-dev" }
|
||||
bevy_sprite = { path = "../bevy_sprite", version = "0.18.0-dev", features = [
|
||||
"bevy_text",
|
||||
|
||||
@@ -15,6 +15,7 @@ use bevy_ecs::{
|
||||
world::Ref,
|
||||
};
|
||||
use bevy_image::prelude::*;
|
||||
use bevy_log::warn_once;
|
||||
use bevy_math::Vec2;
|
||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
||||
use bevy_text::{
|
||||
@@ -369,9 +370,13 @@ pub fn text_system(
|
||||
// There was an error processing the text layout, try again next frame
|
||||
text_flags.needs_recompute = true;
|
||||
}
|
||||
Err(e @ TextError::FailedToGetGlyphImage(key)) => {
|
||||
warn_once!("{e}. Face: {:?}", font_system.get_face_details(key.font_id));
|
||||
text_flags.needs_recompute = false;
|
||||
text_layout_info.clear();
|
||||
}
|
||||
Err(
|
||||
e @ (TextError::FailedToAddGlyph(_)
|
||||
| TextError::FailedToGetGlyphImage(_)
|
||||
| TextError::MissingAtlasLayout
|
||||
| TextError::MissingAtlasTexture
|
||||
| TextError::InconsistentAtlasState),
|
||||
|
||||
Reference in New Issue
Block a user