ppt_rs/generator/layouts/
two_column.rs1use super::common::{SlideXmlBuilder, generate_text_props, ShapePosition, TextContent};
4use crate::generator::slide_content::SlideContent;
5
6pub struct TwoColumnLayout;
8
9impl TwoColumnLayout {
10 pub fn generate(content: &SlideContent) -> String {
13 let title_size = content.title_size.unwrap_or(44) * 100;
14 let content_size = content.content_size.unwrap_or(24) * 100;
15
16 let title_props = generate_text_props(
17 title_size,
18 content.title_bold,
19 content.title_italic,
20 content.title_underline,
21 content.title_color.as_deref(),
22 );
23
24 let content_props = generate_text_props(
25 content_size,
26 content.content_bold,
27 content.content_italic,
28 content.content_underline,
29 content.content_color.as_deref(),
30 );
31
32 let mut builder = SlideXmlBuilder::new()
33 .start_slide_with_bg()
34 .start_sp_tree()
35 .add_title(2, ShapePosition::new(457200, 274638, 8230200, 914400), TextContent::new(&content.title, &title_props), "title");
36
37 let use_styled_bullets = !content.bullets.is_empty();
39 let bullet_count = if use_styled_bullets { content.bullets.len() } else { content.content.len() };
40
41 if bullet_count > 0 {
42 let mid = bullet_count.div_ceil(2);
43
44 builder = builder.raw(r#"
46<p:sp>
47<p:nvSpPr>
48<p:cNvPr id="3" name="Left Content"/>
49<p:cNvSpPr><a:spLocks noGrp="1"/></p:cNvSpPr>
50<p:nvPr><p:ph type="body" idx="1"/></p:nvPr>
51</p:nvSpPr>
52<p:spPr>
53<a:xfrm>
54<a:off x="457200" y="1189200"/>
55<a:ext cx="4115100" cy="5668800"/>
56</a:xfrm>
57<a:prstGeom prst="rect"><a:avLst/></a:prstGeom>
58<a:noFill/>
59</p:spPr>
60<p:txBody>
61<a:bodyPr/>
62<a:lstStyle/>
63"#);
64
65 if use_styled_bullets {
66 for bullet in &content.bullets[..mid] {
67 builder = builder.add_bullet_with_style(&bullet.text, &content_props, bullet.level, bullet.style);
68 }
69 } else {
70 for bullet in &content.content[..mid] {
71 builder = builder.add_bullet_with_style(bullet, &content_props, 0, content.bullet_style);
72 }
73 }
74 builder = builder.raw("</p:txBody>\n</p:sp>\n");
75
76 if mid < bullet_count {
78 builder = builder.raw(r#"
79<p:sp>
80<p:nvSpPr>
81<p:cNvPr id="4" name="Right Content"/>
82<p:cNvSpPr><a:spLocks noGrp="1"/></p:cNvSpPr>
83<p:nvPr><p:ph type="body" idx="2"/></p:nvPr>
84</p:nvSpPr>
85<p:spPr>
86<a:xfrm>
87<a:off x="4572300" y="1189200"/>
88<a:ext cx="4115100" cy="5668800"/>
89</a:xfrm>
90<a:prstGeom prst="rect"><a:avLst/></a:prstGeom>
91<a:noFill/>
92</p:spPr>
93<p:txBody>
94<a:bodyPr/>
95<a:lstStyle/>
96"#);
97
98 if use_styled_bullets {
99 for bullet in &content.bullets[mid..] {
100 builder = builder.add_bullet_with_style(&bullet.text, &content_props, bullet.level, bullet.style);
101 }
102 } else {
103 for bullet in &content.content[mid..] {
104 builder = builder.add_bullet_with_style(bullet, &content_props, 0, content.bullet_style);
105 }
106 }
107 builder = builder.raw("</p:txBody>\n</p:sp>\n");
108 }
109 }
110
111 builder
112 .end_sp_tree()
113 .end_slide()
114 .build()
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn test_two_column_layout() {
124 let content = SlideContent::new("Comparison")
125 .add_bullet("Left 1")
126 .add_bullet("Left 2")
127 .add_bullet("Right 1")
128 .add_bullet("Right 2");
129 let xml = TwoColumnLayout::generate(&content);
130
131 assert!(xml.contains("Left Content"));
132 assert!(xml.contains("Right Content"));
133 assert!(xml.contains("Left 1"));
134 assert!(xml.contains("Right 1"));
135 }
136}