mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Merge pull request #3861 from FractalEncrypt/fix/ruby-font-fallback-stability
Fix false font fallback misses during concurrent loading
This commit is contained in:
@@ -2418,10 +2418,10 @@ impl FontDb {
|
|||||||
return Some(f);
|
return Some(f);
|
||||||
}
|
}
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
if self.system_fallback(c) {
|
self.system_fallback(c);
|
||||||
return self.loaded_fallback(c, exclude, weight, italic);
|
// Another lookup may have loaded the face while this one searched. Loading no new
|
||||||
}
|
// faces does not mean no face covers the character (as with `face`'s catalog lookup).
|
||||||
None
|
self.loaded_fallback(c, exclude, weight, italic)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A face that covers `c`, for text drawn outside the canvas (the app's own UI): the loaded
|
/// A face that covers `c`, for text drawn outside the canvas (the app's own UI): the loaded
|
||||||
|
|||||||
@@ -8,6 +8,48 @@ use crate::fontdb::fonts_outside;
|
|||||||
|
|
||||||
const FAMILY: &str = "Sysfont Sans3";
|
const FAMILY: &str = "Sysfont Sans3";
|
||||||
|
|
||||||
|
/// Concurrent first use must not report a missing glyph just because another lookup loaded it.
|
||||||
|
#[test]
|
||||||
|
fn concurrent_fallback_lookups_all_find_the_installed_face() {
|
||||||
|
const C: char = '\u{F0000}';
|
||||||
|
let dir = std::env::temp_dir().join(format!("vc-sysfonts-{}-concurrent-fallback", std::process::id()));
|
||||||
|
std::fs::create_dir_all(&dir).unwrap();
|
||||||
|
let base = named(&[(3, EN, 1, "Concurrent Sans"), (3, EN, 2, "Regular"), (3, EN, 6, "ConcurrentSans-Regular")]);
|
||||||
|
let font = skrifa::FontRef::new(&base).unwrap();
|
||||||
|
let mut builder = write_fonts::FontBuilder::new();
|
||||||
|
builder.add_raw(write_fonts::types::Tag::new(b"cmap"), crate::embed::cmap(&[(C as u32, 1)]).unwrap());
|
||||||
|
for r in font.table_directory.table_records() {
|
||||||
|
let tag = write_fonts::types::Tag::new(&r.tag().to_be_bytes());
|
||||||
|
if !builder.contains(tag) {
|
||||||
|
builder.add_raw(tag, font.table_data(r.tag()).unwrap().as_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::fs::write(dir.join("Concurrent.ttf"), builder.build()).unwrap();
|
||||||
|
for _ in 0..4 {
|
||||||
|
let db = FontDb::with_font_dirs(vec![dir.clone()]);
|
||||||
|
assert_eq!(db.load_system_fonts(), 1);
|
||||||
|
let start = std::sync::Barrier::new(16);
|
||||||
|
let faces = std::thread::scope(|scope| {
|
||||||
|
let workers: Vec<_> = (0..16)
|
||||||
|
.map(|_| {
|
||||||
|
scope.spawn(|| {
|
||||||
|
start.wait();
|
||||||
|
db.face_covering(C)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
workers.into_iter().map(|worker| worker.join().unwrap()).collect::<Vec<_>>()
|
||||||
|
});
|
||||||
|
assert!(
|
||||||
|
faces.iter().all(|face| face.as_ref().is_some_and(|face| face.family == "Concurrent Sans")),
|
||||||
|
"{} of 16 lookups missed the installed face",
|
||||||
|
faces.iter().filter(|face| face.is_none()).count()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
std::fs::remove_file(dir.join("Concurrent.ttf")).unwrap();
|
||||||
|
std::fs::remove_dir(dir).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
/// A bundled Source Sans 3 file renamed [`FAMILY`] (as long as the original name).
|
/// A bundled Source Sans 3 file renamed [`FAMILY`] (as long as the original name).
|
||||||
fn renamed(file: &str) -> Vec<u8> {
|
fn renamed(file: &str) -> Vec<u8> {
|
||||||
renamed_to(file, FAMILY)
|
renamed_to(file, FAMILY)
|
||||||
|
|||||||
Reference in New Issue
Block a user