Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 41 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,10 @@ impl FromLuaTypst for LuaTable {

fn compile(
lua: &Lua,
(input, data): (LuaString, LuaValue),
(input, data, format): (LuaString, LuaValue, LuaString),
) -> LuaResult<(Option<LuaString>, Option<LuaString>)> {
let input_text = input.to_str()?.to_string();
let format_text = format.to_str()?.to_string();

let typst_value_opt = match data {
LuaValue::Table(_) => {
Expand All @@ -141,7 +142,44 @@ fn compile(
};

// Call typst compiler
let pdf_bytes = match typst_as_library::compile(&input_text, &typst_value_opt) {
let pdf_bytes = match typst_as_library::compile(&input_text, &typst_value_opt, &format_text) {
Ok(bytes) => bytes,
Err(e) => {
let err_msg = lua.create_string(&format!("typst: {e}"))?;
return Ok((None, Some(err_msg)));
}
};

// Convert result to lua string
let pdf = lua.create_string(&pdf_bytes)?;
Ok((Some(pdf), None))
}

fn compile_text(
lua: &Lua,
(input, data, format): (LuaString, LuaValue, LuaString),
) -> LuaResult<(Option<LuaString>, Option<LuaString>)> {
let input_text = input.to_str()?.to_string();
let format_text = format.to_str()?.to_string();

let typst_value_opt = match data {
LuaValue::Table(_) => {
// Only now do we attempt conversion
match data.to_typst(lua) {
Ok(val) => Some(val),
Err(e) => {
let err_msg = lua.create_string(&format!(
"typst-lua: error converting lua table to typst value: {e}"
))?;
return Ok((None, Some(err_msg)));
}
}
}
_ => None,
};

// Call typst compiler
let pdf_bytes = match typst_as_library::compile_text(&input_text, &typst_value_opt, &format_text) {
Ok(bytes) => bytes,
Err(e) => {
let err_msg = lua.create_string(&format!("typst: {e}"))?;
Expand All @@ -162,5 +200,6 @@ fn compile(
fn typst(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("compile", lua.create_function(compile)?)?;
exports.set("compile_text", lua.create_function(compile_text)?)?;
Ok(exports)
}
1 change: 1 addition & 0 deletions typst-as-library/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ ureq = "2.9"
time = "0.3"
ttf-parser = "0.25"
typst-kit = { version = "0.14", features = ["embed-fonts"] }
typst-html = "0.14"
typst-pdf = "0.14"
codespan-reporting = "0.11"

Expand Down
39 changes: 31 additions & 8 deletions typst-as-library/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl TypstWrapperWorld {
let root = PathBuf::from(root);
let fonts = FontSearcher::new().include_system_fonts(true).search();
let lib = {
let builder = Library::builder();
let builder = Library::builder().with_features(typst::Features::from_iter([typst::Feature::Html]));
let builder = if let Some(d) = data {
let dict: Dict = d.clone().cast::<Dict>().unwrap();
builder.with_inputs(dict)
Expand Down Expand Up @@ -265,7 +265,16 @@ fn retry<T, E>(mut f: impl FnMut() -> Result<T, E>) -> Result<T, E> {
}
}

pub fn compile(input: &str, data: &Option<Value>) -> Result<Vec<u8>, String> {
pub fn compile_text(input: &str, data: &Option<Value>, format: &str) -> Result<Vec<u8>, String> {
let current_dir = std::env::current_dir().unwrap();
let root = current_dir.as_path();
let spath = root.to_string_lossy().into_owned();
let source_filename = "<stdin>";
let world = TypstWrapperWorld::new(spath, input.to_string(), source_filename.to_string(), data.clone());
compile_world(world, format)
}

pub fn compile(input: &str, data: &Option<Value>, format: &str) -> Result<Vec<u8>, String> {
let input_path = Path::new(input);
let root = input_path.parent().unwrap_or(Path::new("."));
let content = fs::read_to_string(input)
Expand All @@ -275,14 +284,28 @@ pub fn compile(input: &str, data: &Option<Value>) -> Result<Vec<u8>, String> {
.file_name()
.and_then(|n| n.to_str())
.ok_or_else(|| format!("invalid input path: {input}"))?;

let world = TypstWrapperWorld::new(spath, content, source_filename.to_string(), data.clone());
let Warned { output, warnings } = typst::compile(&world);
compile_world(world, format)
}

match output {
Ok(document) => typst_pdf::pdf(&document, &PdfOptions::default())
.map_err(|errors| render_diagnostics(&world, &errors, &warnings)),
Err(errors) => Err(render_diagnostics(&world, &errors, &warnings)),
fn compile_world(world: TypstWrapperWorld, format: &str) -> Result<Vec<u8>, String> {
if format == "html" {
let Warned { output, warnings } = typst::compile::<typst_html::HtmlDocument>(&world);

match output {
Ok(document) => typst_html::html(&document)
.map(|s| s.into_bytes())
.map_err(|errors| render_diagnostics(&world, &errors, &warnings)),
Err(errors) => Err(render_diagnostics(&world, &errors, &warnings)),
}
} else {
let Warned { output, warnings } = typst::compile(&world);

match output {
Ok(document) => typst_pdf::pdf(&document, &PdfOptions::default())
.map_err(|errors| render_diagnostics(&world, &errors, &warnings)),
Err(errors) => Err(render_diagnostics(&world, &errors, &warnings)),
}
}
}

Expand Down