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 {
81 fn slots(&self) -> impl Iterator<Item = FontSlot>;
83}
84
85impl<T: ReusableFontResolver> ReusableFontResolver for Arc<T> {
86 fn slots(&self) -> impl Iterator<Item = FontSlot> {
87 self.as_ref().slots()
88 }
89}
90
91#[derive(Debug)]
98pub struct FontResolverImpl {
99 pub(crate) font_paths: Vec<PathBuf>,
100 pub(crate) book: LazyHash<FontBook>,
101 pub(crate) slots: Vec<FontSlot>,
102}
103
104impl FontResolverImpl {
105 pub fn new(font_paths: Vec<PathBuf>, book: FontBook, slots: Vec<FontSlot>) -> Self {
107 Self {
108 font_paths,
109 book: LazyHash::new(book),
110 slots,
111 }
112 }
113
114 pub fn new_with_fonts(
115 font_paths: Vec<PathBuf>,
116 fonts: impl Iterator<Item = (FontInfo, FontSlot)>,
117 ) -> Self {
118 let mut book = FontBook::new();
119 let mut slots = Vec::<FontSlot>::new();
120
121 for (info, slot) in fonts {
122 book.push(info);
123 slots.push(slot);
124 }
125
126 Self {
127 font_paths,
128 book: LazyHash::new(book),
129 slots,
130 }
131 }
132
133 pub fn len(&self) -> usize {
135 self.slots.len()
136 }
137
138 pub fn is_empty(&self) -> bool {
140 self.slots.is_empty()
141 }
142
143 pub fn font_paths(&self) -> &[PathBuf] {
145 &self.font_paths
146 }
147
148 #[deprecated(note = "use `fonts` instead")]
150 pub fn get_fonts(&self) -> impl Iterator<Item = (&FontInfo, &FontSlot)> {
151 self.fonts()
152 }
153
154 pub fn fonts(&self) -> impl Iterator<Item = (&FontInfo, &FontSlot)> {
156 self.slots.iter().enumerate().map(|(idx, slot)| {
157 let info = self.book.info(idx).unwrap();
158 (info, slot)
159 })
160 }
161
162 pub fn loaded_fonts(&self) -> impl Iterator<Item = (usize, Font)> + '_ {
164 self.slots.iter().enumerate().flat_map(|(idx, slot)| {
165 let maybe_font = slot.get_uninitialized().flatten();
166 maybe_font.map(|font| (idx, font))
167 })
168 }
169
170 pub fn describe_font(&self, font: &Font) -> Option<Arc<DataSource>> {
172 let f = Some(Some(font.clone()));
173 for slot in &self.slots {
174 if slot.get_uninitialized() == f {
175 return slot.description.clone();
176 }
177 }
178 None
179 }
180
181 pub fn describe_font_by_id(&self, id: usize) -> Option<Arc<DataSource>> {
183 self.slots[id].description.clone()
184 }
185
186 pub fn with_font_paths(mut self, font_paths: Vec<PathBuf>) -> Self {
187 self.font_paths = font_paths;
188 self
189 }
190}
191
192impl FontResolver for FontResolverImpl {
193 fn font_book(&self) -> &LazyHash<FontBook> {
194 &self.book
195 }
196
197 fn slot(&self, idx: usize) -> Option<&FontSlot> {
198 self.slots.get(idx)
199 }
200
201 fn font(&self, idx: usize) -> Option<Font> {
202 self.slots[idx].get_or_init()
203 }
204
205 fn get_by_info(&self, info: &FontInfo) -> Option<Font> {
206 FontResolver::default_get_by_info(self, info)
207 }
208}
209
210impl fmt::Display for FontResolverImpl {
211 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212 for (idx, slot) in self.slots.iter().enumerate() {
213 writeln!(f, "{:?} -> {:?}", idx, slot.get_uninitialized())?;
214 }
215
216 Ok(())
217 }
218}
219
220impl ReusableFontResolver for FontResolverImpl {
221 fn slots(&self) -> impl Iterator<Item = FontSlot> {
222 self.slots.iter().cloned()
223 }
224}