allsorts_subset_browser/tables/variable_fonts/
hvar.rs

1//! `HVAR` — Horizontal Metrics Variations Table
2//!
3//! Optional table in variable fonts to provide horizontal metrics variations.
4//! If absent then `gvar` deltas must be used to determine adjustments to
5//! metrics.
6//!
7//! <https://learn.microsoft.com/en-us/typography/opentype/spec/hvar>
8
9use crate::binary::read::{ReadBinary, ReadCtxt, ReadScope};
10use crate::error::ParseError;
11use crate::tables::variable_fonts::{
12    DeltaSetIndexMap, DeltaSetIndexMapEntry, ItemVariationStore, OwnedTuple,
13};
14use crate::SafeFrom;
15
16/// `HVAR` — Horizontal Metrics Variations Table.
17pub struct HvarTable<'a> {
18    /// Major version number of the horizontal metrics variations table.
19    pub major_version: u16,
20    /// Minor version number of the horizontal metrics variations table.
21    pub minor_version: u16,
22    /// The item variation store table.
23    item_variation_store: ItemVariationStore<'a>,
24    /// The delta-set index mapping for advance widths.
25    advance_width_mapping: Option<DeltaSetIndexMap<'a>>,
26    /// The delta-set index mapping for left side bearings.
27    lsb_mapping: Option<DeltaSetIndexMap<'a>>,
28    /// The delta-set index mapping for right side bearings.
29    rsb_mapping: Option<DeltaSetIndexMap<'a>>,
30}
31
32impl<'a> HvarTable<'a> {
33    /// Calculate the delta for the advance of the supplied `glyph_id`.
34    pub fn advance_delta(&self, instance: &OwnedTuple, glyph_id: u16) -> Result<f32, ParseError> {
35        // Variation data for advance widths is required. A delta-set index mapping subtable for
36        // advance widths can be provided, but is optional. If a mapping subtable is not provided,
37        // glyph indices are used as implicit delta-set indices. To access the delta set for the
38        // advance of given glyph, the delta-set outer-level index is zero, and the glyph ID is
39        // used as the inner-level index.
40        let delta_set_entry =
41            Self::delta_set_entry_for_glyph(glyph_id, self.advance_width_mapping.as_ref())?
42                .unwrap_or_else(|| DeltaSetIndexMapEntry {
43                    outer_index: 0,
44                    inner_index: glyph_id,
45                });
46        self.item_variation_store
47            .adjustment(delta_set_entry, instance)
48    }
49
50    /// Calculate the delta for the left-side bearing of the supplied
51    /// `glyph_id`.
52    pub fn left_side_bearing_delta(
53        &self,
54        instance: &OwnedTuple,
55        glyph_id: u16,
56    ) -> Result<Option<f32>, ParseError> {
57        Self::delta_set_entry_for_glyph(glyph_id, self.lsb_mapping.as_ref())?
58            .map(|delta_set_entry| {
59                self.item_variation_store
60                    .adjustment(delta_set_entry, instance)
61            })
62            .transpose()
63    }
64
65    /// Calculate the delta for the right-side bearing of the supplied
66    /// `glyph_id`.
67    pub fn right_side_bearing_delta(
68        &self,
69        instance: &OwnedTuple,
70        glyph_id: u16,
71    ) -> Result<Option<f32>, ParseError> {
72        Self::delta_set_entry_for_glyph(glyph_id, self.rsb_mapping.as_ref())?
73            .map(|delta_set_entry| {
74                self.item_variation_store
75                    .adjustment(delta_set_entry, instance)
76            })
77            .transpose()
78    }
79
80    fn delta_set_entry_for_glyph(
81        glyph_id: u16,
82        delta_set_index_map: Option<&DeltaSetIndexMap<'_>>,
83    ) -> Result<Option<DeltaSetIndexMapEntry>, ParseError> {
84        delta_set_index_map
85            .map(|mapping| mapping.entry(u32::from(glyph_id)))
86            .transpose()
87    }
88}
89
90impl ReadBinary for HvarTable<'_> {
91    type HostType<'a> = HvarTable<'a>;
92
93    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
94        let scope = ctxt.scope();
95        let major_version = ctxt.read_u16be()?;
96        ctxt.check_version(major_version == 1)?;
97        let minor_version = ctxt.read_u16be()?;
98        let item_variation_store_offset = ctxt.read_u32be()?;
99        let advance_width_mapping_offset = ctxt.read_u32be()?;
100        let lsb_mapping_offset = ctxt.read_u32be()?;
101        let rsb_mapping_offset = ctxt.read_u32be()?;
102
103        let item_variation_store = scope
104            .offset(usize::safe_from(item_variation_store_offset))
105            .read::<ItemVariationStore<'_>>()?;
106        let advance_width_mapping = read_optional_index_map(scope, advance_width_mapping_offset)?;
107        let lsb_mapping = read_optional_index_map(scope, lsb_mapping_offset)?;
108        let rsb_mapping = read_optional_index_map(scope, rsb_mapping_offset)?;
109
110        Ok(HvarTable {
111            major_version,
112            minor_version,
113            item_variation_store,
114            advance_width_mapping,
115            lsb_mapping,
116            rsb_mapping,
117        })
118    }
119}
120
121fn read_optional_index_map(
122    scope: ReadScope<'_>,
123    offset: u32,
124) -> Result<Option<DeltaSetIndexMap<'_>>, ParseError> {
125    (offset > 0)
126        .then(|| {
127            scope
128                .offset(usize::safe_from(offset))
129                .read::<DeltaSetIndexMap<'_>>()
130        })
131        .transpose()
132}