text_typeset/font/
registry.rs1use std::sync::Arc;
2
3use fontdb::{Database, Family, Query, Source, Style, Weight};
4
5use crate::types::FontFaceId;
6
7pub struct FontEntry {
8 pub fontdb_id: fontdb::ID,
9 pub face_index: u32,
10 pub data: Arc<Vec<u8>>,
11 pub swash_cache_key: swash::CacheKey,
12}
13
14pub struct FontRegistry {
15 fontdb: Database,
16 fonts: Vec<Option<FontEntry>>,
17 generic_families: std::collections::HashMap<String, String>,
18 default_font: Option<FontFaceId>,
19 default_size_px: f32,
20}
21
22impl Default for FontRegistry {
23 fn default() -> Self {
24 Self::new()
25 }
26}
27
28impl FontRegistry {
29 pub fn new() -> Self {
30 Self {
31 fontdb: Database::new(),
32 fonts: Vec::new(),
33 generic_families: std::collections::HashMap::new(),
34 default_font: None,
35 default_size_px: 16.0,
36 }
37 }
38
39 pub fn register_font(&mut self, data: &[u8]) -> Vec<FontFaceId> {
42 let arc_data: Arc<Vec<u8>> = Arc::new(data.to_vec());
43 let source = Source::Binary(arc_data.clone());
44 let fontdb_ids = self.fontdb.load_font_source(source);
45
46 let mut face_ids = Vec::new();
47 for fontdb_id in fontdb_ids {
48 let face_index = self.fontdb.face(fontdb_id).map(|f| f.index).unwrap_or(0);
49
50 let swash_cache_key = swash::CacheKey::new();
51 let entry = FontEntry {
52 fontdb_id,
53 face_index,
54 data: arc_data.clone(),
55 swash_cache_key,
56 };
57
58 let face_id = FontFaceId(self.fonts.len() as u32);
59 self.fonts.push(Some(entry));
60 face_ids.push(face_id);
61 }
62 face_ids
63 }
64
65 pub fn register_font_as(
67 &mut self,
68 data: &[u8],
69 family: &str,
70 weight: u16,
71 italic: bool,
72 ) -> Vec<FontFaceId> {
73 let arc_data: Arc<Vec<u8>> = Arc::new(data.to_vec());
74 let source = Source::Binary(arc_data.clone());
75 let fontdb_ids = self.fontdb.load_font_source(source);
76
77 let mut face_ids = Vec::new();
78 for fontdb_id in fontdb_ids {
79 if let Some(face_info) = self.fontdb.face(fontdb_id) {
81 let mut info = face_info.clone();
82 info.families = vec![(family.to_string(), fontdb::Language::English_UnitedStates)];
83 info.weight = Weight(weight);
84 info.style = if italic { Style::Italic } else { Style::Normal };
85 let face_index = info.index;
87 self.fontdb.remove_face(fontdb_id);
88 let new_id = self.fontdb.push_face_info(info);
89
90 let swash_cache_key = swash::CacheKey::new();
91 let entry = FontEntry {
92 fontdb_id: new_id,
93 face_index,
94 data: arc_data.clone(),
95 swash_cache_key,
96 };
97
98 let face_id = FontFaceId(self.fonts.len() as u32);
99 self.fonts.push(Some(entry));
100 face_ids.push(face_id);
101 }
102 }
103 face_ids
104 }
105
106 pub fn set_default_font(&mut self, face: FontFaceId, size_px: f32) {
107 self.default_font = Some(face);
108 self.default_size_px = size_px;
109 }
110
111 pub fn default_font(&self) -> Option<FontFaceId> {
112 self.default_font
113 }
114
115 pub fn default_size_px(&self) -> f32 {
116 self.default_size_px
117 }
118
119 pub fn set_generic_family(&mut self, generic: &str, family: &str) {
120 self.generic_families
121 .insert(generic.to_string(), family.to_string());
122 }
123
124 pub fn resolve_family_name<'a>(&'a self, family: &'a str) -> &'a str {
127 self.generic_families
128 .get(family)
129 .map(|s| s.as_str())
130 .unwrap_or(family)
131 }
132
133 pub fn query_font(&self, family: &str, weight: u16, italic: bool) -> Option<FontFaceId> {
135 let resolved = self.resolve_family_name(family);
136 let style = if italic { Style::Italic } else { Style::Normal };
137
138 let query = Query {
139 families: &[Family::Name(resolved)],
140 weight: Weight(weight),
141 style,
142 ..Query::default()
143 };
144
145 let fontdb_id = self.fontdb.query(&query)?;
146 self.fontdb_id_to_face_id(fontdb_id)
147 }
148
149 pub fn get(&self, face_id: FontFaceId) -> Option<&FontEntry> {
151 self.fonts
152 .get(face_id.0 as usize)
153 .and_then(|opt| opt.as_ref())
154 }
155
156 pub fn query_variant(
159 &self,
160 base_face: FontFaceId,
161 weight: u16,
162 italic: bool,
163 ) -> Option<FontFaceId> {
164 let entry = self.get(base_face)?;
165 let face_info = self.fontdb.face(entry.fontdb_id)?;
166 let family_name = face_info.families.first().map(|(name, _)| name.as_str())?;
167 self.query_font(family_name, weight, italic)
168 }
169
170 fn fontdb_id_to_face_id(&self, fontdb_id: fontdb::ID) -> Option<FontFaceId> {
172 self.fonts.iter().enumerate().find_map(|(i, entry)| {
173 entry
174 .as_ref()
175 .filter(|e| e.fontdb_id == fontdb_id)
176 .map(|_| FontFaceId(i as u32))
177 })
178 }
179
180 pub fn all_entries(&self) -> impl Iterator<Item = (FontFaceId, &FontEntry)> {
182 self.fonts
183 .iter()
184 .enumerate()
185 .filter_map(|(i, opt)| opt.as_ref().map(|entry| (FontFaceId(i as u32), entry)))
186 }
187}