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::stylesheets::{DocumentStyleSheet, FontFaceRule};
18use style::values::computed::font::FontWeight;
19use webrender_api::FontVariation;
20
21use crate::{CSSFontFaceDescriptors, ComputedFontStyleDescriptor, FontDescriptor, FontIdentifier};
22
23#[derive(Clone, Debug, MallocSizeOf)]
25pub struct FontTemplateRef(#[conditional_malloc_size_of] Arc<AtomicRefCell<FontTemplate>>);
26
27impl FontTemplateRef {
28 pub fn new(template: FontTemplate) -> Self {
29 Self(Arc::new(AtomicRefCell::new(template)))
30 }
31}
32
33impl Deref for FontTemplateRef {
34 type Target = Arc<AtomicRefCell<FontTemplate>>;
35 fn deref(&self) -> &Self::Target {
36 &self.0
37 }
38}
39
40#[derive(Clone, Debug, Deserialize, Hash, MallocSizeOf, PartialEq, Serialize)]
45pub struct FontTemplateDescriptor {
46 pub weight: (FontWeight, FontWeight),
47 pub stretch: (FontStretch, FontStretch),
48 pub style: (FontStyle, FontStyle),
49 #[ignore_malloc_size_of = "MallocSizeOf does not yet support RangeInclusive"]
50 pub unicode_range: Option<Vec<RangeInclusive<u32>>>,
51}
52
53impl Default for FontTemplateDescriptor {
54 fn default() -> Self {
55 Self::new(FontWeight::normal(), FontStretch::NORMAL, FontStyle::NORMAL)
56 }
57}
58
59impl Eq for FontTemplateDescriptor {}
62
63impl FontTemplateDescriptor {
64 #[inline]
65 pub fn new(weight: FontWeight, stretch: FontStretch, style: FontStyle) -> Self {
66 Self {
67 weight: (weight, weight),
68 stretch: (stretch, stretch),
69 style: (style, style),
70 unicode_range: None,
71 }
72 }
73
74 pub fn is_variation_font(&self) -> bool {
75 self.weight.0 != self.weight.1 ||
76 self.stretch.0 != self.stretch.1 ||
77 self.style.0 != self.style.1
78 }
79
80 #[inline]
87 fn distance_from(&self, target: &FontDescriptor) -> f32 {
88 let stretch_distance = target.stretch.match_distance(&self.stretch);
89 let style_distance = target.style.match_distance(&self.style);
90 let weight_distance = target.weight.match_distance(&self.weight);
91
92 assert!((0.0..=2000.0).contains(&stretch_distance));
95 assert!((0.0..=500.0).contains(&style_distance));
96 assert!((0.0..=1600.0).contains(&weight_distance));
97
98 const STRETCH_FACTOR: f32 = 1.0e8;
107 const STYLE_FACTOR: f32 = 1.0e4;
108 const WEIGHT_FACTOR: f32 = 1.0e0;
109
110 stretch_distance * STRETCH_FACTOR +
111 style_distance * STYLE_FACTOR +
112 weight_distance * WEIGHT_FACTOR
113 }
114
115 fn matches(&self, descriptor_to_match: &FontDescriptor) -> bool {
116 self.weight.0 <= descriptor_to_match.weight &&
117 self.weight.1 >= descriptor_to_match.weight &&
118 self.style.0 <= descriptor_to_match.style &&
119 self.style.1 >= descriptor_to_match.style &&
120 self.stretch.0 <= descriptor_to_match.stretch &&
121 self.stretch.1 >= descriptor_to_match.stretch
122 }
123
124 pub fn override_values_with_css_font_template_descriptors(
125 &mut self,
126 css_font_template_descriptors: &CSSFontFaceDescriptors,
127 ) {
128 if let Some(weight) = css_font_template_descriptors.weight {
129 self.weight = weight;
130 }
131 self.style = match css_font_template_descriptors.style {
132 Some(ComputedFontStyleDescriptor::Italic) => (FontStyle::ITALIC, FontStyle::ITALIC),
133 Some(ComputedFontStyleDescriptor::Oblique(angle_1, angle_2)) => (
134 FontStyle::oblique(angle_1.to_float()),
135 FontStyle::oblique(angle_2.to_float()),
136 ),
137 None => self.style,
138 };
139 if let Some(stretch) = css_font_template_descriptors.stretch {
140 self.stretch = stretch;
141 }
142 if let Some(ref unicode_range) = css_font_template_descriptors.unicode_range {
143 self.unicode_range = Some(unicode_range.clone());
144 }
145 }
146}
147
148#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
152pub struct FontTemplate {
153 pub identifier: FontIdentifier,
154 pub descriptor: FontTemplateDescriptor,
155 #[serde(skip)]
162 pub stylesheet: Option<DocumentStyleSheet>,
163
164 #[serde(skip)]
167 pub font_face_rule: Option<FontFaceRule>,
168}
169
170impl Debug for FontTemplate {
171 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
172 self.identifier.fmt(f)
173 }
174}
175
176impl FontTemplate {
180 pub fn new(
182 identifier: FontIdentifier,
183 descriptor: FontTemplateDescriptor,
184 stylesheet: Option<DocumentStyleSheet>,
185 font_face_rule: Option<FontFaceRule>,
186 ) -> FontTemplate {
187 assert!(
188 (stylesheet.is_some() && font_face_rule.is_some()) ||
189 (stylesheet.is_none() && font_face_rule.is_none())
190 );
191 FontTemplate {
192 identifier,
193 descriptor,
194 stylesheet,
195 font_face_rule,
196 }
197 }
198
199 pub fn new_for_local_web_font(
203 local_template: FontTemplateRef,
204 css_font_template_descriptors: &CSSFontFaceDescriptors,
205 stylesheet: Option<DocumentStyleSheet>,
206 font_face_rule: Option<FontFaceRule>,
207 ) -> Result<FontTemplate, &'static str> {
208 let mut alias_template = local_template.borrow().clone();
209 alias_template
210 .descriptor
211 .override_values_with_css_font_template_descriptors(css_font_template_descriptors);
212 alias_template.stylesheet = stylesheet;
213 alias_template.font_face_rule = font_face_rule;
214 Ok(alias_template)
215 }
216
217 pub fn identifier(&self) -> &FontIdentifier {
218 &self.identifier
219 }
220
221 pub fn compute_variations(&self, descriptor: &FontDescriptor) -> Vec<FontVariation> {
223 let mut variations: Vec<FontVariation> = vec![];
225
226 let mut add_variation = |variation: FontVariation| {
227 if !variations
228 .iter()
229 .any(|existing_variation| existing_variation.tag == variation.tag)
230 {
231 variations.push(variation);
232 }
233 };
234
235 descriptor
239 .variation_settings
240 .iter()
241 .copied()
242 .for_each(&mut add_variation);
243
244 if let Some(font_face_rule) = &self.font_face_rule {
250 if let Some(variation_settings) =
253 font_face_rule.descriptors.font_variation_settings.as_ref()
254 {
255 variation_settings
256 .0
257 .iter()
258 .map(|variation| FontVariation {
259 tag: variation.tag.0,
260 value: variation.value.get(),
261 })
262 .for_each(&mut add_variation);
263 }
264 }
265
266 if descriptor.weight != FontWeight::NORMAL {
270 add_variation(FontVariation {
271 tag: Tag::new(b"wght").to_u32(),
272 value: descriptor.weight.value(),
273 });
274 }
275
276 if descriptor.stretch != FontStretch::NORMAL {
277 add_variation(FontVariation {
278 tag: Tag::new(b"wdth").to_u32(),
279 value: descriptor.stretch.0.to_float(),
280 });
281 }
282
283 if descriptor.optical_sizing == FontOpticalSizing::Auto {
285 add_variation(FontVariation {
286 tag: Tag::new(b"opsz").to_u32(),
287 value: descriptor.pt_size.to_f32_px(),
288 });
289 }
290
291 variations
292 }
293}
294
295pub trait FontTemplateRefMethods {
296 fn descriptor(&self) -> FontTemplateDescriptor;
298 fn identifier(&self) -> FontIdentifier;
300 fn matches_font_descriptor(&self, descriptor_to_match: &FontDescriptor) -> bool;
302 fn descriptor_distance(&self, descriptor_to_match: &FontDescriptor) -> f32;
305 fn char_in_unicode_range(&self, character: char) -> bool;
308
309 fn font_face_rule(&self) -> Option<AtomicRef<'_, FontFaceRule>>;
311}
312
313impl FontTemplateRefMethods for FontTemplateRef {
314 fn descriptor(&self) -> FontTemplateDescriptor {
315 self.borrow().descriptor.clone()
316 }
317
318 fn identifier(&self) -> FontIdentifier {
319 self.borrow().identifier.clone()
320 }
321
322 fn matches_font_descriptor(&self, descriptor_to_match: &FontDescriptor) -> bool {
323 self.descriptor().matches(descriptor_to_match)
324 }
325
326 fn descriptor_distance(&self, descriptor_to_match: &FontDescriptor) -> f32 {
327 self.descriptor().distance_from(descriptor_to_match)
328 }
329
330 fn char_in_unicode_range(&self, character: char) -> bool {
331 let character = character as u32;
332 self.borrow()
333 .descriptor
334 .unicode_range
335 .as_ref()
336 .is_none_or(|ranges| ranges.iter().any(|range| range.contains(&character)))
337 }
338
339 fn font_face_rule(&self) -> Option<AtomicRef<'_, FontFaceRule>> {
340 AtomicRef::filter_map(self.borrow(), |template| template.font_face_rule.as_ref())
341 }
342}
343
344trait FontMatchDistanceMethod: Sized {
350 fn match_distance(&self, range: &(Self, Self)) -> f32;
351 fn to_float(&self) -> f32;
352}
353
354impl FontMatchDistanceMethod for FontStretch {
355 fn match_distance(&self, range: &(Self, Self)) -> f32 {
356 const REVERSE_DISTANCE: f32 = 1000.0;
358
359 let min_stretch = range.0;
360 let max_stretch = range.1;
361
362 if *self < min_stretch {
368 if *self > FontStretch::NORMAL {
369 return min_stretch.to_float() - self.to_float();
370 }
371 return (min_stretch.to_float() - self.to_float()) + REVERSE_DISTANCE;
372 }
373
374 if *self > max_stretch {
375 if *self <= FontStretch::NORMAL {
376 return self.to_float() - max_stretch.to_float();
377 }
378 return (self.to_float() - max_stretch.to_float()) + REVERSE_DISTANCE;
379 }
380 0.0
381 }
382
383 fn to_float(&self) -> f32 {
384 self.0.to_float()
385 }
386}
387
388impl FontMatchDistanceMethod for FontWeight {
389 fn match_distance(&self, range: &(Self, Self)) -> f32 {
401 const NOT_WITHIN_CENTRAL_RANGE: f32 = 100.0;
403 const REVERSE_DISTANCE: f32 = 600.0;
404
405 let min_weight = range.0;
406 let max_weight = range.1;
407
408 if *self >= min_weight && *self <= max_weight {
409 return 0.0;
411 }
412
413 if *self < FontWeight::NORMAL {
414 if max_weight < *self {
416 return self.to_float() - max_weight.to_float();
417 }
418
419 return (min_weight.to_float() - self.to_float()) + REVERSE_DISTANCE;
421 }
422
423 if *self > FontWeight::from_float(500.) {
424 if min_weight > *self {
426 return min_weight.to_float() - self.to_float();
427 }
428 return (self.to_float() - max_weight.to_float()) + REVERSE_DISTANCE;
430 }
431
432 if min_weight > *self {
434 if min_weight <= FontWeight::from_float(500.) {
435 return min_weight.to_float() - self.to_float();
437 }
438 return (min_weight.to_float() - self.to_float()) + REVERSE_DISTANCE;
440 }
441 (self.to_float() - max_weight.to_float()) + NOT_WITHIN_CENTRAL_RANGE
443 }
444
445 fn to_float(&self) -> f32 {
446 self.value()
447 }
448}
449
450impl FontMatchDistanceMethod for FontStyle {
451 fn match_distance(&self, range: &(Self, Self)) -> f32 {
452 let min_style = range.0;
454 if *self == min_style {
455 return 0.0; }
457
458 const REVERSE: f32 = 100.0;
461
462 const NEGATE: f32 = 200.0;
465
466 if *self == FontStyle::NORMAL {
467 if min_style.is_oblique() {
468 let min_angle = min_style.oblique_degrees();
470 if min_angle >= 0.0 {
471 return 1.0 + min_angle;
472 }
473 let max_style = range.1;
474 let max_angle = max_style.oblique_degrees();
475 if max_angle >= 0.0 {
476 return 1.0;
478 }
479 return NEGATE - max_angle;
481 }
482 assert!(min_style == FontStyle::ITALIC);
485 return REVERSE;
486 }
487
488 let default_oblique_angle = FontStyle::OBLIQUE.oblique_degrees();
489 if *self == FontStyle::ITALIC {
490 if min_style.is_oblique() {
491 let min_angle = min_style.oblique_degrees();
492 if min_angle >= default_oblique_angle {
493 return 1.0 + (min_angle - default_oblique_angle);
494 }
495 let max_style = range.1;
496 let max_angle = max_style.oblique_degrees();
497 if max_angle >= default_oblique_angle {
498 return 1.0;
499 }
500 if max_angle > 0.0 {
501 return REVERSE + (default_oblique_angle - max_angle);
503 }
504 return REVERSE + NEGATE + (default_oblique_angle - max_angle);
506 }
507 assert!(min_style == FontStyle::NORMAL);
509 return NEGATE;
510 }
511
512 let target_angle = self.oblique_degrees();
516 if target_angle >= default_oblique_angle {
517 if min_style.is_oblique() {
518 let min_angle = min_style.oblique_degrees();
519 if min_angle >= target_angle {
520 return min_angle - target_angle;
521 }
522 let max_style = range.1;
523 let max_angle = max_style.oblique_degrees();
524 if max_angle >= target_angle {
525 return 0.0;
526 }
527 if max_angle > 0.0 {
528 return REVERSE + (target_angle - max_angle);
529 }
530 return REVERSE + NEGATE + (target_angle - max_angle);
531 }
532 if min_style == FontStyle::ITALIC {
533 return REVERSE + NEGATE;
534 }
535 return REVERSE + NEGATE + 1.0;
536 }
537
538 if target_angle <= -default_oblique_angle {
539 if min_style.is_oblique() {
540 let max_style = range.1;
541 let max_angle = max_style.oblique_degrees();
542 if max_angle <= target_angle {
543 return target_angle - max_angle;
544 }
545 let min_angle = min_style.oblique_degrees();
546 if min_angle <= target_angle {
547 return 0.0;
548 }
549 if min_angle < 0.0 {
550 return REVERSE + (min_angle - target_angle);
551 }
552 return REVERSE + NEGATE + (min_angle - target_angle);
553 }
554 if min_style == FontStyle::ITALIC {
555 return REVERSE + NEGATE;
556 }
557 return REVERSE + NEGATE + 1.0;
558 }
559
560 if target_angle >= 0.0 {
561 if min_style.is_oblique() {
562 let min_angle = min_style.oblique_degrees();
563 if min_angle > target_angle {
564 return REVERSE + (min_angle - target_angle);
565 }
566 let max_style = range.1;
567 let max_angle = max_style.oblique_degrees();
568 if max_angle >= target_angle {
569 return 0.0;
570 }
571 if max_angle > 0.0 {
572 return target_angle - max_angle;
573 }
574 return REVERSE + NEGATE + (target_angle - max_angle);
575 }
576 if min_style == FontStyle::ITALIC {
577 return REVERSE + NEGATE - 2.0;
578 }
579 return REVERSE + NEGATE - 1.0;
580 }
581
582 if min_style.is_oblique() {
584 let max_style = range.1;
585 let max_angle = max_style.oblique_degrees();
586 if max_angle < target_angle {
587 return REVERSE + (target_angle - max_angle);
588 }
589 let min_angle = min_style.oblique_degrees();
590 if min_angle <= target_angle {
591 return 0.0;
592 }
593 if min_angle < 0.0 {
594 return min_angle - target_angle;
595 }
596 return REVERSE + NEGATE + (min_angle - target_angle);
597 }
598 if min_style == FontStyle::ITALIC {
599 return REVERSE + NEGATE - 2.0;
600 }
601 REVERSE + NEGATE - 1.0
602 }
603
604 fn to_float(&self) -> f32 {
605 unimplemented!("Don't know how to convert FontStyle to float.");
606 }
607}
608
609pub trait IsOblique {
610 fn is_oblique(&self) -> bool;
611}
612
613impl IsOblique for FontStyle {
614 fn is_oblique(&self) -> bool {
615 *self != FontStyle::NORMAL && *self != FontStyle::ITALIC
616 }
617}