From 843ffb6507ed37185282eead34d0f75cb9e746ef Mon Sep 17 00:00:00 2001 From: Tanish Manku Date: Wed, 4 Nov 2026 19:58:44 -0700 Subject: [PATCH] Fonts: a registered font outlives a rescan, also beside a file of the same stem (CodeRabbit on #2803) --- crates/fonts/src/ttf.rs | 14 +++++++++++--- crates/fonts/tests/ttf_font_folders.rs | 6 ++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/crates/fonts/src/ttf.rs b/crates/fonts/src/ttf.rs index 29a8b686c..2f4ffab24 100644 --- a/crates/fonts/src/ttf.rs +++ b/crates/fonts/src/ttf.rs @@ -245,6 +245,9 @@ struct Db { paths: HashMap, /// Loaded or registered font file bytes, by lower-case file stem. files: HashMap>>, + /// The keys of `files` given by [`register`]: kept over a file of the same stem, and by a + /// rescan. + registered: std::collections::HashSet, /// Name lookups already answered, found or not. resolved: HashMap>, fallback: Fallback, @@ -454,9 +457,11 @@ pub fn set_font_dirs>(dirs: &[S]) { /// Forget what was found and look for fonts again (files added to a font folder). pub fn rescan() { let mut g = lock(); - // Fonts registered by name (no file behind them) stay. + // Fonts registered by name stay, also where a found file has the same stem; bytes read + // from found files go. let paths = std::mem::take(&mut g.paths); - g.files.retain(|k, _| !paths.contains_key(k)); + let Db { files, registered, .. } = &mut *g; + files.retain(|k, _| registered.contains(k) || !paths.contains_key(k)); g.scanned = false; g.resolved.clear(); g.fallback = Fallback::default(); @@ -797,7 +802,9 @@ pub(crate) fn rank_fallback(stems: &[String]) -> Vec { /// Register font bytes under a name (web builds and tests). pub fn register(name: &str, bytes: Vec) { let mut g = lock(); - g.files.insert(key(name), Arc::new(bytes)); + let k = key(name); + g.files.insert(k.clone(), Arc::new(bytes)); + g.registered.insert(k); g.resolved.clear(); g.fallback = Fallback::default(); } @@ -809,6 +816,7 @@ pub(crate) fn register_static(name: &str, bytes: &'static [u8]) { let mut g = lock(); let k = key(name); g.files.remove(&k); + g.registered.remove(&k); g.paths.insert(k, Source::Static(bytes)); g.resolved.clear(); g.fallback = Fallback::default(); diff --git a/crates/fonts/tests/ttf_font_folders.rs b/crates/fonts/tests/ttf_font_folders.rs index 6ad257788..e8e3debda 100644 --- a/crates/fonts/tests/ttf_font_folders.rs +++ b/crates/fonts/tests/ttf_font_folders.rs @@ -42,6 +42,11 @@ fn a_font_in_a_user_folder_is_found_listed_and_dropped_with_the_folder() { if !copy_into(&src, &dir) { return; } + // A font registered by name, with a file of the same stem in the folder: looking for fonts + // again (twice, as the folder comes and goes) keeps the registered bytes. + let Ok(bytes) = std::fs::read(&src) else { return }; + let _ = std::fs::copy(&src, dir.join("CadcraftRegistered.ttf")); + ttf::register("CadcraftRegistered.ttf", bytes); let before = ttf::generation(); ttf::set_font_dirs(&[dir.to_string_lossy().to_string()]); assert!(ttf::generation() > before); @@ -55,5 +60,6 @@ fn a_font_in_a_user_folder_is_found_listed_and_dropped_with_the_folder() { } ttf::set_font_dirs::<&str>(&[]); assert!(ttf::find("CadcraftTestFace.ttf").is_none()); + assert!(ttf::find("CadcraftRegistered.ttf").is_some(), "a registered font outlives a rescan"); let _ = std::fs::remove_dir_all(&dir); }