From 78cf30e906afbaf35e9db046108a5b6fb725fde5 Mon Sep 17 00:00:00 2001 From: funman300 Date: Fri, 1 May 2026 18:22:32 +0000 Subject: [PATCH] fix(engine): silence usvg font-substitution warn spam MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bundled hayeah card SVGs declare font-family="Arial" for rank/suit text. usvg matches family names exactly, so on systems without Arial installed (every Linux distro by default) every text node bridged a log::warn! into our tracing output — 50+ lines per launch. Two-part fix: - svg_loader now populates a process-wide fontdb with system fonts (lazy via OnceLock) so substitution actually has faces to fall through to. usvg::Options::default() ships an empty fontdb, which meant text glyphs had nothing to fall back on at all. - LogPlugin extends DEFAULT_FILTER with usvg::text=error so the residual "no match" warns drop. The substitution itself works; the message is purely informational because Arial truly isn't installed. Co-Authored-By: Claude Opus 4.7 (1M context) --- solitaire_app/src/main.rs | 13 ++++++++++ solitaire_engine/src/assets/svg_loader.rs | 29 ++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/solitaire_app/src/main.rs b/solitaire_app/src/main.rs index 076c860..47ff8d3 100644 --- a/solitaire_app/src/main.rs +++ b/solitaire_app/src/main.rs @@ -100,6 +100,19 @@ fn main() { .set(bevy::asset::AssetPlugin { file_path: "../assets".to_string(), ..default() + }) + // The bundled hayeah card SVGs declare `font-family="Arial"` + // for rank/suit text. usvg compares family names exactly, + // so on systems without Arial installed (every Linux + // distro by default) it bridges a `log::warn!` per text + // node into our tracing output — 50+ lines per game on + // launch. The substitution path in `svg_loader::shared_fontdb` + // already resolves the glyphs to whatever sans-serif the + // user does have; the warn is purely informational and + // dropping it leaves real errors visible. + .set(bevy::log::LogPlugin { + filter: format!("{},usvg::text=error", bevy::log::DEFAULT_FILTER), + ..default() }), ) .add_plugins(AssetSourcesPlugin) diff --git a/solitaire_engine/src/assets/svg_loader.rs b/solitaire_engine/src/assets/svg_loader.rs index 1455aed..ec977ab 100644 --- a/solitaire_engine/src/assets/svg_loader.rs +++ b/solitaire_engine/src/assets/svg_loader.rs @@ -19,6 +19,8 @@ //! loading via `load_with_settings(...)`. The default of 512×768 is a //! safe fallback that fits a typical 2:3 playing card. +use std::sync::{Arc, OnceLock}; + use bevy::asset::io::Reader; use bevy::asset::{AssetLoader, LoadContext, RenderAssetUsages}; use bevy::image::Image; @@ -27,6 +29,7 @@ use bevy::reflect::TypePath; use bevy::render::render_resource::{Extent3d, TextureDimension, TextureFormat}; use serde::{Deserialize, Serialize}; use thiserror::Error; +use usvg::fontdb; /// Per-asset settings consumed by [`SvgLoader::load`]. /// @@ -102,7 +105,10 @@ impl AssetLoader for SvgLoader { /// thumbnail generators) can rasterise without going through the /// asset graph. pub fn rasterize_svg(svg_bytes: &[u8], target: UVec2) -> Result { - let opt = usvg::Options::default(); + let opt = usvg::Options { + fontdb: shared_fontdb(), + ..Default::default() + }; let tree = usvg::Tree::from_data(svg_bytes, &opt)?; let svg_size = tree.size(); @@ -140,6 +146,27 @@ pub fn rasterize_svg(svg_bytes: &[u8], target: UVec2) -> Result Arc { + static DB: OnceLock> = OnceLock::new(); + DB.get_or_init(|| { + let mut db = fontdb::Database::new(); + db.load_system_fonts(); + Arc::new(db) + }) + .clone() +} + #[cfg(test)] mod tests { use super::*;