normordis_pdf/elements/
header.rs1use serde::{Deserialize, Serialize};
2
3use super::{Element, RenderContext};
4use crate::styles::{RgbColor, StyleResolver};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct InstitutionalHeader {
12 pub entity_name: String,
14 pub document_title: String,
15 pub document_subtitle: Option<String>,
16 pub logo: Option<Vec<u8>>,
18 pub reference: Option<String>,
20 pub date: Option<String>,
22}
23
24impl InstitutionalHeader {
25 pub fn new(
26 entity_name: impl Into<String>,
27 document_title: impl Into<String>,
28 ) -> Self {
29 Self {
30 entity_name: entity_name.into(),
31 document_title: document_title.into(),
32 document_subtitle: None,
33 logo: None,
34 reference: None,
35 date: None,
36 }
37 }
38
39 pub fn with_subtitle(mut self, subtitle: impl Into<String>) -> Self {
40 self.document_subtitle = Some(subtitle.into());
41 self
42 }
43
44 pub fn with_logo(mut self, bytes: Vec<u8>) -> Self {
45 self.logo = Some(bytes);
46 self
47 }
48
49 pub fn with_reference(mut self, reference: impl Into<String>) -> Self {
50 self.reference = Some(reference.into());
51 self
52 }
53
54 pub fn with_date(mut self, date: impl Into<String>) -> Self {
55 self.date = Some(date.into());
56 self
57 }
58}
59
60impl Element for InstitutionalHeader {
61 fn estimated_height_mm(&self) -> f64 {
62 if self.logo.is_some() { 35.0 } else { 25.0 }
63 }
64
65 fn render(&self, ctx: &mut RenderContext) -> crate::Result<super::RenderResult> {
66 let ua = ctx.ua_config.enabled;
67 if ua { ctx.backend.begin_artifact_content(); }
68
69 let text_color = ctx.style.text_color.clone();
70 let muted_color = RgbColor { r: 0.45, g: 0.45, b: 0.45 };
71 let sep_color = ctx.style.primary_color.clone();
72 let resolver = StyleResolver::new(&ctx.style.named_styles, &ctx.style);
73 let font_family = resolver.resolve("normal").map(|r| r.font_family).unwrap_or_else(|_| "LiberationSans".to_string());
74
75 let content_x = ctx.layout.content_x_mm;
76 let content_w = ctx.layout.content_width_mm;
77 let right_edge = content_x + content_w;
78
79 {
81 let fs_main = 13.0_f64;
82 let fs_meta = 9.0_f64;
83 let y = ctx.flow.cursor_y_mm;
84 if let Some(fref) = ctx.get_font_ref(true, false) {
85 ctx.draw_text(&self.entity_name, content_x, y, fs_main, fref, &text_color)?;
86 }
87 if let Some(ref date) = self.date {
88 let dw = ctx.fonts.get_family(&font_family)
89 .measure_text_mm(date, fs_meta, false, false);
90 if let Some(fref) = ctx.get_font_ref(false, false) {
91 ctx.draw_text(date, right_edge - dw, y, fs_meta, fref, &muted_color)?;
92 }
93 }
94 let lh = ctx.layout_engine.line_height_mm(&ctx.fonts, fs_main);
95 ctx.flow.advance(lh + 1.5);
96 }
97
98 {
100 let fs_main = 11.0_f64;
101 let fs_meta = 9.0_f64;
102 let y = ctx.flow.cursor_y_mm;
103 if let Some(fref) = ctx.get_font_ref(true, false) {
104 ctx.draw_text(&self.document_title, content_x, y, fs_main, fref, &text_color)?;
105 }
106 if let Some(ref reference) = self.reference {
107 let rw = ctx.fonts.get_family(&font_family)
108 .measure_text_mm(reference, fs_meta, false, false);
109 if let Some(fref) = ctx.get_font_ref(false, false) {
110 ctx.draw_text(reference, right_edge - rw, y, fs_meta, fref, &muted_color)?;
111 }
112 }
113 let lh = ctx.layout_engine.line_height_mm(&ctx.fonts, fs_main);
114 ctx.flow.advance(lh + 1.5);
115 }
116
117 if let Some(ref subtitle) = self.document_subtitle {
119 let fs = 9.0_f64;
120 let y = ctx.flow.cursor_y_mm;
121 if let Some(fref) = ctx.get_font_ref(false, true) {
122 ctx.draw_text(subtitle, content_x, y, fs, fref, &muted_color)?;
123 }
124 let lh = ctx.layout_engine.line_height_mm(&ctx.fonts, fs);
125 ctx.flow.advance(lh + 1.5);
126 }
127
128 ctx.flow.advance(1.0);
130 let sep_y = ctx.flow.cursor_y_mm;
131 ctx.backend.draw_line(content_x, sep_y, right_edge, sep_y, 0.75, &sep_color)?;
132 ctx.flow.advance(3.0);
133
134 if ua { ctx.backend.end_tagged_content(); }
135 Ok(super::RenderResult::done())
136 }
137}
138
139#[derive(Debug, Clone, Default)]
161pub struct SectionedHeader {
162 pub first_page: Option<InstitutionalHeader>,
163 pub odd_pages: Option<InstitutionalHeader>,
164 pub even_pages: Option<InstitutionalHeader>,
165}
166
167impl SectionedHeader {
168 pub fn new() -> Self {
169 Self::default()
170 }
171
172 pub fn first_page(mut self, h: InstitutionalHeader) -> Self {
174 self.first_page = Some(h);
175 self
176 }
177
178 pub fn odd_pages(mut self, h: InstitutionalHeader) -> Self {
181 self.odd_pages = Some(h);
182 self
183 }
184
185 pub fn even_pages(mut self, h: InstitutionalHeader) -> Self {
187 self.even_pages = Some(h);
188 self
189 }
190
191 pub fn resolve(&self, page_number: u32) -> Option<&InstitutionalHeader> {
199 if page_number == 1 {
200 if let Some(ref h) = self.first_page {
201 return Some(h);
202 }
203 }
204 if page_number.is_multiple_of(2) {
205 if let Some(ref h) = self.even_pages {
206 return Some(h);
207 }
208 }
209 self.odd_pages.as_ref()
210 }
211}
212