1use arc_swap::ArcSwap;
2use std::collections::HashSet;
3#[cfg(feature = "parse")]
4use std::path::Path;
5use std::sync::atomic::{AtomicU32, Ordering};
6use std::sync::Arc;
7pub use ttf_parser::LineMetrics;
8
9#[cfg(all(target_arch = "wasm32", feature = "wit"))]
10mod bindings;
11mod conv;
12mod error;
13mod font;
14#[cfg(feature = "metrics")]
15mod metrics;
16#[cfg(feature = "ras")]
17mod ras;
18#[cfg(all(target_arch = "wasm32", feature = "wit"))]
19mod wit;
20
21pub use error::Error;
22pub use font::*;
23#[cfg(feature = "metrics")]
24pub use metrics::*;
25#[cfg(feature = "ras")]
26pub use ras::*;
27pub use tiny_skia_path::{self, PathSegment};
28
29#[cfg(all(target_arch = "wasm32", feature = "wit"))]
30pub use bindings::exports::alibaba::fontkit::fontkit_interface::TextMetrics;
31
32#[cfg(target_arch = "wasm32")]
33#[global_allocator]
34static ALLOCATOR: talc::TalckWasm = unsafe { talc::TalckWasm::new_global() };
35
36struct Config {
37 pub lru_limit: u32,
38 pub cache_path: Option<String>,
39}
40
41pub struct FontKit {
42 fonts: dashmap::DashMap<font::FontKey, Font>,
43 fallback_font_key: Box<dyn Fn(font::FontKey) -> Option<font::FontKey> + Send + Sync>,
44 pub(crate) config: ArcSwap<Config>,
45 hit_counter: Arc<AtomicU32>,
46}
47
48impl FontKit {
49 pub fn new() -> Self {
51 FontKit {
52 fonts: dashmap::DashMap::new(),
53 fallback_font_key: Box::new(|_| None),
54 config: ArcSwap::new(Arc::new(Config {
55 lru_limit: 0,
56 cache_path: None,
57 })),
58 hit_counter: Arc::default(),
59 }
60 }
61
62 pub fn len(&self) -> usize {
63 self.fonts.len()
64 }
65
66 pub fn set_fallback(
69 &mut self,
70 callback: impl Fn(font::FontKey) -> Option<font::FontKey> + Send + Sync + 'static,
71 ) {
72 self.fallback_font_key = Box::new(callback);
73 }
74
75 #[cfg(feature = "metrics")]
76 pub fn measure(&self, font_key: &font::FontKey, text: &str) -> Option<metrics::TextMetrics> {
77 let mut used_keys = HashSet::new();
78 let mut current_key = font_key.clone();
79 let mut current_metrics: Option<metrics::TextMetrics> = None;
80 used_keys.insert(current_key.clone());
81 loop {
82 match self
83 .query(¤t_key)
84 .and_then(|font| font.measure(text).ok())
85 {
86 Some(metrics) => {
87 if let Some(m) = current_metrics.as_ref() {
88 m.replace(metrics, true);
89 } else {
90 current_metrics = Some(metrics);
91 }
92 }
93 None => {}
94 }
95 if !current_metrics
96 .as_ref()
97 .map(|m| m.has_missing())
98 .unwrap_or(true)
99 {
100 break;
101 }
102 let callback = self.fallback_font_key.as_ref();
103 match (callback)(current_key) {
104 Some(next_key) => {
105 if used_keys.contains(&next_key) {
106 break;
107 } else {
108 used_keys.insert(next_key.clone());
109 current_key = next_key;
110 }
111 }
112 None => break,
113 }
114 }
115 current_metrics
116 }
117
118 pub fn remove(&self, key: font::FontKey) {
119 self.fonts.remove(&key);
120 }
121
122 pub fn buffer_size(&self) -> usize {
123 self.fonts
124 .iter()
125 .map(|font| font.buffer_size())
126 .sum::<usize>()
127 }
128
129 pub fn check_lru(&self) {
130 let limit = self.config.load().lru_limit as usize * 1024;
131 if limit == 0 {
132 return;
133 }
134 let mut current_size = self.buffer_size();
135 let mut loaded_fonts = self.fonts.iter().filter(|f| f.buffer_size() > 0).count();
136 while current_size > limit && loaded_fonts > 1 {
137 let font = self
138 .fonts
139 .iter()
140 .filter(|f| f.buffer_size() > 0)
141 .min_by(|a, b| {
142 b.hit_index
143 .load(Ordering::SeqCst)
144 .cmp(&a.hit_index.load(Ordering::SeqCst))
145 });
146
147 let hit_index = font
148 .as_ref()
149 .map(|f| f.hit_index.load(Ordering::SeqCst))
150 .unwrap_or(0);
151 if let Some(f) = font {
152 f.unload();
153 }
154 if current_size == self.buffer_size() {
155 break;
156 }
157 current_size = self.buffer_size();
158 self.hit_counter.fetch_sub(hit_index, Ordering::SeqCst);
159 for f in self.fonts.iter() {
160 f.hit_index.fetch_sub(hit_index, Ordering::SeqCst);
161 }
162 loaded_fonts = self.fonts.iter().filter(|f| f.buffer_size() > 0).count();
163 }
164 }
165
166 #[cfg(feature = "parse")]
170 pub fn add_font_from_buffer(&self, buffer: Vec<u8>) -> Result<(), Error> {
171 use std::fs::File;
172 use std::io::Write;
173 use std::path::PathBuf;
174 use std::str::FromStr;
175
176 let mut font = Font::from_buffer(buffer.clone(), self.hit_counter.clone())?;
177 let key = font.first_key();
178 if let Some(v) = self.fonts.get(&key) {
179 if let Some(path) = v.path().cloned() {
180 font.set_path(path);
181 }
182 }
183 let cache_path = self.config.load().cache_path.clone();
184 if let Some(mut path) = cache_path.and_then(|p| PathBuf::from_str(&p).ok()) {
185 if let Some(original_path) = font.path() {
186 let relative_path = if original_path.is_absolute()
187 && !std::fs::exists(original_path.clone()).unwrap_or(false)
188 {
189 format!(".{}", original_path.display())
190 } else {
191 format!("{}", original_path.display())
192 };
193 path.push(relative_path);
194 let mut dir = path.clone();
195 dir.pop();
196 std::fs::create_dir_all(dir)?;
197 } else {
198 path.push(format!(
199 "{}_{}_{}_{}.ttf",
200 key.family.replace(['.', ' '], "_"),
201 key.italic.unwrap_or_default(),
202 key.stretch.unwrap_or(5),
203 key.weight.unwrap_or(400)
204 ));
205 }
206 let mut f = File::create(&path)?;
207 f.write_all(&buffer)?;
208 font.set_path(path);
209 font.unload();
210 }
211 self.fonts.insert(key, font);
212 self.check_lru();
213 Ok(())
214 }
215
216 #[cfg(feature = "parse")]
219 pub fn search_fonts_from_path(&self, path: impl AsRef<Path>) -> Result<(), Error> {
220 use std::io::Read;
221 let mut buffer = Vec::new();
235 let path = path.as_ref();
236 let ext = path
237 .extension()
238 .and_then(|s| s.to_str())
239 .map(|s| s.to_lowercase());
240 let ext = ext.as_deref();
241 let ext = match ext {
242 Some(e) => e,
243 None => return Ok(()),
244 };
245 match ext {
246 "ttf" | "otf" | "ttc" | "woff2" | "woff" => {
247 let mut file = std::fs::File::open(&path).unwrap();
248 buffer.clear();
249 file.read_to_end(&mut buffer).unwrap();
250 let mut font = match Font::from_buffer(buffer, self.hit_counter.clone()) {
251 Ok(f) => f,
252 Err(e) => {
253 log::warn!("Failed loading font {:?}: {:?}", path, e);
254 return Ok(());
255 }
256 };
257 font.set_path(path.to_path_buf());
258 font.unload();
259 self.fonts.insert(font.first_key(), font);
260 self.check_lru();
261 }
262 _ => {}
263 }
264 Ok(())
265 }
266
267 pub fn exact_match(&self, key: &font::FontKey) -> Option<StaticFace> {
268 let face = self.query(key)?;
269 let mut patched_key = key.clone();
270 if patched_key.weight.is_none() {
271 patched_key.weight = Some(400);
272 }
273 if patched_key.stretch.is_none() {
274 patched_key.stretch = Some(5);
275 }
276 if patched_key.italic.is_none() {
277 patched_key.italic = Some(false);
278 }
279 if face.key() == patched_key {
280 return Some(face);
281 } else {
282 return None;
283 }
284 }
285
286 pub fn query(&self, key: &font::FontKey) -> Option<StaticFace> {
287 let result = self.fonts.get(&self.query_font(key)?)?.face(key).ok();
288 self.check_lru();
289 result
290 }
291
292 pub(crate) fn query_font(&self, key: &font::FontKey) -> Option<font::FontKey> {
293 let mut search_results = self
294 .fonts
295 .iter()
296 .map(|item| item.key().clone())
297 .collect::<HashSet<_>>();
298 let filters = Filter::from_key(key);
299 for filter in filters {
300 let mut s = search_results.clone();
301 let is_family = if let Filter::Family(_) = filter {
302 true
303 } else {
304 false
305 };
306 s.retain(|key| {
307 let font = self.fonts.get(key).unwrap();
308 font.fulfils(&filter)
309 });
310 match s.len() {
311 1 => return s.iter().next().cloned(),
312 0 if is_family => return None,
313 0 => {}
314 _ => search_results = s,
315 }
316 }
317 None
318 }
319
320 pub fn keys(&self) -> Vec<FontKey> {
321 self.fonts
322 .iter()
323 .flat_map(|i| {
324 i.value()
325 .variants()
326 .iter()
327 .map(|i| i.key.clone())
328 .collect::<Vec<_>>()
329 })
330 .collect()
331 }
332}
333
334enum Filter<'a> {
335 Family(&'a str),
336 Italic(bool),
337 Weight(u16),
338 Stretch(u16),
339 Variations(&'a Vec<(String, f32)>),
340}
341
342impl<'a> Filter<'a> {
343 pub fn from_key(key: &'a FontKey) -> Vec<Filter<'a>> {
344 let mut filters = vec![Filter::Family(&key.family)];
345 if let Some(italic) = key.italic {
346 filters.push(Filter::Italic(italic));
347 }
348 if let Some(weight) = key.weight {
349 filters.push(Filter::Weight(weight));
350 }
351 if let Some(stretch) = key.stretch {
352 filters.push(Filter::Stretch(stretch));
353 }
354
355 filters.push(Filter::Variations(&key.variations));
356
357 filters.push(Filter::Weight(0));
359 filters
360 }
361}