docx_rs/documents/elements/
pic.rs

1use 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    // For writer only
15    #[serde(skip_serializing_if = "Vec::is_empty")]
16    pub image: Vec<u8>,
17    // (width, height). unit is emu
18    pub size: (u32, u32),
19    pub position_type: DrawingPositionType,
20    /// Specifies that this object shall be positioned using the positioning information in the
21    /// simplePos child element (ยง20.4.2.13). This positioning, when specified, positions the
22    /// object on the page by placing its top left point at the x-y coordinates specified by that
23    /// element.
24    pub simple_pos: bool,
25    // unit is emu
26    pub simple_pos_x: i32,
27    pub simple_pos_y: i32,
28    /// Specifies how this DrawingML object behaves when its anchor is located in a table cell;
29    /// and its specified position would cause it to intersect with a table cell displayed in the
30    /// document. That behavior shall be as follows:
31    pub layout_in_cell: bool,
32    /// Specifies the relative Z-ordering of all DrawingML objects in this document. Each floating
33    /// DrawingML object shall have a Z-ordering value, which determines which object is
34    /// displayed when any two objects intersect. Higher values shall indicate higher Z-order;
35    /// lower values shall indicate lower Z-order.
36    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    /// Specifies the minimum distance which shall be maintained between the top edge of this drawing object and any subsequent text within the document when this graphical object is displayed within the document's contents.,
43    /// The distance shall be measured in EMUs (English Metric Units).,
44    pub dist_t: i32,
45    pub dist_b: i32,
46    pub dist_l: i32,
47    pub dist_r: i32,
48    // deg
49    pub rot: u16,
50}
51
52impl Pic {
53    #[cfg(feature = "image")]
54    /// Make a `Pic`.
55    ///
56    /// Converts the passed image to PNG internally and computes its size.
57    pub fn new(buf: &[u8]) -> Pic {
58        let img = ::image::load_from_memory(buf).expect("Should load image from memory.");
59        let (w, h) = ::image::GenericImageView::dimensions(&img);
60        let mut buf = std::io::Cursor::new(vec![]);
61        img.write_to(&mut buf, ::image::ImageFormat::Png)
62            .expect("Unable to write dynamic image");
63        Self::new_with_dimensions(buf.into_inner(), w, h)
64    }
65
66    /// Make a `Pic` element. For now only PNG is supported.
67    ///
68    /// Use [Pic::new] method, to call `image` crate do conversion for you.
69    pub fn new_with_dimensions(buffer: Vec<u8>, width_px: u32, height_px: u32) -> Pic {
70        let id = create_pic_rid(generate_pic_id());
71        Self {
72            id,
73            image: buffer,
74            size: (from_px(width_px), from_px(height_px)),
75            position_type: DrawingPositionType::Inline,
76            simple_pos: false,
77            simple_pos_x: 0,
78            simple_pos_y: 0,
79            layout_in_cell: false,
80            relative_height: 190500,
81            allow_overlap: false,
82            position_v: DrawingPosition::Offset(0),
83            position_h: DrawingPosition::Offset(0),
84            relative_from_h: RelativeFromHType::default(),
85            relative_from_v: RelativeFromVType::default(),
86            dist_t: 0,
87            dist_b: 0,
88            dist_l: 0,
89            dist_r: 0,
90            rot: 0,
91        }
92    }
93
94    pub(crate) fn with_empty() -> Pic {
95        Self {
96            id: "".to_string(),
97            image: vec![],
98            size: (0, 0),
99            position_type: DrawingPositionType::Inline,
100            simple_pos: false,
101            simple_pos_x: 0,
102            simple_pos_y: 0,
103            layout_in_cell: false,
104            relative_height: 190500,
105            allow_overlap: false,
106            position_v: DrawingPosition::Offset(0),
107            position_h: DrawingPosition::Offset(0),
108            relative_from_h: RelativeFromHType::default(),
109            relative_from_v: RelativeFromVType::default(),
110            dist_t: 0,
111            dist_b: 0,
112            dist_l: 0,
113            dist_r: 0,
114            rot: 0,
115        }
116    }
117
118    pub fn id(mut self, id: impl Into<String>) -> Pic {
119        self.id = id.into();
120        self
121    }
122
123    // unit is emu
124    pub fn size(mut self, w_emu: u32, h_emu: u32) -> Pic {
125        self.size = (w_emu, h_emu);
126        self
127    }
128
129    // unit is deg
130    pub fn rotate(mut self, deg: u16) -> Pic {
131        self.rot = deg;
132        self
133    }
134
135    pub fn floating(mut self) -> Pic {
136        self.position_type = DrawingPositionType::Anchor;
137        self
138    }
139
140    pub fn overlapping(mut self) -> Pic {
141        self.allow_overlap = true;
142        self
143    }
144
145    pub fn offset_x(mut self, x: i32) -> Pic {
146        self.position_h = DrawingPosition::Offset(x);
147        self
148    }
149
150    pub fn offset_y(mut self, y: i32) -> Pic {
151        self.position_v = DrawingPosition::Offset(y);
152        self
153    }
154
155    pub fn position_h(mut self, pos: DrawingPosition) -> Self {
156        self.position_h = pos;
157        self
158    }
159
160    pub fn position_v(mut self, pos: DrawingPosition) -> Self {
161        self.position_v = pos;
162        self
163    }
164
165    pub fn relative_from_h(mut self, t: RelativeFromHType) -> Self {
166        self.relative_from_h = t;
167        self
168    }
169
170    pub fn relative_from_v(mut self, t: RelativeFromVType) -> Self {
171        self.relative_from_v = t;
172        self
173    }
174
175    pub fn dist_t(mut self, v: i32) -> Self {
176        self.dist_t = v;
177        self
178    }
179
180    pub fn dist_b(mut self, v: i32) -> Self {
181        self.dist_b = v;
182        self
183    }
184
185    pub fn dist_l(mut self, v: i32) -> Self {
186        self.dist_l = v;
187        self
188    }
189
190    pub fn dist_r(mut self, v: i32) -> Self {
191        self.dist_r = v;
192        self
193    }
194
195    pub fn simple_pos(mut self, v: bool) -> Self {
196        self.simple_pos = v;
197        self
198    }
199
200    pub fn relative_height(mut self, v: u32) -> Self {
201        self.relative_height = v;
202        self
203    }
204}
205
206impl BuildXML for Pic {
207    fn build_to<W: Write>(
208        &self,
209        stream: xml::writer::EventWriter<W>,
210    ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
211        XMLBuilder::from(stream)
212            .open_pic("http://schemas.openxmlformats.org/drawingml/2006/picture")?
213            .open_pic_nv_pic_pr()?
214            .pic_c_nv_pr("0", "")?
215            .open_pic_c_nv_pic_pr()?
216            .a_pic_locks("1", "1")?
217            .close()?
218            .close()?
219            .open_blip_fill()?
220            .a_blip(&self.id)?
221            .a_src_rect()?
222            .open_a_stretch()?
223            .a_fill_rect()?
224            .close()?
225            .close()?
226            .open_pic_sp_pr("auto")?
227            .open_a_xfrm_with_rot(&format!("{}", (self.rot as u32) * 60 * 1000))?
228            .a_off("0", "0")?
229            .a_ext(&format!("{}", self.size.0), &format!("{}", self.size.1))?
230            .close()?
231            .open_a_prst_geom("rect")?
232            .a_av_lst()?
233            .close()?
234            .close()?
235            .close()?
236            .into_inner()
237    }
238}
239
240#[cfg(test)]
241mod tests {
242
243    use super::*;
244    #[cfg(test)]
245    use pretty_assertions::assert_eq;
246    use std::str;
247
248    #[test]
249    fn test_pic_build() {
250        let b = Pic::new_with_dimensions(Vec::new(), 320, 240).build();
251        assert_eq!(
252            str::from_utf8(&b).unwrap(),
253            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>"#
254        );
255    }
256}