ppt_rs/generator/text/
format.rs1#[derive(Clone, Debug, Default)]
5pub struct TextFormat {
6 pub bold: bool,
7 pub italic: bool,
8 pub underline: bool,
9 pub strikethrough: bool,
10 pub color: Option<String>, pub highlight: Option<String>, pub font_size: Option<u32>, pub font_family: Option<String>, pub subscript: bool,
15 pub superscript: bool,
16}
17
18impl TextFormat {
19 pub fn new() -> Self {
21 Self::default()
22 }
23
24 pub fn bold(mut self) -> Self {
26 self.bold = true;
27 self
28 }
29
30 pub fn italic(mut self) -> Self {
32 self.italic = true;
33 self
34 }
35
36 pub fn underline(mut self) -> Self {
38 self.underline = true;
39 self
40 }
41
42 pub fn strikethrough(mut self) -> Self {
44 self.strikethrough = true;
45 self
46 }
47
48 pub fn color(mut self, hex_color: &str) -> Self {
50 self.color = Some(hex_color.trim_start_matches('#').to_uppercase());
51 self
52 }
53
54 pub fn highlight(mut self, hex_color: &str) -> Self {
56 self.highlight = Some(hex_color.trim_start_matches('#').to_uppercase());
57 self
58 }
59
60 pub fn font_size(mut self, size: u32) -> Self {
62 self.font_size = Some(size);
63 self
64 }
65
66 pub fn font_family(mut self, family: &str) -> Self {
68 self.font_family = Some(family.to_string());
69 self
70 }
71
72 pub fn subscript(mut self) -> Self {
74 self.subscript = true;
75 self.superscript = false; self
77 }
78
79 pub fn superscript(mut self) -> Self {
81 self.superscript = true;
82 self.subscript = false; self
84 }
85
86 pub fn to_xml_attrs(&self) -> String {
88 let mut attrs = String::new();
89
90 if self.bold {
91 attrs.push_str(" b=\"1\"");
92 }
93
94 if self.italic {
95 attrs.push_str(" i=\"1\"");
96 }
97
98 if self.underline {
99 attrs.push_str(" u=\"sng\"");
100 }
101
102 if self.strikethrough {
103 attrs.push_str(" strike=\"sngStrike\"");
104 }
105
106 if self.subscript {
107 attrs.push_str(" baseline=\"-25000\""); } else if self.superscript {
109 attrs.push_str(" baseline=\"30000\""); }
111
112 if let Some(size) = self.font_size {
113 attrs.push_str(&format!(" sz=\"{}\"", size * 100));
114 }
115
116 attrs
117 }
118
119 pub fn to_highlight_xml(&self) -> String {
121 if let Some(ref color) = self.highlight {
122 format!(r#"<a:highlight><a:srgbClr val="{}"/></a:highlight>"#, color)
123 } else {
124 String::new()
125 }
126 }
127}
128
129#[derive(Clone, Debug)]
131pub struct FormattedText {
132 pub text: String,
133 pub format: TextFormat,
134}
135
136impl FormattedText {
137 pub fn new(text: &str) -> Self {
139 FormattedText {
140 text: text.to_string(),
141 format: TextFormat::default(),
142 }
143 }
144
145 pub fn with_format(mut self, format: TextFormat) -> Self {
147 self.format = format;
148 self
149 }
150
151 pub fn bold(mut self) -> Self {
153 self.format = self.format.bold();
154 self
155 }
156
157 pub fn italic(mut self) -> Self {
159 self.format = self.format.italic();
160 self
161 }
162
163 pub fn underline(mut self) -> Self {
165 self.format = self.format.underline();
166 self
167 }
168
169 pub fn strikethrough(mut self) -> Self {
171 self.format = self.format.strikethrough();
172 self
173 }
174
175 pub fn color(mut self, hex_color: &str) -> Self {
177 self.format = self.format.color(hex_color);
178 self
179 }
180
181 pub fn highlight(mut self, hex_color: &str) -> Self {
183 self.format = self.format.highlight(hex_color);
184 self
185 }
186
187 pub fn font_size(mut self, size: u32) -> Self {
189 self.format = self.format.font_size(size);
190 self
191 }
192
193 pub fn subscript(mut self) -> Self {
195 self.format = self.format.subscript();
196 self
197 }
198
199 pub fn superscript(mut self) -> Self {
201 self.format = self.format.superscript();
202 self
203 }
204}
205
206pub fn color_to_xml(hex_color: &str) -> String {
208 let clean_color = hex_color.trim_start_matches('#').to_uppercase();
209 format!("<a:solidFill><a:srgbClr val=\"{}\"/></a:solidFill>", clean_color)
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
217 fn test_text_format_builder() {
218 let format = TextFormat::new()
219 .bold()
220 .italic()
221 .color("FF0000")
222 .font_size(24);
223
224 assert!(format.bold);
225 assert!(format.italic);
226 assert_eq!(format.color, Some("FF0000".to_string()));
227 assert_eq!(format.font_size, Some(24));
228 }
229
230 #[test]
231 fn test_formatted_text_builder() {
232 let text = FormattedText::new("Hello")
233 .bold()
234 .italic()
235 .color("0000FF");
236
237 assert_eq!(text.text, "Hello");
238 assert!(text.format.bold);
239 assert!(text.format.italic);
240 assert_eq!(text.format.color, Some("0000FF".to_string()));
241 }
242
243 #[test]
244 fn test_format_to_xml_attrs() {
245 let format = TextFormat::new().bold().italic().font_size(24);
246 let attrs = format.to_xml_attrs();
247 assert!(attrs.contains("b=\"1\""));
248 assert!(attrs.contains("i=\"1\""));
249 assert!(attrs.contains("sz=\"2400\""));
250 }
251
252 #[test]
253 fn test_color_to_xml() {
254 let xml = color_to_xml("FF0000");
255 assert!(xml.contains("FF0000"));
256 assert!(xml.contains("srgbClr"));
257 }
258
259 #[test]
260 fn test_strikethrough() {
261 let format = TextFormat::new().strikethrough();
262 let attrs = format.to_xml_attrs();
263 assert!(attrs.contains("strike=\"sngStrike\""));
264 }
265
266 #[test]
267 fn test_highlight() {
268 let format = TextFormat::new().highlight("FFFF00");
269 let xml = format.to_highlight_xml();
270 assert!(xml.contains("highlight"));
271 assert!(xml.contains("FFFF00"));
272 }
273
274 #[test]
275 fn test_subscript_superscript() {
276 let sub = TextFormat::new().subscript();
277 let attrs = sub.to_xml_attrs();
278 assert!(attrs.contains("baseline=\"-25000\""));
279
280 let sup = TextFormat::new().superscript();
281 let attrs = sup.to_xml_attrs();
282 assert!(attrs.contains("baseline=\"30000\""));
283 }
284
285 #[test]
286 fn test_formatted_text_strikethrough() {
287 let text = FormattedText::new("Deleted")
288 .strikethrough();
289 assert!(text.format.strikethrough);
290 }
291
292 #[test]
293 fn test_formatted_text_highlight() {
294 let text = FormattedText::new("Important")
295 .highlight("FFFF00");
296 assert_eq!(text.format.highlight, Some("FFFF00".to_string()));
297 }
298
299 #[test]
300 fn test_formatted_text_subscript_superscript() {
301 let sub = FormattedText::new("2").subscript();
302 assert!(sub.format.subscript);
303
304 let sup = FormattedText::new("2").superscript();
305 assert!(sup.format.superscript);
306 }
307}