tinymist_task/compute/
pdf.rs

1//! The computation for pdf export.
2
3use tinymist_std::time::ToUtcDateTime;
4use tinymist_world::args::PdfStandard;
5pub use typst_pdf::PdfStandard as TypstPdfStandard;
6pub use typst_pdf::pdf;
7
8use typst_pdf::{PdfOptions, PdfStandards, Timestamp};
9
10use super::*;
11use crate::model::ExportPdfTask;
12
13/// The computation for pdf export.
14pub struct PdfExport;
15
16impl<F: CompilerFeat> ExportComputation<F, TypstPagedDocument> for PdfExport {
17    type Output = Bytes;
18    type Config = ExportPdfTask;
19
20    fn run(
21        _graph: &Arc<WorldComputeGraph<F>>,
22        doc: &Arc<TypstPagedDocument>,
23        config: &ExportPdfTask,
24    ) -> Result<Bytes> {
25        let creation_timestamp = config
26            .creation_timestamp
27            .map(|ts| ts.to_utc_datetime().context("timestamp is out of range"))
28            .transpose()?
29            .unwrap_or_else(tinymist_std::time::utc_now);
30        // todo: this seems different from `Timestamp::new_local` which also embeds the
31        // timezone information.
32        let timestamp = Timestamp::new_utc(tinymist_std::time::to_typst_time(creation_timestamp));
33
34        let standards = PdfStandards::new(
35            &config
36                .pdf_standards
37                .iter()
38                .map(|standard| match standard {
39                    tinymist_world::args::PdfStandard::V_1_4 => typst_pdf::PdfStandard::V_1_4,
40                    tinymist_world::args::PdfStandard::V_1_5 => typst_pdf::PdfStandard::V_1_5,
41                    tinymist_world::args::PdfStandard::V_1_6 => typst_pdf::PdfStandard::V_1_6,
42                    tinymist_world::args::PdfStandard::V_1_7 => typst_pdf::PdfStandard::V_1_7,
43                    tinymist_world::args::PdfStandard::V_2_0 => typst_pdf::PdfStandard::V_2_0,
44                    tinymist_world::args::PdfStandard::A_1b => typst_pdf::PdfStandard::A_1b,
45                    tinymist_world::args::PdfStandard::A_1a => typst_pdf::PdfStandard::A_1a,
46                    tinymist_world::args::PdfStandard::A_2b => typst_pdf::PdfStandard::A_2b,
47                    tinymist_world::args::PdfStandard::A_2u => typst_pdf::PdfStandard::A_2u,
48                    tinymist_world::args::PdfStandard::A_2a => typst_pdf::PdfStandard::A_2a,
49                    tinymist_world::args::PdfStandard::A_3b => typst_pdf::PdfStandard::A_3b,
50                    tinymist_world::args::PdfStandard::A_3u => typst_pdf::PdfStandard::A_3u,
51                    tinymist_world::args::PdfStandard::A_3a => typst_pdf::PdfStandard::A_3a,
52                    tinymist_world::args::PdfStandard::A_4 => typst_pdf::PdfStandard::A_4,
53                    tinymist_world::args::PdfStandard::A_4f => typst_pdf::PdfStandard::A_4f,
54                    tinymist_world::args::PdfStandard::A_4e => typst_pdf::PdfStandard::A_4e,
55                    tinymist_world::args::PdfStandard::Ua_1 => typst_pdf::PdfStandard::Ua_1,
56                })
57                .collect::<Vec<_>>(),
58        )
59        .context_ut("prepare pdf standards")?;
60
61        let tagged = !config.no_pdf_tags && config.pages.is_none();
62        // todo: emit warning diag
63        if config.pages.is_some() && !config.no_pdf_tags {
64            log::warn!(
65                "the resulting PDF will be inaccessible because using --pages implies --no-pdf-tags"
66            );
67        }
68        if !tagged {
69            const ACCESSIBLE: &[(PdfStandard, &str)] = &[
70                (PdfStandard::A_1a, "PDF/A-1a"),
71                (PdfStandard::A_2a, "PDF/A-2a"),
72                (PdfStandard::A_3a, "PDF/A-3a"),
73                (PdfStandard::Ua_1, "PDF/UA-1"),
74            ];
75
76            for (standard, name) in ACCESSIBLE {
77                if config.pdf_standards.contains(standard) {
78                    if config.no_pdf_tags {
79                        log::warn!("cannot disable PDF tags when exporting a {name} document");
80                    } else {
81                        log::warn!(
82                            "cannot disable PDF tags when exporting a {name} document. Hint: using --pages implies --no-pdf-tags"
83                        );
84                    }
85                }
86            }
87        }
88
89        let options = PdfOptions {
90            page_ranges: config
91                .pages
92                .as_ref()
93                .map(|pages| exported_page_ranges(pages)),
94            timestamp: Some(timestamp),
95            standards,
96            tagged,
97            ..Default::default()
98        };
99        // log::info!("used options for pdf export: {options:?}");
100
101        // todo: Some(pdf_uri.as_str())
102        // todo: ident option
103        Ok(Bytes::new(typst_pdf::pdf(doc, &options)?))
104    }
105}