ppt_rs/generator/text/
paragraph.rs1use super::run::Run;
4use super::TextAlign;
5use crate::core::ToXml;
6
7#[derive(Clone, Debug)]
9pub struct Paragraph {
10 pub runs: Vec<Run>,
11 pub align: TextAlign,
12 pub level: u32,
13 pub bullet: bool,
14 pub spacing_before: Option<u32>,
15 pub spacing_after: Option<u32>,
16 pub line_spacing: Option<u32>,
17}
18
19impl Paragraph {
20 pub fn new() -> Self {
22 Paragraph {
23 runs: Vec::new(),
24 align: TextAlign::Left,
25 level: 0,
26 bullet: false,
27 spacing_before: None,
28 spacing_after: None,
29 line_spacing: None,
30 }
31 }
32
33 pub fn with_text(text: &str) -> Self {
35 let mut p = Self::new();
36 p.runs.push(Run::new(text));
37 p
38 }
39
40 pub fn add_run(mut self, run: Run) -> Self {
42 self.runs.push(run);
43 self
44 }
45
46 pub fn add_text(mut self, text: &str) -> Self {
48 self.runs.push(Run::new(text));
49 self
50 }
51
52 pub fn align(mut self, align: TextAlign) -> Self {
54 self.align = align;
55 self
56 }
57
58 pub fn bullet(mut self) -> Self {
60 self.bullet = true;
61 self
62 }
63
64 pub fn level(mut self, level: u32) -> Self {
66 self.level = level.min(8);
67 self
68 }
69
70 pub fn spacing_before(mut self, points: u32) -> Self {
72 self.spacing_before = Some(points * 100);
73 self
74 }
75
76 pub fn spacing_after(mut self, points: u32) -> Self {
78 self.spacing_after = Some(points * 100);
79 self
80 }
81
82 pub fn to_xml(&self) -> String {
84 let mut xml = String::from("<a:p>");
85
86 let mut ppr = format!(r#"<a:pPr algn="{}" lvl="{}""#, self.align.to_xml(), self.level);
88
89 if self.spacing_before.is_some() || self.spacing_after.is_some() || self.line_spacing.is_some() {
90 ppr.push('>');
91 if let Some(before) = self.spacing_before {
92 ppr.push_str(&format!(r#"<a:spcBef><a:spcPts val="{}"/></a:spcBef>"#, before));
93 }
94 if let Some(after) = self.spacing_after {
95 ppr.push_str(&format!(r#"<a:spcAft><a:spcPts val="{}"/></a:spcAft>"#, after));
96 }
97 if self.bullet {
98 ppr.push_str("<a:buChar char=\"•\"/>");
99 }
100 ppr.push_str("</a:pPr>");
101 } else if self.bullet {
102 ppr.push_str("><a:buChar char=\"•\"/></a:pPr>");
103 } else {
104 ppr.push_str("/>");
105 }
106
107 xml.push_str(&ppr);
108
109 for run in &self.runs {
111 xml.push_str(&run.to_xml());
112 }
113
114 xml.push_str("</a:p>");
115 xml
116 }
117}
118
119impl ToXml for Paragraph {
120 fn to_xml(&self) -> String {
121 Paragraph::to_xml(self)
122 }
123}
124
125impl Default for Paragraph {
126 fn default() -> Self {
127 Self::new()
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn test_paragraph_to_xml() {
137 let para = Paragraph::new()
138 .add_run(Run::new("Bold text").bold())
139 .add_run(Run::new(" normal text"))
140 .align(TextAlign::Center);
141
142 let xml = para.to_xml();
143
144 assert!(xml.contains("<a:p>"));
145 assert!(xml.contains("algn=\"ctr\""));
146 assert!(xml.contains("Bold text"));
147 assert!(xml.contains("normal text"));
148 }
149
150 #[test]
151 fn test_paragraph_with_bullet() {
152 let para = Paragraph::with_text("Bullet item").bullet();
153 let xml = para.to_xml();
154
155 assert!(xml.contains("buChar"));
156 }
157}