verglas/forge/font/
table.rs

1use font_types::{Fixed, LongDateTime};
2use write_fonts::{
3    tables::{
4        cmap::Cmap,
5        glyf::{Bbox, GlyfLocaBuilder, Glyph, SimpleGlyph},
6        head::Head,
7        hhea::Hhea,
8        hmtx::{Hmtx, LongMetric},
9        maxp::Maxp,
10        post::Post,
11    },
12    types::GlyphId,
13    FontBuilder,
14};
15
16mod name;
17
18use self::name::name;
19use super::{glyph::BboxMetrics, ADVANCE};
20use crate::Error;
21
22pub fn add_font_tables(
23    font: &mut FontBuilder,
24    font_name: &str,
25    named_glyphs: &[(String, SimpleGlyph)],
26) -> Result<(), Error> {
27    let num_glyphs = (named_glyphs.len() + 1) as u16; // +1 for .notdef glyph
28    let bbox = named_glyphs
29        .iter()
30        .fold(Bbox::default(), |bbox, (_, glyph)| bbox.union(glyph.bbox));
31    let (x_min, y_min, x_max, y_max) = bbox.bounds();
32
33    // Add name table
34    let name_table = name(font_name);
35    font.add_table(&name_table)?;
36
37    // Add head table
38    let head_table = head(x_min, y_min, x_max, y_max);
39    font.add_table(&head_table)?;
40
41    // Add hhea table
42    let hhea = hhea(num_glyphs);
43    font.add_table(&hhea)?;
44
45    // Add maxp table with glyph count
46    let maxp = maxp(num_glyphs);
47    font.add_table(&maxp)?;
48
49    // Add cmap table for Unicode mapping
50    let mappings = named_glyphs.iter().enumerate().map(|(i, _)| {
51        let unicode = char::from_u32(i as u32 + 0xE000).unwrap();
52        (unicode, GlyphId::new(((i + 1) as u16).into()))
53    });
54    let cmap = Cmap::from_mappings(mappings).unwrap();
55    font.add_table(&cmap)?;
56
57    // Add glyph data
58    let mut glyf_builder = GlyfLocaBuilder::new();
59
60    // Add .notdef glyph
61    glyf_builder.add_glyph(&Glyph::Empty)?;
62    let mut glyph_order = vec![".notdef"];
63    let mut h_metrics = vec![LongMetric::new(ADVANCE, 1000)];
64    let mut left_side_bearings = vec![0];
65
66    // Add actual glyphs
67    for (name, glyph) in named_glyphs {
68        glyf_builder.add_glyph(glyph)?;
69        let side_bearing = (ADVANCE.saturating_sub(glyph.bbox.width()) / 2) as i16;
70        h_metrics.push(LongMetric::new(ADVANCE, side_bearing));
71        left_side_bearings.push(side_bearing);
72        glyph_order.push(name.as_str());
73    }
74
75    // Add hmtx table
76    let hmtx = Hmtx::new(h_metrics, left_side_bearings);
77    font.add_table(&hmtx)?;
78
79    // Add glyf and loca table
80    let (glyf, loca, _loca_format) = glyf_builder.build();
81    font.add_table(&glyf)?;
82    font.add_table(&loca)?;
83
84    // Add post table
85    let post = Post::new_v2(glyph_order);
86    font.add_table(&post)?;
87
88    Ok(())
89}
90
91pub fn head(x_min: i16, y_min: i16, x_max: i16, y_max: i16) -> Head {
92    Head {
93        font_revision: Fixed::from_i32(1),
94        created: LongDateTime::new(0),
95        modified: LongDateTime::new(0),
96        units_per_em: 1000,
97        x_min,
98        y_min,
99        x_max,
100        y_max,
101        index_to_loc_format: 0, // Short format
102        ..Head::default()
103    }
104}
105
106pub fn hhea(number_of_long_metrics: u16) -> Hhea {
107    Hhea {
108        ascender: (ADVANCE as i16).into(),
109        descender: 0.into(),
110        line_gap: 0.into(),
111        advance_width_max: ADVANCE.into(),
112        number_of_long_metrics,
113        ..Hhea::default()
114    }
115}
116
117pub fn maxp(num_glyphs: u16) -> Maxp {
118    Maxp {
119        num_glyphs,
120        ..Maxp::default()
121    }
122}