1use 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::{
18 ComputedFontStretchRange, ComputedFontStyleDescriptor, ComputedFontWeightRange,
19};
20use style::properties::generated::font_face::Descriptors as FontFaceRuleDescriptors;
21use style::values::computed::font::FontWeight;
22use webrender_api::FontVariation;
23
24use crate::{CSSFontFaceDescriptors, FontDescriptor, FontIdentifier};
25
26#[derive(Clone, Debug, MallocSizeOf)]
28pub struct FontTemplateRef(#[conditional_malloc_size_of] Arc<AtomicRefCell<FontTemplate>>);
29
30impl FontTemplateRef {
31 pub fn new(template: FontTemplate) -> Self {
32 Self(Arc::new(AtomicRefCell::new(template)))
33 }
34}
35
36impl Deref for FontTemplateRef {
37 type Target = Arc<AtomicRefCell<FontTemplate>>;
38 fn deref(&self) -> &Self::Target {
39 &self.0
40 }
41}
42
43#[derive(Clone, Debug, Deserialize, Hash, MallocSizeOf, PartialEq, Serialize)]
48pub struct FontTemplateDescriptor {
49 pub weight: ComputedFontWeightRange,
50 pub stretch: ComputedFontStretchRange,
51 pub style: (FontStyle, FontStyle),
52 #[ignore_malloc_size_of = "MallocSizeOf does not yet support RangeInclusive"]
53 pub unicode_range: Option<Vec<RangeInclusive<u32>>>,
54}
55
56impl Default for FontTemplateDescriptor {
57 fn default() -> Self {
58 Self::new(FontWeight::normal(), FontStretch::NORMAL, FontStyle::NORMAL)
59 }
60}
61
62impl Eq for FontTemplateDescriptor {}
65
66impl FontTemplateDescriptor {
67 #[inline]
68 pub fn new(weight: FontWeight, stretch: FontStretch, style: FontStyle) -> Self {
69 Self {
70 weight: ComputedFontWeightRange(weight, weight),
71 stretch: ComputedFontStretchRange(stretch, stretch),
72 style: (style, style),
73 unicode_range: None,
74 }
75 }
76
77 pub fn is_variation_font(&self) -> bool {
78 self.weight.0 != self.weight.1 ||
79 self.stretch.0 != self.stretch.1 ||
80 self.style.0 != self.style.1
81 }
82
83 #[inline]
90 fn distance_from(&self, target: &FontDescriptor) -> f32 {
91 let stretch_distance = target.stretch.match_distance(&self.stretch);
92 let style_distance = target.style.match_distance(&self.style);
93 let weight_distance = target.weight.match_distance(&self.weight);
94
95 assert!((0.0..=2000.0).contains(&stretch_distance));
98 assert!((0.0..=500.0).contains(&style_distance));
99 assert!((0.0..=1600.0).contains(&weight_distance));
100
101 const STRETCH_FACTOR: f32 = 1.0e8;
110 const STYLE_FACTOR: f32 = 1.0e4;
111 const WEIGHT_FACTOR: f32 = 1.0e0;
112
113 stretch_distance * STRETCH_FACTOR +
114 style_distance * STYLE_FACTOR +
115 weight_distance * WEIGHT_FACTOR
116 }
117
118 fn matches(&self, descriptor_to_match: &FontDescriptor) -> bool {
119 self.weight.0 <= descriptor_to_match.weight &&
120 self.weight.1 >= descriptor_to_match.weight &&
121 self.style.0 <= descriptor_to_match.style &&
122 self.style.1 >= descriptor_to_match.style &&
123 self.stretch.0 <= descriptor_to_match.stretch &&
124 self.stretch.1 >= descriptor_to_match.stretch
125 }
126
127 pub fn override_values_with_css_font_template_descriptors(
128 &mut self,
129 css_font_template_descriptors: &CSSFontFaceDescriptors,
130 ) {
131 if let Some(ref weight) = css_font_template_descriptors.weight {
132 self.weight = weight.clone();
133 }
134 self.style = match css_font_template_descriptors.style {
135 Some(ComputedFontStyleDescriptor::Italic) => (FontStyle::ITALIC, FontStyle::ITALIC),
136 Some(ComputedFontStyleDescriptor::Oblique(angle_1, angle_2)) => (
137 FontStyle::oblique(angle_1.to_float()),
138 FontStyle::oblique(angle_2.to_float()),
139 ),
140 None => self.style,
141 };
142 if let Some(ref stretch) = css_font_template_descriptors.stretch {
143 self.stretch = stretch.clone();
144 }
145 if let Some(ref unicode_range) = css_font_template_descriptors.unicode_range {
146 self.unicode_range = Some(unicode_range.clone());
147 }
148 }
149}
150
151#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
155pub struct FontTemplate {
156 pub identifier: FontIdentifier,
157 pub descriptor: FontTemplateDescriptor,
158
159 #[serde(skip)]
162 pub font_face_rule: Option<FontFaceRuleDescriptors>,
163}
164
165impl Debug for FontTemplate {
166 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
167 self.identifier.fmt(f)
168 }
169}
170
171impl FontTemplate {
175 pub fn new(
177 identifier: FontIdentifier,
178 descriptor: FontTemplateDescriptor,
179 font_face_rule: Option<FontFaceRuleDescriptors>,
180 ) -> FontTemplate {
181 FontTemplate {
182 identifier,
183 descriptor,
184 font_face_rule,
185 }
186 }
187
188 pub fn new_for_local_web_font(
192 local_template: FontTemplateRef,
193 css_font_template_descriptors: &CSSFontFaceDescriptors,
194 font_face_rule: Option<FontFaceRuleDescriptors>,
195 ) -> Result<FontTemplate, &'static str> {
196 let mut alias_template = local_template.borrow().clone();
197 alias_template
198 .descriptor
199 .override_values_with_css_font_template_descriptors(css_font_template_descriptors);
200 alias_template.font_face_rule = font_face_rule;
201 Ok(alias_template)
202 }
203
204 pub fn identifier(&self) -> &FontIdentifier {
205 &self.identifier
206 }
207
208 pub fn compute_variations(&self, descriptor: &FontDescriptor) -> Vec<FontVariation> {
210 let mut variations: Vec<FontVariation> = vec![];
212
213 let mut add_variation = |variation: FontVariation| {
214 if !variations
215 .iter()
216 .any(|existing_variation| existing_variation.tag == variation.tag)
217 {
218 variations.push(variation);
219 }
220 };
221
222 descriptor
226 .variation_settings
227 .iter()
228 .copied()
229 .for_each(&mut add_variation);
230
231 if let Some(font_face_rule) = &self.font_face_rule {
237 if let Some(variation_settings) = font_face_rule.font_variation_settings.as_ref() {
240 variation_settings
241 .0
242 .iter()
243 .map(|variation| FontVariation {
244 tag: variation.tag.0,
245 value: variation.value.get().expect(
246 "The value is enforced to be resolvable at parse time \
247 (see FontVariationSettings::parse_for_font_face_rule).",
248 ),
249 })
250 .for_each(&mut add_variation);
251 }
252 }
253
254 add_variation(FontVariation {
258 tag: Tag::new(b"wght").to_u32(),
259 value: descriptor.weight.value(),
260 });
261
262 add_variation(FontVariation {
263 tag: Tag::new(b"wdth").to_u32(),
264 value: descriptor.stretch.0.to_float(),
265 });
266
267 if descriptor.optical_sizing == FontOpticalSizing::Auto {
269 add_variation(FontVariation {
270 tag: Tag::new(b"opsz").to_u32(),
271 value: descriptor.pt_size.to_f32_px(),
272 });
273 }
274
275 variations
276 }
277
278 pub fn is_defined_by_font_face_rule(&self, rule: &FontFaceRuleDescriptors) -> bool {
279 self.font_face_rule
280 .as_ref()
281 .is_some_and(|defining_rule| defining_rule == rule)
282 }
283}
284
285pub trait FontTemplateRefMethods {
286 fn descriptor(&self) -> FontTemplateDescriptor;
288 fn identifier(&self) -> FontIdentifier;
290 fn matches_font_descriptor(&self, descriptor_to_match: &FontDescriptor) -> bool;
292 fn descriptor_distance(&self, descriptor_to_match: &FontDescriptor) -> f32;
295 fn char_in_unicode_range(&self, character: char) -> bool;
298
299 fn font_face_rule(&self) -> Option<AtomicRef<'_, FontFaceRuleDescriptors>>;
301}
302
303impl FontTemplateRefMethods for FontTemplateRef {
304 fn descriptor(&self) -> FontTemplateDescriptor {
305 self.borrow().descriptor.clone()
306 }
307
308 fn identifier(&self) -> FontIdentifier {
309 self.borrow().identifier.clone()
310 }
311
312 fn matches_font_descriptor(&self, descriptor_to_match: &FontDescriptor) -> bool {
313 self.descriptor().matches(descriptor_to_match)
314 }
315
316 fn descriptor_distance(&self, descriptor_to_match: &FontDescriptor) -> f32 {
317 self.descriptor().distance_from(descriptor_to_match)
318 }
319
320 fn char_in_unicode_range(&self, character: char) -> bool {
321 let character = character as u32;
322 self.borrow()
323 .descriptor
324 .unicode_range
325 .as_ref()
326 .is_none_or(|ranges| ranges.iter().any(|range| range.contains(&character)))
327 }
328
329 fn font_face_rule(&self) -> Option<AtomicRef<'_, FontFaceRuleDescriptors>> {
330 AtomicRef::filter_map(self.borrow(), |template| template.font_face_rule.as_ref())
331 }
332}
333
334trait FontMatchDistanceMethod<T>: Sized {
340 fn match_distance(&self, range: &T) -> f32;
341 fn to_float(&self) -> f32;
342}
343
344impl FontMatchDistanceMethod<ComputedFontStretchRange> for FontStretch {
345 fn match_distance(&self, range: &ComputedFontStretchRange) -> f32 {
346 const REVERSE_DISTANCE: f32 = 1000.0;
348
349 let min_stretch = range.0;
350 let max_stretch = range.1;
351
352 if *self < min_stretch {
358 if *self > FontStretch::NORMAL {
359 return min_stretch.to_float() - self.to_float();
360 }
361 return (min_stretch.to_float() - self.to_float()) + REVERSE_DISTANCE;
362 }
363
364 if *self > max_stretch {
365 if *self <= FontStretch::NORMAL {
366 return self.to_float() - max_stretch.to_float();
367 }
368 return (self.to_float() - max_stretch.to_float()) + REVERSE_DISTANCE;
369 }
370 0.0
371 }
372
373 fn to_float(&self) -> f32 {
374 self.0.to_float()
375 }
376}
377
378impl FontMatchDistanceMethod<ComputedFontWeightRange> for FontWeight {
379 fn match_distance(&self, range: &ComputedFontWeightRange) -> f32 {
391 const NOT_WITHIN_CENTRAL_RANGE: f32 = 100.0;
393 const REVERSE_DISTANCE: f32 = 600.0;
394
395 let min_weight = range.0;
396 let max_weight = range.1;
397
398 if *self >= min_weight && *self <= max_weight {
399 return 0.0;
401 }
402
403 if *self < FontWeight::NORMAL {
404 if max_weight < *self {
406 return self.to_float() - max_weight.to_float();
407 }
408
409 return (min_weight.to_float() - self.to_float()) + REVERSE_DISTANCE;
411 }
412
413 if *self > FontWeight::from_float(500.) {
414 if min_weight > *self {
416 return min_weight.to_float() - self.to_float();
417 }
418 return (self.to_float() - max_weight.to_float()) + REVERSE_DISTANCE;
420 }
421
422 if min_weight > *self {
424 if min_weight <= FontWeight::from_float(500.) {
425 return min_weight.to_float() - self.to_float();
427 }
428 return (min_weight.to_float() - self.to_float()) + REVERSE_DISTANCE;
430 }
431 (self.to_float() - max_weight.to_float()) + NOT_WITHIN_CENTRAL_RANGE
433 }
434
435 fn to_float(&self) -> f32 {
436 self.value()
437 }
438}
439
440impl FontMatchDistanceMethod<(Self, Self)> for FontStyle {
441 fn match_distance(&self, range: &(Self, Self)) -> f32 {
442 let min_style = range.0;
444 if *self == min_style {
445 return 0.0; }
447
448 const REVERSE: f32 = 100.0;
451
452 const NEGATE: f32 = 200.0;
455
456 if *self == FontStyle::NORMAL {
457 if min_style.is_oblique() {
458 let min_angle = min_style.oblique_degrees();
460 if min_angle >= 0.0 {
461 return 1.0 + min_angle;
462 }
463 let max_style = range.1;
464 let max_angle = max_style.oblique_degrees();
465 if max_angle >= 0.0 {
466 return 1.0;
468 }
469 return NEGATE - max_angle;
471 }
472 assert!(min_style == FontStyle::ITALIC);
475 return REVERSE;
476 }
477
478 let default_oblique_angle = FontStyle::OBLIQUE.oblique_degrees();
479 if *self == FontStyle::ITALIC {
480 if min_style.is_oblique() {
481 let min_angle = min_style.oblique_degrees();
482 if min_angle >= default_oblique_angle {
483 return 1.0 + (min_angle - default_oblique_angle);
484 }
485 let max_style = range.1;
486 let max_angle = max_style.oblique_degrees();
487 if max_angle >= default_oblique_angle {
488 return 1.0;
489 }
490 if max_angle > 0.0 {
491 return REVERSE + (default_oblique_angle - max_angle);
493 }
494 return REVERSE + NEGATE + (default_oblique_angle - max_angle);
496 }
497 assert!(min_style == FontStyle::NORMAL);
499 return NEGATE;
500 }
501
502 let target_angle = self.oblique_degrees();
506 if target_angle >= default_oblique_angle {
507 if min_style.is_oblique() {
508 let min_angle = min_style.oblique_degrees();
509 if min_angle >= target_angle {
510 return min_angle - target_angle;
511 }
512 let max_style = range.1;
513 let max_angle = max_style.oblique_degrees();
514 if max_angle >= target_angle {
515 return 0.0;
516 }
517 if max_angle > 0.0 {
518 return REVERSE + (target_angle - max_angle);
519 }
520 return REVERSE + NEGATE + (target_angle - max_angle);
521 }
522 if min_style == FontStyle::ITALIC {
523 return REVERSE + NEGATE;
524 }
525 return REVERSE + NEGATE + 1.0;
526 }
527
528 if target_angle <= -default_oblique_angle {
529 if min_style.is_oblique() {
530 let max_style = range.1;
531 let max_angle = max_style.oblique_degrees();
532 if max_angle <= target_angle {
533 return target_angle - max_angle;
534 }
535 let min_angle = min_style.oblique_degrees();
536 if min_angle <= target_angle {
537 return 0.0;
538 }
539 if min_angle < 0.0 {
540 return REVERSE + (min_angle - target_angle);
541 }
542 return REVERSE + NEGATE + (min_angle - target_angle);
543 }
544 if min_style == FontStyle::ITALIC {
545 return REVERSE + NEGATE;
546 }
547 return REVERSE + NEGATE + 1.0;
548 }
549
550 if target_angle >= 0.0 {
551 if min_style.is_oblique() {
552 let min_angle = min_style.oblique_degrees();
553 if min_angle > target_angle {
554 return REVERSE + (min_angle - target_angle);
555 }
556 let max_style = range.1;
557 let max_angle = max_style.oblique_degrees();
558 if max_angle >= target_angle {
559 return 0.0;
560 }
561 if max_angle > 0.0 {
562 return target_angle - max_angle;
563 }
564 return REVERSE + NEGATE + (target_angle - max_angle);
565 }
566 if min_style == FontStyle::ITALIC {
567 return REVERSE + NEGATE - 2.0;
568 }
569 return REVERSE + NEGATE - 1.0;
570 }
571
572 if min_style.is_oblique() {
574 let max_style = range.1;
575 let max_angle = max_style.oblique_degrees();
576 if max_angle < target_angle {
577 return REVERSE + (target_angle - max_angle);
578 }
579 let min_angle = min_style.oblique_degrees();
580 if min_angle <= target_angle {
581 return 0.0;
582 }
583 if min_angle < 0.0 {
584 return min_angle - target_angle;
585 }
586 return REVERSE + NEGATE + (min_angle - target_angle);
587 }
588 if min_style == FontStyle::ITALIC {
589 return REVERSE + NEGATE - 2.0;
590 }
591 REVERSE + NEGATE - 1.0
592 }
593
594 fn to_float(&self) -> f32 {
595 unimplemented!("Don't know how to convert FontStyle to float.");
596 }
597}
598
599pub trait IsOblique {
600 fn is_oblique(&self) -> bool;
601}
602
603impl IsOblique for FontStyle {
604 fn is_oblique(&self) -> bool {
605 *self != FontStyle::NORMAL && *self != FontStyle::ITALIC
606 }
607}