Skip to main content

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        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    /// Make a `Pic` element. For now only PNG is supported.
77    ///
78    /// Use [Pic::new] method, to call `image` crate do conversion for you.
79    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    // unit is emu
134    pub fn size(mut self, w_emu: u32, h_emu: u32) -> Pic {
135        self.size = (w_emu, h_emu);
136        self
137    }
138
139    // unit is deg
140    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}