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