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