use crate::error::PptxResult;
use crate::opc::pack_uri::PackURI;
use crate::opc::part::Part;
use super::OpcPackage;
impl OpcPackage {
pub fn or_add_image_part(
&mut self,
image: &crate::media::Image,
) -> PptxResult<(PackURI, String)> {
let target_sha1 = image.sha1();
if let Some(existing) =
find_existing_media_by_sha1(&self.parts, "/ppt/media/image", target_sha1)
{
return Ok(existing);
}
let partname = self.next_image_partname(image.ext())?;
let part = Part::new(
partname.clone(),
image.content_type(),
image.blob().to_vec(),
);
self.put_part(part);
Ok((partname, image.content_type().to_string()))
}
pub fn or_add_media_part(
&mut self,
video: &crate::media::Video,
) -> PptxResult<(PackURI, String)> {
let target_sha1 = video.sha1();
if let Some(existing) =
find_existing_media_by_sha1(&self.parts, "/ppt/media/media", target_sha1)
{
return Ok(existing);
}
let partname = self.next_media_partname(video.ext())?;
let part = Part::new(
partname.clone(),
video.content_type(),
video.blob().to_vec(),
);
self.put_part(part);
Ok((partname, video.content_type().to_string()))
}
}
fn find_existing_media_by_sha1(
parts: &std::collections::HashMap<String, Part>,
prefix: &str,
target_sha1: &str,
) -> Option<(PackURI, String)> {
for part in parts.values() {
if part.partname.as_str().starts_with(prefix) {
let existing_sha1 = compute_sha1_hex(&part.blob);
if existing_sha1 == target_sha1 {
return Some((part.partname.clone(), part.content_type.clone()));
}
}
}
None
}
fn compute_sha1_hex(data: &[u8]) -> String {
use sha1::{Digest, Sha1};
use std::fmt::Write;
let mut hasher = Sha1::new();
hasher.update(data);
let result = hasher.finalize();
let mut s = String::with_capacity(40);
for b in result.as_slice() {
write!(s, "{b:02x}")
.unwrap_or_else(|_| unreachable!("fmt::Write for String is infallible"));
}
s
}