msoffice_shared/drawingml/text/
paragraphs.rs

1use super::{
2    bullet::{TextBullet, TextBulletColor, TextBulletSize, TextBulletTypeface},
3    runformatting::{TextFont, TextRun, TextUnderlineFill, TextUnderlineLine},
4};
5use crate::drawingml::{
6    colors::Color,
7    core::{Hyperlink, LineProperties},
8    shapeprops::{EffectProperties, FillProperties},
9    simpletypes::{
10        Coordinate32, Guid, Percentage, TextAlignType, TextCapsType, TextFontAlignType, TextFontSize, TextIndent,
11        TextIndentLevelType, TextLanguageID, TextMargin, TextNonNegativePoint, TextPoint, TextSpacingPercent,
12        TextSpacingPoint, TextStrikeType, TextTabAlignType, TextUnderlineType,
13    },
14};
15use crate::error::{Limit, LimitViolationError, MissingAttributeError, MissingChildNodeError, NotGroupMemberError};
16use crate::xml::{parse_xml_bool, XmlNode};
17
18pub type Result<T> = ::std::result::Result<T, Box<dyn (::std::error::Error)>>;
19
20#[derive(Default, Debug, Clone)]
21pub struct TextLineBreak {
22    pub char_properties: Option<Box<TextCharacterProperties>>,
23}
24
25impl TextLineBreak {
26    pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
27        let char_properties = match xml_node.child_nodes.get(0) {
28            Some(node) => Some(Box::new(TextCharacterProperties::from_xml_element(node)?)),
29            None => None,
30        };
31
32        Ok(Self { char_properties })
33    }
34}
35
36#[derive(Debug, Clone)]
37pub struct TextField {
38    /// Specifies the unique to this document, host specified token that is used to identify the
39    /// field. This token is generated when the text field is created and persists in the file as the
40    /// same token until the text field is removed. Any application should check the document
41    /// for conflicting tokens before assigning a new token to a text field.
42    pub id: Guid,
43
44    /// Specifies the type of text that should be used to update this text field. This is used to
45    /// inform the rendering application what text it should use to update this text field. There
46    /// are no specific syntax restrictions placed on this attribute. The generating application can
47    /// use it to represent any text that should be updated before rendering the presentation.
48    ///
49    /// Reserved values:
50    ///
51    /// |Value          |Description                                            |
52    /// |---------------|-------------------------------------------------------|
53    /// |slidenum       |presentation slide number                              |
54    /// |datetime       |default date time format for the rendering application |
55    /// |datetime1      |MM/DD/YYYY date time format                            |
56    /// |datetime2      |Day, Month DD, YYYY date time format                   |
57    /// |datetime3      |DD Month YYYY date time format                         |
58    /// |datetime4      |Month DD, YYYY date time format                        |
59    /// |datetime5      |DD-Mon-YY date time format                             |
60    /// |datetime6      |Month YY date time format                              |
61    /// |datetime7      |Mon-YY date time format                                |
62    /// |datetime8      |MM/DD/YYYY hh:mm AM/PM date time format                |
63    /// |datetime9      |MM/DD/YYYY hh:mm:ss AM/PM date time format             |
64    /// |datetime10     |hh:mm date time format                                 |
65    /// |datetime11     |hh:mm:ss date time format                              |
66    /// |datetime12     |hh:mm AM/PM date time format                           |
67    /// |datetime13     |hh:mm:ss: AM/PM date time format                       |
68    pub field_type: Option<String>,
69
70    /// This element contains all run level text properties for the text runs within a containing paragraph.
71    ///
72    /// # Xml example
73    ///
74    /// ```xml
75    /// <a:p>
76    ///   …
77    ///   <a:rPr u="sng"/>
78    ///   …
79    ///   <a:t>Some Text</a:t>
80    ///   …
81    /// </a:p>
82    /// ```
83    ///
84    /// The run of text described above is formatting with a single underline of text matching color.
85    pub char_properties: Option<Box<TextCharacterProperties>>,
86
87    /// Specifies the paragraph properties for this text field
88    pub paragraph_properties: Option<Box<TextParagraph>>,
89
90    /// The text of this text field.
91    pub text: Option<String>,
92}
93
94impl TextField {
95    pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
96        let mut id = None;
97        let mut field_type = None;
98
99        for (attr, value) in &xml_node.attributes {
100            match attr.as_str() {
101                "id" => id = Some(value.clone()),
102                "type" => field_type = Some(value.clone()),
103                _ => (),
104            }
105        }
106
107        let id = id.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "id"))?;
108
109        let mut char_properties = None;
110        let mut paragraph_properties = None;
111        let mut text = None;
112
113        for child_node in &xml_node.child_nodes {
114            match child_node.local_name() {
115                "rPr" => char_properties = Some(Box::new(TextCharacterProperties::from_xml_element(child_node)?)),
116                "pPr" => paragraph_properties = Some(Box::new(TextParagraph::from_xml_element(child_node)?)),
117                "t" => text = child_node.text.clone(),
118                _ => (),
119            }
120        }
121
122        Ok(Self {
123            id,
124            field_type,
125            char_properties,
126            paragraph_properties,
127            text,
128        })
129    }
130}
131
132#[derive(Default, Debug, Clone)]
133pub struct TextParagraphProperties {
134    /// Specifies the left margin of the paragraph. This is specified in addition to the text body
135    /// inset and applies only to this text paragraph. That is the text body inset and the marL
136    /// attributes are additive with respect to the text position. If this attribute is omitted, then a
137    /// value of 347663 is implied.
138    pub margin_left: Option<TextMargin>,
139
140    /// Specifies the right margin of the paragraph. This is specified in addition to the text body
141    /// inset and applies only to this text paragraph. That is the text body inset and the marR
142    /// attributes are additive with respect to the text position. If this attribute is omitted, then a
143    /// value of 0 is implied.
144    pub margin_right: Option<TextMargin>,
145
146    /// Specifies the particular level text properties that this paragraph follows. The value for this
147    /// attribute is numerical and formats the text according to the corresponding level
148    /// paragraph properties that are listed within the lstStyle element. Since there are nine
149    /// separate level properties defined, this tag has an effective range of 0-8 = 9 available
150    /// values.
151    ///
152    /// # Xml example
153    ///
154    /// Consider the following DrawingML. This would specify that this paragraph
155    /// should follow the lvl2pPr formatting style because once again lvl="1" is considered to be
156    /// level 2.
157    /// ```xml
158    /// <p:txBody>
159    ///   …
160    ///   <a:p>
161    ///     <a:pPr lvl="1" …/>
162    ///     …
163    ///     <a:t>Sample text</a:t>
164    ///     …
165    ///   </a:p>
166    /// </p:txBody>
167    /// ```
168    ///
169    /// # Note
170    ///
171    /// To resolve conflicting paragraph properties the linear hierarchy of paragraph
172    /// properties should be examined starting first with the pPr element. The rule here is that
173    /// properties that are defined at a level closer to the actual text should take precedence.
174    /// That is if there is a conflicting property between the pPr and lvl1pPr elements then the
175    /// pPr property should take precedence because in the property hierarchy it is closer to the
176    /// actual text being represented.
177    pub level: Option<TextIndentLevelType>,
178
179    /// Specifies the indent size that is applied to the first line of text in the paragraph. An
180    /// indentation of 0 is considered to be at the same location as marL attribute. If this
181    /// attribute is omitted, then a value of -342900 is implied.
182    ///
183    /// # Xml example
184    ///
185    /// Consider the scenario where the user now wanted to add a paragraph
186    /// indentation to the first line of text in their two column format book.
187    /// ```xml
188    /// <p:txBody>
189    ///   <a:bodyPr numCol="2" spcCol="914400"…/>
190    ///     <a:normAutofit/>
191    ///   </a:bodyPr>
192    ///   …
193    ///   <a:p>
194    ///     <a:pPr marL="0" indent="571500" algn="just">
195    ///       <a:buNone/>
196    ///     </a:pPr>
197    ///     …
198    ///     <a:t>Here is some…</a:t>
199    ///     …
200    ///   </a:p>
201    /// </p:txBody>
202    /// ```
203    ///
204    /// By adding the indent attribute the user has effectively added a first line indent to this
205    /// paragraph of text.
206    pub indent: Option<TextIndent>,
207
208    /// Specifies the alignment that is to be applied to the paragraph. Possible values for this
209    /// include left, right, centered, justified and distributed. If this attribute is omitted, then a
210    /// value of left is implied.
211    ///
212    /// # Xml example
213    ///
214    /// Consider the case where the user wishes to have two columns of text that
215    /// have a justified alignment, much like text within a book. The following DrawingML could
216    /// describe this.
217    /// ```xml
218    /// <p:txBody>
219    ///   <a:bodyPr numCol="2" spcCol="914400"…/>
220    ///     <a:normAutofit/>
221    ///   </a:bodyPr>
222    ///   …
223    ///   <a:p>
224    ///     <a:pPr marL="0" algn="just">
225    ///       <a:buNone/>
226    ///     </a:pPr>
227    ///     …
228    ///     <a:t>Sample Text …</a:t>
229    ///     …
230    ///   </a:p>
231    /// </p:txBody>
232    /// ```
233    pub align: Option<TextAlignType>,
234
235    /// Specifies the default size for a tab character within this paragraph. This attribute should
236    /// be used to describe the spacing of tabs within the paragraph instead of a leading
237    /// indentation tab. For indentation tabs there are the marL and indent attributes to assist
238    /// with this.
239    ///
240    /// # Xml example
241    ///
242    /// Consider the case where a paragraph contains numerous tabs that need to be
243    /// of a specific size. The following DrawingML would describe this.
244    /// ```xml
245    /// <p:txBody>
246    ///   …
247    ///   <a:p>
248    ///     <a:pPr defTabSz="376300" …/>
249    ///     …
250    ///     <a:t>Sample Text …</a:t>
251    ///     …
252    ///   </a:p>
253    /// </p:txBody>
254    /// ```
255    pub default_tab_size: Option<Coordinate32>,
256
257    /// Specifies whether the text is right-to-left or left-to-right in its flow direction. If this
258    /// attribute is omitted, then a value of 0, or left-to-right is implied.
259    ///
260    /// # Xml example
261    ///
262    /// Consider the following example of a text body with two lines of text. In this
263    /// example, both lines contain English and Arabic text, however, the second line has the
264    /// rtl attribute set to true whereas the first line does not set the rtl attribute.
265    ///
266    /// ```xml
267    /// <p:txBody>
268    ///   …
269    ///   <a:p>
270    ///     <a:r>
271    ///       <a:t>Test </a:t>
272    ///     </a:r>
273    ///     <a:r>
274    ///       <a:rPr>
275    ///         <a:rtl w:val="1"/>
276    ///       </a:rPr>
277    ///       <a:t> تجربة </a:t>
278    ///     </a:r>
279    ///   </a:p>
280    ///   <a:p>
281    ///     <a:pPr rtl="1"/>
282    ///     <a:r>
283    ///       <a:rPr>
284    ///         <a:rtl w:val="0"/>
285    ///       </a:rPr>
286    ///       <a:t>Test </a:t>
287    ///     </a:r>
288    ///     <a:r>
289    ///       <a:t> تجربة </a:t>
290    ///     </a:r>
291    ///   </a:p>
292    /// </p:txBody>
293    /// ```
294    pub rtl: Option<bool>,
295
296    /// Specifies whether an East Asian word can be broken in half and wrapped onto the next
297    /// line without a hyphen being added. To determine whether an East Asian word can be
298    /// broken the presentation application would use the kinsoku settings here. This attribute
299    /// is to be used specifically when there is a word that cannot be broken into multiple pieces
300    /// without a hyphen. That is it is not present within the existence of normal breakable East
301    /// Asian words but is when a special case word arises that should not be broken for a line
302    /// break. If this attribute is omitted, then a value of 1 or true is implied.
303    ///
304    /// # Xml example
305    ///
306    /// Consider the case where the presentation contains a long word that must not
307    /// be divided with a line break. Instead it should be placed, in whole on a new line so that it
308    /// can fit. The picture below shows a normal paragraph where a long word has been broken
309    /// for a line break. The second picture shown below shows that same paragraph with the
310    /// long word specified to not allow a line break. The resulting DrawingML is as follows.
311    /// ```xml
312    /// <p:txBody>
313    ///   …
314    ///   <a:p>
315    ///     <a:pPr eaLnBrk="0" …/>
316    ///     …
317    ///     <a:t>Sample text (Long word)</a:t>
318    ///     …
319    ///   </a:p>
320    /// </p:txBody>
321    /// ```
322    pub east_asian_line_break: Option<bool>,
323
324    /// Determines where vertically on a line of text the actual words are positioned. This deals
325    /// with vertical placement of the characters with respect to the baselines. For instance
326    /// having text anchored to the top baseline, anchored to the bottom baseline, centered in
327    /// between, etc. To understand this attribute and it's use it is helpful to understand what
328    /// baselines are. A diagram describing these different cases is shown below. If this attribute
329    /// is omitted, then a value of base is implied.
330    ///
331    /// # Xml example
332    ///
333    /// Consider the case where the user wishes to represent the chemical compound
334    /// of a water molecule. For this they need to make sure the H, the 2, and the O are all in the
335    /// correct position and are of the correct size. The results below can be achieved through
336    /// the DrawingML shown below.
337    ///
338    /// ```xml
339    /// <a:txtBody>
340    ///   …
341    ///   <a:pPr fontAlgn="b" …/>
342    ///   …
343    ///   <a:r>
344    ///     <a:rPr …/>
345    ///     <a:t>H </a:t>
346    ///   </a:r>
347    ///   <a:r>
348    ///     <a:rPr sz="1200" …/>
349    ///     <a:t>2</a:t>
350    ///   </a:r>
351    ///   <a:r>
352    ///     <a:rPr …/>
353    ///     <a:t>O</a:t>
354    ///   </a:r>
355    ///   …
356    /// </p:txBody>
357    /// ```
358    pub font_align: Option<TextFontAlignType>,
359
360    /// Specifies whether a Latin word can be broken in half and wrapped onto the next line
361    /// without a hyphen being added. This attribute is to be used specifically when there is a
362    /// word that cannot be broken into multiple pieces without a hyphen. It is not present
363    /// within the existence of normal breakable Latin words but is when a special case word
364    /// arises that should not be broken for a line break. If this attribute is omitted, then a value
365    /// of 1 or true is implied.
366    ///
367    /// # Xml example
368    ///
369    /// Consider the case where the presentation contains a long word that must not
370    /// be divided with a line break. Instead it should be placed, in whole on a new line so that it
371    /// can fit. The picture below shows a normal paragraph where a long word has been broken
372    /// for a line break. The second picture shown below shows that same paragraph with the
373    /// long word specified to not allow a line break. The resulting DrawingML is as follows.
374    ///
375    /// ```xml
376    /// <p:txBody>
377    ///   …
378    ///   <a:p>
379    ///     <a:pPr latinLnBrk="0" …/>
380    ///     …
381    ///     <a:t>Sample text (Long word)</a:t>
382    ///     …
383    ///   </a:p>
384    /// </p:txBody>
385    /// ```
386    pub latin_line_break: Option<bool>,
387
388    /// Specifies whether punctuation is to be forcefully laid out on a line of text or put on a
389    /// different line of text. That is, if there is punctuation at the end of a run of text that should
390    /// be carried over to a separate line does it actually get carried over. A true value allows for
391    /// hanging punctuation forcing the punctuation to not be carried over and a value of false
392    /// allows the punctuation to be carried onto the next text line. If this attribute is omitted,
393    /// then a value of 0, or false is implied.
394    pub hanging_punctuations: Option<bool>,
395
396    /// This element specifies the vertical line spacing that is to be used within a paragraph. This can be specified in two
397    /// different ways, percentage spacing and font point spacing. If this element is omitted then the spacing between
398    /// two lines of text should be determined by the point size of the largest piece of text within a line.
399    ///
400    /// # Xml example
401    ///
402    /// ```xml
403    /// <p:txBody>
404    ///   <a:p>
405    ///     <a:pPr>
406    ///       <a:lnSpc>
407    ///         <a:spcPct val="200%"/>
408    ///       </a:lnSpc>
409    ///     </a:pPr>
410    ///     <a:r>
411    ///       <a:rPr lang="en-US" dirty="0" smtClean="0"/>
412    ///       <a:t>Some</a:t>
413    ///     </a:r>
414    ///     <a:br>
415    ///       <a:rPr lang="en-US" smtClean="0"/>
416    ///     </a:br>
417    ///     <a:r>
418    ///      <a:rPr lang="en-US" dirty="0" smtClean="0"/>
419    ///      <a:t>Text</a:t>
420    ///     </a:r>
421    ///   </a:p>
422    /// </p:txBody>
423    /// ```
424    ///
425    /// This paragraph has two lines of text that have percentage based vertical spacing. This kind of spacing should
426    /// change based on the size of the text involved as its size is calculated as a percentage of this.
427    pub line_spacing: Option<TextSpacing>,
428
429    /// This element specifies the amount of vertical white space that is present before a paragraph. This space is
430    /// specified in either percentage or points via the child elements spcPct and spcPts.
431    ///
432    /// # Xml example
433    ///
434    /// ```xml
435    /// <p:txBody>
436    ///   …
437    ///   <a:p>
438    ///     <a:pPr …>
439    ///       <a:spcBef>
440    ///         <a:spcPts val="1800"/>
441    ///       </a:spcBef>
442    ///       <a:spcAft>
443    ///         <a:spcPts val="600"/>
444    ///       </a:spcAft>
445    ///     </a:pPr>
446    ///     …
447    ///     <a:t>Sample Text</a:t>
448    ///     …
449    ///   </a:p>
450    ///   …
451    /// </p:txBody>
452    /// ```
453    ///
454    /// The above paragraph of text is formatted to have a spacing both before and after the paragraph text. The
455    /// spacing before is a size of 18 points, or value=1800 and the spacing after is a size of 6 points, or value=600.
456    pub space_before: Option<TextSpacing>,
457
458    /// This element specifies the amount of vertical white space that is present after a paragraph. This space is
459    /// specified in either percentage or points via the child elements spcPct and spcPts.
460    ///
461    /// # Xml example
462    ///
463    /// ```xml
464    /// <p:txBody>
465    ///   …
466    ///   <a:p>
467    ///     <a:pPr …>
468    ///       <a:spcBef>
469    ///         <a:spcPts val="1800"/>
470    ///       </a:spcBef>
471    ///       <a:spcAft>
472    ///         <a:spcPts val="600"/>
473    ///       </a:spcAft>
474    ///     </a:pPr>
475    ///     …
476    ///     <a:t>Sample Text</a:t>
477    ///     …
478    ///   </a:p>
479    ///   …
480    /// </p:txBody>
481    /// ```
482    ///
483    /// The above paragraph of text is formatted to have a spacing both before and after the paragraph text. The
484    /// spacing before is a size of 18 points, or value=1800 and the spacing after is a size of 6 points, or value=600.
485    pub space_after: Option<TextSpacing>,
486
487    /// Specifies the color of the bullet for this paragraph.
488    pub bullet_color: Option<TextBulletColor>,
489
490    /// Specifies the size of the bullet for this paragraph.
491    pub bullet_size: Option<TextBulletSize>,
492
493    /// Specifies the font properties of the bullet for this paragraph.
494    pub bullet_typeface: Option<TextBulletTypeface>,
495
496    /// Specifies the bullet's properties for this paragraph.
497    pub bullet: Option<TextBullet>,
498
499    /// This element specifies the list of all tab stops that are to be used within a paragraph. These tabs should be used
500    /// when describing any custom tab stops within the document. If these are not specified then the default tab stops
501    /// of the generating application should be used.
502    pub tab_stop_list: Option<Vec<TextTabStop>>,
503
504    /// This element contains all default run level text properties for the text runs within a containing paragraph. These
505    /// properties are to be used when overriding properties have not been defined within the rPr element.
506    ///
507    /// # Xml example
508    ///
509    /// ```xml
510    /// <a:p>
511    ///   …
512    ///   <a:rPr u="sng"/>
513    ///   …
514    ///   <a:t>Some Text</a:t>
515    ///   …
516    /// </a:p>
517    /// ```
518    ///
519    /// The run of text described above is formatting with a single underline of text matching color.
520    pub default_run_properties: Option<Box<TextCharacterProperties>>,
521}
522
523impl TextParagraphProperties {
524    pub fn from_xml_element(xml_node: &XmlNode) -> Result<TextParagraphProperties> {
525        let mut instance: Self = Default::default();
526
527        for (attr, value) in &xml_node.attributes {
528            match attr.as_str() {
529                "marL" => instance.margin_left = Some(value.parse()?),
530                "marR" => instance.margin_right = Some(value.parse()?),
531                "lvl" => instance.level = Some(value.parse()?),
532                "indent" => instance.indent = Some(value.parse()?),
533                "algn" => instance.align = Some(value.parse()?),
534                "defTabSz" => instance.default_tab_size = Some(value.parse()?),
535                "rtl" => instance.rtl = Some(parse_xml_bool(value)?),
536                "eaLnBrk" => instance.east_asian_line_break = Some(parse_xml_bool(value)?),
537                "fontAlgn" => instance.font_align = Some(value.parse()?),
538                "latinLnBrk" => instance.latin_line_break = Some(parse_xml_bool(value)?),
539                "hangingPunct" => instance.hanging_punctuations = Some(parse_xml_bool(value)?),
540                _ => (),
541            }
542        }
543
544        for child_node in &xml_node.child_nodes {
545            if TextBulletColor::is_choice_member(child_node.local_name()) {
546                instance.bullet_color = Some(TextBulletColor::from_xml_element(child_node)?);
547            } else if TextBulletColor::is_choice_member(child_node.local_name()) {
548                instance.bullet_size = Some(TextBulletSize::from_xml_element(child_node)?);
549            } else if TextBulletTypeface::is_choice_member(child_node.local_name()) {
550                instance.bullet_typeface = Some(TextBulletTypeface::from_xml_element(child_node)?);
551            } else if TextBullet::is_choice_member(child_node.local_name()) {
552                instance.bullet = Some(TextBullet::from_xml_element(child_node)?);
553            } else {
554                match child_node.local_name() {
555                    "lnSpc" => {
556                        let line_spacing_node = child_node
557                            .child_nodes
558                            .get(0)
559                            .ok_or_else(|| MissingChildNodeError::new(child_node.name.clone(), "lnSpc child"))?;
560                        instance.line_spacing = Some(TextSpacing::from_xml_element(line_spacing_node)?);
561                    }
562                    "spcBef" => {
563                        let space_before_node = child_node
564                            .child_nodes
565                            .get(0)
566                            .ok_or_else(|| MissingChildNodeError::new(child_node.name.clone(), "spcBef child"))?;
567                        instance.space_before = Some(TextSpacing::from_xml_element(space_before_node)?);
568                    }
569                    "spcAft" => {
570                        let space_after_node = child_node
571                            .child_nodes
572                            .get(0)
573                            .ok_or_else(|| MissingChildNodeError::new(child_node.name.clone(), "spcAft child"))?;
574                        instance.space_after = Some(TextSpacing::from_xml_element(space_after_node)?);
575                    }
576                    "tabLst" => {
577                        let mut vec = Vec::new();
578
579                        for tab_node in &child_node.child_nodes {
580                            vec.push(TextTabStop::from_xml_element(tab_node)?);
581                        }
582
583                        if vec.len() > 32 {
584                            return Err(Box::new(LimitViolationError::new(
585                                xml_node.name.clone(),
586                                "tabLst",
587                                Limit::Value(0),
588                                Limit::Value(32),
589                                vec.len() as u32,
590                            )));
591                        }
592
593                        instance.tab_stop_list = Some(vec);
594                    }
595                    "defRPr" => {
596                        instance.default_run_properties =
597                            Some(Box::new(TextCharacterProperties::from_xml_element(child_node)?))
598                    }
599                    _ => (),
600                }
601            }
602        }
603
604        Ok(instance)
605    }
606}
607
608#[derive(Default, Debug, Clone)]
609pub struct TextParagraph {
610    /// This element contains all paragraph level text properties for the containing paragraph. These paragraph
611    /// properties should override any and all conflicting properties that are associated with the paragraph in question.
612    ///
613    /// # Xml example
614    ///
615    /// ```xml
616    /// <a:p>
617    ///   <a:pPr marL="0" algn="ctr">
618    ///     <a:buNone/>
619    ///   </a:pPr>
620    ///   …
621    ///   <a:t>Some Text</a:t>
622    ///   …
623    /// </a:p>
624    /// ```
625    ///
626    /// The paragraph described above is formatting with a left margin of 0 and has all of text runs contained within it
627    /// centered about the horizontal median of the bounding box for the text body.
628    ///
629    /// # Note
630    ///
631    /// To resolve conflicting paragraph properties the linear hierarchy of paragraph properties should be
632    /// examined starting first with the pPr element. The rule here is that properties that are defined at a level closer to
633    /// the actual text should take precedence. That is if there is a conflicting property between the pPr and lvl1pPr
634    /// elements then the pPr property should take precedence because in the property hierarchy it is closer to the
635    /// actual text being represented.
636    pub properties: Option<Box<TextParagraphProperties>>,
637
638    /// The list of text runs in this paragraph.
639    pub text_run_list: Vec<TextRun>,
640
641    /// This element specifies the text run properties that are to be used if another run is inserted after the last run
642    /// specified. This effectively saves the run property state so that it can be applied when the user enters additional
643    /// text. If this element is omitted, then the application can determine which default properties to apply. It is
644    /// recommended that this element be specified at the end of the list of text runs within the paragraph so that an
645    /// orderly list is maintained.
646    pub end_paragraph_char_properties: Option<Box<TextCharacterProperties>>,
647}
648
649impl TextParagraph {
650    pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
651        let mut instance: Self = Default::default();
652
653        for child_node in &xml_node.child_nodes {
654            let local_name = child_node.local_name();
655            if TextRun::is_choice_member(local_name) {
656                instance.text_run_list.push(TextRun::from_xml_element(child_node)?);
657            } else {
658                match child_node.local_name() {
659                    "pPr" => {
660                        instance.properties = Some(Box::new(TextParagraphProperties::from_xml_element(child_node)?))
661                    }
662                    "endParaRPr" => {
663                        instance.end_paragraph_char_properties =
664                            Some(Box::new(TextCharacterProperties::from_xml_element(child_node)?))
665                    }
666                    _ => (),
667                }
668            }
669        }
670
671        Ok(instance)
672    }
673}
674
675#[derive(Default, Debug, Clone)]
676pub struct TextCharacterProperties {
677    /// Specifies whether the numbers contained within vertical text continue vertically with the
678    /// text or whether they are to be displayed horizontally while the surrounding characters
679    /// continue in a vertical fashion. If this attribute is omitted, than a value of 0, or false is
680    /// assumed.
681    pub kumimoji: Option<bool>,
682
683    /// Specifies the language to be used when the generating application is displaying the user
684    /// interface controls. If this attribute is omitted, than the generating application can select a
685    /// language of its choice.
686    pub language: Option<TextLanguageID>,
687
688    /// Specifies the alternate language to use when the generating application is displaying the
689    /// user interface controls. If this attribute is omitted, than the lang attribute is used here.
690    pub alternative_language: Option<TextLanguageID>,
691
692    /// Specifies the size of text within a text run. Whole points are specified in increments of
693    /// 100 starting with 100 being a point size of 1. For instance a font point size of 12 would be
694    /// 1200 and a font point size of 12.5 would be 1250. If this attribute is omitted, than the
695    /// value in defRPr should be used.
696    pub font_size: Option<TextFontSize>,
697
698    /// Specifies whether a run of text is formatted as bold text. If this attribute is omitted, than
699    /// a value of 0, or false is assumed.
700    pub bold: Option<bool>,
701
702    /// Specifies whether a run of text is formatted as italic text. If this attribute is omitted, than
703    /// a value of 0, or false is assumed.
704    pub italic: Option<bool>,
705
706    /// Specifies whether a run of text is formatted as underlined text. If this attribute is omitted,
707    /// than no underline is assumed.
708    pub underline: Option<TextUnderlineType>,
709
710    /// Specifies whether a run of text is formatted as strikethrough text. If this attribute is
711    /// omitted, than no strikethrough is assumed.
712    pub strikethrough: Option<TextStrikeType>,
713
714    /// Specifies the minimum font size at which character kerning occurs for this text run.
715    /// Whole points are specified in increments of 100 starting with 100 being a point size of 1.
716    /// For instance a font point size of 12 would be 1200 and a font point size of 12.5 would be
717    /// 1250. If this attribute is omitted, than kerning occurs for all font sizes down to a 0 point
718    /// font.
719    pub kerning: Option<TextNonNegativePoint>,
720
721    /// Specifies the capitalization that is to be applied to the text run. This is a render-only
722    /// modification and does not affect the actual characters stored in the text run. This
723    /// attribute is also distinct from the toggle function where the actual characters stored in
724    /// the text run are changed.
725    pub capitalization: Option<TextCapsType>,
726
727    /// Specifies the spacing between characters within a text run. This spacing is specified
728    /// numerically and should be consistently applied across the entire run of text by the
729    /// generating application. Whole points are specified in increments of 100 starting with 100
730    /// being a point size of 1. For instance a font point size of 12 would be 1200 and a font point
731    /// size of 12.5 would be 1250. If this attribute is omitted than a value of 0 or no adjustment
732    /// is assumed.
733    pub spacing: Option<TextPoint>,
734
735    /// Specifies the normalization of height that is to be applied to the text run. This is a renderonly
736    /// modification and does not affect the actual characters stored in the text run. This
737    /// attribute is also distinct from the toggle function where the actual characters stored in
738    /// the text run are changed. If this attribute is omitted, than a value of 0, or false is
739    /// assumed.
740    pub normalize_heights: Option<bool>,
741
742    /// Specifies the baseline for both the superscript and subscript fonts. The size is specified
743    /// using a percentage where 1% is equal to 1 percent of the font size and 100% is equal to
744    /// 100 percent font of the font size.
745    pub baseline: Option<Percentage>,
746
747    /// Specifies that a run of text has been selected by the user to not be checked for mistakes.
748    /// Therefore if there are spelling, grammar, etc mistakes within this text the generating
749    /// application should ignore them.
750    pub no_proofing: Option<bool>,
751
752    /// Specifies that the content of a text run has changed since the proofing tools have last
753    /// been run. Effectively this flags text that is to be checked again by the generating
754    /// application for mistakes such as spelling, grammar, etc.
755    ///
756    /// Defaults to true
757    pub dirty: Option<bool>,
758
759    /// Specifies that when this run of text was checked for spelling, grammar, etc. that a
760    /// mistake was indeed found. This allows the generating application to effectively save the
761    /// state of the mistakes within the document instead of having to perform a full pass check
762    /// upon opening the document.
763    ///
764    /// Defaults to false
765    pub spelling_error: Option<bool>,
766
767    /// Specifies whether or not a text run has been checked for smart tags. This attribute acts
768    /// much like the dirty attribute dose for the checking of spelling, grammar, etc. A value of
769    /// true here indicates to the generating application that this text run should be checked for
770    /// smart tags. If this attribute is omitted, than a value of 0, or false is assumed.
771    pub smarttag_clean: Option<bool>,
772
773    /// Specifies a smart tag identifier for a run of text. This ID is unique throughout the
774    /// presentation and is used to reference corresponding auxiliary information about the
775    /// smart tag.
776    ///
777    /// Defaults to 0
778    ///
779    /// # Xml example
780    ///
781    /// ```xml
782    /// <p:txBody>
783    ///   <a:bodyPr/>
784    ///   <a:lstStyle/>
785    ///   <a:p>
786    ///     <a:r>
787    ///       <a:rPr lang="en-US" dirty="0" smtId="1"/>
788    ///       <a:t>CNTS</a:t>
789    ///     </a:r>
790    ///     <a:endParaRPr lang="en-US" dirty="0"/>
791    ///   </a:p>
792    /// </p:txBody>
793    /// ```
794    ///
795    /// The text run has a smtId attribute value of 1, which denotes that the text should be
796    /// inspected for smart tag information, which in this case maps to a stock ticker symbol.
797    pub smarttag_id: Option<u32>,
798
799    /// Specifies the link target name that is used to reference to the proper link properties in a
800    /// custom XML part within the document.
801    pub bookmark_link_target: Option<String>,
802
803    /// Specifies the outline properties of this character
804    pub line_properties: Option<Box<LineProperties>>,
805
806    /// Specifies the fill properties of this character
807    pub fill_properties: Option<FillProperties>,
808
809    /// Specifies the effect that should be applied to this character
810    pub effect_properties: Option<EffectProperties>,
811
812    /// This element specifies the highlight color that is present for a run of text.
813    ///
814    /// # Xml example
815    ///
816    /// ```xml
817    /// <p:txBody>
818    ///   …
819    ///   <a:p>
820    ///     <a:r>
821    ///       <a:rPr …>
822    ///         <a:highlight>
823    ///           <a:srgbClr val="FFFF00"/>
824    ///         </a:highlight>
825    ///       </a:rPr>
826    ///       …
827    ///       <a:t>Sample Text</a:t>
828    ///       …
829    ///     </a:r>
830    ///   </a:p>
831    ///   …
832    /// </p:txBody>
833    /// ```
834    pub highlight_color: Option<Color>,
835
836    /// Specifies the line properties of the underline for this character.
837    pub text_underline_line: Option<TextUnderlineLine>,
838
839    /// Specifies the fill properties of the underline for this character.
840    pub text_underline_fill: Option<TextUnderlineFill>,
841
842    /// This element specifies that a Latin font be used for a specific run of text. This font is specified with a typeface
843    /// attribute much like the others but is specifically classified as a Latin font.
844    ///
845    /// # Xml example
846    ///
847    /// ```xml
848    /// <a:r>
849    ///   <a:rPr …>
850    ///     <a:latin typeface="Sample Font"/>
851    ///   </a:rPr>
852    ///   <a:t>Sample Text</a:t>
853    /// </a:r>
854    /// ```
855    pub latin_font: Option<TextFont>,
856
857    /// This element specifies that an East Asian font be used for a specific run of text. This font is specified with a
858    /// typeface attribute much like the others but is specifically classified as an East Asian font.
859    ///
860    /// If the specified font is not available on a system being used for rendering, then the attributes of this element can
861    /// be utilized to select an alternative font.
862    ///
863    /// # Xml example
864    ///
865    /// ```xml
866    /// <a:r>
867    ///   <a:rPr …>
868    ///     <a:ea typeface="Sample Font"/>
869    ///   </a:rPr>
870    ///   <a:t>Sample Text</a:t>
871    /// </a:r>
872    /// ```
873    pub east_asian_font: Option<TextFont>,
874
875    /// This element specifies that a complex script font be used for a specific run of text. This font is specified with a
876    /// typeface attribute much like the others but is specifically classified as a complex script font.
877    ///
878    /// If the specified font is not available on a system being used for rendering, then the attributes of this element can
879    /// be utilized to select an alternative font.
880    pub complex_script_font: Option<TextFont>,
881
882    /// This element specifies that a symbol font be used for a specific run of text. This font is specified with a typeface
883    /// attribute much like the others but is specifically classified as a symbol font.
884    ///
885    /// # Xml example
886    ///
887    /// ```xml
888    /// <a:r>
889    ///   <a:rPr …>
890    ///     <a:sym typeface="Sample Font"/>
891    ///   </a:rPr>
892    ///   <a:t>Sample Text</a:t>
893    /// </a:r>
894    /// ```
895    ///
896    /// The above run of text is rendered using the symbol font "Sample Font".
897    pub symbol_font: Option<TextFont>,
898
899    /// Specifies the on-click hyperlink information to be applied to a run of text. When the hyperlink text is clicked the
900    /// link is fetched.
901    ///
902    /// # Xml example
903    ///
904    /// ```xml
905    /// <p:txBody>
906    ///   …
907    ///   <a:p>
908    ///     <a:r>
909    ///       <a:rPr …>
910    ///         <a:hlinkClick r:id="rId2" tooltip="Some Sample Text"/>
911    ///       </a:rPr>
912    ///       …
913    ///       <a:t>Sample Text</a:t>
914    ///       …
915    ///     </a:r>
916    ///   </a:p>
917    ///   …
918    /// </p:txBody>
919    /// ```
920    ///
921    /// The above run of text is a hyperlink that points to the resource pointed at by rId2 within this slides relationship
922    /// file. Additionally this text should display a tooltip when the mouse is hovered over the run of text.
923    pub hyperlink_click: Option<Box<Hyperlink>>,
924
925    /// Specifies the mouse-over hyperlink information to be applied to a run of text. When the mouse is hovered over
926    /// this hyperlink text the link is fetched.
927    ///
928    /// # Xml example
929    ///
930    /// ```xml
931    /// <p:txBody>
932    ///   …
933    ///   <a:p>
934    ///     <a:r>
935    ///       <a:rPr …>
936    ///         <a:hlinkMouseOver r:id="rId2" tooltip="Some Sample Text"/>
937    ///       </a:rPr>
938    ///       …
939    ///       <a:t>Sample Text</a:t>
940    ///       …
941    ///     </a:r>
942    ///   </a:p>
943    ///   …
944    /// </p:txBody>
945    /// ```
946    ///
947    /// The above run of text is a hyperlink that points to the resource pointed at by rId2 within this slides relationship
948    /// file. Additionally this text should display a tooltip when the mouse is hovered over the run of text.
949    pub hyperlink_mouse_over: Option<Box<Hyperlink>>,
950
951    /// This element specifies whether the contents of this run shall have right-to-left characteristics. Specifically, the
952    /// following behaviors are applied when this element’s val attribute is true (or an equivalent):
953    ///
954    /// * Formatting – When the contents of this run are displayed, all characters shall be treated as complex
955    ///   script characters. This means that the values of the cs element (§21.1.2.3.1) shall be used to determine
956    ///   the font face.
957    ///
958    /// * Character Directionality Override – When the contents of this run are displayed, this property acts as a
959    ///   right-to-left override for characters which are classified as follows (using the Unicode Character
960    ///   Database):
961    ///
962    ///   * Weak types except European Number, European Number Terminator, Common Number Separator,
963    ///     Arabic Number and (for Hebrew text) European Number Separator when constituting part of a
964    ///     number
965    ///
966    ///   * Neutral types
967    ///
968    /// * This element provides information used to resolve the (Unicode) classifications of individual characters
969    ///   as either L, R, AN or EN. Once this is determined, the line should be displayed subject to the
970    ///   recommendation of the Unicode Bidirectional Algorithm in reordering resolved levels.
971    ///
972    ///   # Rationale
973    ///
974    ///   This override allows applications to store and utilize higher-level information beyond that
975    ///   implicitly derived from the Unicode Bidirectional algorithm. For example, if the string “first second”
976    ///   appears in a right-to-left paragraph inside a document, the Unicode algorithm would always result in
977    ///   “first second” at display time (since the neutral character is surrounded by strongly classified
978    ///   characters). However, if the whitespace was entered using a right-to-left input method (e.g. a Hebrew
979    ///   keyboard), then that character could be classified as RTL using this property, allowing the display of
980    ///   “second first” in a right-to-left paragraph, since the user explicitly asked for the space in a right-to-left
981    ///   context.
982    ///
983    /// This property shall not be used with strong left-to-right text. Any behavior under that condition is unspecified.
984    /// This property, when off, should not be used with strong right-to-left text. Any behavior under that condition is
985    /// unspecified.
986    ///
987    /// If this element is not present, the default value is to leave the formatting applied at previous level in the style
988    /// hierarchy. If this element is never applied in the style hierarchy, then right to left characteristics shall not be
989    /// applied to the contents of this run.
990    ///
991    /// # Xml example
992    ///
993    /// Consider the following DrawingML visual content: “first second, أولى ثاني ”. This content might
994    /// appear as follows within its parent paragraph:
995    /// ```xml
996    /// <a:p>
997    ///   <a:r>
998    ///     <a:t>first second, </w:t>
999    ///   </a:r>
1000    ///   <a:r>
1001    ///     <a:rPr>
1002    ///       <a:rtl/>
1003    ///     </a:rPr>
1004    ///     <a:t> أولى </a:t>
1005    ///   </a:r>
1006    ///   <a:r>
1007    ///     <a:rPr>
1008    ///       <a:rtl/>
1009    ///     </a:rPr>
1010    ///     <a:t> </a:t>
1011    ///   </a:r>
1012    ///   <a:r>
1013    ///     <a:rPr>
1014    ///       <a:rtl/>
1015    ///     </a:rPr>
1016    ///     <a:t> ثاني </a:t>
1017    ///   </a:r>
1018    /// </a:p>
1019    /// ```
1020    ///
1021    /// The presence of the rtl element on the second, third, and fourth runs specifies that:
1022    ///
1023    /// * The formatting on those runs is specified using the complex-script property variants.
1024    /// * The whitespace character is treated as right-to-left.
1025    ///
1026    /// Note that the second, third and fourth runs could be joined as one run with the rtl element specified.
1027    pub rtl: Option<bool>,
1028}
1029
1030impl TextCharacterProperties {
1031    pub fn from_xml_element(xml_node: &XmlNode) -> Result<TextCharacterProperties> {
1032        let mut instance: Self = Default::default();
1033
1034        for (attr, value) in &xml_node.attributes {
1035            match attr.as_str() {
1036                "kumimoji" => instance.kumimoji = Some(parse_xml_bool(value)?),
1037                "lang" => instance.language = Some(value.clone()),
1038                "altLang" => instance.alternative_language = Some(value.clone()),
1039                "sz" => instance.font_size = Some(value.parse()?),
1040                "b" => instance.bold = Some(parse_xml_bool(value)?),
1041                "i" => instance.italic = Some(parse_xml_bool(value)?),
1042                "u" => instance.underline = Some(value.parse()?),
1043                "strike" => instance.strikethrough = Some(value.parse()?),
1044                "kern" => instance.kerning = Some(value.parse()?),
1045                "cap" => instance.capitalization = Some(value.parse()?),
1046                "spc" => instance.spacing = Some(value.parse()?),
1047                "normalizeH" => instance.normalize_heights = Some(parse_xml_bool(value)?),
1048                "baseline" => instance.baseline = Some(value.parse()?),
1049                "noProof" => instance.no_proofing = Some(parse_xml_bool(value)?),
1050                "dirty" => instance.dirty = Some(parse_xml_bool(value)?),
1051                "err" => instance.spelling_error = Some(parse_xml_bool(value)?),
1052                "smtClean" => instance.smarttag_clean = Some(parse_xml_bool(value)?),
1053                "smtId" => instance.smarttag_id = Some(value.parse()?),
1054                "bmk" => instance.bookmark_link_target = Some(value.clone()),
1055                _ => (),
1056            }
1057        }
1058
1059        for child_node in &xml_node.child_nodes {
1060            let child_local_name = child_node.local_name();
1061            if FillProperties::is_choice_member(child_local_name) {
1062                instance.fill_properties = Some(FillProperties::from_xml_element(child_node)?);
1063            } else if EffectProperties::is_choice_member(child_local_name) {
1064                instance.effect_properties = Some(EffectProperties::from_xml_element(child_node)?);
1065            } else if TextUnderlineLine::is_choice_member(child_local_name) {
1066                instance.text_underline_line = Some(TextUnderlineLine::from_xml_element(child_node)?);
1067            } else if TextUnderlineFill::is_choice_member(child_local_name) {
1068                instance.text_underline_fill = Some(TextUnderlineFill::from_xml_element(child_node)?);
1069            } else {
1070                match child_local_name {
1071                    "ln" => instance.line_properties = Some(Box::new(LineProperties::from_xml_element(child_node)?)),
1072                    "highlight" => {
1073                        let color_node = child_node
1074                            .child_nodes
1075                            .get(0)
1076                            .ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "CT_Color"))?;
1077                        instance.highlight_color = Some(Color::from_xml_element(color_node)?);
1078                    }
1079                    "latin" => instance.latin_font = Some(TextFont::from_xml_element(child_node)?),
1080                    "ea" => instance.east_asian_font = Some(TextFont::from_xml_element(child_node)?),
1081                    "cs" => instance.complex_script_font = Some(TextFont::from_xml_element(child_node)?),
1082                    "sym" => instance.symbol_font = Some(TextFont::from_xml_element(child_node)?),
1083                    "hlinkClick" => instance.hyperlink_click = Some(Box::new(Hyperlink::from_xml_element(child_node)?)),
1084                    "hlinkMouseOver" => {
1085                        instance.hyperlink_mouse_over = Some(Box::new(Hyperlink::from_xml_element(child_node)?))
1086                    }
1087                    "rtl" => {
1088                        instance.rtl = match child_node.text {
1089                            Some(ref s) => Some(parse_xml_bool(s)?),
1090                            None => None,
1091                        }
1092                    }
1093                    _ => (),
1094                }
1095            }
1096        }
1097
1098        Ok(instance)
1099    }
1100}
1101
1102#[derive(Debug, Clone)]
1103pub enum TextSpacing {
1104    /// This element specifies the amount of white space that is to be used between lines and paragraphs in the form of
1105    /// a percentage of the text size. The text size that is used to calculate the spacing here is the text for each run, with
1106    /// the largest text size having precedence. That is if there is a run of text with 10 point font and within the same
1107    /// paragraph on the same line there is a run of text with a 12 point font size then the 12 point should be used to
1108    /// calculate the spacing to be used.
1109    ///
1110    /// # Xml example
1111    ///
1112    /// ```xml
1113    /// <p:txBody>
1114    ///   …
1115    ///   <a:p>
1116    ///     <a:pPr …>
1117    ///       <a:spcBef>
1118    ///         <a:spcPct val="200%"/>
1119    ///       </a:spcBef>
1120    ///     </a:pPr>
1121    ///     …
1122    ///     <a:t>Sample Text</a:t>
1123    ///     …
1124    ///   </a:p>
1125    ///   …
1126    /// </p:txBody>
1127    /// ```
1128    ///
1129    /// The above paragraph of text is formatted to have a spacing before the paragraph text. This spacing is 200% of
1130    /// the size of the largest text on each line.
1131    Percent(TextSpacingPercent),
1132
1133    /// This element specifies the amount of white space that is to be used between lines and paragraphs in the form of
1134    /// a text point size. The size is specified using points where 100 is equal to 1 point font and 1200 is equal to 12
1135    /// point.
1136    ///
1137    /// # Xml example
1138    ///
1139    /// ```xml
1140    /// <p:txBody>
1141    ///   …
1142    ///   <a:p>
1143    ///     <a:pPr …>
1144    ///       <a:spcBef>
1145    ///         <a:spcPts val="1400"/>
1146    ///       </a:spcBef>
1147    ///     </a:pPr>
1148    ///     …
1149    ///     <a:t>Sample Text</a:t>
1150    ///     …
1151    ///   </a:p>
1152    ///   …
1153    /// </p:txBody>
1154    /// ```
1155    ///
1156    /// The above paragraph of text is formatted to have a spacing before the paragraph text. This spacing is a size of 14
1157    /// points due to val="1400".
1158    Point(TextSpacingPoint),
1159}
1160
1161impl TextSpacing {
1162    pub fn from_xml_element(xml_node: &XmlNode) -> Result<TextSpacing> {
1163        match xml_node.local_name() {
1164            "spcPct" => {
1165                let val_attr = xml_node
1166                    .attribute("val")
1167                    .ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "val"))?;
1168                Ok(TextSpacing::Percent(val_attr.parse::<TextSpacingPercent>()?))
1169            }
1170            "spcPts" => {
1171                let val_attr = xml_node
1172                    .attribute("val")
1173                    .ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "val"))?;
1174                Ok(TextSpacing::Point(val_attr.parse::<TextSpacingPoint>()?))
1175            }
1176            _ => Err(NotGroupMemberError::new(xml_node.name.clone(), "EG_TextSpacing").into()),
1177        }
1178    }
1179}
1180
1181/// This element specifies a single tab stop to be used on a line of text when there are one or more tab characters
1182/// present within the text. When there is more than one present than they should be utilized in increasing position
1183/// order which is specified via the pos attribute.
1184///
1185/// # Xml example
1186///
1187/// ```xml
1188/// <p:txBody>
1189///   …
1190///   <a:p>
1191///     <a:pPr …>
1192///       <a:tabLst>
1193///         <a:tab pos="2292350" algn="l"/>
1194///         <a:tab pos="2627313" algn="l"/>
1195///         <a:tab pos="2743200" algn="l"/>
1196///         <a:tab pos="2974975" algn="l"/>
1197///       </a:tabLst>
1198///     </a:pPr>
1199///     …
1200///     <a:t>Sample Text</a:t>
1201///     …
1202///   </a:p>
1203///   …
1204/// </p:txBody>
1205/// ```
1206///
1207/// The paragraph within which this <a:tab> information resides has a total of 4 unique tab stops that should be
1208/// listed in order of increasing position. Along with specifying the tab position each tab allows for the specifying of
1209/// an alignment.
1210#[derive(Default, Debug, Clone)]
1211pub struct TextTabStop {
1212    /// Specifies the position of the tab stop relative to the left margin. If this attribute is omitted
1213    /// then the application default for tab stops is used.
1214    pub position: Option<Coordinate32>,
1215
1216    /// Specifies the alignment that is to be applied to text using this tab stop. If this attribute is
1217    /// omitted then the application default for the generating application.
1218    pub alignment: Option<TextTabAlignType>,
1219}
1220
1221impl TextTabStop {
1222    pub fn from_xml_element(xml_node: &XmlNode) -> Result<TextTabStop> {
1223        let mut instance: Self = Default::default();
1224
1225        for (attr, value) in &xml_node.attributes {
1226            match attr.as_str() {
1227                "pos" => instance.position = Some(value.parse::<Coordinate32>()?),
1228                "algn" => instance.alignment = Some(value.parse::<TextTabAlignType>()?),
1229                _ => (),
1230            }
1231        }
1232
1233        Ok(instance)
1234    }
1235}