docx_rs/documents/elements/
drawing.rs

1use super::*;
2use serde::{ser::*, Serialize};
3use std::io::Write;
4
5use crate::documents::BuildXML;
6use crate::types::*;
7use crate::xml_builder::*;
8
9#[derive(Debug, Clone, PartialEq, Default, Serialize)]
10pub struct Drawing {
11    #[serde(flatten)]
12    pub data: Option<DrawingData>,
13}
14
15#[derive(Debug, Clone, PartialEq)]
16pub enum DrawingData {
17    Pic(Pic),
18    TextBox(TextBox),
19}
20
21impl Serialize for DrawingData {
22    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
23    where
24        S: Serializer,
25    {
26        match *self {
27            DrawingData::Pic(ref pic) => {
28                let mut t = serializer.serialize_struct("Pic", 2)?;
29                t.serialize_field("type", "pic")?;
30                t.serialize_field("data", pic)?;
31                t.end()
32            }
33            DrawingData::TextBox(ref text_box) => {
34                let mut t = serializer.serialize_struct("TextBox", 2)?;
35                t.serialize_field("type", "textBox")?;
36                t.serialize_field("data", text_box)?;
37                t.end()
38            }
39        }
40    }
41}
42
43impl Drawing {
44    pub fn new() -> Drawing {
45        Default::default()
46    }
47
48    pub fn pic(mut self, pic: Pic) -> Drawing {
49        self.data = Some(DrawingData::Pic(pic));
50        self
51    }
52
53    pub fn text_box(mut self, t: TextBox) -> Drawing {
54        self.data = Some(DrawingData::TextBox(t));
55        self
56    }
57}
58
59impl BuildXML for Drawing {
60    fn build_to<W: Write>(
61        &self,
62        stream: xml::writer::EventWriter<W>,
63    ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
64        let b = XMLBuilder::from(stream);
65        let mut b = b.open_drawing()?;
66
67        match &self.data {
68            Some(DrawingData::Pic(p)) => {
69                if let DrawingPositionType::Inline { .. } = p.position_type {
70                    b = b.open_wp_inline(
71                        &format!("{}", p.dist_t),
72                        &format!("{}", p.dist_b),
73                        &format!("{}", p.dist_l),
74                        &format!("{}", p.dist_r),
75                    )?
76                } else {
77                    b = b
78                        .open_wp_anchor(
79                            &format!("{}", p.dist_t),
80                            &format!("{}", p.dist_b),
81                            &format!("{}", p.dist_l),
82                            &format!("{}", p.dist_r),
83                            "0",
84                            if p.simple_pos { "1" } else { "0" },
85                            "0",
86                            "0",
87                            if p.layout_in_cell { "1" } else { "0" },
88                            &format!("{}", p.relative_height),
89                        )?
90                        .simple_pos(
91                            &format!("{}", p.simple_pos_x),
92                            &format!("{}", p.simple_pos_y),
93                        )?
94                        .open_position_h(&format!("{}", p.relative_from_h))?;
95
96                    match p.position_h {
97                        DrawingPosition::Offset(x) => {
98                            let x = format!("{}", x as u32);
99                            b = b.pos_offset(&x)?.close()?;
100                        }
101                        DrawingPosition::Align(x) => {
102                            b = b.align(&x.to_string())?.close()?;
103                        }
104                    }
105
106                    b = b.open_position_v(&format!("{}", p.relative_from_v))?;
107
108                    match p.position_v {
109                        DrawingPosition::Offset(y) => {
110                            let y = format!("{}", y as u32);
111                            b = b.pos_offset(&y)?.close()?;
112                        }
113                        DrawingPosition::Align(a) => {
114                            b = b.align(&a.to_string())?.close()?;
115                        }
116                    }
117                }
118
119                let w = format!("{}", p.size.0);
120                let h = format!("{}", p.size.1);
121                b = b
122                    // Please see 20.4.2.7 extent (Drawing Object Size)
123                    // One inch equates to 914400 EMUs and a centimeter is 360000
124                    .wp_extent(&w, &h)?
125                    .wp_effect_extent("0", "0", "0", "0")?;
126                if p.allow_overlap {
127                    b = b.wrap_none()?;
128                } else if p.position_type == DrawingPositionType::Anchor {
129                    b = b.wrap_square("bothSides")?;
130                }
131                b = b
132                    .wp_doc_pr("1", "Figure")?
133                    .open_wp_c_nv_graphic_frame_pr()?
134                    .a_graphic_frame_locks(
135                        "http://schemas.openxmlformats.org/drawingml/2006/main",
136                        "1",
137                    )?
138                    .close()?
139                    .open_a_graphic("http://schemas.openxmlformats.org/drawingml/2006/main")?
140                    .open_a_graphic_data(
141                        "http://schemas.openxmlformats.org/drawingml/2006/picture",
142                    )?
143                    .add_child(&p.clone())?
144                    .close()?
145                    .close()?;
146            }
147            Some(DrawingData::TextBox(_t)) => unimplemented!("TODO: Support textBox writer"),
148            None => {
149                unimplemented!()
150            }
151        }
152        b.close()?.close()?.into_inner()
153    }
154}
155
156#[cfg(test)]
157mod tests {
158
159    use super::*;
160    #[cfg(test)]
161    use pretty_assertions::assert_eq;
162    use std::str;
163
164    #[test]
165    fn test_drawing_build_with_pic() {
166        let pic = Pic::new_with_dimensions(Vec::new(), 320, 240);
167        let d = Drawing::new().pic(pic).build();
168        assert_eq!(
169            str::from_utf8(&d).unwrap(),
170            r#"<w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="3048000" cy="2286000" /><wp:effectExtent b="0" l="0" r="0" t="0" /><wp:docPr id="1" name="Figure" /><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1" /></wp:cNvGraphicFramePr><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><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></a:graphicData></a:graphic></wp:inline></w:drawing>"#
171        );
172    }
173
174    #[test]
175    fn test_drawing_build_with_pic_overlap() {
176        let pic = Pic::new_with_dimensions(Vec::new(), 320, 240).overlapping();
177        let d = Drawing::new().pic(pic).build();
178        assert_eq!(
179            str::from_utf8(&d).unwrap(),
180            r#"<w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="3048000" cy="2286000" /><wp:effectExtent b="0" l="0" r="0" t="0" /><wp:wrapNone /><wp:docPr id="1" name="Figure" /><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1" /></wp:cNvGraphicFramePr><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><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></a:graphicData></a:graphic></wp:inline></w:drawing>"#
181        );
182    }
183
184    #[test]
185    fn test_drawing_build_with_pic_align_right() {
186        let mut pic = Pic::new_with_dimensions(Vec::new(), 320, 240).floating();
187        pic = pic.relative_from_h(RelativeFromHType::Column);
188        pic = pic.relative_from_v(RelativeFromVType::Paragraph);
189        pic = pic.position_h(DrawingPosition::Align(PicAlign::Right));
190        let d = Drawing::new().pic(pic).build();
191        assert_eq!(
192            str::from_utf8(&d).unwrap(),
193            r#"<w:drawing><wp:anchor distT="0" distB="0" distL="0" distR="0" simplePos="0" allowOverlap="0" behindDoc="0" locked="0" layoutInCell="0" relativeHeight="190500"><wp:simplePos x="0" y="0" /><wp:positionH relativeFrom="column"><wp:align>right</wp:align></wp:positionH><wp:positionV relativeFrom="paragraph"><wp:posOffset>0</wp:posOffset></wp:positionV><wp:extent cx="3048000" cy="2286000" /><wp:effectExtent b="0" l="0" r="0" t="0" /><wp:wrapSquare wrapText="bothSides" /><wp:docPr id="1" name="Figure" /><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1" /></wp:cNvGraphicFramePr><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><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></a:graphicData></a:graphic></wp:anchor></w:drawing>"#
194        );
195    }
196
197    #[test]
198    fn test_issue686() {
199        let pic = Pic::new_with_dimensions(Vec::new(), 320, 240)
200            .size(320 * 9525, 240 * 9525)
201            .floating()
202            .offset_x(300 * 9525)
203            .offset_y(400 * 9525);
204
205        let d = Drawing::new().pic(pic).build();
206        assert_eq!(
207            str::from_utf8(&d).unwrap(),
208            r#"<w:drawing><wp:anchor distT="0" distB="0" distL="0" distR="0" simplePos="0" allowOverlap="0" behindDoc="0" locked="0" layoutInCell="0" relativeHeight="190500"><wp:simplePos x="0" y="0" /><wp:positionH relativeFrom="margin"><wp:posOffset>2857500</wp:posOffset></wp:positionH><wp:positionV relativeFrom="margin"><wp:posOffset>3810000</wp:posOffset></wp:positionV><wp:extent cx="3048000" cy="2286000" /><wp:effectExtent b="0" l="0" r="0" t="0" /><wp:wrapSquare wrapText="bothSides" /><wp:docPr id="1" name="Figure" /><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1" /></wp:cNvGraphicFramePr><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><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></a:graphicData></a:graphic></wp:anchor></w:drawing>"#
209        );
210    }
211}