1use crate::core::{append_usize, escape_xml};
4use crate::generator::slide_content::presentation_settings::PresentationSettings;
5use crate::generator::slide_content::embedded_fonts::EmbeddedFontList;
6use crate::generator::charts::chart_embedding_filename;
7use crate::generator::layout_parts::append_layout_content_type_overrides;
8use crate::generator::layout_parts::STANDARD_LAYOUT_COUNT;
9use crate::generator::theme_xml::layout_rel_target;
10
11pub const SLIDE_MASTER_ID: u32 = 2_147_483_648;
13pub const SLIDE_LAYOUT_ID: u32 = SLIDE_MASTER_ID + 1;
15pub const NOTES_MASTER_ID: u32 = SLIDE_LAYOUT_ID + STANDARD_LAYOUT_COUNT as u32;
17pub const HANDOUT_MASTER_ID: u32 = NOTES_MASTER_ID + 1;
19
20const DEFAULT_TEXT_STYLE: &str = r#"<p:defaultTextStyle><a:defPPr><a:defRPr lang="en-US"/></a:defPPr><a:lvl1pPr marL="0" algn="l" defTabSz="457200" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1"><a:defRPr sz="1800" kern="1200"><a:solidFill><a:schemeClr val="tx1"/></a:solidFill><a:latin typeface="+mn-lt"/><a:ea typeface="+mn-ea"/><a:cs typeface="+mn-cs"/></a:defRPr></a:lvl1pPr><a:lvl2pPr marL="457200" algn="l" defTabSz="457200" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1"><a:defRPr sz="1800" kern="1200"><a:solidFill><a:schemeClr val="tx1"/></a:solidFill><a:latin typeface="+mn-lt"/><a:ea typeface="+mn-ea"/><a:cs typeface="+mn-cs"/></a:defRPr></a:lvl2pPr><a:lvl3pPr marL="914400" algn="l" defTabSz="457200" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1"><a:defRPr sz="1800" kern="1200"><a:solidFill><a:schemeClr val="tx1"/></a:solidFill><a:latin typeface="+mn-lt"/><a:ea typeface="+mn-ea"/><a:cs typeface="+mn-cs"/></a:defRPr></a:lvl3pPr><a:lvl4pPr marL="1371600" algn="l" defTabSz="457200" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1"><a:defRPr sz="1800" kern="1200"><a:solidFill><a:schemeClr val="tx1"/></a:solidFill><a:latin typeface="+mn-lt"/><a:ea typeface="+mn-ea"/><a:cs typeface="+mn-cs"/></a:defRPr></a:lvl4pPr><a:lvl5pPr marL="1828800" algn="l" defTabSz="457200" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1"><a:defRPr sz="1800" kern="1200"><a:solidFill><a:schemeClr val="tx1"/></a:solidFill><a:latin typeface="+mn-lt"/><a:ea typeface="+mn-ea"/><a:cs typeface="+mn-cs"/></a:defRPr></a:lvl5pPr><a:lvl6pPr marL="2286000" algn="l" defTabSz="457200" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1"><a:defRPr sz="1800" kern="1200"><a:solidFill><a:schemeClr val="tx1"/></a:solidFill><a:latin typeface="+mn-lt"/><a:ea typeface="+mn-ea"/><a:cs typeface="+mn-cs"/></a:defRPr></a:lvl6pPr><a:lvl7pPr marL="2743200" algn="l" defTabSz="457200" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1"><a:defRPr sz="1800" kern="1200"><a:solidFill><a:schemeClr val="tx1"/></a:solidFill><a:latin typeface="+mn-lt"/><a:ea typeface="+mn-ea"/><a:cs typeface="+mn-cs"/></a:defRPr></a:lvl7pPr><a:lvl8pPr marL="3200400" algn="l" defTabSz="457200" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1"><a:defRPr sz="1800" kern="1200"><a:solidFill><a:schemeClr val="tx1"/></a:solidFill><a:latin typeface="+mn-lt"/><a:ea typeface="+mn-ea"/><a:cs typeface="+mn-cs"/></a:defRPr></a:lvl8pPr><a:lvl9pPr marL="3657600" algn="l" defTabSz="457200" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1"><a:defRPr sz="1800" kern="1200"><a:solidFill><a:schemeClr val="tx1"/></a:solidFill><a:latin typeface="+mn-lt"/><a:ea typeface="+mn-ea"/><a:cs typeface="+mn-cs"/></a:defRPr></a:lvl9pPr></p:defaultTextStyle>"#;
21
22const CONTENT_TYPES_HEADER: &str = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
23<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
24<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
25<Default Extension="xml" ContentType="application/xml"/>
26<Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/>"#;
27
28fn append_image_content_type_defaults(xml: &mut String, media_exts: &[String]) {
29 let mut has_jpg = false;
30 let mut has_png = false;
31 let mut has_gif = false;
32 for ext in media_exts {
33 match ext.as_str() {
34 "jpg" | "jpeg" => has_jpg = true,
35 "png" => has_png = true,
36 "gif" => has_gif = true,
37 _ => {}
38 }
39 }
40 if has_jpg {
41 xml.push_str(r#"<Default Extension="jpg" ContentType="image/jpeg"/>"#);
42 }
43 if has_png {
44 xml.push_str(r#"<Default Extension="png" ContentType="image/png"/>"#);
45 }
46 if has_gif {
47 xml.push_str(r#"<Default Extension="gif" ContentType="image/gif"/>"#);
48 }
49}
50
51pub fn content_types_opening(media_exts: &[String], chart_count: usize) -> String {
52 let mut xml = String::from(
53 r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
54<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">"#,
55 );
56 append_image_content_type_defaults(&mut xml, media_exts);
57 xml.push_str(
58 r#"<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
59<Default Extension="xml" ContentType="application/xml"/>"#,
60 );
61 if chart_count > 0 {
62 xml.push_str(
63 r#"<Default Extension="xlsx" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"/>"#,
64 );
65 }
66 xml.push_str(
67 r#"<Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/>"#,
68 );
69 xml
70}
71
72const CONTENT_TYPES_FOOTER: &str = r#"
73<Override PartName="/ppt/slideMasters/slideMaster1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"/>
74<Override PartName="/ppt/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>
75<Override PartName="/ppt/tableStyles.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml"/>
76<Override PartName="/ppt/viewProps.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml"/>
77<Override PartName="/ppt/presProps.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presProps+xml"/>
78<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
79<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
80</Types>"#;
81
82const SLIDE_OVERRIDE_PREFIX: &str =
83 "\n<Override PartName=\"/ppt/slides/slide";
84const SLIDE_OVERRIDE_SUFFIX: &str =
85 ".xml\" ContentType=\"application/vnd.openxmlformats-officedocument.presentationml.slide+xml\"/>";
86
87fn append_slide_overrides(xml: &mut String, slides: usize) {
88 for i in 1..=slides {
89 xml.push_str(SLIDE_OVERRIDE_PREFIX);
90 append_usize(xml, i);
91 xml.push_str(SLIDE_OVERRIDE_SUFFIX);
92 }
93}
94
95pub const SLIDE_MASTER_REL_ID: usize = 1;
97
98pub fn first_slide_rel_id(_has_notes: bool, _has_handout: bool) -> usize {
100 2
101}
102
103pub fn slide_rel_id(slide_num: usize, _has_notes: bool, _has_handout: bool) -> usize {
105 slide_num + 1
106}
107
108pub fn notes_master_rel_id(slide_count: usize) -> usize {
110 slide_count + 2
111}
112
113pub fn handout_master_rel_id(slide_count: usize, has_notes: bool) -> usize {
115 slide_count + 2 + usize::from(has_notes)
116}
117
118pub fn pres_props_rel_id(slide_count: usize, has_notes: bool, has_handout: bool) -> usize {
120 slide_count + 2 + usize::from(has_notes) + usize::from(has_handout)
121}
122
123pub fn view_props_rel_id(slide_count: usize, has_notes: bool, has_handout: bool) -> usize {
125 pres_props_rel_id(slide_count, has_notes, has_handout) + 1
126}
127
128pub fn theme_rel_id(slide_count: usize, has_notes: bool, has_handout: bool) -> usize {
130 pres_props_rel_id(slide_count, has_notes, has_handout) + 2
131}
132
133pub fn table_styles_rel_id(slide_count: usize, has_notes: bool, has_handout: bool) -> usize {
135 pres_props_rel_id(slide_count, has_notes, has_handout) + 3
136}
137
138pub fn slide_id_value(slide_num: usize) -> usize {
140 255 + slide_num
141}
142
143pub fn create_content_types_xml(slides: usize) -> String {
145 let mut xml = String::with_capacity(1024 + slides * 140);
146 xml.push_str(CONTENT_TYPES_HEADER);
147 append_slide_overrides(&mut xml, slides);
148 append_layout_content_type_overrides(&mut xml, STANDARD_LAYOUT_COUNT);
149 xml.push_str(CONTENT_TYPES_FOOTER);
150 xml
151}
152
153const PACKAGE_RELS_XML: &str = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
154<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
155<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="ppt/presentation.xml"/>
156<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
157<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
158</Relationships>"#;
159
160pub fn create_rels_xml() -> &'static str {
162 PACKAGE_RELS_XML
163}
164
165pub fn create_rels_xml_with_signature(has_signature: bool) -> String {
167 let mut xml = String::from(PACKAGE_RELS_XML);
168 if has_signature {
169 if let Some(pos) = xml.rfind("</Relationships>") {
172 xml.insert_str(
173 pos,
174 r#"<Relationship Id="rId4" Type="http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin" Target="_xmlsignatures/origin.sigs"/>"#,
175 );
176 }
177 }
178 xml
179}
180
181pub fn create_presentation_rels_xml(slides: usize) -> String {
183 create_presentation_rels_xml_full(slides, false, false)
184}
185
186pub fn create_presentation_rels_xml_with_notes(slides: usize) -> String {
188 create_presentation_rels_xml_full(slides, true, false)
189}
190
191pub fn create_presentation_rels_xml_full(
193 slides: usize,
194 has_notes: bool,
195 has_handout: bool,
196) -> String {
197 let mut xml = String::with_capacity(768 + slides * 120);
198 xml.push_str(
199 r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
200<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
201<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="slideMasters/slideMaster1.xml"/>"#,
202 );
203
204 for i in 1..=slides {
205 let rid = slide_rel_id(i, has_notes, has_handout);
206 xml.push_str("\n<Relationship Id=\"rId");
207 append_usize(&mut xml, rid);
208 xml.push_str("\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide\" Target=\"slides/slide");
209 append_usize(&mut xml, i);
210 xml.push_str(".xml\"/>");
211 }
212
213 if has_notes {
214 xml.push_str("\n<Relationship Id=\"rId");
215 append_usize(&mut xml, notes_master_rel_id(slides));
216 xml.push_str("\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster\" Target=\"notesMasters/notesMaster1.xml\"/>");
217 }
218
219 if has_handout {
220 xml.push_str("\n<Relationship Id=\"rId");
221 append_usize(&mut xml, handout_master_rel_id(slides, has_notes));
222 xml.push_str("\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/handoutMaster\" Target=\"handoutMasters/handoutMaster1.xml\"/>");
223 }
224
225 xml.push_str("\n<Relationship Id=\"rId");
226 append_usize(&mut xml, pres_props_rel_id(slides, has_notes, has_handout));
227 xml.push_str("\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps\" Target=\"presProps.xml\"/>");
228
229 xml.push_str("\n<Relationship Id=\"rId");
230 append_usize(&mut xml, view_props_rel_id(slides, has_notes, has_handout));
231 xml.push_str("\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/viewProps\" Target=\"viewProps.xml\"/>");
232
233 xml.push_str("\n<Relationship Id=\"rId");
234 append_usize(&mut xml, theme_rel_id(slides, has_notes, has_handout));
235 xml.push_str("\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme\" Target=\"theme/theme1.xml\"/>");
236
237 xml.push_str("\n<Relationship Id=\"rId");
238 append_usize(&mut xml, table_styles_rel_id(slides, has_notes, has_handout));
239 xml.push_str("\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles\" Target=\"tableStyles.xml\"/>");
240
241 xml.push_str("\n</Relationships>");
242 xml
243}
244
245pub fn create_presentation_rels_xml_full_with_fonts(
247 slides: usize,
248 has_notes: bool,
249 has_handout: bool,
250 fonts: &EmbeddedFontList,
251) -> String {
252 let mut xml = create_presentation_rels_xml_full(slides, has_notes, has_handout);
253
254 for font in fonts.fonts() {
255 let target = font.rel_target();
256 xml.insert_str(
257 xml.rfind("</Relationships>").unwrap_or(xml.len()),
258 &format!(
259 r#"<Relationship Id="{}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/font" Target="{}"/>"#,
260 font.relationship_id,
261 escape_xml(&target),
262 ),
263 );
264 }
265
266 xml
267}
268
269pub fn create_table_styles_xml() -> &'static str {
271 r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
272<a:tblStyleLst xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" def="{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}"/>"#
273}
274
275pub fn create_pres_props_xml(settings: Option<&PresentationSettings>) -> String {
277 let mut xml = String::from(
278 r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><p:presentationPr xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">"#,
279 );
280
281 if let Some(s) = settings {
282 if let Some(ref show) = s.slide_show {
283 xml.push_str(&show.to_xml());
284 }
285 }
289
290 xml.push_str(
291 r#"<p:extLst><p:ext uri="{E76CE94A-603C-4142-B9EB-6D1370010A27}"><p14:discardImageEditData xmlns:p14="http://schemas.microsoft.com/office/powerpoint/2010/main" val="0"/></p:ext><p:ext uri="{D31A062A-798A-4329-ABDD-BBA856620510}"><p14:defaultImageDpi xmlns:p14="http://schemas.microsoft.com/office/powerpoint/2010/main" val="0"/></p:ext><p:ext uri="{FD5EFAAD-0ECE-453E-9831-46B23BE46B34}"><p15:chartTrackingRefBased xmlns:p15="http://schemas.microsoft.com/office/powerpoint/2012/main" val="0"/></p:ext></p:extLst></p:presentationPr>"#,
292 );
293 xml
294}
295
296pub fn create_view_props_xml() -> &'static str {
298 r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
305<p:viewPr xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
306<p:normalViewPr><p:restoredLeft sz="15620"/><p:restoredTop sz="94660"/></p:normalViewPr>
307<p:slideViewPr><p:cSldViewPr><p:cViewPr varScale="1"><p:scale><a:sx n="64" d="100"/><a:sy n="64" d="100"/></p:scale><p:origin x="-1392" y="-96"/></p:cViewPr><p:guideLst/></p:cSldViewPr></p:slideViewPr>
308<p:notesTextViewPr><p:cViewPr><p:scale><a:sx n="1" d="1"/><a:sy n="1" d="1"/></p:scale><p:origin x="0" y="0"/></p:cViewPr></p:notesTextViewPr>
309<p:gridSpacing cx="72008" cy="72008"/>
310</p:viewPr>"#
311}
312
313pub fn create_presentation_xml(
315 _title: &str,
316 slides: usize,
317 has_notes: bool,
318 has_handout: bool,
319) -> String {
320 let mut xml = String::with_capacity(896 + slides * 48);
321 xml.push_str(
322 r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
323<p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" saveSubsetFonts="1" autoCompressPictures="0">
324<p:sldMasterIdLst>
325<p:sldMasterId id=""#,
326 );
327 append_usize(&mut xml, SLIDE_MASTER_ID as usize);
328 xml.push_str(r#"" r:id="rId1"/>
329</p:sldMasterIdLst>"#);
330
331 if has_notes {
332 xml.push_str("\n<p:notesMasterIdLst>\n<p:notesMasterId r:id=\"rId");
333 append_usize(&mut xml, notes_master_rel_id(slides));
334 xml.push_str("\"/>\n</p:notesMasterIdLst>");
335 }
336
337 if has_handout {
338 xml.push_str("\n<p:handoutMasterIdLst>\n<p:handoutMasterId r:id=\"rId");
339 append_usize(&mut xml, handout_master_rel_id(slides, has_notes));
340 xml.push_str("\"/>\n</p:handoutMasterIdLst>");
341 }
342
343 xml.push_str("\n<p:sldIdLst>");
344
345 for i in 1..=slides {
346 let id = slide_id_value(i);
347 let rid = slide_rel_id(i, has_notes, has_handout);
348 xml.push_str("\n<p:sldId id=\"");
349 append_usize(&mut xml, id);
350 xml.push_str("\" r:id=\"rId");
351 append_usize(&mut xml, rid);
352 xml.push_str("\"/>");
353 }
354
355 xml.push_str(
356 r#"
357</p:sldIdLst>
358<p:sldSz cx="9144000" cy="6858000" type="screen4x3"/>
359<p:notesSz cx="6858000" cy="9144000"/>
360"#,
361 );
362 xml.push_str(DEFAULT_TEXT_STYLE);
363 xml.push_str(
364 r#"<p:extLst><p:ext uri="{EFAFB233-063F-42B5-8137-9DF3F51BA10A}"><p15:sldGuideLst xmlns:p15="http://schemas.microsoft.com/office/powerpoint/2012/main"/></p:ext></p:extLst></p:presentation>"#,
365 );
366 xml
367}
368
369pub fn create_presentation_xml_with_fonts(
371 title: &str,
372 slides: usize,
373 has_notes: bool,
374 has_handout: bool,
375 fonts: &EmbeddedFontList,
376) -> String {
377 let mut xml = create_presentation_xml(title, slides, has_notes, has_handout);
378 let font_xml = fonts.to_xml();
379 if !font_xml.is_empty() {
380 if let Some(pos) = xml.rfind("</p:presentation>") {
382 xml.insert_str(pos, &font_xml);
383 }
384 }
385 xml
386}
387
388pub fn create_content_types_xml_with_notes_and_charts(
390 slides: usize,
391 custom_slides: Option<&[super::slide_content::SlideContent]>,
392 chart_count: usize,
393 has_handout: bool,
394 media_exts: &[String],
395) -> String {
396 let notes_count = custom_slides
397 .map(|slides_slice| slides_slice.iter().filter(|slide| slide.notes.is_some()).count())
398 .unwrap_or(0);
399 let mut xml = String::with_capacity(2048 + slides * 140 + notes_count * 140 + chart_count * 120);
400 xml.push_str(&content_types_opening(media_exts, chart_count));
401 append_slide_overrides(&mut xml, slides);
402
403 if let Some(slides_slice) = custom_slides {
404 let mut notes_index = 0usize;
405 for slide in slides_slice {
406 if slide.notes.is_some() {
407 notes_index += 1;
408 xml.push_str("\n<Override PartName=\"/ppt/notesSlides/notesSlide");
409 append_usize(&mut xml, notes_index);
410 xml.push_str(".xml\" ContentType=\"application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml\"/>");
411 }
412 }
413 if notes_count > 0 {
414 xml.push_str("\n<Override PartName=\"/ppt/notesMasters/notesMaster1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml\"/>");
415 xml.push_str("\n<Override PartName=\"/ppt/theme/theme2.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.theme+xml\"/>");
416 }
417 }
418
419 if has_handout {
420 xml.push_str("\n<Override PartName=\"/ppt/handoutMasters/handoutMaster1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.presentationml.handoutMaster+xml\"/>");
421 xml.push_str("\n<Override PartName=\"/ppt/theme/theme3.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.theme+xml\"/>");
422 }
423
424 for i in 1..=chart_count {
425 xml.push_str("\n<Override PartName=\"/ppt/charts/chart");
426 append_usize(&mut xml, i);
427 xml.push_str(".xml\" ContentType=\"application/vnd.openxmlformats-officedocument.drawingml.chart+xml\"/>");
428 xml.push_str("\n<Override PartName=\"/ppt/embeddings/");
429 xml.push_str(&chart_embedding_filename(i));
430 xml.push_str("\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\"/>");
431 }
432
433 append_layout_content_type_overrides(&mut xml, STANDARD_LAYOUT_COUNT);
434 xml.push_str(CONTENT_TYPES_FOOTER);
435 xml
436}
437
438pub fn append_digital_signature_content_type(xml: &mut String) {
441 if let Some(pos) = xml.rfind("</Types>") {
442 xml.insert_str(
443 pos,
444 r#"<Default Extension="sigs" ContentType="application/vnd.openxmlformats-package.digital-signature-origin"/><Override PartName="/_xmlsignatures/sig1.xml" ContentType="application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"/>"#,
445 );
446 }
447}
448
449pub fn append_embedded_font_content_type(xml: &mut String) {
452 if let Some(pos) = xml.rfind("</Types>") {
453 xml.insert_str(
454 pos,
455 r#"<Default Extension="fntdata" ContentType="application/x-fontdata"/>"#,
456 );
457 }
458}
459
460pub fn append_ink_content_types(xml: &mut String, ink_count: usize) {
463 if ink_count == 0 {
464 return;
465 }
466 if let Some(pos) = xml.rfind("</Types>") {
467 let mut overrides = String::with_capacity(ink_count * 140);
468 for i in 1..=ink_count {
469 overrides.push_str(&format!(
470 "\n<Override PartName=\"/ppt/ink/ink{i}.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.presentationml.ink+xml\"/>"
471 ));
472 }
473 xml.insert_str(pos, &overrides);
474 }
475}
476
477pub fn create_handout_master_rels_xml() -> String {
479 r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
480<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
481<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="../theme/theme3.xml"/>
482</Relationships>"#.to_string()
483}
484
485pub fn create_slide_rels_xml_extended(
487 layout_number: usize,
488 has_notes: bool,
489 notes_part_num: usize,
490 chart_rels: &[(String, String)],
491) -> String {
492 let layout_target = layout_rel_target(layout_number);
493 let mut xml = String::with_capacity(512 + chart_rels.len() * 96);
494 xml.push_str(
495 r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
496<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
497<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target=""#,
498 );
499 xml.push_str(&layout_target);
500 xml.push_str("\"/>");
501
502 if has_notes {
503 xml.push_str("\n<Relationship Id=\"rId2\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide\" Target=\"../notesSlides/notesSlide");
504 append_usize(&mut xml, notes_part_num);
505 xml.push_str(".xml\"/>");
506 }
507
508 for (rid, target) in chart_rels {
509 xml.push_str("\n<Relationship Id=\"");
510 xml.push_str(rid);
511 xml.push_str("\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart\" Target=\"");
512 xml.push_str(target);
513 xml.push_str("\"/>");
514 }
515
516 xml.push_str("\n</Relationships>");
517 xml
518}
519
520pub fn create_slide_rels_xml_with_images(
522 layout_number: usize,
523 has_notes: bool,
524 notes_part_num: usize,
525 chart_rels: &[(String, String)],
526 images: &[(usize, String)],
527 hyperlink_rels: &[String],
528 ink_rel: Option<(usize, usize)>,
529) -> String {
530 let layout_target = layout_rel_target(layout_number);
531 let mut xml = String::with_capacity(
532 512 + images.len() * 96 + chart_rels.len() * 96 + hyperlink_rels.len() * 160 + 128,
533 );
534 xml.push_str(
535 r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
536<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
537<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target=""#,
538 );
539 xml.push_str(&layout_target);
540 xml.push_str("\"/>");
541
542 let mut next_rid = 2usize;
543 if has_notes {
544 xml.push_str("\n<Relationship Id=\"rId");
545 append_usize(&mut xml, next_rid);
546 xml.push_str("\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide\" Target=\"../notesSlides/notesSlide");
547 append_usize(&mut xml, notes_part_num);
548 xml.push_str(".xml\"/>");
549 next_rid += 1;
550 }
551
552 for (i, (image_num, ext)) in images.iter().enumerate() {
553 xml.push_str("\n<Relationship Id=\"rId");
554 append_usize(&mut xml, next_rid + i);
555 xml.push_str("\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image\" Target=\"../media/image");
556 append_usize(&mut xml, *image_num);
557 xml.push_str(".");
558 xml.push_str(ext);
559 xml.push_str("\"/>");
560 }
561
562 for (rid, target) in chart_rels {
563 xml.push_str("\n<Relationship Id=\"");
564 xml.push_str(rid);
565 xml.push_str("\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart\" Target=\"");
566 xml.push_str(target);
567 xml.push_str("\"/>");
568 }
569
570 if let Some((rid, ink_num)) = ink_rel {
571 xml.push_str("\n<Relationship Id=\"rId");
572 append_usize(&mut xml, rid);
573 xml.push_str("\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/ink\" Target=\"../ink/ink");
574 append_usize(&mut xml, ink_num);
575 xml.push_str(".xml\"/>");
576 }
577
578 for rel in hyperlink_rels {
579 xml.push('\n');
580 xml.push_str(rel);
581 }
582
583 xml.push_str("\n</Relationships>");
584 xml
585}