1use std::any::Any;
18use std::fs::File;
19use std::path::{Path, PathBuf};
20use walkdir::WalkDir;
21
22#[cfg(not(any(target_os = "android", target_family = "windows", target_env = "ohos")))]
23use dirs;
24#[cfg(target_family = "windows")]
25use std::ffi::OsString;
26#[cfg(target_family = "windows")]
27use std::os::windows::ffi::OsStringExt;
28#[cfg(target_family = "windows")]
29use winapi::shared::minwindef::{MAX_PATH, UINT};
30#[cfg(target_family = "windows")]
31use winapi::um::sysinfoapi;
32
33use crate::error::SelectionError;
34use crate::family_handle::FamilyHandle;
35use crate::family_name::FamilyName;
36use crate::file_type::FileType;
37use crate::font::Font;
38use crate::handle::Handle;
39use crate::properties::Properties;
40use crate::source::Source;
41use crate::sources::mem::MemSource;
42
43#[allow(missing_debug_implementations)]
49pub struct FsSource {
50 mem_source: MemSource,
51}
52
53impl Default for FsSource {
54 fn default() -> Self {
55 Self::new()
56 }
57}
58
59impl FsSource {
60 pub fn new() -> FsSource {
66 let mut fonts = vec![];
67 for font_directory in default_font_directories() {
68 fonts.extend(Self::discover_fonts(&font_directory));
69 }
70
71 FsSource {
72 mem_source: MemSource::from_fonts(fonts.into_iter()).unwrap(),
73 }
74 }
75
76 fn discover_fonts(path: &Path) -> Vec<Handle> {
77 let mut fonts = vec![];
78 for directory_entry in WalkDir::new(path).into_iter() {
79 let directory_entry = match directory_entry {
80 Ok(directory_entry) => directory_entry,
81 Err(_) => continue,
82 };
83 let path = directory_entry.path();
84 let mut file = match File::open(path) {
85 Err(_) => continue,
86 Ok(file) => file,
87 };
88 match Font::analyze_file(&mut file) {
89 Err(_) => continue,
90 Ok(FileType::Single) => fonts.push(Handle::from_path(path.to_owned(), 0)),
91 Ok(FileType::Collection(font_count)) => {
92 for font_index in 0..font_count {
93 fonts.push(Handle::from_path(path.to_owned(), font_index))
94 }
95 }
96 }
97 }
98 fonts
99 }
100
101 pub fn in_path<P>(path: P) -> FsSource
103 where
104 P: AsRef<Path>,
105 {
106 let fonts = Self::discover_fonts(path.as_ref());
107 FsSource {
108 mem_source: MemSource::from_fonts(fonts.into_iter()).unwrap(),
109 }
110 }
111
112 pub fn all_fonts(&self) -> Result<Vec<Handle>, SelectionError> {
114 self.mem_source.all_fonts()
115 }
116
117 pub fn all_families(&self) -> Result<Vec<String>, SelectionError> {
119 self.mem_source.all_families()
120 }
121
122 pub fn select_family_by_name(&self, family_name: &str) -> Result<FamilyHandle, SelectionError> {
124 self.mem_source.select_family_by_name(family_name)
125 }
126
127 pub fn select_by_postscript_name(
132 &self,
133 postscript_name: &str,
134 ) -> Result<Handle, SelectionError> {
135 self.mem_source.select_by_postscript_name(postscript_name)
136 }
137
138 #[inline]
141 pub fn select_best_match(
142 &self,
143 family_names: &[FamilyName],
144 properties: &Properties,
145 ) -> Result<Handle, SelectionError> {
146 <Self as Source>::select_best_match(self, family_names, properties)
147 }
148}
149
150impl Source for FsSource {
151 #[inline]
152 fn all_fonts(&self) -> Result<Vec<Handle>, SelectionError> {
153 self.all_fonts()
154 }
155
156 #[inline]
157 fn all_families(&self) -> Result<Vec<String>, SelectionError> {
158 self.all_families()
159 }
160
161 fn select_family_by_name(&self, family_name: &str) -> Result<FamilyHandle, SelectionError> {
162 self.select_family_by_name(family_name)
163 }
164
165 fn select_by_postscript_name(&self, postscript_name: &str) -> Result<Handle, SelectionError> {
166 self.select_by_postscript_name(postscript_name)
167 }
168
169 #[inline]
170 fn as_any(&self) -> &dyn Any {
171 self
172 }
173
174 #[inline]
175 fn as_mut_any(&mut self) -> &mut dyn Any {
176 self
177 }
178}
179
180#[cfg(any(target_os = "android", target_env = "ohos"))]
181fn default_font_directories() -> Vec<PathBuf> {
182 vec![PathBuf::from("/system/fonts")]
183}
184
185#[cfg(target_family = "windows")]
186fn default_font_directories() -> Vec<PathBuf> {
187 unsafe {
188 let mut buffer = vec![0; MAX_PATH];
189 let len = sysinfoapi::GetWindowsDirectoryW(buffer.as_mut_ptr(), buffer.len() as UINT);
190 assert!(len != 0);
191 buffer.truncate(len as usize);
192
193 let mut path = PathBuf::from(OsString::from_wide(&buffer));
194 path.push("Fonts");
195 vec![path]
196 }
197}
198
199#[cfg(target_os = "macos")]
200fn default_font_directories() -> Vec<PathBuf> {
201 let mut directories = vec![
202 PathBuf::from("/System/Library/Fonts"),
203 PathBuf::from("/Library/Fonts"),
204 PathBuf::from("/Network/Library/Fonts"),
205 ];
206 if let Some(mut path) = dirs::home_dir() {
207 path.push("Library");
208 path.push("Fonts");
209 directories.push(path);
210 }
211 directories
212}
213
214#[cfg(not(any(
215 target_os = "android",
216 target_family = "windows",
217 target_os = "macos",
218 target_env = "ohos"
219)))]
220fn default_font_directories() -> Vec<PathBuf> {
221 let mut directories = vec![
222 PathBuf::from("/usr/share/fonts"),
223 PathBuf::from("/usr/local/share/fonts"),
224 PathBuf::from("/var/run/host/usr/share/fonts"), PathBuf::from("/var/run/host/usr/local/share/fonts"),
226 ];
227 if let Some(path) = dirs::home_dir() {
228 directories.push(path.join(".fonts")); directories.push(path.join("local").join("share").join("fonts")); }
231 if let Some(mut path) = dirs::data_dir() {
232 path.push("fonts");
233 directories.push(path);
234 }
235 directories
236}