fret_render_text/
font_stack.rs1use 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}