ttf_parser/tables/
gdef.rs1use 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#[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#[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 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>(); s.skip::<Offset16>(); 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 #[inline]
109 pub fn has_glyph_classes(&self) -> bool {
110 self.glyph_classes.is_some()
111 }
112
113 #[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 #[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 #[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 #[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}