tinymist_world/font/
resolver.rs1use core::fmt;
2use std::{
3 collections::HashMap,
4 num::NonZeroUsize,
5 path::PathBuf,
6 sync::{Arc, Mutex},
7};
8
9use tinymist_std::debug_loc::DataSource;
10use typst::text::{Font, FontBook, FontInfo};
11use typst::utils::LazyHash;
12
13use super::{BufferFontLoader, FontProfile, FontSlot, PartialFontBook};
14use crate::Bytes;
15
16pub trait FontResolver {
20 fn revision(&self) -> Option<NonZeroUsize> {
21 None
22 }
23
24 fn font_book(&self) -> &LazyHash<FontBook>;
25 fn font(&self, idx: usize) -> Option<Font>;
26
27 fn default_get_by_info(&self, info: &FontInfo) -> Option<Font> {
28 let mut alternative_text = 'c';
30 if let Some(codepoint) = info.coverage.iter().next() {
31 alternative_text = std::char::from_u32(codepoint).unwrap();
32 };
33
34 let idx = self
35 .font_book()
36 .select_fallback(Some(info), info.variant, &alternative_text.to_string())
37 .unwrap();
38 self.font(idx)
39 }
40 fn get_by_info(&self, info: &FontInfo) -> Option<Font> {
41 self.default_get_by_info(info)
42 }
43}
44
45#[derive(Debug)]
46pub struct FontResolverImpl {
48 font_paths: Vec<PathBuf>,
49 book: LazyHash<FontBook>,
50 partial_book: Arc<Mutex<PartialFontBook>>,
51 fonts: Vec<FontSlot>,
52 profile: FontProfile,
53}
54
55impl FontResolverImpl {
56 pub fn new(
57 font_paths: Vec<PathBuf>,
58 book: FontBook,
59 partial_book: Arc<Mutex<PartialFontBook>>,
60 fonts: Vec<FontSlot>,
61 profile: FontProfile,
62 ) -> Self {
63 Self {
64 font_paths,
65 book: LazyHash::new(book),
66 partial_book,
67 fonts,
68 profile,
69 }
70 }
71
72 pub fn len(&self) -> usize {
73 self.fonts.len()
74 }
75
76 pub fn is_empty(&self) -> bool {
77 self.fonts.is_empty()
78 }
79
80 pub fn profile(&self) -> &FontProfile {
81 &self.profile
82 }
83
84 pub fn font_paths(&self) -> &[PathBuf] {
85 &self.font_paths
86 }
87
88 pub fn partial_resolved(&self) -> bool {
89 self.partial_book.lock().unwrap().partial_hit
90 }
91
92 pub fn loaded_fonts(&self) -> impl Iterator<Item = (usize, Font)> + '_ {
93 let slots_with_index = self.fonts.iter().enumerate();
94
95 slots_with_index.flat_map(|(idx, slot)| {
96 let maybe_font = slot.get_uninitialized().flatten();
97 maybe_font.map(|font| (idx, font))
98 })
99 }
100
101 pub fn describe_font(&self, font: &Font) -> Option<Arc<DataSource>> {
102 let f = Some(Some(font.clone()));
103 for slot in &self.fonts {
104 if slot.get_uninitialized() == f {
105 return slot.description.clone();
106 }
107 }
108 None
109 }
110
111 pub fn modify_font_data(&mut self, idx: usize, buffer: Bytes) {
112 let mut font_book = self.partial_book.lock().unwrap();
113 for (i, info) in FontInfo::iter(buffer.as_slice()).enumerate() {
114 let buffer = buffer.clone();
115 let modify_idx = if i > 0 { None } else { Some(idx) };
116
117 font_book.push((
118 modify_idx,
119 info,
120 FontSlot::new(Box::new(BufferFontLoader {
121 buffer: Some(buffer),
122 index: i as u32,
123 })),
124 ));
125 }
126 }
127
128 pub fn append_font(&mut self, info: FontInfo, slot: FontSlot) {
129 let mut font_book = self.partial_book.lock().unwrap();
130 font_book.push((None, info, slot));
131 }
132
133 pub fn rebuild(&mut self) {
134 let mut partial_book = self.partial_book.lock().unwrap();
135 if !partial_book.partial_hit {
136 return;
137 }
138 partial_book.revision += 1;
139
140 let mut book = FontBook::default();
141
142 let mut font_changes = HashMap::new();
143 let mut new_fonts = vec![];
144 for (idx, info, slot) in partial_book.changes.drain(..) {
145 if let Some(idx) = idx {
146 font_changes.insert(idx, (info, slot));
147 } else {
148 new_fonts.push((info, slot));
149 }
150 }
151 partial_book.changes.clear();
152 partial_book.partial_hit = false;
153
154 let mut font_slots = Vec::new();
155 font_slots.append(&mut self.fonts);
156 self.fonts.clear();
157
158 for (i, slot_ref) in font_slots.iter_mut().enumerate() {
159 let (info, slot) = if let Some((_, v)) = font_changes.remove_entry(&i) {
160 v
161 } else {
162 book.push(self.book.info(i).unwrap().clone());
163 continue;
164 };
165
166 book.push(info);
167 *slot_ref = slot;
168 }
169
170 for (info, slot) in new_fonts.drain(..) {
171 book.push(info);
172 font_slots.push(slot);
173 }
174
175 self.book = LazyHash::new(book);
176 self.fonts = font_slots;
177 }
178
179 pub fn add_glyph_packs(&mut self) {
180 todo!()
181 }
182}
183
184impl FontResolver for FontResolverImpl {
185 fn font_book(&self) -> &LazyHash<FontBook> {
186 &self.book
187 }
188
189 fn font(&self, idx: usize) -> Option<Font> {
190 self.fonts[idx].get_or_init()
191 }
192
193 fn get_by_info(&self, info: &FontInfo) -> Option<Font> {
194 FontResolver::default_get_by_info(self, info)
195 }
196}
197
198impl fmt::Display for FontResolverImpl {
199 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200 for (idx, slot) in self.fonts.iter().enumerate() {
201 writeln!(f, "{:?} -> {:?}", idx, slot.get_uninitialized())?;
202 }
203
204 Ok(())
205 }
206}