typst_library/pdf/attach.rs
1use ecow::EcoString;
2use typst_syntax::Spanned;
3
4use crate::World;
5use crate::diag::At;
6use crate::foundations::{Bytes, Cast, Derived, elem};
7use crate::introspection::Locatable;
8
9/// A file that will be attached to the output PDF.
10///
11/// This can be used to distribute additional files associated with the PDF
12/// within it. PDF readers will display the files in a file listing.
13///
14/// Some international standards use this mechanism to attach machine-readable
15/// data (e.g., ZUGFeRD/Factur-X for invoices) that mirrors the visual content
16/// of the PDF.
17///
18/// # Example
19/// ```typ
20/// #pdf.attach(
21/// "experiment.csv",
22/// relationship: "supplement",
23/// mime-type: "text/csv",
24/// description: "Raw Oxygen readings from the Arctic experiment",
25/// )
26/// ```
27///
28/// # Notes
29/// - This element is ignored if exporting to a format other than PDF.
30/// - File attachments are not currently supported for PDF/A-2, even if the
31/// attached file conforms to PDF/A-1 or PDF/A-2.
32#[elem(keywords = ["embed"], Locatable)]
33pub struct AttachElem {
34 /// The [path]($syntax/#paths) of the file to be attached.
35 ///
36 /// Must always be specified, but is only read from if no data is provided
37 /// in the following argument.
38 #[required]
39 #[parse(
40 let Spanned { v: path, span } =
41 args.expect::<Spanned<EcoString>>("path")?;
42 let id = span.resolve_path(&path).at(span)?;
43 // The derived part is the project-relative resolved path.
44 let resolved = id.vpath().as_rootless_path().to_string_lossy().replace("\\", "/").into();
45 Derived::new(path.clone(), resolved)
46 )]
47 pub path: Derived<EcoString, EcoString>,
48
49 /// Raw file data, optionally.
50 ///
51 /// If omitted, the data is read from the specified path.
52 #[positional]
53 // Not actually required as an argument, but always present as a field.
54 // We can't distinguish between the two at the moment.
55 #[required]
56 #[parse(
57 match args.eat::<Bytes>()? {
58 Some(data) => data,
59 None => engine.world.file(id).at(span)?,
60 }
61 )]
62 pub data: Bytes,
63
64 /// The relationship of the attached file to the document.
65 ///
66 /// Ignored if export doesn't target PDF/A-3.
67 pub relationship: Option<AttachedFileRelationship>,
68
69 /// The MIME type of the attached file.
70 pub mime_type: Option<EcoString>,
71
72 /// A description for the attached file.
73 pub description: Option<EcoString>,
74}
75
76/// The relationship of an attached file with the document.
77#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
78pub enum AttachedFileRelationship {
79 /// The PDF document was created from the source file.
80 Source,
81 /// The file was used to derive a visual presentation in the PDF.
82 Data,
83 /// An alternative representation of the document.
84 Alternative,
85 /// Additional resources for the document.
86 Supplement,
87}