ppt_rs/generator/text/
rtl.rs1#[derive(Clone, Debug, Copy, PartialEq, Eq, Default)]
7pub enum TextDirection {
8 #[default]
9 LTR,
10 RTL,
11}
12
13impl TextDirection {
14 pub fn to_xml_attr(&self) -> &'static str {
16 match self {
17 TextDirection::LTR => "0",
18 TextDirection::RTL => "1",
19 }
20 }
21
22 pub fn is_rtl(&self) -> bool {
24 matches!(self, TextDirection::RTL)
25 }
26}
27
28#[derive(Clone, Debug, Copy, PartialEq, Eq)]
30pub enum RtlLanguage {
31 Arabic,
32 Hebrew,
33 Urdu,
34 Persian,
35 Pashto,
36 Sindhi,
37 Kurdish,
38 Yiddish,
39}
40
41impl RtlLanguage {
42 pub fn lang_tag(&self) -> &'static str {
44 match self {
45 RtlLanguage::Arabic => "ar-SA",
46 RtlLanguage::Hebrew => "he-IL",
47 RtlLanguage::Urdu => "ur-PK",
48 RtlLanguage::Persian => "fa-IR",
49 RtlLanguage::Pashto => "ps-AF",
50 RtlLanguage::Sindhi => "sd-PK",
51 RtlLanguage::Kurdish => "ku-IQ",
52 RtlLanguage::Yiddish => "yi-001",
53 }
54 }
55
56 pub fn default_font(&self) -> &'static str {
58 match self {
59 RtlLanguage::Arabic | RtlLanguage::Urdu | RtlLanguage::Persian
60 | RtlLanguage::Pashto | RtlLanguage::Sindhi | RtlLanguage::Kurdish => "Arial",
61 RtlLanguage::Hebrew | RtlLanguage::Yiddish => "Arial",
62 }
63 }
64
65 pub fn direction(&self) -> TextDirection {
67 TextDirection::RTL
68 }
69}
70
71#[derive(Clone, Debug, Default)]
73pub struct RtlTextProps {
74 pub direction: TextDirection,
75 pub language: Option<String>,
76 pub font_complex_script: Option<String>,
77}
78
79impl RtlTextProps {
80 pub fn new() -> Self {
81 Self::default()
82 }
83
84 pub fn rtl(mut self) -> Self {
86 self.direction = TextDirection::RTL;
87 self
88 }
89
90 pub fn ltr(mut self) -> Self {
92 self.direction = TextDirection::LTR;
93 self
94 }
95
96 pub fn language(mut self, lang: &str) -> Self {
98 self.language = Some(lang.to_string());
99 self
100 }
101
102 pub fn from_language(mut self, lang: RtlLanguage) -> Self {
104 self.direction = lang.direction();
105 self.language = Some(lang.lang_tag().to_string());
106 self.font_complex_script = Some(lang.default_font().to_string());
107 self
108 }
109
110 pub fn complex_script_font(mut self, font: &str) -> Self {
112 self.font_complex_script = Some(font.to_string());
113 self
114 }
115
116 pub fn to_ppr_xml_attr(&self) -> String {
118 if self.direction.is_rtl() {
119 r#" rtl="1""#.to_string()
120 } else {
121 String::new()
122 }
123 }
124
125 pub fn to_rpr_xml_attrs(&self) -> String {
127 let mut attrs = String::new();
128 if let Some(ref lang) = self.language {
129 attrs.push_str(&format!(r#" lang="{lang}""#));
130 }
131 attrs
132 }
133
134 pub fn to_cs_font_xml(&self) -> String {
136 if let Some(ref font) = self.font_complex_script {
137 format!(r#"<a:cs typeface="{}"/>"#, super::escape_xml(font))
138 } else {
139 String::new()
140 }
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn test_text_direction_default() {
150 let dir = TextDirection::default();
151 assert_eq!(dir, TextDirection::LTR);
152 assert!(!dir.is_rtl());
153 }
154
155 #[test]
156 fn test_text_direction_rtl() {
157 let dir = TextDirection::RTL;
158 assert!(dir.is_rtl());
159 assert_eq!(dir.to_xml_attr(), "1");
160 }
161
162 #[test]
163 fn test_text_direction_ltr() {
164 let dir = TextDirection::LTR;
165 assert!(!dir.is_rtl());
166 assert_eq!(dir.to_xml_attr(), "0");
167 }
168
169 #[test]
170 fn test_rtl_language_arabic() {
171 let lang = RtlLanguage::Arabic;
172 assert_eq!(lang.lang_tag(), "ar-SA");
173 assert_eq!(lang.default_font(), "Arial");
174 assert_eq!(lang.direction(), TextDirection::RTL);
175 }
176
177 #[test]
178 fn test_rtl_language_hebrew() {
179 let lang = RtlLanguage::Hebrew;
180 assert_eq!(lang.lang_tag(), "he-IL");
181 assert_eq!(lang.default_font(), "Arial");
182 }
183
184 #[test]
185 fn test_rtl_language_all_variants() {
186 let languages = [
187 RtlLanguage::Arabic, RtlLanguage::Hebrew, RtlLanguage::Urdu,
188 RtlLanguage::Persian, RtlLanguage::Pashto, RtlLanguage::Sindhi,
189 RtlLanguage::Kurdish, RtlLanguage::Yiddish,
190 ];
191 for lang in &languages {
192 assert_eq!(lang.direction(), TextDirection::RTL);
193 assert!(!lang.lang_tag().is_empty());
194 assert!(!lang.default_font().is_empty());
195 }
196 }
197
198 #[test]
199 fn test_rtl_text_props_default() {
200 let props = RtlTextProps::new();
201 assert_eq!(props.direction, TextDirection::LTR);
202 assert!(props.language.is_none());
203 assert!(props.font_complex_script.is_none());
204 }
205
206 #[test]
207 fn test_rtl_text_props_builder() {
208 let props = RtlTextProps::new()
209 .rtl()
210 .language("ar-SA")
211 .complex_script_font("Arial");
212 assert!(props.direction.is_rtl());
213 assert_eq!(props.language.as_deref(), Some("ar-SA"));
214 assert_eq!(props.font_complex_script.as_deref(), Some("Arial"));
215 }
216
217 #[test]
218 fn test_rtl_text_props_from_language() {
219 let props = RtlTextProps::new().from_language(RtlLanguage::Arabic);
220 assert!(props.direction.is_rtl());
221 assert_eq!(props.language.as_deref(), Some("ar-SA"));
222 assert_eq!(props.font_complex_script.as_deref(), Some("Arial"));
223 }
224
225 #[test]
226 fn test_rtl_ppr_xml_attr() {
227 let rtl_props = RtlTextProps::new().rtl();
228 assert_eq!(rtl_props.to_ppr_xml_attr(), r#" rtl="1""#);
229
230 let ltr_props = RtlTextProps::new().ltr();
231 assert_eq!(ltr_props.to_ppr_xml_attr(), "");
232 }
233
234 #[test]
235 fn test_rtl_rpr_xml_attrs() {
236 let props = RtlTextProps::new().language("he-IL");
237 assert!(props.to_rpr_xml_attrs().contains(r#"lang="he-IL""#));
238 }
239
240 #[test]
241 fn test_rtl_cs_font_xml() {
242 let props = RtlTextProps::new().complex_script_font("Arial");
243 let xml = props.to_cs_font_xml();
244 assert!(xml.contains(r#"<a:cs typeface="Arial"/>"#));
245
246 let empty = RtlTextProps::new();
247 assert_eq!(empty.to_cs_font_xml(), "");
248 }
249
250 #[test]
251 fn test_rtl_props_ltr_override() {
252 let props = RtlTextProps::new().rtl().ltr();
253 assert!(!props.direction.is_rtl());
254 }
255}