Skip to main content

appcore_filemaker/
source_page.rs

1// =============================================================================
2//        #######
3//     ###       ###     F: source_page.rs
4//    ##   ## ##   ##    P: AppCore-Runtime
5//         ## ##
6//                       C: 2026/08/30 05:00:00 by dnettoRaw
7//    ##   ## ##   ##    U: 2026/08/30 05:00:00 by dnettoRaw
8//      ###########      S: 1.0.2-rc
9// =============================================================================
10
11//! Defines bounded source page contracts and behavior for this crate.
12
13use serde::{Deserialize, Serialize};
14
15use crate::source::{CollisionSource, ElementSource};
16use crate::{Length, Orientation};
17
18/// Page or canvas geometry and role-layer source.
19#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
20#[serde(deny_unknown_fields)]
21pub struct PageSource {
22    /// Named preset, mutually exclusive with explicit dimensions.
23    #[serde(default)]
24    pub preset: Option<String>,
25    /// Explicit width.
26    #[serde(default)]
27    pub width: Option<Length>,
28    /// Explicit height.
29    #[serde(default)]
30    pub height: Option<Length>,
31    /// Orientation applied after preset lookup.
32    #[serde(default)]
33    pub orientation: Orientation,
34    /// Page margins.
35    #[serde(default)]
36    pub margin: EdgeSource,
37    /// Bleed outside the trim box.
38    #[serde(default)]
39    pub bleed: EdgeSource,
40    /// Safe inset inside the page.
41    #[serde(default)]
42    pub safe: EdgeSource,
43    /// Request crop marks from compatible exporters.
44    #[serde(default)]
45    pub crop_marks: bool,
46    /// Page collision policy inherited after the document policy.
47    #[serde(default)]
48    pub collision: Option<CollisionSource>,
49    /// Layer composited on every physical page.
50    #[serde(default)]
51    pub master: PageLayerSource,
52    /// Layer composited on the first page.
53    #[serde(default)]
54    pub first: PageLayerSource,
55    /// Layer composited only on middle pages.
56    #[serde(default)]
57    pub continuation: PageLayerSource,
58    /// Layer composited on the last page when the document has multiple pages.
59    #[serde(default)]
60    pub last: PageLayerSource,
61}
62
63impl PageSource {
64    pub(crate) fn has_layer_elements(&self) -> bool {
65        self.element_lists()
66            .iter()
67            .any(|elements| !elements.is_empty())
68    }
69
70    pub(crate) fn element_lists(&self) -> [&[ElementSource]; 12] {
71        [
72            &self.master.background,
73            &self.master.header,
74            &self.master.footer,
75            &self.first.background,
76            &self.first.header,
77            &self.first.footer,
78            &self.continuation.background,
79            &self.continuation.header,
80            &self.continuation.footer,
81            &self.last.background,
82            &self.last.header,
83            &self.last.footer,
84        ]
85    }
86
87    pub(crate) fn element_lists_mut(&mut self) -> [&mut Vec<ElementSource>; 12] {
88        [
89            &mut self.master.background,
90            &mut self.master.header,
91            &mut self.master.footer,
92            &mut self.first.background,
93            &mut self.first.header,
94            &mut self.first.footer,
95            &mut self.continuation.background,
96            &mut self.continuation.header,
97            &mut self.continuation.footer,
98            &mut self.last.background,
99            &mut self.last.header,
100            &mut self.last.footer,
101        ]
102    }
103
104    pub(crate) fn placed_element_lists(&self) -> [(crate::PagePlacement, &[ElementSource]); 12] {
105        use crate::{PageBand::*, PageRole::*};
106        [
107            (placement(Master, Background), &self.master.background),
108            (placement(Master, Header), &self.master.header),
109            (placement(Master, Footer), &self.master.footer),
110            (placement(First, Background), &self.first.background),
111            (placement(First, Header), &self.first.header),
112            (placement(First, Footer), &self.first.footer),
113            (
114                placement(Continuation, Background),
115                &self.continuation.background,
116            ),
117            (placement(Continuation, Header), &self.continuation.header),
118            (placement(Continuation, Footer), &self.continuation.footer),
119            (placement(Last, Background), &self.last.background),
120            (placement(Last, Header), &self.last.header),
121            (placement(Last, Footer), &self.last.footer),
122        ]
123    }
124}
125
126const fn placement(role: crate::PageRole, band: crate::PageBand) -> crate::PagePlacement {
127    crate::PagePlacement { role, band }
128}
129
130/// Elements painted in one semantic page layer.
131#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
132#[serde(default, deny_unknown_fields)]
133pub struct PageLayerSource {
134    /// Elements painted behind normal body content.
135    pub background: Vec<ElementSource>,
136    /// Header elements painted above normal body content.
137    pub header: Vec<ElementSource>,
138    /// Footer elements painted above normal body content.
139    pub footer: Vec<ElementSource>,
140}
141
142/// Four independently specified edges.
143#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
144#[serde(default, deny_unknown_fields)]
145pub struct EdgeSource {
146    /// Top edge.
147    pub top: Option<Length>,
148    /// Right edge.
149    pub right: Option<Length>,
150    /// Bottom edge.
151    pub bottom: Option<Length>,
152    /// Left edge.
153    pub left: Option<Length>,
154}