fonts_traits/
font_template.rs1use std::fmt::{Debug, Error, Formatter};
6use std::ops::{Deref, RangeInclusive};
7use std::sync::Arc;
8
9use atomic_refcell::{AtomicRef, AtomicRefCell};
10use malloc_size_of_derive::MallocSizeOf;
11use read_fonts::collections::int_set::Domain;
12use read_fonts::types::Tag;
13use serde::{Deserialize, Serialize};
14use style::computed_values::font_optical_sizing::T as FontOpticalSizing;
15use style::computed_values::font_stretch::T as FontStretch;
16use style::computed_values::font_style::T as FontStyle;
17use style::font_face::{ComputedFontStretchRange, ComputedFontStyleRange, ComputedFontWeightRange};
18use style::properties::generated::font_face::Descriptors as FontFaceRuleDescriptors;
19use style::values::computed::font::FontWeight;
20use webrender_api::FontVariation;
21
22use crate::{CSSFontFaceDescriptors, FontDescriptor, FontIdentifier};
23
24#[derive(Clone, Debug, MallocSizeOf)]
26pub struct FontTemplateRef(#[conditional_malloc_size_of] Arc<AtomicRefCell<FontTemplate>>);
27
28impl FontTemplateRef {
29 pub fn new(template: FontTemplate) -> Self {
30 Self(Arc::new(AtomicRefCell::new(template)))
31 }
32}
33
34impl Deref for FontTemplateRef {
35 type Target = Arc<AtomicRefCell<FontTemplate>>;
36 fn deref(&self) -> &Self::Target {
37 &self.0
38 }
39}
40
41#[derive(Clone, Debug, Deserialize, Hash, MallocSizeOf, PartialEq, Serialize)]
46pub struct FontTemplateDescriptor {
47 pub weight: ComputedFontWeightRange,
48 pub stretch: ComputedFontStretchRange,
49 pub style: ComputedFontStyleRange,
50 #[ignore_malloc_size_of = "MallocSizeOf does not yet support RangeInclusive"]
51 pub unicode_range: Option<Vec<RangeInclusive<u32>>>,
52}
53
54impl Default for FontTemplateDescriptor {
55 fn default() -> Self {
56 Self::new(FontWeight::normal(), FontStretch::NORMAL, FontStyle::NORMAL)
57 }
58}
59
60impl FontTemplateDescriptor {
61 #[inline]
62 pub fn new(weight: FontWeight, stretch: FontStretch, style: FontStyle) -> Self {
63 Self {
64 weight: ComputedFontWeightRange(weight, weight),
65 stretch: ComputedFontStretchRange(stretch, stretch),
66 style: ComputedFontStyleRange(style, style),
67 unicode_range: None,
68 }
69 }
70
71 pub fn is_variation_font(&self) -> bool {
72 self.weight.0 != self.weight.1 ||
73 self.stretch.0 != self.stretch.1 ||
74 self.style.0 != self.style.1
75 }
76
77 #[inline]
84 fn distance_from(&self, target: &FontDescriptor) -> f32 {
85 let stretch_distance = target.stretch.match_distance(&self.stretch);
86 let style_distance = target.style.match_distance(&self.style);
87 let weight_distance = target.weight.match_distance(&self.weight);
88
89 assert!((0.0..=2000.0).contains(&stretch_distance));
92 assert!((0.0..=500.0).contains(&style_distance));
93 assert!((0.0..=1600.0).contains(&weight_distance));
94
95 const STRETCH_FACTOR: f32 = 1.0e8;
104 const STYLE_FACTOR: f32 = 1.0e4;
105 const WEIGHT_FACTOR: f32 = 1.0e0;
106
107 stretch_distance * STRETCH_FACTOR +
108 style_distance * STYLE_FACTOR +
109 weight_distance * WEIGHT_FACTOR
110 }
111
112 fn matches(&self, descriptor_to_match: &FontDescriptor) -> bool {
113 self.weight.0 <= descriptor_to_match.weight &&
114 self.weight.1 >= descriptor_to_match.weight &&
115 self.style.0 <= descriptor_to_match.style &&
116 self.style.1 >= descriptor_to_match.style &&
117 self.stretch.0 <= descriptor_to_match.stretch &&
118 self.stretch.1 >= descriptor_to_match.stretch
119 }
120
121 pub fn override_values_with_css_font_template_descriptors(
122 &mut self,
123 css_font_template_descriptors: &CSSFontFaceDescriptors,
124 ) {
125 if let Some(ref weight) = css_font_template_descriptors.weight {
126 self.weight = weight.clone();
127 }
128 if let Some(ref style) = css_font_template_descriptors.style {
129 self.style = style.clone();
130 }
131 if let Some(ref stretch) = css_font_template_descriptors.stretch {
132 self.stretch = stretch.clone();
133 }
134 if let Some(ref unicode_range) = css_font_template_descriptors.unicode_range {
135 self.unicode_range = Some(unicode_range.clone());
136 }
137 }
138}
139
140#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
144pub struct FontTemplate {
145 pub identifier: FontIdentifier,
146 pub descriptor: FontTemplateDescriptor,
147
148 #[serde(skip)]
151 pub font_face_rule: Option<FontFaceRuleDescriptors>,
152}
153
154impl Debug for FontTemplate {
155 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
156 self.identifier.fmt(f)
157 }
158}
159
160impl FontTemplate {
164 pub fn new(
166 identifier: FontIdentifier,
167 descriptor: FontTemplateDescriptor,
168 font_face_rule: Option<FontFaceRuleDescriptors>,
169 ) -> FontTemplate {
170 FontTemplate {
171 identifier,
172 descriptor,
173 font_face_rule,
174 }
175 }
176
177 pub fn new_for_local_web_font(
181 local_template: FontTemplateRef,
182 css_font_template_descriptors: &CSSFontFaceDescriptors,
183 font_face_rule: Option<FontFaceRuleDescriptors>,
184 ) -> Result<FontTemplate, &'static str> {
185 let mut alias_template = local_template.borrow().clone();
186 alias_template
187 .descriptor
188 .override_values_with_css_font_template_descriptors(css_font_template_descriptors);
189 alias_template.font_face_rule = font_face_rule;
190 Ok(alias_template)
191 }
192
193 pub fn identifier(&self) -> &FontIdentifier {
194 &self.identifier
195 }
196
197 pub fn compute_variations(&self, descriptor: &FontDescriptor) -> Vec<FontVariation> {
199 let mut variations: Vec<FontVariation> = vec![];
201
202 let mut add_variation = |variation: FontVariation| {
203 if !variations
204 .iter()
205 .any(|existing_variation| existing_variation.tag == variation.tag)
206 {
207 variations.push(variation);
208 }
209 };
210
211 descriptor
215 .variation_settings
216 .iter()
217 .copied()
218 .for_each(&mut add_variation);
219
220 if let Some(font_face_rule) = &self.font_face_rule {
226 if let Some(variation_settings) = font_face_rule.font_variation_settings.as_ref() {
229 variation_settings
230 .0
231 .iter()
232 .map(|variation| FontVariation {
233 tag: variation.tag.0,
234 value: variation.value.get().expect(
235 "The value is enforced to be resolvable at parse time \
236 (see FontVariationSettings::parse_for_font_face_rule).",
237 ),
238 })
239 .for_each(&mut add_variation);
240 }
241 }
242
243 add_variation(FontVariation {
247 tag: Tag::new(b"wght").to_u32(),
248 value: descriptor.weight.value(),
249 });
250
251 add_variation(FontVariation {
252 tag: Tag::new(b"wdth").to_u32(),
253 value: descriptor.stretch.0.to_float(),
254 });
255
256 if descriptor.optical_sizing == FontOpticalSizing::Auto {
258 add_variation(FontVariation {
259 tag: Tag::new(b"opsz").to_u32(),
260 value: descriptor.pt_size.to_f32_px(),
261 });
262 }
263
264 variations
265 }
266
267 pub fn is_defined_by_font_face_rule(&self, rule: &FontFaceRuleDescriptors) -> bool {
268 self.font_face_rule
269 .as_ref()
270 .is_some_and(|defining_rule| defining_rule == rule)
271 }
272}
273
274pub trait FontTemplateRefMethods {
275 fn descriptor(&self) -> FontTemplateDescriptor;
277 fn identifier(&self) -> FontIdentifier;
279 fn matches_font_descriptor(&self, descriptor_to_match: &FontDescriptor) -> bool;
281 fn descriptor_distance(&self, descriptor_to_match: &FontDescriptor) -> f32;
284 fn char_in_unicode_range(&self, character: char) -> bool;
287
288 fn font_face_rule(&self) -> Option<AtomicRef<'_, FontFaceRuleDescriptors>>;
290}
291
292impl FontTemplateRefMethods for FontTemplateRef {
293 fn descriptor(&self) -> FontTemplateDescriptor {
294 self.borrow().descriptor.clone()
295 }
296
297 fn identifier(&self) -> FontIdentifier {
298 self.borrow().identifier.clone()
299 }
300
301 fn matches_font_descriptor(&self, descriptor_to_match: &FontDescriptor) -> bool {
302 self.descriptor().matches(descriptor_to_match)
303 }
304
305 fn descriptor_distance(&self, descriptor_to_match: &FontDescriptor) -> f32 {
306 self.descriptor().distance_from(descriptor_to_match)
307 }
308
309 fn char_in_unicode_range(&self, character: char) -> bool {
310 let character = character as u32;
311 self.borrow()
312 .descriptor
313 .unicode_range
314 .as_ref()
315 .is_none_or(|ranges| ranges.iter().any(|range| range.contains(&character)))
316 }
317
318 fn font_face_rule(&self) -> Option<AtomicRef<'_, FontFaceRuleDescriptors>> {
319 AtomicRef::filter_map(self.borrow(), |template| template.font_face_rule.as_ref())
320 }
321}
322
323trait FontMatchDistanceMethod<T>: Sized {
329 fn match_distance(&self, range: &T) -> f32;
330 fn to_float(&self) -> f32;
331}
332
333impl FontMatchDistanceMethod<ComputedFontStretchRange> for FontStretch {
334 fn match_distance(&self, range: &ComputedFontStretchRange) -> f32 {
335 const REVERSE_DISTANCE: f32 = 1000.0;
337
338 let min_stretch = range.0;
339 let max_stretch = range.1;
340
341 if *self < min_stretch {
347 if *self > FontStretch::NORMAL {
348 return min_stretch.to_float() - self.to_float();
349 }
350 return (min_stretch.to_float() - self.to_float()) + REVERSE_DISTANCE;
351 }
352
353 if *self > max_stretch {
354 if *self <= FontStretch::NORMAL {
355 return self.to_float() - max_stretch.to_float();
356 }
357 return (self.to_float() - max_stretch.to_float()) + REVERSE_DISTANCE;
358 }
359 0.0
360 }
361
362 fn to_float(&self) -> f32 {
363 self.0.to_float()
364 }
365}
366
367impl FontMatchDistanceMethod<ComputedFontWeightRange> for FontWeight {
368 fn match_distance(&self, range: &ComputedFontWeightRange) -> f32 {
380 const NOT_WITHIN_CENTRAL_RANGE: f32 = 100.0;
382 const REVERSE_DISTANCE: f32 = 600.0;
383
384 let min_weight = range.0;
385 let max_weight = range.1;
386
387 if *self >= min_weight && *self <= max_weight {
388 return 0.0;
390 }
391
392 if *self < FontWeight::NORMAL {
393 if max_weight < *self {
395 return self.to_float() - max_weight.to_float();
396 }
397
398 return (min_weight.to_float() - self.to_float()) + REVERSE_DISTANCE;
400 }
401
402 if *self > FontWeight::from_float(500.) {
403 if min_weight > *self {
405 return min_weight.to_float() - self.to_float();
406 }
407 return (self.to_float() - max_weight.to_float()) + REVERSE_DISTANCE;
409 }
410
411 if min_weight > *self {
413 if min_weight <= FontWeight::from_float(500.) {
414 return min_weight.to_float() - self.to_float();
416 }
417 return (min_weight.to_float() - self.to_float()) + REVERSE_DISTANCE;
419 }
420 (self.to_float() - max_weight.to_float()) + NOT_WITHIN_CENTRAL_RANGE
422 }
423
424 fn to_float(&self) -> f32 {
425 self.value()
426 }
427}
428
429impl FontMatchDistanceMethod<ComputedFontStyleRange> for FontStyle {
430 fn match_distance(&self, range: &ComputedFontStyleRange) -> f32 {
431 let min_style = range.0;
433 if *self == min_style {
434 return 0.0; }
436
437 const REVERSE: f32 = 100.0;
440
441 const NEGATE: f32 = 200.0;
444
445 if *self == FontStyle::NORMAL {
446 if min_style.is_oblique() {
447 let min_angle = min_style.oblique_degrees();
449 if min_angle >= 0.0 {
450 return 1.0 + min_angle;
451 }
452 let max_style = range.1;
453 let max_angle = max_style.oblique_degrees();
454 if max_angle >= 0.0 {
455 return 1.0;
457 }
458 return NEGATE - max_angle;
460 }
461 assert!(min_style == FontStyle::ITALIC);
464 return REVERSE;
465 }
466
467 let default_oblique_angle = FontStyle::OBLIQUE.oblique_degrees();
468 if *self == FontStyle::ITALIC {
469 if min_style.is_oblique() {
470 let min_angle = min_style.oblique_degrees();
471 if min_angle >= default_oblique_angle {
472 return 1.0 + (min_angle - default_oblique_angle);
473 }
474 let max_style = range.1;
475 let max_angle = max_style.oblique_degrees();
476 if max_angle >= default_oblique_angle {
477 return 1.0;
478 }
479 if max_angle > 0.0 {
480 return REVERSE + (default_oblique_angle - max_angle);
482 }
483 return REVERSE + NEGATE + (default_oblique_angle - max_angle);
485 }
486 assert!(min_style == FontStyle::NORMAL);
488 return NEGATE;
489 }
490
491 let target_angle = self.oblique_degrees();
495 if target_angle >= default_oblique_angle {
496 if min_style.is_oblique() {
497 let min_angle = min_style.oblique_degrees();
498 if min_angle >= target_angle {
499 return min_angle - target_angle;
500 }
501 let max_style = range.1;
502 let max_angle = max_style.oblique_degrees();
503 if max_angle >= target_angle {
504 return 0.0;
505 }
506 if max_angle > 0.0 {
507 return REVERSE + (target_angle - max_angle);
508 }
509 return REVERSE + NEGATE + (target_angle - max_angle);
510 }
511 if min_style == FontStyle::ITALIC {
512 return REVERSE + NEGATE;
513 }
514 return REVERSE + NEGATE + 1.0;
515 }
516
517 if target_angle <= -default_oblique_angle {
518 if min_style.is_oblique() {
519 let max_style = range.1;
520 let max_angle = max_style.oblique_degrees();
521 if max_angle <= target_angle {
522 return target_angle - max_angle;
523 }
524 let min_angle = min_style.oblique_degrees();
525 if min_angle <= target_angle {
526 return 0.0;
527 }
528 if min_angle < 0.0 {
529 return REVERSE + (min_angle - target_angle);
530 }
531 return REVERSE + NEGATE + (min_angle - target_angle);
532 }
533 if min_style == FontStyle::ITALIC {
534 return REVERSE + NEGATE;
535 }
536 return REVERSE + NEGATE + 1.0;
537 }
538
539 if target_angle >= 0.0 {
540 if min_style.is_oblique() {
541 let min_angle = min_style.oblique_degrees();
542 if min_angle > target_angle {
543 return REVERSE + (min_angle - target_angle);
544 }
545 let max_style = range.1;
546 let max_angle = max_style.oblique_degrees();
547 if max_angle >= target_angle {
548 return 0.0;
549 }
550 if max_angle > 0.0 {
551 return target_angle - max_angle;
552 }
553 return REVERSE + NEGATE + (target_angle - max_angle);
554 }
555 if min_style == FontStyle::ITALIC {
556 return REVERSE + NEGATE - 2.0;
557 }
558 return REVERSE + NEGATE - 1.0;
559 }
560
561 if min_style.is_oblique() {
563 let max_style = range.1;
564 let max_angle = max_style.oblique_degrees();
565 if max_angle < target_angle {
566 return REVERSE + (target_angle - max_angle);
567 }
568 let min_angle = min_style.oblique_degrees();
569 if min_angle <= target_angle {
570 return 0.0;
571 }
572 if min_angle < 0.0 {
573 return min_angle - target_angle;
574 }
575 return REVERSE + NEGATE + (min_angle - target_angle);
576 }
577 if min_style == FontStyle::ITALIC {
578 return REVERSE + NEGATE - 2.0;
579 }
580 REVERSE + NEGATE - 1.0
581 }
582
583 fn to_float(&self) -> f32 {
584 unimplemented!("Don't know how to convert FontStyle to float.");
585 }
586}
587
588pub trait IsOblique {
589 fn is_oblique(&self) -> bool;
590}
591
592impl IsOblique for FontStyle {
593 fn is_oblique(&self) -> bool {
594 *self != FontStyle::NORMAL && *self != FontStyle::ITALIC
595 }
596}