Skip to main content

fret_render_text/
font_stack.rs

1use crate::{fallback_policy, parley_shaper::ParleyShaper};
2use parley::fontique::FamilyId as ParleyFamilyId;
3use parley::fontique::GenericFamily as ParleyGenericFamily;
4use std::collections::HashMap;
5
6#[derive(Debug, Default, Clone)]
7pub struct GenericFamilyInjectionState {
8    injected_by_generic: HashMap<ParleyGenericFamily, Vec<ParleyFamilyId>>,
9}
10
11impl GenericFamilyInjectionState {
12    pub fn clear(&mut self) {
13        self.injected_by_generic.clear();
14    }
15
16    pub(crate) fn apply_generic_stack(
17        &mut self,
18        shaper: &mut ParleyShaper,
19        generic: ParleyGenericFamily,
20        primary: Option<ParleyFamilyId>,
21        fallbacks: &[ParleyFamilyId],
22    ) -> bool {
23        let mut injected: Vec<ParleyFamilyId> = Vec::new();
24        if let Some(id) = primary {
25            injected.push(id);
26        }
27        for &id in fallbacks {
28            if !injected.contains(&id) {
29                injected.push(id);
30            }
31        }
32
33        let prev_injected = self
34            .injected_by_generic
35            .get(&generic)
36            .cloned()
37            .unwrap_or_default();
38
39        let mut base = shaper.generic_family_ids(generic);
40        if !prev_injected.is_empty() {
41            base.retain(|id| !prev_injected.contains(id));
42        }
43
44        let mut next: Vec<ParleyFamilyId> = Vec::new();
45        next.extend_from_slice(&injected);
46        for id in base {
47            if !next.contains(&id) {
48                next.push(id);
49            }
50        }
51
52        self.injected_by_generic.insert(generic, injected);
53        shaper.set_generic_family_ids(generic, &next)
54    }
55}
56
57pub(crate) fn pick_primary_family_id(
58    shaper: &mut ParleyShaper,
59    overrides: &[String],
60    defaults: &'static [&'static str],
61) -> Option<ParleyFamilyId> {
62    for candidate in overrides {
63        if let Some(id) = shaper.resolve_family_id(candidate) {
64            return Some(id);
65        }
66    }
67    fallback_policy::first_available_family_id(shaper, defaults)
68}
69
70pub(crate) fn resolve_common_fallback_ids_and_suffix(
71    shaper: &mut ParleyShaper,
72    candidates: &[String],
73) -> (Vec<ParleyFamilyId>, String) {
74    let mut resolved_suffix: Vec<String> = Vec::new();
75    let mut fallback_ids: Vec<ParleyFamilyId> = Vec::new();
76    let max = fallback_policy::common_fallback_stack_suffix_max_families();
77
78    for family in candidates {
79        if let Some(id) = shaper.resolve_family_id(family) {
80            let pushed = if !fallback_ids.contains(&id) {
81                fallback_ids.push(id);
82                true
83            } else {
84                false
85            };
86            if pushed && resolved_suffix.len() < max {
87                resolved_suffix.push(family.clone());
88            }
89        }
90    }
91
92    (fallback_ids, resolved_suffix.join(", "))
93}
94
95pub fn apply_font_families_inner(
96    shaper: &mut ParleyShaper,
97    policy: &mut fallback_policy::TextFallbackPolicyV1,
98    injections: &mut GenericFamilyInjectionState,
99    config: &fret_core::TextFontFamilyConfig,
100) -> (bool, bool, bool) {
101    let prev_generic_mode = policy.generic_common_fallback_mode();
102    let prev_named_mode = policy.common_fallback_mode();
103
104    policy.set_font_family_config(config.clone());
105    policy.refresh_derived(shaper);
106    let mode_changed = policy.generic_common_fallback_mode() != prev_generic_mode
107        || policy.common_fallback_mode() != prev_named_mode;
108
109    let sans = pick_primary_family_id(
110        shaper,
111        &config.ui_sans,
112        fallback_policy::default_sans_candidates(shaper),
113    );
114    let serif = pick_primary_family_id(
115        shaper,
116        &config.ui_serif,
117        fallback_policy::default_serif_candidates(shaper),
118    );
119    let mono = pick_primary_family_id(
120        shaper,
121        &config.ui_mono,
122        fallback_policy::default_monospace_candidates(shaper),
123    );
124
125    let (fallback_ids, suffix) = if policy.prefer_common_fallback_for_generics() {
126        resolve_common_fallback_ids_and_suffix(shaper, policy.common_fallback_candidates())
127    } else {
128        (Vec::new(), String::new())
129    };
130
131    policy.set_common_fallback_stack_suffix(if policy.prefer_common_fallback() {
132        suffix
133    } else {
134        String::new()
135    });
136    let suffix_changed =
137        shaper.set_common_fallback_stack_suffix(policy.common_fallback_stack_suffix().to_string());
138
139    let mut changed = false;
140    changed |=
141        injections.apply_generic_stack(shaper, ParleyGenericFamily::SansSerif, sans, &fallback_ids);
142    changed |=
143        injections.apply_generic_stack(shaper, ParleyGenericFamily::SystemUi, sans, &fallback_ids);
144    changed |= injections.apply_generic_stack(
145        shaper,
146        ParleyGenericFamily::UiSansSerif,
147        sans,
148        &fallback_ids,
149    );
150    changed |=
151        injections.apply_generic_stack(shaper, ParleyGenericFamily::Serif, serif, &fallback_ids);
152    changed |=
153        injections.apply_generic_stack(shaper, ParleyGenericFamily::UiSerif, serif, &fallback_ids);
154    changed |=
155        injections.apply_generic_stack(shaper, ParleyGenericFamily::Monospace, mono, &fallback_ids);
156    changed |= injections.apply_generic_stack(
157        shaper,
158        ParleyGenericFamily::UiMonospace,
159        mono,
160        &fallback_ids,
161    );
162    changed |=
163        injections.apply_generic_stack(shaper, ParleyGenericFamily::Emoji, None, &fallback_ids);
164
165    (changed, suffix_changed, mode_changed)
166}