use crate::error::DocxError;
use image::{GenericImageView, load_from_memory};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use uuid::Uuid;
pub static DOCX_EMU: f32 = 360000.0;
pub static DOCX_MAX_EMU: u64 = (21.0 * 360000.0 / 2.0) as u64;
static DPI: f64 = 96f64;
static EMU: f64 = 914400f64;
#[derive(Debug, Clone)]
pub struct DocxImage {
pub image_path: String,
pub image_ext: String,
pub image_data: Vec<u8>,
pub relation_id: String,
pub width: u64,
pub height: u64,
}
impl DocxImage {
pub fn new(image_path: &str) -> Result<Self, DocxError> {
let mut file = File::open(image_path)?;
let mut image_data = Vec::new();
file.read_to_end(&mut image_data)?;
let ext = get_extension(image_path)?;
let (width_emu, height_emu) = get_image_size(&image_data)?;
Self::new_image_data_size(image_path, image_data, ext, width_emu, height_emu)
}
pub fn new_size(image_path: &str, width: u64, height: u64) -> Result<Self, DocxError> {
let mut file = File::open(image_path)?;
let mut image_data = Vec::new();
file.read_to_end(&mut image_data)?;
let ext = get_extension(image_path)?;
DocxImage::new_image_data_size(image_path, image_data, ext, width, height)
}
pub fn new_image_data(
image_url: &str,
image_data: Vec<u8>,
image_ext: &str,
) -> Result<Self, DocxError> {
let (width_emu, height_emu) = get_image_size(&image_data)?;
DocxImage::new_image_data_size(image_url, image_data, image_ext, width_emu, height_emu)
}
pub fn new_image_data_size(
image_url: &str,
image_data: Vec<u8>,
image_ext: &str,
width: u64,
height: u64,
) -> Result<Self, DocxError> {
Ok(DocxImage {
image_path: image_url.to_string(),
image_ext: image_ext.to_string(),
relation_id: format!("rId{}", Uuid::new_v4().simple()),
width,
height,
image_data,
})
}
pub fn new_image_data_size_relation(
image_url: &str,
image_data: Vec<u8>,
image_ext: &str,
relation_id: &str,
width: u64,
height: u64,
) -> Self {
DocxImage {
image_path: image_url.to_string(),
image_ext: image_ext.to_string(),
relation_id: relation_id.to_string(),
width,
height,
image_data,
}
}
pub fn clone_image_reset_size(docx_image: &DocxImage, width: u64, height: u64) -> Self {
DocxImage {
image_path: docx_image.image_path.clone(),
image_ext: docx_image.image_ext.clone(),
relation_id: docx_image.relation_id.clone(),
width,
height,
image_data: docx_image.image_data.clone(),
}
}
}
pub fn get_image_size(image_data: &[u8]) -> Result<(u64, u64), DocxError> {
let img = load_from_memory(image_data)?;
let (width_px, height_px) = img.dimensions();
let mut width_emu = (width_px as f64 * EMU / DPI) as u64;
let mut height_emu = (height_px as f64 * EMU / DPI) as u64;
if width_emu > DOCX_MAX_EMU {
height_emu = DOCX_MAX_EMU * height_emu / width_emu;
width_emu = DOCX_MAX_EMU;
Ok((width_emu, height_emu))
} else {
Ok((width_emu, height_emu))
}
}
pub fn get_extension(image_path: &str) -> Result<&str, DocxError> {
Path::new(image_path)
.extension()
.and_then(|s| s.to_str())
.ok_or_else(|| DocxError::ImageNotFound("Could not determine image extension".to_string()))
}