ttf_parser/tables/
gdef.rs

1//! A [Glyph Definition Table](
2//! https://docs.microsoft.com/en-us/typography/opentype/spec/gdef) implementation.
3
4use crate::GlyphId;
5use crate::opentype_layout::{Class, ClassDefinition, Coverage};
6use crate::parser::{LazyArray16, Offset, Offset16, Offset32, Stream, FromSlice};
7
8#[cfg(feature = "variable-fonts")] use crate::NormalizedCoordinate;
9#[cfg(feature = "variable-fonts")] use crate::var_store::ItemVariationStore;
10
11
12/// A [glyph class](https://docs.microsoft.com/en-us/typography/opentype/spec/gdef#glyph-class-definition-table).
13#[allow(missing_docs)]
14#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
15pub enum GlyphClass {
16    Base      = 1,
17    Ligature  = 2,
18    Mark      = 3,
19    Component = 4,
20}
21
22
23/// A [Glyph Definition Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gdef).
24#[allow(missing_debug_implementations)]
25#[derive(Clone, Copy, Default)]
26pub struct Table<'a> {
27    glyph_classes: Option<ClassDefinition<'a>>,
28    mark_attach_classes: Option<ClassDefinition<'a>>,
29    mark_glyph_coverage_offsets: Option<(&'a [u8], LazyArray16<'a, Offset32>)>,
30    #[cfg(feature = "variable-fonts")] variation_store: Option<ItemVariationStore<'a>>,
31}
32
33impl<'a> Table<'a> {
34    /// Parses a table from raw data.
35    pub fn parse(data: &'a [u8]) -> Option<Self> {
36        let mut s = Stream::new(data);
37        let version = s.read::<u32>()?;
38        if !(version == 0x00010000 || version == 0x00010002 || version == 0x00010003) {
39            return None;
40        }
41
42        let glyph_class_def_offset = s.read::<Option<Offset16>>()?;
43        s.skip::<Offset16>(); // attachListOffset
44        s.skip::<Offset16>(); // ligCaretListOffset
45        let mark_attach_class_def_offset = s.read::<Option<Offset16>>()?;
46
47        let mut mark_glyph_sets_def_offset: Option<Offset16> = None;
48        if version > 0x00010000 {
49            mark_glyph_sets_def_offset = s.read::<Option<Offset16>>()?;
50        }
51
52        #[allow(unused_mut)]
53        #[allow(unused_variables)]
54        let mut var_store_offset: Option<Offset32> = None;
55
56        #[cfg(feature = "variable-fonts")]
57        {
58            if version > 0x00010002 {
59                var_store_offset = s.read::<Option<Offset32>>()?;
60            }
61        }
62
63        let mut table = Table::default();
64
65        if let Some(offset) = glyph_class_def_offset {
66
67            if let Some(subdata) = data.get(offset.to_usize()..) {
68                table.glyph_classes = ClassDefinition::parse(subdata);
69            }
70        }
71
72        if let Some(offset) = mark_attach_class_def_offset {
73            if let Some(subdata) = data.get(offset.to_usize()..) {
74                table.mark_attach_classes = ClassDefinition::parse(subdata);
75            }
76        }
77
78        if let Some(offset) = mark_glyph_sets_def_offset {
79            if let Some(subdata) = data.get(offset.to_usize()..) {
80                let mut s = Stream::new(subdata);
81                let format = s.read::<u16>()?;
82                if format == 1 {
83                    if let Some(count) = s.read::<u16>() {
84                        if let Some(array) = s.read_array16::<Offset32>(count) {
85                            table.mark_glyph_coverage_offsets = Some((subdata, array));
86                        }
87                    }
88                }
89            }
90        }
91
92        #[cfg(feature = "variable-fonts")]
93        {
94            if let Some(offset) = var_store_offset {
95                if let Some(subdata) = data.get(offset.to_usize()..) {
96                    let s = Stream::new(subdata);
97                    table.variation_store = ItemVariationStore::parse(s);
98                }
99            }
100        }
101
102        Some(table)
103    }
104
105    /// Checks that face has
106    /// [Glyph Class Definition Table](
107    /// https://docs.microsoft.com/en-us/typography/opentype/spec/gdef#glyph-class-definition-table).
108    #[inline]
109    pub fn has_glyph_classes(&self) -> bool {
110        self.glyph_classes.is_some()
111    }
112
113    /// Returns glyph's class according to
114    /// [Glyph Class Definition Table](
115    /// https://docs.microsoft.com/en-us/typography/opentype/spec/gdef#glyph-class-definition-table).
116    ///
117    /// Returns `None` when *Glyph Class Definition Table* is not set
118    /// or glyph class is not set or invalid.
119    #[inline]
120    pub fn glyph_class(&self, glyph_id: GlyphId) -> Option<GlyphClass> {
121        match self.glyph_classes?.get(glyph_id) {
122            1 => Some(GlyphClass::Base),
123            2 => Some(GlyphClass::Ligature),
124            3 => Some(GlyphClass::Mark),
125            4 => Some(GlyphClass::Component),
126            _ => None,
127        }
128    }
129
130    /// Returns glyph's mark attachment class according to
131    /// [Mark Attachment Class Definition Table](
132    /// https://docs.microsoft.com/en-us/typography/opentype/spec/gdef#mark-attachment-class-definition-table).
133    ///
134    /// All glyphs not assigned to a class fall into Class 0.
135    #[inline]
136    pub fn glyph_mark_attachment_class(&self, glyph_id: GlyphId) -> Class {
137        self.mark_attach_classes
138            .map(|def| def.get(glyph_id))
139            .unwrap_or(0)
140    }
141
142    /// Checks that glyph is a mark according to
143    /// [Mark Glyph Sets Table](
144    /// https://docs.microsoft.com/en-us/typography/opentype/spec/gdef#mark-glyph-sets-table).
145    ///
146    /// `set_index` allows checking a specific glyph coverage set.
147    /// Otherwise all sets will be checked.
148    #[inline]
149    pub fn is_mark_glyph(&self, glyph_id: GlyphId, set_index: Option<u16>) -> bool {
150        is_mark_glyph_impl(self, glyph_id, set_index).is_some()
151    }
152
153    /// Returns glyph's variation delta at a specified index according to
154    /// [Item Variation Store Table](
155    /// https://docs.microsoft.com/en-us/typography/opentype/spec/gdef#item-variation-store-table).
156    #[cfg(feature = "variable-fonts")]
157    #[inline]
158    pub fn glyph_variation_delta(
159        &self,
160        outer_index: u16,
161        inner_index: u16,
162        coordinates: &[NormalizedCoordinate],
163    ) -> Option<f32> {
164        self.variation_store
165            .and_then(|store| store.parse_delta(outer_index, inner_index, coordinates))
166    }
167}
168
169#[inline(never)]
170fn is_mark_glyph_impl(
171    table: &Table,
172    glyph_id: GlyphId,
173    set_index: Option<u16>,
174) -> Option<()> {
175    let (data, offsets) = table.mark_glyph_coverage_offsets?;
176
177    if let Some(set_index) = set_index {
178        if let Some(offset) = offsets.get(set_index) {
179            let table = Coverage::parse(data.get(offset.to_usize()..)?)?;
180            if table.contains(glyph_id) {
181                return Some(());
182            }
183        }
184    } else {
185        for offset in offsets {
186            let table = Coverage::parse(data.get(offset.to_usize()..)?)?;
187            if table.contains(glyph_id) {
188                return Some(());
189            }
190        }
191    }
192
193    None
194}