use crate::common::{Error, Result, Metadata};
use crate::odf::core::{Content, Meta, Package, Styles, Manifest};
use std::io::Cursor;
use std::path::Path;
pub struct Presentation {
_package: Package<Cursor<Vec<u8>>>,
_content: Content,
_styles: Option<Styles>,
_meta: Option<Meta>,
_manifest: Manifest,
}
impl Presentation {
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
let path = path.as_ref();
let bytes = std::fs::read(path)?;
Self::from_bytes(bytes)
}
pub fn from_bytes(bytes: Vec<u8>) -> Result<Self> {
let cursor = Cursor::new(bytes);
let mut package = Package::from_reader(cursor)?;
let mime_type = package.mimetype();
if !mime_type.contains("opendocument.presentation") {
return Err(Error::InvalidFormat(format!(
"Not an ODP file: MIME type is {}", mime_type
)));
}
let content_bytes = package.get_file("content.xml")?;
let content = Content::from_bytes(&content_bytes)?;
let styles = if package.has_file("styles.xml") {
let styles_bytes = package.get_file("styles.xml")?;
Some(Styles::from_bytes(&styles_bytes)?)
} else {
None
};
let meta = if package.has_file("meta.xml") {
let meta_bytes = package.get_file("meta.xml")?;
Some(Meta::from_bytes(&meta_bytes)?)
} else {
None
};
let manifest = package.manifest().clone();
Ok(Self {
_package: package,
_content: content,
_styles: styles,
_meta: meta,
_manifest: manifest,
})
}
pub fn slide_count(&mut self) -> Result<usize> {
let slides = self.slides()?;
Ok(slides.len())
}
pub fn slides(&mut self) -> Result<Vec<Slide>> {
let content_bytes = self._package.get_file("content.xml")?;
let content = Content::from_bytes(&content_bytes)?;
use quick_xml::events::Event;
use quick_xml::Reader;
let mut reader = Reader::from_str(content.xml_content());
let mut buf = Vec::new();
let mut slides = Vec::new();
let mut current_slide_text = String::new();
let mut current_slide_title: Option<String> = None;
let mut in_slide = false;
let mut slide_index = 0;
loop {
match reader.read_event_into(&mut buf) {
Ok(Event::Start(ref e)) => {
match e.name().as_ref() {
b"draw:page" => {
if in_slide && !current_slide_text.trim().is_empty() {
slides.push(Slide {
title: current_slide_title.clone(),
text: current_slide_text.trim().to_string(),
index: slide_index,
notes: None,
});
slide_index += 1;
}
current_slide_title = None;
for attr_result in e.attributes() {
if let Ok(attr) = attr_result
&& attr.key.as_ref() == b"draw:name" {
if let Ok(name) = String::from_utf8(attr.value.to_vec()) {
current_slide_title = Some(name);
}
break;
}
}
if current_slide_title.is_none() {
current_slide_title = Some(format!("Slide{}", slide_index + 1));
}
current_slide_text.clear();
in_slide = true;
}
b"text:p" | b"text:span" => {
}
_ => {}
}
}
Ok(Event::Text(ref t)) => {
if in_slide
&& let Ok(text) = String::from_utf8(t.to_vec())
&& !text.trim().is_empty() {
if !current_slide_text.is_empty() {
current_slide_text.push(' ');
}
current_slide_text.push_str(text.trim());
}
}
Ok(Event::End(ref e)) => {
if e.name().as_ref() == b"draw:page" {
if in_slide && !current_slide_text.trim().is_empty() {
slides.push(Slide {
title: current_slide_title.clone(),
text: current_slide_text.trim().to_string(),
index: slide_index,
notes: None,
});
slide_index += 1;
}
in_slide = false;
}
}
Ok(Event::Eof) => break,
Err(e) => return Err(crate::common::Error::InvalidFormat(format!("XML parsing error: {}", e))),
_ => {}
}
buf.clear();
}
if in_slide && !current_slide_text.trim().is_empty() {
slides.push(Slide {
title: current_slide_title,
text: current_slide_text.trim().to_string(),
index: slide_index,
notes: None,
});
}
Ok(slides)
}
pub fn slide(&mut self, index: usize) -> Result<Option<Slide>> {
let slides = self.slides()?;
Ok(slides.into_iter().nth(index))
}
pub fn text(&mut self) -> Result<String> {
let slides = self.slides()?;
let mut all_text = Vec::new();
for slide in slides {
if !slide.text.trim().is_empty() {
all_text.push(slide.text.trim().to_string());
}
}
Ok(all_text.join("\n\n"))
}
pub fn metadata(&self) -> Result<Metadata> {
if let Some(meta) = &self._meta {
Ok(meta.extract_metadata())
} else {
Ok(Metadata::default())
}
}
}
#[derive(Clone)]
pub struct Slide {
pub title: Option<String>,
pub text: String,
pub index: usize,
pub notes: Option<String>,
}
impl Slide {
pub fn title(&self) -> Result<Option<String>> {
Ok(self.title.clone())
}
pub fn text(&self) -> Result<String> {
Ok(self.text.clone())
}
pub fn shapes(&self) -> Result<Vec<Shape>> {
Ok(Vec::new()) }
pub fn index(&self) -> usize {
self.index
}
pub fn notes(&self) -> Result<Option<String>> {
Ok(self.notes.clone())
}
}
pub struct Shape {
}
impl Shape {
pub fn text(&self) -> Result<String> {
Ok(String::new()) }
pub fn shape_type(&self) -> crate::common::ShapeType {
crate::common::ShapeType::AutoShape }
pub fn has_text(&self) -> bool {
false }
}