1use crate::setting::parse_location;
2use read_fonts::{types::NameId, FontRef, ReadError, TableProvider};
3use skrifa::{instance::Location, setting::VariationSetting, MetadataProvider};
4use std::{
5 borrow::Cow,
6 collections::{HashMap, HashSet},
7};
8use ttj::monkeypatching::DenormalizeLocation;
9use ucd::Codepoint;
10
11#[derive(Debug, Clone)]
13pub struct DFont {
14 pub backing: Vec<u8>,
16 pub location: Vec<VariationSetting>,
18 pub normalized_location: Location,
20 pub codepoints: HashSet<u32>,
22}
23
24impl DFont {
25 pub fn new(string: &[u8]) -> Self {
27 let backing: Vec<u8> = string.to_vec();
28
29 let mut fnt = DFont {
30 backing,
31 codepoints: HashSet::new(),
32 normalized_location: Location::default(),
33 location: vec![],
34 };
35 let cmap = fnt.fontref().charmap();
36 fnt.codepoints = cmap.mappings().map(|(cp, _)| cp).collect();
37 fnt
38 }
39
40 pub fn normalize_location(&mut self) {
45 self.normalized_location = self.fontref().axes().location(&self.location);
46 }
47
48 pub fn set_location(&mut self, variations: &str) -> Result<(), String> {
50 self.location = parse_location(variations)?;
51 self.normalize_location();
52 Ok(())
53 }
54
55 pub fn instances(&self) -> Vec<String> {
57 self.fontref()
58 .named_instances()
59 .iter()
60 .flat_map(|ni| {
61 self.fontref()
62 .localized_strings(ni.subfamily_name_id())
63 .english_or_first()
64 })
65 .map(|s| s.to_string())
66 .collect()
67 }
68
69 pub fn set_instance(&mut self, instance: &str) -> Result<(), String> {
71 let instance = self
72 .fontref()
73 .named_instances()
74 .iter()
75 .find(|ni| {
76 self.fontref()
77 .localized_strings(ni.subfamily_name_id())
78 .any(|s| instance == s.chars().collect::<Cow<str>>())
79 })
80 .ok_or_else(|| format!("No instance named {}", instance))?;
81 let user_coords = instance.user_coords();
82 let location = instance.location();
83 self.location = self
84 .fontref()
85 .axes()
86 .iter()
87 .zip(user_coords)
88 .map(|(a, v)| (a.tag(), v).into())
89 .collect();
90 self.normalized_location = location;
91 Ok(())
92 }
93
94 pub fn fontref(&self) -> FontRef<'_> {
95 FontRef::new(&self.backing).expect("Couldn't parse font")
96 }
97 pub fn family_name(&self) -> String {
98 self.fontref()
99 .localized_strings(NameId::FAMILY_NAME)
100 .english_or_first()
101 .map_or_else(|| "Unknown".to_string(), |s| s.chars().collect())
102 }
103
104 pub fn style_name(&self) -> String {
105 self.fontref()
106 .localized_strings(NameId::SUBFAMILY_NAME)
107 .english_or_first()
108 .map_or_else(|| "Regular".to_string(), |s| s.chars().collect())
109 }
110
111 pub fn axis_info(&self) -> HashMap<String, (f32, f32, f32)> {
115 self.fontref()
116 .axes()
117 .iter()
118 .map(|axis| {
119 (
120 axis.tag().to_string(),
121 (axis.min_value(), axis.default_value(), axis.max_value()),
122 )
123 })
124 .collect()
125 }
126
127 pub fn supported_scripts(&self) -> HashSet<String> {
130 let cmap = self.fontref().charmap();
131 let mut strings = HashSet::new();
132 for (codepoint, _glyphid) in cmap.mappings() {
133 if let Some(script) = char::from_u32(codepoint).and_then(|c| c.script()) {
134 strings.insert(format!("{:?}", script));
136 }
137 }
138 strings
139 }
140
141 pub fn masters(&self) -> Result<Vec<Vec<VariationSetting>>, ReadError> {
146 let gvar = self.fontref().gvar()?;
147 let tuples = gvar.shared_tuples()?.tuples();
148 let peaks: Vec<Vec<VariationSetting>> = tuples
149 .iter()
150 .flatten()
151 .flat_map(|tuple| {
152 let location = tuple
153 .values()
154 .iter()
155 .map(|x| x.get().to_f32())
156 .collect::<Vec<f32>>();
157 self.fontref().denormalize_location(&location)
158 })
159 .collect();
160 Ok(peaks)
161 }
162}
163
164type InstancePositions = Vec<(String, HashMap<String, f32>)>;
165type AxisDescription = HashMap<String, (f32, f32, f32)>;
166
167pub fn shared_axes(f_a: &DFont, f_b: &DFont) -> (AxisDescription, InstancePositions) {
169 let mut axes = f_a.axis_info();
170 let b_axes = f_b.axis_info();
171 let a_axes_names: Vec<String> = axes.keys().cloned().collect();
172 for axis_tag in a_axes_names.iter() {
173 if !b_axes.contains_key(axis_tag) {
174 axes.remove(axis_tag);
175 }
176 }
177 for (axis_tag, values) in b_axes.iter() {
178 let (our_min, _our_default, our_max) = values;
179 axes.entry(axis_tag.clone())
180 .and_modify(|(their_min, _their_default, their_max)| {
181 *their_min = their_min.max(*our_min);
185 *their_max = their_max.min(*our_max);
186 });
187 }
188 let axis_names: Vec<String> = f_a
189 .fontref()
190 .axes()
191 .iter()
192 .map(|axis| axis.tag().to_string())
193 .collect();
194 let instances = f_a
195 .fontref()
196 .named_instances()
197 .iter()
198 .map(|ni| {
199 let name = f_a
200 .fontref()
201 .localized_strings(ni.subfamily_name_id())
202 .english_or_first()
203 .map_or_else(|| "Unknown".to_string(), |s| s.chars().collect());
204 let location_map = axis_names.iter().cloned().zip(ni.user_coords()).collect();
205 (name, location_map)
206 })
207 .collect::<Vec<(String, HashMap<String, f32>)>>();
208 (axes, instances)
209}