Skip to main content

laser_pdf/serde_elements/
elements.rs

1use std::ops::Index;
2
3use elements::rotate::Rotation;
4
5use crate::{
6    elements::{h_align::HorizontalAlignment, row::Flex, text::TextAlign},
7    *,
8};
9
10use super::{Font, SerdeElement, SerdeElementElement};
11
12const fn default_false() -> bool {
13    false
14}
15
16const fn default_0u8() -> u8 {
17    0
18}
19
20#[derive(Clone, Serialize, Deserialize)]
21pub struct None;
22
23impl SerdeElement for None {
24    fn element(
25        &self,
26        _: &impl for<'a> Index<&'a str, Output = Font>,
27        _: impl CompositeElementCallback,
28    ) {
29    }
30}
31
32#[derive(Clone, Serialize, Deserialize)]
33pub struct Empty;
34
35impl SerdeElement for Empty {
36    fn element(
37        &self,
38        _: &impl for<'a> Index<&'a str, Output = Font>,
39        callback: impl CompositeElementCallback,
40    ) {
41        callback.call(&elements::empty::Empty);
42    }
43}
44
45#[derive(Clone, Serialize, Deserialize)]
46pub struct Debug<E> {
47    pub element: Box<E>,
48
49    #[serde(default = "default_0u8")]
50    pub color: u8,
51
52    #[serde(default = "default_false")]
53    pub show_max_width: bool,
54
55    #[serde(default = "default_false")]
56    pub show_last_location_max_height: bool,
57}
58
59impl<E: SerdeElement> SerdeElement for Debug<E> {
60    fn element(
61        &self,
62        fonts: &impl for<'a> Index<&'a str, Output = Font>,
63        callback: impl CompositeElementCallback,
64    ) {
65        callback.call(&elements::debug::Debug {
66            element: SerdeElementElement {
67                element: &*self.element,
68                fonts,
69            },
70            color: self.color,
71            show_max_width: self.show_max_width,
72            show_last_location_max_height: self.show_last_location_max_height,
73        });
74    }
75}
76
77#[derive(Clone, Serialize, Deserialize)]
78pub struct Text {
79    pub text: String,
80    pub font: String,
81    pub size: f32,
82    pub color: u32,
83    pub underline: bool,
84    pub extra_character_spacing: f32,
85    pub extra_word_spacing: f32,
86    pub extra_line_height: f32,
87    pub align: TextAlign,
88}
89
90impl SerdeElement for Text {
91    fn element(
92        &self,
93        fonts: &impl for<'a> Index<&'a str, Output = Font>,
94        callback: impl CompositeElementCallback,
95    ) {
96        callback.call(&elements::text::Text {
97            text: &self.text,
98            font: &*fonts[&self.font],
99            size: self.size,
100            color: self.color,
101            underline: self.underline,
102            extra_character_spacing: self.extra_character_spacing,
103            extra_word_spacing: self.extra_word_spacing,
104            extra_line_height: self.extra_line_height,
105            align: self.align,
106        });
107    }
108}
109
110#[derive(Clone, Serialize, Deserialize)]
111pub struct RichTextSpan {
112    /// The text content to render
113    pub text: String,
114    /// Font reference
115    pub font: String,
116    /// Font size in points
117    pub size: f32,
118    /// Text color as RGBA (default: black 0x00_00_00_FF)
119    pub color: u32,
120    /// Whether to underline the text
121    pub underline: bool,
122    /// Additional spacing between characters
123    pub extra_character_spacing: f32,
124    /// Additional spacing between words
125    pub extra_word_spacing: f32,
126    /// Additional line height
127    pub extra_line_height: f32,
128}
129
130#[derive(Clone, Serialize, Deserialize)]
131pub struct RichText {
132    pub spans: Vec<RichTextSpan>,
133    pub align: TextAlign,
134}
135
136impl SerdeElement for RichText {
137    fn element(
138        &self,
139        fonts: &impl for<'a> Index<&'a str, Output = Font>,
140        callback: impl CompositeElementCallback,
141    ) {
142        callback.call(&elements::rich_text::RichText {
143            spans: self.spans.iter().map(|s| elements::rich_text::Span {
144                text: &s.text,
145                font: &*fonts[&s.font],
146                size: s.size,
147                color: s.color,
148                underline: s.underline,
149                extra_character_spacing: s.extra_character_spacing,
150                extra_word_spacing: s.extra_word_spacing,
151                extra_line_height: s.extra_line_height,
152            }),
153            align: self.align,
154        });
155    }
156}
157
158#[derive(Clone, Serialize, Deserialize)]
159pub struct VGap {
160    pub gap: f32,
161}
162
163impl SerdeElement for VGap {
164    fn element(
165        &self,
166        _: &impl for<'a> Index<&'a str, Output = Font>,
167        callback: impl CompositeElementCallback,
168    ) {
169        callback.call(&elements::v_gap::VGap(self.gap));
170    }
171}
172
173#[derive(Clone, Serialize, Deserialize)]
174pub struct HAlign<E> {
175    pub alignment: HorizontalAlignment,
176    pub element: Box<E>,
177}
178
179impl<E: SerdeElement> SerdeElement for HAlign<E> {
180    fn element(
181        &self,
182        fonts: &impl for<'a> Index<&'a str, Output = Font>,
183        callback: impl CompositeElementCallback,
184    ) {
185        callback.call(&elements::h_align::HAlign(
186            self.alignment,
187            SerdeElementElement {
188                element: &*self.element,
189                fonts,
190            },
191        ));
192    }
193}
194
195#[derive(Clone, Serialize, Deserialize)]
196pub struct Padding<E> {
197    pub left: f32,
198    pub right: f32,
199    pub top: f32,
200    pub bottom: f32,
201    pub element: Box<E>,
202}
203
204impl<E: SerdeElement> SerdeElement for Padding<E> {
205    fn element(
206        &self,
207        fonts: &impl for<'a> Index<&'a str, Output = Font>,
208        callback: impl CompositeElementCallback,
209    ) {
210        callback.call(&elements::padding::Padding {
211            left: self.left,
212            right: self.right,
213            top: self.top,
214            bottom: self.bottom,
215            element: SerdeElementElement {
216                element: &*self.element,
217                fonts,
218            },
219        });
220    }
221}
222
223#[derive(Clone, Serialize, Deserialize)]
224pub struct StyledBox<E> {
225    pub element: Box<E>,
226    pub padding_left: f32,
227    pub padding_right: f32,
228    pub padding_top: f32,
229    pub padding_bottom: f32,
230    pub border_radius: f32,
231    pub fill: Option<u32>,
232    pub outline: Option<LineStyle>,
233}
234
235impl<E: SerdeElement> SerdeElement for StyledBox<E> {
236    fn element(
237        &self,
238        fonts: &impl for<'a> Index<&'a str, Output = Font>,
239        callback: impl CompositeElementCallback,
240    ) {
241        callback.call(&elements::styled_box::StyledBox {
242            element: SerdeElementElement {
243                element: &*self.element,
244                fonts,
245            },
246            padding_left: self.padding_left,
247            padding_right: self.padding_right,
248            padding_top: self.padding_top,
249            padding_bottom: self.padding_bottom,
250            border_radius: self.border_radius,
251            fill: self.fill,
252            outline: self.outline,
253        });
254    }
255}
256
257#[derive(Clone, Serialize, Deserialize)]
258pub struct Line {
259    pub style: LineStyle,
260}
261
262impl SerdeElement for Line {
263    fn element(
264        &self,
265        _: &impl for<'a> Index<&'a str, Output = Font>,
266        callback: impl CompositeElementCallback,
267    ) {
268        callback.call(&elements::line::Line { style: self.style });
269    }
270}
271
272#[derive(Clone, Deserialize)]
273pub struct Image {
274    #[serde(rename = "path", deserialize_with = "crate::image::deserialize_image")]
275    pub image: crate::image::Image,
276}
277
278impl SerdeElement for Image {
279    fn element(
280        &self,
281        _: &impl for<'a> Index<&'a str, Output = Font>,
282        callback: impl CompositeElementCallback,
283    ) {
284        callback.call(&elements::image::ImageElement { image: &self.image });
285    }
286}
287
288#[derive(Clone, Serialize, Deserialize)]
289pub struct Rectangle {
290    pub size: (f32, f32),
291    pub fill: Option<u32>,
292    pub outline: Option<(f32, u32)>,
293}
294
295impl SerdeElement for Rectangle {
296    fn element(
297        &self,
298        _: &impl for<'a> Index<&'a str, Output = Font>,
299        callback: impl CompositeElementCallback,
300    ) {
301        callback.call(&elements::rectangle::Rectangle {
302            size: self.size,
303            fill: self.fill,
304            outline: self.outline,
305        });
306    }
307}
308
309#[derive(Clone, Serialize, Deserialize)]
310pub struct Circle {
311    pub radius: f32,
312    pub fill: Option<u32>,
313    pub outline: Option<(f32, u32)>,
314}
315
316impl SerdeElement for Circle {
317    fn element(
318        &self,
319        _: &impl for<'a> Index<&'a str, Output = Font>,
320        callback: impl CompositeElementCallback,
321    ) {
322        callback.call(&elements::circle::Circle {
323            radius: self.radius,
324            fill: self.fill,
325            outline: self.outline,
326        });
327    }
328}
329
330#[derive(Clone, Serialize, Deserialize)]
331pub struct Column<E> {
332    pub content: Vec<E>,
333    pub gap: f32,
334
335    #[serde(default = "default_false")]
336    pub collapse: bool,
337}
338
339impl<E: SerdeElement> SerdeElement for Column<E> {
340    fn element(
341        &self,
342        fonts: &impl for<'a> Index<&'a str, Output = Font>,
343        callback: impl CompositeElementCallback,
344    ) {
345        callback.call(&elements::column::Column {
346            content: |mut content| {
347                for element in &self.content {
348                    content = content.add(&SerdeElementElement { element, fonts })?;
349                }
350
351                Option::None
352            },
353            gap: self.gap,
354            collapse: self.collapse,
355        });
356    }
357}
358
359#[derive(Clone, Serialize, Deserialize)]
360pub struct RowElement<E> {
361    pub element: E,
362    pub flex: Flex,
363}
364
365#[derive(Clone, Serialize, Deserialize)]
366pub struct Row<E> {
367    pub content: Vec<RowElement<E>>,
368    pub gap: f32,
369    pub expand: bool,
370    pub collapse: bool,
371}
372
373impl<E: SerdeElement> SerdeElement for Row<E> {
374    fn element(
375        &self,
376        fonts: &impl for<'a> Index<&'a str, Output = Font>,
377        callback: impl CompositeElementCallback,
378    ) {
379        callback.call(&elements::row::Row {
380            content: |content| {
381                for RowElement { element, flex } in &self.content {
382                    content.add(&SerdeElementElement { element, fonts }, *flex);
383                }
384            },
385            gap: self.gap,
386            expand: self.expand,
387            collapse: self.collapse,
388        });
389    }
390}
391
392#[derive(Clone, Serialize, Deserialize)]
393pub struct BreakList<E> {
394    pub content: Vec<E>,
395    pub gap: f32,
396}
397
398impl<E: SerdeElement> SerdeElement for BreakList<E> {
399    fn element(
400        &self,
401        fonts: &impl for<'a> Index<&'a str, Output = Font>,
402        callback: impl CompositeElementCallback,
403    ) {
404        callback.call(&elements::break_list::BreakList {
405            content: |mut content| {
406                for element in &self.content {
407                    content = content.add(&SerdeElementElement { element, fonts })?;
408                }
409
410                Option::None
411            },
412            gap: self.gap,
413        });
414    }
415}
416
417#[derive(Clone, Serialize, Deserialize)]
418pub struct Stack<E> {
419    pub content: Vec<E>,
420    pub expand: bool,
421}
422
423impl<E: SerdeElement> SerdeElement for Stack<E> {
424    fn element(
425        &self,
426        fonts: &impl for<'a> Index<&'a str, Output = Font>,
427        callback: impl CompositeElementCallback,
428    ) {
429        callback.call(&elements::stack::Stack {
430            content: |content| {
431                for element in &self.content {
432                    content.add(&SerdeElementElement { element, fonts });
433                }
434            },
435            expand: self.expand,
436        });
437    }
438}
439
440#[derive(Clone, Serialize, Deserialize)]
441pub struct TableRowElement<E> {
442    pub element: E,
443    pub flex: elements::table_row::Flex,
444}
445
446#[derive(Clone, Serialize, Deserialize)]
447pub struct TableRow<E> {
448    pub content: Vec<TableRowElement<E>>,
449    pub line_style: LineStyle,
450
451    #[serde(alias = "y_expand")]
452    pub expand: bool,
453}
454
455impl<E: SerdeElement> SerdeElement for TableRow<E> {
456    fn element(
457        &self,
458        fonts: &impl for<'a> Index<&'a str, Output = Font>,
459        callback: impl CompositeElementCallback,
460    ) {
461        callback.call(&elements::table_row::TableRow {
462            content: |content| {
463                for TableRowElement { element, flex } in &self.content {
464                    content.add(&SerdeElementElement { element, fonts }, *flex);
465                }
466            },
467            line_style: self.line_style,
468            expand: self.expand,
469        });
470    }
471}
472
473#[derive(Clone, Serialize, Deserialize)]
474pub struct Titled<E> {
475    pub title: Box<E>,
476    pub content: Box<E>,
477    pub gap: f32,
478
479    #[serde(default = "default_false")]
480    pub collapse_on_empty_content: bool,
481}
482
483impl<E: SerdeElement> SerdeElement for Titled<E> {
484    fn element(
485        &self,
486        fonts: &impl for<'a> Index<&'a str, Output = Font>,
487        callback: impl CompositeElementCallback,
488    ) {
489        callback.call(&elements::titled::Titled {
490            title: SerdeElementElement {
491                element: &*self.title,
492                fonts,
493            },
494            content: SerdeElementElement {
495                element: &*self.content,
496                fonts,
497            },
498            gap: self.gap,
499            collapse_on_empty_content: self.collapse_on_empty_content,
500        });
501    }
502}
503
504#[derive(Clone, Serialize, Deserialize)]
505pub struct TitleOrBreak<E> {
506    pub title: Box<E>,
507    pub content: Box<E>,
508    pub gap: f32,
509
510    #[serde(default = "default_false")]
511    pub collapse_on_empty_content: bool,
512}
513
514impl<E: SerdeElement> SerdeElement for TitleOrBreak<E> {
515    fn element(
516        &self,
517        fonts: &impl for<'a> Index<&'a str, Output = Font>,
518        callback: impl CompositeElementCallback,
519    ) {
520        callback.call(&elements::title_or_break::TitleOrBreak {
521            title: &SerdeElementElement {
522                element: &*self.title,
523                fonts,
524            },
525            content: &SerdeElementElement {
526                element: &*self.content,
527                fonts,
528            },
529            gap: self.gap,
530            collapse_on_empty_content: self.collapse_on_empty_content,
531        });
532    }
533}
534
535#[derive(Clone, Serialize, Deserialize)]
536pub struct ChangingTitle<E> {
537    pub first_title: Box<E>,
538
539    #[serde(alias = "second_title")]
540    pub remaining_title: Box<E>,
541
542    pub content: Box<E>,
543    pub gap: f32,
544
545    #[serde(default = "default_false")]
546    pub collapse: bool,
547}
548
549impl<E: SerdeElement> SerdeElement for ChangingTitle<E> {
550    fn element(
551        &self,
552        fonts: &impl for<'a> Index<&'a str, Output = Font>,
553        callback: impl CompositeElementCallback,
554    ) {
555        callback.call(&elements::changing_title::ChangingTitle {
556            first_title: SerdeElementElement {
557                element: &*self.first_title,
558                fonts,
559            },
560            remaining_title: SerdeElementElement {
561                element: &*self.remaining_title,
562                fonts,
563            },
564            content: SerdeElementElement {
565                element: &*self.content,
566                fonts,
567            },
568            gap: self.gap,
569            collapse: self.collapse,
570        });
571    }
572}
573
574#[derive(Clone, Serialize, Deserialize)]
575pub struct RepeatAfterBreak<E> {
576    pub title: Box<E>,
577    pub content: Box<E>,
578    pub gap: f32,
579
580    #[serde(default = "default_false")]
581    pub collapse_on_empty_content: bool,
582}
583
584impl<E: SerdeElement> SerdeElement for RepeatAfterBreak<E> {
585    fn element(
586        &self,
587        fonts: &impl for<'a> Index<&'a str, Output = Font>,
588        callback: impl CompositeElementCallback,
589    ) {
590        callback.call(&elements::repeat_after_break::RepeatAfterBreak {
591            title: &SerdeElementElement {
592                element: &*self.title,
593                fonts,
594            },
595            content: &SerdeElementElement {
596                element: &*self.content,
597                fonts,
598            },
599            gap: self.gap,
600            collapse_on_empty_content: self.collapse_on_empty_content,
601        });
602    }
603}
604
605#[derive(Clone, Serialize, Deserialize)]
606pub struct RepeatBottom<E> {
607    pub content: Box<E>,
608    pub bottom: Box<E>,
609    pub gap: f32,
610
611    #[serde(default = "default_false")]
612    pub collapse: bool,
613}
614
615impl<E: SerdeElement> SerdeElement for RepeatBottom<E> {
616    fn element(
617        &self,
618        fonts: &impl for<'a> Index<&'a str, Output = Font>,
619        callback: impl CompositeElementCallback,
620    ) {
621        callback.call(&elements::repeat_bottom::RepeatBottom {
622            content: SerdeElementElement {
623                element: &*self.content,
624                fonts,
625            },
626            bottom: SerdeElementElement {
627                element: &*self.bottom,
628                fonts,
629            },
630            gap: self.gap,
631            collapse: self.collapse,
632        });
633    }
634}
635
636#[derive(Clone, Serialize, Deserialize)]
637pub struct PinBelow<E> {
638    pub content: Box<E>,
639    pub pinned_element: Box<E>,
640    pub gap: f32,
641
642    #[serde(default = "default_false")]
643    pub collapse: bool,
644}
645
646impl<E: SerdeElement> SerdeElement for PinBelow<E> {
647    fn element(
648        &self,
649        fonts: &impl for<'a> Index<&'a str, Output = Font>,
650        callback: impl CompositeElementCallback,
651    ) {
652        callback.call(&elements::pin_below::PinBelow {
653            content: SerdeElementElement {
654                element: &*self.content,
655                fonts,
656            },
657            pinned_element: SerdeElementElement {
658                element: &*self.pinned_element,
659                fonts,
660            },
661            gap: self.gap,
662            collapse: self.collapse,
663        });
664    }
665}
666
667#[derive(Clone, Serialize, Deserialize)]
668pub struct ForceBreak;
669
670impl SerdeElement for ForceBreak {
671    fn element(
672        &self,
673        _: &impl for<'a> Index<&'a str, Output = Font>,
674        callback: impl CompositeElementCallback,
675    ) {
676        callback.call(&elements::force_break::ForceBreak);
677    }
678}
679
680#[derive(Clone, Serialize, Deserialize)]
681pub struct BreakWhole<E> {
682    pub element: Box<E>,
683}
684
685impl<E: SerdeElement> SerdeElement for BreakWhole<E> {
686    fn element(
687        &self,
688        fonts: &impl for<'a> Index<&'a str, Output = Font>,
689        callback: impl CompositeElementCallback,
690    ) {
691        callback.call(&elements::break_whole::BreakWhole(&SerdeElementElement {
692            element: &*self.element,
693            fonts,
694        }));
695    }
696}
697
698#[derive(Clone, Serialize, Deserialize)]
699pub struct MinFirstHeight<E> {
700    pub element: Box<E>,
701    pub min_first_height: f32,
702}
703
704impl<E: SerdeElement> SerdeElement for MinFirstHeight<E> {
705    fn element(
706        &self,
707        fonts: &impl for<'a> Index<&'a str, Output = Font>,
708        callback: impl CompositeElementCallback,
709    ) {
710        callback.call(&elements::min_first_height::MinFirstHeight {
711            element: SerdeElementElement {
712                element: &*self.element,
713                fonts,
714            },
715            min_first_height: self.min_first_height,
716        });
717    }
718}
719
720#[derive(Clone, Serialize, Deserialize)]
721pub struct AlignLocationBottom<E> {
722    pub element: Box<E>,
723}
724
725impl<E: SerdeElement> SerdeElement for AlignLocationBottom<E> {
726    fn element(
727        &self,
728        fonts: &impl for<'a> Index<&'a str, Output = Font>,
729        callback: impl CompositeElementCallback,
730    ) {
731        callback.call(&elements::align_location_bottom::AlignLocationBottom(
732            SerdeElementElement {
733                element: &*self.element,
734                fonts,
735            },
736        ));
737    }
738}
739
740#[derive(Clone, Serialize, Deserialize)]
741pub struct AlignPreferredHeightBottom<E> {
742    pub element: Box<E>,
743}
744
745impl<E: SerdeElement> SerdeElement for AlignPreferredHeightBottom<E> {
746    fn element(
747        &self,
748        fonts: &impl for<'a> Index<&'a str, Output = Font>,
749        callback: impl CompositeElementCallback,
750    ) {
751        callback.call(
752            &elements::align_preferred_height_bottom::AlignPreferredHeightBottom(
753                SerdeElementElement {
754                    element: &*self.element,
755                    fonts,
756                },
757            ),
758        );
759    }
760}
761
762#[derive(Clone, Serialize, Deserialize)]
763pub struct ExpandToPreferredHeight<E> {
764    pub element: Box<E>,
765}
766
767impl<E: SerdeElement> SerdeElement for ExpandToPreferredHeight<E> {
768    fn element(
769        &self,
770        fonts: &impl for<'a> Index<&'a str, Output = Font>,
771        callback: impl CompositeElementCallback,
772    ) {
773        callback.call(
774            &elements::expand_to_preferred_height::ExpandToPreferredHeight(SerdeElementElement {
775                element: &*self.element,
776                fonts,
777            }),
778        );
779    }
780}
781
782#[derive(Clone, Serialize, Deserialize)]
783pub struct CenterInPreferredHeight<E> {
784    pub element: Box<E>,
785}
786
787impl<E: SerdeElement> SerdeElement for CenterInPreferredHeight<E> {
788    fn element(
789        &self,
790        fonts: &impl for<'a> Index<&'a str, Output = Font>,
791        callback: impl CompositeElementCallback,
792    ) {
793        callback.call(
794            &elements::center_in_preferred_height::CenterInPreferredHeight(SerdeElementElement {
795                element: &*self.element,
796                fonts,
797            }),
798        );
799    }
800}
801
802#[derive(Clone, Serialize, Deserialize)]
803pub struct ShrinkToFit<E> {
804    pub element: Box<E>,
805    pub min_height: f32,
806}
807
808impl<E: SerdeElement> SerdeElement for ShrinkToFit<E> {
809    fn element(
810        &self,
811        fonts: &impl for<'a> Index<&'a str, Output = Font>,
812        callback: impl CompositeElementCallback,
813    ) {
814        callback.call(&elements::shrink_to_fit::ShrinkToFit {
815            element: SerdeElementElement {
816                element: &*self.element,
817                fonts,
818            },
819            min_height: self.min_height,
820        });
821    }
822}
823
824#[derive(Clone, Serialize, Deserialize)]
825pub struct Rotate<E> {
826    pub element: Box<E>,
827    pub rotation: Rotation,
828}
829
830impl<E: SerdeElement> SerdeElement for Rotate<E> {
831    fn element(
832        &self,
833        fonts: &impl for<'a> Index<&'a str, Output = Font>,
834        callback: impl CompositeElementCallback,
835    ) {
836        callback.call(&elements::rotate::Rotate {
837            element: SerdeElementElement {
838                element: &*self.element,
839                fonts,
840            },
841            rotation: self.rotation,
842        });
843    }
844}
845
846#[derive(Clone, Serialize, Deserialize)]
847pub struct MaxWidth<E> {
848    pub element: Box<E>,
849    pub max_width: f32,
850}
851
852impl<E: SerdeElement> SerdeElement for MaxWidth<E> {
853    fn element(
854        &self,
855        fonts: &impl for<'a> Index<&'a str, Output = Font>,
856        callback: impl CompositeElementCallback,
857    ) {
858        callback.call(&elements::max_width::MaxWidth {
859            element: SerdeElementElement {
860                element: &*self.element,
861                fonts,
862            },
863            max_width: self.max_width,
864        });
865    }
866}
867
868#[derive(Clone, Serialize, Deserialize)]
869pub enum PageNumberText {
870    Current {
871        before: String,
872        after: String,
873    },
874    Total {
875        before: String,
876        after: String,
877    },
878    CurrentAndTotal {
879        before: String,
880        between: String,
881        after: String,
882    },
883}
884
885#[derive(Clone, Serialize, Deserialize)]
886pub struct PageNumber {
887    pub skip_pages: usize,
888    pub pos: (elements::page::X, elements::page::Y),
889    pub text: PageNumberText,
890    pub font: String,
891    pub size: f32,
892    pub color: u32,
893    pub underline: bool,
894}
895
896#[derive(Clone, Serialize, Deserialize)]
897pub struct DecorationElement<E> {
898    pub element: E,
899    pub pos: (elements::page::X, elements::page::Y),
900    pub width: Option<f32>,
901    pub skip_pages: usize,
902    pub repeat: bool,
903}
904
905#[derive(Clone, Serialize, Deserialize)]
906pub struct Page<E> {
907    pub primary: Box<E>,
908    pub border_left: f32,
909    pub border_right: f32,
910    pub border_top: f32,
911    pub border_bottom: f32,
912    pub decoration_elements: Vec<DecorationElement<E>>,
913    pub page_numbers: Vec<PageNumber>,
914}
915
916impl<E: SerdeElement> SerdeElement for Page<E> {
917    fn element(
918        &self,
919        fonts: &impl for<'a> Index<&'a str, Output = Font>,
920        callback: impl CompositeElementCallback,
921    ) {
922        callback.call(&elements::page::Page {
923            primary: SerdeElementElement {
924                element: &*self.primary,
925                fonts,
926            },
927            border_left: self.border_left,
928            border_right: self.border_right,
929            border_top: self.border_top,
930            border_bottom: self.border_bottom,
931            decoration_elements: |content, page, page_count| {
932                for decoration_element in &self.decoration_elements {
933                    if page == decoration_element.skip_pages
934                        || decoration_element.repeat && page > decoration_element.skip_pages
935                    {
936                        content.add(
937                            &SerdeElementElement {
938                                element: &decoration_element.element,
939                                fonts,
940                            },
941                            decoration_element.pos,
942                            decoration_element.width,
943                        );
944                    }
945                }
946
947                for page_number in &self.page_numbers {
948                    if page >= page_number.skip_pages {
949                        content.add(
950                            &elements::text::Text {
951                                underline: page_number.underline,
952                                color: page_number.color,
953                                ..elements::text::Text::basic(
954                                    // Since the decoration_elements callback is only called when
955                                    // drawing it shouldn't be a problem to be allocating a string here,
956                                    // but it could potentially be optimized by reusing the buffer.
957                                    &match page_number.text {
958                                        PageNumberText::Current {
959                                            ref before,
960                                            ref after,
961                                        } => {
962                                            format!("{before}{}{after}", page + 1)
963                                        }
964                                        PageNumberText::Total {
965                                            ref before,
966                                            ref after,
967                                        } => format!("{before}{page_count}{after}"),
968                                        PageNumberText::CurrentAndTotal {
969                                            ref before,
970                                            ref between,
971                                            ref after,
972                                        } => {
973                                            format!(
974                                                "{before}{}{between}{page_count}{after}",
975                                                page + 1
976                                            )
977                                        }
978                                    },
979                                    &*fonts[&page_number.font],
980                                    page_number.size,
981                                )
982                            },
983                            page_number.pos,
984                            Option::None,
985                        );
986                    }
987                }
988            },
989        });
990    }
991}