tinymist_world/font/
resolver.rs1use core::fmt;
2use std::{num::NonZeroUsize, path::PathBuf, sync::Arc};
3
4use typst::text::{Font, FontBook, FontInfo};
5use typst::utils::LazyHash;
6
7use super::FontSlot;
8use crate::debug_loc::DataSource;
9
10pub trait FontResolver {
13 fn revision(&self) -> Option<NonZeroUsize> {
25 None
26 }
27
28 fn font_book(&self) -> &LazyHash<FontBook>;
30
31 fn slot(&self, index: usize) -> Option<&FontSlot>;
34
35 fn font(&self, index: usize) -> Option<Font>;
38
39 fn get_by_info(&self, info: &FontInfo) -> Option<Font> {
41 self.default_get_by_info(info)
42 }
43
44 fn default_get_by_info(&self, info: &FontInfo) -> Option<Font> {
46 let mut alternative_text = 'c';
50 if let Some(codepoint) = info.coverage.iter().next() {
51 alternative_text = std::char::from_u32(codepoint).unwrap();
52 };
53
54 let index = self
55 .font_book()
56 .select_fallback(Some(info), info.variant, &alternative_text.to_string())
57 .unwrap();
58 self.font(index)
59 }
60}
61
62impl<T: FontResolver> FontResolver for Arc<T> {
63 fn font_book(&self) -> &LazyHash<FontBook> {
64 self.as_ref().font_book()
65 }
66
67 fn slot(&self, index: usize) -> Option<&FontSlot> {
68 self.as_ref().slot(index)
69 }
70
71 fn font(&self, index: usize) -> Option<Font> {
72 self.as_ref().font(index)
73 }
74
75 fn get_by_info(&self, info: &FontInfo) -> Option<Font> {
76 self.as_ref().get_by_info(info)
77 }
78}
79
80pub trait ReusableFontResolver: FontResolver {
82 fn slots(&self) -> impl Iterator<Item = FontSlot>;
84}
85
86impl<T: ReusableFontResolver> ReusableFontResolver for Arc<T> {
87 fn slots(&self) -> impl Iterator<Item = FontSlot> {
88 self.as_ref().slots()
89 }
90}
91
92#[derive(Debug)]
99pub struct FontResolverImpl {
100 pub(crate) font_paths: Vec<PathBuf>,
102 pub(crate) book: LazyHash<FontBook>,
104 pub(crate) slots: Vec<FontSlot>,
106}
107
108impl FontResolverImpl {
109 pub fn new(font_paths: Vec<PathBuf>, book: FontBook, slots: Vec<FontSlot>) -> Self {
111 Self {
112 font_paths,
113 book: LazyHash::new(book),
114 slots,
115 }
116 }
117
118 pub fn new_with_fonts(
120 font_paths: Vec<PathBuf>,
121 fonts: impl Iterator<Item = (FontInfo, FontSlot)>,
122 ) -> Self {
123 let mut book = FontBook::new();
124 let mut slots = Vec::<FontSlot>::new();
125
126 for (info, slot) in fonts {
127 book.push(info);
128 slots.push(slot);
129 }
130
131 Self {
132 font_paths,
133 book: LazyHash::new(book),
134 slots,
135 }
136 }
137
138 pub fn len(&self) -> usize {
140 self.slots.len()
141 }
142
143 pub fn is_empty(&self) -> bool {
145 self.slots.is_empty()
146 }
147
148 pub fn font_paths(&self) -> &[PathBuf] {
150 &self.font_paths
151 }
152
153 #[deprecated(note = "use `fonts` instead")]
155 pub fn get_fonts(&self) -> impl Iterator<Item = (&FontInfo, &FontSlot)> {
156 self.fonts()
157 }
158
159 pub fn fonts(&self) -> impl Iterator<Item = (&FontInfo, &FontSlot)> {
161 self.slots.iter().enumerate().map(|(idx, slot)| {
162 let info = self.book.info(idx).unwrap();
163 (info, slot)
164 })
165 }
166
167 pub fn loaded_fonts(&self) -> impl Iterator<Item = (usize, Font)> + '_ {
169 self.slots.iter().enumerate().flat_map(|(idx, slot)| {
170 let maybe_font = slot.get_uninitialized().flatten();
171 maybe_font.map(|font| (idx, font))
172 })
173 }
174
175 pub fn describe_font(&self, font: &Font) -> Option<Arc<DataSource>> {
177 let f = Some(Some(font.clone()));
178 for slot in &self.slots {
179 if slot.get_uninitialized() == f {
180 return slot.description.clone();
181 }
182 }
183 None
184 }
185
186 pub fn describe_font_by_id(&self, index: usize) -> Option<Arc<DataSource>> {
188 self.slot(index)?.description.clone()
189 }
190
191 pub fn with_font_paths(mut self, font_paths: Vec<PathBuf>) -> Self {
193 self.font_paths = font_paths;
194 self
195 }
196}
197
198impl FontResolver for FontResolverImpl {
199 fn font_book(&self) -> &LazyHash<FontBook> {
200 &self.book
201 }
202
203 fn slot(&self, idx: usize) -> Option<&FontSlot> {
204 self.slots.get(idx)
205 }
206
207 fn font(&self, index: usize) -> Option<Font> {
208 self.slot(index)?.get_or_init()
209 }
210
211 fn get_by_info(&self, info: &FontInfo) -> Option<Font> {
212 FontResolver::default_get_by_info(self, info)
213 }
214}
215
216impl fmt::Display for FontResolverImpl {
217 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
218 for (idx, slot) in self.slots.iter().enumerate() {
219 writeln!(f, "{:?} -> {:?}", idx, slot.get_uninitialized())?;
220 }
221
222 Ok(())
223 }
224}
225
226impl ReusableFontResolver for FontResolverImpl {
227 fn slots(&self) -> impl Iterator<Item = FontSlot> {
229 self.slots.iter().cloned()
230 }
231}