fonts_traits/
font_identifier.rs1use malloc_size_of_derive::MallocSizeOf;
6pub use platform::LocalFontIdentifier;
7use serde::{Deserialize, Serialize};
8use servo_url::ServoUrl;
9use uuid::Uuid;
10
11#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
12pub enum FontIdentifier {
13 Local(LocalFontIdentifier),
14 Web(ServoUrl),
15 ArrayBuffer(Uuid),
16}
17
18impl FontIdentifier {
19 pub fn index(&self) -> u32 {
20 match *self {
21 Self::Local(ref local_font_identifier) => local_font_identifier.index(),
22 Self::Web(_) | Self::ArrayBuffer(_) => 0,
23 }
24 }
25}
26
27#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
28mod platform {
29 use std::fs::File;
30 use std::path::{Path, PathBuf};
31
32 use malloc_size_of_derive::MallocSizeOf;
33 use memmap2::Mmap;
34 use serde::{Deserialize, Serialize};
35 use style::Atom;
36 use webrender_api::NativeFontHandle;
37
38 use crate::{FontData, FontDataAndIndex};
39
40 #[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
42 pub struct LocalFontIdentifier {
43 pub path: Atom,
45 pub face_index: u16,
47 pub named_instance_index: u16,
51 }
52
53 impl LocalFontIdentifier {
54 pub fn index(&self) -> u32 {
55 self.face_index as u32
56 }
57
58 pub fn named_instance_index(&self) -> u32 {
59 self.named_instance_index as u32
60 }
61
62 pub fn native_font_handle(&self) -> NativeFontHandle {
63 NativeFontHandle {
64 path: PathBuf::from(&*self.path),
65 index: self.face_index as u32,
66 }
67 }
68
69 #[expect(unsafe_code)]
70 pub fn font_data_and_index(&self) -> Option<FontDataAndIndex> {
71 let file = File::open(Path::new(&*self.path)).ok()?;
72 let mmap = unsafe { Mmap::map(&file).ok()? };
73
74 Some(FontDataAndIndex {
75 data: FontData::from_mmap(mmap),
76 index: self.face_index as u32,
77 })
78 }
79
80 pub fn face_index_for_freetype(&self) -> u32 {
88 ((self.named_instance_index()) << 16) | self.index()
89 }
90 }
91}
92
93#[cfg(target_os = "macos")]
94mod platform {
95 use std::fs::File;
96 use std::path::Path;
97
98 use log::warn;
99 use malloc_size_of_derive::MallocSizeOf;
100 use memmap2::Mmap;
101 use read_fonts::types::NameId;
102 use read_fonts::{FileRef, TableProvider};
103 use serde::{Deserialize, Serialize};
104 use style::Atom;
105 use webrender_api::NativeFontHandle;
106
107 use crate::{FontData, FontDataAndIndex};
108
109 #[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
113 pub struct LocalFontIdentifier {
114 pub postscript_name: Atom,
115 pub path: Atom,
116 }
117
118 impl LocalFontIdentifier {
119 pub fn native_font_handle(&self) -> NativeFontHandle {
120 NativeFontHandle {
121 name: self.postscript_name.to_string(),
122 path: self.path.to_string(),
123 }
124 }
125
126 pub(crate) fn index(&self) -> u32 {
127 0
128 }
129
130 #[expect(unsafe_code)]
131 pub fn font_data_and_index(&self) -> Option<FontDataAndIndex> {
132 let file = File::open(Path::new(&*self.path)).ok()?;
133 let mmap = unsafe { Mmap::map(&file).ok()? };
134
135 let file_ref = FileRef::new(mmap.as_ref()).ok()?;
137 let index = ttc_index_from_postscript_name(file_ref, &self.postscript_name);
138
139 Some(FontDataAndIndex {
140 data: FontData::from_mmap(mmap),
141 index,
142 })
143 }
144 }
145
146 fn ttc_index_from_postscript_name(font_file: FileRef<'_>, postscript_name: &str) -> u32 {
154 match font_file {
155 FileRef::Font(_) => 0,
157 FileRef::Collection(collection) => {
160 for i in 0..collection.len() {
161 let font = collection.get(i).unwrap();
162 let name_table = font.name().unwrap();
163 if name_table
164 .name_record()
165 .iter()
166 .filter(|record| record.name_id() == NameId::POSTSCRIPT_NAME)
167 .any(|record| {
168 record
169 .string(name_table.string_data())
170 .unwrap()
171 .chars()
172 .eq(postscript_name.chars())
173 })
174 {
175 return i;
176 }
177 }
178
179 warn!(
181 "Font with postscript_name {} not found in collection",
182 postscript_name
183 );
184 0
185 },
186 }
187 }
188}
189
190#[cfg(target_os = "windows")]
191mod platform {
192 use std::hash::Hash;
193 use std::sync::Arc;
194
195 use dwrote::{FontCollection, FontDescriptor};
196 use malloc_size_of_derive::MallocSizeOf;
197 use serde::{Deserialize, Serialize};
198 use webrender_api::NativeFontHandle;
199
200 use crate::{FontData, FontDataAndIndex};
201
202 #[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
204 pub struct LocalFontIdentifier {
205 #[ignore_malloc_size_of = "dwrote does not support MallocSizeOf"]
207 pub font_descriptor: Arc<FontDescriptor>,
208 }
209
210 impl LocalFontIdentifier {
211 pub fn index(&self) -> u32 {
212 FontCollection::system()
213 .font_from_descriptor(&self.font_descriptor)
214 .ok()
215 .flatten()
216 .map_or(0, |font| font.create_font_face().get_index())
217 }
218
219 pub fn native_font_handle(&self) -> NativeFontHandle {
220 let face = FontCollection::system()
221 .font_from_descriptor(&self.font_descriptor)
222 .ok()
223 .flatten()
224 .expect("Could not create Font from FontDescriptor")
225 .create_font_face();
226 let path = face
227 .files()
228 .ok()
229 .and_then(|files| files.first().cloned())
230 .expect("Could not get FontFace files")
231 .font_file_path()
232 .ok()
233 .expect("Could not get FontFace files path");
234 NativeFontHandle {
235 path,
236 index: face.get_index(),
237 }
238 }
239
240 pub fn font_data_and_index(&self) -> Option<FontDataAndIndex> {
241 let font = FontCollection::system()
242 .font_from_descriptor(&self.font_descriptor)
243 .ok()??;
244 let face = font.create_font_face();
245 let index = face.get_index();
246 let files = face.files().ok()?;
247 assert!(!files.is_empty());
248
249 let data = files[0].font_file_bytes().ok()?;
250 let data = FontData::from_bytes(&data);
251
252 Some(FontDataAndIndex { data, index })
253 }
254 }
255
256 impl Eq for LocalFontIdentifier {}
257
258 impl Hash for LocalFontIdentifier {
259 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
260 self.font_descriptor.family_name.hash(state);
261 self.font_descriptor.weight.to_u32().hash(state);
262 self.font_descriptor.stretch.to_u32().hash(state);
263 self.font_descriptor.style.to_u32().hash(state);
264 }
265 }
266}