tinymist_world/font/
resolver.rsuse core::fmt;
use std::{num::NonZeroUsize, path::PathBuf, sync::Arc};
use typst::text::{Font, FontBook, FontInfo};
use typst::utils::LazyHash;
use super::FontSlot;
use crate::debug_loc::DataSource;
pub trait FontResolver {
fn revision(&self) -> Option<NonZeroUsize> {
None
}
fn font_book(&self) -> &LazyHash<FontBook>;
fn slot(&self, index: usize) -> Option<&FontSlot>;
fn font(&self, index: usize) -> Option<Font>;
fn get_by_info(&self, info: &FontInfo) -> Option<Font> {
self.default_get_by_info(info)
}
fn default_get_by_info(&self, info: &FontInfo) -> Option<Font> {
let mut alternative_text = 'c';
if let Some(codepoint) = info.coverage.iter().next() {
alternative_text = std::char::from_u32(codepoint).unwrap();
};
let index = self
.font_book()
.select_fallback(Some(info), info.variant, &alternative_text.to_string())
.unwrap();
self.font(index)
}
}
impl<T: FontResolver> FontResolver for Arc<T> {
fn font_book(&self) -> &LazyHash<FontBook> {
self.as_ref().font_book()
}
fn slot(&self, index: usize) -> Option<&FontSlot> {
self.as_ref().slot(index)
}
fn font(&self, index: usize) -> Option<Font> {
self.as_ref().font(index)
}
fn get_by_info(&self, info: &FontInfo) -> Option<Font> {
self.as_ref().get_by_info(info)
}
}
pub trait ReusableFontResolver: FontResolver {
fn slots(&self) -> impl Iterator<Item = FontSlot>;
}
impl<T: ReusableFontResolver> ReusableFontResolver for Arc<T> {
fn slots(&self) -> impl Iterator<Item = FontSlot> {
self.as_ref().slots()
}
}
#[derive(Debug)]
pub struct FontResolverImpl {
pub(crate) font_paths: Vec<PathBuf>,
pub(crate) book: LazyHash<FontBook>,
pub(crate) slots: Vec<FontSlot>,
}
impl FontResolverImpl {
pub fn new(font_paths: Vec<PathBuf>, book: FontBook, slots: Vec<FontSlot>) -> Self {
Self {
font_paths,
book: LazyHash::new(book),
slots,
}
}
pub fn new_with_fonts(
font_paths: Vec<PathBuf>,
fonts: impl Iterator<Item = (FontInfo, FontSlot)>,
) -> Self {
let mut book = FontBook::new();
let mut slots = Vec::<FontSlot>::new();
for (info, slot) in fonts {
book.push(info);
slots.push(slot);
}
Self {
font_paths,
book: LazyHash::new(book),
slots,
}
}
pub fn len(&self) -> usize {
self.slots.len()
}
pub fn is_empty(&self) -> bool {
self.slots.is_empty()
}
pub fn font_paths(&self) -> &[PathBuf] {
&self.font_paths
}
#[deprecated(note = "use `fonts` instead")]
pub fn get_fonts(&self) -> impl Iterator<Item = (&FontInfo, &FontSlot)> {
self.fonts()
}
pub fn fonts(&self) -> impl Iterator<Item = (&FontInfo, &FontSlot)> {
self.slots.iter().enumerate().map(|(idx, slot)| {
let info = self.book.info(idx).unwrap();
(info, slot)
})
}
pub fn loaded_fonts(&self) -> impl Iterator<Item = (usize, Font)> + '_ {
self.slots.iter().enumerate().flat_map(|(idx, slot)| {
let maybe_font = slot.get_uninitialized().flatten();
maybe_font.map(|font| (idx, font))
})
}
pub fn describe_font(&self, font: &Font) -> Option<Arc<DataSource>> {
let f = Some(Some(font.clone()));
for slot in &self.slots {
if slot.get_uninitialized() == f {
return slot.description.clone();
}
}
None
}
pub fn describe_font_by_id(&self, id: usize) -> Option<Arc<DataSource>> {
self.slots[id].description.clone()
}
pub fn with_font_paths(mut self, font_paths: Vec<PathBuf>) -> Self {
self.font_paths = font_paths;
self
}
}
impl FontResolver for FontResolverImpl {
fn font_book(&self) -> &LazyHash<FontBook> {
&self.book
}
fn slot(&self, idx: usize) -> Option<&FontSlot> {
self.slots.get(idx)
}
fn font(&self, idx: usize) -> Option<Font> {
self.slots[idx].get_or_init()
}
fn get_by_info(&self, info: &FontInfo) -> Option<Font> {
FontResolver::default_get_by_info(self, info)
}
}
impl fmt::Display for FontResolverImpl {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (idx, slot) in self.slots.iter().enumerate() {
writeln!(f, "{:?} -> {:?}", idx, slot.get_uninitialized())?;
}
Ok(())
}
}
impl ReusableFontResolver for FontResolverImpl {
fn slots(&self) -> impl Iterator<Item = FontSlot> {
self.slots.iter().cloned()
}
}