pub struct ParserConfig {
pub extract_images: bool,
pub compress_images: bool,
pub quality: u8,
pub image_handling_mode: ImageHandlingMode,
pub image_output_path: Option<PathBuf>,
pub include_slide_number_as_comment: bool,
pub include_speaker_notes: bool,
pub include_comments: bool,
pub include_presentation_metadata: bool,
}Expand description
Configuration options for the PPTX parser.
Use ParserConfig::builder() to create a configuration instance.
This allows you to customize only the desired fields while falling back to sensible defaults for the rest.
§Configuration Options
| Parameter | Type | Default | Description |
|---|---|---|---|
extract_images | bool | true | Whether images are extracted from slides or not. If false, images can not be extracted manually either |
compress_images | bool | true | Whether images are compressed before encoding or not. Effects manually extracted images too |
quality | u8 | 80 | Compression level (0-100); higher values retain more detail but increase file size |
image_handling_mode | ImageHandlingMode | InMarkdown | Determines how images are handled during content export |
image_output_path | Option<PathBuf> | None | Output directory path for ImageHandlingMode::Save (mandatory for the saving mode) |
include_slide_number_as_comment | bool | true | Whether the slide number comment is included (<!-- Slide [n] -->) |
include_speaker_notes | bool | false | Whether speaker notes are appended to Markdown as blockquotes |
include_comments | bool | false | Whether presentation comments are appended to Markdown as blockquotes |
include_presentation_metadata | bool | true | Whether presentation-wide Markdown starts with a metadata comment |
§Example
use std::path::PathBuf;
use pptx_to_md::{ImageHandlingMode, ParserConfig};
let config = ParserConfig::builder()
.extract_images(true)
.compress_images(true)
.quality(75)
.image_handling_mode(ImageHandlingMode::Save)
.image_output_path(PathBuf::from("/path/to/output/dir/"))
.build();Fields§
§extract_images: bool§compress_images: bool§quality: u8§image_handling_mode: ImageHandlingMode§image_output_path: Option<PathBuf>§include_slide_number_as_comment: bool§include_speaker_notes: bool§include_comments: bool§include_presentation_metadata: boolImplementations§
Source§impl ParserConfig
impl ParserConfig
Sourcepub fn builder() -> ParserConfigBuilder
pub fn builder() -> ParserConfigBuilder
Examples found in repository?
examples/save_images.rs (line 25)
11fn main() -> Result<()> {
12 let args: Vec<String> = env::args().collect();
13 let Some(input_path) = args.get(1) else {
14 eprintln!(
15 "Usage: cargo run --example save_images <presentation.pptx|presentation.odp> [image-directory] [output.md]"
16 );
17 return Ok(());
18 };
19 let image_directory = args
20 .get(2)
21 .map(PathBuf::from)
22 .unwrap_or_else(|| PathBuf::from("extracted_images"));
23 let output_path = args.get(3).map(String::as_str).unwrap_or("output.md");
24
25 let config = ParserConfig::builder()
26 .image_handling_mode(ImageHandlingMode::Save)
27 .image_output_path(image_directory)
28 .build();
29 let mut presentation = PresentationContainer::open(Path::new(input_path), config)?;
30
31 fs::write(output_path, presentation.convert_to_md()?)?;
32 println!("Saved Markdown to {output_path}");
33 Ok(())
34}More examples
examples/basic_usage.rs (line 26)
10fn main() -> Result<()> {
11 let args: Vec<String> = env::args().collect();
12 let pptx_path = if args.len() > 1 {
13 &args[1]
14 } else {
15 eprintln!("Usage: cargo run --example basic_usage <presentation.pptx|presentation.odp> <extract_images>\ncargo run --example basic_usage sample.pptx true");
16 return Ok(());
17 };
18
19 // Tries to read if the extract_images flag is false else set to true
20 let extract_images = if args.len() > 2 {
21 !(args[2] == "false" || args[2] == "False" || args[2] == "0")
22 } else {
23 true
24 };
25
26 let config = ParserConfig::builder()
27 .extract_images(extract_images)
28 .include_presentation_metadata(true)
29 .include_comments(true)
30 .include_speaker_notes(true)
31 .build();
32 let mut container = PresentationContainer::open(Path::new(pptx_path), config)?;
33 let markdown = container.convert_to_md()?;
34
35 fs::write("output.md", markdown)?;
36 println!("Converted {:?} presentation to output.md", container.format());
37 Ok(())
38}examples/manual_image_extraction.rs (line 26)
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 "",
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}examples/performance_test.rs (line 103)
70fn main() -> Result<()> {
71 // Get the PPTX file path and optional iteration count from command line arguments
72 let args: Vec<String> = env::args().collect();
73 let pptx_path = if args.len() > 1 {
74 &args[1]
75 } else {
76 eprintln!(
77 "Usage: cargo run --example performance_test <presentation.pptx|presentation.odp> [iterations]"
78 );
79 return Ok(());
80 };
81
82 let iterations = if args.len() > 2 {
83 args[2].parse().unwrap_or(5)
84 } else {
85 10 // Default to 10 iterations
86 };
87
88 println!(
89 "Performance testing with {} iterations on: {}",
90 iterations, pptx_path
91 );
92
93 // =========== Single-threaded Approach ===========
94 let mut single_thread_bench = Benchmark::new("Single-threaded parsing");
95
96 let mut total_slides = 0;
97
98 for i in 0..iterations {
99 println!("\nIteration {} (Single-threaded)", i + 1);
100
101 // Measure container creation
102 let mut container = single_thread_bench.measure(|| {
103 let config = ParserConfig::builder().extract_images(true).build();
104 PresentationContainer::open(Path::new(pptx_path), config)
105 .expect("Failed to open presentation")
106 });
107
108 // Measure parsing
109 let slides =
110 single_thread_bench.measure(|| container.parse_all().expect("Failed to parse slides"));
111 println!(" Found {} slides in the presentation", slides.len());
112
113 // Measure conversion
114 let _md_content = single_thread_bench.measure(|| {
115 slides
116 .iter()
117 .filter_map(|slide| slide.convert_to_md().ok())
118 .collect::<Vec<String>>()
119 });
120
121 total_slides += slides.len();
122 }
123
124 single_thread_bench.report();
125 println!(
126 "Average slides per presentation: {}",
127 total_slides / iterations
128 );
129
130 // =========== Single-threaded Streamed Approach ===========
131 let mut single_thread_streamed_bench = Benchmark::new("Single-threaded streamed parsing");
132
133 total_slides = 0;
134
135 for i in 0..iterations {
136 println!("\nIteration {} (Single-threaded streamed)", i + 1);
137
138 // Measure container creation
139 let mut container = single_thread_streamed_bench.measure(|| {
140 let config = ParserConfig::builder().extract_images(true).build();
141 PresentationContainer::open(Path::new(pptx_path), config)
142 .expect("Failed to open presentation")
143 });
144
145 // Measure slide processing (including parsing and conversion)
146 let slides_processed = single_thread_streamed_bench.measure(|| {
147 let mut processed = 0;
148
149 // Process slides one by one using the iterator
150 for slide_result in container.iter_slides() {
151 match slide_result {
152 Ok(slide) => {
153 // Konvertiere den Slide zu Markdown
154 let _md_content = slide.convert_to_md();
155 processed += 1;
156 }
157 Err(e) => {
158 eprintln!("Error processing slide: {:?}", e);
159 }
160 }
161 }
162
163 processed
164 });
165
166 println!(" Processed {} slides", slides_processed);
167 total_slides += slides_processed;
168 }
169
170 single_thread_streamed_bench.report();
171 println!(
172 "Average slides per presentation: {}",
173 total_slides / iterations
174 );
175
176 // =========== Optimized Multi-threaded Approach ===========
177 let mut optimized_multi_thread_bench = Benchmark::new("Optimized Multi-threaded parsing");
178
179 total_slides = 0;
180
181 for i in 0..iterations {
182 println!("\nIteration {} (Optimized Multi-threaded)", i + 1);
183
184 // Container öffnen mit der gewünschten Konfiguration
185 let mut container = optimized_multi_thread_bench.measure(|| {
186 let config = ParserConfig::builder().extract_images(true).build();
187 PresentationContainer::open(Path::new(pptx_path), config)
188 .expect("Failed to open presentation")
189 });
190
191 let slides = optimized_multi_thread_bench.measure(|| {
192 container
193 .parse_all_multi_threaded()
194 .expect("Failed to parse slides")
195 });
196
197 println!(" Successfully processed {} slides", slides.len());
198
199 // Parallel zu Markdown konvertieren (bleibt unverändert)
200 let _md_content = optimized_multi_thread_bench.measure(|| {
201 slides
202 .par_iter()
203 .filter_map(|slide| slide.convert_to_md().ok())
204 .collect::<Vec<String>>()
205 });
206
207 total_slides += slides.len();
208 }
209
210 optimized_multi_thread_bench.report();
211 println!(
212 "Average slides per presentation: {}",
213 total_slides / iterations
214 );
215
216 // =========== Performance Comparison ===========
217 if !single_thread_bench.results.is_empty()
218 && !single_thread_streamed_bench.results.is_empty()
219 && !optimized_multi_thread_bench.results.is_empty()
220 {
221 let single_avg: Duration = single_thread_bench.results.iter().sum::<Duration>()
222 / single_thread_bench.results.len() as u32;
223 let single_streamed_avg: Duration = single_thread_streamed_bench
224 .results
225 .iter()
226 .sum::<Duration>()
227 / single_thread_streamed_bench.results.len() as u32;
228 let optimized_multi_avg: Duration = optimized_multi_thread_bench
229 .results
230 .iter()
231 .sum::<Duration>()
232 / optimized_multi_thread_bench.results.len() as u32;
233
234 println!("\nPerformance Comparison");
235 println!("=====================");
236 println!("Single-threaded average: {:?}", single_avg);
237 println!(
238 "Single-threaded streaming average: {:?}",
239 single_streamed_avg
240 );
241 println!(
242 "Optimized multi-threaded average: {:?}",
243 optimized_multi_avg
244 );
245
246 // Compare single-threaded vs single-threaded streaming
247 if single_avg > single_streamed_avg {
248 let speedup = single_avg.as_secs_f64() / single_streamed_avg.as_secs_f64();
249 println!(
250 "Single-threaded streaming is {:.2}x faster than single-threaded",
251 speedup
252 );
253 } else {
254 let slowdown = single_streamed_avg.as_secs_f64() / single_avg.as_secs_f64();
255 println!(
256 "Single-threaded streaming is {:.2}x slower than single-threaded",
257 slowdown
258 );
259 }
260
261 // Compare single-threaded vs optimized multithreaded
262 if single_avg > optimized_multi_avg {
263 let speedup = single_avg.as_secs_f64() / optimized_multi_avg.as_secs_f64();
264 println!(
265 "Optimized multi-threaded is {:.2}x faster than single-threaded",
266 speedup
267 );
268 } else {
269 let slowdown = optimized_multi_avg.as_secs_f64() / single_avg.as_secs_f64();
270 println!(
271 "Optimized multi-threaded is {:.2}x slower than single-threaded",
272 slowdown
273 );
274 }
275
276 // Compare single-threaded streaming vs optimized multithreaded
277 if single_streamed_avg > optimized_multi_avg {
278 let speedup = single_streamed_avg.as_secs_f64() / optimized_multi_avg.as_secs_f64();
279 println!(
280 "Optimized multi-threaded is {:.2}x faster than single-threaded streaming",
281 speedup
282 );
283 } else {
284 let slowdown = optimized_multi_avg.as_secs_f64() / single_streamed_avg.as_secs_f64();
285 println!(
286 "Optimized multi-threaded is {:.2}x slower than single-threaded streaming",
287 slowdown
288 );
289 }
290
291 // Determine the overall fastest approach
292 let fastest_approach = if single_avg <= single_streamed_avg
293 && single_avg <= optimized_multi_avg
294 {
295 "Single-threaded"
296 } else if single_streamed_avg <= single_avg && single_streamed_avg <= optimized_multi_avg {
297 "Single-threaded streaming"
298 } else {
299 "Optimized multi-threaded"
300 };
301
302 println!(
303 "\nOverall result: {} approach is the fastest for this workload.",
304 fastest_approach
305 );
306 }
307
308 Ok(())
309}Trait Implementations§
Source§impl Clone for ParserConfig
impl Clone for ParserConfig
Source§fn clone(&self) -> ParserConfig
fn clone(&self) -> ParserConfig
Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreSource§impl Debug for ParserConfig
impl Debug for ParserConfig
Auto Trait Implementations§
impl Freeze for ParserConfig
impl RefUnwindSafe for ParserConfig
impl Send for ParserConfig
impl Sync for ParserConfig
impl Unpin for ParserConfig
impl UnsafeUnpin for ParserConfig
impl UnwindSafe for ParserConfig
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<R, P> ReadPrimitive<R> for P
impl<R, P> ReadPrimitive<R> for P
Source§fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
Read this value from the supplied reader. Same as
ReadEndian::read_from_little_endian().