common/parser_tools/pdf_options.rs
1//! Page geometry + typography + font bytes for PDF export (via embedded Typst).
2//!
3//! Unlike [`super::docx_options::DocxExportOptions`] (whose `None`s mean "use docx-rs's own
4//! built-in defaults"), a Typst document is compiled from markup this crate emits itself, so
5//! there is no "plain" PDF the way `to_docx` has docx-rs's defaults — every geometry/typography
6//! field here always lands in the generated `#set` preamble. [`Default`] therefore picks
7//! concrete, reasonable values (A4, 12pt body text, a modest first-paragraph-less layout)
8//! rather than leaving them unset.
9//!
10//! **Font bytes are supplied by the caller, not looked up here.** `text-document` has no font
11//! file access or font-selection policy of its own (see [`font_bytes`](PdfExportOptions::font_bytes)) — the
12//! caller (e.g. Skribisto's compiler, which owns the bundled OFL fonts and the writer's chosen
13//! family) hands over raw TTF/OTF blobs, which are passed straight to the embedded Typst
14//! compiler's in-memory font book. No system/typst-kit font search ever runs.
15
16use serde::{Deserialize, Serialize};
17
18/// Page geometry + typography + embedded font bytes for a PDF export.
19///
20/// All lengths are in **millimetres** (matching `Preset`'s existing unit in callers such as
21/// Skribisto's compiler); they are converted to Typst's `cm`/`pt` length literals at
22/// markup-emission time, not here — this struct stays a plain, unit-tagged data record.
23#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
24pub struct PdfExportOptions {
25 /// Page width, in millimetres.
26 pub page_width_mm: f32,
27 /// Page height, in millimetres.
28 pub page_height_mm: f32,
29 /// Top page margin, in millimetres.
30 pub margin_top_mm: f32,
31 /// Bottom page margin, in millimetres.
32 pub margin_bottom_mm: f32,
33 /// Left page margin, in millimetres.
34 pub margin_left_mm: f32,
35 /// Right page margin, in millimetres.
36 pub margin_right_mm: f32,
37
38 /// Body font family name, emitted as `#set text(font: ..)`. Empty ⇒ the emitter omits the
39 /// `font:` argument entirely, letting Typst fall back to its own built-in default face.
40 pub font_family: String,
41 /// Raw TTF/OTF font blobs, handed directly to the embedded Typst compiler's `.fonts(..)`.
42 /// At least one entry is required for `document_io::typst_compile::compile_typst_pdf` to
43 /// succeed; this struct itself does not enforce that (`Default` is an empty list).
44 pub font_bytes: Vec<Vec<u8>>,
45 /// Bytes for the document's inline images, keyed by their `src`, registered
46 /// with the embedded Typst compiler as in-memory virtual files. Exactly the
47 /// same contract as [`font_bytes`](Self::font_bytes) — the caller hands over
48 /// blobs, and no filesystem access happens here.
49 #[serde(default)]
50 pub images: super::image_options::ExportImages,
51 /// Body font size, in points.
52 pub font_size_pt: f32,
53 /// Paragraph line spacing (Typst `leading`), in em. Typst's own default is `0.65`.
54 pub line_spacing: f32,
55 /// First-line paragraph indent, in millimetres. `None` ⇒ no first-line indent.
56 pub first_line_indent_mm: Option<f32>,
57 /// Extra space after each paragraph, in points. `None` ⇒ Typst's own default spacing.
58 pub paragraph_spacing_pt: Option<f32>,
59 /// Justify body paragraphs (document-level default; per-block alignment still overrides).
60 pub justify: bool,
61
62 /// Document-level base reading direction. Per-block RTL (`fmt_direction`) still wraps its
63 /// own content in a local `#text(dir: ..)` override regardless of this value.
64 pub base_rtl: bool,
65 /// Document-wide primary language, as an ISO 639-1 tag (e.g. `"en"`, `"fr"`), emitted as
66 /// `#set text(lang: ..)`. `None` ⇒ the emitter omits the `lang:` argument.
67 pub lang: Option<String>,
68
69 /// PDF `/Title` metadata (via `#set document(title: ..)`). `None` ⇒ no title is emitted —
70 /// Typst/krilla's own defaults apply (no explicit `/Title`).
71 pub title: Option<String>,
72 /// PDF `/Author` metadata (via `#set document(author: ..)`). `None` ⇒ no author is emitted.
73 pub author: Option<String>,
74
75 /// Emit the `#set page/text/par/smartquote/heading` preamble. Mirrors
76 /// `ExportLatexDto.include_preamble` — `false` produces a bare body, e.g. for embedding this
77 /// export's markup inside a larger hand-authored Typst document.
78 pub include_preamble: bool,
79}
80
81impl Default for PdfExportOptions {
82 fn default() -> Self {
83 Self {
84 // A4.
85 page_width_mm: 210.0,
86 page_height_mm: 297.0,
87 margin_top_mm: 25.0,
88 margin_bottom_mm: 25.0,
89 margin_left_mm: 20.0,
90 margin_right_mm: 20.0,
91
92 font_family: String::new(),
93 images: Default::default(),
94 font_bytes: Vec::new(),
95 font_size_pt: 12.0,
96 line_spacing: 0.65,
97 first_line_indent_mm: None,
98 paragraph_spacing_pt: None,
99 justify: true,
100
101 base_rtl: false,
102 lang: None,
103
104 title: None,
105 author: None,
106
107 include_preamble: true,
108 }
109 }
110}