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 /// Body font size, in points.
46 pub font_size_pt: f32,
47 /// Paragraph line spacing (Typst `leading`), in em. Typst's own default is `0.65`.
48 pub line_spacing: f32,
49 /// First-line paragraph indent, in millimetres. `None` ⇒ no first-line indent.
50 pub first_line_indent_mm: Option<f32>,
51 /// Extra space after each paragraph, in points. `None` ⇒ Typst's own default spacing.
52 pub paragraph_spacing_pt: Option<f32>,
53 /// Justify body paragraphs (document-level default; per-block alignment still overrides).
54 pub justify: bool,
55
56 /// Document-level base reading direction. Per-block RTL (`fmt_direction`) still wraps its
57 /// own content in a local `#text(dir: ..)` override regardless of this value.
58 pub base_rtl: bool,
59 /// Document-wide primary language, as an ISO 639-1 tag (e.g. `"en"`, `"fr"`), emitted as
60 /// `#set text(lang: ..)`. `None` ⇒ the emitter omits the `lang:` argument.
61 pub lang: Option<String>,
62
63 /// PDF `/Title` metadata (via `#set document(title: ..)`). `None` ⇒ no title is emitted —
64 /// Typst/krilla's own defaults apply (no explicit `/Title`).
65 pub title: Option<String>,
66 /// PDF `/Author` metadata (via `#set document(author: ..)`). `None` ⇒ no author is emitted.
67 pub author: Option<String>,
68
69 /// Emit the `#set page/text/par/smartquote/heading` preamble. Mirrors
70 /// `ExportLatexDto.include_preamble` — `false` produces a bare body, e.g. for embedding this
71 /// export's markup inside a larger hand-authored Typst document.
72 pub include_preamble: bool,
73}
74
75impl Default for PdfExportOptions {
76 fn default() -> Self {
77 Self {
78 // A4.
79 page_width_mm: 210.0,
80 page_height_mm: 297.0,
81 margin_top_mm: 25.0,
82 margin_bottom_mm: 25.0,
83 margin_left_mm: 20.0,
84 margin_right_mm: 20.0,
85
86 font_family: String::new(),
87 font_bytes: Vec::new(),
88 font_size_pt: 12.0,
89 line_spacing: 0.65,
90 first_line_indent_mm: None,
91 paragraph_spacing_pt: None,
92 justify: true,
93
94 base_rtl: false,
95 lang: None,
96
97 title: None,
98 author: None,
99
100 include_preamble: true,
101 }
102 }
103}