we (web engine): Experimental web browser project to understand the limits of Claude
1//! `hmtx` — Horizontal Metrics table.
2//!
3//! Contains per-glyph horizontal metrics (advance width + left side bearing).
4//! Reference: <https://learn.microsoft.com/en-us/typography/opentype/spec/hmtx>
5
6use crate::font::parse::Reader;
7use crate::font::FontError;
8
9/// Parsed `hmtx` table.
10///
11/// Both `advances` and `lsbs` are indexed by glyph ID and have exactly
12/// `num_glyphs` entries.
13#[derive(Debug)]
14pub struct HmtxTable {
15 /// Advance widths for each glyph (in font units).
16 pub advances: Vec<u16>,
17 /// Left side bearings for each glyph (in font units).
18 pub lsbs: Vec<i16>,
19}
20
21impl HmtxTable {
22 /// Parse the `hmtx` table from raw bytes.
23 ///
24 /// `num_long_hor_metrics` comes from `hhea`, `num_glyphs` from `maxp`.
25 pub fn parse(
26 data: &[u8],
27 num_long_hor_metrics: u16,
28 num_glyphs: u16,
29 ) -> Result<HmtxTable, FontError> {
30 let r = Reader::new(data);
31 let n_long = num_long_hor_metrics as usize;
32 let n_glyphs = num_glyphs as usize;
33
34 let mut advances = Vec::with_capacity(n_glyphs);
35 let mut lsbs = Vec::with_capacity(n_glyphs);
36
37 // First n_long entries are (advance_width: u16, lsb: i16) pairs.
38 for i in 0..n_long {
39 let offset = i * 4;
40 advances.push(r.u16(offset)?);
41 lsbs.push(r.i16(offset + 2)?);
42 }
43
44 // Remaining glyphs share the last advance width, but have individual lsbs.
45 let last_advance = advances.last().copied().unwrap_or(0);
46 let remaining = n_glyphs.saturating_sub(n_long);
47 let lsb_offset = n_long * 4;
48 for i in 0..remaining {
49 advances.push(last_advance);
50 lsbs.push(r.i16(lsb_offset + i * 2)?);
51 }
52
53 Ok(HmtxTable { advances, lsbs })
54 }
55}