msoffice_pptx 0.2.1

pptx file format deserializer
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
use enum_from_str::ParseEnumVariantError;
use enum_from_str_derive::FromStr;
use msoffice_shared::error::{MissingAttributeError, MissingChildNodeError};
use msoffice_shared::relationship::RelationshipId;
use msoffice_shared::xml::{parse_xml_bool, XmlNode};
use std::io::{Read, Seek};

pub type Result<T> = ::std::result::Result<T, Box<dyn (::std::error::Error)>>;

/// This simple type specifies the allowed numbering for the slide identifier.
/// 
/// Values represented by this type are restricted to: 256 <= n <= 2147483648
pub type SlideId = u32;
/// This simple type sets the bounds for the slide layout id value. This layout id is used to identify the different slide
/// layout designs.
/// 
/// Values represented by this type are restricted to: 2147483648 <= n
pub type SlideLayoutId = u32;
/// This simple type specifies the allowed numbering for the slide master identifier.
/// 
/// Values represented by this type are restricted to: 2147483648 <= n
pub type SlideMasterId = u32;
/// This simple type specifies constraints for value of the Bookmark ID seed.
/// 
/// Values represented by this type are restricted to: 1 <= n <= 2147483648
pub type BookmarkIdSeed = u32;
/// This simple type specifies the slide size coordinate in EMUs (English Metric Units).AsRef
/// 
/// Values represented by this type are restricted to: 914400 <= n <= 51206400
pub type SlideSizeCoordinate = msoffice_shared::drawingml::PositiveCoordinate32;
/// This simple type specifies a name, such as for a comment author or custom show.
pub type Name = String;

#[derive(Debug, Clone, Copy, PartialEq, FromStr)]
pub enum ConformanceClass {
    #[from_str = "strict"]
    Strict,
    #[from_str = "transitional"]
    Transitional,
}

/// This simple type specifies the kind of slide size that the slide should be optimized for.
#[derive(Debug, Clone, Copy, PartialEq, FromStr)]
pub enum SlideSizeType {
    /// Slide size should be optimized for 35mm film output
    #[from_str = "mm35"]
    Mm35,
    /// Slide size should be optimized for A3 output
    #[from_str = "a3"]
    A3,
    /// Slide size should be optimized for A4 output
    #[from_str = "a4"]
    A4,
    /// Slide size should be optimized for B4ISO output
    #[from_str = "b4ISO"]
    B4ISO,
    /// Slide size should be optimized for B4JIS output
    #[from_str = "b4JIS"]
    B4JIS,
    /// Slide size should be optimized for B5ISO output
    #[from_str = "b5ISO"]
    B5ISO,
    /// Slide size should be optimized for B5JIS output
    #[from_str = "b5JIS"]
    B5JIS,
    /// Slide size should be optimized for banner output
    #[from_str = "banner"]
    Banner,
    /// Slide size should be optimized for custom output
    #[from_str = "custom"]
    Custom,
    /// Slide size should be optimized for hagaki card output
    #[from_str = "hagakiCard"]
    HagakiCard,
    /// Slide size should be optimized for ledger output
    #[from_str = "ledger"]
    Ledger,
    /// Slide size should be optimized for letter output
    #[from_str = "letter"]
    Letter,
    /// Slide size should be optimized for overhead output
    #[from_str = "overhead"]
    Overhead,
    /// Slide size should be optimized for 16x10 screen output
    #[from_str = "screen16x10"]
    Screen16x10,
    /// Slide size should be optimized for 16x9 screen output
    #[from_str = "screen16x9"]
    Screen16x9,
    /// Slide size should be optimized for 4x3 screen output
    #[from_str = "screen4x3"]
    Screen4x3,
}

/// This simple type specifies the values for photo layouts within a photo album presentation.
/// See Fundamentals And Markup Language Reference for examples
#[derive(Debug, Clone, Copy, PartialEq, FromStr)]
pub enum PhotoAlbumLayout {
    /// Fit Photos to Slide
    #[from_str = "fitToSlide"]
    FitToSlide,
    /// 1 Photo per Slide
    #[from_str = "pic1"]
    Pic1,
    /// 2 Photo per Slide
    #[from_str = "pic2"]
    Pic2,
    /// 4 Photo per Slide
    #[from_str = "pic4"]
    Pic4,
    /// 1 Photo per Slide with Titles
    #[from_str = "picTitle1"]
    PicTitle1,
    /// 2 Photo per Slide with Titles
    #[from_str = "picTitle2"]
    PicTitle2,
    /// 4 Photo per Slide with Titles
    #[from_str = "picTitle4"]
    PicTitle4,
}

/// This simple type specifies the values for photo frame types within a photo album presentation.
/// See Fundamentals And Markup Language Reference for examples
#[derive(Debug, Clone, Copy, PartialEq, FromStr)]
pub enum PhotoAlbumFrameShape {
    /// Rectangle Photo Frame
    #[from_str = "frameStyle1"]
    FrameStyle1,
    /// Rounded Rectangle Photo Frame
    #[from_str = "frameStyle2"]
    FrameStyle2,
    /// Simple White Photo Frame
    #[from_str = "frameStyle3"]
    FrameStyle3,
    /// Simple Black Photo Frame
    #[from_str = "frameStyle4"]
    FrameStyle4,
    /// Compound Black Photo Frame
    #[from_str = "frameStyle5"]
    FrameStyle5,
    /// Center Shadow Photo Frame
    #[from_str = "frameStyle6"]
    FrameStyle6,
    /// Soft Edge Photo Frame
    #[from_str = "frameStyle7"]
    FrameStyle7,
}

/// This simple type determines if the Embedded object is re-colored to reflect changes to the color schemes.
#[derive(Debug, Clone, Copy, PartialEq, FromStr)]
pub enum OleObjectFollowColorScheme {
    /// Setting this enumeration causes the Embedded object to not respond to changes in the color scheme in the
    /// presentation.
    #[from_str = "none"]
    None,
    /// Setting this enumeration causes the Embedded object to respond to all changes in the color scheme in the
    /// presentation.
    #[from_str = "full"]
    Full,
    /// Setting this enumeration causes the Embedded object to respond only to changes in the text and background
    /// colors of the color scheme in the presentation.
    #[from_str = "textAndBackground"]
    TextAndBackground,
}

#[derive(Default, Debug, Clone)]
pub struct CustomerDataList {
    pub customer_data_list: Vec<RelationshipId>,
    /// This element specifies the existence of customer data in the form of tags. This allows for the storage of customer
    /// data within the PresentationML framework. While this is similar to the ext tag in that it can be used store
    /// information, this tag mainly focuses on referencing to other parts of the presentation document. This is
    /// accomplished via the relationship identification attribute that is required for all specified tags.
    pub tags: Option<RelationshipId>,
}

impl CustomerDataList {
    pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
        let mut customer_data_list = Vec::new();
        let mut tags = None;

        for child_node in &xml_node.child_nodes {
            match child_node.local_name() {
                "custData" => {
                    let id_attr = child_node
                        .attribute("r:id")
                        .ok_or_else(|| MissingAttributeError::new(child_node.name.clone(), "r:id"))?;
                    customer_data_list.push(id_attr.clone());
                }
                "tags" => {
                    let id_attr = child_node
                        .attribute("r:id")
                        .ok_or_else(|| MissingAttributeError::new(child_node.name.clone(), "r:id"))?;
                    tags = Some(id_attr.clone());
                }
                _ => (),
            }
        }

        Ok(Self {
            customer_data_list,
            tags,
        })
    }
}

#[derive(Debug, Clone)]
pub struct SlideSize {
    /// Specifies the length of the extents rectangle in EMUs. This rectangle shall dictate the size
    /// of the object as displayed (the result of any scaling to the original object).
    ///
    /// # Xml example
    ///
    /// ```xml
    /// <... cx="1828800" cy="200000"/>
    /// ```xml
    pub width: SlideSizeCoordinate,
    /// Specifies the width of the extents rectangle in EMUs. This rectangle shall dictate the size
    /// of the object as displayed (the result of any scaling to the original object).
    ///
    /// # Xml example
    ///
    /// ```xml
    /// < ... cx="1828800" cy="200000"/>
    /// ```
    pub height: SlideSizeCoordinate,
    /// Specifies the kind of slide size that should be used. This identifies in particular the
    /// expected delivery platform for this presentation.
    pub size_type: Option<SlideSizeType>,
}

impl SlideSize {
    pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
        let mut width = None;
        let mut height = None;
        let mut size_type = None;

        for (attr, value) in &xml_node.attributes {
            match attr.as_str() {
                "cx" => width = Some(value.parse()?),
                "cy" => height = Some(value.parse()?),
                "type" => size_type = Some(value.parse()?),
                _ => (),
            }
        }

        let width = width.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "cx"))?;
        let height = height.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "cy"))?;

        Ok(Self {
            width,
            height,
            size_type,
        })
    }
}

#[derive(Debug, Clone)]
pub struct SlideIdListEntry {
    /// Specifies the slide identifier that is to contain a value that is unique throughout the presentation.
    pub id: SlideId,
    /// Specifies the relationship identifier that is used in conjunction with a corresponding
    /// relationship file to resolve the location within a presentation of the sld element defining
    /// this slide.
    pub relationship_id: RelationshipId,
}

impl SlideIdListEntry {
    pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
        let mut id = None;
        let mut relationship_id = None;

        for (attr, value) in &xml_node.attributes {
            match attr.as_str() {
                "id" => id = Some(value.parse()?),
                "r:id" => relationship_id = Some(value.clone()),
                _ => (),
            }
        }

        let id = id.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "id"))?;
        let relationship_id =
            relationship_id.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "r:id"))?;

        Ok(Self { id, relationship_id })
    }
}

#[derive(Debug, Clone)]
pub struct SlideLayoutIdListEntry {
    /// Specifies the identification number that uniquely identifies this slide layout within the
    /// presentation file.
    pub id: Option<SlideLayoutId>,
    /// Specifies the relationship id value that the generating application can use to resolve
    /// which slide layout is used in the creation of the slide. This relationship id is used within
    /// the relationship file for the master slide to expose the location of the corresponding
    /// layout file within the presentation.
    pub relationship_id: RelationshipId,
}

impl SlideLayoutIdListEntry {
    pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
        let mut id = None;
        let mut relationship_id = None;
        for (attr, value) in &xml_node.attributes {
            match attr.as_str() {
                "id" => id = Some(value.parse()?),
                "r:id" => relationship_id = Some(value.clone()),
                _ => (),
            }
        }

        let relationship_id =
            relationship_id.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "r:id"))?;

        Ok(Self { id, relationship_id })
    }
}

#[derive(Debug, Clone)]
pub struct SlideMasterIdListEntry {
    /// Specifies the slide master identifier that is to contain a value that is unique throughout
    /// the presentation.
    pub id: Option<SlideMasterId>,
    /// Specifies the relationship identifier that is used in conjunction with a corresponding
    /// relationship file to resolve the location within a presentation of the sldMaster element
    /// defining this slide master.
    pub relationship_id: RelationshipId,
}

impl SlideMasterIdListEntry {
    pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
        let mut id = None;
        let mut relationship_id = None;

        for (attr, value) in &xml_node.attributes {
            match attr.as_str() {
                "id" => id = Some(value.parse()?),
                "r:id" => relationship_id = Some(value.clone()),
                _ => (),
            }
        }

        let relationship_id =
            relationship_id.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "r:id"))?;

        Ok(Self { id, relationship_id })
    }
}

#[derive(Debug, Clone)]
pub struct NotesMasterIdListEntry {
    /// Specifies the relationship identifier that is used in conjunction with a corresponding
    /// relationship file to resolve the location within a presentation of the notesMaster element
    /// defining this notes master.
    pub relationship_id: RelationshipId,
}

impl NotesMasterIdListEntry {
    pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
        let r_id_attr = xml_node
            .attribute("r:id")
            .ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "r:id"))?;

        Ok(Self {
            relationship_id: r_id_attr.clone(),
        })
    }
}

#[derive(Debug, Clone)]
pub struct HandoutMasterIdListEntry {
    /// Specifies the relationship identifier that is used in conjunction with a corresponding
    /// relationship file to resolve the location within a presentation of the handoutMaster
    /// element defining this handout master.
    pub relationship_id: RelationshipId,
}

impl HandoutMasterIdListEntry {
    pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
        let r_id_attr = xml_node
            .attribute("r:id")
            .ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "r:id"))?;

        Ok(Self {
            relationship_id: r_id_attr.clone(),
        })
    }
}

#[derive(Debug, Clone)]
pub struct EmbeddedFontListEntry {
    /// This element specifies specific properties describing an embedded font. Once specified, this font is available
    /// for use within the presentation.
    /// Within a font specification there can be regular, bold, italic and boldItalic versions of the font specified.
    /// The actual font data for each of these is referenced using a relationships file that contains links to all
    /// available fonts.
    /// This font data contains font information for each of the characters to be made available in each version of
    /// the font.
    ///
    /// # Xml example
    ///
    /// ```xml
    /// <p:embeddedFont>
    ///   <p:font typeface="MyFont" pitchFamily="34" charset="0"/>
    ///   <p:regular r:id="rId2"/>
    /// </p:embeddedFont>
    /// ```
    ///
    /// # Font Substitution Logic
    ///
    /// If the specified font is not available on a system being used for rendering, then the attributes of this
    /// element are to be utilized in selecting an alternate font.
    ///
    /// # Note
    ///
    /// Not all characters for a typeface must be stored. It is up to the generating application to determine which
    /// characters are to be stored in the corresponding font data files.
    pub font: msoffice_shared::drawingml::TextFont,
    /// This element specifies a regular embedded font that is linked to a parent typeface. Once specified, this regular
    /// version of the given typeface name is available for use within the presentation. The actual font data is
    /// referenced using a relationships file that contains links to all fonts available. This font data contains font
    /// information for each of the characters to be made available.
    ///
    /// # Xml example
    ///
    /// ```xml
    /// <p:embeddedFont>
    ///   <p:font typeface="MyFont" pitchFamily="34" charset="0"/>
    ///   <p:regular r:id="rId2"/>
    /// </p:embeddedFont>
    /// ```
    ///
    /// # Note
    ///
    /// Not all characters for a typeface must be stored. It is up to the generating application to determine which
    /// characters are to be stored in the corresponding font data files.
    pub regular: Option<RelationshipId>,
    /// This element specifies a bold embedded font that is linked to a parent typeface. Once specified, this bold
    /// version of the given typeface name is available for use within the presentation. The actual font data is
    /// referenced using a relationships file that contains links to all fonts available. This font data contains font
    /// information for each of the characters to be made available.
    ///
    /// # Xml example
    ///
    /// ```xml
    /// <p:embeddedFont>
    ///   <p:font typeface="MyFont" pitchFamily="34" charset="0"/>
    ///   <p:bold r:id="rId2"/>
    /// </p:embeddedFont>
    /// ```
    ///
    /// # Note
    ///
    /// Not all characters for a typeface must be stored. It is up to the generating application to determine
    /// which characters are to be stored in the corresponding font data files.
    pub bold: Option<RelationshipId>,
    /// This element specifies an italic embedded font that is linked to a parent typeface. Once specified, this italic
    /// version of the given typeface name is available for use within the presentation. The actual font data is
    /// referenced using a relationships file that contains links to all fonts available. This font data contains font
    /// information for each of the characters to be made available.
    ///
    /// # Xml example
    ///
    /// ```xml
    /// <p:embeddedFont>
    ///   <p:font typeface="MyFont" pitchFamily="34" charset="0"/>
    ///   <p:italic r:id="rId2"/>
    /// </p:embeddedFont>
    /// ```
    ///
    /// # Note
    ///
    /// Not all characters for a typeface must be stored. It is up to the generating application to determine which
    /// characters are to be stored in the corresponding font data files.
    pub italic: Option<RelationshipId>,
    /// This element specifies a bold italic embedded font that is linked to a parent typeface. Once specified, this
    /// bold italic version of the given typeface name is available for use within the presentation. The actual font
    /// data is referenced using a relationships file that contains links to all fonts available. This font data
    /// contains font information for each of the characters to be made available.
    ///
    /// # Xml example
    ///
    /// ```xml
    /// <p:embeddedFont>
    ///   <p:font typeface="MyFont" pitchFamily="34" charset="0"/>
    ///   <p:boldItalic r:id="rId2"/>
    /// </p:embeddedFont>
    /// ```
    ///
    /// # Note
    ///
    /// Not all characters for a typeface must be stored. It is up to the generating application to determine
    /// which characters are to be stored in the corresponding font data files.
    pub bold_italic: Option<RelationshipId>,
}

impl EmbeddedFontListEntry {
    pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
        let mut font = None;
        let mut regular = None;
        let mut bold = None;
        let mut italic = None;
        let mut bold_italic = None;

        for child_node in &xml_node.child_nodes {
            match child_node.local_name() {
                "font" => font = Some(msoffice_shared::drawingml::TextFont::from_xml_element(child_node)?),
                "regular" => {
                    let id_attr = child_node
                        .attribute("r:id")
                        .ok_or_else(|| MissingAttributeError::new(child_node.name.clone(), "r:id"))?;
                    regular = Some(id_attr.clone());
                }
                "bold" => {
                    let id_attr = child_node
                        .attribute("r:id")
                        .ok_or_else(|| MissingAttributeError::new(child_node.name.clone(), "r:id"))?;
                    bold = Some(id_attr.clone());
                }
                "italic" => {
                    let id_attr = child_node
                        .attribute("r:id")
                        .ok_or_else(|| MissingAttributeError::new(child_node.name.clone(), "r:id"))?;
                    italic = Some(id_attr.clone());
                }
                "boldItalic" => {
                    let id_attr = child_node
                        .attribute("r:id")
                        .ok_or_else(|| MissingAttributeError::new(child_node.name.clone(), "r:id"))?;
                    bold_italic = Some(id_attr.clone());
                }
                _ => (),
            }
        }

        let font = font.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "font"))?;

        Ok(Self {
            font,
            regular,
            bold,
            italic,
            bold_italic,
        })
    }
}

#[derive(Debug, Clone)]
pub struct CustomShow {
    /// Specifies a name for the custom show.
    pub name: Name,
    /// Specifies the identification number for this custom show. This should be unique among
    /// all the custom shows within the corresponding presentation.
    pub id: u32,
    pub slides: Vec<RelationshipId>,
}

impl CustomShow {
    pub fn from_xml_element(xml_node: &XmlNode) -> Result<CustomShow> {
        let mut name = None;
        let mut id = None;

        for (attr, value) in &xml_node.attributes {
            match attr.as_str() {
                "name" => name = Some(value.clone()),
                "id" => id = Some(value.parse::<u32>()?),
                _ => (),
            }
        }

        let mut slides = Vec::new();

        for child_node in &xml_node.child_nodes {
            if child_node.local_name() == "sldLst" {
                for slide_node in &child_node.child_nodes {
                    let id_attr = slide_node
                        .attribute("r:id")
                        .ok_or_else(|| MissingAttributeError::new(slide_node.name.clone(), "r:id"))?;
                    slides.push(id_attr.clone());
                }
            }
        }

        let name = name.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "name"))?;
        let id = id.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "id"))?;

        Ok(Self { name, id, slides })
    }
}

#[derive(Default, Debug, Clone)]
pub struct PhotoAlbum {
    /// Specifies whether all pictures in the photo album are to be displayed as black and white.
    ///
    /// Defaults to false
    pub black_and_white: Option<bool>,
    /// Specifies whether to show captions for pictures in the photo album. Captions are text
    /// boxes grouped with each image, with the group set to not allow ungrouping.
    ///
    /// Defaults to false
    pub show_captions: Option<bool>,
    /// Specifies the layout that is to be used to arrange the pictures in the photo album on
    /// individual slides.
    ///
    /// Defaults to PhotoAlbumLayout::FitToSlide
    pub layout: Option<PhotoAlbumLayout>,
    /// Specifies the frame type that is to be used on all the pictures in the photo album.
    ///
    /// Defaults to PhotoAlbumFrameShape::FrameStyle1
    pub frame: Option<PhotoAlbumFrameShape>,
}

impl PhotoAlbum {
    pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
        let mut instance: Self = Default::default();

        for (attr, value) in &xml_node.attributes {
            match attr.as_str() {
                "bw" => instance.black_and_white = Some(parse_xml_bool(value)?),
                "showCaptions" => instance.show_captions = Some(parse_xml_bool(value)?),
                "layout" => instance.layout = Some(value.parse()?),
                "frame" => instance.frame = Some(value.parse()?),
                _ => (),
            }
        }

        Ok(instance)
    }
}

#[derive(Debug, Clone)]
pub struct Kinsoku {
    /// Specifies the corresponding East Asian language that these settings apply to.
    pub language: Option<String>,
    /// Specifies the characters that cannot start a line of text.
    pub invalid_start_chars: String,
    /// Specifies the characters that cannot end a line of text.
    pub invalid_end_chars: String,
}

impl Kinsoku {
    pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
        let mut language = None;
        let mut invalid_start_chars = None;
        let mut invalid_end_chars = None;

        for (attr, value) in &xml_node.attributes {
            match attr.as_str() {
                "lang" => language = Some(value.clone()),
                "invalStChars" => invalid_start_chars = Some(value.clone()),
                "invalEndChars" => invalid_end_chars = Some(value.clone()),
                _ => (),
            }
        }

        let invalid_start_chars =
            invalid_start_chars.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "invalStChars"))?;
        let invalid_end_chars =
            invalid_end_chars.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "invalEndChars"))?;

        Ok(Self {
            language,
            invalid_start_chars,
            invalid_end_chars,
        })
    }
}

#[derive(Default, Debug, Clone)]
pub struct ModifyVerifier {
    /// Specifies the specific cryptographic hashing algorithm which shall be used along with the
    /// salt attribute and input password in order to compute the hash value.
    ///
    /// The following values are reserved:
    /// * MD2: Specifies that the MD2 algorithm, as defined by RFC 1319, shall be used.
    /// __It is recommended that applications should avoid using this algorithm to store new hash values, due to
    /// publically known breaks.__
    ///
    /// * MD4: Specifies that the MD4 algorithm, as defined by RFC 1320, shall be used.
    /// __It is recommended that applications should avoid using this algorithm to store new hash values, due to
    /// publically known breaks.__
    ///
    /// * MD5: Specifies that the MD5 algorithm, as defined by RFC 1321, shall be used.
    /// __It is recommended that applications should avoid using this algorithm to store new hash values, due to
    /// publically known breaks.__
    ///
    /// * RIPEMD-128: Specifies that the RIPEMD-128 algorithm, as defined by ISO/IEC 10118-3:2004 shall be used.
    /// __It is recommended that applications should avoid using this algorithm to store new hash values, due to
    /// publically known breaks.__
    ///
    /// * RIPEMD-160: Specifies that the RIPEMD-160 algorithm, as defined by ISO/IEC 10118-3:2004 shall be used.
    /// * SHA-1: Specifies that the SHA-1 algorithm, as defined by ISO/IEC 10118-3:2004 shall be used.
    /// * SHA-256: Specifies that the SHA-256 algorithm, as defined by ISO/IEC 10118-3:2004 shall be used.
    /// * SHA-384: Specifies that the SHA-384 algorithm, as defined by ISO/IEC 10118-3:2004 shall be used.
    /// * SHA-512: Specifies that the SHA-512 algorithm, as defined by ISO/IEC 10118-3:2004 shall be used.
    /// * WHIRLPOOL: Specifies that the WHIRLPOOL algorithm, as defined by ISO/IEC 10118-3:2004 shall be used.
    ///
    /// # Xml example
    ///
    /// Consider an Office Open XML document with the following information stored in one of its protection elements:
    /// ```xml
    /// < ... algorithmName="SHA-1" hashValue="9oN7nWkCAyEZib1RomSJTjmPpCY=" />
    /// ```
    /// The algorithm_name attribute value of “SHA-1” specifies that the SHA-1 hashing algorithm must be used to
    /// generate a hash from the user-defined password.
    pub algorithm_name: Option<String>,
    /// Specifies the hash value for the password required to edit this chartsheet. This value shall
    /// be compared with the resulting hash value after hashing the user-supplied password
    /// using the algorithm specified by the preceding attributes and parent XML element, and if
    /// the two values match, the protection shall no longer be enforced.
    ///
    /// If this value is omitted, then the reservationPassword attribute shall contain the
    /// password hash for the workbook.
    ///
    /// # Xml example
    ///
    /// Consider an Office Open XML document with the following information stored in one of its protection elements:
    /// ```xml
    /// <... algorithmName="SHA-1" hashValue="9oN7nWkCAyEZib1RomSJTjmPpCY=" />
    /// ```
    /// The hashValue attribute value of 9oN7nWkCAyEZib1RomSJTjmPpCY= specifies that the
    /// user-supplied password must be hashed using the pre-processing defined by the parent
    /// element (if any) followed by the SHA-1 algorithm (specified via the algorithmName
    /// attribute value of SHA-1 ) and that the resulting has value must be
    /// 9oN7nWkCAyEZib1RomSJTjmPpCY= for the protection to be disabled.
    pub hash_value: Option<String>,
    /// Specifies the salt which was prepended to the user-supplied password before it was
    /// hashed using the hashing algorithm defined by the preceding attribute values to generate
    /// the hashValue attribute, and which shall also be prepended to the user-supplied
    /// password before attempting to generate a hash value for comparison. A salt is a random
    /// string which is added to a user-supplied password before it is hashed in order to prevent
    /// a malicious party from pre-calculating all possible password/hash combinations and
    /// simply using those pre-calculated values (often referred to as a "dictionary attack").
    ///
    /// If this attribute is omitted, then no salt shall be prepended to the user-supplied password
    /// before it is hashed for comparison with the stored hash value.
    ///
    /// # Xml example
    ///
    /// Consider an Office Open XML document with the following information stored in one of its protection elements:
    /// ```xml
    /// <... saltValue="ZUdHa+D8F/OAKP3I7ssUnQ==" hashValue="9oN7nWkCAyEZib1RomSJTjmPpCY=" />
    /// ```
    ///
    /// The saltValue attribute value of ZUdHa+D8F/OAKP3I7ssUnQ== specifies that the user-
    /// supplied password must have this value prepended before it is run through the specified
    /// hashing algorithm to generate a resulting hash value for comparison.
    pub salt_value: Option<String>,
    /// Specifies the number of times the hashing function shall be iteratively run (runs using
    /// each iteration's result plus a 4 byte value (0-based, little endian) containing the number
    /// of the iteration as the input for the next iteration) when attempting to compare a user-
    /// supplied password with the value stored in the hashValue attribute.
    ///
    /// # Rationale
    ///
    /// Running the algorithm many times increases the cost of exhaustive search
    /// attacks correspondingly. Storing this value allows for the number of iterations to be
    /// increased over time to accommodate faster hardware (and hence the ability to run more
    /// iterations in less time).
    ///
    /// # Xml example
    ///
    /// Consider an Office Open XML document with the following information stored in one of its protection elements:
    /// ```xml
    /// <... spinCount="100000" hashValue="9oN7nWkCAyEZib1RomSJTjmPpCY=" />
    /// ```
    /// The spinCount attribute value of 100000 specifies that the hashing function must be run
    /// one hundred thousand times to generate a hash value for comparison with the
    /// hashValue attribute.
    pub spin_value: Option<u32>,
}

impl ModifyVerifier {
    pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
        let mut instance: Self = Default::default();

        for (attr, value) in &xml_node.attributes {
            match attr.as_str() {
                "algorithmName" => instance.algorithm_name = Some(value.clone()),
                "hashValue" => instance.hash_value = Some(value.clone()),
                "saltValue" => instance.salt_value = Some(value.clone()),
                "spinValue" => instance.spin_value = Some(value.parse()?),
                _ => (),
            }
        }

        Ok(instance)
    }
}

/// This element specifies within it fundamental presentation-wide properties.
///
/// # Xml example
///
/// Consider the following presentation with a single slide master and two slides. In addition to these
/// commonly used elements there can also be the specification of other properties such as slide size, notes size and
/// default text styles.
/// ```xml
/// <p:presentation xmlns:a="..." xmlns:r="..." xmlns:p="...">
///  <p:sldMasterIdLst>
///    <p:sldMasterId id="2147483648" r:id="rId1"/>
///  </p:sldMasterIdLst>
///  <p:sldIdLst>
///    <p:sldId id="256" r:id="rId3"/>
///    <p:sldId id="257" r:id="rId4"/>
///  </p:sldIdLst>
///  <p:sldSz cx="9144000" cy="6858000" type="screen4x3"/>
///  <p:notesSz cx="6858000" cy="9144000"/>
///  <p:defaultTextStyle>
///  ...
///  </p:defaultTextStyle>
/// </p:presentation>
/// ```
#[derive(Default, Debug, Clone)]
pub struct Presentation {
    /// Specifies the scaling to be used when the presentation is embedded in another
    /// document. The embedded slides are to be scaled by this percentage.
    ///
    /// Defaults to 50_000
    pub server_zoom: Option<msoffice_shared::drawingml::Percentage>,
    /// Specifies the first slide number in the presentation.
    ///
    /// Defaults to 1
    pub first_slide_num: Option<i32>,
    /// Specifies whether to show the header and footer placeholders on the title slides.
    ///
    /// Defaults to true
    pub show_special_placeholders_on_title_slide: Option<bool>,
    /// Specifies if the current view of the user interface is oriented right-to-left or left-to-right.
    /// The view is right-to-left is this value is set to true, and left-to-right otherwise.
    ///
    /// Defaults to false
    pub rtl: Option<bool>,
    /// Specifies whether to automatically remove personal information when the presentation
    /// document is saved.
    ///
    /// Defaults to false
    pub remove_personal_info_on_save: Option<bool>,
    /// Specifies whether the generating application is to be in a compatibility mode which
    /// serves to inform the user of any loss of content or functionality when working with older
    /// formats.
    ///
    /// Defaults to false
    pub compatibility_mode: Option<bool>,
    /// Specifies whether to use strict characters for starting and ending lines of Japanese text.
    ///
    /// Defaults to true
    pub strict_first_and_last_chars: Option<bool>,
    /// Specifies whether the generating application should automatically embed true type fonts or not.
    ///
    /// Defaults to false
    pub embed_true_type_fonts: Option<bool>,
    /// Specifies to save only the subset of characters used in the presentation when a font is embedded.
    ///
    /// Defaults to false
    pub save_subset_fonts: Option<bool>,
    /// Specifies whether the generating application should automatically compress all pictures
    /// for this presentation.
    ///
    /// Defaults to true
    pub auto_compress_pictures: Option<bool>,
    /// Specifies a seed for generating bookmark IDs to ensure IDs remain unique across the
    /// document. This value specifies the number to be used as the ID for the next new
    /// bookmark created.
    ///
    /// Defaults to 1
    pub bookmark_id_seed: Option<BookmarkIdSeed>,
    /// Specifies the conformance class (§2.1) to which the PresentationML document conforms.
    ///
    /// Defaults to ConformanceClass::Transitional
    pub conformance: Option<ConformanceClass>,
    /// This element specifies a list of identification information for the slide master slides that are available
    /// within the corresponding presentation.
    /// A slide master is a slide that is specifically designed to be a template for all related child layout slides.
    ///
    /// The SlideMasterIdListEntry specifies a slide master that is available within the corresponding presentation.
    /// A slide master is a slide that is specifically designed to be a template for all related child layout slides.
    ///
    /// # Xml example
    ///
    /// ```xml
    /// <p:presentation xmlns:a="..." xmlns:r="..." xmlns:p="..." embedTrueTypeFonts="1">
    ///   ...
    ///   <p:sldMasterIdLst>
    ///     <p:sldMasterId id="2147483648" r:id="rId1"/>
    ///   </p:sldMasterIdLst>
    ///   ...
    /// </p:presentation>
    pub slide_master_id_list: Vec<SlideMasterIdListEntry>,
    /// The specifies a list of identification information for the notes master slides that are available within the
    /// corresponding presentation. A notes master is a slide that is specifically designed for the printing of the slide
    /// along with any attached notes.
    ///
    /// The NotesMasterIdListEntry specifies a notes master that is available within the corresponding presentation.
    /// A notes master is a slide that is specifically designed for the printing of the slide along with any
    /// attached notes.
    ///
    /// # Xml example
    ///
    /// Consider the following specification of a notes master within a presentation
    /// ```xml
    /// <p:presentation xmlns:a="..." xmlns:r="..." xmlns:p="..." embedTrueTypeFonts="1">
    ///   ...
    ///   <p:notesMasterIdLst>
    ///     <p:notesMasterId r:id="rId8"/>
    ///   </p:notesMasterIdLst>
    ///   ...
    /// </p:presentation>
    /// ```xml
    ///
    /// # Note
    ///
    /// Even though the reference documentation states that this element is a list, the Xml schema states that it has
    /// only a single element.
    pub notes_master_id: Option<NotesMasterIdListEntry>,
    /// This element specifies a list of identification information for the handout master slides that are available
    /// within the corresponding presentation.
    /// A handout master is a slide that is specifically designed for printing as a handout.
    ///
    /// The HandoutMasterIdListEntry specifies a handout master that is available within the corresponding presentation. A handout
    /// master is a slide that is specifically designed for printing as a handout.
    ///
    /// # Xml example
    ///
    /// ```xml
    /// <p:presentation xmlns:a="..." xmlns:r="..." xmlns:p="..." embedTrueTypeFonts="1">
    ///   ...
    ///   <p:handoutMasterIdLst>
    ///     <p:handoutMasterId r:id="rId8"/>
    ///   </p:handoutMasterIdLst>
    ///   ...
    /// </p:presentation>
    /// ```
    ///
    /// # Note
    ///
    /// Even though the reference documentation states that this element is a list, the Xml schema states that it has
    /// only a single element.
    pub handout_master_id: Option<HandoutMasterIdListEntry>,
    /// This element specifies a list of identification information for the slides that are available within the
    /// corresponding presentation. A slide contains the information that is specific to a single slide such as slide-
    /// specific shape and text information.
    ///
    /// The SlideIdListEntry specifies a list of presentation slides. A presentation slide contains the information
    /// that is specific to a single slide such as slide-specific shape and text information.
    pub slide_id_list: Vec<SlideIdListEntry>,
    /// This element specifies the size of the presentation slide surface. Objects within a presentation slide can be
    /// specified outside these extents, but this is the size of background surface that is shown when the slide is
    /// presented or printed.
    ///
    /// # Xml example
    ///
    /// ```xml
    /// <p:presentation xmlns:a="..." xmlns:r="..." xmlns:p="..." embedTrueTypeFonts="1">
    ///   ...
    ///   <p:sldSz cx="9144000" cy="6858000" type="screen4x3"/>
    ///   ...
    /// </p:presentation>
    pub slide_size: Option<SlideSize>,
    /// This element specifies the size of slide surface used for notes slides and handout slides. Objects within a
    /// notes slide can be specified outside these extents, but the notes slide has a background surface of the
    /// specified size when presented or printed.
    /// This element is intended to specify the region to which content is fitted in any special format of printout
    /// the application might choose to generate, such as an outline handout.
    ///
    /// # Xml example
    ///
    /// Consider the following specifying of the size of a notes slide.
    /// ```xml
    /// <p:presentation xmlns:a="..." xmlns:r="..." xmlns:p="..." embedTrueTypeFonts="1">
    ///   ...
    ///   <p:notesSz cx="9144000" cy="6858000"/>
    ///   ...
    /// </p:presentation>
    /// ```
    pub notes_size: Option<msoffice_shared::drawingml::PositiveSize2D>,
    /// This element specifies that references to smart tags exist within this document.
    /// To denote the location of smart tags on individual runs of text, there smart tag identifier attributes are
    /// specified for each run to which a smart tag applies.
    /// These are further specified in the run property attributes within DrawingML.
    ///
    /// # Xml example
    ///
    /// ```xml
    /// <p:presentation>
    ///   ...
    ///   <p:smartTags r:id="rId1"/>
    /// </p:presentation>
    /// ```
    ///
    /// The presence of the smartTags element specifies that there is smart tag information within the PresentationML
    /// package. Individual runs are then inspected for the value of the smtId attribute to determine where smart tags
    /// might apply, for example:
    ///
    /// ```xml
    /// <p:txBody>
    ///  <a:bodyPr/>
    ///  <a:lstStyle/>
    ///  <a:p>
    ///    <a:r>
    ///      <a:rPr lang="en-US" dirty="0" smtId="1"/>
    ///      <a:t>CNTS</a:t>
    ///    </a:r>
    ///    <a:endParaRPr lang="en-US" dirty="0"/>
    ///  </a:p>
    /// </p:txBody>
    /// ```
    ///
    /// In the sample above there is a smart tag identifier of 1 specified for this run of text to denote that the text
    /// should be inspected for smart tag information.
    ///
    /// # Note
    ///
    /// For a complete definition of smart tags, which are semantically identical throughout Office Open XML,
    /// see §17.5.1.
    pub smart_tags: Option<RelationshipId>,
    /// This element specifies a list of fonts that are embedded within the corresponding presentation. The font data
    /// for these fonts is stored alongside the other document parts within the document container. The actual font
    /// data is referenced within the EmbeddedFontListEntry element.
    ///
    /// The EmbeddedFontListEntry element specifies an embedded font. Once specified, this font is available for use within the
    /// presentation.
    /// Within a font specification there can be regular, bold, italic and boldItalic versions of the font specified.
    /// The actual font data for each of these is referenced using a relationships file that contains links to all
    /// available fonts.
    /// This font data contains font information for each of the characters to be made available in each version of the
    /// font.
    ///
    /// # Xml example
    ///
    /// ```xml
    /// <p:embeddedFont>
    ///   <p:font typeface="MyFont" pitchFamily="34" charset="0"/>
    ///   <p:regular r:id="rId2"/>
    /// </p:embeddedFont>
    /// ```
    ///
    /// # Note
    ///
    /// Not all characters for a typeface must be stored. It is up to the generating application to determine
    /// which characters are to be stored in the corresponding font data files.
    pub embedded_font_list: Vec<Box<EmbeddedFontListEntry>>,
    /// This element specifies a list of all custom shows that are available within the corresponding presentation.
    /// A custom show is a defined slide sequence that allows for the displaying of the slides with the presentation in
    /// any arbitrary order.
    pub custom_show_list: Vec<CustomShow>,
    /// This element specifies that the corresponding presentation contains a photo album. A photo album specifies a
    /// list of images within the presentation that spread across one or more slides, all of which share a consistent
    /// layout. Each image in the album is formatted with a consistent style. This functionality enables the application
    /// to manage all of the images together and modify their ordering, layout, and formatting as a set.
    ///
    /// This element does not enforce the specified properties on individual photo album images; rather, it specifies
    /// common settings that should be applied by default to all photo album images and their containing slides.
    /// Images that are part of the photo album are identified by the presence of the isPhoto element in the definition
    /// of the picture.
    ///
    /// # Xml example
    ///
    /// ```xml
    /// <p:presentation xmlns:a="..." xmlns:r="..." xmlns:p="..." embedTrueTypeFonts="1">
    ///   ...
    ///   <p:photoAlbum bw="1" layout="2pic"/>
    ///   ...
    /// </p:presentation>
    pub photo_album: Option<PhotoAlbum>,
    /// This element allows for the specifying of customer defined data within the PresentationML framework.
    /// References to custom data or tags can be defined within this list.
    ///
    /// The elements of this list specifies customer data which allows for the specifying and persistence of customer
    /// specific data within the presentation.
    pub customer_data_list: Option<CustomerDataList>,
    /// This element specifies the presentation-wide kinsoku settings that define the line breaking behaviour of East
    /// Asian text within the corresponding presentation.
    pub kinsoku: Option<Box<Kinsoku>>,
    /// This element specifies the default text styles that are to be used within the presentation.
    /// The text style defined here can be referenced when inserting a new slide if that slide is not associated with a
    /// master slide or if no styling information has been otherwise specified for the text within the
    /// presentation slide.
    pub default_text_style: Option<Box<msoffice_shared::drawingml::TextListStyle>>,
    /// This element specifies the write protection settings which have been applied to a PresentationML document.
    /// Write protection refers to a mode in which the document's contents should not be modified, and the document
    /// should not be resaved using the same file name.
    ///
    /// When present, the application shall require a password to enable modifications to the document. If the
    /// supplied password does not match the hash value in this attribute, then write protection shall be enabled.
    /// If this element is omitted, then no write protection shall be applied to the current document.
    /// Since this protection does not encrypt the document, malicious applications might circumvent its use.
    ///
    /// The password supplied to the algorithm is to be a UTF-16LE encoded string; strings longer than 510 octets are
    /// truncated to 510 octets. If there is a leading BOM character (U+FEFF) in the encoded password it is removed
    /// before hash calculation. The attributes of this element specify the algorithm to be used to verify the password
    /// provided by the user.
    ///
    /// # Xml example
    ///
    /// Consider a PresentationML document that can only be opened in a write protected state unless a
    /// password is provided, in which case the file would be opened in an editable state. This requirement would be
    /// specified using the following PresentationML:
    /// ```xml
    /// <p:modifyVerifier p:algorithmName="SHA-512" ...
    /// p:hashValue="9oN7nWkCAyEZib1RomSJTjmPpCY=" ... />
    /// ```
    /// ...In order for the hosting application to enable edits to the document, the hosting application would have to
    /// be provided with a password that the hosting application would then hash using the algorithm specified by the
    /// algorithm attributes and compare to the value of the hashValue attribute ( 9oN7nWkCAyEZib1RomSJTjmPpCY= ).
    /// If the two values matched, the file would be opened in an editable state.
    pub modify_verifier: Option<Box<ModifyVerifier>>,
}

impl Presentation {
    pub fn from_zip<R>(zipper: &mut zip::ZipArchive<R>) -> Result<Self>
    where
        R: Read + Seek,
    {
        let mut presentation_file = zipper.by_name("ppt/presentation.xml")?;
        let mut xml_string = String::new();
        presentation_file.read_to_string(&mut xml_string)?;

        let root = XmlNode::from_str(xml_string.as_str())?;
        Self::from_xml_element(&root)
    }

    pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
        let mut instance: Self = Default::default();

        for (attr, value) in &xml_node.attributes {
            match attr.as_str() {
                "serverZoom" => instance.server_zoom = Some(value.parse()?),
                "firstSlideNum" => instance.first_slide_num = Some(value.parse()?),
                "showSpecialPlsOnTitleSld" => {
                    instance.show_special_placeholders_on_title_slide = Some(parse_xml_bool(value)?);
                }
                "rtl" => instance.rtl = Some(parse_xml_bool(value)?),
                "removePersonalInfoOnSave" => instance.remove_personal_info_on_save = Some(parse_xml_bool(value)?),
                "compatMode" => instance.compatibility_mode = Some(parse_xml_bool(value)?),
                "strictFirstAndLastChars" => instance.strict_first_and_last_chars = Some(parse_xml_bool(value)?),
                "embedTrueTypeFonts" => instance.embed_true_type_fonts = Some(parse_xml_bool(value)?),
                "saveSubsetFonts" => instance.save_subset_fonts = Some(parse_xml_bool(value)?),
                "autoCompressPictures" => instance.auto_compress_pictures = Some(parse_xml_bool(value)?),
                "bookmarkIdSeed" => instance.bookmark_id_seed = Some(value.parse()?),
                "conformance" => instance.conformance = Some(value.parse()?),
                _ => (),
            }
        }

        for child_node in &xml_node.child_nodes {
            match child_node.local_name() {
                "sldMasterIdLst" => {
                    for slide_master_id_node in &child_node.child_nodes {
                        instance
                            .slide_master_id_list
                            .push(SlideMasterIdListEntry::from_xml_element(slide_master_id_node)?);
                    }
                }
                "notesMasterIdLst" => {
                    if let Some(notes_master_id_node) = child_node.child_nodes.get(0) {
                        instance.notes_master_id =
                            Some(NotesMasterIdListEntry::from_xml_element(notes_master_id_node)?);
                    }
                }
                "handoutMasterIdLst" => {
                    if let Some(handout_master_id_node) = child_node.child_nodes.get(0) {
                        instance.handout_master_id =
                            Some(HandoutMasterIdListEntry::from_xml_element(handout_master_id_node)?);
                    }
                }
                "sldIdLst" => {
                    for slide_id_node in &child_node.child_nodes {
                        instance
                            .slide_id_list
                            .push(SlideIdListEntry::from_xml_element(slide_id_node)?);
                    }
                }
                "sldSz" => instance.slide_size = Some(SlideSize::from_xml_element(child_node)?),
                "notesSz" => {
                    instance.notes_size = Some(msoffice_shared::drawingml::PositiveSize2D::from_xml_element(
                        child_node,
                    )?)
                }
                "smartTags" => {
                    let r_id_attr = child_node
                        .attribute("r:id")
                        .ok_or_else(|| MissingAttributeError::new(child_node.name.clone(), "r:id"))?;
                    instance.smart_tags = Some(r_id_attr.clone());
                }
                "embeddedFontLst" => {
                    for embedded_font_node in &child_node.child_nodes {
                        instance
                            .embedded_font_list
                            .push(Box::new(EmbeddedFontListEntry::from_xml_element(embedded_font_node)?));
                    }
                }
                "custShowLst" => {
                    for custom_show_node in &child_node.child_nodes {
                        instance
                            .custom_show_list
                            .push(CustomShow::from_xml_element(custom_show_node)?);
                    }
                }
                "photoAlbum" => instance.photo_album = Some(PhotoAlbum::from_xml_element(child_node)?),
                "custDataLst" => instance.customer_data_list = Some(CustomerDataList::from_xml_element(child_node)?),
                "kinsoku" => instance.kinsoku = Some(Box::new(Kinsoku::from_xml_element(child_node)?)),
                "defaultTextStyle" => {
                    instance.default_text_style = Some(Box::new(
                        msoffice_shared::drawingml::TextListStyle::from_xml_element(child_node)?,
                    ))
                }
                "modifyVerifier" => {
                    instance.modify_verifier = Some(Box::new(ModifyVerifier::from_xml_element(child_node)?))
                }
                _ => (),
            }
        }

        Ok(instance)
    }
}