pptx_to_md/
presentation.rs1use crate::container::SlideIterator;
2use crate::odp::{OdpContainer, OdpSlideIterator};
3use crate::{ParserConfig, PptxContainer, Presentation, PresentationMetadata, Result, Slide};
4use std::io::Read;
5use std::path::Path;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[non_exhaustive]
10pub enum PresentationFormat {
11 Pptx,
12 Odp,
13}
14
15enum ContainerInner {
16 Pptx(PptxContainer),
17 Odp(OdpContainer),
18}
19
20pub struct PresentationContainer {
25 format: PresentationFormat,
26 inner: ContainerInner,
27}
28
29impl PresentationContainer {
30 pub fn open(path: &Path, config: ParserConfig) -> Result<Self> {
35 let format = detect_format(path)?;
36 Self::open_as(path, config, format)
37 }
38
39 pub fn open_as(path: &Path, config: ParserConfig, format: PresentationFormat) -> Result<Self> {
44 let inner = match format {
45 PresentationFormat::Pptx => ContainerInner::Pptx(PptxContainer::open(path, config)?),
46 PresentationFormat::Odp => ContainerInner::Odp(OdpContainer::open(path, config)?),
47 };
48
49 Ok(Self { format, inner })
50 }
51
52 pub fn format(&self) -> PresentationFormat {
53 self.format
54 }
55
56 pub fn metadata(&self) -> &PresentationMetadata {
57 match &self.inner {
58 ContainerInner::Pptx(container) => container.metadata(),
59 ContainerInner::Odp(container) => container.metadata(),
60 }
61 }
62
63 pub fn parse_all(&mut self) -> Result<Vec<Slide>> {
64 match &mut self.inner {
65 ContainerInner::Pptx(container) => container.parse_all(),
66 ContainerInner::Odp(container) => container.parse_all(),
67 }
68 }
69
70 pub fn parse_document(&mut self) -> Result<Presentation> {
72 let metadata = self.metadata().clone();
73 let slides = self.parse_all()?;
74 let diagnostics = slides
75 .iter()
76 .flat_map(|slide| slide.diagnostics.iter().cloned())
77 .collect();
78 Ok(Presentation {
79 metadata,
80 slides,
81 diagnostics,
82 })
83 }
84
85 pub fn parse_all_multi_threaded(&mut self) -> Result<Vec<Slide>> {
86 match &mut self.inner {
87 ContainerInner::Pptx(container) => container.parse_all_multi_threaded(),
88 ContainerInner::Odp(container) => container.parse_all(),
91 }
92 }
93
94 pub fn convert_to_md(&mut self) -> Result<String> {
95 match &mut self.inner {
96 ContainerInner::Pptx(container) => container.convert_to_md(),
97 ContainerInner::Odp(container) => container.convert_to_md(),
98 }
99 }
100
101 pub fn convert_to_md_multi_threaded(&mut self) -> Result<String> {
102 match &mut self.inner {
103 ContainerInner::Pptx(container) => container.convert_to_md_multi_threaded(),
104 ContainerInner::Odp(container) => container.convert_to_md(),
105 }
106 }
107
108 pub fn iter_slides(&mut self) -> PresentationSlideIterator<'_> {
109 let inner = match &mut self.inner {
110 ContainerInner::Pptx(container) => {
111 PresentationIteratorInner::Pptx(container.iter_slides())
112 }
113 ContainerInner::Odp(container) => {
114 PresentationIteratorInner::Odp(container.iter_slides())
115 }
116 };
117 PresentationSlideIterator { inner }
118 }
119}
120
121fn detect_format(path: &Path) -> Result<PresentationFormat> {
122 let file = std::fs::File::open(path)?;
123 let mut archive = zip::ZipArchive::new(file)?;
124
125 if let Ok(mut mimetype) = archive.by_name("mimetype") {
126 let mut value = String::new();
127 mimetype.read_to_string(&mut value)?;
128 if value.trim() == "application/vnd.oasis.opendocument.presentation" {
129 return Ok(PresentationFormat::Odp);
130 }
131 }
132
133 if archive.by_name("[Content_Types].xml").is_ok()
134 && archive.by_name("ppt/presentation.xml").is_ok()
135 {
136 return Ok(PresentationFormat::Pptx);
137 }
138
139 Err(crate::Error::ParseError("Unsupported presentation format"))
140}
141
142pub struct PresentationSlideIterator<'a> {
144 inner: PresentationIteratorInner<'a>,
145}
146
147enum PresentationIteratorInner<'a> {
148 Pptx(SlideIterator<'a>),
149 Odp(OdpSlideIterator<'a>),
150}
151
152impl Iterator for PresentationSlideIterator<'_> {
153 type Item = Result<Slide>;
154
155 fn next(&mut self) -> Option<Self::Item> {
156 match &mut self.inner {
157 PresentationIteratorInner::Pptx(iterator) => iterator.next(),
158 PresentationIteratorInner::Odp(iterator) => iterator.next(),
159 }
160 }
161}