Skip to main content

ppt_rs/import/
mod.rs

1pub mod html;
2
3use crate::api::Presentation;
4use crate::oxml::presentation::PresentationReader;
5use crate::generator::{SlideContent, Shape, ShapeType, TableBuilder, TableRow, TableCell};
6use crate::exc::Result;
7
8pub use html::{parse_html, parse_html_with_options, HtmlParseOptions, Html2Ppt};
9
10/// Import a presentation from a file path
11pub fn import_pptx(path: &str) -> Result<Presentation> {
12    let reader = PresentationReader::open(path)?;
13    let mut presentation = Presentation::new();
14    
15    if let Some(title) = &reader.info().title {
16        presentation = presentation.title(title);
17    }
18    
19    for parsed_slide in reader.get_all_slides()? {
20        let mut content = SlideContent::new(parsed_slide.title.as_deref().unwrap_or(""));
21        
22        // Add body text as bullets
23        for text in parsed_slide.body_text {
24            content = content.add_bullet(&text);
25        }
26        
27        // Add shapes (skip title and body)
28        for parsed_shape in parsed_slide.shapes {
29            if !parsed_shape.is_title && !parsed_shape.is_body {
30                let mut shape = Shape::new(
31                    map_shape_type(&parsed_shape.shape_type),
32                    parsed_shape.x.max(0) as u32,
33                    parsed_shape.y.max(0) as u32,
34                    parsed_shape.width.max(0) as u32,
35                    parsed_shape.height.max(0) as u32
36                );
37                
38                // Set text
39                let text = parsed_shape.text();
40                if !text.is_empty() {
41                    shape = shape.with_text(&text);
42                }
43                
44                content.shapes.push(shape);
45            }
46        }
47        
48        // Add tables
49        for parsed_table in parsed_slide.tables {
50             // Determine column count from first row
51             let col_count = parsed_table.rows.first().map(|r| r.len()).unwrap_or(0);
52             if col_count == 0 { continue; }
53             
54             // Default column width (approx 2 inches)
55             let col_widths = vec![1828800; col_count];
56             
57             let mut table_builder = TableBuilder::new(col_widths);
58             for row in parsed_table.rows {
59                 let cells: Vec<TableCell> = row.into_iter()
60                     .map(|cell| TableCell::new(&cell.text))
61                     .collect();
62                 let table_row = TableRow::new(cells);
63                 table_builder = table_builder.add_row(table_row);
64             }
65             
66             // SlideContent currently supports only one table via 'table' field
67             if content.table.is_none() {
68                 content.table = Some(table_builder.build());
69                 content.has_table = true;
70             }
71        }
72        
73        presentation = presentation.add_slide(content);
74    }
75    
76    Ok(presentation)
77}
78
79fn map_shape_type(type_name: &Option<String>) -> ShapeType {
80    if let Some(name) = type_name {
81        match name.as_str() {
82            "rect" => ShapeType::Rectangle,
83            "roundRect" => ShapeType::RoundedRectangle,
84            "ellipse" => ShapeType::Ellipse,
85            "triangle" => ShapeType::Triangle,
86            "rtTriangle" => ShapeType::RightTriangle,
87            "diamond" => ShapeType::Diamond,
88            "pentagon" => ShapeType::Pentagon,
89            "hexagon" => ShapeType::Hexagon,
90            "octagon" => ShapeType::Octagon,
91            "star5" => ShapeType::Star5,
92            "rightArrow" => ShapeType::RightArrow,
93            _ => ShapeType::Rectangle, // Default
94        }
95    } else {
96        ShapeType::Rectangle
97    }
98}