1use std::sync::{Arc, Mutex};
6
7use app_units::Au;
8use fonts::{
9 FallbackFontSelectionOptions, FontContext, FontDescriptor, FontFamilyDescriptor,
10 FontSearchScope, fallback_font_families,
11};
12use net_traits::image_cache::FontResolver;
13use resvg::usvg::{Font, FontFamily, FontStretch, FontStyle, fontdb};
14use rustc_hash::FxHashMap;
15use style::computed_values::font_optical_sizing::T as FontOpticalSizing;
16use style::properties::longhands::font_variant_caps::computed_value::T as FontVariantCaps;
17use style::values::computed::font::{
18 FamilyName, FontFamilyNameSyntax, GenericFontFamily, SingleFontFamily,
19};
20use style::values::computed::{
21 FontStretch as ServoFontStretch, FontStyle as ServoFontStyle, FontSynthesis, FontWeight,
22};
23use webrender_api::FontVariation;
24
25pub struct SvgFontResolver {
27 font_id_cache: Mutex<FxHashMap<Font, fontdb::ID>>,
29 fallback_id_cache: Mutex<FxHashMap<char, Vec<fontdb::ID>>>,
30 context: Arc<FontContext>,
31}
32
33impl SvgFontResolver {
34 pub(crate) fn new(context: Arc<FontContext>) -> Self {
35 Self {
36 font_id_cache: Mutex::new(FxHashMap::default()),
37 fallback_id_cache: Mutex::new(FxHashMap::default()),
38 context,
39 }
40 }
41
42 fn insert_into_database(
44 &self,
45 font: &Font,
46 database: &mut Arc<fontdb::Database>,
47 ) -> Option<fontdb::ID> {
48 let font_descriptor = font_to_fontdescriptor(font);
49
50 for family in font.families() {
51 let family_descriptor = FontFamilyDescriptor::new(
52 fontfamily_to_singlefontfamily(family),
53 FontSearchScope::Any,
54 );
55
56 let Some(font_template) = self
57 .context
58 .matching_templates(&font_descriptor, &family_descriptor)
59 .into_iter()
60 .next()
61 else {
62 log::debug!(
63 "Cannot find matching font_template from font {font:?}, font-descriptor {font_descriptor:?}, family_descriptor: {family_descriptor:?} for this family {family:?}"
64 );
65 continue;
66 };
67
68 let Some(font_ref) = self.context.font(font_template, &font_descriptor) else {
69 continue;
70 };
71
72 let Ok(data_and_index) = font_ref.font_data_and_index() else {
73 continue;
74 };
75 let ids = Arc::make_mut(database).load_font_source(fontdb::Source::Binary(
76 data_and_index.data.as_ipc_shared_memory(),
77 ));
78
79 if let Some(id) = ids.get(data_and_index.index as usize).copied() {
80 self.font_id_cache.lock().unwrap().insert(font.clone(), id);
81 return Some(id);
82 }
83 }
84
85 None
86 }
87}
88
89fn font_to_fontdescriptor(font: &Font) -> FontDescriptor {
90 let style = match font.style() {
91 FontStyle::Normal => ServoFontStyle::normal(),
92 FontStyle::Italic => ServoFontStyle::ITALIC,
93 FontStyle::Oblique => ServoFontStyle::OBLIQUE,
94 };
95
96 let stretch = match font.stretch() {
97 FontStretch::UltraCondensed => ServoFontStretch::ULTRA_CONDENSED,
98 FontStretch::ExtraCondensed => ServoFontStretch::EXTRA_CONDENSED,
99 FontStretch::Condensed => ServoFontStretch::CONDENSED,
100 FontStretch::SemiCondensed => ServoFontStretch::SEMI_CONDENSED,
101 FontStretch::Normal => ServoFontStretch::NORMAL,
102 FontStretch::SemiExpanded => ServoFontStretch::SEMI_EXPANDED,
103 FontStretch::Expanded => ServoFontStretch::EXPANDED,
104 FontStretch::ExtraExpanded => ServoFontStretch::EXTRA_EXPANDED,
105 FontStretch::UltraExpanded => ServoFontStretch::ULTRA_EXPANDED,
106 };
107
108 let variation_settings = font
109 .variations()
110 .iter()
111 .map(|variation| FontVariation {
112 tag: u32::from_be_bytes(variation.tag),
113 value: variation.value,
114 })
115 .collect();
116
117 FontDescriptor {
118 weight: FontWeight::from_float(font.weight() as f32),
119 stretch,
120 style,
121 variant: FontVariantCaps::Normal,
122 pt_size: Au::from_px(16),
123 variation_settings,
124 synthesis_weight: FontSynthesis::Auto,
125 optical_sizing: FontOpticalSizing::Auto,
126 }
127}
128
129fn fallback_descriptor() -> FontDescriptor {
130 FontDescriptor {
131 weight: FontWeight::normal(),
132 stretch: ServoFontStretch::hundred(),
133 style: ServoFontStyle::normal(),
134 variant: FontVariantCaps::Normal,
135 pt_size: Au::from_px(16),
136 variation_settings: vec![],
137 synthesis_weight: FontSynthesis::Auto,
138 optical_sizing: FontOpticalSizing::Auto,
139 }
140}
141
142fn fontfamily_to_singlefontfamily(family: &FontFamily) -> SingleFontFamily {
143 match family {
144 FontFamily::Serif => SingleFontFamily::Generic(GenericFontFamily::Serif),
145 FontFamily::SansSerif => SingleFontFamily::Generic(GenericFontFamily::SansSerif),
146 FontFamily::Cursive => SingleFontFamily::Generic(GenericFontFamily::Cursive),
147 FontFamily::Fantasy => SingleFontFamily::Generic(GenericFontFamily::Fantasy),
148 FontFamily::Monospace => SingleFontFamily::Generic(GenericFontFamily::Monospace),
149 FontFamily::Named(name) => SingleFontFamily::FamilyName(FamilyName {
150 name: name.as_str().into(),
151 syntax: FontFamilyNameSyntax::Quoted,
152 }),
153 }
154}
155
156impl FontResolver for SvgFontResolver {
157 fn resolve(&self, font: &Font, database: &mut Arc<fontdb::Database>) -> Option<fontdb::ID> {
158 {
159 let id_cache = self.font_id_cache.lock().unwrap();
160 if let Some(font_id) = id_cache.get(font) {
161 return Some(*font_id);
162 }
163 }
164 self.insert_into_database(font, database)
165 }
166
167 fn resolve_fallback(
168 &self,
169 character: char,
170 excluded: &[fontdb::ID],
171 database: &mut Arc<fontdb::Database>,
172 ) -> Option<fontdb::ID> {
173 {
174 let id_cache = self.fallback_id_cache.lock().unwrap();
175 if let Some(font_id) = id_cache
176 .get(&character)
177 .and_then(|font_ids| font_ids.iter().find(|font_id| !excluded.contains(font_id)))
178 {
179 return Some(*font_id);
180 }
181 }
182 let fallback_options =
183 FallbackFontSelectionOptions::new(character, None, icu_locid::subtags::Language::UND);
184 for family in fallback_font_families(fallback_options) {
185 let family = FontFamilyDescriptor::new(
186 SingleFontFamily::FamilyName(FamilyName {
187 name: family.into(),
188 syntax: FontFamilyNameSyntax::Quoted,
189 }),
190 FontSearchScope::Any,
191 );
192 let fallback_descriptor = fallback_descriptor();
193
194 let font_templates = self
195 .context
196 .matching_templates(&fallback_descriptor, &family);
197
198 for font_template in font_templates {
199 let Some(font_ref) = self.context.font(font_template, &fallback_descriptor) else {
200 continue;
201 };
202 if !font_ref.has_glyph_for(character) {
203 continue;
204 }
205
206 let Ok(data_and_index) = font_ref.font_data_and_index() else {
207 continue;
208 };
209
210 let ids = Arc::make_mut(database).load_font_source(fontdb::Source::Binary(
211 data_and_index.data.as_ipc_shared_memory(),
212 ));
213
214 let Some(id) = ids.get(data_and_index.index as usize) else {
215 continue;
216 };
217
218 if excluded.contains(id) {
219 continue;
220 }
221
222 self.fallback_id_cache
223 .lock()
224 .unwrap()
225 .entry(character)
226 .or_default()
227 .push(*id);
228 return Some(*id);
229 }
230 }
231 None
232 }
233}