use std::fs;
use std::sync::Arc;
use embedded_typst::{EmbeddedWorld, Fonts};
use typst::foundations::{Dict, IntoValue};
use typst::syntax::VirtualPath;
use typst::World;
use typst_layout::PagedDocument;
fn fonts() -> Arc<Fonts> {
Arc::new(Fonts::builder().typst_fonts().build())
}
fn vpath(path: &str) -> VirtualPath {
VirtualPath::new(path).unwrap()
}
fn text(world: &EmbeddedWorld) -> String {
let document = typst::compile::<PagedDocument>(world)
.output
.unwrap_or_else(|errors| panic!("{errors:?}"));
let mut text = String::new();
for page in document.pages() {
collect(&page.frame, &mut text);
}
text
}
fn collect(frame: &typst::layout::Frame, text: &mut String) {
for (_, item) in frame.items() {
match item {
typst::layout::FrameItem::Group(group) => collect(&group.frame, text),
typst::layout::FrameItem::Text(item) => text.push_str(item.text.as_str()),
_ => {}
}
}
}
#[test]
fn in_memory_files_are_imported() {
let world = EmbeddedWorld::new(vpath("main.typ"), "#import \"lib.typ\": x\n#x", fonts());
world.add_file(vpath("lib.typ"), b"#let x = [memory]".to_vec());
assert_eq!(text(&world), "memory");
}
#[test]
fn roots_are_searched_in_order() {
let first = tempfile::tempdir().unwrap();
let second = tempfile::tempdir().unwrap();
fs::write(second.path().join("a.typ"), "#let a = [second-a]").unwrap();
fs::write(second.path().join("b.typ"), "#let b = [second-b]").unwrap();
fs::write(first.path().join("a.typ"), "#let a = [first-a]").unwrap();
let world = EmbeddedWorld::new(
vpath("main.typ"),
"#import \"a.typ\": a\n#import \"b.typ\": b\n#a #b",
fonts(),
)
.with_roots(vec![first.path().into(), second.path().into()]);
assert_eq!(text(&world), "first-a second-b");
let mut names: Vec<_> = world.dependencies().into_iter().map(|d| d.name).collect();
names.sort();
assert_eq!(names, ["a.typ", "b.typ"]);
}
#[test]
fn missing_files_are_errors() {
let world = EmbeddedWorld::new(vpath("main.typ"), "#import \"nope.typ\": x", fonts());
assert!(typst::compile::<PagedDocument>(&world).output.is_err());
}
#[test]
fn inputs_reach_sys_inputs() {
let mut inputs = Dict::new();
inputs.insert("name".into(), "Ada".into_value());
let world =
EmbeddedWorld::new(vpath("main.typ"), "#sys.inputs.name", fonts()).with_inputs(inputs);
assert_eq!(text(&world), "Ada");
}
#[test]
fn creation_timestamp_fixes_today() {
let world = EmbeddedWorld::new(vpath("main.typ"), "#datetime.today().display()", fonts())
.with_creation_timestamp(1_700_000_000);
assert_eq!(text(&world), "2023-11-14");
}
#[test]
fn font_files_load_lazily() {
let dir = tempfile::tempdir().unwrap();
let data = typst_assets::fonts().next().unwrap();
fs::write(dir.path().join("font.otf"), data).unwrap();
let fonts = Fonts::builder().path(dir.path()).build();
assert!(fonts.book().families().count() > 0);
assert!(fonts.loaded_files().is_empty());
assert!(fonts.font(0).is_some());
assert_eq!(fonts.loaded_files().len(), 1);
}
#[test]
fn data_fonts_are_registered() {
let data = typst_assets::fonts().next().unwrap();
let fonts = Fonts::builder().data(data).build();
assert!(fonts.font(0).is_some());
let world = EmbeddedWorld::new(vpath("main.typ"), "", Arc::new(fonts));
assert!(world.book().families().count() > 0);
}
#[cfg(feature = "packages")]
#[test]
fn local_packages_resolve() {
let data = tempfile::tempdir().unwrap();
let cache = tempfile::tempdir().unwrap();
let package = data.path().join("local/greet/0.1.0");
fs::create_dir_all(&package).unwrap();
fs::write(
package.join("typst.toml"),
"[package]\nname = \"greet\"\nversion = \"0.1.0\"\nentrypoint = \"lib.typ\"\n",
)
.unwrap();
fs::write(package.join("lib.typ"), "#let hello = [package]").unwrap();
let packages =
embedded_typst::Packages::new(Some(data.path().into()), Some(cache.path().into()));
let world = EmbeddedWorld::new(
vpath("main.typ"),
"#import \"@local/greet:0.1.0\": hello\n#hello",
fonts(),
)
.with_packages(packages);
assert_eq!(text(&world), "package");
let mut names: Vec<_> = world.dependencies().into_iter().map(|d| d.name).collect();
names.sort();
assert_eq!(
names,
[
"@local/greet:0.1.0/lib.typ",
"@local/greet:0.1.0/typst.toml"
]
);
}