1use crate::font::Font;
2use crate::text_document::Tab;
3use crate::ModelError;
4
5pub(crate) type FormatChangeResult = Result<Option<()>, ModelError>;
6
7#[derive(Clone, Eq, PartialEq, Debug)]
8pub enum Format {
9 FrameFormat(FrameFormat),
10 TextFormat(TextFormat),
11 BlockFormat(BlockFormat),
12 ImageFormat(ImageFormat),
13}
14
15pub(crate) trait IsFormat {
16 fn merge_with(&mut self, other_format: &Self) -> FormatChangeResult
17 where
18 Self: Sized;
19}
20
21#[derive(Default, Clone, Eq, PartialEq, Debug)]
22pub struct FrameFormat {
23 pub height: Option<usize>,
24 pub width: Option<usize>,
25 pub top_margin: Option<usize>,
26 pub bottom_margin: Option<usize>,
27 pub left_margin: Option<usize>,
28 pub right_margin: Option<usize>,
29 pub padding: Option<usize>,
30 pub border: Option<usize>,
31 pub position: Option<Position>,
32}
33
34impl FrameFormat {
35 pub fn new() -> Self {
36 FrameFormat {
37 ..Default::default()
38 }
39 }
40}
41
42impl IsFormat for FrameFormat {
43 fn merge_with(&mut self, other_format: &Self) -> FormatChangeResult
44 where
45 Self: Sized,
46 {
47 if let Some(value) = other_format.height {
48 self.height = Some(value);
49 }
50 if let Some(value) = other_format.width {
51 self.width = Some(value);
52 }
53 if let Some(value) = other_format.top_margin {
54 self.top_margin = Some(value);
55 }
56 if let Some(value) = other_format.bottom_margin {
57 self.bottom_margin = Some(value);
58 }
59 if let Some(value) = other_format.left_margin {
60 self.left_margin = Some(value);
61 }
62 if let Some(value) = other_format.right_margin {
63 self.right_margin = Some(value);
64 }
65 if let Some(value) = other_format.padding {
66 self.padding = Some(value);
67 }
68 if let Some(value) = other_format.border {
69 self.border = Some(value);
70 }
71 if let Some(value) = other_format.position {
72 self.position = Some(value);
73 }
74
75 Ok(Some(()))
76 }
77}
78
79#[derive(Clone, Copy, Eq, PartialEq, Debug)]
80pub enum Position {
81 InFlow,
82 FloatLeft,
83 FloatRight,
84}
85
86#[derive(Default, Clone, Eq, PartialEq, Debug)]
87pub struct TextFormat {
88 pub anchor_href: Option<String>,
89 pub anchor_names: Option<Vec<String>>,
90 pub is_anchor: Option<bool>,
91 pub font: Font,
92 pub tool_tip: Option<String>,
94 pub underline_style: Option<UnderlineStyle>,
96 pub vertical_alignment: Option<CharVerticalAlignment>,
97}
98
99impl TextFormat {
100 pub fn new() -> Self {
101 TextFormat {
102 ..Default::default()
103 }
104 }
105}
106
107impl IsFormat for TextFormat {
108 fn merge_with(&mut self, other_format: &Self) -> FormatChangeResult
109 where
110 Self: Sized,
111 {
112 if let Some(value) = &other_format.anchor_href {
113 self.anchor_href = Some(value.clone());
114 }
115
116 if let Some(value) = &other_format.anchor_names {
117 self.anchor_names = Some(value.clone());
118 }
119
120 if let Some(value) = other_format.is_anchor {
121 self.is_anchor = Some(value);
122 }
123
124 self.font.merge_with(&other_format.font)?;
125
126 if let Some(value) = &other_format.tool_tip {
127 self.tool_tip = Some(value.clone());
128 }
129
130 if let Some(value) = other_format.underline_style {
131 self.underline_style = Some(value);
132 }
133
134 if let Some(value) = other_format.vertical_alignment {
135 self.vertical_alignment = Some(value);
136 }
137
138 Ok(Some(()))
139 }
140}
141
142impl std::ops::Deref for TextFormat {
143 type Target = Font;
144 fn deref(&self) -> &Self::Target {
145 &self.font
146 }
147}
148
149impl std::ops::DerefMut for TextFormat {
150 fn deref_mut(&mut self) -> &mut Self::Target {
151 &mut self.font
152 }
153}
154
155#[derive(Clone, Copy, Eq, PartialEq, Debug)]
156pub enum CharVerticalAlignment {
157 AlignNormal,
158 AlignSuperScript,
159 AlignSubScript,
160 AlignMiddle,
161 AlignBottom,
162 AlignTop,
163 AlignBaseline,
164}
165
166#[derive(Clone, Copy, Eq, PartialEq, Debug)]
167pub enum UnderlineStyle {
168 NoUnderline,
169 SingleUnderline,
170 DashUnderline,
171 DotLine,
172 DashDotLine,
173 DashDotDotLine,
174 WaveUnderline,
175 SpellCheckUnderline,
176}
177
178#[derive(Clone, Eq, PartialEq, Debug, Default)]
179pub struct BlockFormat {
180 pub alignment: Option<Alignment>,
181 pub top_margin: Option<usize>,
182 pub bottom_margin: Option<usize>,
183 pub left_margin: Option<usize>,
184 pub right_margin: Option<usize>,
185 pub heading_level: Option<u8>,
186 pub indent: Option<u8>,
187 pub text_indent: Option<usize>,
188 pub tab_positions: Option<Vec<Tab>>,
189 pub marker: Option<MarkerType>,
190}
191
192impl BlockFormat {
193 pub fn new() -> Self {
194 BlockFormat {
195 ..Default::default()
196 }
197 }
198}
199
200impl IsFormat for BlockFormat {
201 fn merge_with(&mut self, other_format: &Self) -> FormatChangeResult
202 where
203 Self: Sized,
204 {
205 if let Some(value) = other_format.alignment {
206 self.alignment = Some(value);
207 }
208 if let Some(value) = other_format.top_margin {
209 self.top_margin = Some(value);
210 }
211 if let Some(value) = other_format.bottom_margin {
212 self.bottom_margin = Some(value);
213 }
214 if let Some(value) = other_format.left_margin {
215 self.left_margin = Some(value);
216 }
217 if let Some(value) = other_format.right_margin {
218 self.right_margin = Some(value);
219 }
220 if let Some(value) = other_format.heading_level {
221 self.heading_level = Some(value);
222 }
223
224 if let Some(value) = other_format.indent {
225 self.indent = Some(value);
226 }
227
228 if let Some(value) = other_format.text_indent {
229 self.text_indent = Some(value);
230 }
231
232 if let Some(value) = &other_format.tab_positions {
233 self.tab_positions = Some(value.clone());
234 }
235
236 if let Some(value) = other_format.marker {
237 self.marker = Some(value);
238 }
239
240 Ok(Some(()))
241 }
242}
243
244#[derive(Clone, Copy, Eq, PartialEq, Debug)]
245pub enum Alignment {
246 AlignLeft,
247 AlignRight,
248 AlignHCenter,
249 AlignJustify,
250}
251
252#[derive(Clone, Copy, Eq, PartialEq, Debug)]
253pub enum MarkerType {
254 NoMarker,
255 Unchecked,
256 Checked,
257}
258
259#[derive(Default, Clone, Eq, PartialEq, Debug)]
260pub struct ImageFormat {
261 pub(crate) text_format: TextFormat,
262 pub height: Option<usize>,
263 pub width: Option<usize>,
264 pub quality: Option<u8>,
265 pub name: Option<String>,
266}
267
268impl ImageFormat {
269 pub fn new() -> Self {
270 ImageFormat {
271 ..Default::default()
272 }
273 }
274}
275
276impl IsFormat for ImageFormat {
277 fn merge_with(&mut self, other_format: &Self) -> FormatChangeResult
279 where
280 Self: Sized,
281 {
282 self.text_format.merge_with(&other_format.text_format)?;
283
284 if let Some(value) = other_format.height {
285 self.height = Some(value)
286 }
287
288 if let Some(value) = other_format.width {
289 self.width = Some(value)
290 }
291
292 if let Some(value) = other_format.quality {
293 self.quality = Some(value)
294 }
295
296 if let Some(value) = other_format.name.clone() {
297 self.name = Some(value)
298 }
299
300 Ok(Some(()))
301 }
302}
303
304impl std::ops::Deref for ImageFormat {
305 type Target = TextFormat;
306 fn deref(&self) -> &Self::Target {
307 &self.text_format
308 }
309}
310
311pub(crate) trait FormattedElement<F: IsFormat> {
312 fn format(&self) -> F;
313
314 fn set_format(&self, format: &F) -> FormatChangeResult;
315
316 fn merge_format(&self, format: &F) -> FormatChangeResult;
317}
318
319#[cfg(test)]
320mod tests {
321
322 use super::*;
323
324 #[test]
325 fn merge_image_formats() {
326 let mut first = ImageFormat::new();
327 first.width = Some(40);
328 let mut second = ImageFormat::new();
329 second.height = Some(10);
330
331 first.merge_with(&second).unwrap();
332
333 assert_eq!(first.width, Some(40));
334 assert_eq!(first.height, Some(10));
335 }
336
337 #[test]
338 fn merge_block_formats() {
339 let mut first = BlockFormat::new();
340 first.alignment = Some(Alignment::AlignRight);
341 let mut second = BlockFormat::new();
342 second.left_margin = Some(10);
343
344 first.merge_with(&second).unwrap();
345
346 assert_eq!(first.alignment, Some(Alignment::AlignRight));
347 assert_eq!(first.left_margin, Some(10));
348 }
349
350 #[test]
351 fn merge_frame_formats() {
352 let mut first = FrameFormat::new();
353 first.position = Some(Position::FloatLeft);
354 let mut second = FrameFormat::new();
355 second.height = Some(10);
356
357 first.merge_with(&second).unwrap();
358
359 assert_eq!(first.position, Some(Position::FloatLeft));
360 assert_eq!(first.height, Some(10));
361 }
362
363 #[test]
364 fn merge_char_foramts() {
365 let mut first = TextFormat::new();
366 first.letter_spacing = Some(40);
367 let mut second = TextFormat::new();
368 second.underline = Some(true);
369
370 first.merge_with(&second).unwrap();
371
372 assert_eq!(first.letter_spacing, Some(40));
373 assert_eq!(first.underline, Some(true));
374 }
375}