Skip to main content

harfrust/hb/
glyph_metrics.rs

1use crate::{
2    hb::{
3        face::{BasicFontMetrics, Scale},
4        tables::TableRanges,
5    },
6    GlyphExtents, GlyphInfo, GlyphPosition, Tag,
7};
8use read_fonts::{
9    tables::{
10        glyf::Glyf,
11        gvar::Gvar,
12        hmtx::{Hmtx, LongMetric},
13        hvar::Hvar,
14        loca::Loca,
15        mvar::Mvar,
16        vmtx::Vmtx,
17        vorg::Vorg,
18        vvar::Vvar,
19    },
20    types::{BoundingBox, F2Dot14, Fixed, GlyphId, Point},
21    FontRef, TableProvider,
22};
23
24#[derive(Clone)]
25pub struct GlyphMetrics<'a> {
26    _hmtx: Option<Hmtx<'a>>,
27    h_metrics: &'a [LongMetric],
28    hvar: Option<Hvar<'a>>,
29    vmtx: Option<Vmtx<'a>>,
30    vvar: Option<Vvar<'a>>,
31    vorg: Option<Vorg<'a>>,
32    glyf: Option<GlyfTables<'a>>,
33    mvar: Option<Mvar<'a>>,
34    num_glyphs: u32,
35    upem: u16,
36    ascent: i16,
37    descent: i16,
38}
39
40#[derive(Clone)]
41struct GlyfTables<'a> {
42    loca: Loca<'a>,
43    glyf: Glyf<'a>,
44    gvar: Option<Gvar<'a>>,
45}
46
47impl<'a> GlyphMetrics<'a> {
48    pub(crate) fn new(font: &FontRef<'a>, table_ranges: &TableRanges) -> Self {
49        let num_glyphs = table_ranges.num_glyphs;
50        let upem = table_ranges.units_per_em;
51        let hmtx = table_ranges
52            .hmtx
53            .resolve_data(font)
54            .and_then(|data| Hmtx::read(data, table_ranges.num_h_metrics).ok());
55        let h_metrics = hmtx
56            .as_ref()
57            .map(|hmtx| hmtx.h_metrics())
58            .unwrap_or_default();
59        let hvar = table_ranges.hvar.resolve_table(font);
60        let vmtx = table_ranges
61            .vmtx
62            .resolve_data(font)
63            .and_then(|data| Vmtx::read(data, table_ranges.num_v_metrics).ok());
64        let vvar = table_ranges.vvar.resolve_table(font);
65        let vorg = table_ranges.vorg.resolve_table(font);
66        let loca = table_ranges
67            .loca
68            .resolve_data(font)
69            .and_then(|data| Loca::read(data, table_ranges.loca_long).ok());
70        let glyf = table_ranges.glyf.resolve_table(font);
71        let glyf = if let Some((loca, glyf)) = loca.zip(glyf) {
72            let gvar = table_ranges.gvar.resolve_table(font);
73            Some(GlyfTables { loca, glyf, gvar })
74        } else {
75            None
76        };
77        let mvar = table_ranges.mvar.resolve_table(font);
78        let ascent = table_ranges.ascent;
79        let descent = table_ranges.descent;
80        Self {
81            _hmtx: hmtx,
82            h_metrics,
83            hvar,
84            vmtx,
85            vvar,
86            vorg,
87            glyf,
88            mvar,
89            num_glyphs,
90            upem,
91            ascent,
92            descent,
93        }
94    }
95
96    pub(crate) fn from_tables(font: &impl TableProvider<'a>, metrics: &BasicFontMetrics) -> Self {
97        let hmtx = font.hmtx().ok();
98        let h_metrics = hmtx
99            .as_ref()
100            .map(|hmtx| hmtx.h_metrics())
101            .unwrap_or_default();
102        let hvar = font.hvar().ok();
103        let vmtx = font.vmtx().ok();
104        let vvar = font.vvar().ok();
105        let vorg = font.vorg().ok();
106        let loca = font.loca(None).ok();
107        let glyf = font.glyf().ok();
108        let glyf = if let Some((loca, glyf)) = loca.zip(glyf) {
109            let gvar = font.gvar().ok();
110            Some(GlyfTables { loca, glyf, gvar })
111        } else {
112            None
113        };
114        let mvar = font.mvar().ok();
115        Self {
116            _hmtx: hmtx,
117            h_metrics,
118            hvar,
119            vmtx,
120            vvar,
121            vorg,
122            glyf,
123            mvar,
124            num_glyphs: metrics.num_glyphs,
125            upem: metrics.units_per_em,
126            ascent: metrics.ascent,
127            descent: metrics.descent,
128        }
129    }
130
131    pub(crate) fn advance_width(&self, gid: impl Into<GlyphId>, coords: &[F2Dot14]) -> Option<i32> {
132        let gid = gid.into();
133        let Some(mut advance) = self
134            .h_metrics
135            .get(gid.to_u32() as usize)
136            .or_else(|| self.h_metrics.last())
137            .map(|metric| metric.advance() as i32)
138        else {
139            return (gid.to_u32() < self.num_glyphs).then_some(self.upem as i32 / 2);
140        };
141        if !coords.is_empty() {
142            if let Some(hvar) = self.hvar.as_ref() {
143                advance += hvar
144                    .advance_width_delta(gid, coords)
145                    .unwrap_or_default()
146                    .to_i32();
147            } else if let Some(deltas) = self.phantom_deltas(gid, coords) {
148                advance += deltas[1].x.to_i32() - deltas[0].x.to_i32();
149            }
150        }
151        Some(advance)
152    }
153
154    pub(crate) fn populate_advance_widths(
155        &self,
156        infos: &[GlyphInfo],
157        pos: &mut [GlyphPosition],
158        coords: &[F2Dot14],
159        scale: Scale,
160    ) {
161        for (info, pos) in infos.iter().zip(pos.iter_mut()) {
162            pos.x_advance = self
163                .h_metrics
164                .get(info.glyph_id as usize)
165                .or_else(|| self.h_metrics.last())
166                .map(|metric| metric.advance() as i32)
167                .or_else(|| (info.glyph_id < self.num_glyphs).then_some(self.upem as i32 / 2))
168                .unwrap_or_default();
169        }
170        if !coords.is_empty() {
171            if let Some(hvar) = self.hvar.as_ref() {
172                for (info, pos) in infos.iter().zip(pos.iter_mut()) {
173                    pos.x_advance += hvar
174                        .advance_width_delta(info.as_glyph(), coords)
175                        .unwrap_or_default()
176                        .to_i32();
177                }
178            } else {
179                for (info, pos) in infos.iter().zip(pos.iter_mut()) {
180                    if let Some(deltas) = self.phantom_deltas(info.as_glyph(), coords) {
181                        pos.x_advance += deltas[1].x.to_i32() - deltas[0].x.to_i32();
182                    }
183                }
184            }
185        }
186        for pos in pos.iter_mut() {
187            pos.x_advance = scale.scale_x(pos.x_advance);
188        }
189    }
190
191    pub(crate) fn _left_side_bearing(
192        &self,
193        gid: impl Into<GlyphId>,
194        coords: &[F2Dot14],
195    ) -> Option<i32> {
196        let gid = gid.into();
197        let mut bearing = if let Some(hmtx) = self._hmtx.as_ref() {
198            hmtx.side_bearing(gid).unwrap_or_default() as i32
199        } else if let Some(extents) = self.bounds(gid, coords) {
200            return Some(extents.x_min);
201        } else {
202            return None;
203        };
204        if !coords.is_empty() {
205            if let Some(hvar) = self.hvar.as_ref() {
206                bearing += hvar.lsb_delta(gid, coords).unwrap_or_default().to_i32();
207            } else if let Some(deltas) = self.phantom_deltas(gid, coords) {
208                bearing += deltas[0].x.to_i32();
209            }
210        }
211        Some(bearing)
212    }
213
214    pub(crate) fn advance_height(
215        &self,
216        gid: impl Into<GlyphId>,
217        coords: &[F2Dot14],
218    ) -> Option<i32> {
219        let gid = gid.into();
220        let Some(mut advance) = self
221            .vmtx
222            .as_ref()
223            .and_then(|vmtx| vmtx.advance(gid))
224            .map(|advance| advance as i32)
225        else {
226            return Some(self.ascent as i32 - self.descent as i32);
227        };
228        if !coords.is_empty() {
229            if let Some(vvar) = self.vvar.as_ref() {
230                advance += vvar
231                    .advance_height_delta(gid, coords)
232                    .unwrap_or_default()
233                    .to_i32();
234            } else if let Some(deltas) = self.phantom_deltas(gid, coords) {
235                advance += deltas[3].y.to_i32() - deltas[2].y.to_i32();
236            }
237        }
238        Some(advance)
239    }
240
241    pub(crate) fn top_side_bearing(
242        &self,
243        gid: impl Into<GlyphId>,
244        coords: &[F2Dot14],
245    ) -> Option<i32> {
246        let gid = gid.into();
247        let mut bearing = if let Some(vmtx) = self.vmtx.as_ref() {
248            vmtx.side_bearing(gid).unwrap_or_default() as i32
249        } else {
250            return None;
251        };
252        if !coords.is_empty() {
253            if let Some(vvar) = self.vvar.as_ref() {
254                bearing += vvar.tsb_delta(gid, coords).unwrap_or_default().to_i32();
255            } else if let Some(deltas) = self.phantom_deltas(gid, coords) {
256                bearing += deltas[3].y.to_i32();
257            }
258        }
259        Some(bearing)
260    }
261
262    pub(crate) fn v_origin(&self, gid: impl Into<GlyphId>, coords: &[F2Dot14]) -> Option<i32> {
263        let gid = gid.into();
264        let origin = if let Some(vorg) = self.vorg.as_ref() {
265            let mut origin = vorg.vertical_origin_y(gid) as i32;
266            if !coords.is_empty() {
267                if let Some(vvar) = self.vvar.as_ref() {
268                    origin += vvar.v_org_delta(gid, coords).unwrap_or_default().to_i32();
269                }
270            }
271            origin
272        } else if let Some(extents) = self.bounds(gid, coords) {
273            let origin = if self.vmtx.is_some() {
274                let mut origin = Some(extents.y_max);
275                let tsb = self.top_side_bearing(gid, coords);
276                if let Some(tsb) = tsb {
277                    origin = Some(origin.unwrap() + tsb);
278                } else {
279                    origin = None;
280                }
281                if origin.is_some() && !coords.is_empty() {
282                    if let Some(vvar) = self.vvar.as_ref() {
283                        origin = Some(
284                            origin.unwrap()
285                                + vvar.v_org_delta(gid, coords).unwrap_or_default().to_i32(),
286                        );
287                    }
288                }
289                origin
290            } else {
291                None
292            };
293
294            if let Some(origin) = origin {
295                origin
296            } else {
297                let mut advance = self.ascent as i32 - self.descent as i32;
298                if let Some(mvar) = self.mvar.as_ref() {
299                    advance += mvar
300                        .metric_delta(Tag::new(b"hasc"), coords)
301                        .unwrap_or_default()
302                        .to_i32()
303                        - mvar
304                            .metric_delta(Tag::new(b"hdsc"), coords)
305                            .unwrap_or_default()
306                            .to_i32();
307                }
308                let diff = advance - (extents.y_max - extents.y_min);
309                extents.y_max + (diff >> 1)
310            }
311        } else {
312            let mut ascent = self.ascent as i32;
313            if let Some(mvar) = self.mvar.as_ref() {
314                ascent += mvar
315                    .metric_delta(Tag::new(b"hasc"), coords)
316                    .unwrap_or_default()
317                    .to_i32();
318            }
319            ascent
320        };
321        Some(origin)
322    }
323
324    fn bounds(&self, gid: impl Into<GlyphId>, coords: &[F2Dot14]) -> Option<BoundingBox<i32>> {
325        let gid = gid.into();
326        let glyf = self.glyf.as_ref()?;
327        let glyph = glyf.loca.get_glyf(gid, &glyf.glyf).ok()?;
328        let Some(glyph) = glyph else {
329            // Return empty extents for empty glyph
330            return Some(BoundingBox::default());
331        };
332        if !coords.is_empty() {
333            return None; // TODO https://github.com/harfbuzz/harfrust/pull/52#issuecomment-2878117808
334        }
335        Some(BoundingBox {
336            x_min: glyph.x_min() as i32,
337            y_min: glyph.y_min() as i32,
338            x_max: glyph.x_max() as i32,
339            y_max: glyph.y_max() as i32,
340        })
341    }
342
343    pub(crate) fn extents(
344        &self,
345        gid: impl Into<GlyphId>,
346        coords: &[F2Dot14],
347    ) -> Option<GlyphExtents> {
348        let gid = gid.into();
349        let glyf = self.glyf.as_ref()?;
350        let glyph = glyf.loca.get_glyf(gid, &glyf.glyf).ok()?;
351        let Some(glyph) = glyph else {
352            // Return empty extents for empty glyph
353            return Some(GlyphExtents::default());
354        };
355        if !coords.is_empty() {
356            return None; // TODO https://github.com/harfbuzz/harfrust/pull/52#issuecomment-2878117808
357        }
358        Some(GlyphExtents {
359            x_bearing: glyph.x_min() as i32,
360            y_bearing: glyph.y_max() as i32,
361            width: glyph.x_max() as i32 - glyph.x_min() as i32,
362            height: glyph.y_min() as i32 - glyph.y_max() as i32,
363        })
364    }
365
366    fn phantom_deltas(&self, gid: GlyphId, coords: &[F2Dot14]) -> Option<[Point<Fixed>; 4]> {
367        let glyf = self.glyf.as_ref()?;
368        let gvar = glyf.gvar.as_ref()?;
369        gvar.phantom_point_deltas(&glyf.glyf, &glyf.loca, coords, gid)
370            .ok()?
371    }
372}