1use ttf_parser::Face;
4
5use crate::dim::Dim;
6use crate::error::{Error, FontError};
7
8pub static STIX_TWO_MATH_OTF: &[u8] =
14 include_bytes!("../../fonts/stix-two-math/STIXTwoMath-Regular.otf");
15
16pub const STIX_TWO_MATH_SHA256: &str =
18 "f2076b9f1676438439dd41e23676f5ab99056e83d6b8f8c27841591ef2ccfa72";
19
20pub const STIX_TWO_MATH_NAME: &str = "STIX Two Math";
22
23#[derive(Clone, Debug, PartialEq, Eq)]
36pub struct GlyphMetrics {
37 pub ch: char,
39 pub glyph_id: u16,
41 pub advance_fu: u16,
43 pub advance: Dim,
45 pub height: Dim,
47 pub depth: Dim,
49}
50
51pub struct MathFont {
62 raw: &'static [u8],
63 face: Face<'static>,
64 units_per_em: u16,
65 ascender_fu: i16,
66 descender_fu: i16,
67}
68
69impl MathFont {
70 pub fn stix_two_math() -> Result<Self, Error> {
83 Self::from_bytes(STIX_TWO_MATH_OTF)
84 }
85
86 pub fn from_bytes(raw: &'static [u8]) -> Result<Self, Error> {
89 let face = Face::parse(raw, 0).map_err(|_| FontError::InvalidFace)?;
90 let units_per_em = face.units_per_em();
91 if units_per_em == 0 {
92 return Err(FontError::InvalidFace.into());
93 }
94 let ascender_fu = face.ascender();
95 let descender_fu = face.descender();
96 Ok(Self {
97 raw,
98 face,
99 units_per_em,
100 ascender_fu,
101 descender_fu,
102 })
103 }
104
105 #[must_use]
126 pub fn face(&self) -> &Face<'static> {
127 &self.face
128 }
129
130 #[must_use]
132 pub fn bytes(&self) -> &'static [u8] {
133 self.raw
134 }
135
136 #[must_use]
138 pub fn units_per_em(&self) -> u16 {
139 self.units_per_em
140 }
141
142 #[must_use]
144 pub fn ascender_fu(&self) -> i16 {
145 self.ascender_fu
146 }
147
148 #[must_use]
150 pub fn descender_fu(&self) -> i16 {
151 self.descender_fu
152 }
153
154 #[must_use]
156 pub fn ascender(&self) -> Dim {
157 Dim::from_font_units(i64::from(self.ascender_fu), self.units_per_em)
158 }
159
160 #[must_use]
162 pub fn descender(&self) -> Dim {
163 let d = i64::from(self.descender_fu);
164 Dim::from_font_units(-d, self.units_per_em)
165 }
166
167 pub fn glyph(&self, ch: char) -> Result<GlyphMetrics, Error> {
169 let face = self.face();
170 let gid = face.glyph_index(ch).ok_or(FontError::MissingGlyph { ch })?;
171 let advance_fu = face
172 .glyph_hor_advance(gid)
173 .ok_or(FontError::MissingGlyph { ch })?;
174 let mut height_fu = 0i64;
175 let mut depth_fu = 0i64;
176 if let Some(bbox) = face.glyph_bounding_box(gid) {
177 height_fu = i64::from(bbox.y_max).max(0);
178 depth_fu = i64::from(-bbox.y_min).max(0);
179 }
180 let upem = self.units_per_em;
181 Ok(GlyphMetrics {
182 ch,
183 glyph_id: gid.0,
184 advance_fu,
185 advance: Dim::from_font_units(i64::from(advance_fu), upem),
186 height: Dim::from_font_units(height_fu, upem),
187 depth: Dim::from_font_units(depth_fu, upem),
188 })
189 }
190
191 pub fn glyph_id(&self, ch: char, gid: u16) -> Result<GlyphMetrics, Error> {
193 let face = self.face();
194 let gid = ttf_parser::GlyphId(gid);
195 let advance_fu = face
196 .glyph_hor_advance(gid)
197 .ok_or(FontError::MissingGlyph { ch })?;
198 let mut height_fu = 0i64;
199 let mut depth_fu = 0i64;
200 if let Some(bbox) = face.glyph_bounding_box(gid) {
201 height_fu = i64::from(bbox.y_max).max(0);
202 depth_fu = i64::from(-bbox.y_min).max(0);
203 }
204 let upem = self.units_per_em;
205 Ok(GlyphMetrics {
206 ch,
207 glyph_id: gid.0,
208 advance_fu,
209 advance: Dim::from_font_units(i64::from(advance_fu), upem),
210 height: Dim::from_font_units(height_fu, upem),
211 depth: Dim::from_font_units(depth_fu, upem),
212 })
213 }
214
215 pub fn italic_correction(&self, glyph_id: u16) -> Dim {
217 let face = self.face();
218 let Some(math) = face.tables().math else {
219 return Dim::zero();
220 };
221 let Some(info) = math.glyph_info else {
222 return Dim::zero();
223 };
224 let Some(table) = info.italic_corrections else {
225 return Dim::zero();
226 };
227 match table.get(ttf_parser::GlyphId(glyph_id)) {
228 Some(v) => Dim::from_font_units(i64::from(v.value), self.units_per_em),
229 None => Dim::zero(),
230 }
231 }
232
233 pub fn top_accent_attachment(&self, glyph_id: u16) -> Option<Dim> {
235 let face = self.face();
236 let math = face.tables().math?;
237 let info = math.glyph_info?;
238 let table = info.top_accent_attachments?;
239 let v = table.get(ttf_parser::GlyphId(glyph_id))?;
240 Some(Dim::from_font_units(i64::from(v.value), self.units_per_em))
241 }
242
243 pub fn horizontal_assembly_parts(&self, glyph_id: u16) -> Vec<(u16, u16, u16, u16, bool)> {
246 let mut out = Vec::new();
247 let face = self.face();
248 let Some(math) = face.tables().math else {
249 return out;
250 };
251 let Some(variants) = math.variants else {
252 return out;
253 };
254 let Some(cons) = variants
255 .horizontal_constructions
256 .get(ttf_parser::GlyphId(glyph_id))
257 else {
258 return out;
259 };
260 let Some(assembly) = cons.assembly else {
261 return out;
262 };
263 for i in 0..assembly.parts.len() {
264 if let Some(p) = assembly.parts.get(i) {
265 out.push((
266 p.glyph_id.0,
267 p.start_connector_length,
268 p.end_connector_length,
269 p.full_advance,
270 p.part_flags.extender(),
271 ));
272 }
273 }
274 out
275 }
276
277 pub fn horizontal_variants(&self, glyph_id: u16) -> Vec<u16> {
279 let mut out = vec![glyph_id];
280 let face = self.face();
281 let Some(math) = face.tables().math else {
282 return out;
283 };
284 let Some(variants) = math.variants else {
285 return out;
286 };
287 let Some(cons) = variants
288 .horizontal_constructions
289 .get(ttf_parser::GlyphId(glyph_id))
290 else {
291 return out;
292 };
293 for i in 0..cons.variants.len() {
294 if let Some(v) = cons.variants.get(i) {
295 out.push(v.variant_glyph.0);
296 }
297 }
298 out
299 }
300
301 pub fn vertical_variants(&self, glyph_id: u16) -> Vec<u16> {
303 let mut out = vec![glyph_id];
304 let face = self.face();
305 let Some(math) = face.tables().math else {
306 return out;
307 };
308 let Some(variants) = math.variants else {
309 return out;
310 };
311 let Some(cons) = variants
312 .vertical_constructions
313 .get(ttf_parser::GlyphId(glyph_id))
314 else {
315 return out;
316 };
317 for i in 0..cons.variants.len() {
318 if let Some(v) = cons.variants.get(i) {
319 out.push(v.variant_glyph.0);
320 }
321 }
322 out
323 }
324
325 #[must_use]
327 pub fn sha256_hex(bytes: &[u8]) -> String {
328 let d = crate::hash::sha256(bytes);
329 let mut s = String::with_capacity(64);
330 for b in d {
331 s.push_str(&hex_byte(b));
332 }
333 s
334 }
335}
336
337fn hex_byte(b: u8) -> String {
338 const H: &[u8; 16] = b"0123456789abcdef";
339 let hi = H[(b >> 4) as usize];
340 let lo = H[(b & 0xf) as usize];
341 let mut out = String::with_capacity(2);
342 out.push(hi as char);
343 out.push(lo as char);
344 out
345}