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