Skip to main content

manual_image_extraction/
manual_image_extraction.rs

1//! Manually extract images while parsing either a PPTX or ODP presentation.
2//!
3//! Run with:
4//! cargo run --example manual_image_extraction <presentation.pptx|presentation.odp>
5
6use base64::Engine;
7use base64::engine::general_purpose;
8use pptx_to_md::{ImageHandlingMode, ParserConfig, PresentationContainer, Result};
9use std::fs::File;
10use std::io::Write;
11use std::path::Path;
12use std::{env, fs};
13
14fn main() -> Result<()> {
15    let args: Vec<String> = env::args().collect();
16    let Some(input_path) = args.get(1) else {
17        eprintln!(
18            "Usage: cargo run --example manual_image_extraction <presentation.pptx|presentation.odp>"
19        );
20        return Ok(());
21    };
22
23    println!("Processing presentation: {input_path}");
24
25    // Use the config builder to build your config
26    let config = ParserConfig::builder()
27        .extract_images(true)
28        .compress_images(true)
29        .quality(75)
30        .image_handling_mode(ImageHandlingMode::Manually)
31        .build();
32
33    let mut container = PresentationContainer::open(Path::new(input_path), config)?;
34
35    // Parse all slides
36    let slides = container.parse_all()?;
37
38    println!("Found {} slides", slides.len());
39
40    // create a new Markdown file
41    let mut md_file = File::create("output.md")?;
42
43    // Create output directory
44    let output_dir = "extracted_images";
45    fs::create_dir_all(output_dir)?;
46
47    let mut image_count = 1;
48
49    // Convert each slide to Markdown and save
50    for slide in slides {
51        writeln!(md_file, "{}", slide.convert_to_md()?)?;
52
53        // Manually load the base64 encoded image strings from the slide
54        if let Some(images) = slide.load_images_manually() {
55            for image in images {
56                // Decode the base64 strings back to raw image data
57                let image_data = general_purpose::STANDARD
58                    .decode(image.base64_content.clone())
59                    .expect("parser returned invalid base64 image data");
60
61                // Extract image extension if the image is not compressed, otherwise its always `.jpg`
62                let ext = if slide.config.compress_images {
63                    "jpg".to_string()
64                } else {
65                    slide.get_image_extension(&image.img_ref.target.clone())
66                };
67
68                // Construct a unique file name
69                let file_name = format!(
70                    "slide{}_image{}_{}",
71                    slide.slide_number, image_count, &image.img_ref.id
72                );
73
74                // Save the image
75                let output_path = format!("{}/{}.{}", output_dir, &file_name, ext);
76                fs::write(&output_path, image_data)?;
77                println!("Saved image to {}", output_path);
78
79                // Write the image data into the Markdown file
80                writeln!(
81                    md_file,
82                    "![{}](data:image/{};base64,{})",
83                    file_name, ext, image.base64_content
84                )?;
85
86                image_count += 1;
87            }
88        }
89    }
90
91    println!("All slides converted successfully!");
92
93    Ok(())
94}