use std::path::Path;
use base64::Engine;
use crate::oxml::shape::Pic as OxmlPic;
use crate::oxml::sppr::ShapeProperties;
use crate::shape::base::Shape;
use crate::units::Emu;
#[derive(Clone, Debug, Default)]
pub struct Picture {
pub(crate) pic: OxmlPic,
pub blob: Option<Vec<u8>>,
pub ext: String,
}
impl Picture {
pub fn from_path(path: impl AsRef<Path>) -> crate::Result<Self> {
let path = path.as_ref();
let blob = std::fs::read(path)?;
let ext = path
.extension()
.and_then(|e| e.to_str())
.map(|e| format!(".{}", e.to_lowercase()))
.unwrap_or_else(|| ".png".to_string());
Ok(Picture {
pic: OxmlPic::default(),
blob: Some(blob),
ext,
})
}
pub fn from_bytes(bytes: impl Into<Vec<u8>>, ext: impl Into<String>) -> Self {
let ext_s = ext.into();
let ext_norm = if ext_s.starts_with('.') {
ext_s
} else {
format!(".{}", ext_s)
};
Picture {
pic: OxmlPic::default(),
blob: Some(bytes.into()),
ext: ext_norm,
}
}
pub fn from_pic(pic: OxmlPic) -> Self {
Picture {
pic,
blob: None,
ext: ".png".to_string(),
}
}
pub fn pic(&self) -> &OxmlPic {
&self.pic
}
pub fn pic_mut(&mut self) -> &mut OxmlPic {
&mut self.pic
}
pub fn properties(&self) -> &ShapeProperties {
&self.pic.properties
}
pub fn properties_mut(&mut self) -> &mut ShapeProperties {
&mut self.pic.properties
}
pub fn set_fill_mode(&mut self, mode: crate::oxml::sppr::BlipFillMode) {
self.pic.fill_mode = mode;
}
pub fn set_stretch(&mut self) {
self.pic.fill_mode = crate::oxml::sppr::BlipFillMode::Stretch;
}
pub fn set_tile(
&mut self,
tx: Option<i64>,
ty: Option<i64>,
sx: Option<i32>,
sy: Option<i32>,
flip: Option<&str>,
algn: Option<&str>,
) {
self.pic.fill_mode = crate::oxml::sppr::BlipFillMode::Tile {
tx,
ty,
sx,
sy,
flip: flip.map(|s| s.to_string()),
algn: algn.map(|s| s.to_string()),
};
}
pub fn fill_mode(&self) -> &crate::oxml::sppr::BlipFillMode {
&self.pic.fill_mode
}
pub fn crop(&mut self, left: i32, top: i32, right: i32, bottom: i32) {
self.pic.src_rect = Some((left, top, right, bottom));
}
pub fn set_crop(&mut self, left: i32, top: i32, right: i32, bottom: i32) {
self.crop(left, top, right, bottom);
}
pub fn clear_crop(&mut self) {
self.pic.src_rect = None;
}
pub fn crop_rect(&self) -> Option<(i32, i32, i32, i32)> {
self.pic.src_rect
}
pub fn crop_left(&self) -> i32 {
self.pic.src_rect.map(|(l, _, _, _)| l).unwrap_or(0)
}
pub fn crop_top(&self) -> i32 {
self.pic.src_rect.map(|(_, t, _, _)| t).unwrap_or(0)
}
pub fn crop_right(&self) -> i32 {
self.pic.src_rect.map(|(_, _, r, _)| r).unwrap_or(0)
}
pub fn crop_bottom(&self) -> i32 {
self.pic.src_rect.map(|(_, _, _, b)| b).unwrap_or(0)
}
pub fn set_crop_left(&mut self, left: i32) {
let mut r = self.pic.src_rect.unwrap_or((0, 0, 0, 0));
r.0 = left;
self.pic.src_rect = Some(r);
}
pub fn set_crop_top(&mut self, top: i32) {
let mut r = self.pic.src_rect.unwrap_or((0, 0, 0, 0));
r.1 = top;
self.pic.src_rect = Some(r);
}
pub fn set_crop_right(&mut self, right: i32) {
let mut r = self.pic.src_rect.unwrap_or((0, 0, 0, 0));
r.2 = right;
self.pic.src_rect = Some(r);
}
pub fn set_crop_bottom(&mut self, bottom: i32) {
let mut r = self.pic.src_rect.unwrap_or((0, 0, 0, 0));
r.3 = bottom;
self.pic.src_rect = Some(r);
}
pub fn set_placeholder(&mut self, ph_idx: u32, ph_type: Option<&str>) {
self.pic.is_placeholder = true;
self.pic.ph_idx = Some(ph_idx);
self.pic.ph_type = ph_type.map(|s| s.to_string());
}
pub fn clear_placeholder(&mut self) {
self.pic.is_placeholder = false;
self.pic.ph_idx = None;
self.pic.ph_type = None;
}
pub fn is_placeholder(&self) -> bool {
self.pic.is_placeholder
}
pub fn ph_idx(&self) -> Option<u32> {
self.pic.ph_idx
}
pub fn ph_type(&self) -> Option<&str> {
self.pic.ph_type.as_deref()
}
pub fn base64(&self) -> Option<String> {
self.blob
.as_ref()
.map(|b| base64::engine::general_purpose::STANDARD.encode(b))
}
pub fn set_video(&mut self, rid: impl Into<String>) {
self.pic.media = Some(crate::oxml::shape::MediaKind::Video { rid: rid.into() });
}
pub fn set_audio(&mut self, rid: impl Into<String>) {
self.pic.media = Some(crate::oxml::shape::MediaKind::Audio { rid: rid.into() });
}
pub fn media_kind(&self) -> Option<&crate::oxml::shape::MediaKind> {
self.pic.media.as_ref()
}
pub fn clear_media(&mut self) {
self.pic.media = None;
}
}
impl Shape for Picture {
fn id(&self) -> u32 {
self.pic.id
}
fn set_id(&mut self, id: u32) {
self.pic.id = id;
}
fn name(&self) -> &str {
&self.pic.name
}
fn set_name(&mut self, name: String) {
self.pic.name = name;
}
fn shape_type(&self) -> &'static str {
"picture"
}
fn left(&self) -> Emu {
self.pic.properties.xfrm.off_x.unwrap_or_default()
}
fn set_left(&mut self, emu: Emu) {
self.pic.properties.xfrm.off_x = Some(emu);
}
fn top(&self) -> Emu {
self.pic.properties.xfrm.off_y.unwrap_or_default()
}
fn set_top(&mut self, emu: Emu) {
self.pic.properties.xfrm.off_y = Some(emu);
}
fn width(&self) -> Emu {
self.pic.properties.xfrm.ext_cx.unwrap_or_default()
}
fn set_width(&mut self, emu: Emu) {
self.pic.properties.xfrm.ext_cx = Some(emu);
}
fn height(&self) -> Emu {
self.pic.properties.xfrm.ext_cy.unwrap_or_default()
}
fn set_height(&mut self, emu: Emu) {
self.pic.properties.xfrm.ext_cy = Some(emu);
}
fn rotation(&self) -> f64 {
self.pic.properties.rot_deg.unwrap_or(0.0)
}
fn set_rotation(&mut self, deg: f64) {
self.pic.properties.rot_deg = Some(deg);
let rot = (deg * 60_000.0) as i32;
self.pic.properties.xfrm.rot = Some(rot);
}
}
pub fn content_type_for(ext: &str) -> &'static str {
match ext.to_ascii_lowercase().as_str() {
".png" => "image/png",
".jpg" | ".jpeg" => "image/jpeg",
".gif" => "image/gif",
".bmp" => "image/bmp",
".svg" => "image/svg+xml",
".tif" | ".tiff" => "image/tiff",
_ => "application/octet-stream",
}
}