1use super::family::parse_families;
2use super::index::*;
3use super::library::FontLibrary;
4use super::types::{FamilyId, FamilyKey, FontId, FontKey, SourceId};
5use super::{shared_data::SharedData, Font};
6#[cfg(feature = "emacs")]
7use crate::emacs::FontSpec;
8use crate::util::fxhash::FxHashMap;
9use std::sync::Arc;
10use swash::proxy::CharmapProxy;
11use swash::text::{
12 cluster::{CharCluster, Status},
13 Cjk, Language, Script,
14};
15use swash::{Attributes, Synthesis};
16pub type FontGroupKey = (u64, Attributes);
17type Epoch = u64;
18
19#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
21pub struct FontGroupId(pub u64);
22
23const MAX_INLINE: usize = 6;
24
25pub struct FontContext {
26 library: FontLibrary,
27 fonts: FontCache,
28 groups: GroupCache,
29}
30
31impl FontContext {
32 pub fn new(library: FontLibrary) -> Self {
33 let index = library.inner.index.read().unwrap().clone();
34 let fonts = FontCache {
35 index,
36 sources: FxHashMap::default(),
37 epoch: 0,
38 };
39 Self {
40 library,
41 fonts,
42 groups: GroupCache::default(),
43 }
44 }
45
46 pub fn library(&self) -> &FontLibrary {
48 &self.library
49 }
50
51 pub fn reset_group_state(&mut self) {
54 self.groups.reset();
55 }
56
57 pub fn register_group(&mut self, families: &str, key: u64, attrs: Attributes) -> FontGroupId {
59 self.groups.get(&self.fonts, families, key, attrs)
60 }
61
62 pub fn select_group(&mut self, descriptor: FontGroupId) {
64 self.groups.select(descriptor);
65 }
66
67 pub fn select_fallbacks(&mut self, script: Script, language: Option<&Language>) {
69 self.groups
70 .select_fallbacks(script, language.map(|l| l.cjk()).unwrap_or(Cjk::None))
71 }
72
73 pub fn map_cluster(
77 &mut self,
78 cluster: &mut CharCluster,
79 synthesis: &mut Synthesis,
80 ) -> Option<Font> {
81 let mut best = None;
82 let list = &self.groups.state.fonts;
83 for entry in self.groups.fonts.get_mut(list.start..list.end)?.iter_mut() {
84 match entry.map_cluster(&mut self.fonts, cluster, synthesis, best.is_none()) {
85 Some((font, status)) => {
86 if status == Status::Complete {
87 return Some(font);
88 }
89 best = Some(font);
90 }
91 None => continue,
92 }
93 }
94 let attrs = list.attributes;
95 if cluster.info().is_emoji() {
98 if let Some(entry) = self.groups.emoji(&self.fonts, attrs) {
99 match entry.map_cluster(&mut self.fonts, cluster, synthesis, best.is_none()) {
100 Some((font, status)) => {
101 if status == Status::Complete {
102 return Some(font);
103 }
104 best = Some(font);
105 }
106 None => {}
107 }
108 }
109 }
110 if !self.groups.state.fallbacks_ready {
111 self.groups.fill_fallbacks(&self.fonts);
112 }
113 for &family in &self.groups.state.fallbacks {
114 let entry = match self.groups.state.fallback_map.get_mut(&(family, attrs)) {
115 Some(entry) => entry,
116 _ => match self.fonts.query(family, attrs) {
117 Some(font) => {
118 self.groups
119 .state
120 .fallback_map
121 .insert((family, attrs), font.selector(attrs).into());
122 self.groups
123 .state
124 .fallback_map
125 .get_mut(&(family, attrs))
126 .unwrap()
127 }
128 _ => continue,
129 },
130 };
131 match entry.map_cluster(&mut self.fonts, cluster, synthesis, best.is_none()) {
132 Some((font, status)) => {
133 if status == Status::Complete {
134 return Some(font);
135 }
136 best = Some(font);
137 }
138 None => continue,
139 }
140 }
141 best
142 }
143}
144
145pub struct FontCache {
146 pub index: Arc<StaticIndex>,
147 sources: FxHashMap<SourceId, Option<(SharedData, Epoch)>>,
148 epoch: Epoch,
149}
150
151impl FontCache {
152 pub fn default() -> Self {
153 let index = StaticIndex::global().clone();
154 FontCache {
155 index,
156 sources: FxHashMap::default(),
157 epoch: 0,
158 }
159 }
160
161 pub fn query<'a>(
164 &'a self,
165 family: impl Into<FamilyKey<'a>>,
166 attributes: impl Into<Attributes>,
167 ) -> Option<FontEntry<'a>> {
168 self.index.query(family, attributes)
169 }
170
171 #[cfg(feature = "emacs")]
172 pub fn list<'a>(&'a self, spec: FontSpec) -> Vec<FontEntry<'a>> {
173 self.index.list(spec)
174 }
175 #[cfg(feature = "emacs")]
176 pub fn match_<'a>(&'a self, spec: FontSpec) -> Option<FontEntry<'a>> {
177 self.index.match_(spec)
178 }
179
180 pub fn font_by_id<'a>(&'a self, id: FontId) -> Option<FontEntry<'a>> {
182 self.index.font_by_id(id)
183 }
184
185 pub fn get<'k>(&mut self, key: impl Into<FontKey<'k>>) -> Option<Font> {
187 let (source_id, offset, attributes, key) = match key.into() {
188 FontKey::Id(id) => {
189 let font = self.font_by_id(id)?;
190 (
191 font.source().id(),
192 font.offset(),
193 font.attributes(),
194 font.cache_key(),
195 )
196 }
197 FontKey::Descriptor(family, attrs) => {
198 let font = self.query(family, attrs)?;
199 (
200 font.source().id(),
201 font.offset(),
202 font.attributes(),
203 font.cache_key(),
204 )
205 }
206 };
207 let epoch = self.epoch;
208 match self.sources.get_mut(&source_id) {
209 Some(data) => {
210 return data.as_mut().map(|d| {
211 d.1 = epoch;
212 Font {
213 data: d.0.clone(),
214 offset,
215 attributes,
216 key,
217 }
218 })
219 }
220 _ => {}
221 }
222 let source = self.index.base.sources.get(source_id.to_usize())?;
223 match source.get() {
224 Some(data) => {
225 self.sources.insert(source_id, Some((data.clone(), epoch)));
226 Some(Font {
227 data,
228 offset,
229 attributes,
230 key,
231 })
232 }
233 _ => {
234 self.sources.insert(source_id, None);
235 None
236 }
237 }
238 }
239}
240
241#[derive(Default)]
252struct GroupCache {
253 key_map: FxHashMap<FontGroupKey, CachedGroup>,
256 tmp: Vec<(FontId, Attributes)>,
258 next_id: u64,
260 font_map: FxHashMap<FontGroupId, CachedFontList>,
263 fonts: Vec<CachedFont>,
265 state: GroupCacheState,
268}
269
270struct GroupCacheState {
272 id: FontGroupId,
274 fonts: CachedFontList,
276 fallback: Option<(Script, Cjk)>,
278 fallbacks_ready: bool,
280 fallback_map: FxHashMap<(FamilyId, Attributes), CachedFont>,
282 fallbacks: Vec<FamilyId>,
284 emoji_ready: bool,
286 emoji: Option<CachedFont>,
288}
289
290impl Default for GroupCacheState {
291 fn default() -> Self {
292 Self {
293 id: FontGroupId(!0),
294 fonts: CachedFontList::default(),
295 fallback: None,
296 fallbacks_ready: true,
297 fallback_map: FxHashMap::default(),
298 fallbacks: Vec::new(),
299 emoji_ready: false,
300 emoji: None,
301 }
302 }
303}
304
305impl GroupCacheState {
306 fn reset(&mut self) {
307 self.id = FontGroupId(!0);
308 self.fonts = CachedFontList::default();
309 self.fallback = None;
310 self.fallbacks_ready = true;
311 self.fallback_map.clear();
312 self.fallbacks.clear();
313 self.emoji_ready = false;
314 self.emoji = None;
315 }
316}
317
318impl GroupCache {
319 fn get(&mut self, fonts: &FontCache, names: &str, key: u64, attrs: Attributes) -> FontGroupId {
321 use std::collections::hash_map::Entry;
322 let key = (key, attrs);
323 match self.key_map.get_mut(&key) {
325 Some(item) => {
326 item.epoch = fonts.epoch;
327 match self.font_map.entry(item.id) {
328 Entry::Occupied(..) => {}
329 Entry::Vacant(e) => {
330 let start = self.fonts.len();
331 self.fonts.extend(
332 item.data
333 .get()
334 .iter()
335 .map(|&sel| (sel.0, sel.1, attrs).into()),
336 );
337 let end = self.fonts.len();
338 e.insert(CachedFontList {
339 attributes: attrs,
340 start,
341 end,
342 });
343 }
344 }
345 return item.id;
346 }
347 _ => {}
348 }
349 self.tmp.clear();
351 for family in parse_families(names) {
352 match fonts.query(family, attrs).map(|f| f.selector(attrs)) {
353 Some(sel) => self.tmp.push((sel.0, sel.1)),
354 _ => {}
355 }
356 }
357 for (item_key, item) in &self.key_map {
359 if item_key.1 != attrs {
360 continue;
361 }
362 let existing = item.data.get();
363 if existing == &self.tmp {
364 match self.font_map.entry(item.id) {
365 Entry::Occupied(..) => {}
366 Entry::Vacant(e) => {
367 let start = self.fonts.len();
368 self.fonts.extend(
369 item.data
370 .get()
371 .iter()
372 .map(|&sel| (sel.0, sel.1, attrs).into()),
373 );
374 let end = self.fonts.len();
375 e.insert(CachedFontList {
376 attributes: attrs,
377 start,
378 end,
379 });
380 }
381 }
382 return item.id;
383 }
384 }
385 let id = FontGroupId(self.next_id);
387 self.next_id += 1;
388 let mut data = GroupData::Inline(0, [(FontId(0), Attributes::default()); MAX_INLINE]);
389 for font in &self.tmp {
390 data.push(font.0, font.1);
391 }
392 let start = self.fonts.len();
393 self.fonts
394 .extend(self.tmp.iter().map(|&sel| (sel.0, sel.1, attrs).into()));
395 let end = self.fonts.len();
396 self.font_map.insert(
397 id,
398 CachedFontList {
399 attributes: attrs,
400 start,
401 end,
402 },
403 );
404 let desc = CachedGroup {
405 id,
406 epoch: fonts.epoch,
407 data,
408 };
409 self.key_map.insert(key, desc);
410 id
411 }
412
413 fn select(&mut self, id: FontGroupId) {
415 if self.state.id == id {
416 return;
417 }
418 match self.font_map.get(&id) {
419 Some(fonts) => self.state.fonts = *fonts,
420 _ => self.state.fonts = CachedFontList::default(),
421 }
422 self.state.id = id;
423 }
424
425 fn select_fallbacks(&mut self, script: Script, cjk: Cjk) {
427 if self.state.fallback != Some((script, cjk)) {
428 self.state.fallback = Some((script, cjk));
429 self.state.fallbacks_ready = false;
430 self.state.fallbacks.clear();
431 }
432 }
433
434 fn fill_fallbacks(&mut self, fonts: &FontCache) {
435 self.state.fallbacks.clear();
436 self.state.fallbacks_ready = true;
437 match self.state.fallback {
438 Some((script, cjk)) => {
439 self.state
440 .fallbacks
441 .extend_from_slice(fonts.index.fallbacks(script, cjk));
442 }
443 _ => {}
444 }
445 }
446
447 fn emoji(&mut self, fonts: &FontCache, attrs: Attributes) -> Option<&mut CachedFont> {
448 if !self.state.emoji_ready {
449 self.state.emoji_ready = true;
450 let family = fonts.index.emoji_family()?;
451 let sel = fonts.query(family, ())?.selector(attrs);
452 self.state.emoji = Some(sel.into());
453 }
454 self.state.emoji.as_mut()
455 }
456
457 fn reset(&mut self) {
459 self.state.reset();
460 self.font_map.clear();
461 self.fonts.clear();
462 }
463
464 }
489
490struct CachedGroup {
491 id: FontGroupId,
492 epoch: Epoch,
493 data: GroupData,
494}
495
496#[derive(Clone)]
497enum GroupData {
498 Inline(u8, [(FontId, Attributes); MAX_INLINE]),
499 Heap(Vec<(FontId, Attributes)>),
500}
501
502impl GroupData {
503 fn push(&mut self, font: FontId, attrs: Attributes) {
515 match self {
516 Self::Inline(len, ids) => {
517 if *len as usize == ids.len() {
518 let mut vec = Vec::from(&ids[..]);
519 vec.push((font, attrs));
520 *self = Self::Heap(vec);
521 } else {
522 ids[*len as usize] = (font, attrs);
523 *len += 1;
524 }
525 }
526 Self::Heap(vec) => {
527 vec.push((font, attrs));
528 }
529 }
530 }
531
532 fn get(&self) -> &[(FontId, Attributes)] {
533 match self {
534 Self::Inline(len, ids) => &ids[..*len as usize],
535 Self::Heap(vec) => &vec,
536 }
537 }
538}
539
540#[derive(Copy, Clone, Default)]
541struct CachedFontList {
542 attributes: Attributes,
544 start: usize,
546 end: usize,
548}
549
550struct CachedFont {
551 id: FontId,
552 font: Option<Font>,
553 charmap: CharmapProxy,
554 synth: Synthesis,
555 error: bool,
556}
557
558impl CachedFont {
559 #[inline]
560 fn map_cluster(
561 &mut self,
562 fonts: &mut FontCache,
563 cluster: &mut CharCluster,
564 synth: &mut Synthesis,
565 first: bool,
566 ) -> Option<(Font, Status)> {
567 if self.error {
568 return None;
569 }
570 let font = match &self.font {
571 Some(font) => font,
572 None => {
573 let font = fonts.get(self.id);
574 let font = match font {
575 Some(f) => f,
576 _ => {
577 self.error = true;
578 return None;
579 }
580 };
581 self.charmap = CharmapProxy::from_font(&font.as_ref());
582 self.font = Some(font);
583 self.font.as_ref().unwrap()
584 }
585 };
586 let charmap = self.charmap.materialize(&font.as_ref());
587 let status = cluster.map(|ch| charmap.map(ch));
588 if status != Status::Discard || first {
589 *synth = self.synth;
590 Some((font.clone(), status))
591 } else {
592 None
593 }
594 }
595}
596
597impl From<(FontId, Attributes, Attributes)> for CachedFont {
598 fn from(v: (FontId, Attributes, Attributes)) -> Self {
599 let synth = v.1.synthesize(v.2);
600 Self {
601 id: v.0,
602 font: None,
603 charmap: CharmapProxy::default(),
604 synth,
605 error: false,
606 }
607 }
608}