pdfsmith/config.rs
1// ─────────────────────────────────────────────────────────────────────────────
2// config.rs — All user-facing configuration types
3//
4// Every aspect of the generated PDF is customizable.
5// `PdfConfig::default()` produces a clean, minimal PDF.
6// ─────────────────────────────────────────────────────────────────────────────
7
8use serde::{Deserialize, Serialize};
9
10// ─────────────────────────────────────────────────────────────────────────────
11// Paper size
12// ─────────────────────────────────────────────────────────────────────────────
13
14/// Standard paper sizes (width × height in inches).
15#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
16pub enum PaperSize {
17 /// 8.27 × 11.69 in
18 A4,
19 /// 8.5 × 11 in
20 Letter,
21 /// 8.5 × 14 in
22 Legal,
23 /// Custom width × height in inches.
24 Custom { width: f64, height: f64 },
25}
26
27impl PaperSize {
28 /// Returns `(width, height)` in inches.
29 pub fn dimensions(&self) -> (f64, f64) {
30 match self {
31 PaperSize::A4 => (8.27, 11.69),
32 PaperSize::Letter => (8.5, 11.0),
33 PaperSize::Legal => (8.5, 14.0),
34 PaperSize::Custom { width, height } => (*width, *height),
35 }
36 }
37}
38
39impl Default for PaperSize {
40 fn default() -> Self {
41 PaperSize::A4
42 }
43}
44
45// ─────────────────────────────────────────────────────────────────────────────
46// Page margins
47// ─────────────────────────────────────────────────────────────────────────────
48
49/// Page margins in inches.
50#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
51pub struct PageMargins {
52 pub top: f64,
53 pub bottom: f64,
54 pub left: f64,
55 pub right: f64,
56}
57
58impl Default for PageMargins {
59 fn default() -> Self {
60 Self {
61 top: 0.75,
62 bottom: 0.75,
63 left: 0.75,
64 right: 0.75,
65 }
66 }
67}
68
69// ─────────────────────────────────────────────────────────────────────────────
70// Header configuration
71// ─────────────────────────────────────────────────────────────────────────────
72
73/// Controls the repeating page header.
74///
75/// **Option A — custom_html**: provide your own HTML template. Chrome's
76/// header sandbox renders this on every page. Use `<span class="pageNumber">`
77/// and `<span class="totalPages">` for automatic page numbers.
78///
79/// **Option B — structured fields**: set `left`, `center`, `right` text and
80/// the library builds the template for you.
81///
82/// **Option C — do nothing**: leave everything `None` and the default header
83/// is just the document title, small and centred.
84#[derive(Debug, Clone, Default, Serialize, Deserialize)]
85pub struct HeaderConfig {
86 /// Full custom HTML template — used as-is, everything else is ignored.
87 pub custom_html: Option<String>,
88
89 /// Text for the left side of the header.
90 pub left: Option<String>,
91 /// Text for the centre of the header.
92 pub center: Option<String>,
93 /// Text for the right side of the header.
94 pub right: Option<String>,
95
96 /// Font size (CSS value, e.g. `"10px"`, `"8pt"`). Default: `"9px"`.
97 pub font_size: Option<String>,
98 /// Font colour (CSS value). Default: `"#555"`.
99 pub color: Option<String>,
100}
101
102// ─────────────────────────────────────────────────────────────────────────────
103// Footer configuration
104// ─────────────────────────────────────────────────────────────────────────────
105
106/// Controls the repeating page footer.
107///
108/// Same three options as [`HeaderConfig`]: custom HTML, structured
109/// left/center/right fields, or just leave defaults.
110///
111/// The default footer is a simple centred page number: `Page 1 / 5`.
112///
113/// To use Chrome's auto page numbers in `custom_html`, include
114/// `<span class="pageNumber"></span>` and `<span class="totalPages"></span>`.
115#[derive(Debug, Clone, Default, Serialize, Deserialize)]
116pub struct FooterConfig {
117 /// Full custom HTML template — used as-is, everything else is ignored.
118 pub custom_html: Option<String>,
119
120 /// Text for the left side of the footer.
121 pub left: Option<String>,
122 /// Text for the centre of the footer. Default: `"Page <pageNumber> / <totalPages>"`.
123 pub center: Option<String>,
124 /// Text for the right side of the footer.
125 pub right: Option<String>,
126
127 /// Font size (CSS value). Default: `"8px"`.
128 pub font_size: Option<String>,
129 /// Font colour (CSS value). Default: `"#555"`.
130 pub color: Option<String>,
131}
132
133// ─────────────────────────────────────────────────────────────────────────────
134// Markdown options
135// ─────────────────────────────────────────────────────────────────────────────
136
137/// Controls which comrak Markdown extensions are enabled.
138///
139/// All default to `true` — the renderer is batteries-included.
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct MarkdownOptions {
142 pub unsafe_html: bool,
143 pub tables: bool,
144 pub footnotes: bool,
145 pub description_lists: bool,
146 pub strikethrough: bool,
147 pub tasklist: bool,
148 pub autolink: bool,
149 pub superscript: bool,
150}
151
152impl Default for MarkdownOptions {
153 fn default() -> Self {
154 Self {
155 unsafe_html: true,
156 tables: true,
157 footnotes: true,
158 description_lists: true,
159 strikethrough: true,
160 tasklist: true,
161 autolink: true,
162 superscript: true,
163 }
164 }
165}
166
167// ─────────────────────────────────────────────────────────────────────────────
168// Main configuration struct
169// ─────────────────────────────────────────────────────────────────────────────
170
171/// Top-level configuration for PDF generation.
172///
173/// Every field has a sensible default. Use `PdfConfig::default()` for a
174/// clean, no-frills PDF, or customise exactly what you need.
175///
176/// # Minimal
177/// ```no_run
178/// use pdfsmith::PdfConfig;
179/// let cfg = PdfConfig::default();
180/// ```
181///
182/// # Custom
183/// ```no_run
184/// use pdfsmith::{PdfConfig, PaperSize, PageMargins, FooterConfig};
185/// let cfg = PdfConfig {
186/// custom_css: Some("body { font-family: Georgia, serif; font-size: 12pt; }".into()),
187/// paper_size: PaperSize::Letter,
188/// margins: PageMargins { top: 0.5, bottom: 0.5, left: 0.5, right: 0.5 },
189/// footer: FooterConfig { right: Some("Confidential".into()), ..Default::default() },
190/// ..Default::default()
191/// };
192/// ```
193#[derive(Debug, Clone, Serialize, Deserialize)]
194pub struct PdfConfig {
195 // ── Document metadata ────────────────────────────────────────────────
196
197 /// Document title (used in `<title>` and default header).
198 pub title: String,
199
200 // ── Styling ──────────────────────────────────────────────────────────
201
202 /// Custom CSS that **replaces** the built-in stylesheet entirely.
203 pub custom_css: Option<String>,
204
205 /// Extra CSS **appended** after the base stylesheet.
206 /// Ideal for small tweaks without replacing the whole default.
207 pub extra_css: Option<String>,
208
209 // ── Page layout ──────────────────────────────────────────────────────
210
211 pub paper_size: PaperSize,
212 pub margins: PageMargins,
213 /// Landscape orientation. Default: `false` (portrait).
214 pub landscape: bool,
215
216 // ── Header / Footer ──────────────────────────────────────────────────
217
218 pub header: HeaderConfig,
219 pub footer: FooterConfig,
220
221 /// Show header & footer at all. Default: `false` (clean pages).
222 pub display_header_footer: bool,
223
224 // ── Rendering ────────────────────────────────────────────────────────
225
226 /// Print background colours/images. Default: `true`.
227 pub print_background: bool,
228
229 /// Markdown parsing options.
230 pub markdown_options: MarkdownOptions,
231
232 /// Automatically number headings with hierarchical numbers
233 /// (1, 1.1, 1.1.1, etc.) via CSS counters.
234 ///
235 /// Works with **all** input types: Markdown, JSON, and HTML.
236 /// Default: `false`.
237 pub heading_numbers: bool,
238
239 // ── Chrome ───────────────────────────────────────────────────────────
240
241 /// Chrome headless window size `(width, height)`.
242 pub chrome_window_size: (u32, u32),
243
244 /// Seconds to wait after page load before printing. Default: `2`.
245 pub page_load_wait_secs: u64,
246}
247
248impl Default for PdfConfig {
249 fn default() -> Self {
250 Self {
251 title: String::new(),
252 custom_css: None,
253 extra_css: None,
254 paper_size: PaperSize::default(),
255 margins: PageMargins::default(),
256 landscape: false,
257 header: HeaderConfig::default(),
258 footer: FooterConfig::default(),
259 display_header_footer: false,
260 print_background: true,
261 markdown_options: MarkdownOptions::default(),
262 heading_numbers: false,
263 chrome_window_size: (1280, 900),
264 page_load_wait_secs: 5,
265 }
266 }
267}