docx_rs/documents/elements/
drawing.rs1use 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 .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}