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