1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
use anyhow::bail;
use quick_xml::events::{BytesStart, Event};
use std::io::Read;
use crate::{
excel::XmlReader,
helper::{string_to_bool, string_to_int, string_to_unsignedint},
raw::drawing::{
scene::scene_3d_type::XlsxScene3DType,
shape::{shape_3d_type::XlsxShape3DType, shape_autofit::XlsxShapeAutofit},
st_types::{STAngle, STCoordinate, STPositiveCoordinate},
},
};
use super::{
flat_text::XlsxFlatText, no_autofit::XlsxNoAutoFit, norm_autofit::XlsxNormAutoFit,
preset_text_warp::XlsxPresetTextWarp,
};
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties?view=openxml-3.0.1
///
/// defines the body properties for the text body within a shape.
///
/// Example:
/// ```
/// <a:bodyPr rot="0" spcFirstLastPara="1" vertOverflow="overflow" horzOverflow="overflow" vert="horz" wrap="square" lIns="50800" tIns="50800" rIns="50800" bIns="50800" numCol="1" spcCol="38100" rtlCol="0" anchor="ctr" upright="0">
/// <a:spAutoFit />
/// <prstTxWarp prst="textArchUp">
/// <a:avLst>
/// <a:gd name="myGuide" fmla="val 2"/>
/// </a:avLst>
/// </prstTxWarp>
/// </a:bodyPr>
/// ```
#[derive(Debug, Clone, PartialEq)]
pub struct XlsxBodyProperties {
// child: extLst (Extension List) Not supported
// Child Elements
// flatTx (No text in 3D scene) §20.1.5.8
pub flat_tx: Option<XlsxFlatText>,
/// NoAutoFit: https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.noautofit?view=openxml-3.0.1
/// This element specifies that text within the text body should not be auto-fit to the bounding box.
/// Auto-fitting is when text within a text box is scaled in order to remain inside the text box.
///
/// Example
/// ```
/// <a:bodyPr wrap="none" rtlCol="0">
/// <a:noAutofit/>
/// </a:bodyPr>
/// ```
// noAutofit (No AutoFit) §21.1.2.1.2
pub no_autofit: Option<XlsxNoAutoFit>,
/// NormalAutoFit: https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.normalautofit?view=openxml-3.0.1
/// This element specifies that text within the text body should be normally auto-fit to the bounding box.
/// If this element is omitted, then noAutofit or auto-fit off is implied.
///
/// Example
/// ```
/// <a:bodyPr wrap="none" rtlCol="0">
/// <a:normAutofit fontScale="92.000%" lnSpcReduction="20.000%"/>
/// </a:bodyPr>
/// ```
// normAutofit (Normal AutoFit) §21.1.2.1.3
pub norm_autofit: Option<XlsxNormAutoFit>,
// prstTxWarp (Preset Text Warp) §20.1.9.19
pub preset_text_warp: Option<XlsxPresetTextWarp>,
// scene3d (3D Scene Properties) §20.1.4.1.26
pub scene3d: Option<XlsxScene3DType>,
// sp3d (Apply 3D shape properties) §20.1.5.12
pub shape3d: Option<XlsxShape3DType>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.shapeautofit?view=openxml-3.0.1
///
/// This element specifies that a shape should be auto-fit to fully contain the text described within it.
/// Auto-fitting is when text within a shape is scaled in order to contain all the text inside.
/// If this element is omitted, then noAutofit or auto-fit off is implied.
///
/// Example:
/// ```
/// <a:bodyPr rot="0" spcFirstLastPara="1" vertOverflow="overflow" horzOverflow="overflow" vert="horz" wrap="square" lIns="50800" tIns="50800" rIns="50800" bIns="50800" numCol="1" spcCol="38100" rtlCol="0" anchor="ctr" upright="0">
/// <a:spAutoFit />
/// </a:bodyPr>
/// ```
// spAutoFit (Shape AutoFit)
pub shape_autofit: Option<XlsxShapeAutofit>,
// attributes
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.anchor?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-anchor
///
/// Specifies the anchoring position of the txBody within the shape.
/// If this attribute is omitted, then a value of t, meaning top, is implied.
///
/// possible values: https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.textanchoringtypevalues?view=openxml-3.0.1
///
/// Example:
/// ```
/// <a:bodyPr anchor="ctr" … />
/// ```
// tag: anchor
pub anchor: Option<String>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.anchorcenter?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-anchorcenter
///
/// Specifies the centering of the text box.
/// If this attribute is omitted, then a value of 0 (False) is implied.
///
/// Example:
/// ```
/// <a:bodyPr anchor="ctr" anchorCtr="1" … />
/// ```
// tag: anchorCtr
pub anchor_center: Option<bool>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.bottominset?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-bottominset
///
/// Specifies the bottom inset of the bounding rectangle.
/// If this attribute is omitted, a value of 45720, meaning 0.05 inches, is implied.
///
/// Example
/// ```
/// <a:bodyPr lIns="91440" tIns="91440" rIns="91440" bIns="91440" … />
/// ```
///
// tag: bIns
pub bottom_inset: Option<STCoordinate>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.compatiblelinespacing?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-compatiblelinespacing
///
/// Specifies that the line spacing for this text body will be decided in a simplistic manner using the font scene.
/// If this attribute is omitted, a value of 0, meaning false, is implied.
// tag: compatLnSpc
pub compatible_line_spacing: Option<bool>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.forceantialias?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-forceantialias
///
/// Forces the text to be rendered anti-aliased regardless of the font size.
/// If this attribute is omitted, then a value of 0, meaning off, is implied.
// tag: forceAA
pub force_anti_alias: Option<bool>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.fromwordart?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-fromwordart
///
/// Specifies that text within this textbox is converted text from a WordArt object.
/// If this attribute is omitted, then a value of 0, meaning off, is implied.
// tag: fromWordArt
pub from_word_art: Option<bool>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.horizontaloverflow?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-horizontaloverflow
///
/// Determines whether the text can flow out of the bounding box horizontally.
/// This is used to determine what will happen in the event that the text within a shape is too large for the bounding box it is contained within.
/// If this attribute is omitted, then a value of `overflow` is implied.
///
/// Possible values: https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.texthorizontaloverflowvalues?view=openxml-3.0.1
// tag: horzOverflow
pub horizontal_overflow: Option<String>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.leftinset?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-leftinset
///
/// Specifies the left inset of the bounding rectangle.
/// If this attribute is omitted, then a value of 91440, meaning 0.1 inches, is implied.
// tag: lIns
pub left_inset: Option<STCoordinate>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.columncount?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-columncount
///
/// Specifies the number of columns of text in the bounding rectangle.
/// If this attribute is omitted, then a value of 1 is implied.
// tag: numCol
pub column_count: Option<u64>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.rightinset?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-rightinset
///
/// Specifies the right inset of the bounding rectangle.
/// If this attribute is omitted, then a value of 91440, meaning 0.1 inches, is implied.
// tag: rIns
pub right_inset: Option<STCoordinate>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.rotation?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-rotation
///
/// Specifies the rotation that is being applied to the text within the bounding box.
/// if this attribute is omitted, then a value of 0 is implied.
// tag: rot
pub rotation: Option<STAngle>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.righttoleftcolumns?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-righttoleftcolumns
///
/// Specifies whether columns are used in a right-to-left or left-to-right order.
/// If this attribute is omitted, then a value of 0 is implied in which case text will start in the leftmost column and flow to the right.
// tag: rtlCol
pub right_to_left_columns: Option<bool>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.columnspacing?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-columnspacing
///
/// Specifies the space between text columns in the text area.
/// If this attribute is omitted, then a value of 0 is implied.
// tag: spcCol
pub column_spacing: Option<STPositiveCoordinate>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.useparagraphspacing?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-useparagraphspacing
///
/// Specifies whether the before and after paragraph spacing defined by the user is to be respected.
/// If this attribute is omitted, then a value of 0, meaning false, is implied.
// tag: spcFirstLastPara
pub use_paragraph_spacing: Option<bool>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.topinset?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-topinset
///
/// Specifies the top inset of the bounding rectangle.
/// If this attribute is omitted, then a value of 45720, meaning 0.05 inches, is implied.
// tag: tIns
pub top_inset: Option<STCoordinate>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.upright?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-upright
///
/// Specifies whether text should remain upright, regardless of the transform applied to it and the accompanying shape transform.
/// If this attribute is omitted, then a value of 0, meaning false, will be implied.
// tag: upright
pub upright: Option<bool>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.vertical?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-vertical
///
/// Determines if the text within the given text body should be displayed vertically.
/// If this attribute is omitted, then a value of `horz`, meaning no vertical text, is implied.
///
/// Possible values: https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.textverticalvalues?view=openxml-3.0.1
// tag: vert
pub vertical: Option<String>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.verticaloverflow?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-verticaloverflow
///
/// Determines whether the text can flow out of the bounding box vertically.
/// If this attribute is omitted, then a value of `overflow` is implied.
///
/// Possible values: https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.textverticaloverflowvalues?view=openxml-3.0.1
// tag: vertOverflow
pub vertical_overflow: Option<String>,
/// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.wrap?view=openxml-3.0.1#documentformat-openxml-drawing-bodyproperties-wrap
///
/// Specifies the wrapping options to be used for this text body.
/// If this attribute is omitted, then a value of `square` is implied which will wrap the text using the bounding text box.
///
/// Possible values: https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.textwrappingvalues?view=openxml-3.0.1
// tag: wrap
pub wrap: Option<String>,
}
impl XlsxBodyProperties {
pub(crate) fn load(reader: &mut XmlReader<impl Read>, e: &BytesStart) -> anyhow::Result<Self> {
let mut buf = Vec::new();
let mut properties = Self {
flat_tx: None,
no_autofit: None,
norm_autofit: None,
preset_text_warp: None,
scene3d: None,
shape3d: None,
shape_autofit: None,
anchor: None,
anchor_center: None,
bottom_inset: None,
compatible_line_spacing: None,
force_anti_alias: None,
from_word_art: None,
horizontal_overflow: None,
left_inset: None,
column_count: None,
right_inset: None,
rotation: None,
right_to_left_columns: None,
column_spacing: None,
use_paragraph_spacing: None,
top_inset: None,
upright: None,
vertical: None,
vertical_overflow: None,
wrap: None,
};
let attributes = e.attributes();
for a in attributes {
match a {
Ok(a) => {
let string_value = String::from_utf8(a.value.to_vec())?;
match a.key.local_name().as_ref() {
b"anchor" => properties.anchor = Some(string_value),
b"anchorCtr" => properties.anchor_center = string_to_bool(&string_value),
b"bIns" => properties.bottom_inset = string_to_int(&string_value),
b"compatLnSpc" => {
properties.compatible_line_spacing = string_to_bool(&string_value)
}
b"forceAA" => properties.force_anti_alias = string_to_bool(&string_value),
b"fromWordArt" => properties.from_word_art = string_to_bool(&string_value),
b"horzOverflow" => properties.horizontal_overflow = Some(string_value),
b"lIns" => properties.left_inset = string_to_int(&string_value),
b"numCol" => properties.column_count = string_to_unsignedint(&string_value),
b"rIns" => properties.right_inset = string_to_int(&string_value),
b"rot" => properties.rotation = string_to_int(&string_value),
b"rtlCol" => {
properties.right_to_left_columns = string_to_bool(&string_value)
}
b"spcCol" => {
properties.column_spacing = string_to_unsignedint(&string_value)
}
b"spcFirstLastPara" => {
properties.use_paragraph_spacing = string_to_bool(&string_value)
}
b"tIns" => properties.top_inset = string_to_int(&string_value),
b"upright" => properties.upright = string_to_bool(&string_value),
b"vert" => properties.vertical = Some(string_value),
b"vertOverflow" => properties.vertical_overflow = Some(string_value),
b"wrap" => properties.wrap = Some(string_value),
_ => {}
}
}
Err(error) => {
bail!(error.to_string())
}
}
}
loop {
buf.clear();
match reader.read_event_into(&mut buf) {
Ok(Event::Start(ref e)) if e.local_name().as_ref() == b"extLst" => {
let _ = reader.read_to_end_into(e.to_end().to_owned().name(), &mut Vec::new());
}
Ok(Event::Start(ref e)) if e.local_name().as_ref() == b"flatTx" => {
properties.flat_tx = Some(XlsxFlatText::load(e)?);
}
Ok(Event::Start(ref e)) if e.local_name().as_ref() == b"noAutofit" => {
properties.no_autofit = Some(true);
}
Ok(Event::Start(ref e)) if e.local_name().as_ref() == b"normAutofit" => {
properties.norm_autofit = Some(XlsxNormAutoFit::load(e)?);
}
Ok(Event::Start(ref e)) if e.local_name().as_ref() == b"prstTxWarp" => {
properties.preset_text_warp = Some(XlsxPresetTextWarp::load(reader, e)?);
}
Ok(Event::Start(ref e)) if e.local_name().as_ref() == b"scene3d" => {
properties.scene3d = Some(XlsxScene3DType::load(reader)?);
}
Ok(Event::Start(ref e)) if e.local_name().as_ref() == b"sp3d" => {
properties.shape3d = Some(XlsxShape3DType::load(reader, e)?);
}
Ok(Event::Start(ref e)) if e.local_name().as_ref() == b"spAutoFit" => {
properties.shape_autofit = Some(true);
}
Ok(Event::End(ref e)) if e.local_name().as_ref() == b"bodyPr" => break,
Ok(Event::Eof) => bail!("unexpected end of file."),
Err(e) => bail!(e.to_string()),
_ => (),
}
}
if properties.norm_autofit.is_none() && properties.shape_autofit.is_none() {
properties.no_autofit = Some(true);
}
Ok(properties)
}
}