ttf_parser/tables/
gdef.rs

1// https://docs.microsoft.com/en-us/typography/opentype/spec/gdef
2
3use crate::GlyphId;
4use crate::parser::{Stream, Offset, Offset16, Offset32, LazyArray16};
5use crate::ggg::{Class, ClassDefinitionTable, CoverageTable};
6
7#[cfg(feature = "variable-fonts")] use crate::NormalizedCoordinate;
8#[cfg(feature = "variable-fonts")] use crate::var_store::ItemVariationStore;
9
10
11/// A [glyph class](https://docs.microsoft.com/en-us/typography/opentype/spec/gdef#glyph-class-definition-table).
12#[derive(Clone, Copy, PartialEq, Debug)]
13#[allow(missing_docs)]
14pub enum GlyphClass {
15    Base      = 1,
16    Ligature  = 2,
17    Mark      = 3,
18    Component = 4,
19}
20
21
22#[derive(Clone, Copy, Default)]
23pub struct Table<'a> {
24    glyph_classes: Option<ClassDefinitionTable<'a>>,
25    mark_attach_classes: Option<ClassDefinitionTable<'a>>,
26    mark_glyph_coverage_offsets: Option<(&'a [u8], LazyArray16<'a, Offset32>)>,
27    #[cfg(feature = "variable-fonts")] variation_store: Option<ItemVariationStore<'a>>,
28}
29
30impl<'a> Table<'a> {
31    pub fn parse(data: &'a [u8]) -> Option<Self> {
32        let mut s = Stream::new(data);
33        let version: u32 = s.read()?;
34        if !(version == 0x00010000 || version == 0x00010002 || version == 0x00010003) {
35            return None;
36        }
37
38        let glyph_class_def_offset: Option<Offset16> = s.read()?;
39        s.skip::<Offset16>(); // attachListOffset
40        s.skip::<Offset16>(); // ligCaretListOffset
41        let mark_attach_class_def_offset: Option<Offset16> = s.read()?;
42
43        let mut mark_glyph_sets_def_offset: Option<Offset16> = None;
44        if version > 0x00010000 {
45            mark_glyph_sets_def_offset = s.read()?;
46        }
47
48        #[allow(unused_mut)]
49        #[allow(unused_variables)]
50        let mut var_store_offset: Option<Offset32> = None;
51
52        #[cfg(feature = "variable-fonts")]
53        {
54            if version > 0x00010002 {
55                var_store_offset = s.read();
56            }
57        }
58
59        let mut table = Table::default();
60
61        if let Some(offset) = glyph_class_def_offset {
62            if let Some(subdata) = data.get(offset.to_usize()..) {
63                table.glyph_classes = Some(ClassDefinitionTable::new(subdata));
64            }
65        }
66
67        if let Some(offset) = mark_attach_class_def_offset {
68            if let Some(subdata) = data.get(offset.to_usize()..) {
69                table.mark_attach_classes = Some(ClassDefinitionTable::new(subdata));
70            }
71        }
72
73        if let Some(offset) = mark_glyph_sets_def_offset {
74            if let Some(subdata) = data.get(offset.to_usize()..) {
75                let mut s = Stream::new(subdata);
76                let format: u16 = s.read()?;
77                if format == 1 {
78                    if let Some(count) = s.read::<u16>() {
79                        if let Some(array) = s.read_array16::<Offset32>(count) {
80                            table.mark_glyph_coverage_offsets = Some((subdata, array));
81                        }
82                    }
83                }
84            }
85        }
86
87        #[cfg(feature = "variable-fonts")]
88        {
89            if let Some(offset) = var_store_offset {
90                if let Some(subdata) = data.get(offset.to_usize()..) {
91                    let s = Stream::new(subdata);
92                    table.variation_store = ItemVariationStore::parse(s);
93                }
94            }
95        }
96
97        Some(table)
98    }
99
100    #[inline]
101    pub fn has_glyph_classes(&self) -> bool {
102        self.glyph_classes.is_some()
103    }
104
105    #[inline]
106    pub fn glyph_class(&self, glyph_id: GlyphId) -> Option<GlyphClass> {
107        match self.glyph_classes?.get(glyph_id).0 {
108            1 => Some(GlyphClass::Base),
109            2 => Some(GlyphClass::Ligature),
110            3 => Some(GlyphClass::Mark),
111            4 => Some(GlyphClass::Component),
112            _ => None,
113        }
114    }
115
116    #[inline]
117    pub fn glyph_mark_attachment_class(&self, glyph_id: GlyphId) -> Class {
118        self.mark_attach_classes
119            .map(|def| def.get(glyph_id))
120            .unwrap_or(Class(0))
121    }
122
123    #[inline]
124    pub fn is_mark_glyph(&self, glyph_id: GlyphId, set_index: Option<u16>) -> bool {
125        is_mark_glyph_impl(self, glyph_id, set_index).is_some()
126    }
127
128    #[cfg(feature = "variable-fonts")]
129    #[inline]
130    pub fn variation_delta(
131        &self,
132        outer_index: u16,
133        inner_index: u16,
134        coordinates: &[NormalizedCoordinate],
135    ) -> Option<f32> {
136        self.variation_store
137            .and_then(|store| store.parse_delta(outer_index, inner_index, coordinates))
138    }
139}
140
141#[inline(never)]
142fn is_mark_glyph_impl(
143    table: &Table,
144    glyph_id: GlyphId,
145    set_index: Option<u16>,
146) -> Option<()> {
147    let (data, offsets) = table.mark_glyph_coverage_offsets?;
148
149    if let Some(set_index) = set_index {
150        if let Some(offset) = offsets.get(set_index) {
151            let table = CoverageTable::new(data.get(offset.to_usize()..)?);
152            if table.contains(glyph_id) {
153                return Some(());
154            }
155        }
156    } else {
157        for offset in offsets {
158            let table = CoverageTable::new(data.get(offset.to_usize()..)?);
159            if table.contains(glyph_id) {
160                return Some(());
161            }
162        }
163    }
164
165    None
166}