fonts_traits/
font_template.rs1use std::fmt::{Debug, Error, Formatter};
6use std::ops::{Deref, RangeInclusive};
7use std::sync::Arc;
8
9use atomic_refcell::AtomicRefCell;
10use malloc_size_of_derive::MallocSizeOf;
11use serde::{Deserialize, Serialize};
12use style::computed_values::font_stretch::T as FontStretch;
13use style::computed_values::font_style::T as FontStyle;
14use style::stylesheets::DocumentStyleSheet;
15use style::values::computed::font::FontWeight;
16
17use crate::{CSSFontFaceDescriptors, ComputedFontStyleDescriptor, FontDescriptor, FontIdentifier};
18
19#[derive(Clone, Debug, MallocSizeOf)]
21pub struct FontTemplateRef(#[conditional_malloc_size_of] Arc<AtomicRefCell<FontTemplate>>);
22
23impl FontTemplateRef {
24 pub fn new(template: FontTemplate) -> Self {
25 Self(Arc::new(AtomicRefCell::new(template)))
26 }
27}
28
29impl Deref for FontTemplateRef {
30 type Target = Arc<AtomicRefCell<FontTemplate>>;
31 fn deref(&self) -> &Self::Target {
32 &self.0
33 }
34}
35
36#[derive(Clone, Debug, Deserialize, Hash, MallocSizeOf, PartialEq, Serialize)]
41pub struct FontTemplateDescriptor {
42 pub weight: (FontWeight, FontWeight),
43 pub stretch: (FontStretch, FontStretch),
44 pub style: (FontStyle, FontStyle),
45 #[ignore_malloc_size_of = "MallocSizeOf does not yet support RangeInclusive"]
46 pub unicode_range: Option<Vec<RangeInclusive<u32>>>,
47}
48
49impl Default for FontTemplateDescriptor {
50 fn default() -> Self {
51 Self::new(FontWeight::normal(), FontStretch::NORMAL, FontStyle::NORMAL)
52 }
53}
54
55impl Eq for FontTemplateDescriptor {}
58
59impl FontTemplateDescriptor {
60 #[inline]
61 pub fn new(weight: FontWeight, stretch: FontStretch, style: FontStyle) -> Self {
62 Self {
63 weight: (weight, weight),
64 stretch: (stretch, stretch),
65 style: (style, style),
66 unicode_range: None,
67 }
68 }
69
70 pub fn is_variation_font(&self) -> bool {
71 self.weight.0 != self.weight.1 ||
72 self.stretch.0 != self.stretch.1 ||
73 self.style.0 != self.style.1
74 }
75
76 #[inline]
83 fn distance_from(&self, target: &FontDescriptor) -> f32 {
84 let stretch_distance = target.stretch.match_distance(&self.stretch);
85 let style_distance = target.style.match_distance(&self.style);
86 let weight_distance = target.weight.match_distance(&self.weight);
87
88 assert!((0.0..=2000.0).contains(&stretch_distance));
91 assert!((0.0..=500.0).contains(&style_distance));
92 assert!((0.0..=1600.0).contains(&weight_distance));
93
94 const STRETCH_FACTOR: f32 = 1.0e8;
103 const STYLE_FACTOR: f32 = 1.0e4;
104 const WEIGHT_FACTOR: f32 = 1.0e0;
105
106 stretch_distance * STRETCH_FACTOR +
107 style_distance * STYLE_FACTOR +
108 weight_distance * WEIGHT_FACTOR
109 }
110
111 fn matches(&self, descriptor_to_match: &FontDescriptor) -> bool {
112 self.weight.0 <= descriptor_to_match.weight &&
113 self.weight.1 >= descriptor_to_match.weight &&
114 self.style.0 <= descriptor_to_match.style &&
115 self.style.1 >= descriptor_to_match.style &&
116 self.stretch.0 <= descriptor_to_match.stretch &&
117 self.stretch.1 >= descriptor_to_match.stretch
118 }
119
120 pub fn override_values_with_css_font_template_descriptors(
121 &mut self,
122 css_font_template_descriptors: &CSSFontFaceDescriptors,
123 ) {
124 if let Some(weight) = css_font_template_descriptors.weight {
125 self.weight = weight;
126 }
127 self.style = match css_font_template_descriptors.style {
128 Some(ComputedFontStyleDescriptor::Italic) => (FontStyle::ITALIC, FontStyle::ITALIC),
129 Some(ComputedFontStyleDescriptor::Oblique(angle_1, angle_2)) => (
130 FontStyle::oblique(angle_1.to_float()),
131 FontStyle::oblique(angle_2.to_float()),
132 ),
133 None => self.style,
134 };
135 if let Some(stretch) = css_font_template_descriptors.stretch {
136 self.stretch = stretch;
137 }
138 if let Some(ref unicode_range) = css_font_template_descriptors.unicode_range {
139 self.unicode_range = Some(unicode_range.clone());
140 }
141 }
142}
143
144#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
148pub struct FontTemplate {
149 pub identifier: FontIdentifier,
150 pub descriptor: FontTemplateDescriptor,
151 #[serde(skip)]
158 pub stylesheet: Option<DocumentStyleSheet>,
159}
160
161impl Debug for FontTemplate {
162 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
163 self.identifier.fmt(f)
164 }
165}
166
167impl FontTemplate {
171 pub fn new(
173 identifier: FontIdentifier,
174 descriptor: FontTemplateDescriptor,
175 stylesheet: Option<DocumentStyleSheet>,
176 ) -> FontTemplate {
177 FontTemplate {
178 identifier,
179 descriptor,
180 stylesheet,
181 }
182 }
183
184 pub fn new_for_local_web_font(
188 local_template: FontTemplateRef,
189 css_font_template_descriptors: &CSSFontFaceDescriptors,
190 stylesheet: Option<DocumentStyleSheet>,
191 ) -> Result<FontTemplate, &'static str> {
192 let mut alias_template = local_template.borrow().clone();
193 alias_template
194 .descriptor
195 .override_values_with_css_font_template_descriptors(css_font_template_descriptors);
196 alias_template.stylesheet = stylesheet;
197 Ok(alias_template)
198 }
199
200 pub fn identifier(&self) -> &FontIdentifier {
201 &self.identifier
202 }
203}
204
205pub trait FontTemplateRefMethods {
206 fn descriptor(&self) -> FontTemplateDescriptor;
208 fn identifier(&self) -> FontIdentifier;
210 fn matches_font_descriptor(&self, descriptor_to_match: &FontDescriptor) -> bool;
212 fn descriptor_distance(&self, descriptor_to_match: &FontDescriptor) -> f32;
215 fn char_in_unicode_range(&self, character: char) -> bool;
218}
219
220impl FontTemplateRefMethods for FontTemplateRef {
221 fn descriptor(&self) -> FontTemplateDescriptor {
222 self.borrow().descriptor.clone()
223 }
224
225 fn identifier(&self) -> FontIdentifier {
226 self.borrow().identifier.clone()
227 }
228
229 fn matches_font_descriptor(&self, descriptor_to_match: &FontDescriptor) -> bool {
230 self.descriptor().matches(descriptor_to_match)
231 }
232
233 fn descriptor_distance(&self, descriptor_to_match: &FontDescriptor) -> f32 {
234 self.descriptor().distance_from(descriptor_to_match)
235 }
236
237 fn char_in_unicode_range(&self, character: char) -> bool {
238 let character = character as u32;
239 self.borrow()
240 .descriptor
241 .unicode_range
242 .as_ref()
243 .is_none_or(|ranges| ranges.iter().any(|range| range.contains(&character)))
244 }
245}
246
247trait FontMatchDistanceMethod: Sized {
253 fn match_distance(&self, range: &(Self, Self)) -> f32;
254 fn to_float(&self) -> f32;
255}
256
257impl FontMatchDistanceMethod for FontStretch {
258 fn match_distance(&self, range: &(Self, Self)) -> f32 {
259 const REVERSE_DISTANCE: f32 = 1000.0;
261
262 let min_stretch = range.0;
263 let max_stretch = range.1;
264
265 if *self < min_stretch {
271 if *self > FontStretch::NORMAL {
272 return min_stretch.to_float() - self.to_float();
273 }
274 return (min_stretch.to_float() - self.to_float()) + REVERSE_DISTANCE;
275 }
276
277 if *self > max_stretch {
278 if *self <= FontStretch::NORMAL {
279 return self.to_float() - max_stretch.to_float();
280 }
281 return (self.to_float() - max_stretch.to_float()) + REVERSE_DISTANCE;
282 }
283 0.0
284 }
285
286 fn to_float(&self) -> f32 {
287 self.0.to_float()
288 }
289}
290
291impl FontMatchDistanceMethod for FontWeight {
292 fn match_distance(&self, range: &(Self, Self)) -> f32 {
304 const NOT_WITHIN_CENTRAL_RANGE: f32 = 100.0;
306 const REVERSE_DISTANCE: f32 = 600.0;
307
308 let min_weight = range.0;
309 let max_weight = range.1;
310
311 if *self >= min_weight && *self <= max_weight {
312 return 0.0;
314 }
315
316 if *self < FontWeight::NORMAL {
317 if max_weight < *self {
319 return self.to_float() - max_weight.to_float();
320 }
321
322 return (min_weight.to_float() - self.to_float()) + REVERSE_DISTANCE;
324 }
325
326 if *self > FontWeight::from_float(500.) {
327 if min_weight > *self {
329 return min_weight.to_float() - self.to_float();
330 }
331 return (self.to_float() - max_weight.to_float()) + REVERSE_DISTANCE;
333 }
334
335 if min_weight > *self {
337 if min_weight <= FontWeight::from_float(500.) {
338 return min_weight.to_float() - self.to_float();
340 }
341 return (min_weight.to_float() - self.to_float()) + REVERSE_DISTANCE;
343 }
344 (self.to_float() - max_weight.to_float()) + NOT_WITHIN_CENTRAL_RANGE
346 }
347
348 fn to_float(&self) -> f32 {
349 self.value()
350 }
351}
352
353impl FontMatchDistanceMethod for FontStyle {
354 fn match_distance(&self, range: &(Self, Self)) -> f32 {
355 let min_style = range.0;
357 if *self == min_style {
358 return 0.0; }
360
361 const REVERSE: f32 = 100.0;
364
365 const NEGATE: f32 = 200.0;
368
369 if *self == FontStyle::NORMAL {
370 if min_style.is_oblique() {
371 let min_angle = min_style.oblique_degrees();
373 if min_angle >= 0.0 {
374 return 1.0 + min_angle;
375 }
376 let max_style = range.1;
377 let max_angle = max_style.oblique_degrees();
378 if max_angle >= 0.0 {
379 return 1.0;
381 }
382 return NEGATE - max_angle;
384 }
385 assert!(min_style == FontStyle::ITALIC);
388 return REVERSE;
389 }
390
391 let default_oblique_angle = FontStyle::OBLIQUE.oblique_degrees();
392 if *self == FontStyle::ITALIC {
393 if min_style.is_oblique() {
394 let min_angle = min_style.oblique_degrees();
395 if min_angle >= default_oblique_angle {
396 return 1.0 + (min_angle - default_oblique_angle);
397 }
398 let max_style = range.1;
399 let max_angle = max_style.oblique_degrees();
400 if max_angle >= default_oblique_angle {
401 return 1.0;
402 }
403 if max_angle > 0.0 {
404 return REVERSE + (default_oblique_angle - max_angle);
406 }
407 return REVERSE + NEGATE + (default_oblique_angle - max_angle);
409 }
410 assert!(min_style == FontStyle::NORMAL);
412 return NEGATE;
413 }
414
415 let target_angle = self.oblique_degrees();
419 if target_angle >= default_oblique_angle {
420 if min_style.is_oblique() {
421 let min_angle = min_style.oblique_degrees();
422 if min_angle >= target_angle {
423 return min_angle - target_angle;
424 }
425 let max_style = range.1;
426 let max_angle = max_style.oblique_degrees();
427 if max_angle >= target_angle {
428 return 0.0;
429 }
430 if max_angle > 0.0 {
431 return REVERSE + (target_angle - max_angle);
432 }
433 return REVERSE + NEGATE + (target_angle - max_angle);
434 }
435 if min_style == FontStyle::ITALIC {
436 return REVERSE + NEGATE;
437 }
438 return REVERSE + NEGATE + 1.0;
439 }
440
441 if target_angle <= -default_oblique_angle {
442 if min_style.is_oblique() {
443 let max_style = range.1;
444 let max_angle = max_style.oblique_degrees();
445 if max_angle <= target_angle {
446 return target_angle - max_angle;
447 }
448 let min_angle = min_style.oblique_degrees();
449 if min_angle <= target_angle {
450 return 0.0;
451 }
452 if min_angle < 0.0 {
453 return REVERSE + (min_angle - target_angle);
454 }
455 return REVERSE + NEGATE + (min_angle - target_angle);
456 }
457 if min_style == FontStyle::ITALIC {
458 return REVERSE + NEGATE;
459 }
460 return REVERSE + NEGATE + 1.0;
461 }
462
463 if target_angle >= 0.0 {
464 if min_style.is_oblique() {
465 let min_angle = min_style.oblique_degrees();
466 if min_angle > target_angle {
467 return REVERSE + (min_angle - target_angle);
468 }
469 let max_style = range.1;
470 let max_angle = max_style.oblique_degrees();
471 if max_angle >= target_angle {
472 return 0.0;
473 }
474 if max_angle > 0.0 {
475 return target_angle - max_angle;
476 }
477 return REVERSE + NEGATE + (target_angle - max_angle);
478 }
479 if min_style == FontStyle::ITALIC {
480 return REVERSE + NEGATE - 2.0;
481 }
482 return REVERSE + NEGATE - 1.0;
483 }
484
485 if min_style.is_oblique() {
487 let max_style = range.1;
488 let max_angle = max_style.oblique_degrees();
489 if max_angle < target_angle {
490 return REVERSE + (target_angle - max_angle);
491 }
492 let min_angle = min_style.oblique_degrees();
493 if min_angle <= target_angle {
494 return 0.0;
495 }
496 if min_angle < 0.0 {
497 return min_angle - target_angle;
498 }
499 return REVERSE + NEGATE + (min_angle - target_angle);
500 }
501 if min_style == FontStyle::ITALIC {
502 return REVERSE + NEGATE - 2.0;
503 }
504 REVERSE + NEGATE - 1.0
505 }
506
507 fn to_float(&self) -> f32 {
508 unimplemented!("Don't know how to convert FontStyle to float.");
509 }
510}
511
512pub trait IsOblique {
513 fn is_oblique(&self) -> bool;
514}
515
516impl IsOblique for FontStyle {
517 fn is_oblique(&self) -> bool {
518 *self != FontStyle::NORMAL && *self != FontStyle::ITALIC
519 }
520}