use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event};
use quick_xml::Reader;
use quick_xml::Writer;
use crate::error::{PptxError, PptxResult};
use crate::oxml::ns::{NS_CP, NS_DC, NS_DCMITYPE, NS_DCTERMS, NS_XSI};
use crate::xml_util::local_name_owned;
use super::CoreProperties;
impl CoreProperties {
pub fn from_xml(xml: &[u8]) -> PptxResult<Self> {
let mut props = Self::new();
let mut reader = Reader::from_reader(xml);
reader.config_mut().trim_text(true);
let mut buf = Vec::new();
let mut current_element: Option<String> = None;
loop {
match reader.read_event_into(&mut buf) {
Ok(Event::Start(ref e)) => {
let qname = e.name();
current_element = Some(local_name_owned(qname.as_ref()));
}
Ok(Event::Text(ref e)) => {
if let Some(ref elem_name) = current_element {
let text = e.decode().map_err(|err| {
PptxError::InvalidXml(format!("XML text error: {err}"))
})?;
let text = text.into_owned();
match elem_name.as_str() {
"title" => props.title = text,
"creator" => props.author = text,
"subject" => props.subject = text,
"keywords" => props.keywords = text,
"description" => props.comments = text,
"category" => props.category = text,
"created" => props.created = text,
"modified" => props.modified = text,
"lastModifiedBy" => props.last_modified_by = text,
"revision" => props.revision = text,
"contentStatus" => props.content_status = text,
"language" => props.language = text,
"version" => props.version = text,
"identifier" => props.identifier = Some(text),
"lastPrinted" => props.last_printed = Some(text),
_ => {}
}
}
}
Ok(Event::End(_)) => {
current_element = None;
}
Ok(Event::Eof) => break,
Err(e) => return Err(PptxError::Xml(e)),
_ => {}
}
buf.clear();
}
Ok(props)
}
pub fn to_xml(&self) -> PptxResult<Vec<u8>> {
let mut writer = Writer::new(Vec::new());
writer.write_event(Event::Decl(BytesDecl::new(
"1.0",
Some("UTF-8"),
Some("yes"),
)))?;
let mut root = BytesStart::new("cp:coreProperties");
root.push_attribute(("xmlns:cp", NS_CP));
root.push_attribute(("xmlns:dc", NS_DC));
root.push_attribute(("xmlns:dcterms", NS_DCTERMS));
root.push_attribute(("xmlns:dcmitype", NS_DCMITYPE));
root.push_attribute(("xmlns:xsi", NS_XSI));
writer.write_event(Event::Start(root))?;
if !self.title.is_empty() {
write_simple_element(&mut writer, "dc:title", &self.title)?;
}
if !self.author.is_empty() {
write_simple_element(&mut writer, "dc:creator", &self.author)?;
}
if !self.subject.is_empty() {
write_simple_element(&mut writer, "dc:subject", &self.subject)?;
}
if !self.comments.is_empty() {
write_simple_element(&mut writer, "dc:description", &self.comments)?;
}
if !self.keywords.is_empty() {
write_simple_element(&mut writer, "cp:keywords", &self.keywords)?;
}
if !self.category.is_empty() {
write_simple_element(&mut writer, "cp:category", &self.category)?;
}
if !self.last_modified_by.is_empty() {
write_simple_element(&mut writer, "cp:lastModifiedBy", &self.last_modified_by)?;
}
if !self.revision.is_empty() {
write_simple_element(&mut writer, "cp:revision", &self.revision)?;
}
if !self.content_status.is_empty() {
write_simple_element(&mut writer, "cp:contentStatus", &self.content_status)?;
}
if !self.language.is_empty() {
write_simple_element(&mut writer, "dc:language", &self.language)?;
}
if !self.version.is_empty() {
write_simple_element(&mut writer, "cp:version", &self.version)?;
}
if let Some(ref identifier) = self.identifier {
write_simple_element(&mut writer, "dc:identifier", identifier)?;
}
if let Some(ref last_printed) = self.last_printed {
write_simple_element(&mut writer, "cp:lastPrinted", last_printed)?;
}
if !self.created.is_empty() {
write_datetime_element(&mut writer, "dcterms:created", &self.created)?;
}
if !self.modified.is_empty() {
write_datetime_element(&mut writer, "dcterms:modified", &self.modified)?;
}
writer.write_event(Event::End(BytesEnd::new("cp:coreProperties")))?;
Ok(writer.into_inner())
}
}
fn write_simple_element(writer: &mut Writer<Vec<u8>>, tag: &str, text: &str) -> PptxResult<()> {
writer.write_event(Event::Start(BytesStart::new(tag)))?;
writer.write_event(Event::Text(BytesText::new(text)))?;
writer.write_event(Event::End(BytesEnd::new(tag)))?;
Ok(())
}
fn write_datetime_element(writer: &mut Writer<Vec<u8>>, tag: &str, text: &str) -> PptxResult<()> {
let mut elem = BytesStart::new(tag);
elem.push_attribute(("xsi:type", "dcterms:W3CDTF"));
writer.write_event(Event::Start(elem))?;
writer.write_event(Event::Text(BytesText::new(text)))?;
writer.write_event(Event::End(BytesEnd::new(tag)))?;
Ok(())
}