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}