docx_template/
image.rs

1use crate::error::DocxError;
2use image::{GenericImageView, load_from_memory};
3use std::fs::File;
4use std::io::Read;
5use std::path::Path;
6use uuid::Uuid;
7
8// 1厘米约等于360000EMU
9pub static DOCX_EMU: f32 = 360000.0;
10pub static DOCX_MAX_EMU: u64 = (21.0 * 360000.0 / 2.0) as u64;
11// 1英寸=96像素
12static DPI: f64 = 96f64;
13// 1英寸=914400 EMU
14static EMU: f64 = 914400f64;
15
16// 添加的图标对象
17pub struct DocxImage {
18    // 图片路径
19    pub image_path: String,
20    // 图片扩展名
21    pub image_ext: String,
22    // 图片数据
23    pub image_data: Vec<u8>,
24    // 关联id
25    pub relation_id: String,
26    // 图片高度
27    pub width: u64,
28    // 图片高度
29    pub height: u64,
30}
31
32impl DocxImage {
33    /// 创建图片对象  
34    /// @param image_path 本地图片路径  
35    pub fn new(image_path: &str) -> Result<Self, DocxError> {
36        // 打开文件读取数据到数组中
37        let mut file = File::open(image_path)?;
38        let mut image_data = Vec::new();
39        file.read_to_end(&mut image_data)?;
40        // 获取扩展名
41        let ext = get_extension(image_path)?;
42        let (width_emu, height_emu) = get_image_size(&image_data)?;
43        Self::new_image_data_size(image_path, image_data, ext, width_emu, height_emu)
44    }
45
46    /// 设置图片大小  
47    /// @param image_path 图片路径  
48    /// @param width 图片宽度(emu)
49    /// @param height 图片高度 (emu)
50    pub fn new_size(image_path: &str, width: u64, height: u64) -> Result<Self, DocxError> {
51        // 打开文件读取数据到数组中
52        let mut file = File::open(image_path)?;
53        let mut image_data = Vec::new();
54        file.read_to_end(&mut image_data)?;
55        // 获取扩展名
56        let ext = get_extension(image_path)?;
57        DocxImage::new_image_data_size(image_path, image_data, ext, width, height)
58    }
59
60    /// 设置图片大小  
61    /// @param image_url 图片路径  
62    /// @param image_data 图片数据  
63    pub fn new_image_data(
64        image_url: &str,
65        image_data: Vec<u8>,
66        image_ext: &str,
67    ) -> Result<Self, DocxError> {
68        let (width_emu, height_emu) = get_image_size(&image_data)?;
69        DocxImage::new_image_data_size(image_url, image_data, image_ext, width_emu, height_emu)
70    }
71
72    /// 设置图片大小  
73    /// @param image_url 图片路径  
74    /// @param image_data 图片数据  
75    /// @param image_ext 图片扩展名  
76    /// @param width 图片宽度(emu) 
77    /// @param height 图片高度(emu) 
78    pub fn new_image_data_size(
79        image_url: &str,
80        image_data: Vec<u8>,
81        image_ext: &str,
82        width: u64,
83        height: u64,
84    ) -> Result<Self, DocxError> {
85        Ok(DocxImage {
86            image_path: image_url.to_string(),
87            image_ext: image_ext.to_string(),
88            relation_id: format!("rId{}", Uuid::new_v4().simple()),
89            width,
90            height,
91            image_data,
92        })
93    }
94}
95
96fn get_image_size(image_data: &[u8]) -> Result<(u64, u64), DocxError> {
97    let img = load_from_memory(image_data)?;
98    let (width_px, height_px) = img.dimensions();
99    let mut width_emu = (width_px as f64 * EMU / DPI) as u64;
100    let mut height_emu = (height_px as f64 * EMU / DPI) as u64;
101    // 判断图片是否大于文档宽度
102    if width_emu > DOCX_MAX_EMU {
103        height_emu = DOCX_MAX_EMU * height_emu / width_emu;
104        width_emu = DOCX_MAX_EMU;
105        Ok((width_emu, height_emu))
106    } else {
107        Ok((width_emu, height_emu))
108    }
109}
110
111/// 获取本地图片的扩展名
112/// @param image_path 本地图片路径
113/// @return 图片扩展名
114fn get_extension(image_path: &str) -> Result<&str, DocxError> {
115    Path::new(image_path)
116        .extension()
117        .and_then(|s| s.to_str())
118        .ok_or_else(|| DocxError::ImageNotFound("Could not determine image extension".to_string()))
119}