ppt_rs/parts/
slide_layout.rs1use super::base::{Part, PartType, ContentType};
6use crate::exc::PptxError;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum LayoutType {
11 Title,
12 TitleAndContent,
13 SectionHeader,
14 TwoContent,
15 Comparison,
16 TitleOnly,
17 Blank,
18 ContentWithCaption,
19 PictureWithCaption,
20 TitleAndVerticalText,
21 VerticalTitleAndText,
22 Custom,
23}
24
25impl LayoutType {
26 pub fn name(&self) -> &'static str {
28 match self {
29 LayoutType::Title => "Title Slide",
30 LayoutType::TitleAndContent => "Title and Content",
31 LayoutType::SectionHeader => "Section Header",
32 LayoutType::TwoContent => "Two Content",
33 LayoutType::Comparison => "Comparison",
34 LayoutType::TitleOnly => "Title Only",
35 LayoutType::Blank => "Blank",
36 LayoutType::ContentWithCaption => "Content with Caption",
37 LayoutType::PictureWithCaption => "Picture with Caption",
38 LayoutType::TitleAndVerticalText => "Title and Vertical Text",
39 LayoutType::VerticalTitleAndText => "Vertical Title and Text",
40 LayoutType::Custom => "Custom",
41 }
42 }
43
44 pub fn type_value(&self) -> &'static str {
46 match self {
47 LayoutType::Title => "title",
48 LayoutType::TitleAndContent => "obj",
49 LayoutType::SectionHeader => "secHead",
50 LayoutType::TwoContent => "twoObj",
51 LayoutType::Comparison => "twoTxTwoObj",
52 LayoutType::TitleOnly => "titleOnly",
53 LayoutType::Blank => "blank",
54 LayoutType::ContentWithCaption => "objTx",
55 LayoutType::PictureWithCaption => "picTx",
56 LayoutType::TitleAndVerticalText => "vertTx",
57 LayoutType::VerticalTitleAndText => "vertTitleAndTx",
58 LayoutType::Custom => "cust",
59 }
60 }
61}
62
63#[derive(Debug, Clone)]
65pub struct SlideLayoutPart {
66 path: String,
67 layout_number: usize,
68 layout_type: LayoutType,
69 name: String,
70 master_rel_id: String,
71 xml_content: Option<String>,
72}
73
74impl SlideLayoutPart {
75 pub fn new(layout_number: usize, layout_type: LayoutType) -> Self {
77 SlideLayoutPart {
78 path: format!("ppt/slideLayouts/slideLayout{}.xml", layout_number),
79 layout_number,
80 layout_type,
81 name: layout_type.name().to_string(),
82 master_rel_id: "rId1".to_string(),
83 xml_content: None,
84 }
85 }
86
87 pub fn layout_number(&self) -> usize {
89 self.layout_number
90 }
91
92 pub fn layout_type(&self) -> LayoutType {
94 self.layout_type
95 }
96
97 pub fn name(&self) -> &str {
99 &self.name
100 }
101
102 pub fn set_name(&mut self, name: impl Into<String>) {
104 self.name = name.into();
105 }
106
107 pub fn set_master_rel_id(&mut self, rel_id: impl Into<String>) {
109 self.master_rel_id = rel_id.into();
110 }
111
112 pub fn rel_target(&self) -> String {
114 format!("slideLayouts/slideLayout{}.xml", self.layout_number)
115 }
116
117 fn generate_xml(&self) -> String {
118 format!(
119 r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
120<p:sldLayout 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" type="{}" preserve="1">
121 <p:cSld name="{}">
122 <p:spTree>
123 <p:nvGrpSpPr>
124 <p:cNvPr id="1" name=""/>
125 <p:cNvGrpSpPr/>
126 <p:nvPr/>
127 </p:nvGrpSpPr>
128 <p:grpSpPr>
129 <a:xfrm>
130 <a:off x="0" y="0"/>
131 <a:ext cx="0" cy="0"/>
132 <a:chOff x="0" y="0"/>
133 <a:chExt cx="0" cy="0"/>
134 </a:xfrm>
135 </p:grpSpPr>
136 </p:spTree>
137 </p:cSld>
138 <p:clrMapOvr>
139 <a:masterClrMapping/>
140 </p:clrMapOvr>
141</p:sldLayout>"#,
142 self.layout_type.type_value(),
143 self.name
144 )
145 }
146}
147
148impl Part for SlideLayoutPart {
149 fn path(&self) -> &str {
150 &self.path
151 }
152
153 fn part_type(&self) -> PartType {
154 PartType::SlideLayout
155 }
156
157 fn content_type(&self) -> ContentType {
158 ContentType::SlideLayout
159 }
160
161 fn to_xml(&self) -> Result<String, PptxError> {
162 if let Some(ref xml) = self.xml_content {
163 return Ok(xml.clone());
164 }
165 Ok(self.generate_xml())
166 }
167
168 fn from_xml(xml: &str) -> Result<Self, PptxError> {
169 Ok(SlideLayoutPart {
170 path: "ppt/slideLayouts/slideLayout1.xml".to_string(),
171 layout_number: 1,
172 layout_type: LayoutType::TitleAndContent,
173 name: "Layout".to_string(),
174 master_rel_id: "rId1".to_string(),
175 xml_content: Some(xml.to_string()),
176 })
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn test_slide_layout_new() {
186 let layout = SlideLayoutPart::new(1, LayoutType::Title);
187 assert_eq!(layout.layout_number(), 1);
188 assert_eq!(layout.layout_type(), LayoutType::Title);
189 assert_eq!(layout.path(), "ppt/slideLayouts/slideLayout1.xml");
190 }
191
192 #[test]
193 fn test_layout_type_name() {
194 assert_eq!(LayoutType::Title.name(), "Title Slide");
195 assert_eq!(LayoutType::Blank.name(), "Blank");
196 assert_eq!(LayoutType::TwoContent.name(), "Two Content");
197 }
198
199 #[test]
200 fn test_layout_type_value() {
201 assert_eq!(LayoutType::Title.type_value(), "title");
202 assert_eq!(LayoutType::Blank.type_value(), "blank");
203 assert_eq!(LayoutType::TitleAndContent.type_value(), "obj");
204 }
205
206 #[test]
207 fn test_slide_layout_to_xml() {
208 let layout = SlideLayoutPart::new(1, LayoutType::Title);
209 let xml = layout.to_xml().unwrap();
210 assert!(xml.contains("p:sldLayout"));
211 assert!(xml.contains("type=\"title\""));
212 }
213
214 #[test]
215 fn test_slide_layout_rel_target() {
216 let layout = SlideLayoutPart::new(3, LayoutType::Blank);
217 assert_eq!(layout.rel_target(), "slideLayouts/slideLayout3.xml");
218 }
219}