tinymist_task/compute/
png.rs1use std::sync::Arc;
4
5use tinymist_std::error::prelude::*;
6use tinymist_std::typst::TypstPagedDocument;
7use tinymist_world::{CompilerFeat, ExportComputation, WorldComputeGraph};
8use typst::foundations::Bytes;
9
10use crate::compute::{parse_color, parse_length, select_pages};
11use crate::model::ExportPngTask;
12use crate::{ImageOutput, PageMerge, PagedOutput};
13
14pub struct PngExport;
16
17impl<F: CompilerFeat> ExportComputation<F, TypstPagedDocument> for PngExport {
18 type Output = ImageOutput<Bytes>;
19 type Config = ExportPngTask;
20
21 fn run(
22 _graph: &Arc<WorldComputeGraph<F>>,
23 doc: &Arc<TypstPagedDocument>,
24 config: &ExportPngTask,
25 ) -> Result<Self::Output> {
26 let ppi = config.ppi.to_f32();
27 if ppi <= 1e-6 {
28 bail!("invalid ppi: {ppi}");
29 }
30
31 let fill = if let Some(fill) = &config.fill {
32 Some(parse_color(fill).map_err(|err| anyhow::anyhow!("invalid fill ({err})"))?)
33 } else {
34 None
35 };
36
37 let ppp = ppi / 72.;
38
39 let exported_pages = select_pages(doc, &config.pages);
40 if let Some(PageMerge { ref gap }) = config.merge {
41 let dummy_doc = TypstPagedDocument {
42 pages: exported_pages
43 .into_iter()
44 .map(|(_, page)| page.clone())
45 .collect(),
46 ..Default::default()
47 };
48 let gap = gap
49 .as_ref()
50 .and_then(|gap| parse_length(gap).ok())
51 .unwrap_or_default();
52 let pixmap = typst_render::render_merged(&dummy_doc, ppp, gap, fill);
53 let png = pixmap
54 .encode_png()
55 .map(Bytes::new)
56 .context_ut("failed to encode PNG")?;
57 Ok(ImageOutput::Merged(png))
58 } else {
59 let exported = exported_pages
60 .into_iter()
61 .map(|(i, page)| {
62 let pixmap = typst_render::render(page, ppp);
63 let png = pixmap
64 .encode_png()
65 .map(Bytes::new)
66 .context_ut("failed to encode PNG")?;
67 Ok(PagedOutput {
68 page: i,
69 value: png,
70 })
71 })
72 .collect::<Result<Vec<_>>>()?;
73 Ok(ImageOutput::Paged(exported))
74 }
75 }
76}
77
78