mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-08 08:20:05 +00:00
fix(text): load at most 64 faces from a font collection given as bytes
A collection header names its faces in four bytes each, so a small upload could declare any number of faces over the same tables; every one was parsed and kept its own character map under the font database's lock. The largest real collections hold a few dozen faces; 64 are loaded.
This commit is contained in:
@@ -640,6 +640,9 @@ struct FallbackLists {
|
||||
|
||||
/// The most styles whose fallback faces are kept (a file naming more starts the list again).
|
||||
const MAX_FALLBACK_STYLES: usize = 64;
|
||||
/// Most faces loaded from one font collection given as bytes (the largest real collections,
|
||||
/// the CJK ones, hold a few dozen).
|
||||
const MAX_COLLECTION_FACES: u32 = 64;
|
||||
|
||||
/// [`FontDb::key`]'s and [`FontDb::resolve`]'s answers for the families known in one generation.
|
||||
#[derive(Default)]
|
||||
@@ -711,7 +714,10 @@ impl FontDb {
|
||||
pub fn add_bytes(&mut self, bytes: Vec<u8>) -> Vec<Arc<Face>> {
|
||||
let shared = Arc::new(bytes);
|
||||
let count = match FileRef::new(shared.as_slice()) {
|
||||
Ok(FileRef::Collection(c)) => c.len(),
|
||||
// A collection's header may name any number of faces, each a few bytes pointing at
|
||||
// the same tables, and every face keeps its own character map: at most
|
||||
// [`MAX_COLLECTION_FACES`] of them are loaded.
|
||||
Ok(FileRef::Collection(c)) => c.len().min(MAX_COLLECTION_FACES),
|
||||
Ok(FileRef::Font(_)) => 1,
|
||||
Err(_) => 0,
|
||||
};
|
||||
|
||||
@@ -595,3 +595,15 @@ fn system_font_dirs_are_walked_once_each() {
|
||||
let unique: std::collections::HashSet<_> = dirs.iter().collect();
|
||||
assert_eq!(unique.len(), dirs.len(), "{dirs:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_collection_header_naming_billions_of_faces_loads_a_bounded_few() {
|
||||
// Four bytes per face offset: a header can name far more faces than the data holds, and each
|
||||
// one would be parsed (and keep a character map) in turn.
|
||||
let mut bytes = synthetic_font_collection(32);
|
||||
bytes[8..12].copy_from_slice(&u32::MAX.to_be_bytes());
|
||||
let start = std::time::Instant::now();
|
||||
let faces = FontDb::default().add_bytes(bytes);
|
||||
assert!(faces.len() <= 64, "{}", faces.len());
|
||||
assert!(start.elapsed() < std::time::Duration::from_secs(5), "{:?}", start.elapsed());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user