1use super::{CellRef, RangeRef};
2use serde::{Deserialize, Serialize};
3
4#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
5pub enum Orientation {
6 #[default]
7 Portrait,
8 Landscape,
9}
10
11#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
12pub struct PrintSettings {
13 pub orientation: Orientation,
14 pub paper: String,
16 pub margins: [f32; 6],
18 pub print_area: Option<RangeRef>,
19 pub title_rows: Option<(u32, u32)>,
20 pub title_cols: Option<(u32, u32)>,
21 pub scale: u16,
23 pub fit_to: Option<(u16, u16)>,
24 pub gridlines: bool,
25 pub headings: bool,
26 pub center_h: bool,
27 pub center_v: bool,
28 pub header: String,
30 pub footer: String,
31 pub row_breaks: Vec<u32>,
32 pub col_breaks: Vec<u32>,
33 #[serde(default)]
35 pub different_first: bool,
36 #[serde(default)]
38 pub different_odd_even: bool,
39 #[serde(default)]
40 pub first_header: String,
41 #[serde(default)]
42 pub first_footer: String,
43 #[serde(default)]
44 pub even_header: String,
45 #[serde(default)]
46 pub even_footer: String,
47 #[serde(default = "yes")]
49 pub hf_scale_with_doc: bool,
50 #[serde(default = "yes")]
51 pub hf_align_with_margins: bool,
52 #[serde(default)]
55 pub page_order: PageOrder,
56}
57
58fn yes() -> bool {
59 true
60}
61
62#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
64#[serde(rename_all = "camelCase")]
65pub enum PageOrder {
66 #[default]
68 DownThenOver,
69 OverThenDown,
71}
72
73impl PageOrder {
74 pub fn id(self) -> &'static str {
76 match self {
77 PageOrder::DownThenOver => "downThenOver",
78 PageOrder::OverThenDown => "overThenDown",
79 }
80 }
81 pub fn parse(id: &str) -> Option<PageOrder> {
82 match id {
83 "downThenOver" => Some(PageOrder::DownThenOver),
84 "overThenDown" => Some(PageOrder::OverThenDown),
85 _ => None,
86 }
87 }
88 pub fn index(self, row: usize, col: usize, rows: usize, cols: usize) -> usize {
91 match self {
92 PageOrder::DownThenOver => col.saturating_mul(rows).saturating_add(row),
93 PageOrder::OverThenDown => row.saturating_mul(cols).saturating_add(col),
94 }
95 }
96}
97
98#[derive(Clone, Copy, Debug, PartialEq, Eq)]
100pub enum HfPage {
101 Odd,
102 Even,
103 First,
104}
105
106impl HfPage {
107 pub fn id(self) -> &'static str {
108 match self {
109 HfPage::Odd => "odd",
110 HfPage::Even => "even",
111 HfPage::First => "first",
112 }
113 }
114 pub fn parse(id: &str) -> Option<HfPage> {
115 match id {
116 "odd" | "all" => Some(HfPage::Odd),
117 "even" => Some(HfPage::Even),
118 "first" => Some(HfPage::First),
119 _ => None,
120 }
121 }
122}
123
124impl PrintSettings {
125 pub fn hf_page(&self, first: bool, number: usize) -> HfPage {
127 if self.different_first && first {
128 HfPage::First
129 } else if self.different_odd_even && number.is_multiple_of(2) {
130 HfPage::Even
131 } else {
132 HfPage::Odd
133 }
134 }
135 pub fn hf(&self, page: HfPage, footer: bool) -> &String {
137 match (page, footer) {
138 (HfPage::Odd, false) => &self.header,
139 (HfPage::Odd, true) => &self.footer,
140 (HfPage::Even, false) => &self.even_header,
141 (HfPage::Even, true) => &self.even_footer,
142 (HfPage::First, false) => &self.first_header,
143 (HfPage::First, true) => &self.first_footer,
144 }
145 }
146 pub fn hf_mut(&mut self, page: HfPage, footer: bool) -> &mut String {
147 match (page, footer) {
148 (HfPage::Odd, false) => &mut self.header,
149 (HfPage::Odd, true) => &mut self.footer,
150 (HfPage::Even, false) => &mut self.even_header,
151 (HfPage::Even, true) => &mut self.even_footer,
152 (HfPage::First, false) => &mut self.first_header,
153 (HfPage::First, true) => &mut self.first_footer,
154 }
155 }
156 pub fn has_header_footer(&self) -> bool {
158 [&self.header, &self.footer, &self.first_header, &self.first_footer, &self.even_header, &self.even_footer].iter().any(|t| !t.is_empty())
159 }
160}
161
162impl Default for PrintSettings {
163 fn default() -> Self {
164 PrintSettings {
165 orientation: Orientation::Portrait,
166 paper: "Letter".into(),
167 margins: [0.7, 0.7, 0.75, 0.75, 0.3, 0.3],
168 print_area: None,
169 title_rows: None,
170 title_cols: None,
171 scale: 100,
172 fit_to: None,
173 gridlines: false,
174 headings: false,
175 center_h: false,
176 center_v: false,
177 header: String::new(),
178 footer: String::new(),
179 row_breaks: vec![],
180 col_breaks: vec![],
181 different_first: false,
182 different_odd_even: false,
183 first_header: String::new(),
184 first_footer: String::new(),
185 even_header: String::new(),
186 even_footer: String::new(),
187 hf_scale_with_doc: true,
188 hf_align_with_margins: true,
189 page_order: PageOrder::DownThenOver,
190 }
191 }
192}
193
194#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
195pub struct SheetProtection {
196 #[serde(default, skip_serializing_if = "Option::is_none")]
199 pub password: Option<crate::model::PasswordHash>,
200 #[serde(rename = "password_hash", default, skip_serializing_if = "Option::is_none")]
203 pub legacy_password_hash: Option<u64>,
204 pub select_locked: bool,
205 pub select_unlocked: bool,
206 pub format_cells: bool,
207 pub format_columns: bool,
208 pub format_rows: bool,
209 pub insert_columns: bool,
210 pub insert_rows: bool,
211 pub delete_columns: bool,
212 pub delete_rows: bool,
213 pub sort: bool,
214 pub autofilter: bool,
215}
216
217#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
220pub struct EditRange {
221 pub title: String,
222 pub ranges: Vec<RangeRef>,
223 pub password: Option<crate::model::PasswordHash>,
224 #[serde(default, skip_serializing_if = "Option::is_none")]
226 pub security_descriptor: Option<String>,
227}
228
229impl EditRange {
230 pub fn contains(&self, c: CellRef) -> bool {
231 self.ranges.iter().any(|r| r.contains(c))
232 }
233}
234
235pub fn password_hash(pw: &str) -> u64 {
238 let mut h: u64 = 0xcbf29ce484222325 ^ 0x5348_4545_5443;
239 for b in pw.bytes() {
240 h ^= b as u64;
241 h = h.wrapping_mul(0x100000001b3);
242 }
243 h
244}
245
246#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
248pub struct LineInfo {
249 pub size: Option<f32>,
251 pub hidden: bool,
252 pub outline: u8,
253 pub collapsed: bool,
254 pub style: Option<crate::model::style::StyleId>,
255 #[serde(default)]
257 pub custom: bool,
258}
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263
264 #[test]
265 fn page_order_numbers_pages_and_old_settings_load_down_then_over() {
266 let place = |order: PageOrder| [(0, 0), (1, 0), (0, 1), (1, 1), (0, 2), (1, 2)].map(|(r, c)| order.index(r, c, 2, 3));
268 assert_eq!(place(PageOrder::DownThenOver), [0, 1, 2, 3, 4, 5]);
269 assert_eq!(place(PageOrder::OverThenDown), [0, 3, 1, 4, 2, 5]);
270 assert_eq!(PageOrder::OverThenDown.index(usize::MAX, usize::MAX, usize::MAX, usize::MAX), usize::MAX);
271 for order in [PageOrder::DownThenOver, PageOrder::OverThenDown] {
272 assert_eq!(PageOrder::parse(order.id()), Some(order));
273 }
274 assert_eq!(PageOrder::parse("OverThenDown"), None);
275 let mut p = PrintSettings { page_order: PageOrder::OverThenDown, ..Default::default() };
278 let mut saved = serde_json::to_value(&p).unwrap();
279 assert_eq!(saved["page_order"], "overThenDown");
280 assert!(saved.as_object_mut().unwrap().remove("page_order").is_some());
281 p = serde_json::from_value(saved).unwrap();
282 assert_eq!(p, PrintSettings::default());
283 }
284}