From 73afcb7dd9c5d75a4ea9684fae844e304fe669bd Mon Sep 17 00:00:00 2001 From: krut_ni Date: Tue, 14 Jul 2026 20:22:46 +0200 Subject: [PATCH] included comments, speaker notes and metadata; upgraded to new high level API --- runtime/src/file_data.rs | 70 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/runtime/src/file_data.rs b/runtime/src/file_data.rs index 43446f46..b28f1891 100644 --- a/runtime/src/file_data.rs +++ b/runtime/src/file_data.rs @@ -12,7 +12,7 @@ use calamine::{open_workbook_auto, Reader}; use file_format::{FileFormat, Kind}; use futures::{Stream, StreamExt}; use pdfium_render::prelude::Pdfium; -use pptx_to_md::{ImageHandlingMode, ParserConfig, PptxContainer}; +use pptx_to_md::{ImageHandlingMode, ParserConfig, PresentationContainer, PresentationFormat, PresentationMetadata }; use serde::{Deserialize, Deserializer, Serialize}; use serde::de::{Error as SerdeError, Visitor}; use std::path::Path; @@ -203,7 +203,8 @@ async fn stream_data(file_path: &str, extract_images: bool) -> Result stream_pptx(file_path, extract_images).await?, + "pptx" => stream_presentation(file_path, extract_images, PresentationFormat::Pptx).await?, + "odp" => stream_presentation(file_path, extract_images, PresentationFormat::Odp).await?, "xlsx" | "ods" | "xls" | "xlsm" | "xlsb" | "xla" | "xlam" => { stream_spreadsheet_as_csv(file_path).await? @@ -244,8 +245,11 @@ async fn stream_data(file_path: &str, extract_images: bool) -> Result match fmt { FileFormat::OfficeOpenXmlPresentation => { - stream_pptx(file_path, extract_images).await? + stream_presentation(file_path, extract_images, PresentationFormat::Pptx).await? }, + FileFormat::OpendocumentPresentation => { + stream_presentation(file_path, extract_images, PresentationFormat::Odp).await? + } _ => stream_text_file(file_path, false, None).await?, }, @@ -448,7 +452,7 @@ async fn chunk_image(file_path: &str) -> Result { Ok(Box::pin(stream)) } -async fn stream_pptx(file_path: &str, extract_images: bool) -> Result { +async fn stream_presentation(file_path: &str, extract_images: bool, format: PresentationFormat) -> Result { let path = Path::new(file_path).to_owned(); let parser_config = ParserConfig::builder() @@ -456,21 +460,30 @@ async fn stream_pptx(file_path: &str, extract_images: bool) -> Result) + PresentationContainer::open_as(&path, parser_config, format).map_err(|e| Box::new(e) as Box) }).await??; let (tx, rx) = mpsc::channel(32); tokio::spawn(async move { + let metadata_md = presentation_metadata_to_markdown(streamer.metadata()); + for slide_result in streamer.iter_slides() { match slide_result { Ok(slide) => { - if let Some(md_content) = slide.convert_to_md() { + if let Some(mut content) = slide.convert_to_md() { + if slide.slide_number == 1 && let Some(metadata) = metadata_md.as_deref() { + content = format!("{metadata}\n\n{content}"); + } + let chunk = Chunk::new( - md_content, + content, Metadata::Presentation { slide_number: slide.slide_number, image: None, @@ -529,3 +542,46 @@ async fn stream_pptx(file_path: &str, extract_images: bool) -> Result Option { + let mut fields = Vec::new(); + push_presentation_metadata_field(&mut fields, "Title", metadata.title.as_deref()); + push_presentation_metadata_field(&mut fields, "Author", metadata.author.as_deref()); + push_presentation_metadata_field(&mut fields, "Last Modified By", metadata.last_modified_by.as_deref()); + push_presentation_metadata_field(&mut fields, "Subject", metadata.subject.as_deref()); + push_presentation_metadata_field(&mut fields, "Description", metadata.description.as_deref()); + if !metadata.keywords.is_empty() { + fields.push(format!( + "Keywords: {}", + sanitize_presentation_metadata_value(&metadata.keywords.join("; ")) + )); + } + push_presentation_metadata_field(&mut fields, "Created", metadata.created_at.as_deref()); + push_presentation_metadata_field(&mut fields, "Modified", metadata.modified_at.as_deref()); + + if fields.is_empty() { + None + } else { + Some(format!( + "", + fields.join("\n") + )) + } +} + +fn push_presentation_metadata_field(fields: &mut Vec, label: &str, value: Option<&str>) { + if let Some(value) = value { + fields.push(format!( + "{label}: {}", + sanitize_presentation_metadata_value(value) + )); + } +} + +fn sanitize_presentation_metadata_value(value: &str) -> String { + value + .split_whitespace() + .collect::>() + .join(" ") + .replace("--", "--") +}