docx_rs/documents/elements/
pic.rs1use serde::Serialize;
2use std::io::Write;
3
4use crate::documents::*;
5use crate::types::*;
6use crate::xml_builder::*;
7
8#[derive(Debug, Clone, Serialize, PartialEq)]
9#[cfg_attr(feature = "wasm", derive(ts_rs::TS))]
10#[cfg_attr(feature = "wasm", ts(export))]
11#[serde(rename_all = "camelCase")]
12pub struct Pic {
13 pub id: String,
14 #[serde(skip_serializing_if = "Vec::is_empty")]
16 pub image: Vec<u8>,
17 pub size: (u32, u32),
19 pub position_type: DrawingPositionType,
20 pub simple_pos: bool,
25 pub simple_pos_x: i32,
27 pub simple_pos_y: i32,
28 pub layout_in_cell: bool,
32 pub relative_height: u32,
37 pub allow_overlap: bool,
38 pub position_h: DrawingPosition,
39 pub position_v: DrawingPosition,
40 pub relative_from_h: RelativeFromHType,
41 pub relative_from_v: RelativeFromVType,
42 pub dist_t: i32,
45 pub dist_b: i32,
46 pub dist_l: i32,
47 pub dist_r: i32,
48 pub rot: u16,
50}
51
52impl Pic {
53 #[cfg(feature = "image")]
54 pub fn new(buf: &[u8]) -> Pic {
58 if buf.starts_with(&[137, 80, 78, 71, 13, 10, 26, 10]) {
59 let reader = ::image::ImageReader::new(std::io::Cursor::new(buf))
60 .with_guessed_format()
61 .expect("Should detect PNG format.");
62 let (w, h) = reader
63 .into_dimensions()
64 .expect("Should read PNG dimensions.");
65 return Self::new_with_dimensions(buf.to_vec(), w, h);
66 }
67
68 let img = ::image::load_from_memory(buf).expect("Should load image from memory.");
69 let (w, h) = ::image::GenericImageView::dimensions(&img);
70 let mut buf = std::io::Cursor::new(vec![]);
71 img.write_to(&mut buf, ::image::ImageFormat::Png)
72 .expect("Unable to write dynamic image");
73 Self::new_with_dimensions(buf.into_inner(), w, h)
74 }
75
76 pub fn new_with_dimensions(buffer: Vec<u8>, width_px: u32, height_px: u32) -> Pic {
80 let id = create_pic_rid(generate_pic_id());
81 Self {
82 id,
83 image: buffer,
84 size: (from_px(width_px), from_px(height_px)),
85 position_type: DrawingPositionType::Inline,
86 simple_pos: false,
87 simple_pos_x: 0,
88 simple_pos_y: 0,
89 layout_in_cell: false,
90 relative_height: 190500,
91 allow_overlap: false,
92 position_v: DrawingPosition::Offset(0),
93 position_h: DrawingPosition::Offset(0),
94 relative_from_h: RelativeFromHType::default(),
95 relative_from_v: RelativeFromVType::default(),
96 dist_t: 0,
97 dist_b: 0,
98 dist_l: 0,
99 dist_r: 0,
100 rot: 0,
101 }
102 }
103
104 pub(crate) fn with_empty() -> Pic {
105 Self {
106 id: "".to_string(),
107 image: vec![],
108 size: (0, 0),
109 position_type: DrawingPositionType::Inline,
110 simple_pos: false,
111 simple_pos_x: 0,
112 simple_pos_y: 0,
113 layout_in_cell: false,
114 relative_height: 190500,
115 allow_overlap: false,
116 position_v: DrawingPosition::Offset(0),
117 position_h: DrawingPosition::Offset(0),
118 relative_from_h: RelativeFromHType::default(),
119 relative_from_v: RelativeFromVType::default(),
120 dist_t: 0,
121 dist_b: 0,
122 dist_l: 0,
123 dist_r: 0,
124 rot: 0,
125 }
126 }
127
128 pub fn id(mut self, id: impl Into<String>) -> Pic {
129 self.id = id.into();
130 self
131 }
132
133 pub fn size(mut self, w_emu: u32, h_emu: u32) -> Pic {
135 self.size = (w_emu, h_emu);
136 self
137 }
138
139 pub fn rotate(mut self, deg: u16) -> Pic {
141 self.rot = deg;
142 self
143 }
144
145 pub fn floating(mut self) -> Pic {
146 self.position_type = DrawingPositionType::Anchor;
147 self
148 }
149
150 pub fn overlapping(mut self) -> Pic {
151 self.allow_overlap = true;
152 self
153 }
154
155 pub fn offset_x(mut self, x: i32) -> Pic {
156 self.position_h = DrawingPosition::Offset(x);
157 self
158 }
159
160 pub fn offset_y(mut self, y: i32) -> Pic {
161 self.position_v = DrawingPosition::Offset(y);
162 self
163 }
164
165 pub fn position_h(mut self, pos: DrawingPosition) -> Self {
166 self.position_h = pos;
167 self
168 }
169
170 pub fn position_v(mut self, pos: DrawingPosition) -> Self {
171 self.position_v = pos;
172 self
173 }
174
175 pub fn relative_from_h(mut self, t: RelativeFromHType) -> Self {
176 self.relative_from_h = t;
177 self
178 }
179
180 pub fn relative_from_v(mut self, t: RelativeFromVType) -> Self {
181 self.relative_from_v = t;
182 self
183 }
184
185 pub fn dist_t(mut self, v: i32) -> Self {
186 self.dist_t = v;
187 self
188 }
189
190 pub fn dist_b(mut self, v: i32) -> Self {
191 self.dist_b = v;
192 self
193 }
194
195 pub fn dist_l(mut self, v: i32) -> Self {
196 self.dist_l = v;
197 self
198 }
199
200 pub fn dist_r(mut self, v: i32) -> Self {
201 self.dist_r = v;
202 self
203 }
204
205 pub fn simple_pos(mut self, v: bool) -> Self {
206 self.simple_pos = v;
207 self
208 }
209
210 pub fn relative_height(mut self, v: u32) -> Self {
211 self.relative_height = v;
212 self
213 }
214}
215
216impl BuildXML for Pic {
217 fn build_to<W: Write>(
218 &self,
219 stream: crate::xml::writer::EventWriter<W>,
220 ) -> crate::xml::writer::Result<crate::xml::writer::EventWriter<W>> {
221 XMLBuilder::from(stream)
222 .open_pic("http://schemas.openxmlformats.org/drawingml/2006/picture")?
223 .open_pic_nv_pic_pr()?
224 .pic_c_nv_pr("0", "")?
225 .open_pic_c_nv_pic_pr()?
226 .a_pic_locks("1", "1")?
227 .close()?
228 .close()?
229 .open_blip_fill()?
230 .a_blip(&self.id)?
231 .a_src_rect()?
232 .open_a_stretch()?
233 .a_fill_rect()?
234 .close()?
235 .close()?
236 .open_pic_sp_pr("auto")?
237 .open_a_xfrm_with_rot(&format!("{}", (self.rot as u32) * 60 * 1000))?
238 .a_off("0", "0")?
239 .a_ext(&format!("{}", self.size.0), &format!("{}", self.size.1))?
240 .close()?
241 .open_a_prst_geom("rect")?
242 .a_av_lst()?
243 .close()?
244 .close()?
245 .close()?
246 .into_inner()
247 }
248}
249
250#[cfg(test)]
251mod tests {
252
253 use super::*;
254 #[cfg(test)]
255 use pretty_assertions::assert_eq;
256 use std::str;
257
258 #[test]
259 fn test_pic_build() {
260 let b = Pic::new_with_dimensions(Vec::new(), 320, 240).build();
261 assert_eq!(
262 str::from_utf8(&b).unwrap(),
263 r#"<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="0" name="" /><pic:cNvPicPr><a:picLocks noChangeAspect="1" noChangeArrowheads="1" /></pic:cNvPicPr></pic:nvPicPr><pic:blipFill><a:blip r:embed="rIdImage123" /><a:srcRect /><a:stretch><a:fillRect /></a:stretch></pic:blipFill><pic:spPr bwMode="auto"><a:xfrm rot="0"><a:off x="0" y="0" /><a:ext cx="3048000" cy="2286000" /></a:xfrm><a:prstGeom prst="rect"><a:avLst /></a:prstGeom></pic:spPr></pic:pic>"#
264 );
265 }
266}