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