1use egui::{FontData, FontDefinitions, FontFamily};
27use std::collections::BTreeMap;
28use system_fonts::FoundFontSource;
29pub use system_fonts::{FontPreset, FontRegion, FontStyle};
30
31pub fn set_auto(ctx: &egui::Context, style: FontStyle) -> Vec<String> {
45 let (locale, region, fonts) = system_fonts::find_for_system_locale(style);
46 log::info!(
47 "Detected locale: {:?}, region: {:?}, style: {:?}, candidates: {}",
48 locale,
49 region,
50 style,
51 fonts.len()
52 );
53 set_found_fonts(ctx, fonts)
54}
55
56pub fn set_with_region(ctx: &egui::Context, region: FontRegion, style: FontStyle) -> Vec<String> {
70 let presets = system_fonts::presets_for_region(region);
71 set_with_presets(ctx, presets, style)
72}
73
74pub fn set_with_presets<I>(ctx: &egui::Context, presets: I, style: FontStyle) -> Vec<String>
89where
90 I: IntoIterator<Item = FontPreset>,
91{
92 let fonts = system_fonts::find_from_presets(presets, style);
93 set_found_fonts(ctx, fonts)
94}
95
96pub fn extend_auto(
114 ctx: &egui::Context,
115 defs: &mut FontDefinitions,
116 style: FontStyle,
117) -> Vec<String> {
118 let (locale, region, fonts) = system_fonts::find_for_system_locale(style);
119 log::info!(
120 "Detected locale: {:?}, region: {:?}, style: {:?}, candidates: {}",
121 locale,
122 region,
123 style,
124 fonts.len()
125 );
126 let installed = append_found_fonts(defs, fonts);
127 if !installed.is_empty() {
128 ctx.set_fonts(defs.clone());
129 }
130 installed
131}
132
133pub fn extend_with_region(
148 ctx: &egui::Context,
149 defs: &mut FontDefinitions,
150 region: FontRegion,
151 style: FontStyle,
152) -> Vec<String> {
153 let presets = system_fonts::presets_for_region(region);
154 extend_with_presets(ctx, defs, presets, style)
155}
156
157pub fn extend_with_presets<I>(
173 ctx: &egui::Context,
174 defs: &mut FontDefinitions,
175 presets: I,
176 style: FontStyle,
177) -> Vec<String>
178where
179 I: IntoIterator<Item = FontPreset>,
180{
181 let fonts = system_fonts::find_from_presets(presets, style);
182 let installed = append_found_fonts(defs, fonts);
183 if !installed.is_empty() {
184 ctx.set_fonts(defs.clone());
185 }
186 installed
187}
188
189fn set_found_fonts(ctx: &egui::Context, fonts: Vec<system_fonts::FoundFont>) -> Vec<String> {
190 let mut defs = FontDefinitions::default();
191
192 let mut installed_names: Vec<String> = Vec::new();
193 let mut keys_in_priority: Vec<String> = Vec::new();
194
195 for f in fonts {
196 let Some(bytes) = read_font_bytes(f.source) else {
197 continue;
198 };
199
200 defs.font_data
201 .insert(f.key.clone(), FontData::from_owned(bytes).into());
202
203 keys_in_priority.push(f.key.clone());
204 installed_names.push(f.family);
205 }
206
207 if installed_names.is_empty() {
208 log::warn!("No matching system fonts found.");
209 return vec![];
210 }
211
212 for key in keys_in_priority.into_iter().rev() {
213 insert_front(&mut defs.families, FontFamily::Proportional, key.clone());
214 insert_front(&mut defs.families, FontFamily::Monospace, key);
215 }
216
217 ctx.set_fonts(defs);
218 log::info!("Set fonts (family names): {:?}", installed_names);
219
220 installed_names
221}
222
223fn append_found_fonts(
224 defs: &mut FontDefinitions,
225 fonts: Vec<system_fonts::FoundFont>,
226) -> Vec<String> {
227 let mut installed_names: Vec<String> = Vec::new();
228 let mut keys_in_priority: Vec<String> = Vec::new();
229
230 for f in fonts {
231 if defs.font_data.contains_key(&f.key) {
232 continue;
233 }
234
235 let Some(bytes) = read_font_bytes(f.source) else {
236 continue;
237 };
238
239 defs.font_data
240 .insert(f.key.clone(), FontData::from_owned(bytes).into());
241
242 keys_in_priority.push(f.key.clone());
243 installed_names.push(f.family);
244 }
245
246 if installed_names.is_empty() {
247 return vec![];
248 }
249
250 for key in keys_in_priority.into_iter() {
251 insert_back(&mut defs.families, FontFamily::Proportional, key.clone());
252 insert_back(&mut defs.families, FontFamily::Monospace, key);
253 }
254
255 installed_names
256}
257
258fn read_font_bytes(source: FoundFontSource) -> Option<Vec<u8>> {
259 match source {
260 FoundFontSource::Path(path) => match std::fs::read(&path) {
261 Ok(b) => Some(b),
262 Err(e) => {
263 log::debug!("Failed to read font file {:?}: {}", path, e);
264 None
265 }
266 },
267 FoundFontSource::Bytes(b) => Some(b.as_ref().to_vec()),
268 }
269}
270
271fn insert_front(families: &mut BTreeMap<FontFamily, Vec<String>>, family: FontFamily, key: String) {
272 let list = families.entry(family).or_default();
273 if list.iter().any(|k| k == &key) {
274 return;
275 }
276 list.insert(0, key);
277}
278
279fn insert_back(families: &mut BTreeMap<FontFamily, Vec<String>>, family: FontFamily, key: String) {
280 let list = families.entry(family).or_default();
281 if list.iter().any(|k| k == &key) {
282 return;
283 }
284 list.push(key);
285}