1 | use crate::table::parse::*; |
2 | use hashbrown::HashMap; |
3 | |
4 | // Apple: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html |
5 | // Microsoft: https://docs.microsoft.com/en-us/typography/opentype/spec/kern |
6 | |
7 | #[derive (Debug)] |
8 | pub struct TableKern { |
9 | pub horizontal_mappings: HashMap<u32, i16>, |
10 | } |
11 | |
12 | #[derive (Copy, Clone, Debug)] |
13 | struct Header { |
14 | pub version_major: u16, |
15 | pub version_minor: u16, |
16 | pub number_sub_tables: u32, |
17 | } |
18 | |
19 | #[derive (Copy, Clone, Debug)] |
20 | struct SubTableHeader { |
21 | pub version: u16, |
22 | pub length: usize, |
23 | pub coverage: Coverage, |
24 | pub format: u8, |
25 | pub tuple_index: u16, |
26 | } |
27 | |
28 | #[derive (Copy, Clone, Debug)] |
29 | struct Coverage { |
30 | is_horizontal: bool, |
31 | } |
32 | |
33 | impl Coverage { |
34 | pub const fn aat(cov: u8) -> Coverage { |
35 | Coverage { |
36 | is_horizontal: cov & 0x80 != 0x80, |
37 | } |
38 | } |
39 | |
40 | pub const fn ot(cov: u8) -> Coverage { |
41 | Coverage { |
42 | is_horizontal: cov & 0x01 == 0x01, |
43 | } |
44 | } |
45 | } |
46 | |
47 | impl TableKern { |
48 | pub fn new(kern: &[u8]) -> Option<TableKern> { |
49 | let mut stream = Stream::new(kern); |
50 | let version_major = stream.read_u16()?; |
51 | |
52 | let header; |
53 | match version_major { |
54 | 0x0000 => header = Self::read_ot_header(&mut stream)?, |
55 | 0x0001 => header = Self::read_aat_header(&mut stream)?, |
56 | _ => return None, // Font.kern: Unsupported kern table version. |
57 | } |
58 | |
59 | for _ in 0..header.number_sub_tables { |
60 | let sub_table_start = stream.offset(); |
61 | let sub_header = if version_major == 0x0000 { |
62 | Self::read_ot_subtable(&mut stream)? |
63 | } else { |
64 | Self::read_aat_subtable(&mut stream)? |
65 | }; |
66 | match sub_header.format { |
67 | // Ordered List of Kerning Pairs |
68 | 0 => { |
69 | if sub_header.coverage.is_horizontal { |
70 | let mappings = Self::read_format0(&mut stream)?; |
71 | return Some(TableKern { |
72 | horizontal_mappings: mappings, |
73 | }); |
74 | } |
75 | } |
76 | // State Table for Contextual Kerning |
77 | // 1 => { /* TODO: State Table for Contextual Kerning */ } |
78 | // Simple n x m Array of Kerning Values |
79 | // 2 => { /* TODO: Simple n x m Array of Kerning Values */ } |
80 | // Simple n x m Array of Kerning Indices |
81 | 3 => { |
82 | if sub_header.coverage.is_horizontal { |
83 | let mappings = Self::read_format3(&mut stream)?; |
84 | return Some(TableKern { |
85 | horizontal_mappings: mappings, |
86 | }); |
87 | } |
88 | } |
89 | _ => { |
90 | stream.seek(sub_table_start + sub_header.length); |
91 | } |
92 | } |
93 | } |
94 | |
95 | None // Font.kern: No supported sub-table format available. |
96 | } |
97 | |
98 | fn read_format0(stream: &mut Stream) -> Option<HashMap<u32, i16>> { |
99 | let pairs = stream.read_u16()?; |
100 | stream.skip(6); // searchRange: u16, entrySelector: u16, rangeShift: u16 |
101 | let mut mappings = HashMap::new(); |
102 | for _ in 0..pairs { |
103 | let left = stream.read_u16()?; |
104 | let right = stream.read_u16()?; |
105 | let id = u32::from(left) << 16 | u32::from(right); |
106 | let value = stream.read_i16()?; |
107 | mappings.insert(id, value); |
108 | } |
109 | Some(mappings) |
110 | } |
111 | |
112 | fn read_format3(stream: &mut Stream) -> Option<HashMap<u32, i16>> { |
113 | let glyph_count = stream.read_u16()?; |
114 | let kerning_values_count = stream.read_u8()?; |
115 | let left_hand_classes_count = stream.read_u8()?; |
116 | let right_hand_classes_count = stream.read_u8()?; |
117 | stream.skip(1); // flags - reserved |
118 | let indices_count = u16::from(left_hand_classes_count) * u16::from(right_hand_classes_count); |
119 | |
120 | let kerning_values = stream.read_i16_slice(usize::from(kerning_values_count))?; |
121 | let left_hand_classes = stream.read_u8_slice(usize::from(glyph_count))?; |
122 | let right_hand_classes = stream.read_u8_slice(usize::from(glyph_count))?; |
123 | let indices = stream.read_u8_slice(usize::from(indices_count))?; |
124 | |
125 | let mut mappings = HashMap::new(); |
126 | for left in 0..glyph_count { |
127 | for right in 0..glyph_count { |
128 | if let Some((id, value)) = { |
129 | let left_class = left_hand_classes.get(usize::from(left))?; |
130 | let right_class = right_hand_classes.get(usize::from(right))?; |
131 | |
132 | if left_class > left_hand_classes_count || right_class > right_hand_classes_count { |
133 | continue; |
134 | } |
135 | |
136 | let index = |
137 | u16::from(left_class) * u16::from(right_hand_classes_count) + u16::from(right_class); |
138 | let index = indices.get(usize::from(index))?; |
139 | let id = u32::from(left) << 16 | u32::from(right); |
140 | let value = kerning_values.get(usize::from(index))?; |
141 | Some((id, value)) |
142 | } { |
143 | mappings.insert(id, value); |
144 | }; |
145 | } |
146 | } |
147 | |
148 | Some(mappings) |
149 | } |
150 | |
151 | fn read_ot_header(stream: &mut Stream) -> Option<Header> { |
152 | let version_major = 0x0000; |
153 | let version_minor = 0x0000; |
154 | let number_sub_tables = stream.read_u16()? as u32; |
155 | Some(Header { |
156 | version_major, |
157 | version_minor, |
158 | number_sub_tables, |
159 | }) |
160 | } |
161 | |
162 | fn read_aat_header(stream: &mut Stream) -> Option<Header> { |
163 | let version_major = 0x0001; |
164 | let version_minor = stream.read_u16()?; |
165 | let number_sub_tables = stream.read_u32()?; |
166 | Some(Header { |
167 | version_major, |
168 | version_minor, |
169 | number_sub_tables, |
170 | }) |
171 | } |
172 | |
173 | fn read_ot_subtable(stream: &mut Stream) -> Option<SubTableHeader> { |
174 | let version = stream.read_u16()?; |
175 | let length = stream.read_u16()? as usize; |
176 | let format = stream.read_u8()?; |
177 | let coverage = Coverage::ot(stream.read_u8()?); |
178 | Some(SubTableHeader { |
179 | version, |
180 | length, |
181 | coverage, |
182 | format, |
183 | tuple_index: 0, |
184 | }) |
185 | } |
186 | |
187 | fn read_aat_subtable(stream: &mut Stream) -> Option<SubTableHeader> { |
188 | let length = stream.read_u32()? as usize; |
189 | let coverage = Coverage::aat(stream.read_u8()?); |
190 | let format = stream.read_u8()?; |
191 | let tuple_index = stream.read_u16()?; |
192 | Some(SubTableHeader { |
193 | version: 0x0001, |
194 | length, |
195 | coverage, |
196 | format, |
197 | tuple_index, |
198 | }) |
199 | } |
200 | } |
201 | |