Skip to main content

docx_rs/documents/elements/
style.rs

1use serde::Serialize;
2use std::io::Write;
3
4use crate::documents::BuildXML;
5use crate::escape::escape;
6use crate::types::*;
7use crate::xml_builder::*;
8use crate::StyleType;
9
10use super::*;
11
12#[derive(Debug, Clone, PartialEq, Serialize)]
13#[serde(rename_all = "camelCase")]
14pub struct Style {
15    pub style_id: String,
16    pub name: Name,
17    pub style_type: StyleType,
18    pub run_property: RunProperty,
19    pub paragraph_property: ParagraphProperty,
20    pub table_property: TableProperty,
21    pub table_cell_property: TableCellProperty,
22    pub based_on: Option<BasedOn>,
23    pub next: Option<Next>,
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub link: Option<Link>,
26    #[serde(skip_serializing_if = "is_true")]
27    pub q_format: bool,
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub ui_priority: Option<usize>,
30    #[serde(skip_serializing_if = "is_false")]
31    pub semi_hidden: bool,
32    #[serde(skip_serializing_if = "is_false")]
33    pub unhide_when_used: bool,
34}
35
36const fn is_true(v: &bool) -> bool {
37    *v
38}
39
40const fn is_false(v: &bool) -> bool {
41    !*v
42}
43
44impl Default for Style {
45    fn default() -> Self {
46        let name = Name::new("");
47        let rpr = RunProperty::new();
48        let ppr = ParagraphProperty::new();
49        Style {
50            style_id: "".to_owned(),
51            style_type: StyleType::Paragraph,
52            name,
53            run_property: rpr,
54            paragraph_property: ppr,
55            table_property: TableProperty::new(),
56            table_cell_property: TableCellProperty::new(),
57            based_on: None,
58            next: None,
59            link: None,
60            q_format: true,
61            ui_priority: None,
62            semi_hidden: false,
63            unhide_when_used: false,
64        }
65    }
66}
67
68impl Style {
69    pub fn new(style_id: impl Into<String>, style_type: StyleType) -> Self {
70        let default = Default::default();
71        Style {
72            style_id: escape(&style_id.into()),
73            style_type,
74            ..default
75        }
76    }
77
78    pub fn name(mut self, name: impl Into<String>) -> Self {
79        self.name = Name::new(name);
80        self
81    }
82
83    pub fn based_on(mut self, base: impl Into<String>) -> Self {
84        self.based_on = Some(BasedOn::new(base));
85        self
86    }
87
88    pub fn next(mut self, next: impl Into<String>) -> Self {
89        self.next = Some(Next::new(next));
90        self
91    }
92
93    pub fn link(mut self, link: impl Into<String>) -> Self {
94        self.link = Some(Link::new(link));
95        self
96    }
97
98    pub fn q_format(mut self, q_format: bool) -> Self {
99        self.q_format = q_format;
100        self
101    }
102
103    pub fn ui_priority(mut self, ui_priority: usize) -> Self {
104        self.ui_priority = Some(ui_priority);
105        self
106    }
107
108    pub fn semi_hidden(mut self) -> Self {
109        self.semi_hidden = true;
110        self
111    }
112
113    pub fn unhide_when_used(mut self) -> Self {
114        self.unhide_when_used = true;
115        self
116    }
117
118    pub fn size(mut self, size: usize) -> Self {
119        self.run_property = self.run_property.size(size);
120        self
121    }
122
123    pub fn color(mut self, color: impl Into<String>) -> Self {
124        self.run_property = self.run_property.color(color);
125        self
126    }
127
128    /// Sets the theme color reference (`w:themeColor`) on the style's run color.
129    pub fn theme_color(mut self, theme_color: crate::types::ThemeColor) -> Self {
130        self.run_property = self.run_property.theme_color(theme_color);
131        self
132    }
133
134    /// Sets the theme shade modifier (`w:themeShade`) on the style's run color.
135    pub fn theme_shade(mut self, theme_shade: impl Into<String>) -> Self {
136        self.run_property = self.run_property.theme_shade(theme_shade);
137        self
138    }
139
140    /// Sets the theme tint modifier (`w:themeTint`) on the style's run color.
141    pub fn theme_tint(mut self, theme_tint: impl Into<String>) -> Self {
142        self.run_property = self.run_property.theme_tint(theme_tint);
143        self
144    }
145
146    pub fn highlight(mut self, color: impl Into<String>) -> Self {
147        self.run_property = self.run_property.highlight(color);
148        self
149    }
150
151    pub fn bold(mut self) -> Self {
152        self.run_property = self.run_property.bold();
153        self
154    }
155
156    pub fn italic(mut self) -> Self {
157        self.run_property = self.run_property.italic();
158        self
159    }
160
161    pub fn underline(mut self, line_type: impl Into<String>) -> Self {
162        self.run_property = self.run_property.underline(line_type);
163        self
164    }
165
166    pub fn vanish(mut self) -> Self {
167        self.run_property = self.run_property.vanish();
168        self
169    }
170
171    pub fn text_border(mut self, b: TextBorder) -> Self {
172        self.run_property = self.run_property.text_border(b);
173        self
174    }
175
176    pub fn fonts(mut self, f: RunFonts) -> Self {
177        self.run_property = self.run_property.fonts(f);
178        self
179    }
180
181    pub fn align(mut self, alignment_type: AlignmentType) -> Self {
182        self.paragraph_property = self.paragraph_property.align(alignment_type);
183        self
184    }
185
186    pub fn text_alignment(mut self, alignment_type: TextAlignmentType) -> Self {
187        self.paragraph_property = self.paragraph_property.text_alignment(alignment_type);
188        self
189    }
190
191    pub fn snap_to_grid(mut self, v: bool) -> Self {
192        self.paragraph_property = self.paragraph_property.snap_to_grid(v);
193        self
194    }
195
196    pub fn line_spacing(mut self, spacing: LineSpacing) -> Self {
197        self.paragraph_property = self.paragraph_property.line_spacing(spacing);
198        self
199    }
200
201    pub fn indent(
202        mut self,
203        left: Option<i32>,
204        special_indent: Option<SpecialIndentType>,
205        end: Option<i32>,
206        start_chars: Option<i32>,
207    ) -> Self {
208        self.paragraph_property =
209            self.paragraph_property
210                .indent(left, special_indent, end, start_chars);
211        self
212    }
213
214    pub fn hanging_chars(mut self, chars: i32) -> Self {
215        self.paragraph_property = self.paragraph_property.hanging_chars(chars);
216        self
217    }
218
219    pub fn first_line_chars(mut self, chars: i32) -> Self {
220        self.paragraph_property = self.paragraph_property.first_line_chars(chars);
221        self
222    }
223
224    pub fn outline_lvl(mut self, l: usize) -> Self {
225        self.paragraph_property = self.paragraph_property.outline_lvl(l);
226        self
227    }
228
229    pub fn table_property(mut self, p: TableProperty) -> Self {
230        self.table_property = p;
231        self
232    }
233
234    pub fn table_indent(mut self, v: i32) -> Self {
235        self.table_property = self.table_property.indent(v);
236        self
237    }
238
239    pub fn table_align(mut self, v: TableAlignmentType) -> Self {
240        self.table_property = self.table_property.align(v);
241        self
242    }
243
244    pub fn style(mut self, s: impl Into<String>) -> Self {
245        self.table_property = self.table_property.style(s);
246        self
247    }
248
249    pub fn layout(mut self, t: TableLayoutType) -> Self {
250        self.table_property = self.table_property.layout(t);
251        self
252    }
253
254    pub fn width(mut self, w: usize, t: WidthType) -> Self {
255        self.table_property = self.table_property.width(w, t);
256        self
257    }
258
259    pub fn margins(mut self, margins: TableCellMargins) -> Self {
260        self.table_property = self.table_property.set_margins(margins);
261        self
262    }
263
264    pub fn set_borders(mut self, borders: TableBorders) -> Self {
265        self.table_property = self.table_property.set_borders(borders);
266        self
267    }
268
269    pub fn set_border(mut self, border: TableBorder) -> Self {
270        self.table_property = self.table_property.set_border(border);
271        self
272    }
273
274    pub fn clear_border(mut self, position: TableBorderPosition) -> Self {
275        self.table_property = self.table_property.clear_border(position);
276        self
277    }
278
279    pub fn clear_all_border(mut self) -> Self {
280        self.table_property = self.table_property.clear_all_border();
281        self
282    }
283
284    pub fn table_cell_property(mut self, p: TableCellProperty) -> Self {
285        self.table_cell_property = p;
286        self
287    }
288
289    // frameProperty
290    pub fn wrap(mut self, wrap: impl Into<String>) -> Self {
291        self.paragraph_property.frame_property = Some(FrameProperty {
292            wrap: Some(wrap.into()),
293            ..self.paragraph_property.frame_property.unwrap_or_default()
294        });
295        self
296    }
297
298    pub fn v_anchor(mut self, anchor: impl Into<String>) -> Self {
299        self.paragraph_property.frame_property = Some(FrameProperty {
300            v_anchor: Some(anchor.into()),
301            ..self.paragraph_property.frame_property.unwrap_or_default()
302        });
303        self
304    }
305
306    pub fn h_anchor(mut self, anchor: impl Into<String>) -> Self {
307        self.paragraph_property.frame_property = Some(FrameProperty {
308            h_anchor: Some(anchor.into()),
309            ..self.paragraph_property.frame_property.unwrap_or_default()
310        });
311        self
312    }
313
314    pub fn h_rule(mut self, r: impl Into<String>) -> Self {
315        self.paragraph_property.frame_property = Some(FrameProperty {
316            h_rule: Some(r.into()),
317            ..self.paragraph_property.frame_property.unwrap_or_default()
318        });
319        self
320    }
321
322    pub fn x_align(mut self, align: impl Into<String>) -> Self {
323        self.paragraph_property.frame_property = Some(FrameProperty {
324            x_align: Some(align.into()),
325            ..self.paragraph_property.frame_property.unwrap_or_default()
326        });
327        self
328    }
329
330    pub fn y_align(mut self, align: impl Into<String>) -> Self {
331        self.paragraph_property.frame_property = Some(FrameProperty {
332            y_align: Some(align.into()),
333            ..self.paragraph_property.frame_property.unwrap_or_default()
334        });
335        self
336    }
337
338    pub fn h_space(mut self, x: i32) -> Self {
339        self.paragraph_property.frame_property = Some(FrameProperty {
340            h_space: Some(x),
341            ..self.paragraph_property.frame_property.unwrap_or_default()
342        });
343        self
344    }
345
346    pub fn v_space(mut self, x: i32) -> Self {
347        self.paragraph_property.frame_property = Some(FrameProperty {
348            v_space: Some(x),
349            ..self.paragraph_property.frame_property.unwrap_or_default()
350        });
351        self
352    }
353
354    pub fn frame_x(mut self, x: i32) -> Self {
355        self.paragraph_property.frame_property = Some(FrameProperty {
356            x: Some(x),
357            ..self.paragraph_property.frame_property.unwrap_or_default()
358        });
359        self
360    }
361
362    pub fn frame_y(mut self, y: i32) -> Self {
363        self.paragraph_property.frame_property = Some(FrameProperty {
364            y: Some(y),
365            ..self.paragraph_property.frame_property.unwrap_or_default()
366        });
367        self
368    }
369
370    pub fn frame_width(mut self, n: u32) -> Self {
371        self.paragraph_property.frame_property = Some(FrameProperty {
372            w: Some(n),
373            ..self.paragraph_property.frame_property.unwrap_or_default()
374        });
375        self
376    }
377
378    pub fn frame_height(mut self, n: u32) -> Self {
379        self.paragraph_property.frame_property = Some(FrameProperty {
380            h: Some(n),
381            ..self.paragraph_property.frame_property.unwrap_or_default()
382        });
383        self
384    }
385}
386
387impl BuildXML for Style {
388    fn build_to<W: Write>(
389        &self,
390        stream: crate::xml::writer::EventWriter<W>,
391    ) -> crate::xml::writer::Result<crate::xml::writer::EventWriter<W>> {
392        // Set "Normal" as default if you need change these values please fix it
393        XMLBuilder::from(stream)
394            .open_style(self.style_type, &self.style_id)?
395            .add_child(&self.name)?
396            .add_child(&self.run_property)?
397            .add_child(&self.paragraph_property)?
398            .apply_if(self.style_type == StyleType::Table, |b| {
399                b.add_child(&self.table_cell_property)?
400                    .add_child(&self.table_property)
401            })?
402            .add_optional_child(&self.next)?
403            .add_optional_child(&self.link)?
404            .apply_if(self.q_format, |b| b.add_child(&QFormat::new()))?
405            .apply_if(self.ui_priority.is_some(), |b| {
406                b.ui_priority(self.ui_priority.unwrap_or_default())
407            })?
408            .apply_if(self.semi_hidden, |b| b.semi_hidden())?
409            .apply_if(self.unhide_when_used, |b| b.unhide_when_used())?
410            .add_optional_child(&self.based_on)?
411            .close()?
412            .into_inner()
413    }
414}
415
416#[cfg(test)]
417mod tests {
418
419    use super::*;
420    #[cfg(test)]
421    use pretty_assertions::assert_eq;
422    use std::str;
423
424    #[test]
425    fn test_build() {
426        let c = Style::new("Heading", StyleType::Paragraph).name("Heading1");
427        let b = c.build();
428        assert_eq!(
429            str::from_utf8(&b).unwrap(),
430            r#"<w:style w:type="paragraph" w:styleId="Heading"><w:name w:val="Heading1" /><w:rPr /><w:pPr><w:rPr /></w:pPr><w:qFormat /></w:style>"#
431        );
432    }
433
434    #[test]
435    fn test_build_with_visibility_flags() {
436        let c = Style::new("MyStyle", StyleType::Paragraph)
437            .name("My Style")
438            .q_format(false)
439            .ui_priority(99)
440            .semi_hidden()
441            .unhide_when_used();
442        let b = c.build();
443        assert_eq!(
444            str::from_utf8(&b).unwrap(),
445            r#"<w:style w:type="paragraph" w:styleId="MyStyle"><w:name w:val="My Style" /><w:rPr /><w:pPr><w:rPr /></w:pPr><w:uiPriority w:val="99" /><w:semiHidden /><w:unhideWhenUsed /></w:style>"#
446        );
447    }
448}