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
use crate::{
error::{MissingAttributeError, MissingChildNodeError, NotGroupMemberError},
shared::relationship::RelationshipId,
xml::XmlNode,
xsdtypes::{XsdChoice, XsdType},
};
pub type Result<T> = ::std::result::Result<T, Box<dyn (::std::error::Error)>>;
#[derive(Debug, Clone, PartialEq)]
pub struct AudioCD {
/// This element specifies the start point for a CD Audio sound element. Encompassed within this element are the
/// time and track at which the sound should begin its playback. This element is used in conjunction with an Audio
/// End Time element to specify the time span for an entire audioCD sound element.
///
/// # Xml example
///
/// ```xml
/// <a:audioCd>
/// <a:st track="1" time="2"/>
/// <a:end track="3" time="65"/>
/// </a:audioCd>
/// ```
///
/// In the above example, the audioCD sound element shown specifies for a portion of audio spanning from 2
/// seconds into the first track to 1 minute, 5 seconds into the third track.
pub start_time: AudioCDTime,
/// This element specifies the end point for a CD Audio sound element. Encompassed within this element are the
/// time and track at which the sound should halt its playback. This element is used in conjunction with an Audio
/// Start Time element to specify the time span for an entire audioCD sound element.
///
/// # Xml example
///
/// ```xml
/// <a:audioCd>
/// <a:st track="1" time="2"/>
/// <a:end track="3" time="65"/>
/// </a:audioCd>
/// ```
///
/// In the above example, the audioCD sound element shown specifies for a portion of audio spanning from 2
/// seconds into the first track to 1 minute, 5 seconds into the third track.
pub end_time: AudioCDTime,
}
impl AudioCD {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let mut start_time = None;
let mut end_time = None;
for child_node in &xml_node.child_nodes {
match child_node.local_name() {
"st" => start_time = Some(AudioCDTime::from_xml_element(child_node)?),
"end" => end_time = Some(AudioCDTime::from_xml_element(child_node)?),
_ => (),
}
}
let start_time = start_time.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "st"))?;
let end_time = end_time.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "end"))?;
Ok(Self { start_time, end_time })
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct AudioFile {
/// Specifies the identification information for a linked object. This attribute is used to
/// specify the location of an object that does not reside within this file.
pub link: RelationshipId,
/// Specifies the content type for the external file that is referenced by this element. Content
/// types define a media type, a subtype, and an optional set of parameters, as defined in
/// Part 2. If a rendering application cannot process external content of the content type
/// specified, then the specified content can be ignored.
///
/// If this attribute is omitted, application should attempt to determine the content type by
/// reading the contents of the relationship’s target.
///
/// Suggested audio types:
/// * aiff
/// * midi
/// * ogg
/// * mpeg
///
/// A producer that wants interoperability should use the following standard format:
/// * audio
/// * mpeg ISO
/// * IEC 11172-3
pub content_type: Option<String>,
}
impl AudioFile {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let mut link = None;
let mut content_type = None;
for (attr, value) in &xml_node.attributes {
match attr.as_str() {
"r:link" => link = Some(value.clone()),
"contentType" => content_type = Some(value.clone()),
_ => (),
}
}
let link = link.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "r:link"))?;
Ok(Self { link, content_type })
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct AudioCDTime {
/// Specifies which track of the CD this Audio begins playing on. This attribute is required and
/// cannot be omitted.
pub track: u8,
/// Specifies the time in seconds that the CD Audio should be started at.
///
/// Defaults to 0
pub time: Option<u32>,
}
impl AudioCDTime {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let mut track = None;
let mut time = None;
for (attr, value) in &xml_node.attributes {
match attr.as_str() {
"track" => track = Some(value.parse()?),
"time" => time = Some(value.parse()?),
_ => (),
}
}
let track = track.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "track"))?;
Ok(Self { track, time })
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct QuickTimeFile {
/// Specifies the identification information for a linked object. This attribute is used to
/// specify the location of an object that does not reside within this file.
pub link: RelationshipId,
}
impl QuickTimeFile {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let link = xml_node
.attributes
.get("r:link")
.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "r:link"))?
.clone();
Ok(Self { link })
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct VideoFile {
/// Specifies the identification information for a linked video file. This attribute is used to
/// specify the location of an object that does not reside within this file.
pub link: RelationshipId,
/// Specifies the content type for the external file that is referenced by this element. Content
/// types define a media type, a subtype, and an optional set of parameters, as defined in
/// Part 2. If a rendering application cannot process external content of the content type
/// specified, then the specified content can be ignored.
///
/// Suggested video formats:
/// * avi
/// * mpg
/// * mpeg
/// * ogg
/// * quicktime
/// * vc1
///
/// If this attribute is omitted, application should attempt to determine the content type by
/// reading the contents of the relationship’s target.
pub content_type: Option<String>,
}
impl VideoFile {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let mut link = None;
let mut content_type = None;
for (attr, value) in &xml_node.attributes {
match attr.as_str() {
"r:link" => link = Some(value.clone()),
"contentType" => content_type = Some(value.clone()),
_ => (),
}
}
let link = link.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "r:link"))?;
Ok(Self { link, content_type })
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct EmbeddedWAVAudioFile {
/// Specifies the identification information for an embedded audio file. This attribute is used
/// to specify the location of an object that resides locally within the file.
pub embed_rel_id: RelationshipId,
/// Specifies the original name or given short name for the corresponding sound. This is used
/// to distinguish this sound from others by providing a human readable name for the
/// attached sound should the user need to identify the sound among others within the UI.
pub name: Option<String>,
//pub built_in: Option<bool>, // false
}
impl EmbeddedWAVAudioFile {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let mut embed_rel_id = None;
let mut name = None;
for (attr, value) in &xml_node.attributes {
match attr.as_str() {
"r:embed" => embed_rel_id = Some(value.clone()),
"name" => name = Some(value.clone()),
_ => (),
}
}
let embed_rel_id = embed_rel_id.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "r:embed"))?;
Ok(Self { embed_rel_id, name })
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Media {
/// This element specifies the existence of Audio from a CD. This element is specified within the non-visual
/// properties of an object. The audio shall be attached to an object as this is how it is represented within the
/// document. The actual playing of the sound however is done within the timing node list that is specified under
/// the timing element.
///
/// # Xml example
///
/// ```xml
/// <p:pic>
/// <p:nvPicPr>
/// <p:cNvPr id="7" name="Rectangle 6">
/// <a:hlinkClick r:id="" action="ppaction://media"/>
/// </p:cNvPr>
/// <p:cNvPicPr>
/// <a:picLocks noRot="1"/>
/// </p:cNvPicPr>
/// <p:nvPr>
/// <a:audioCd>
/// <a:st track="1"/>
/// <a:end track="3" time="65"/>
/// </a:audioCd>
/// </p:nvPr>
/// </p:nvPicPr>
/// ...
/// </p:pic>
/// ```
///
/// In the above example, we see that there is a single audioCD element attached to this picture. This picture is
/// placed within the document just as a normal picture or shape would be. The id of this picture, namely 7 in this
/// case, is used to refer to this audioCD element from within the timing node list. For this example we see that the
/// audio for this CD starts playing at the 0 second mark on the first track and ends on the 1 minute 5 second mark
/// of the third track.
AudioCd(AudioCD),
/// This element specifies the existence of an audio WAV file. This element is specified within the non-visual
/// properties of an object. The audio shall be attached to an object as this is how it is represented within the
/// document. The actual playing of the audio however is done within the timing node list that is specified under the
/// timing element.
///
/// # Xml example
///
/// ```xml
/// <p:pic>
/// <p:nvPicPr>
/// <p:cNvPr id="7" name="Rectangle 6">
/// <a:hlinkClick r:id="" action="ppaction://media"/>
/// </p:cNvPr>
/// <p:cNvPicPr>
/// <a:picLocks noRot="1"/>
/// </p:cNvPicPr>
/// <p:nvPr>
/// <a:wavAudioFile r:embed="rId2"/>
/// </p:nvPr>
/// </p:nvPicPr>
/// ...
/// </p:pic>
/// ```
///
/// In the above example, we see that there is a single wavAudioFile element attached to this picture. This picture
/// is placed within the document just as a normal picture or shape would be. The id of this picture, namely 7 in this
/// case, is used to refer to this wavAudioFile element from within the timing node list. The Embedded relationship
/// id is used to retrieve the actual audio file for playback purposes.
WavAudioFile(EmbeddedWAVAudioFile),
/// This element specifies the existence of an audio file. This element is specified within the non-visual properties of
/// an object. The audio shall be attached to an object as this is how it is represented within the document. The
/// actual playing of the audio however is done within the timing node list that is specified under the timing
/// element.
///
/// # Xml example
///
/// ```xml
/// <p:pic>
/// <p:nvPicPr>
/// <p:cNvPr id="7" name="Rectangle 6">
/// <a:hlinkClick r:id="" action="ppaction://media"/>
/// </p:cNvPr>
/// <p:cNvPicPr>
/// <a:picLocks noRot="1"/>
/// </p:cNvPicPr>
/// <p:nvPr>
/// <a:audioFile r:link="rId1"/>
/// </p:nvPr>
/// </p:nvPicPr>
/// ...
/// </p:pic>
/// ```
///
/// In the above example, we see that there is a single audioFile element attached to this picture. This picture is
/// placed within the document just as a normal picture or shape would be. The id of this picture, namely 7 in this
/// case, is used to refer to this audioFile element from within the timing node list. The Linked relationship id is
/// used to retrieve the actual audio file for playback purposes.
AudioFile(AudioFile),
/// This element specifies the existence of a video file. This element is specified within the non-visual properties of
/// an object. The video shall be attached to an object as this is how it is represented within the document. The
/// actual playing of the video however is done within the timing node list that is specified under the timing
/// element.
///
/// # Xml example
///
/// ```xml
/// <p:pic>
/// <p:nvPicPr>
/// <p:cNvPr id="7" name="Rectangle 6">
/// <a:hlinkClick r:id="" action="ppaction://media"/>
/// </p:cNvPr>
/// <p:cNvPicPr>
/// <a:picLocks noRot="1"/>
/// </p:cNvPicPr>
/// <p:nvPr>
/// <a:videoFile r:link="rId1"/>
/// </p:nvPr>
/// </p:nvPicPr>
/// ...
/// </p:pic>
/// ```
///
/// In the above example, we see that there is a single videoFile element attached to this picture. This picture is
/// placed within the document just as a normal picture or shape would be. The id of this picture, namely 7 in this
/// case, is used to refer to this videoFile element from within the timing node list. The Linked relationship id is
/// used to retrieve the actual video file for playback purposes.
VideoFile(VideoFile),
/// This element specifies the existence of a QuickTime file. This element is specified within the non-visual
/// properties of an object. The QuickTime file shall be attached to an object as this is how it is represented
/// within the document. The actual playing of the QuickTime however is done within the timing node list that is
/// specified under the timing element.
///
/// # Xml example
///
/// ```xml
/// <p:pic>
/// <p:nvPicPr>
/// <p:cNvPr id="7" name="Rectangle 6">
/// <a:hlinkClick r:id="" action="ppaction://media"/>
/// </p:cNvPr>
/// <p:cNvPicPr>
/// <a:picLocks noRot="1"/>
/// </p:cNvPicPr>
/// <p:nvPr>
/// <a:quickTimeFile r:link="rId1"/>
/// </p:nvPr>
/// </p:nvPicPr>
/// ...
/// </p:pic>
/// ```
///
/// In the above example, we see that there is a single quickTimeFile element attached to this picture. This picture
/// is placed within the document just as a normal picture or shape would be. The id of this picture, namely 7 in this
/// case, is used to refer to this quickTimeFile element from within the timing node list. The Linked relationship id
/// is used to retrieve the actual video file for playback purposes.
QuickTimeFile(QuickTimeFile),
}
impl XsdType for Media {
fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
match xml_node.local_name() {
"audioCd" => Ok(Media::AudioCd(AudioCD::from_xml_element(xml_node)?)),
"wavAudioFile" => Ok(Media::WavAudioFile(EmbeddedWAVAudioFile::from_xml_element(xml_node)?)),
"audioFile" => Ok(Media::AudioFile(AudioFile::from_xml_element(xml_node)?)),
"videoFile" => Ok(Media::VideoFile(VideoFile::from_xml_element(xml_node)?)),
"quickTimeFile" => Ok(Media::QuickTimeFile(QuickTimeFile::from_xml_element(xml_node)?)),
_ => Err(Box::new(NotGroupMemberError::new(xml_node.name.clone(), "EG_Media"))),
}
}
}
impl XsdChoice for Media {
fn is_choice_member<T>(name: T) -> bool
where
T: AsRef<str>,
{
match name.as_ref() {
"audioCd" | "wavAudioFile" | "audioFile" | "videoFile" | "quickTimeFile" => true,
_ => false,
}
}
}