Skip to main content

docx_rs/documents/elements/
paragraph.rs

1use serde::ser::{SerializeStruct, Serializer};
2use serde::Serialize;
3use std::io::Write;
4
5use super::*;
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 Paragraph {
13    pub id: String,
14    pub children: Vec<ParagraphChild>,
15    pub property: ParagraphProperty,
16    pub has_numbering: bool,
17}
18
19impl Default for Paragraph {
20    fn default() -> Self {
21        Self {
22            id: crate::generate_para_id(),
23            children: Vec::new(),
24            property: ParagraphProperty::new(),
25            has_numbering: false,
26        }
27    }
28}
29
30#[derive(Debug, Clone, PartialEq)]
31pub enum ParagraphChild {
32    Run(Box<Run>),
33    Insert(Insert),
34    Delete(Delete),
35    MoveFrom(MoveFrom),
36    MoveTo(MoveTo),
37    BookmarkStart(BookmarkStart),
38    Hyperlink(Hyperlink),
39    BookmarkEnd(BookmarkEnd),
40    CommentStart(Box<CommentRangeStart>),
41    CommentEnd(CommentRangeEnd),
42    StructuredDataTag(Box<StructuredDataTag>),
43    PageNum(Box<PageNum>),
44    NumPages(Box<NumPages>),
45}
46
47impl BuildXML for ParagraphChild {
48    fn build_to<W: Write>(
49        &self,
50        stream: crate::xml::writer::EventWriter<W>,
51    ) -> crate::xml::writer::Result<crate::xml::writer::EventWriter<W>> {
52        match self {
53            ParagraphChild::Run(v) => v.build_to(stream),
54            ParagraphChild::Insert(v) => v.build_to(stream),
55            ParagraphChild::Delete(v) => v.build_to(stream),
56            ParagraphChild::MoveFrom(v) => v.build_to(stream),
57            ParagraphChild::MoveTo(v) => v.build_to(stream),
58            ParagraphChild::Hyperlink(v) => v.build_to(stream),
59            ParagraphChild::BookmarkStart(v) => v.build_to(stream),
60            ParagraphChild::BookmarkEnd(v) => v.build_to(stream),
61            ParagraphChild::CommentStart(v) => v.build_to(stream),
62            ParagraphChild::CommentEnd(v) => v.build_to(stream),
63            ParagraphChild::StructuredDataTag(v) => v.build_to(stream),
64            ParagraphChild::PageNum(v) => v.build_to(stream),
65            ParagraphChild::NumPages(v) => v.build_to(stream),
66        }
67    }
68}
69
70impl Serialize for ParagraphChild {
71    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
72    where
73        S: Serializer,
74    {
75        match *self {
76            ParagraphChild::Run(ref r) => {
77                let mut t = serializer.serialize_struct("Run", 2)?;
78                t.serialize_field("type", "run")?;
79                t.serialize_field("data", r)?;
80                t.end()
81            }
82            ParagraphChild::Insert(ref r) => {
83                let mut t = serializer.serialize_struct("Insert", 2)?;
84                t.serialize_field("type", "insert")?;
85                t.serialize_field("data", r)?;
86                t.end()
87            }
88            ParagraphChild::Delete(ref r) => {
89                let mut t = serializer.serialize_struct("Delete", 2)?;
90                t.serialize_field("type", "delete")?;
91                t.serialize_field("data", r)?;
92                t.end()
93            }
94            ParagraphChild::MoveFrom(ref r) => {
95                let mut t = serializer.serialize_struct("MoveFrom", 2)?;
96                t.serialize_field("type", "moveFrom")?;
97                t.serialize_field("data", r)?;
98                t.end()
99            }
100            ParagraphChild::MoveTo(ref r) => {
101                let mut t = serializer.serialize_struct("MoveTo", 2)?;
102                t.serialize_field("type", "moveTo")?;
103                t.serialize_field("data", r)?;
104                t.end()
105            }
106            ParagraphChild::Hyperlink(ref r) => {
107                let mut t = serializer.serialize_struct("hyperlink", 2)?;
108                t.serialize_field("type", "hyperlink")?;
109                t.serialize_field("data", r)?;
110                t.end()
111            }
112            ParagraphChild::BookmarkStart(ref r) => {
113                let mut t = serializer.serialize_struct("BookmarkStart", 2)?;
114                t.serialize_field("type", "bookmarkStart")?;
115                t.serialize_field("data", r)?;
116                t.end()
117            }
118            ParagraphChild::BookmarkEnd(ref r) => {
119                let mut t = serializer.serialize_struct("BookmarkEnd", 2)?;
120                t.serialize_field("type", "bookmarkEnd")?;
121                t.serialize_field("data", r)?;
122                t.end()
123            }
124            ParagraphChild::CommentStart(ref r) => {
125                let mut t = serializer.serialize_struct("CommentRangeStart", 2)?;
126                t.serialize_field("type", "commentRangeStart")?;
127                t.serialize_field("data", r)?;
128                t.end()
129            }
130            ParagraphChild::CommentEnd(ref r) => {
131                let mut t = serializer.serialize_struct("CommentRangeEnd", 2)?;
132                t.serialize_field("type", "commentRangeEnd")?;
133                t.serialize_field("data", r)?;
134                t.end()
135            }
136            ParagraphChild::StructuredDataTag(ref r) => {
137                let mut t = serializer.serialize_struct("StructuredDataTag", 2)?;
138                t.serialize_field("type", "structuredDataTag")?;
139                t.serialize_field("data", r)?;
140                t.end()
141            }
142            ParagraphChild::PageNum(ref r) => {
143                let mut t = serializer.serialize_struct("PageNum", 2)?;
144                t.serialize_field("type", "pageNum")?;
145                t.serialize_field("data", r)?;
146                t.end()
147            }
148            ParagraphChild::NumPages(ref r) => {
149                let mut t = serializer.serialize_struct("NumPages", 2)?;
150                t.serialize_field("type", "numPages")?;
151                t.serialize_field("data", r)?;
152                t.end()
153            }
154        }
155    }
156}
157
158impl Paragraph {
159    pub fn new() -> Paragraph {
160        Default::default()
161    }
162
163    pub fn id(mut self, id: impl Into<String>) -> Self {
164        self.id = id.into();
165        self
166    }
167
168    pub fn children(&self) -> &Vec<ParagraphChild> {
169        &self.children
170    }
171
172    pub fn add_run(mut self, run: Run) -> Paragraph {
173        self.children.push(ParagraphChild::Run(Box::new(run)));
174        self
175    }
176
177    pub(crate) fn unshift_run(mut self, run: Run) -> Paragraph {
178        self.children.insert(0, ParagraphChild::Run(Box::new(run)));
179        self
180    }
181
182    pub(crate) fn wrap_by_bookmark(mut self, id: usize, name: impl Into<String>) -> Paragraph {
183        self.children.insert(
184            0,
185            ParagraphChild::BookmarkStart(BookmarkStart::new(id, name)),
186        );
187        self.children
188            .push(ParagraphChild::BookmarkEnd(BookmarkEnd::new(id)));
189
190        self
191    }
192
193    pub fn add_hyperlink(mut self, link: Hyperlink) -> Self {
194        self.children.push(ParagraphChild::Hyperlink(link));
195        self
196    }
197
198    pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Self {
199        self.children
200            .push(ParagraphChild::StructuredDataTag(Box::new(t)));
201        self
202    }
203
204    pub fn add_insert(mut self, insert: Insert) -> Paragraph {
205        self.children.push(ParagraphChild::Insert(insert));
206        self
207    }
208
209    pub fn add_delete(mut self, delete: Delete) -> Paragraph {
210        self.children.push(ParagraphChild::Delete(delete));
211        self
212    }
213
214    pub fn add_move_from(mut self, move_from: MoveFrom) -> Paragraph {
215        self.children.push(ParagraphChild::MoveFrom(move_from));
216        self
217    }
218
219    pub fn add_move_to(mut self, move_to: MoveTo) -> Paragraph {
220        self.children.push(ParagraphChild::MoveTo(move_to));
221        self
222    }
223
224    pub fn add_bookmark_start(mut self, id: usize, name: impl Into<String>) -> Paragraph {
225        self.children
226            .push(ParagraphChild::BookmarkStart(BookmarkStart::new(id, name)));
227        self
228    }
229
230    pub fn add_bookmark_end(mut self, id: usize) -> Paragraph {
231        self.children
232            .push(ParagraphChild::BookmarkEnd(BookmarkEnd::new(id)));
233        self
234    }
235
236    pub fn add_comment_start(mut self, comment: Comment) -> Paragraph {
237        self.children.push(ParagraphChild::CommentStart(Box::new(
238            CommentRangeStart::new(comment),
239        )));
240        self
241    }
242
243    pub fn add_comment_end(mut self, id: usize) -> Paragraph {
244        self.children
245            .push(ParagraphChild::CommentEnd(CommentRangeEnd::new(id)));
246        self
247    }
248
249    pub fn align(mut self, alignment_type: AlignmentType) -> Paragraph {
250        self.property = self.property.align(alignment_type);
251        self
252    }
253
254    pub fn style(mut self, style_id: &str) -> Paragraph {
255        self.property = self.property.style(style_id);
256        self
257    }
258
259    pub fn snap_to_grid(mut self, v: bool) -> Self {
260        self.property = self.property.snap_to_grid(v);
261        self
262    }
263
264    pub fn keep_next(mut self, v: bool) -> Self {
265        self.property = self.property.keep_next(v);
266        self
267    }
268
269    pub fn keep_lines(mut self, v: bool) -> Self {
270        self.property = self.property.keep_lines(v);
271        self
272    }
273
274    pub fn outline_lvl(mut self, v: usize) -> Self {
275        self.property = self.property.outline_lvl(v);
276        self
277    }
278
279    pub fn page_break_before(mut self, v: bool) -> Self {
280        self.property = self.property.page_break_before(v);
281        self
282    }
283
284    pub fn widow_control(mut self, v: bool) -> Self {
285        self.property = self.property.widow_control(v);
286        self
287    }
288
289    pub fn section_property(mut self, s: SectionProperty) -> Self {
290        self.property = self.property.section_property(s);
291        self
292    }
293
294    pub fn add_tab(mut self, t: Tab) -> Self {
295        self.property = self.property.add_tab(t);
296        self
297    }
298
299    pub fn tabs(mut self, tabs: &[Tab]) -> Self {
300        for tab in tabs {
301            self.property = self.property.add_tab(tab.clone());
302        }
303        self
304    }
305
306    pub fn indent(
307        mut self,
308        left: Option<i32>,
309        special_indent: Option<SpecialIndentType>,
310        end: Option<i32>,
311        start_chars: Option<i32>,
312    ) -> Paragraph {
313        self.property = self.property.indent(left, special_indent, end, start_chars);
314        self
315    }
316
317    pub fn hanging_chars(mut self, chars: i32) -> Paragraph {
318        self.property = self.property.hanging_chars(chars);
319        self
320    }
321
322    pub fn first_line_chars(mut self, chars: i32) -> Paragraph {
323        self.property = self.property.first_line_chars(chars);
324        self
325    }
326
327    pub fn numbering(mut self, id: NumberingId, level: IndentLevel) -> Self {
328        self.property = self.property.numbering(id, level);
329        self.has_numbering = true;
330        self
331    }
332
333    pub fn size(mut self, size: usize) -> Self {
334        self.property.run_property = self.property.run_property.size(size);
335        self
336    }
337
338    pub fn color(mut self, c: impl Into<String>) -> Self {
339        self.property.run_property = self.property.run_property.color(c);
340        self
341    }
342
343    pub fn bold(mut self) -> Self {
344        self.property.run_property = self.property.run_property.bold();
345        self
346    }
347
348    pub fn italic(mut self) -> Self {
349        self.property.run_property = self.property.run_property.italic();
350        self
351    }
352
353    pub fn fonts(mut self, f: RunFonts) -> Self {
354        self.property.run_property = self.property.run_property.fonts(f);
355        self
356    }
357
358    pub fn run_property(mut self, p: RunProperty) -> Self {
359        self.property.run_property = p;
360        self
361    }
362
363    pub fn line_spacing(mut self, spacing: LineSpacing) -> Self {
364        self.property = self.property.line_spacing(spacing);
365        self
366    }
367
368    pub fn character_spacing(mut self, spacing: i32) -> Self {
369        self.property = self.property.character_spacing(spacing);
370        self
371    }
372
373    pub fn delete(mut self, author: impl Into<String>, date: impl Into<String>) -> Self {
374        self.property.run_property.del = Some(Delete::new().author(author).date(date));
375        self
376    }
377
378    pub fn insert(mut self, author: impl Into<String>, date: impl Into<String>) -> Self {
379        self.property.run_property.ins = Some(Insert::new_with_empty().author(author).date(date));
380        self
381    }
382
383    pub fn paragraph_property_change(mut self, p: ParagraphPropertyChange) -> Self {
384        self.property = self.property.paragraph_property_change(p);
385        self
386    }
387
388    // internal
389    pub(crate) fn paragraph_property(mut self, p: ParagraphProperty) -> Self {
390        self.property = p;
391        self
392    }
393
394    pub fn raw_text(&self) -> String {
395        let mut s = "".to_string();
396        // For now support only run, ins, and moveTo.
397        // MoveFrom is skipped — it contains ghost text from the original location.
398        for c in self.children.iter() {
399            match c {
400                ParagraphChild::Insert(i) => {
401                    for c in i.children.iter() {
402                        if let InsertChild::Run(r) = c {
403                            for c in r.children.iter() {
404                                match c {
405                                    RunChild::Text(t) => {
406                                        s.push_str(&t.text);
407                                    }
408                                    RunChild::Tab(_t) => {
409                                        s.push('\t');
410                                    }
411                                    RunChild::PTab(_t) => {
412                                        s.push('\t');
413                                    }
414                                    RunChild::Break(_b) => {
415                                        s.push('\n');
416                                    }
417                                    RunChild::CarriageReturn(_cr) => {
418                                        s.push('\n');
419                                    }
420                                    _ => {}
421                                }
422                            }
423                        }
424                    }
425                }
426                ParagraphChild::Run(run) => {
427                    for c in run.children.iter() {
428                        match c {
429                            RunChild::Text(t) => {
430                                s.push_str(&t.text);
431                            }
432                            RunChild::Tab(_t) => {
433                                s.push('\t');
434                            }
435                            RunChild::PTab(_t) => {
436                                s.push('\t');
437                            }
438                            RunChild::Break(_b) => {
439                                s.push('\n');
440                            }
441                            RunChild::CarriageReturn(_cr) => {
442                                s.push('\n');
443                            }
444                            _ => {}
445                        }
446                    }
447                }
448                ParagraphChild::MoveTo(mt) => {
449                    for c in mt.children.iter() {
450                        if let MoveToChild::Run(r) = c {
451                            for c in r.children.iter() {
452                                if let RunChild::Text(t) = c {
453                                    s.push_str(&t.text);
454                                }
455                            }
456                        }
457                    }
458                }
459                ParagraphChild::MoveFrom(_) => {
460                    // Skip — ghost text from original location
461                }
462                _ => {}
463            }
464        }
465        s
466    }
467
468    pub fn add_page_num(mut self, p: PageNum) -> Self {
469        self.children.push(ParagraphChild::PageNum(Box::new(p)));
470        self
471    }
472
473    pub fn add_num_pages(mut self, p: NumPages) -> Self {
474        self.children.push(ParagraphChild::NumPages(Box::new(p)));
475        self
476    }
477
478    // frameProperty
479    pub fn wrap(mut self, wrap: impl Into<String>) -> Self {
480        self.property.frame_property = Some(FrameProperty {
481            wrap: Some(wrap.into()),
482            ..self.property.frame_property.unwrap_or_default()
483        });
484        self
485    }
486
487    pub fn v_anchor(mut self, anchor: impl Into<String>) -> Self {
488        self.property.frame_property = Some(FrameProperty {
489            v_anchor: Some(anchor.into()),
490            ..self.property.frame_property.unwrap_or_default()
491        });
492        self
493    }
494
495    pub fn h_anchor(mut self, anchor: impl Into<String>) -> Self {
496        self.property.frame_property = Some(FrameProperty {
497            h_anchor: Some(anchor.into()),
498            ..self.property.frame_property.unwrap_or_default()
499        });
500        self
501    }
502
503    pub fn h_rule(mut self, r: impl Into<String>) -> Self {
504        self.property.frame_property = Some(FrameProperty {
505            h_rule: Some(r.into()),
506            ..self.property.frame_property.unwrap_or_default()
507        });
508        self
509    }
510
511    pub fn x_align(mut self, align: impl Into<String>) -> Self {
512        self.property.frame_property = Some(FrameProperty {
513            x_align: Some(align.into()),
514            ..self.property.frame_property.unwrap_or_default()
515        });
516        self
517    }
518
519    pub fn y_align(mut self, align: impl Into<String>) -> Self {
520        self.property.frame_property = Some(FrameProperty {
521            y_align: Some(align.into()),
522            ..self.property.frame_property.unwrap_or_default()
523        });
524        self
525    }
526
527    pub fn h_space(mut self, x: i32) -> Self {
528        self.property.frame_property = Some(FrameProperty {
529            h_space: Some(x),
530            ..self.property.frame_property.unwrap_or_default()
531        });
532        self
533    }
534
535    pub fn v_space(mut self, x: i32) -> Self {
536        self.property.frame_property = Some(FrameProperty {
537            v_space: Some(x),
538            ..self.property.frame_property.unwrap_or_default()
539        });
540        self
541    }
542
543    pub fn frame_x(mut self, x: i32) -> Self {
544        self.property.frame_property = Some(FrameProperty {
545            x: Some(x),
546            ..self.property.frame_property.unwrap_or_default()
547        });
548        self
549    }
550
551    pub fn frame_y(mut self, y: i32) -> Self {
552        self.property.frame_property = Some(FrameProperty {
553            y: Some(y),
554            ..self.property.frame_property.unwrap_or_default()
555        });
556        self
557    }
558
559    pub fn frame_width(mut self, n: u32) -> Self {
560        self.property.frame_property = Some(FrameProperty {
561            w: Some(n),
562            ..self.property.frame_property.unwrap_or_default()
563        });
564        self
565    }
566
567    pub fn frame_height(mut self, n: u32) -> Self {
568        self.property.frame_property = Some(FrameProperty {
569            h: Some(n),
570            ..self.property.frame_property.unwrap_or_default()
571        });
572        self
573    }
574}
575
576impl BuildXML for Paragraph {
577    fn build_to<W: Write>(
578        &self,
579        stream: crate::xml::writer::EventWriter<W>,
580    ) -> crate::xml::writer::Result<crate::xml::writer::EventWriter<W>> {
581        XMLBuilder::from(stream)
582            .open_paragraph(&self.id)?
583            .add_child(&self.property)?
584            .add_children(&self.children)?
585            .close()?
586            .into_inner()
587    }
588}
589
590#[cfg(test)]
591mod tests {
592
593    use super::*;
594    #[cfg(test)]
595    use pretty_assertions::assert_eq;
596    use std::str;
597
598    #[test]
599    fn test_paragraph() {
600        let b = Paragraph::new()
601            .add_run(Run::new().add_text("Hello"))
602            .build();
603        assert_eq!(
604            str::from_utf8(&b).unwrap(),
605            r#"<w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:p>"#
606        );
607    }
608
609    #[test]
610    fn test_bookmark() {
611        let b = Paragraph::new()
612            .add_bookmark_start(0, "article")
613            .add_run(Run::new().add_text("Hello"))
614            .add_bookmark_end(0)
615            .build();
616        assert_eq!(
617            str::from_utf8(&b).unwrap(),
618            r#"<w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:bookmarkStart w:id="0" w:name="article" /><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r><w:bookmarkEnd w:id="0" /></w:p>"#
619        );
620    }
621
622    #[test]
623    fn test_comment() {
624        let b = Paragraph::new()
625            .add_comment_start(Comment::new(1))
626            .add_run(Run::new().add_text("Hello"))
627            .add_comment_end(1)
628            .build();
629        assert_eq!(
630            str::from_utf8(&b).unwrap(),
631            r#"<w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:commentRangeStart w:id="1" /><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r><w:r><w:rPr /></w:r><w:commentRangeEnd w:id="1" /><w:r><w:commentReference w:id="1" /></w:r></w:p>"#
632        );
633    }
634
635    #[test]
636    fn test_numbering() {
637        let b = Paragraph::new()
638            .add_run(Run::new().add_text("Hello"))
639            .numbering(NumberingId::new(0), IndentLevel::new(1))
640            .build();
641        assert_eq!(
642            str::from_utf8(&b).unwrap(),
643            r#"<w:p w14:paraId="12345678"><w:pPr><w:rPr /><w:numPr><w:numId w:val="0" /><w:ilvl w:val="1" /></w:numPr></w:pPr><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:p>"#
644        );
645    }
646
647    #[test]
648    fn test_line_spacing_and_character_spacing() {
649        let spacing = LineSpacing::new()
650            .line_rule(LineSpacingType::Auto)
651            .before(20)
652            .after(30)
653            .line(200);
654        let b = Paragraph::new()
655            .line_spacing(spacing)
656            .add_run(Run::new().add_text("Hello"))
657            .build();
658        assert_eq!(
659            str::from_utf8(&b).unwrap(),
660            r#"<w:p w14:paraId="12345678"><w:pPr><w:rPr /><w:spacing w:before="20" w:after="30" w:line="200" w:lineRule="auto" /></w:pPr><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:p>"#
661        );
662    }
663
664    #[test]
665    fn test_paragraph_run_json() {
666        let run = Run::new().add_text("Hello");
667        let p = Paragraph::new().add_run(run);
668        assert_eq!(
669            serde_json::to_string(&p).unwrap(),
670            r#"{"id":"12345678","children":[{"type":"run","data":{"runProperty":{},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"property":{"runProperty":{},"tabs":[]},"hasNumbering":false}"#,
671        );
672    }
673
674    #[test]
675    fn test_paragraph_insert_json() {
676        let run = Run::new().add_text("Hello");
677        let ins = Insert::new(run);
678        let p = Paragraph::new().add_insert(ins);
679        assert_eq!(
680            serde_json::to_string(&p).unwrap(),
681            r#"{"id":"12345678","children":[{"type":"insert","data":{"children":[{"type":"run","data":{"runProperty":{},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"author":"unnamed","date":"1970-01-01T00:00:00Z"}}],"property":{"runProperty":{},"tabs":[]},"hasNumbering":false}"#
682        );
683    }
684
685    #[test]
686    fn test_raw_text() {
687        let b = Paragraph::new()
688            .add_run(Run::new().add_text("Hello"))
689            .add_insert(Insert::new(Run::new().add_text("World")))
690            .add_delete(Delete::new().add_run(Run::new().add_delete_text("!!!!!")))
691            .raw_text();
692        assert_eq!(b, "HelloWorld".to_owned());
693    }
694
695    #[test]
696    fn test_raw_text_move_from_to_dedup() {
697        // MoveFrom text should be excluded (ghost text from original location),
698        // MoveTo text should be included (destination, live text).
699        let b = Paragraph::new()
700            .add_run(Run::new().add_text("Hello "))
701            .add_move_from(MoveFrom::new().add_run(Run::new().add_text("world")))
702            .add_move_to(MoveTo::new(Run::new().add_text("world")))
703            .raw_text();
704        assert_eq!(b, "Hello world".to_owned());
705    }
706
707    #[test]
708    fn test_raw_text_mixed_content_with_move() {
709        let b = Paragraph::new()
710            .add_run(Run::new().add_text("Start "))
711            .add_move_from(MoveFrom::new().add_run(Run::new().add_text("moved")))
712            .add_move_to(MoveTo::new(Run::new().add_text("moved")))
713            .add_run(Run::new().add_text(" end"))
714            .raw_text();
715        assert_eq!(b, "Start moved end".to_owned());
716    }
717}