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