use std::io::{Cursor, Read};
pub struct PptCfb<'a> {
compound: cfb::CompoundFile<Cursor<&'a [u8]>>,
}
impl<'a> PptCfb<'a> {
pub fn open(bytes: &'a [u8]) -> Result<Self, String> {
let compound = cfb::CompoundFile::open(Cursor::new(bytes))
.map_err(|e| format!("Cannot open .ppt file (invalid CFB format): {e}"))?;
Ok(PptCfb { compound })
}
pub fn powerpoint_document_stream(&mut self) -> Result<Vec<u8>, String> {
let path = if self.compound.is_stream("/PowerPoint Document") {
"/PowerPoint Document"
} else if self
.compound
.is_stream("/PP97_DUALSTORAGE/PowerPoint Document")
{
"/PP97_DUALSTORAGE/PowerPoint Document"
} else if self.compound.exists("/PP40") {
return Err("This is a PowerPoint 95 (or earlier) file, which is not \
supported. Convert it to .pptx first."
.to_string());
} else {
return Err("Missing 'PowerPoint Document' stream — not a valid .ppt file".to_string());
};
let mut buf = Vec::new();
self.compound
.open_stream(path)
.map_err(|_| {
"Missing 'PowerPoint Document' stream — not a valid .ppt file".to_string()
})?
.read_to_end(&mut buf)
.map_err(|e| format!("Failed to read PowerPoint Document stream: {e}"))?;
Ok(buf)
}
pub fn pictures_stream(&mut self) -> Result<Option<Vec<u8>>, String> {
let mut stream = match self.compound.open_stream("/Pictures") {
Ok(s) => s,
Err(_) => return Ok(None),
};
let mut buf = Vec::new();
stream
.read_to_end(&mut buf)
.map_err(|e| format!("Failed to read Pictures stream: {e}"))?;
Ok(Some(buf))
}
}
impl<'a> PptCfb<'a> {
pub fn document_and_current_user(&mut self) -> Result<(Vec<u8>, Option<Vec<u8>>), String> {
let dual = !self.compound.is_stream("/PowerPoint Document")
&& self
.compound
.is_stream("/PP97_DUALSTORAGE/PowerPoint Document");
let doc = self.powerpoint_document_stream()?;
let cu_path = if dual {
"/PP97_DUALSTORAGE/Current User"
} else {
"/Current User"
};
let cu = match self.compound.open_stream(cu_path) {
Ok(mut s) => {
let mut buf = Vec::new();
match s.read_to_end(&mut buf) {
Ok(_) => Some(buf),
Err(_) => None,
}
}
Err(_) => None,
};
Ok((doc, cu))
}
}
pub fn read_powerpoint_document_stream(bytes: &[u8]) -> Result<Vec<u8>, String> {
PptCfb::open(bytes)?.powerpoint_document_stream()
}
pub fn read_pictures_stream(bytes: &[u8]) -> Result<Option<Vec<u8>>, String> {
PptCfb::open(bytes)?.pictures_stream()
}