1pub mod fixed;
2pub mod fixed_image;
3pub mod fixed_line;
4pub mod fixed_text;
5pub mod footnote;
6pub mod footer;
7pub mod form;
8pub mod header;
9pub mod image;
10pub mod list;
11pub mod page_break;
12pub mod paragraph;
13pub mod section;
14pub mod section_break;
15pub mod spacer;
16pub mod table;
17pub mod toc;
18
19use std::collections::HashMap;
20
21use crate::{
22 backend::{FontRef, PdfBackend},
23 compliance::ua::{AccessibilityConfig, StructTag, StructureTree},
24 fonts::FontRegistry,
25 layout::{FixedBox, GlyphUsageTracker, PageFlow, TextLayoutEngine},
26 page::PageLayout,
27 styles::{DocumentStyle, RgbColor},
28};
29
30#[derive(Debug, Clone)]
32pub enum LayoutMode {
33 Flow,
35 Fixed(FixedBox),
37}
38
39#[derive(Debug, Clone)]
41pub struct RenderResult {
42 pub has_more: bool,
43}
44
45impl RenderResult {
46 pub fn done() -> Self { Self { has_more: false } }
47 pub fn more() -> Self { Self { has_more: true } }
48}
49
50pub struct RenderContext {
55 pub backend: Box<dyn PdfBackend>,
57 pub font_map: HashMap<String, FontRef>,
59 pub flow: PageFlow,
61 pub layout: PageLayout,
63 pub layout_engine: TextLayoutEngine,
65 pub style: DocumentStyle,
67 pub fonts: FontRegistry,
69 pub force_page_break: bool,
71 pub default_font_family: String,
73 pub page_number: u32,
75 pub total_pages: u32,
77 pub resume_index: usize,
79 pub glyph_tracker: GlyphUsageTracker,
81 pub reserved_footnotes_mm: f64,
83 pub ua_config: AccessibilityConfig,
85 pub ua_events: StructureTree,
87 pub mcid_counter: u32,
89 pub last_heading_level: Option<u8>,
91}
92
93impl RenderContext {
94 pub fn reset_resume(&mut self) {
95 self.resume_index = 0;
96 }
97
98 pub fn ua_enabled(&self) -> bool {
100 self.ua_config.enabled
101 }
102
103 pub fn next_mcid(&mut self) -> u32 {
105 let mcid = self.mcid_counter;
106 self.mcid_counter += 1;
107 mcid
108 }
109
110 pub fn ua_tag_element(&mut self, tag: StructTag, alt: Option<String>) -> u32 {
112 let mcid = self.next_mcid();
113 let page_idx = self.page_number as usize - 1;
114 self.ua_events.begin_group(tag, alt);
115 self.ua_events.add_content_ref(mcid, page_idx);
116 self.ua_events.end_group();
117 mcid
118 }
119
120 pub fn ua_begin_group(&mut self, tag: StructTag, alt: Option<String>) {
122 self.ua_events.begin_group(tag, alt);
123 }
124
125 pub fn ua_content_ref(&mut self, mcid: u32) {
127 let page_idx = self.page_number as usize - 1;
128 self.ua_events.add_content_ref(mcid, page_idx);
129 }
130
131 pub fn ua_end_group(&mut self) {
133 self.ua_events.end_group();
134 }
135
136 pub fn get_font_ref(&self, bold: bool, italic: bool) -> Option<FontRef> {
138 self.get_font_ref_for(&self.default_font_family.clone(), bold, italic)
139 }
140
141 pub fn get_font_ref_for(&self, family: &str, bold: bool, italic: bool) -> Option<FontRef> {
143 if bold && italic {
144 self.font_map.get(&format!("{family}::bold_italic"))
145 .or_else(|| self.font_map.get(&format!("{family}::bold")))
146 .or_else(|| self.font_map.get(&format!("{family}::italic")))
147 .or_else(|| self.font_map.get(&format!("{family}::regular")))
148 .copied()
149 } else if bold {
150 self.font_map.get(&format!("{family}::bold"))
151 .or_else(|| self.font_map.get(&format!("{family}::regular")))
152 .copied()
153 } else if italic {
154 self.font_map.get(&format!("{family}::italic"))
155 .or_else(|| self.font_map.get(&format!("{family}::regular")))
156 .copied()
157 } else {
158 self.font_map.get(&format!("{family}::regular")).copied()
159 }
160 }
161
162 pub fn draw_text(
164 &mut self,
165 text: &str,
166 x_mm: f64,
167 y_mm: f64,
168 font_size_pt: f64,
169 font_ref: FontRef,
170 color: &RgbColor,
171 ) -> crate::Result<()> {
172 self.backend.draw_text(text, x_mm, y_mm, font_size_pt, font_ref, color, 0.0)
173 }
174
175 pub fn draw_text_spaced(
177 &mut self,
178 text: &str,
179 x_mm: f64,
180 y_mm: f64,
181 font_size_pt: f64,
182 font_ref: FontRef,
183 color: &RgbColor,
184 letter_spacing_pt: f32,
185 ) -> crate::Result<()> {
186 self.backend.draw_text(text, x_mm, y_mm, font_size_pt, font_ref, color, letter_spacing_pt)
187 }
188
189 pub fn draw_hline(
191 &mut self,
192 x0_mm: f64,
193 x1_mm: f64,
194 y_mm: f64,
195 width_pt: f32,
196 color: &RgbColor,
197 ) -> crate::Result<()> {
198 self.backend.draw_line(x0_mm, y_mm, x1_mm, y_mm, width_pt, color)
199 }
200
201 pub fn draw_vline(
203 &mut self,
204 x_mm: f64,
205 y0_mm: f64,
206 y1_mm: f64,
207 width_pt: f32,
208 color: &RgbColor,
209 ) -> crate::Result<()> {
210 self.backend.draw_line(x_mm, y0_mm, x_mm, y1_mm, width_pt, color)
211 }
212}
213
214pub trait Element {
216 fn layout_mode(&self) -> LayoutMode {
217 LayoutMode::Flow
218 }
219
220 fn estimated_height_mm(&self) -> f64 {
221 0.0
222 }
223
224 fn as_section_info(&self) -> Option<(u8, &str)> {
225 None
226 }
227
228 fn inject_toc_entries(&mut self, _entries: &[crate::elements::toc::TocEntry]) {}
229
230 fn render(&self, ctx: &mut RenderContext) -> crate::Result<RenderResult>;
231}