Skip to main content

sheetkit_xml/
drawing.rs

1//! DrawingML Spreadsheet Drawing XML schema structures.
2//!
3//! Represents `xl/drawings/drawing{N}.xml` in the OOXML package.
4
5use serde::{Deserialize, Serialize};
6
7use crate::namespaces;
8
9/// Root element for a spreadsheet drawing part.
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11#[serde(rename = "wsDr")]
12pub struct WsDr {
13    #[serde(rename = "@xmlns:xdr")]
14    pub xmlns_xdr: String,
15
16    #[serde(rename = "@xmlns:a")]
17    pub xmlns_a: String,
18
19    #[serde(rename = "@xmlns:r")]
20    pub xmlns_r: String,
21
22    #[serde(rename = "xdr:twoCellAnchor", default)]
23    pub two_cell_anchors: Vec<TwoCellAnchor>,
24
25    #[serde(rename = "xdr:oneCellAnchor", default)]
26    pub one_cell_anchors: Vec<OneCellAnchor>,
27}
28
29impl Default for WsDr {
30    fn default() -> Self {
31        Self {
32            xmlns_xdr: namespaces::DRAWING_ML_SPREADSHEET.to_string(),
33            xmlns_a: namespaces::DRAWING_ML.to_string(),
34            xmlns_r: namespaces::RELATIONSHIPS.to_string(),
35            two_cell_anchors: vec![],
36            one_cell_anchors: vec![],
37        }
38    }
39}
40
41/// An anchor defined by two cell markers (from/to).
42#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
43pub struct TwoCellAnchor {
44    #[serde(rename = "xdr:from")]
45    pub from: MarkerType,
46
47    #[serde(rename = "xdr:to")]
48    pub to: MarkerType,
49
50    #[serde(rename = "xdr:graphicFrame", skip_serializing_if = "Option::is_none")]
51    pub graphic_frame: Option<GraphicFrame>,
52
53    #[serde(rename = "xdr:pic", skip_serializing_if = "Option::is_none")]
54    pub pic: Option<Picture>,
55
56    #[serde(rename = "xdr:clientData")]
57    pub client_data: ClientData,
58}
59
60/// An anchor defined by one cell marker and an extent.
61#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
62pub struct OneCellAnchor {
63    #[serde(rename = "xdr:from")]
64    pub from: MarkerType,
65
66    #[serde(rename = "xdr:ext")]
67    pub ext: Extent,
68
69    #[serde(rename = "xdr:pic", skip_serializing_if = "Option::is_none")]
70    pub pic: Option<Picture>,
71
72    #[serde(rename = "xdr:clientData")]
73    pub client_data: ClientData,
74}
75
76/// A cell marker indicating column, column offset, row, and row offset.
77#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
78pub struct MarkerType {
79    #[serde(rename = "xdr:col")]
80    pub col: u32,
81
82    #[serde(rename = "xdr:colOff")]
83    pub col_off: u64,
84
85    #[serde(rename = "xdr:row")]
86    pub row: u32,
87
88    #[serde(rename = "xdr:rowOff")]
89    pub row_off: u64,
90}
91
92/// Extent (size) in EMU (English Metric Units).
93#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
94pub struct Extent {
95    #[serde(rename = "@cx")]
96    pub cx: u64,
97
98    #[serde(rename = "@cy")]
99    pub cy: u64,
100}
101
102/// Graphic frame containing a chart reference.
103#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
104pub struct GraphicFrame {
105    #[serde(rename = "xdr:nvGraphicFramePr")]
106    pub nv_graphic_frame_pr: NvGraphicFramePr,
107
108    #[serde(rename = "xdr:xfrm")]
109    pub xfrm: Xfrm,
110
111    #[serde(rename = "a:graphic")]
112    pub graphic: Graphic,
113}
114
115/// Non-visual graphic frame properties.
116#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
117pub struct NvGraphicFramePr {
118    #[serde(rename = "xdr:cNvPr")]
119    pub c_nv_pr: CNvPr,
120
121    #[serde(rename = "xdr:cNvGraphicFramePr")]
122    pub c_nv_graphic_frame_pr: CNvGraphicFramePr,
123}
124
125/// Common non-visual properties (id and name).
126#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
127pub struct CNvPr {
128    #[serde(rename = "@id")]
129    pub id: u32,
130
131    #[serde(rename = "@name")]
132    pub name: String,
133}
134
135/// Non-visual graphic frame properties (empty marker).
136#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
137pub struct CNvGraphicFramePr {}
138
139/// Transform (position and size) for a graphic frame.
140#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
141pub struct Xfrm {
142    #[serde(rename = "a:off")]
143    pub off: Offset,
144
145    #[serde(rename = "a:ext")]
146    pub ext: AExt,
147}
148
149/// Offset position.
150#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
151pub struct Offset {
152    #[serde(rename = "@x")]
153    pub x: i64,
154
155    #[serde(rename = "@y")]
156    pub y: i64,
157}
158
159/// DrawingML extent (width/height).
160#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
161pub struct AExt {
162    #[serde(rename = "@cx")]
163    pub cx: u64,
164
165    #[serde(rename = "@cy")]
166    pub cy: u64,
167}
168
169/// Graphic element containing chart data.
170#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
171pub struct Graphic {
172    #[serde(rename = "a:graphicData")]
173    pub graphic_data: GraphicData,
174}
175
176/// Graphic data referencing a chart.
177#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
178pub struct GraphicData {
179    #[serde(rename = "@uri")]
180    pub uri: String,
181
182    #[serde(rename = "c:chart")]
183    pub chart: ChartRef,
184}
185
186/// Reference to a chart part via relationship ID.
187#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
188pub struct ChartRef {
189    #[serde(rename = "@xmlns:c")]
190    pub xmlns_c: String,
191
192    #[serde(rename = "@r:id")]
193    pub r_id: String,
194}
195
196/// Picture element for images.
197#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
198pub struct Picture {
199    #[serde(rename = "xdr:nvPicPr")]
200    pub nv_pic_pr: NvPicPr,
201
202    #[serde(rename = "xdr:blipFill")]
203    pub blip_fill: BlipFill,
204
205    #[serde(rename = "xdr:spPr")]
206    pub sp_pr: SpPr,
207}
208
209/// Non-visual picture properties.
210#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
211pub struct NvPicPr {
212    #[serde(rename = "xdr:cNvPr")]
213    pub c_nv_pr: CNvPr,
214
215    #[serde(rename = "xdr:cNvPicPr")]
216    pub c_nv_pic_pr: CNvPicPr,
217}
218
219/// Non-visual picture-specific properties (empty marker).
220#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
221pub struct CNvPicPr {}
222
223/// Blip fill referencing an embedded image.
224#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
225pub struct BlipFill {
226    #[serde(rename = "a:blip")]
227    pub blip: Blip,
228
229    #[serde(rename = "a:stretch")]
230    pub stretch: Stretch,
231}
232
233/// Blip (Binary Large Image or Picture) reference.
234#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
235pub struct Blip {
236    #[serde(rename = "@r:embed")]
237    pub r_embed: String,
238}
239
240/// Stretch fill mode.
241#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
242pub struct Stretch {
243    #[serde(rename = "a:fillRect")]
244    pub fill_rect: FillRect,
245}
246
247/// Fill rectangle (empty element indicating full fill).
248#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
249pub struct FillRect {}
250
251/// Shape properties for a picture.
252#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
253pub struct SpPr {
254    #[serde(rename = "a:xfrm")]
255    pub xfrm: Xfrm,
256
257    #[serde(rename = "a:prstGeom")]
258    pub prst_geom: PrstGeom,
259}
260
261/// Preset geometry (e.g., rectangle).
262#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
263pub struct PrstGeom {
264    #[serde(rename = "@prst")]
265    pub prst: String,
266}
267
268/// Client data (empty element required by spec).
269#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
270pub struct ClientData {}
271
272#[cfg(test)]
273mod tests {
274    use super::*;
275
276    #[test]
277    fn test_ws_dr_default() {
278        let dr = WsDr::default();
279        assert_eq!(dr.xmlns_xdr, namespaces::DRAWING_ML_SPREADSHEET);
280        assert_eq!(dr.xmlns_a, namespaces::DRAWING_ML);
281        assert_eq!(dr.xmlns_r, namespaces::RELATIONSHIPS);
282        assert!(dr.two_cell_anchors.is_empty());
283        assert!(dr.one_cell_anchors.is_empty());
284    }
285
286    #[test]
287    fn test_marker_type_serialize() {
288        let marker = MarkerType {
289            col: 1,
290            col_off: 0,
291            row: 2,
292            row_off: 0,
293        };
294        let xml = quick_xml::se::to_string(&marker).unwrap();
295        assert!(xml.contains("<xdr:col>1</xdr:col>"));
296        assert!(xml.contains("<xdr:row>2</xdr:row>"));
297    }
298
299    #[test]
300    fn test_extent_serialize() {
301        let ext = Extent {
302            cx: 9525000,
303            cy: 4762500,
304        };
305        let xml = quick_xml::se::to_string(&ext).unwrap();
306        assert!(xml.contains("cx=\"9525000\""));
307        assert!(xml.contains("cy=\"4762500\""));
308    }
309
310    #[test]
311    fn test_chart_ref_serialize() {
312        let chart_ref = ChartRef {
313            xmlns_c: namespaces::DRAWING_ML_CHART.to_string(),
314            r_id: "rId1".to_string(),
315        };
316        let xml = quick_xml::se::to_string(&chart_ref).unwrap();
317        assert!(xml.contains("r:id=\"rId1\""));
318    }
319
320    #[test]
321    fn test_blip_serialize() {
322        let blip = Blip {
323            r_embed: "rId2".to_string(),
324        };
325        let xml = quick_xml::se::to_string(&blip).unwrap();
326        assert!(xml.contains("r:embed=\"rId2\""));
327    }
328
329    #[test]
330    fn test_prst_geom_serialize() {
331        let geom = PrstGeom {
332            prst: "rect".to_string(),
333        };
334        let xml = quick_xml::se::to_string(&geom).unwrap();
335        assert!(xml.contains("prst=\"rect\""));
336    }
337}