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
8pub static DOCX_EMU: f32 = 360000.0;
10pub static DOCX_MAX_EMU: u64 = (21.0 * 360000.0 / 2.0) as u64;
11static DPI: f64 = 96f64;
13static EMU: f64 = 914400f64;
15
16#[derive(Debug, Clone)]
18pub struct DocxImage {
19 pub image_path: String,
21 pub image_ext: String,
23 pub image_data: Vec<u8>,
25 pub relation_id: String,
27 pub width: u64,
29 pub height: u64,
31}
32
33impl DocxImage {
34 pub fn new(image_path: &str) -> Result<Self, DocxError> {
37 let mut file = File::open(image_path)?;
39 let mut image_data = Vec::new();
40 file.read_to_end(&mut image_data)?;
41 let ext = get_extension(image_path)?;
43 let (width_emu, height_emu) = get_image_size(&image_data)?;
44 Self::new_image_data_size(image_path, image_data, ext, width_emu, height_emu)
45 }
46
47 pub fn new_size(image_path: &str, width: u64, height: u64) -> Result<Self, DocxError> {
52 let mut file = File::open(image_path)?;
54 let mut image_data = Vec::new();
55 file.read_to_end(&mut image_data)?;
56 let ext = get_extension(image_path)?;
58 DocxImage::new_image_data_size(image_path, image_data, ext, width, height)
59 }
60
61 pub fn new_image_data(
65 image_url: &str,
66 image_data: Vec<u8>,
67 image_ext: &str,
68 ) -> Result<Self, DocxError> {
69 let (width_emu, height_emu) = get_image_size(&image_data)?;
70 DocxImage::new_image_data_size(image_url, image_data, image_ext, width_emu, height_emu)
71 }
72
73 pub fn new_image_data_size(
80 image_url: &str,
81 image_data: Vec<u8>,
82 image_ext: &str,
83 width: u64,
84 height: u64,
85 ) -> Result<Self, DocxError> {
86 Ok(DocxImage {
87 image_path: image_url.to_string(),
88 image_ext: image_ext.to_string(),
89 relation_id: format!("rId{}", Uuid::new_v4().simple()),
90 width,
91 height,
92 image_data,
93 })
94 }
95
96 pub fn new_image_data_size_relation(
104 image_url: &str,
105 image_data: Vec<u8>,
106 image_ext: &str,
107 relation_id: &str,
108 width: u64,
109 height: u64,
110 ) -> Self {
111 DocxImage {
112 image_path: image_url.to_string(),
113 image_ext: image_ext.to_string(),
114 relation_id: relation_id.to_string(),
115 width,
116 height,
117 image_data,
118 }
119 }
120
121 pub fn clone_image_reset_size(docx_image: &DocxImage, width: u64, height: u64) -> Self {
126 DocxImage {
127 image_path: docx_image.image_path.clone(),
128 image_ext: docx_image.image_ext.clone(),
129 relation_id: docx_image.relation_id.clone(),
130 width,
131 height,
132 image_data: docx_image.image_data.clone(),
133 }
134 }
135}
136
137pub fn get_image_size(image_data: &[u8]) -> Result<(u64, u64), DocxError> {
138 let img = load_from_memory(image_data)?;
139 let (width_px, height_px) = img.dimensions();
140 let mut width_emu = (width_px as f64 * EMU / DPI) as u64;
141 let mut height_emu = (height_px as f64 * EMU / DPI) as u64;
142 if width_emu > DOCX_MAX_EMU {
144 height_emu = DOCX_MAX_EMU * height_emu / width_emu;
145 width_emu = DOCX_MAX_EMU;
146 Ok((width_emu, height_emu))
147 } else {
148 Ok((width_emu, height_emu))
149 }
150}
151
152pub fn get_extension(image_path: &str) -> Result<&str, DocxError> {
156 Path::new(image_path)
157 .extension()
158 .and_then(|s| s.to_str())
159 .ok_or_else(|| DocxError::ImageNotFound("Could not determine image extension".to_string()))
160}