easyofd_core/text/
ct_font.rs1#[allow(non_camel_case_types, clippy::struct_excessive_bools)]
8#[derive(Debug, Clone)]
9pub struct CT_Font {
10 pub id: u32,
12 pub font_name: String,
14 pub family_name: Option<String>,
16 pub charset: Option<String>,
18 pub italic: bool,
20 pub bold: bool,
22 pub serif: bool,
24 pub fixed_width: bool,
26 pub font_file: Option<String>,
28}
29
30impl CT_Font {
31 #[must_use]
33 pub fn new(id: u32, font_name: impl Into<String>) -> Self {
34 Self {
35 id,
36 font_name: font_name.into(),
37 family_name: None,
38 charset: None,
39 italic: false,
40 bold: false,
41 serif: false,
42 fixed_width: false,
43 font_file: None,
44 }
45 }
46
47 #[must_use]
49 pub fn font_name(mut self, name: impl Into<String>) -> Self {
50 self.font_name = name.into();
51 self
52 }
53
54 #[must_use]
56 pub fn family_name(mut self, name: impl Into<String>) -> Self {
57 self.family_name = Some(name.into());
58 self
59 }
60
61 #[must_use]
63 pub fn charset(mut self, charset: impl Into<String>) -> Self {
64 self.charset = Some(charset.into());
65 self
66 }
67
68 #[must_use]
70 pub fn italic(mut self, italic: bool) -> Self {
71 self.italic = italic;
72 self
73 }
74
75 #[must_use]
77 pub fn bold(mut self, bold: bool) -> Self {
78 self.bold = bold;
79 self
80 }
81
82 #[must_use]
84 pub fn serif(mut self, serif: bool) -> Self {
85 self.serif = serif;
86 self
87 }
88
89 #[must_use]
91 pub fn fixed_width(mut self, fixed: bool) -> Self {
92 self.fixed_width = fixed;
93 self
94 }
95
96 #[must_use]
98 pub fn font_file(mut self, path: impl Into<String>) -> Self {
99 self.font_file = Some(path.into());
100 self
101 }
102
103 #[must_use]
105 pub fn get_id(&self) -> u32 {
106 self.id
107 }
108
109 #[must_use]
111 pub fn get_font_name(&self) -> &str {
112 &self.font_name
113 }
114
115 #[must_use]
117 pub fn get_family_name(&self) -> Option<&str> {
118 self.family_name.as_deref()
119 }
120
121 #[must_use]
123 pub fn get_charset(&self) -> Option<&str> {
124 self.charset.as_deref()
125 }
126
127 #[must_use]
129 pub fn is_italic(&self) -> bool {
130 self.italic
131 }
132
133 #[must_use]
135 pub fn is_bold(&self) -> bool {
136 self.bold
137 }
138
139 #[must_use]
141 pub fn is_serif(&self) -> bool {
142 self.serif
143 }
144
145 #[must_use]
147 pub fn is_fixed_width(&self) -> bool {
148 self.fixed_width
149 }
150
151 #[must_use]
153 pub fn to_xml_string(&self) -> String {
154 use std::fmt::Write;
155 let mut xml = format!(
156 "<ofd:Font ID=\"{}\" FontName=\"{}\"",
157 self.id, self.font_name
158 );
159 if let Some(ref fn_) = self.family_name {
160 write!(xml, " FamilyName=\"{fn_}\"").expect("写入内存缓冲区不会失败");
161 }
162 if let Some(ref cs) = self.charset {
163 write!(xml, " Charset=\"{cs}\"").expect("写入内存缓冲区不会失败");
164 }
165 if self.italic {
166 xml.push_str(" Italic=\"true\"");
167 }
168 if self.bold {
169 xml.push_str(" Bold=\"true\"");
170 }
171 if self.serif {
172 xml.push_str(" Serif=\"true\"");
173 }
174 if self.fixed_width {
175 xml.push_str(" FixedWidth=\"true\"");
176 }
177 if let Some(ref ff) = self.font_file {
178 write!(xml, " FontFile=\"{ff}\"").expect("写入内存缓冲区不会失败");
179 }
180 xml.push_str(" />");
181 xml
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn test_ct_font_new() {
191 let font = CT_Font::new(1, "SimSun");
192 assert_eq!(font.id, 1);
193 assert_eq!(font.font_name, "SimSun");
194 assert!(!font.italic);
195 assert!(!font.bold);
196 assert!(!font.serif);
197 assert!(!font.fixed_width);
198 assert!(font.family_name.is_none());
199 assert!(font.font_file.is_none());
200 }
201
202 #[test]
203 fn test_ct_font_builder() {
204 let font = CT_Font::new(2, "SimHei")
205 .family_name("Sans-Serif")
206 .charset("Unicode")
207 .italic(true)
208 .bold(true)
209 .serif(false)
210 .fixed_width(true)
211 .font_file("simhei.ttf");
212 assert_eq!(font.get_font_name(), "SimHei");
213 assert_eq!(font.get_family_name(), Some("Sans-Serif"));
214 assert_eq!(font.get_charset(), Some("Unicode"));
215 assert!(font.is_italic());
216 assert!(font.is_bold());
217 assert!(!font.is_serif());
218 assert!(font.is_fixed_width());
219 assert_eq!(font.font_file.as_deref(), Some("simhei.ttf"));
220 }
221
222 #[test]
223 fn test_ct_font_to_xml_minimal() {
224 let font = CT_Font::new(1, "SimSun");
225 let xml = font.to_xml_string();
226 assert!(xml.contains("ID=\"1\""));
227 assert!(xml.contains("FontName=\"SimSun\""));
228 assert!(xml.contains("<ofd:Font"));
229 assert!(xml.ends_with(" />"));
230 }
231
232 #[test]
233 fn test_ct_font_to_xml_full() {
234 let font = CT_Font::new(5, "Arial")
235 .family_name("Sans-Serif")
236 .charset("Unicode")
237 .bold(true)
238 .italic(true)
239 .serif(true)
240 .fixed_width(true)
241 .font_file("arial.ttf");
242 let xml = font.to_xml_string();
243 assert!(xml.contains("ID=\"5\""));
244 assert!(xml.contains("FontName=\"Arial\""));
245 assert!(xml.contains("FamilyName=\"Sans-Serif\""));
246 assert!(xml.contains("Charset=\"Unicode\""));
247 assert!(xml.contains("Bold=\"true\""));
248 assert!(xml.contains("Italic=\"true\""));
249 assert!(xml.contains("Serif=\"true\""));
250 assert!(xml.contains("FixedWidth=\"true\""));
251 assert!(xml.contains("FontFile=\"arial.ttf\""));
252 }
253
254 #[test]
255 fn test_ct_font_clone_debug() {
256 let font = CT_Font::new(1, "x");
257 let font2 = font.clone();
258 assert_eq!(font2.get_font_name(), "x");
259 assert!(format!("{font:?}").contains("CT_Font"));
260 }
261}