Skip to main content

docx_rs/documents/elements/
run.rs

1use super::*;
2use serde::ser::{SerializeStruct, Serializer};
3use serde::Serialize;
4use std::io::Write;
5
6use crate::documents::BuildXML;
7use crate::types::*;
8use crate::xml_builder::*;
9
10#[derive(Serialize, Debug, Clone, PartialEq)]
11#[serde(rename_all = "camelCase")]
12pub struct Run {
13    pub run_property: RunProperty,
14    pub children: Vec<RunChild>,
15}
16
17impl Default for Run {
18    fn default() -> Self {
19        let run_property = RunProperty::new();
20        Self {
21            run_property,
22            children: vec![],
23        }
24    }
25}
26
27#[derive(Debug, Clone, PartialEq)]
28pub enum RunChild {
29    Text(Text),
30    Sym(Sym),
31    DeleteText(DeleteText),
32    Tab(Tab),
33    PTab(PositionalTab),
34    Break(Break),
35    CarriageReturn(CarriageReturn),
36    Drawing(Box<Drawing>),
37    Shape(Box<Shape>),
38    CommentStart(Box<CommentRangeStart>),
39    CommentEnd(CommentRangeEnd),
40    FieldChar(FieldChar),
41    InstrText(Box<InstrText>),
42    DeleteInstrText(Box<DeleteInstrText>),
43    // For reader
44    InstrTextString(String),
45    FootnoteReference(FootnoteReference),
46    Shading(Shading),
47}
48
49impl Serialize for RunChild {
50    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
51    where
52        S: Serializer,
53    {
54        match *self {
55            RunChild::Text(ref s) => {
56                let mut t = serializer.serialize_struct("Text", 2)?;
57                t.serialize_field("type", "text")?;
58                t.serialize_field("data", s)?;
59                t.end()
60            }
61            RunChild::Sym(ref s) => {
62                let mut t = serializer.serialize_struct("Sym", 2)?;
63                t.serialize_field("type", "sym")?;
64                t.serialize_field("data", s)?;
65                t.end()
66            }
67            RunChild::DeleteText(ref s) => {
68                let mut t = serializer.serialize_struct("DeleteText", 2)?;
69                t.serialize_field("type", "deleteText")?;
70                t.serialize_field("data", s)?;
71                t.end()
72            }
73            RunChild::Tab(_) => {
74                let mut t = serializer.serialize_struct("Tab", 1)?;
75                t.serialize_field("type", "tab")?;
76                t.end()
77            }
78            RunChild::PTab(ref s) => {
79                let mut t = serializer.serialize_struct("PTab", 1)?;
80                t.serialize_field("type", "ptab")?;
81                t.serialize_field("data", s)?;
82                t.end()
83            }
84            RunChild::Break(ref s) => {
85                let mut t = serializer.serialize_struct("Break", 2)?;
86                t.serialize_field("type", "break")?;
87                t.serialize_field("data", s)?;
88                t.end()
89            }
90            RunChild::CarriageReturn(_) => {
91                let mut t = serializer.serialize_struct("CarriageReturn", 1)?;
92                t.serialize_field("type", "carriageReturn")?;
93                t.end()
94            }
95            RunChild::Drawing(ref s) => {
96                let mut t = serializer.serialize_struct("Drawing", 2)?;
97                t.serialize_field("type", "drawing")?;
98                t.serialize_field("data", s)?;
99                t.end()
100            }
101            RunChild::Shape(ref s) => {
102                let mut t = serializer.serialize_struct("Shape", 2)?;
103                t.serialize_field("type", "shape")?;
104                t.serialize_field("data", s)?;
105                t.end()
106            }
107            RunChild::CommentStart(ref r) => {
108                let mut t = serializer.serialize_struct("CommentRangeStart", 2)?;
109                t.serialize_field("type", "commentRangeStart")?;
110                t.serialize_field("data", r)?;
111                t.end()
112            }
113            RunChild::CommentEnd(ref r) => {
114                let mut t = serializer.serialize_struct("CommentRangeEnd", 2)?;
115                t.serialize_field("type", "commentRangeEnd")?;
116                t.serialize_field("data", r)?;
117                t.end()
118            }
119            RunChild::FieldChar(ref f) => {
120                let mut t = serializer.serialize_struct("FieldChar", 2)?;
121                t.serialize_field("type", "fieldChar")?;
122                t.serialize_field("data", f)?;
123                t.end()
124            }
125            RunChild::InstrText(ref i) => {
126                let mut t = serializer.serialize_struct("InstrText", 2)?;
127                t.serialize_field("type", "instrText")?;
128                t.serialize_field("data", i)?;
129                t.end()
130            }
131            RunChild::DeleteInstrText(ref i) => {
132                let mut t = serializer.serialize_struct("DeleteInstrText", 2)?;
133                t.serialize_field("type", "deleteInstrText")?;
134                t.serialize_field("data", i)?;
135                t.end()
136            }
137            RunChild::InstrTextString(ref i) => {
138                let mut t = serializer.serialize_struct("InstrTextString", 2)?;
139                t.serialize_field("type", "instrTextString")?;
140                t.serialize_field("data", i)?;
141                t.end()
142            }
143            RunChild::FootnoteReference(ref f) => {
144                let mut t = serializer.serialize_struct("FootnoteReference", 2)?;
145                t.serialize_field("type", "footnoteReference")?;
146                t.serialize_field("data", f)?;
147                t.end()
148            }
149            RunChild::Shading(ref f) => {
150                let mut t = serializer.serialize_struct("Shading", 2)?;
151                t.serialize_field("type", "shading")?;
152                t.serialize_field("data", f)?;
153                t.end()
154            }
155        }
156    }
157}
158
159impl Run {
160    pub fn new() -> Run {
161        Run {
162            ..Default::default()
163        }
164    }
165
166    pub fn add_text(mut self, text: impl Into<String>) -> Run {
167        self.children
168            .push(RunChild::Text(Text::new(text.into().replace('\n', ""))));
169        self
170    }
171
172    pub(crate) fn add_text_without_escape(mut self, text: impl Into<String>) -> Run {
173        self.children.push(RunChild::Text(Text::without_escape(
174            text.into().replace('\n', ""),
175        )));
176        self
177    }
178
179    pub fn add_delete_text(mut self, text: impl Into<String>) -> Run {
180        self.children.push(RunChild::DeleteText(DeleteText::new(
181            text.into().replace('\n', ""),
182        )));
183        self
184    }
185
186    pub(crate) fn add_delete_text_without_escape(mut self, text: impl Into<String>) -> Run {
187        self.children
188            .push(RunChild::DeleteText(DeleteText::without_escape(
189                text.into().replace('\n', ""),
190            )));
191        self
192    }
193
194    pub fn add_field_char(mut self, t: crate::types::FieldCharType, dirty: bool) -> Run {
195        let mut f = FieldChar::new(t);
196        if dirty {
197            f = f.dirty();
198        };
199        self.children.push(RunChild::FieldChar(f));
200        self
201    }
202
203    pub fn add_tc(mut self, tc: InstrTC) -> Run {
204        self = self.add_field_char(crate::types::FieldCharType::Begin, false);
205        self = self.add_instr_text(InstrText::TC(tc));
206        self = self.add_field_char(crate::types::FieldCharType::End, false);
207        self
208    }
209
210    pub fn add_instr_text(mut self, i: InstrText) -> Run {
211        self.children.push(RunChild::InstrText(Box::new(i)));
212        self
213    }
214
215    pub fn add_delete_instr_text(mut self, i: DeleteInstrText) -> Run {
216        self.children.push(RunChild::DeleteInstrText(Box::new(i)));
217        self
218    }
219
220    pub fn add_tab(mut self) -> Run {
221        self.children.push(RunChild::Tab(Tab::new()));
222        self
223    }
224
225    pub fn add_ptab(mut self, ptab: PositionalTab) -> Run {
226        self.children.push(RunChild::PTab(ptab));
227        self
228    }
229
230    pub fn add_image(mut self, pic: Pic) -> Run {
231        self.children
232            .push(RunChild::Drawing(Box::new(Drawing::new().pic(pic))));
233        self
234    }
235
236    pub(crate) fn add_drawing(mut self, d: Drawing) -> Run {
237        self.children.push(RunChild::Drawing(Box::new(d)));
238        self
239    }
240
241    // For now reader only
242    //    pub(crate) fn add_shape(mut self, d: Shape) -> Run {
243    //        self.children.push(RunChild::Shape(Box::new(d)));
244    //        self
245    //    }
246
247    pub fn add_break(mut self, break_type: BreakType) -> Run {
248        self.children.push(RunChild::Break(Break::new(break_type)));
249        self
250    }
251
252    pub fn add_carriage_return(mut self) -> Run {
253        self.children
254            .push(RunChild::CarriageReturn(CarriageReturn::new()));
255        self
256    }
257
258    pub fn add_sym(mut self, sym: Sym) -> Run {
259        self.children.push(RunChild::Sym(sym));
260        self
261    }
262
263    pub fn style(mut self, style_id: &str) -> Self {
264        self.run_property = self.run_property.style(style_id);
265        self
266    }
267
268    pub fn size(mut self, size: usize) -> Run {
269        self.run_property = self.run_property.size(size);
270        self
271    }
272
273    pub fn character_spacing(mut self, v: i32) -> Run {
274        self.run_property = self.run_property.spacing(v);
275        self
276    }
277
278    pub fn stretch(mut self, v: i32) -> Run {
279        self.run_property = self.run_property.stretch(v);
280        self
281    }
282
283    pub fn color(mut self, color: impl Into<String>) -> Run {
284        self.run_property = self.run_property.color(color);
285        self
286    }
287
288    /// Sets the theme color reference (`w:themeColor`) on the run color.
289    pub fn theme_color(mut self, theme_color: crate::types::ThemeColor) -> Run {
290        self.run_property = self.run_property.theme_color(theme_color);
291        self
292    }
293
294    /// Sets the theme shade modifier (`w:themeShade`) on the run color.
295    pub fn theme_shade(mut self, theme_shade: impl Into<String>) -> Run {
296        self.run_property = self.run_property.theme_shade(theme_shade);
297        self
298    }
299
300    /// Sets the theme tint modifier (`w:themeTint`) on the run color.
301    pub fn theme_tint(mut self, theme_tint: impl Into<String>) -> Run {
302        self.run_property = self.run_property.theme_tint(theme_tint);
303        self
304    }
305
306    pub fn highlight(mut self, color: impl Into<String>) -> Run {
307        self.run_property = self.run_property.highlight(color);
308        self
309    }
310
311    pub fn bold(mut self) -> Run {
312        self.run_property = self.run_property.bold();
313        self
314    }
315
316    pub fn disable_bold(mut self) -> Run {
317        self.run_property = self.run_property.disable_bold();
318        self
319    }
320
321    pub fn italic(mut self) -> Run {
322        self.run_property = self.run_property.italic();
323        self
324    }
325
326    pub fn strike(mut self) -> Run {
327        self.run_property = self.run_property.strike();
328        self
329    }
330
331    pub fn dstrike(mut self) -> Run {
332        self.run_property = self.run_property.dstrike();
333        self
334    }
335
336    pub fn text_border(mut self, b: TextBorder) -> Run {
337        self.run_property = self.run_property.text_border(b);
338        self
339    }
340
341    pub fn disable_italic(mut self) -> Run {
342        self.run_property = self.run_property.disable_italic();
343        self
344    }
345
346    pub fn underline(mut self, line_type: impl Into<String>) -> Run {
347        self.run_property = self.run_property.underline(line_type);
348        self
349    }
350
351    pub fn vanish(mut self) -> Run {
352        self.run_property = self.run_property.vanish();
353        self
354    }
355
356    pub fn fonts(mut self, f: RunFonts) -> Run {
357        self.run_property = self.run_property.fonts(f);
358        self
359    }
360
361    pub(crate) fn set_property(mut self, p: RunProperty) -> Run {
362        self.run_property = p;
363        self
364    }
365
366    pub fn add_footnote_reference(mut self, footnote: Footnote) -> Run {
367        self.run_property = RunProperty::new().style("FootnoteReference");
368        self.children
369            .push(RunChild::FootnoteReference(footnote.into()));
370        self
371    }
372
373    pub fn shading(mut self, shading: Shading) -> Run {
374        self.run_property = self.run_property.shading(shading);
375        self
376    }
377}
378
379impl BuildXML for RunChild {
380    fn build_to<W: Write>(
381        &self,
382        stream: crate::xml::writer::EventWriter<W>,
383    ) -> crate::xml::writer::Result<crate::xml::writer::EventWriter<W>> {
384        match self {
385            RunChild::Text(t) => t.build_to(stream),
386            RunChild::Sym(t) => t.build_to(stream),
387            RunChild::DeleteText(t) => t.build_to(stream),
388            RunChild::Tab(t) => t.build_to(stream),
389            RunChild::PTab(t) => t.build_to(stream),
390            RunChild::Break(t) => t.build_to(stream),
391            RunChild::CarriageReturn(t) => t.build_to(stream),
392            RunChild::Drawing(t) => t.build_to(stream),
393            RunChild::Shape(_t) => {
394                todo!("Support shape writer.")
395            }
396            RunChild::CommentStart(c) => c.build_to(stream),
397            RunChild::CommentEnd(c) => c.build_to(stream),
398            RunChild::FieldChar(c) => c.build_to(stream),
399            RunChild::InstrText(c) => c.build_to(stream),
400            RunChild::DeleteInstrText(c) => c.build_to(stream),
401            RunChild::InstrTextString(_) => unreachable!(),
402            RunChild::FootnoteReference(c) => c.build_to(stream),
403            RunChild::Shading(s) => s.build_to(stream),
404        }
405    }
406}
407
408impl BuildXML for Run {
409    fn build_to<W: Write>(
410        &self,
411        stream: crate::xml::writer::EventWriter<W>,
412    ) -> crate::xml::writer::Result<crate::xml::writer::EventWriter<W>> {
413        XMLBuilder::from(stream)
414            .open_run()?
415            .add_child(&self.run_property)?
416            .add_children(&self.children)?
417            .close()?
418            .into_inner()
419    }
420}
421
422#[cfg(test)]
423mod tests {
424
425    use super::*;
426    #[cfg(test)]
427    use pretty_assertions::assert_eq;
428    use std::str;
429
430    #[test]
431    fn test_build() {
432        let b = Run::new().add_text("Hello").build();
433        assert_eq!(
434            str::from_utf8(&b).unwrap(),
435            r#"<w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r>"#
436        );
437    }
438
439    #[test]
440    fn test_underline() {
441        let b = Run::new().add_text("Hello").underline("single").build();
442        assert_eq!(
443            str::from_utf8(&b).unwrap(),
444            r#"<w:r><w:rPr><w:u w:val="single" /></w:rPr><w:t xml:space="preserve">Hello</w:t></w:r>"#
445        );
446    }
447
448    #[test]
449    fn test_strike() {
450        let b = Run::new().add_text("Hello").strike().build();
451        assert_eq!(
452            str::from_utf8(&b).unwrap(),
453            r#"<w:r><w:rPr><w:strike /></w:rPr><w:t xml:space="preserve">Hello</w:t></w:r>"#
454        );
455    }
456
457    #[test]
458    fn test_child_json() {
459        let c = RunChild::Text(Text::new("Hello"));
460        assert_eq!(
461            serde_json::to_string(&c).unwrap(),
462            r#"{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}"#
463        );
464    }
465
466    #[test]
467    fn test_run_json() {
468        let run = Run {
469            children: vec![
470                RunChild::Tab(Tab::new()),
471                RunChild::Text(Text::new("Hello")),
472                RunChild::Break(Break::new(BreakType::Page)),
473                RunChild::CarriageReturn(CarriageReturn::new()),
474                RunChild::DeleteText(DeleteText::new("deleted")),
475            ],
476            run_property: RunProperty {
477                sz: Some(Sz::new(30)),
478                sz_cs: Some(SzCs::new(30)),
479                color: Some(Color::new("C9211E")),
480                highlight: Some(Highlight::new("yellow")),
481                underline: Some(Underline::new("single")),
482                bold: Some(Bold::new()),
483                bold_cs: Some(BoldCs::new()),
484                italic: Some(Italic::new()),
485                italic_cs: Some(ItalicCs::new()),
486                vanish: Some(Vanish::new()),
487                character_spacing: Some(CharacterSpacing::new(100)),
488                ..RunProperty::default()
489            },
490        };
491        assert_eq!(
492            serde_json::to_string(&run).unwrap(),
493            r#"{"runProperty":{"sz":30,"szCs":30,"color":"C9211E","highlight":"yellow","underline":"single","bold":true,"boldCs":true,"italic":true,"italicCs":true,"vanish":true,"characterSpacing":100},"children":[{"type":"tab"},{"type":"text","data":{"preserveSpace":true,"text":"Hello"}},{"type":"break","data":{"breakType":"page"}},{"type":"carriageReturn"},{"type":"deleteText","data":{"text":"deleted","preserveSpace":true}}]}"#,
494        );
495    }
496
497    #[test]
498    fn test_run_footnote_reference() {
499        let c = RunChild::FootnoteReference(FootnoteReference::new(1));
500        assert_eq!(
501            serde_json::to_string(&c).unwrap(),
502            r#"{"type":"footnoteReference","data":{"id":1}}"#
503        );
504    }
505
506    #[test]
507    fn test_run_shading() {
508        let c = RunChild::Shading(Shading::new());
509        assert_eq!(
510            serde_json::to_string(&c).unwrap(),
511            r#"{"type":"shading","data":{"shdType":"clear","color":"auto","fill":"FFFFFF"}}"#
512        );
513    }
514}