diff --git a/Cargo.lock b/Cargo.lock index d562890..0a5ccc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2463,6 +2463,7 @@ dependencies = [ "time", "ttf-parser", "typst", + "typst-html", "typst-kit", "typst-pdf", "ureq", diff --git a/src/lib.rs b/src/lib.rs index 6613536..975db78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -120,9 +120,10 @@ impl FromLuaTypst for LuaTable { fn compile( lua: &Lua, - (input, data): (LuaString, LuaValue), + (input, data, format): (LuaString, LuaValue, LuaString), ) -> LuaResult<(Option, Option)> { let input_text = input.to_str()?.to_string(); + let format_text = format.to_str()?.to_string(); let typst_value_opt = match data { LuaValue::Table(_) => { @@ -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, Option)> { + 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}"))?; @@ -162,5 +200,6 @@ fn compile( fn typst(lua: &Lua) -> LuaResult { let exports = lua.create_table()?; exports.set("compile", lua.create_function(compile)?)?; + exports.set("compile_text", lua.create_function(compile_text)?)?; Ok(exports) } diff --git a/typst-as-library/Cargo.toml b/typst-as-library/Cargo.toml index 4497172..9bc5eac 100644 --- a/typst-as-library/Cargo.toml +++ b/typst-as-library/Cargo.toml @@ -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" diff --git a/typst-as-library/src/lib.rs b/typst-as-library/src/lib.rs index 9ab4f42..c9b9161 100644 --- a/typst-as-library/src/lib.rs +++ b/typst-as-library/src/lib.rs @@ -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::().unwrap(); builder.with_inputs(dict) @@ -265,7 +265,16 @@ fn retry(mut f: impl FnMut() -> Result) -> Result { } } -pub fn compile(input: &str, data: &Option) -> Result, String> { +pub fn compile_text(input: &str, data: &Option, format: &str) -> Result, 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 = ""; + 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, format: &str) -> Result, String> { let input_path = Path::new(input); let root = input_path.parent().unwrap_or(Path::new(".")); let content = fs::read_to_string(input) @@ -275,14 +284,28 @@ pub fn compile(input: &str, data: &Option) -> Result, 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, String> { + if format == "html" { + let Warned { output, warnings } = typst::compile::(&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)), + } } }