use crate::common::{Error, Result, Metadata};
use crate::odf::core::{Content, Meta, Package, Styles, Manifest};
use crate::odf::elements::text::{TextElements, Paragraph as ElementParagraph};
use crate::odf::elements::table::Table as ElementTable;
use crate::odf::elements::style::{StyleRegistry, StyleElements};
use std::io::Cursor;
use std::path::Path;
#[allow(dead_code)]
pub struct Document {
package: Package<Cursor<Vec<u8>>>,
content: Content,
styles: Option<Styles>,
meta: Option<Meta>,
manifest: Manifest,
style_registry: StyleRegistry,
}
impl Document {
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.text") {
return Err(Error::InvalidFormat(format!(
"Not an ODT 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();
let mut style_registry = StyleRegistry::new();
if let Some(ref styles_part) = styles
&& let Ok(registry) = StyleElements::parse_styles(styles_part.xml_content()) {
style_registry = registry;
}
if let Ok(content_registry) = StyleElements::parse_styles(content.xml_content()) {
for (_name, style) in content_registry.styles {
style_registry.add_style(style);
}
}
Ok(Self {
package,
content,
styles,
meta,
manifest,
style_registry,
})
}
pub fn text(&mut self) -> Result<String> {
TextElements::extract_text(self.content.xml_content())
}
pub fn paragraphs(&mut self) -> Result<Vec<ElementParagraph>> {
TextElements::parse_paragraphs(self.content.xml_content())
}
pub fn tables(&mut self) -> Result<Vec<ElementTable>> {
use crate::odf::elements::table::TableElements;
TableElements::parse_tables_from_content(self.content.xml_content())
}
pub fn metadata(&self) -> Result<Metadata> {
if let Some(meta) = &self.meta {
Ok(meta.extract_metadata())
} else {
Ok(Metadata::default())
}
}
pub fn styles(&self) -> &StyleRegistry {
&self.style_registry
}
pub fn get_style_properties(&self, style_name: &str) -> crate::odf::elements::style::StyleProperties {
self.style_registry.get_resolved_properties(style_name)
}
}