1pub mod compile;
38pub mod convert;
39mod error_mapping;
40mod filters;
41mod world;
42use filters::{
43 asset_filter, content_filter, date_filter, dict_filter, lines_filter, string_filter,
44};
45use quillmark_core::{Artifact, Backend, Glue, OutputFormat, Quill, RenderError, RenderOptions};
46
47pub struct TypstBackend;
49
50impl Backend for TypstBackend {
51 fn id(&self) -> &'static str {
52 "typst"
53 }
54
55 fn supported_formats(&self) -> &'static [OutputFormat] {
56 &[OutputFormat::Pdf, OutputFormat::Svg]
57 }
58
59 fn glue_type(&self) -> &'static str {
60 ".typ"
61 }
62
63 fn register_filters(&self, glue: &mut Glue) {
64 glue.register_filter("String", string_filter);
66 glue.register_filter("Lines", lines_filter);
67 glue.register_filter("Date", date_filter);
68 glue.register_filter("Dict", dict_filter);
69 glue.register_filter("Content", content_filter);
70 glue.register_filter("Asset", asset_filter);
71 }
72
73 fn compile(
74 &self,
75 glued_content: &str,
76 quill: &Quill,
77 opts: &RenderOptions,
78 ) -> Result<Vec<Artifact>, RenderError> {
79 let format = opts.output_format.unwrap_or(OutputFormat::Pdf);
80
81 if !self.supported_formats().contains(&format) {
83 return Err(RenderError::FormatNotSupported {
84 backend: self.id().to_string(),
85 format,
86 });
87 }
88
89 println!("Typst backend compiling for quill: {}", quill.name);
90
91 match format {
92 OutputFormat::Pdf => {
93 let bytes = compile::compile_to_pdf(quill, glued_content)?;
94 Ok(vec![Artifact {
95 bytes,
96 output_format: OutputFormat::Pdf,
97 }])
98 }
99 OutputFormat::Svg => {
100 let svg_pages = compile::compile_to_svg(quill, glued_content)?;
101 Ok(svg_pages
102 .into_iter()
103 .map(|bytes| Artifact {
104 bytes,
105 output_format: OutputFormat::Svg,
106 })
107 .collect())
108 }
109 OutputFormat::Txt => Err(RenderError::FormatNotSupported {
110 backend: self.id().to_string(),
111 format: OutputFormat::Txt,
112 }),
113 }
114 }
115}
116
117impl Default for TypstBackend {
118 fn default() -> Self {
120 Self
121 }
122}
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn test_backend_info() {
129 let backend = TypstBackend::default();
130 assert_eq!(backend.id(), "typst");
131 assert_eq!(backend.glue_type(), ".typ");
132 assert!(backend.supported_formats().contains(&OutputFormat::Pdf));
133 assert!(backend.supported_formats().contains(&OutputFormat::Svg));
134 }
135}
136
137pub use TypstBackend as backend;