ppt-rs 0.2.17

Create, read, and update PowerPoint 2007+ (.pptx) files with rich formatting, bullet styles, themes, and templates.
Documentation
//! Additional content rendering (shapes, images, code blocks, connectors)

use crate::generator::shapes_xml::generate_shape_xml;
use crate::generator::slide_content::SlideContent;

/// Render additional content elements (shapes, images, code blocks, connectors, charts)
pub fn render_additional_content(xml: &mut String, content: &SlideContent, chart_rids: &[String]) {
    let extra_elements = content.shapes.len()
        + content.images.len()
        + content.code_blocks.len()
        + content.connectors.len()
        + content.charts.len().min(chart_rids.len());
    if extra_elements > 0 {
        xml.reserve(extra_elements * 512);
    }

    // Render shapes - use shape's fixed ID if set, otherwise auto-assign
    for (i, shape) in content.shapes.iter().enumerate() {
        xml.push('\n');
        let shape_id = shape.id.unwrap_or((i + 10) as u32);
        xml.push_str(&generate_shape_xml(shape, shape_id));
    }

    // Render images (actual picture elements, not placeholders)
    let image_start_id = 20 + content.shapes.len();
    for (i, image) in content.images.iter().enumerate() {
        xml.push('\n');
        // Use rel_id starting from 2 (rId1 is slideLayout)
        let rel_id = i + 2;
        xml.push_str(&crate::generator::images_xml::generate_image_xml(
            image,
            image_start_id + i,
            rel_id,
        ));
    }

    // Render code blocks with syntax highlighting
    let code_start_id = 30 + content.shapes.len() + content.images.len();
    for (i, code_block) in content.code_blocks.iter().enumerate() {
        xml.push('\n');
        xml.push_str(&generate_code_block(code_start_id + i, code_block));
    }

    // Render connectors
    let connector_start_id =
        50 + content.shapes.len() + content.images.len() + content.code_blocks.len();
    for (i, connector) in content.connectors.iter().enumerate() {
        xml.push('\n');
        let id = connector_start_id + i;
        xml.push_str(&crate::generator::connectors::generate_connector_xml(
            connector, id,
        ));
    }

    // Render charts
    let chart_start_id = 100
        + content.shapes.len()
        + content.images.len()
        + content.code_blocks.len()
        + content.connectors.len();
    for (i, chart) in content.charts.iter().enumerate() {
        if i < chart_rids.len() {
            xml.push('\n');
            let r_id = &chart_rids[i];
            xml.push_str(&crate::generator::charts::generate_chart_ref_xml(
                chart,
                r_id,
                chart_start_id + i,
            ));
        }
    }
}

/// Generate image placeholder XML
#[allow(dead_code)]
fn generate_image_placeholder(id: usize, image: &crate::generator::images::Image) -> String {
    let filename = &image.filename;
    let x = image.x;
    let y = image.y;
    let width = image.width;
    let height = image.height;

    format!(
        r#"<p:sp>
<p:nvSpPr>
<p:cNvPr id="{id}" name="Image Placeholder: {filename}"/>
<p:cNvSpPr/>
<p:nvPr/>
</p:nvSpPr>
<p:spPr>
<a:xfrm>
<a:off x="{x}" y="{y}"/>
<a:ext cx="{width}" cy="{height}"/>
</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>📷 {filename}</a:t>
</a:r>
</a:p>
</p:txBody>
</p:sp>"#
    )
}

/// Generate code block XML with syntax highlighting
fn generate_code_block(
    id: usize,
    code_block: &crate::generator::slide_content::CodeBlock,
) -> String {
    // Use syntax highlighting when available, otherwise use plain text
    #[cfg(feature = "syntect")]
    let highlighted_xml =
        crate::cli::syntax::generate_highlighted_code_xml(&code_block.code, &code_block.language);

    #[cfg(not(feature = "syntect"))]
    let highlighted_xml = {
        // Fallback to basic XML without syntax highlighting
        let escaped_code = code_block.code
            .replace('&', "&amp;")
            .replace('<', "&lt;")
            .replace('>', "&gt;");
        format!("<a:t>{}</a:t>", escaped_code)
    };

    let x = code_block.x;
    let y = code_block.y;
    let width = code_block.width;
    let height = code_block.height;

    format!(
        r#"<p:sp>
<p:nvSpPr>
<p:cNvPr id="{id}" name="Code Block"/>
<p:cNvSpPr txBox="1"/>
<p:nvPr/>
</p:nvSpPr>
<p:spPr>
<a:xfrm>
<a:off x="{x}" y="{y}"/>
<a:ext cx="{width}" cy="{height}"/>
</a:xfrm>
<a:prstGeom prst="rect"><a:avLst/></a:prstGeom>
<a:solidFill><a:srgbClr val="002B36"/></a:solidFill>
<a:ln w="12700"><a:solidFill><a:srgbClr val="073642"/></a:solidFill></a:ln>
</p:spPr>
<p:txBody>
<a:bodyPr wrap="square" rtlCol="0" anchor="t" lIns="91440" tIns="45720" rIns="91440" bIns="45720"/>
<a:lstStyle/>
{highlighted_xml}</p:txBody>
</p:sp>"#
    )
}