1mod sources;
6mod vmo_stream;
7
8use anyhow::{format_err, Error};
9use byteorder::{BigEndian, ReadBytesExt};
10use char_set::CharSet;
11use freetype_ffi::{
12 FT_Add_Default_Modules, FT_Done_Face, FT_Done_Library, FT_Err_Ok, FT_Face, FT_Get_First_Char,
13 FT_Get_Next_Char, FT_Get_Postscript_Name, FT_Get_Sfnt_Name, FT_Get_Sfnt_Name_Count, FT_Library,
14 FT_New_Library, FT_Open_Face, FT_SfntName, FT_MEMORY, TT_MS_ID_SYMBOL_CS, TT_MS_ID_UNICODE_CS,
15 TT_MS_LANGID_ENGLISH_UNITED_STATES, TT_NAME_ID_FULL_NAME, TT_PLATFORM_MICROSOFT,
16};
17use std::ffi::CStr;
18use std::io::Cursor;
19use std::ops::Range;
20use std::ptr;
21
22pub use crate::sources::{FTOpenArgs, FontAssetSource};
23
24#[derive(Default, Clone, Debug, Eq, PartialEq)]
26pub struct FontInfo {
27 pub char_set: CharSet,
28 pub postscript_name: Option<String>,
30 pub full_name: Option<String>,
32}
33
34pub trait FontInfoLoader {
38 fn load_font_info<S, E>(&self, source: S, index: u32) -> Result<FontInfo, Error>
46 where
47 S: TryInto<FontAssetSource, Error = E>,
48 E: Sync + Send + Into<Error>;
49}
50
51#[derive(Debug)]
55pub struct FontInfoLoaderImpl {
56 library: FTLibrary,
57}
58
59impl FontInfoLoaderImpl {
60 pub fn new() -> Result<FontInfoLoaderImpl, Error> {
61 let library = FTLibrary::new()?;
62 Ok(Self { library })
63 }
64
65 pub fn load_font_info<S, E>(&self, source: S, index: u32) -> Result<FontInfo, Error>
66 where
67 S: TryInto<FontAssetSource, Error = E>,
68 E: Sync + Send + Into<Error>,
69 {
70 let source: FontAssetSource = source.try_into().map_err(|e| e.into())?;
71 let open_args: FTOpenArgs = source.try_into()?;
72
73 let face = self.library.open_face(open_args, index)?;
74 let code_points = face.code_points().collect();
75 let postscript_name = face.postscript_name().ok().filter(|x| !x.is_empty());
76 let full_name = face.full_name()?.filter(|x| !x.is_empty());
77
78 Ok(FontInfo { char_set: CharSet::new(code_points), postscript_name, full_name })
79 }
80}
81
82impl FontInfoLoader for FontInfoLoaderImpl {
83 fn load_font_info<S, E>(&self, source: S, index: u32) -> Result<FontInfo, Error>
84 where
85 S: TryInto<FontAssetSource, Error = E>,
86 E: Sync + Send + Into<Error>,
87 {
88 FontInfoLoaderImpl::load_font_info(&self, source, index)
89 }
90}
91
92#[derive(Debug)]
93struct FTLibrary {
94 ft_library: FT_Library,
95}
96
97impl FTLibrary {
99 pub fn new() -> Result<FTLibrary, Error> {
100 unsafe {
103 let mut ft_library = ptr::null_mut();
104 if FT_New_Library(&FT_MEMORY, &mut ft_library) != FT_Err_Ok {
105 return Err(format_err!("Failed to initialize FreeType library."));
106 }
107 FT_Add_Default_Modules(ft_library);
108 Ok(FTLibrary { ft_library })
109 }
110 }
111
112 pub fn open_face(&self, open_args: FTOpenArgs, index: u32) -> Result<FTFace, Error> {
114 unsafe {
117 let mut ft_face: FT_Face = ptr::null_mut();
118 let err = FT_Open_Face(self.ft_library, open_args.as_ref(), index as i64, &mut ft_face);
119
120 if err != FT_Err_Ok {
121 return Err(format_err!(
122 "Failed to parse font {} from {:?}: FreeType error {}",
123 index,
124 open_args.source,
125 err
126 ));
127 }
128
129 Ok(FTFace { ft_face, open_args })
130 }
131 }
132}
133
134impl std::ops::Drop for FTLibrary {
135 fn drop(&mut self) {
136 unsafe {
139 FT_Done_Library(self.ft_library);
140 }
141 }
142}
143
144struct FTFace {
147 ft_face: FT_Face,
148
149 #[allow(dead_code)]
151 open_args: FTOpenArgs,
152}
153
154impl FTFace {
155 pub fn code_points<'f>(&'f self) -> CodePointsIterator<'f> {
157 CodePointsIterator { face: self, state: CodePointsIteratorState::Uninitialized }
158 }
159
160 pub fn postscript_name(&self) -> Result<String, Error> {
162 let postscript_name = unsafe {
164 let postscript_name =
170 FT_Get_Postscript_Name(self.ft_face) as *const std::os::raw::c_char;
171 let postscript_name = CStr::from_ptr(postscript_name);
172 postscript_name
173 .to_str()
174 .map_err(|e| format_err!("Failed to decode Postscript name. Error: {:?}", e))?
175 .to_string()
176 };
177 Ok(postscript_name)
178 }
179
180 pub fn full_name(&self) -> Result<Option<String>, Error> {
182 self.get_name(TT_NAME_ID_FULL_NAME)
183 }
184
185 fn get_name(&self, name_id: u16) -> Result<Option<String>, Error> {
191 for name_entry in self.sfnt_names() {
192 let name_entry = name_entry?;
193 if let Some(name) = FTFace::decode_name_if_matches(&name_entry, name_id)? {
194 return Ok(Some(name));
195 }
196 }
197 Ok(None)
198 }
199
200 fn sfnt_name_count(&self) -> u32 {
201 unsafe { FT_Get_Sfnt_Name_Count(self.ft_face) }
202 }
203
204 fn sfnt_names<'a>(&'a self) -> SfntNamesIterator<'a> {
205 SfntNamesIterator { face: self, iter: 0..self.sfnt_name_count() }
206 }
207
208 fn decode_name_if_matches(
213 name_entry: &FT_SfntName,
214 name_id: u16,
215 ) -> Result<Option<String>, Error> {
216 if name_entry.name_id != name_id {
217 return Ok(None);
218 }
219 match (name_entry.platform_id, name_entry.encoding_id, name_entry.language_id) {
220 (TT_PLATFORM_MICROSOFT, TT_MS_ID_SYMBOL_CS, TT_MS_LANGID_ENGLISH_UNITED_STATES)
221 | (TT_PLATFORM_MICROSOFT, TT_MS_ID_UNICODE_CS, TT_MS_LANGID_ENGLISH_UNITED_STATES) => {
222 let name = FTFace::decode_name_utf16_be(name_entry)?;
223 Ok(Some(name))
224 }
225 _ => Ok(None),
226 }
227 }
228
229 fn decode_name_utf16_be(name_entry: &FT_SfntName) -> Result<String, Error> {
231 let as_u8 = unsafe {
232 std::slice::from_raw_parts(name_entry.string, name_entry.string_len as usize)
233 };
234 let num_code_units = (name_entry.string_len / 2) as usize;
236 let mut reader = Cursor::new(as_u8);
237 let mut as_u16 = Vec::with_capacity(num_code_units);
238 for _i in 0..num_code_units {
239 let ch = reader.read_u16::<BigEndian>()?;
240 as_u16.push(ch);
241 }
242 Ok(String::from_utf16(as_u16.as_slice())?)
243 }
244}
245
246impl std::ops::Drop for FTFace {
247 fn drop(&mut self) {
248 unsafe {
251 FT_Done_Face(self.ft_face);
252 }
253 }
254}
255
256#[derive(Debug, Copy, Clone)]
258enum CodePointsIteratorState {
259 Uninitialized,
260 Active(u64),
262 Fused,
264}
265
266pub struct CodePointsIterator<'f> {
268 face: &'f FTFace,
269 state: CodePointsIteratorState,
270}
271
272impl<'f> CodePointsIterator<'f> {
273 fn next_internal(&mut self, prev_code_point: Option<u64>) -> Option<u32> {
274 let mut glyph_index: u32 = 0;
275 let code_point = unsafe {
278 match prev_code_point {
279 Some(prev_code_point) => {
280 FT_Get_Next_Char(self.face.ft_face, prev_code_point, &mut glyph_index)
281 }
282 None => FT_Get_First_Char(self.face.ft_face, &mut glyph_index),
283 }
284 };
285 if glyph_index > 0 {
288 self.state = CodePointsIteratorState::Active(code_point);
289 Some(code_point as u32)
290 } else {
291 self.state = CodePointsIteratorState::Fused;
292 None
293 }
294 }
295}
296
297impl<'f> std::iter::Iterator for CodePointsIterator<'f> {
298 type Item = u32;
299
300 fn next(&mut self) -> Option<Self::Item> {
301 match self.state.clone() {
302 CodePointsIteratorState::Uninitialized => self.next_internal(None),
303 CodePointsIteratorState::Active(prev_code_point) => {
304 self.next_internal(Some(prev_code_point))
305 }
306 CodePointsIteratorState::Fused => None,
307 }
308 }
309}
310
311impl<'f> std::iter::FusedIterator for CodePointsIterator<'f> {}
312
313pub struct SfntNamesIterator<'f> {
315 face: &'f FTFace,
316 iter: Range<u32>,
317}
318
319impl<'f> SfntNamesIterator<'f> {
320 fn get(&self, idx: u32) -> Result<FT_SfntName, Error> {
321 let mut entry = FT_SfntName::default();
322 let err_code = unsafe { FT_Get_Sfnt_Name(self.face.ft_face, idx, &mut entry) };
323 if err_code == FT_Err_Ok {
324 Ok(entry)
325 } else {
326 Err(format_err!("FreeType error while getting name {}: {}", idx, err_code))
327 }
328 }
329}
330
331impl<'f> Iterator for SfntNamesIterator<'f> {
332 type Item = Result<FT_SfntName, Error>;
333
334 fn next(&mut self) -> Option<Self::Item> {
335 self.iter.next().map(|idx| self.get(idx))
336 }
337}
338
339impl<'f> ExactSizeIterator for SfntNamesIterator<'f> {
340 fn len(&self) -> usize {
341 self.iter.len()
342 }
343}