Skip to main content

ppt_rs/generator/layouts/
two_column.rs

1//! Two-column slide layout
2
3use super::common::{SlideXmlBuilder, generate_text_props, ShapePosition, TextContent};
4use crate::generator::slide_content::SlideContent;
5
6/// Two-column slide layout generator
7pub struct TwoColumnLayout;
8
9impl TwoColumnLayout {
10    /// Generate two-column slide XML
11    /// Bullets are automatically split between left and right columns
12    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        // Determine which bullets to use
38        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            // Left column
45            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            // Right column
77            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}