1 | //! A [Glyph Definition Table]( |
2 | //! https://docs.microsoft.com/en-us/typography/opentype/spec/gdef) implementation. |
3 | |
4 | use crate::opentype_layout::{Class, ClassDefinition, Coverage}; |
5 | use crate::parser::{FromSlice, LazyArray16, Offset, Offset16, Offset32, Stream}; |
6 | use crate::GlyphId; |
7 | |
8 | #[cfg (feature = "variable-fonts" )] |
9 | use crate::var_store::ItemVariationStore; |
10 | #[cfg (feature = "variable-fonts" )] |
11 | use crate::NormalizedCoordinate; |
12 | |
13 | /// A [glyph class](https://docs.microsoft.com/en-us/typography/opentype/spec/gdef#glyph-class-definition-table). |
14 | #[allow (missing_docs)] |
15 | #[derive (Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] |
16 | pub enum GlyphClass { |
17 | Base = 1, |
18 | Ligature = 2, |
19 | Mark = 3, |
20 | Component = 4, |
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)] |
26 | pub 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" )] |
31 | variation_store: Option<ItemVariationStore<'a>>, |
32 | } |
33 | |
34 | impl<'a> Table<'a> { |
35 | /// Parses a table from raw data. |
36 | pub fn parse(data: &'a [u8]) -> Option<Self> { |
37 | let mut s = Stream::new(data); |
38 | let version = s.read::<u32>()?; |
39 | if !(version == 0x00010000 || version == 0x00010002 || version == 0x00010003) { |
40 | return None; |
41 | } |
42 | |
43 | let glyph_class_def_offset = s.read::<Option<Offset16>>()?; |
44 | s.skip::<Offset16>(); // attachListOffset |
45 | s.skip::<Offset16>(); // ligCaretListOffset |
46 | let mark_attach_class_def_offset = s.read::<Option<Offset16>>()?; |
47 | |
48 | let mut mark_glyph_sets_def_offset: Option<Offset16> = None; |
49 | if version > 0x00010000 { |
50 | mark_glyph_sets_def_offset = s.read::<Option<Offset16>>()?; |
51 | } |
52 | |
53 | #[allow (unused_mut)] |
54 | #[allow (unused_variables)] |
55 | let mut var_store_offset: Option<Offset32> = None; |
56 | |
57 | #[cfg (feature = "variable-fonts" )] |
58 | { |
59 | if version > 0x00010002 { |
60 | var_store_offset = s.read::<Option<Offset32>>()?; |
61 | } |
62 | } |
63 | |
64 | let mut table = Table::default(); |
65 | |
66 | if let Some(offset) = glyph_class_def_offset { |
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)] |
170 | fn is_mark_glyph_impl(table: &Table, glyph_id: GlyphId, set_index: Option<u16>) -> Option<()> { |
171 | let (data: &[u8], offsets: LazyArray16<'_, Offset32>) = table.mark_glyph_coverage_offsets?; |
172 | |
173 | if let Some(set_index: u16) = set_index { |
174 | if let Some(offset: Offset32) = offsets.get(set_index) { |
175 | let table: Coverage<'_> = Coverage::parse(data:data.get(index:offset.to_usize()..)?)?; |
176 | if table.contains(glyph_id) { |
177 | return Some(()); |
178 | } |
179 | } |
180 | } else { |
181 | for offset: Offset32 in offsets { |
182 | let table: Coverage<'_> = Coverage::parse(data:data.get(index:offset.to_usize()..)?)?; |
183 | if table.contains(glyph_id) { |
184 | return Some(()); |
185 | } |
186 | } |
187 | } |
188 | |
189 | None |
190 | } |
191 | |