Skip to content

Commit df7cedc

Browse files
authored
Fontique: add load_system_fonts and load_fonts_from_paths methods (#540)
Adds additional methods with the aim of making `fontique` a suitable replacement for `fontdb` in resvg: - `load_system_fonts` loads system fonts into a `Collection` that did not specify loading them at creation time - `load_fonts_from_paths` loads all fonts from the specified directory(s) --------- Signed-off-by: Nico Burns <nico@nicoburns.com>
1 parent ac25155 commit df7cedc

File tree

1 file changed

+94
-17
lines changed

1 file changed

+94
-17
lines changed

fontique/src/collection/mod.rs

Lines changed: 94 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ use alloc::{string::String, sync::Arc, vec::Vec};
2626
use hashbrown::HashMap;
2727
use read_fonts::types::NameId;
2828
#[cfg(feature = "std")]
29+
use std::path::Path;
30+
#[cfg(feature = "std")]
2931
use std::sync::{Mutex, atomic::Ordering};
3032

3133
type FamilyMap = HashMap<FamilyId, Option<FamilyInfo>>;
@@ -83,6 +85,19 @@ impl Collection {
8385
}
8486
}
8587

88+
/// Load system fonts. If system fonts are already loaded then this does nothing.
89+
pub fn load_system_fonts(&mut self) {
90+
if self.inner.system.is_none() {
91+
self.inner.load_system_fonts();
92+
}
93+
}
94+
95+
/// Loads all fonts contained within the specified directory(s)
96+
#[cfg(feature = "std")]
97+
pub fn load_fonts_from_paths(&mut self, paths: impl IntoIterator<Item = impl AsRef<Path>>) {
98+
self.inner.load_fonts_from_paths(paths);
99+
}
100+
86101
/// Returns an iterator over all available family names in the collection.
87102
///
88103
/// If `fontique` was compiled with the `"system"` feature, then it will
@@ -239,6 +254,11 @@ impl Inner {
239254
}
240255
}
241256

257+
/// Load system fonts. If system fonts are already loaded then they will be reloaded.
258+
pub fn load_system_fonts(&mut self) {
259+
self.system = Some(System::new());
260+
}
261+
242262
/// Returns an iterator over all available family names in the collection.
243263
///
244264
/// This includes both system and registered fonts.
@@ -443,6 +463,20 @@ impl Inner {
443463
self.data.fallbacks.append(key, families)
444464
}
445465

466+
/// Loads all fonts that exist in the specified directory(s)
467+
#[cfg(feature = "std")]
468+
pub fn load_fonts_from_paths(&mut self, paths: impl IntoIterator<Item = impl AsRef<Path>>) {
469+
#[cfg(feature = "std")]
470+
if let Some(shared) = &self.shared {
471+
shared.data.lock().unwrap().load_fonts_from_paths(paths);
472+
shared.bump_version();
473+
} else {
474+
self.data.load_fonts_from_paths(paths);
475+
}
476+
#[cfg(not(feature = "std"))]
477+
self.data.register_fonts(paths)
478+
}
479+
446480
/// Registers all fonts that exist in the given data.
447481
///
448482
/// Returns a list of pairs each containing the family identifier and fonts
@@ -626,16 +660,64 @@ struct CommonData {
626660
}
627661

628662
impl CommonData {
663+
#[cfg(feature = "std")]
664+
fn load_fonts_from_paths(&mut self, paths: impl IntoIterator<Item = impl AsRef<Path>>) {
665+
let mut families: HashMap<FamilyId, (FamilyName, Vec<FontInfo>)> = HashMap::default();
666+
let mut scratch_family_name = String::default();
667+
crate::scan::scan_paths(paths, 16, |scanned_font| {
668+
let source = SourceInfo {
669+
id: SourceId::new(),
670+
kind: SourceKind::Path(Arc::from(scanned_font.path.unwrap())),
671+
};
672+
673+
let font_data = scanned_font.font.data().as_bytes();
674+
self.register_font_impl(
675+
font_data,
676+
source,
677+
None,
678+
&mut scratch_family_name,
679+
&mut families,
680+
);
681+
});
682+
}
683+
629684
fn register_fonts(
630685
&mut self,
631686
data: Blob<u8>,
632687
info_override: Option<FontInfoOverride<'_>>,
633688
) -> Vec<(FamilyId, Vec<FontInfo>)> {
634689
let mut families: HashMap<FamilyId, (FamilyName, Vec<FontInfo>)> = HashMap::default();
635-
let mut family_name = String::default();
636-
let data_id = SourceId::new();
637-
super::scan::scan_memory(data.as_ref(), |scanned_font| {
638-
family_name.clear();
690+
let mut scratch_family_name = String::default();
691+
692+
let source = SourceInfo {
693+
id: SourceId::new(),
694+
kind: SourceKind::Memory(data.clone()),
695+
};
696+
697+
self.register_font_impl(
698+
data.as_ref(),
699+
source,
700+
info_override,
701+
&mut scratch_family_name,
702+
&mut families,
703+
);
704+
705+
families
706+
.into_iter()
707+
.map(|(id, (_, fonts))| (id, fonts))
708+
.collect()
709+
}
710+
711+
fn register_font_impl(
712+
&mut self,
713+
font_data: &[u8],
714+
source: SourceInfo,
715+
info_override: Option<FontInfoOverride<'_>>,
716+
scratch_family_name: &mut String,
717+
families: &mut HashMap<FamilyId, (FamilyName, Vec<FontInfo>)>,
718+
) {
719+
super::scan::scan_memory(font_data, |scanned_font| {
720+
scratch_family_name.clear();
639721

640722
let family_name =
641723
if let Some(override_family_name) = info_override.and_then(|o| o.family_name) {
@@ -648,19 +730,18 @@ impl CommonData {
648730
let Some(family_chars) = family_chars else {
649731
return;
650732
};
651-
family_name.extend(family_chars);
652-
&family_name
733+
scratch_family_name.extend(family_chars);
734+
735+
#[allow(clippy::needless_borrow)] // false positive
736+
&scratch_family_name
653737
};
654738

655739
if family_name.is_empty() {
656740
return;
657741
}
658-
let data = SourceInfo {
659-
id: data_id,
660-
kind: SourceKind::Memory(data.clone()),
661-
};
742+
662743
let Some(mut font) =
663-
FontInfo::from_font_ref(&scanned_font.font, data, scanned_font.index)
744+
FontInfo::from_font_ref(&scanned_font.font, source.clone(), scanned_font.index)
664745
else {
665746
return;
666747
};
@@ -669,14 +750,14 @@ impl CommonData {
669750
font.apply_override(info_override);
670751
}
671752

672-
let name = self.family_names.get_or_insert(family_name);
753+
let name = self.family_names.get_or_insert(scratch_family_name);
673754
families
674755
.entry(name.id())
675756
.or_insert_with(|| (name, Vec::default()))
676757
.1
677758
.push(font);
678759
});
679-
for (id, (name, fonts)) in &families {
760+
for (id, (name, fonts)) in families.iter() {
680761
if let Some(Some(family)) = self.families.get_mut(id) {
681762
let new_fonts = family.fonts().iter().chain(fonts).cloned();
682763
*family = FamilyInfo::new(name.clone(), new_fonts);
@@ -685,10 +766,6 @@ impl CommonData {
685766
self.families.insert(*id, Some(family));
686767
}
687768
}
688-
families
689-
.into_iter()
690-
.map(|(id, (_, fonts))| (id, fonts))
691-
.collect()
692769
}
693770

694771
fn unregister_font(

0 commit comments

Comments
 (0)