1mod attach;
4mod convert;
5mod image;
6mod link;
7mod metadata;
8mod outline;
9mod page;
10mod paint;
11mod shape;
12mod tags;
13mod text;
14mod util;
15
16pub use self::metadata::{Timestamp, Timezone};
17
18use std::fmt::{self, Debug, Formatter};
19
20use ecow::eco_format;
21use krilla::configure::Validator;
22use serde::{Deserialize, Serialize};
23use typst_library::diag::{SourceResult, StrResult, bail};
24use typst_library::foundations::Smart;
25use typst_library::layout::{PageRanges, PagedDocument};
26
27#[typst_macros::time(name = "pdf")]
31pub fn pdf(document: &PagedDocument, options: &PdfOptions) -> SourceResult<Vec<u8>> {
32 convert::convert(document, options)
33}
34
35#[doc(hidden)]
37pub fn pdf_tags(document: &PagedDocument, options: &PdfOptions) -> SourceResult<String> {
38 convert::tag_tree(document, options)
39}
40
41#[derive(Debug)]
43pub struct PdfOptions<'a> {
44 pub ident: Smart<&'a str>,
56 pub timestamp: Option<Timestamp>,
59 pub page_ranges: Option<PageRanges>,
62 pub standards: PdfStandards,
64 pub tagged: bool,
69}
70
71impl PdfOptions<'_> {
72 pub(crate) fn is_pdf_ua(&self) -> bool {
75 self.standards.config.validator() == Validator::UA1
76 }
77}
78
79impl Default for PdfOptions<'_> {
80 fn default() -> Self {
81 Self {
82 ident: Smart::Auto,
83 timestamp: None,
84 page_ranges: None,
85 standards: PdfStandards::default(),
86 tagged: true,
87 }
88 }
89}
90
91#[derive(Clone)]
93pub struct PdfStandards {
94 pub(crate) config: krilla::configure::Configuration,
95}
96
97impl PdfStandards {
98 pub fn new(list: &[PdfStandard]) -> StrResult<Self> {
101 use krilla::configure::{Configuration, PdfVersion, Validator};
102
103 let mut version: Option<PdfVersion> = None;
104 let mut set_version = |v: PdfVersion| -> StrResult<()> {
105 if let Some(prev) = version {
106 bail!(
107 "PDF cannot conform to {} and {} at the same time",
108 prev.as_str(),
109 v.as_str()
110 );
111 }
112 version = Some(v);
113 Ok(())
114 };
115
116 let mut validator = None;
117 let mut set_validator = |v: Validator| -> StrResult<()> {
118 if validator.is_some() {
119 bail!("Typst currently only supports one PDF substandard at a time");
120 }
121 validator = Some(v);
122 Ok(())
123 };
124
125 for standard in list {
126 match standard {
127 PdfStandard::V_1_4 => set_version(PdfVersion::Pdf14)?,
128 PdfStandard::V_1_5 => set_version(PdfVersion::Pdf15)?,
129 PdfStandard::V_1_6 => set_version(PdfVersion::Pdf16)?,
130 PdfStandard::V_1_7 => set_version(PdfVersion::Pdf17)?,
131 PdfStandard::V_2_0 => set_version(PdfVersion::Pdf20)?,
132 PdfStandard::A_1b => set_validator(Validator::A1_B)?,
133 PdfStandard::A_1a => set_validator(Validator::A1_A)?,
134 PdfStandard::A_2b => set_validator(Validator::A2_B)?,
135 PdfStandard::A_2u => set_validator(Validator::A2_U)?,
136 PdfStandard::A_2a => set_validator(Validator::A2_A)?,
137 PdfStandard::A_3b => set_validator(Validator::A3_B)?,
138 PdfStandard::A_3u => set_validator(Validator::A3_U)?,
139 PdfStandard::A_3a => set_validator(Validator::A3_A)?,
140 PdfStandard::A_4 => set_validator(Validator::A4)?,
141 PdfStandard::A_4f => set_validator(Validator::A4F)?,
142 PdfStandard::A_4e => set_validator(Validator::A4E)?,
143 PdfStandard::Ua_1 => set_validator(Validator::UA1)?,
144 }
145 }
146
147 let config = match (version, validator) {
148 (Some(version), Some(validator)) => {
149 Configuration::new_with(validator, version).ok_or_else(|| {
150 eco_format!(
151 "{} is not compatible with {}",
152 version.as_str(),
153 validator.as_str()
154 )
155 })?
156 }
157 (Some(version), None) => Configuration::new_with_version(version),
158 (None, Some(validator)) => Configuration::new_with_validator(validator),
159 (None, None) => Configuration::new_with_version(PdfVersion::Pdf17),
160 };
161
162 Ok(Self { config })
163 }
164}
165
166impl Debug for PdfStandards {
167 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
168 f.pad("PdfStandards(..)")
169 }
170}
171
172impl Default for PdfStandards {
173 fn default() -> Self {
174 use krilla::configure::{Configuration, PdfVersion};
175 Self {
176 config: Configuration::new_with_version(PdfVersion::Pdf17),
177 }
178 }
179}
180
181#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
185#[allow(non_camel_case_types)]
186#[non_exhaustive]
187pub enum PdfStandard {
188 #[serde(rename = "1.4")]
190 V_1_4,
191 #[serde(rename = "1.5")]
193 V_1_5,
194 #[serde(rename = "1.6")]
196 V_1_6,
197 #[serde(rename = "1.7")]
199 V_1_7,
200 #[serde(rename = "2.0")]
202 V_2_0,
203 #[serde(rename = "a-1b")]
205 A_1b,
206 #[serde(rename = "a-1a")]
208 A_1a,
209 #[serde(rename = "a-2b")]
211 A_2b,
212 #[serde(rename = "a-2u")]
214 A_2u,
215 #[serde(rename = "a-2a")]
217 A_2a,
218 #[serde(rename = "a-3b")]
220 A_3b,
221 #[serde(rename = "a-3u")]
223 A_3u,
224 #[serde(rename = "a-3a")]
226 A_3a,
227 #[serde(rename = "a-4")]
229 A_4,
230 #[serde(rename = "a-4f")]
232 A_4f,
233 #[serde(rename = "a-4e")]
235 A_4e,
236 #[serde(rename = "ua-1")]
238 Ua_1,
239}