ttf_parser/tables/
gdef.rs
1use 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#[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>(); s.skip::<Offset16>(); 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}