tinymist_task/compute/
png.rs

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