docx_rs/documents/elements/
run_property.rs1use serde::Serialize;
2use std::io::Write;
3
4use super::*;
5use crate::documents::BuildXML;
6use crate::types::*;
7use crate::xml_builder::*;
8
9#[derive(Debug, Clone, Serialize, PartialEq, Default)]
10#[serde(rename_all = "camelCase")]
11pub struct RunProperty {
12 #[serde(skip_serializing_if = "Option::is_none")]
13 pub style: Option<RunStyle>,
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub sz: Option<Sz>,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub sz_cs: Option<SzCs>,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub color: Option<Color>,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub highlight: Option<Highlight>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub vert_align: Option<VertAlign>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub underline: Option<Underline>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub bold: Option<Bold>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub bold_cs: Option<BoldCs>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub caps: Option<Caps>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub italic: Option<Italic>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub italic_cs: Option<ItalicCs>,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub vanish: Option<Vanish>,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub spec_vanish: Option<SpecVanish>,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub character_spacing: Option<CharacterSpacing>,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub stretch: Option<Stretch>,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub fonts: Option<RunFonts>,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 pub text_border: Option<TextBorder>,
48 #[serde(skip_serializing_if = "Option::is_none")]
49 pub del: Option<Delete>,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub ins: Option<Insert>,
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub strike: Option<Strike>,
54 #[serde(skip_serializing_if = "Option::is_none")]
55 pub dstrike: Option<Dstrike>,
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub positional_tab: Option<PositionalTab>,
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub shading: Option<Shading>,
60}
61
62impl RunProperty {
63 pub fn new() -> RunProperty {
64 Default::default()
65 }
66
67 pub fn style(mut self, style_id: &str) -> Self {
68 self.style = Some(RunStyle::new(style_id));
69 self
70 }
71
72 pub fn size(mut self, size: usize) -> RunProperty {
73 self.sz = Some(Sz::new(size));
74 self.sz_cs = Some(SzCs::new(size));
75 self
76 }
77
78 pub fn spacing(mut self, spacing: i32) -> RunProperty {
79 self.character_spacing = Some(CharacterSpacing::new(spacing));
80 self
81 }
82
83 pub fn color(mut self, color: impl Into<String>) -> RunProperty {
84 self.color = Some(Color::new(color));
85 self
86 }
87
88 pub fn highlight(mut self, color: impl Into<String>) -> RunProperty {
89 self.highlight = Some(Highlight::new(color));
90 self
91 }
92
93 pub fn vert_align(mut self, a: VertAlignType) -> Self {
94 self.vert_align = Some(VertAlign::new(a));
95 self
96 }
97
98 pub fn bold(mut self) -> RunProperty {
99 self.bold = Some(Bold::new());
100 self.bold_cs = Some(BoldCs::new());
101 self
102 }
103
104 pub fn disable_bold(mut self) -> RunProperty {
105 self.bold = Some(Bold::new().disable());
106 self.bold_cs = Some(BoldCs::new().disable());
107 self
108 }
109
110 pub fn caps(mut self) -> RunProperty {
111 self.caps = Some(Caps::new());
112 self
113 }
114
115 pub fn italic(mut self) -> RunProperty {
116 self.italic = Some(Italic::new());
117 self.italic_cs = Some(ItalicCs::new());
118 self
119 }
120
121 pub fn strike(mut self) -> RunProperty {
122 self.strike = Some(Strike::new());
123 self.dstrike = None;
124 self
125 }
126
127 pub fn disable_strike(mut self) -> RunProperty {
128 self.strike = Some(Strike::new().disable());
129 self
130 }
131
132 pub fn dstrike(mut self) -> RunProperty {
133 self.dstrike = Some(Dstrike::new());
134 self.strike = None;
135 self
136 }
137
138 pub fn disable_dstrike(mut self) -> RunProperty {
139 self.dstrike = Some(Dstrike::new().disable());
140 self
141 }
142
143 pub fn disable_italic(mut self) -> RunProperty {
144 self.italic = Some(Italic::new().disable());
145 self.italic_cs = Some(ItalicCs::new().disable());
146 self
147 }
148
149 pub fn underline(mut self, line_type: impl Into<String>) -> RunProperty {
150 self.underline = Some(Underline::new(line_type));
151 self
152 }
153
154 pub fn vanish(mut self) -> RunProperty {
155 self.vanish = Some(Vanish::new());
156 self
157 }
158
159 pub fn spec_vanish(mut self) -> RunProperty {
160 self.spec_vanish = Some(SpecVanish::new());
161 self
162 }
163
164 pub fn fonts(mut self, font: RunFonts) -> RunProperty {
165 self.fonts = Some(font);
166 self
167 }
168
169 pub fn character_spacing(mut self, v: i32) -> RunProperty {
170 self.character_spacing = Some(CharacterSpacing::new(v));
171 self
172 }
173
174 pub fn stretch(mut self, v: i32) -> RunProperty {
175 self.stretch = Some(Stretch::new(v));
176 self
177 }
178
179 pub fn text_border(mut self, b: TextBorder) -> Self {
180 self.text_border = Some(b);
181 self
182 }
183
184 pub fn delete(mut self, d: Delete) -> Self {
185 self.del = Some(d);
186 self
187 }
188
189 pub fn insert(mut self, i: Insert) -> Self {
190 self.ins = Some(i);
191 self
192 }
193
194 pub fn ptab(mut self, ptab: PositionalTab) -> Self {
195 self.positional_tab = Some(ptab);
196 self
197 }
198
199 pub fn shading(mut self, s: Shading) -> Self {
200 self.shading = Some(s);
201 self
202 }
203}
204
205impl BuildXML for RunProperty {
206 fn build_to<W: Write>(
207 &self,
208 stream: xml::writer::EventWriter<W>,
209 ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
210 XMLBuilder::from(stream)
211 .open_run_property()?
212 .add_optional_child(&self.sz)?
213 .add_optional_child(&self.sz_cs)?
214 .add_optional_child(&self.color)?
215 .add_optional_child(&self.bold)?
216 .add_optional_child(&self.bold_cs)?
217 .add_optional_child(&self.caps)?
218 .add_optional_child(&self.italic)?
219 .add_optional_child(&self.italic_cs)?
220 .add_optional_child(&self.strike)?
221 .add_optional_child(&self.dstrike)?
222 .add_optional_child(&self.highlight)?
223 .add_optional_child(&self.underline)?
224 .add_optional_child(&self.vanish)?
225 .add_optional_child(&self.spec_vanish)?
226 .add_optional_child(&self.fonts)?
227 .add_optional_child(&self.text_border)?
228 .add_optional_child(&self.ins)?
229 .add_optional_child(&self.del)?
230 .add_optional_child(&self.vert_align)?
231 .add_optional_child(&self.character_spacing)?
232 .add_optional_child(&self.stretch)?
233 .add_optional_child(&self.style)?
234 .add_optional_child(&self.positional_tab)?
235 .add_optional_child(&self.shading)?
236 .close()?
237 .into_inner()
238 }
239}
240
241#[cfg(test)]
242mod tests {
243
244 use super::*;
245 #[cfg(test)]
246 use pretty_assertions::assert_eq;
247 use std::str;
248
249 #[test]
250 fn test_size() {
251 let c = RunProperty::new().size(10).color("FFFFFF");
252 let b = c.build();
253 assert_eq!(
254 str::from_utf8(&b).unwrap(),
255 r#"<w:rPr><w:sz w:val="10" /><w:szCs w:val="10" /><w:color w:val="FFFFFF" /></w:rPr>"#
256 );
257 }
258
259 #[test]
260 fn test_highlight() {
261 let c = RunProperty::new().highlight("FFFFFF");
262 let b = c.build();
263 assert_eq!(
264 str::from_utf8(&b).unwrap(),
265 r#"<w:rPr><w:highlight w:val="FFFFFF" /></w:rPr>"#
266 );
267 }
268
269 #[test]
270 fn test_bold() {
271 let c = RunProperty::new().bold();
272 let b = c.build();
273 assert_eq!(
274 str::from_utf8(&b).unwrap(),
275 r#"<w:rPr><w:b /><w:bCs /></w:rPr>"#
276 );
277 }
278
279 #[test]
280 fn test_strike() {
281 let c = RunProperty::new().strike();
282 let b = c.build();
283 assert_eq!(
284 str::from_utf8(&b).unwrap(),
285 r#"<w:rPr><w:strike /></w:rPr>"#
286 );
287 }
288
289 #[test]
290 fn test_underline() {
291 let c = RunProperty::new().underline("single");
292 let b = c.build();
293 assert_eq!(
294 str::from_utf8(&b).unwrap(),
295 r#"<w:rPr><w:u w:val="single" /></w:rPr>"#
296 );
297 }
298
299 #[test]
300 fn test_vanish() {
301 let c = RunProperty::new().vanish();
302 let b = c.build();
303 assert_eq!(
304 str::from_utf8(&b).unwrap(),
305 r#"<w:rPr><w:vanish /></w:rPr>"#
306 );
307 }
308
309 #[test]
310 fn test_run_fonts() {
311 let c = RunProperty::new().fonts(RunFonts::new().east_asia("Hiragino"));
312 let b = c.build();
313 assert_eq!(
314 str::from_utf8(&b).unwrap(),
315 r#"<w:rPr><w:rFonts w:eastAsia="Hiragino" /></w:rPr>"#
316 );
317 }
318
319 #[test]
320 fn test_character_spacing() {
321 let c = RunProperty::new().spacing(20);
322 let b = c.build();
323 assert_eq!(
324 str::from_utf8(&b).unwrap(),
325 r#"<w:rPr><w:spacing w:val="20" /></w:rPr>"#
326 );
327 }
328
329 #[test]
330 fn test_stretch() {
331 let c = RunProperty::new().stretch(80);
332 let b = c.build();
333 assert_eq!(
334 str::from_utf8(&b).unwrap(),
335 r#"<w:rPr><w:w w:val="80" /></w:rPr>"#
336 );
337 }
338
339 #[test]
340 fn test_ptab() {
341 let c = RunProperty::new().ptab(PositionalTab::new(
342 PositionalTabAlignmentType::Left,
343 PositionalTabRelativeTo::Margin,
344 TabLeaderType::None,
345 ));
346 let b = c.build();
347 assert_eq!(
348 str::from_utf8(&b).unwrap(),
349 r#"<w:rPr><w:ptab w:alignment="left" w:relativeTo="margin" w:leader="none" /></w:rPr>"#
350 );
351 }
352
353 #[test]
354 fn test_character_shading() {
355 let c = RunProperty::new().shading(
356 Shading::new()
357 .shd_type(ShdType::Clear)
358 .fill("FFFFFF")
359 .color("auto"),
360 );
361 let b = c.build();
362 assert_eq!(
363 str::from_utf8(&b).unwrap(),
364 r#"<w:rPr><w:shd w:val="clear" w:color="auto" w:fill="FFFFFF" /></w:rPr>"#
365 );
366 }
367
368 #[test]
369 fn test_dstrike() {
370 let c = RunProperty::new().dstrike();
371 let b = c.build();
372 assert_eq!(
373 str::from_utf8(&b).unwrap(),
374 r#"<w:rPr><w:dstrike /></w:rPr>"#
375 );
376 }
377}