use crate::ole::ppt::package::{PptError, Result};
use crate::ole::ppt::records::PptRecord;
use crate::ole::ppt::persist::PersistMapping;
use crate::ole::consts::PptRecordType;
pub struct SlideFactory<'doc> {
doc_data: &'doc [u8],
persist_mapping: &'doc PersistMapping,
}
impl<'doc> SlideFactory<'doc> {
#[inline]
pub fn new(doc_data: &'doc [u8], persist_mapping: &'doc PersistMapping) -> Self {
Self {
doc_data,
persist_mapping,
}
}
pub fn slide_ids(&self) -> Vec<u32> {
let all_ids = self.persist_mapping.get_persist_ids();
all_ids.into_iter()
.filter(|&persist_id| {
if let Some(offset) = self.persist_mapping.get_offset(persist_id) {
let offset = offset as usize;
if offset + 4 < self.doc_data.len() {
let record_type = u16::from_le_bytes([
self.doc_data[offset + 2],
self.doc_data[offset + 3]
]);
record_type == 1006
} else {
false
}
} else {
false
}
})
.collect()
}
pub fn parse_slide(&self, persist_id: u32) -> Result<SlideData<'doc>> {
let offset = self.persist_mapping
.get_offset(persist_id)
.ok_or_else(|| PptError::InvalidFormat(
format!("No offset found for persist_id {}", persist_id)
))?;
self.parse_slide_at_offset(offset, persist_id)
}
fn parse_slide_at_offset(&self, offset: u32, persist_id: u32) -> Result<SlideData<'doc>> {
let offset = offset as usize;
if offset + 8 > self.doc_data.len() {
return Err(PptError::Corrupted(
format!("Offset {} exceeds document length", offset)
));
}
let (record, _consumed) = PptRecord::parse(self.doc_data, offset)?;
if record.record_type != PptRecordType::Slide {
return Err(PptError::InvalidFormat(
format!("Expected Slide record, got {:?}", record.record_type)
));
}
Ok(SlideData {
persist_id,
offset,
record,
doc_data: self.doc_data,
})
}
pub fn slides(&self) -> impl Iterator<Item = Result<SlideData<'doc>>> + '_ {
self.slide_ids()
.into_iter()
.map(move |persist_id| self.parse_slide(persist_id))
}
}
#[derive(Debug)]
pub struct SlideData<'doc> {
pub persist_id: u32,
pub offset: usize,
pub record: PptRecord,
doc_data: &'doc [u8],
}
impl<'doc> SlideData<'doc> {
#[inline]
pub fn slide_atom(&self) -> Option<&PptRecord> {
self.record.find_child(PptRecordType::SlideAtom)
}
#[inline]
pub fn ppdrawing(&self) -> Option<&PptRecord> {
self.record.find_child(PptRecordType::PPDrawing)
}
#[inline]
pub fn has_shapes(&self) -> bool {
self.ppdrawing().is_some()
}
#[inline]
pub fn doc_data(&self) -> &'doc [u8] {
self.doc_data
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_slide_factory_creation() {
let doc_data = vec![0u8; 1024];
let mapping = PersistMapping::new();
let factory = SlideFactory::new(&doc_data, &mapping);
assert_eq!(factory.slide_ids().len(), 0);
}
}