use serde::Serialize;
use typst::WorldExt;
use typst::World;
use typst_pdf::PdfOptions;
use crate::error::Result;
use crate::template::Template;
use crate::typst::TypstWorld;
use crate::PapermakeError;
#[derive(Debug, Clone)]
pub struct RenderOptions {
pub paper_size: String,
pub compress: bool,
}
impl Default for RenderOptions {
fn default() -> Self {
RenderOptions {
paper_size: "a4".to_string(),
compress: true,
}
}
}
#[derive(Debug, Serialize)]
pub struct RenderError {
pub message: String,
pub start: usize,
pub end: usize
}
#[derive(Debug, Serialize)]
pub struct RenderResult {
pub pdf: Option<Vec<u8>>,
pub errors: Vec<RenderError>,
}
pub fn render_pdf(
template: &Template,
data: &serde_json::Value,
_options: Option<RenderOptions>,
) -> Result<RenderResult> {
template.validate_data(data)?;
let world = TypstWorld::new(
template.content.clone(),
serde_json::to_string(&data).map_err(|e| PapermakeError::Rendering(e.to_string()))?,
);
let compile_result = typst::compile(&world);
let mut errors = Vec::new();
let mut pdf = None;
match compile_result.output {
Ok(document) => {
pdf = Some(typst_pdf::pdf(&document, &PdfOptions::default()).unwrap());
}
Err(diagnostics) => {
for diagnostic in diagnostics {
let span = diagnostic.span;
if let Some(id) = span.id() {
if let Ok(_file) = world.source(id) {
if let Some(range) = world.range(span) {
errors.push(RenderError {
message: diagnostic.message.to_string(),
start: range.start,
end: range.end,});
}
}
}
}
}
}
Ok(RenderResult {
pdf,
errors,
})
}
pub fn render_pdf_with_cache(
template: &Template,
data: &serde_json::Value,
world_cache: Option<&mut TypstWorld>, _options: Option<RenderOptions>,
) -> Result<RenderResult> {
template.validate_data(data)?;
let world = match world_cache {
Some(cached_world) => {
cached_world.update_data(
serde_json::to_string(&data).map_err(|e| PapermakeError::Rendering(e.to_string()))?,
).map_err(|e| PapermakeError::Rendering(e.to_string()))?;
cached_world
}
None => &mut TypstWorld::new(
template.content.clone(),
serde_json::to_string(&data).map_err(|e| PapermakeError::Rendering(e.to_string()))?,
),
};
let compile_result = typst::compile(world as &dyn World);
let mut errors = Vec::new();
let mut pdf = None;
match compile_result.output {
Ok(document) => {
pdf = Some(typst_pdf::pdf(&document, &PdfOptions::default()).unwrap());
}
Err(diagnostics) => {
for diagnostic in diagnostics {
let span = diagnostic.span;
if let Some(id) = span.id() {
if let Ok(_file) = world.source(id) {
if let Some(range) = world.range(span) {
errors.push(RenderError {
message: diagnostic.message.to_string(),
start: range.start,
end: range.end,});
}
}
}
}
}
}
Ok(RenderResult {
pdf,
errors,
})
}