use std::collections::HashMap;
use typst::diag::{FileError, FileResult};
use typst::foundations::{Bytes, Datetime};
use typst::syntax::{FileId, Source};
use typst::text::{Font, FontBook};
use typst::utils::LazyHash;
use typst::{Library, World};
#[allow(dead_code)]
pub struct ReconWorld {
library: LazyHash<Library>,
book: LazyHash<FontBook>,
fonts: Vec<Font>,
main: Source,
#[allow(dead_code)]
files: HashMap<FileId, Bytes>,
}
const BUNDLED_FONTS: &[&[u8]] = &[
include_bytes!("../../assets/fonts/IBMPlexSans-Regular.ttf"),
include_bytes!("../../assets/fonts/IBMPlexSans-Italic.ttf"),
include_bytes!("../../assets/fonts/IBMPlexSans-Bold.ttf"),
include_bytes!("../../assets/fonts/IBMPlexSans-BoldItalic.ttf"),
];
#[allow(dead_code)]
impl ReconWorld {
pub fn new(main_src: String, files: HashMap<FileId, Bytes>, font_dirs: &[String]) -> Self {
let mut fonts: Vec<Font> = typst_assets::fonts()
.flat_map(|data| Font::iter(Bytes::new(data)))
.collect();
for data in BUNDLED_FONTS {
fonts.extend(Font::iter(Bytes::new(*data)));
}
for dir in font_dirs {
load_fonts_from_dir(std::path::Path::new(dir), &mut fonts);
}
let book = FontBook::from_fonts(&fonts);
let main = Source::detached(main_src);
Self {
library: LazyHash::new(Library::default()),
book: LazyHash::new(book),
fonts,
main,
files,
}
}
}
fn load_fonts_from_dir(dir: &std::path::Path, fonts: &mut Vec<Font>) {
let entries = match std::fs::read_dir(dir) {
Ok(e) => e,
Err(_) => return,
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
load_fonts_from_dir(&path, fonts);
continue;
}
let is_font = path
.extension()
.and_then(|e| e.to_str())
.map(|e| matches!(e.to_ascii_lowercase().as_str(), "ttf" | "otf" | "ttc" | "otc"))
.unwrap_or(false);
if !is_font {
continue;
}
if let Ok(data) = std::fs::read(&path) {
fonts.extend(Font::iter(Bytes::new(data)));
}
}
}
impl World for ReconWorld {
fn library(&self) -> &LazyHash<Library> {
&self.library
}
fn book(&self) -> &LazyHash<FontBook> {
&self.book
}
fn main(&self) -> FileId {
self.main.id()
}
fn source(&self, id: FileId) -> FileResult<Source> {
if id == self.main.id() {
Ok(self.main.clone())
} else {
Err(FileError::NotFound(id.vpath().as_rootless_path().into()))
}
}
fn file(&self, id: FileId) -> FileResult<Bytes> {
self.files
.get(&id)
.cloned()
.ok_or_else(|| FileError::NotFound(id.vpath().as_rootless_path().into()))
}
fn font(&self, index: usize) -> Option<Font> {
self.fonts.get(index).cloned()
}
fn today(&self, _offset: Option<i64>) -> Option<Datetime> {
None
}
}