use super::common::{SlideXmlBuilder, generate_text_props, escape_xml, ShapePosition, TextContent};
use crate::generator::slide_content::SlideContent;
use crate::generator::shapes_xml::generate_shape_xml;
use crate::generator::constants::{
TITLE_X, TITLE_Y, TITLE_WIDTH, TITLE_HEIGHT, TITLE_HEIGHT_BIG,
CONTENT_X, CONTENT_Y_START, CONTENT_Y_START_BIG,
CONTENT_WIDTH, CONTENT_HEIGHT, CONTENT_HEIGHT_BIG,
TITLE_FONT_SIZE, CONTENT_FONT_SIZE,
};
pub struct TitleContentLayout;
impl TitleContentLayout {
pub fn generate(content: &SlideContent) -> String {
let title_size = content.title_size.unwrap_or(TITLE_FONT_SIZE / 100 * 100);
let content_size = content.content_size.unwrap_or(CONTENT_FONT_SIZE / 100 * 100);
let title_props = generate_text_props(
title_size,
content.title_bold,
content.title_italic,
content.title_underline,
content.title_color.as_deref(),
);
let content_props = generate_text_props(
content_size,
content.content_bold,
content.content_italic,
content.content_underline,
content.content_color.as_deref(),
);
let mut builder = SlideXmlBuilder::new()
.start_slide_with_bg()
.start_sp_tree()
.add_title(2, ShapePosition::new(TITLE_X, TITLE_Y, TITLE_WIDTH, TITLE_HEIGHT), TextContent::new(&content.title, &title_props), "title");
if let Some(ref table) = content.table {
builder = builder.raw(&crate::generator::table::generate_table_xml(table, 3));
} else if !content.bullets.is_empty() {
builder = builder.start_content_body(3, CONTENT_X, CONTENT_Y_START, CONTENT_WIDTH, CONTENT_HEIGHT);
for bullet in &content.bullets {
builder = builder.add_bullet_with_style(&bullet.text, &content_props, bullet.level, bullet.style);
}
builder = builder.end_content_body();
} else if !content.content.is_empty() {
builder = builder.start_content_body(3, CONTENT_X, CONTENT_Y_START, CONTENT_WIDTH, CONTENT_HEIGHT);
for bullet in &content.content {
builder = builder.add_bullet_with_style(bullet, &content_props, 0, content.bullet_style);
}
builder = builder.end_content_body();
}
for (i, shape) in content.shapes.iter().enumerate() {
builder = builder.raw("\n").raw(&generate_shape_xml(shape, (i + 10) as u32));
}
let image_start_id = 20 + content.shapes.len();
for (i, image) in content.images.iter().enumerate() {
builder = builder.raw(&format!(
r#"
<p:sp>
<p:nvSpPr>
<p:cNvPr id="{}" name="Image Placeholder: {}"/>
<p:cNvSpPr/>
<p:nvPr/>
</p:nvSpPr>
<p:spPr>
<a:xfrm>
<a:off x="{}" y="{}"/>
<a:ext cx="{}" cy="{}"/>
</a:xfrm>
<a:prstGeom prst="rect"><a:avLst/></a:prstGeom>
<a:solidFill><a:srgbClr val="E0E0E0"/></a:solidFill>
<a:ln w="12700"><a:solidFill><a:srgbClr val="808080"/></a:solidFill></a:ln>
</p:spPr>
<p:txBody>
<a:bodyPr wrap="square" rtlCol="0" anchor="ctr"/>
<a:lstStyle/>
<a:p>
<a:pPr algn="ctr"/>
<a:r>
<a:rPr lang="en-US" sz="1400"/>
<a:t>📷 {}</a:t>
</a:r>
</a:p>
</p:txBody>
</p:sp>"#,
image_start_id + i,
escape_xml(&image.filename),
image.x,
image.y,
image.width,
image.height,
escape_xml(&image.filename)
));
}
builder
.end_sp_tree()
.end_slide()
.build()
}
}
pub struct TitleBigContentLayout;
impl TitleBigContentLayout {
pub fn generate(content: &SlideContent) -> String {
let title_size = content.title_size.unwrap_or(TITLE_FONT_SIZE / 100 * 100);
let content_size = content.content_size.unwrap_or(CONTENT_FONT_SIZE / 100 * 100);
let title_props = generate_text_props(
title_size,
content.title_bold,
content.title_italic,
content.title_underline,
content.title_color.as_deref(),
);
let content_props = generate_text_props(
content_size,
content.content_bold,
content.content_italic,
content.content_underline,
content.content_color.as_deref(),
);
let mut builder = SlideXmlBuilder::new()
.start_slide_with_bg()
.start_sp_tree()
.add_title(2, ShapePosition::new(TITLE_X, TITLE_Y, TITLE_WIDTH, TITLE_HEIGHT_BIG), TextContent::new(&content.title, &title_props), "title");
if !content.bullets.is_empty() {
builder = builder.start_content_body(3, CONTENT_X, CONTENT_Y_START_BIG, CONTENT_WIDTH, CONTENT_HEIGHT_BIG);
for bullet in &content.bullets {
builder = builder.add_bullet_with_style(&bullet.text, &content_props, bullet.level, bullet.style);
}
builder = builder.end_content_body();
} else if !content.content.is_empty() {
builder = builder.start_content_body(3, CONTENT_X, CONTENT_Y_START_BIG, CONTENT_WIDTH, CONTENT_HEIGHT_BIG);
for bullet in &content.content {
builder = builder.add_bullet_with_style(bullet, &content_props, 0, content.bullet_style);
}
builder = builder.end_content_body();
}
builder
.end_sp_tree()
.end_slide()
.build()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_title_content_layout() {
let content = SlideContent::new("Test")
.add_bullet("Point 1")
.add_bullet("Point 2");
let xml = TitleContentLayout::generate(&content);
assert!(xml.contains("Test"));
assert!(xml.contains("Point 1"));
assert!(xml.contains("Point 2"));
}
#[test]
fn test_title_big_content_layout() {
let content = SlideContent::new("Big Content")
.add_bullet("Item");
let xml = TitleBigContentLayout::generate(&content);
assert!(xml.contains("Big Content"));
assert!(xml.contains("5668800")); }
}