/src/ttf-parser/src/aat.rs
Line | Count | Source (jump to first uncovered line) |
1 | | /*! |
2 | | A collection of [Apple Advanced Typography]( |
3 | | https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html) |
4 | | related types. |
5 | | */ |
6 | | |
7 | | use core::num::NonZeroU16; |
8 | | |
9 | | use crate::parser::{FromData, LazyArray16, NumFrom, Offset, Offset16, Offset32, Stream}; |
10 | | use crate::GlyphId; |
11 | | |
12 | | /// Predefined states. |
13 | | pub mod state { |
14 | | #![allow(missing_docs)] |
15 | | pub const START_OF_TEXT: u16 = 0; |
16 | | } |
17 | | |
18 | | /// Predefined classes. |
19 | | /// |
20 | | /// Search for _Class Code_ in [Apple Advanced Typography Font Tables]( |
21 | | /// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html). |
22 | | pub mod class { |
23 | | #![allow(missing_docs)] |
24 | | pub const END_OF_TEXT: u8 = 0; |
25 | | pub const OUT_OF_BOUNDS: u8 = 1; |
26 | | pub const DELETED_GLYPH: u8 = 2; |
27 | | } |
28 | | |
29 | | /// A State Table entry. |
30 | | /// |
31 | | /// Used by legacy and extended tables. |
32 | | #[derive(Clone, Copy, Debug)] |
33 | | pub struct GenericStateEntry<T: FromData> { |
34 | | /// A new state. |
35 | | pub new_state: u16, |
36 | | /// Entry flags. |
37 | | pub flags: u16, |
38 | | /// Additional data. |
39 | | /// |
40 | | /// Use `()` if no data expected. |
41 | | pub extra: T, |
42 | | } |
43 | | |
44 | | impl<T: FromData> FromData for GenericStateEntry<T> { |
45 | | const SIZE: usize = 4 + T::SIZE; |
46 | | |
47 | | #[inline] |
48 | 0 | fn parse(data: &[u8]) -> Option<Self> { |
49 | 0 | let mut s = Stream::new(data); |
50 | 0 | Some(GenericStateEntry { |
51 | 0 | new_state: s.read::<u16>()?, |
52 | 0 | flags: s.read::<u16>()?, |
53 | 0 | extra: s.read::<T>()?, |
54 | | }) |
55 | 0 | } |
56 | | } |
57 | | |
58 | | impl<T: FromData> GenericStateEntry<T> { |
59 | | /// Checks that entry has an offset. |
60 | | #[inline] |
61 | 0 | pub fn has_offset(&self) -> bool { |
62 | 0 | self.flags & 0x3FFF != 0 |
63 | 0 | } |
64 | | |
65 | | /// Returns a value offset. |
66 | | /// |
67 | | /// Used by kern::format1 subtable. |
68 | | #[inline] |
69 | 0 | pub fn value_offset(&self) -> ValueOffset { |
70 | 0 | ValueOffset(self.flags & 0x3FFF) |
71 | 0 | } |
72 | | |
73 | | /// If set, reset the kerning data (clear the stack). |
74 | | #[inline] |
75 | 0 | pub fn has_reset(&self) -> bool { |
76 | 0 | self.flags & 0x2000 != 0 |
77 | 0 | } |
78 | | |
79 | | /// If set, advance to the next glyph before going to the new state. |
80 | | #[inline] |
81 | 0 | pub fn has_advance(&self) -> bool { |
82 | 0 | self.flags & 0x4000 == 0 |
83 | 0 | } |
84 | | |
85 | | /// If set, push this glyph on the kerning stack. |
86 | | #[inline] |
87 | 0 | pub fn has_push(&self) -> bool { |
88 | 0 | self.flags & 0x8000 != 0 |
89 | 0 | } |
90 | | |
91 | | /// If set, remember this glyph as the marked glyph. |
92 | | /// |
93 | | /// Used by kerx::format4 subtable. |
94 | | /// |
95 | | /// Yes, the same as [`has_push`](Self::has_push). |
96 | | #[inline] |
97 | 0 | pub fn has_mark(&self) -> bool { |
98 | 0 | self.flags & 0x8000 != 0 |
99 | 0 | } |
100 | | } |
101 | | |
102 | | /// A legacy state entry used by [StateTable]. |
103 | | pub type StateEntry = GenericStateEntry<()>; |
104 | | |
105 | | /// A type-safe wrapper for a kerning value offset. |
106 | | #[derive(Clone, Copy, Debug)] |
107 | | pub struct ValueOffset(u16); |
108 | | |
109 | | impl ValueOffset { |
110 | | /// Returns the next offset. |
111 | | /// |
112 | | /// After reaching u16::MAX will start from 0. |
113 | | #[inline] |
114 | 0 | pub fn next(self) -> Self { |
115 | 0 | ValueOffset(self.0.wrapping_add(u16::SIZE as u16)) |
116 | 0 | } |
117 | | } |
118 | | |
119 | | /// A [State Table]( |
120 | | /// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html). |
121 | | /// |
122 | | /// Also called `STHeader`. |
123 | | /// |
124 | | /// Currently used by `kern` table. |
125 | | #[derive(Clone)] |
126 | | pub struct StateTable<'a> { |
127 | | number_of_classes: u16, |
128 | | first_glyph: GlyphId, |
129 | | class_table: &'a [u8], |
130 | | state_array_offset: u16, |
131 | | state_array: &'a [u8], |
132 | | entry_table: &'a [u8], |
133 | | actions: &'a [u8], |
134 | | } |
135 | | |
136 | | impl<'a> StateTable<'a> { |
137 | 2 | pub(crate) fn parse(data: &'a [u8]) -> Option<Self> { |
138 | 2 | let mut s = Stream::new(data); |
139 | | |
140 | 2 | let number_of_classes: u16 = s.read()?; |
141 | | // Note that in format1 subtable, offsets are not from the subtable start, |
142 | | // but from subtable start + `header_size`. |
143 | | // So there is not need to subtract the `header_size`. |
144 | 2 | let class_table_offset = s.read::<Offset16>()?.to_usize(); |
145 | 2 | let state_array_offset = s.read::<Offset16>()?.to_usize(); |
146 | 2 | let entry_table_offset = s.read::<Offset16>()?.to_usize(); |
147 | | // Ignore `values_offset` since we don't use it. |
148 | | |
149 | | // Parse class subtable. |
150 | 2 | let mut s = Stream::new_at(data, class_table_offset)?; |
151 | 2 | let first_glyph: GlyphId = s.read()?; |
152 | 2 | let number_of_glyphs: u16 = s.read()?; |
153 | | // The class table contains u8, so it's easier to use just a slice |
154 | | // instead of a LazyArray. |
155 | 2 | let class_table = s.read_bytes(usize::from(number_of_glyphs))?; |
156 | | |
157 | | Some(StateTable { |
158 | 2 | number_of_classes, |
159 | 2 | first_glyph, |
160 | 2 | class_table, |
161 | 2 | state_array_offset: state_array_offset as u16, |
162 | 2 | // We don't know the actual data size and it's kinda expensive to calculate. |
163 | 2 | // So we are simply storing all the data past the offset. |
164 | 2 | // Despite the fact that they may overlap. |
165 | 2 | state_array: data.get(state_array_offset..)?, |
166 | 2 | entry_table: data.get(entry_table_offset..)?, |
167 | | // `ValueOffset` defines an offset from the start of the subtable data. |
168 | | // We do not check that the provided offset is actually after `values_offset`. |
169 | 2 | actions: data, |
170 | | }) |
171 | 2 | } |
172 | | |
173 | | /// Returns a glyph class. |
174 | | #[inline] |
175 | 0 | pub fn class(&self, glyph_id: GlyphId) -> Option<u8> { |
176 | 0 | if glyph_id.0 == 0xFFFF { |
177 | 0 | return Some(class::DELETED_GLYPH); |
178 | 0 | } |
179 | | |
180 | 0 | let idx = glyph_id.0.checked_sub(self.first_glyph.0)?; |
181 | 0 | self.class_table.get(usize::from(idx)).copied() |
182 | 0 | } |
183 | | |
184 | | /// Returns a class entry. |
185 | | #[inline] |
186 | 0 | pub fn entry(&self, state: u16, mut class: u8) -> Option<StateEntry> { |
187 | 0 | if u16::from(class) >= self.number_of_classes { |
188 | 0 | class = class::OUT_OF_BOUNDS; |
189 | 0 | } |
190 | | |
191 | 0 | let entry_idx = self |
192 | 0 | .state_array |
193 | 0 | .get(usize::from(state) * usize::from(self.number_of_classes) + usize::from(class))?; |
194 | | |
195 | 0 | Stream::read_at(self.entry_table, usize::from(*entry_idx) * StateEntry::SIZE) |
196 | 0 | } |
197 | | |
198 | | /// Returns kerning at offset. |
199 | | #[inline] |
200 | 0 | pub fn kerning(&self, offset: ValueOffset) -> Option<i16> { |
201 | 0 | Stream::read_at(self.actions, usize::from(offset.0)) |
202 | 0 | } |
203 | | |
204 | | /// Produces a new state. |
205 | | #[inline] |
206 | 0 | pub fn new_state(&self, state: u16) -> u16 { |
207 | 0 | let n = (i32::from(state) - i32::from(self.state_array_offset)) |
208 | 0 | / i32::from(self.number_of_classes); |
209 | | |
210 | | use core::convert::TryFrom; |
211 | 0 | u16::try_from(n).unwrap_or(0) |
212 | 0 | } |
213 | | } |
214 | | |
215 | | impl core::fmt::Debug for StateTable<'_> { |
216 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
217 | 0 | write!(f, "StateTable {{ ... }}") |
218 | 0 | } |
219 | | } |
220 | | |
221 | | /// An [Extended State Table]( |
222 | | /// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html). |
223 | | /// |
224 | | /// Also called `STXHeader`. |
225 | | /// |
226 | | /// Currently used by `kerx` and `morx` tables. |
227 | | #[derive(Clone)] |
228 | | pub struct ExtendedStateTable<'a, T> { |
229 | | number_of_classes: u32, |
230 | | lookup: Lookup<'a>, |
231 | | state_array: &'a [u8], |
232 | | entry_table: &'a [u8], |
233 | | entry_type: core::marker::PhantomData<T>, |
234 | | } |
235 | | |
236 | | impl<'a, T: FromData> ExtendedStateTable<'a, T> { |
237 | | // TODO: make private |
238 | | /// Parses an Extended State Table from a stream. |
239 | | /// |
240 | | /// `number_of_glyphs` is from the `maxp` table. |
241 | 2 | pub fn parse(number_of_glyphs: NonZeroU16, s: &mut Stream<'a>) -> Option<Self> { |
242 | 2 | let data = s.tail()?; |
243 | | |
244 | 2 | let number_of_classes = s.read::<u32>()?; |
245 | | // Note that offsets are not from the subtable start, |
246 | | // but from subtable start + `header_size`. |
247 | | // So there is not need to subtract the `header_size`. |
248 | 2 | let lookup_table_offset = s.read::<Offset32>()?.to_usize(); |
249 | 2 | let state_array_offset = s.read::<Offset32>()?.to_usize(); |
250 | 2 | let entry_table_offset = s.read::<Offset32>()?.to_usize(); |
251 | 2 | |
252 | 2 | Some(ExtendedStateTable { |
253 | 2 | number_of_classes, |
254 | 2 | lookup: Lookup::parse(number_of_glyphs, data.get(lookup_table_offset..)?)?, |
255 | | // We don't know the actual data size and it's kinda expensive to calculate. |
256 | | // So we are simply storing all the data past the offset. |
257 | | // Despite the fact that they may overlap. |
258 | 1 | state_array: data.get(state_array_offset..)?, |
259 | 1 | entry_table: data.get(entry_table_offset..)?, |
260 | 1 | entry_type: core::marker::PhantomData, |
261 | | }) |
262 | 2 | } <ttf_parser::aat::ExtendedStateTable<ttf_parser::tables::kerx::EntryData>>::parse Line | Count | Source | 241 | 2 | pub fn parse(number_of_glyphs: NonZeroU16, s: &mut Stream<'a>) -> Option<Self> { | 242 | 2 | let data = s.tail()?; | 243 | | | 244 | 2 | let number_of_classes = s.read::<u32>()?; | 245 | | // Note that offsets are not from the subtable start, | 246 | | // but from subtable start + `header_size`. | 247 | | // So there is not need to subtract the `header_size`. | 248 | 2 | let lookup_table_offset = s.read::<Offset32>()?.to_usize(); | 249 | 2 | let state_array_offset = s.read::<Offset32>()?.to_usize(); | 250 | 2 | let entry_table_offset = s.read::<Offset32>()?.to_usize(); | 251 | 2 | | 252 | 2 | Some(ExtendedStateTable { | 253 | 2 | number_of_classes, | 254 | 2 | lookup: Lookup::parse(number_of_glyphs, data.get(lookup_table_offset..)?)?, | 255 | | // We don't know the actual data size and it's kinda expensive to calculate. | 256 | | // So we are simply storing all the data past the offset. | 257 | | // Despite the fact that they may overlap. | 258 | 1 | state_array: data.get(state_array_offset..)?, | 259 | 1 | entry_table: data.get(entry_table_offset..)?, | 260 | 1 | entry_type: core::marker::PhantomData, | 261 | | }) | 262 | 2 | } |
Unexecuted instantiation: <ttf_parser::aat::ExtendedStateTable<ttf_parser::tables::morx::InsertionEntryData>>::parse Unexecuted instantiation: <ttf_parser::aat::ExtendedStateTable<ttf_parser::tables::morx::ContextualEntryData>>::parse Unexecuted instantiation: <ttf_parser::aat::ExtendedStateTable<u16>>::parse Unexecuted instantiation: <ttf_parser::aat::ExtendedStateTable<()>>::parse |
263 | | |
264 | | /// Returns a glyph class. |
265 | | #[inline] |
266 | 0 | pub fn class(&self, glyph_id: GlyphId) -> Option<u16> { |
267 | 0 | if glyph_id.0 == 0xFFFF { |
268 | 0 | return Some(u16::from(class::DELETED_GLYPH)); |
269 | 0 | } |
270 | 0 |
|
271 | 0 | self.lookup.value(glyph_id) |
272 | 0 | } |
273 | | |
274 | | /// Returns a class entry. |
275 | | #[inline] |
276 | 0 | pub fn entry(&self, state: u16, mut class: u16) -> Option<GenericStateEntry<T>> { |
277 | 0 | if u32::from(class) >= self.number_of_classes { |
278 | 0 | class = u16::from(class::OUT_OF_BOUNDS); |
279 | 0 | } |
280 | | |
281 | 0 | let state_idx = |
282 | 0 | usize::from(state) * usize::num_from(self.number_of_classes) + usize::from(class); |
283 | | |
284 | 0 | let entry_idx: u16 = Stream::read_at(self.state_array, state_idx * u16::SIZE)?; |
285 | 0 | Stream::read_at( |
286 | 0 | self.entry_table, |
287 | 0 | usize::from(entry_idx) * GenericStateEntry::<T>::SIZE, |
288 | 0 | ) |
289 | 0 | } |
290 | | } |
291 | | |
292 | | impl<T> core::fmt::Debug for ExtendedStateTable<'_, T> { |
293 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
294 | 0 | write!(f, "ExtendedStateTable {{ ... }}") |
295 | 0 | } |
296 | | } |
297 | | |
298 | | /// A [lookup table]( |
299 | | /// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html). |
300 | | /// |
301 | | /// u32 values in Format10 tables will be truncated to u16. |
302 | | /// u64 values in Format10 tables are not supported. |
303 | | #[derive(Clone)] |
304 | | pub struct Lookup<'a> { |
305 | | data: LookupInner<'a>, |
306 | | } |
307 | | |
308 | | impl<'a> Lookup<'a> { |
309 | | /// Parses a lookup table from raw data. |
310 | | /// |
311 | | /// `number_of_glyphs` is from the `maxp` table. |
312 | | #[inline] |
313 | 2.14k | pub fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> { |
314 | 2.14k | LookupInner::parse(number_of_glyphs, data).map(|data| Self { data }) |
315 | 2.14k | } <ttf_parser::aat::Lookup>::parse Line | Count | Source | 313 | 169 | pub fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> { | 314 | 169 | LookupInner::parse(number_of_glyphs, data).map(|data| Self { data }) | 315 | 169 | } |
<ttf_parser::aat::Lookup>::parse Line | Count | Source | 313 | 1.97k | pub fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> { | 314 | 1.97k | LookupInner::parse(number_of_glyphs, data).map(|data| Self { data }) | 315 | 1.97k | } |
|
316 | | |
317 | | /// Returns a value associated with the specified glyph. |
318 | | #[inline] |
319 | 1.34k | pub fn value(&self, glyph_id: GlyphId) -> Option<u16> { |
320 | 1.34k | self.data.value(glyph_id) |
321 | 1.34k | } <ttf_parser::aat::Lookup>::value Line | Count | Source | 319 | 22 | pub fn value(&self, glyph_id: GlyphId) -> Option<u16> { | 320 | 22 | self.data.value(glyph_id) | 321 | 22 | } |
<ttf_parser::aat::Lookup>::value Line | Count | Source | 319 | 1.32k | pub fn value(&self, glyph_id: GlyphId) -> Option<u16> { | 320 | 1.32k | self.data.value(glyph_id) | 321 | 1.32k | } |
|
322 | | } |
323 | | |
324 | | impl core::fmt::Debug for Lookup<'_> { |
325 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
326 | 0 | write!(f, "Lookup {{ ... }}") |
327 | 0 | } |
328 | | } |
329 | | |
330 | | #[derive(Clone)] |
331 | | enum LookupInner<'a> { |
332 | | Format1(LazyArray16<'a, u16>), |
333 | | Format2(BinarySearchTable<'a, LookupSegment>), |
334 | | Format4(BinarySearchTable<'a, LookupSegment>, &'a [u8]), |
335 | | Format6(BinarySearchTable<'a, LookupSingle>), |
336 | | Format8 { |
337 | | first_glyph: u16, |
338 | | values: LazyArray16<'a, u16>, |
339 | | }, |
340 | | Format10 { |
341 | | value_size: u16, |
342 | | first_glyph: u16, |
343 | | glyph_count: u16, |
344 | | data: &'a [u8], |
345 | | }, |
346 | | } |
347 | | |
348 | | impl<'a> LookupInner<'a> { |
349 | 2.14k | fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> { |
350 | 2.14k | let mut s = Stream::new(data); |
351 | 2.14k | let format = s.read::<u16>()?; |
352 | 2.14k | match format { |
353 | | 0 => { |
354 | 887 | let values = s.read_array16::<u16>(number_of_glyphs.get())?; |
355 | 884 | Some(Self::Format1(values)) |
356 | | } |
357 | | 2 => { |
358 | 815 | let bsearch = BinarySearchTable::<LookupSegment>::parse(s.tail()?)?; |
359 | 445 | Some(Self::Format2(bsearch)) |
360 | | } |
361 | | 4 => { |
362 | 120 | let bsearch = BinarySearchTable::<LookupSegment>::parse(s.tail()?)?; |
363 | 41 | Some(Self::Format4(bsearch, data)) |
364 | | } |
365 | | 6 => { |
366 | 75 | let bsearch = BinarySearchTable::<LookupSingle>::parse(s.tail()?)?; |
367 | 16 | Some(Self::Format6(bsearch)) |
368 | | } |
369 | | 8 => { |
370 | 81 | let first_glyph = s.read::<u16>()?; |
371 | 79 | let glyph_count = s.read::<u16>()?; |
372 | 77 | let values = s.read_array16::<u16>(glyph_count)?; |
373 | 17 | Some(Self::Format8 { |
374 | 17 | first_glyph, |
375 | 17 | values, |
376 | 17 | }) |
377 | | } |
378 | | 10 => { |
379 | 37 | let value_size = s.read::<u16>()?; |
380 | 36 | let first_glyph = s.read::<u16>()?; |
381 | 34 | let glyph_count = s.read::<u16>()?; |
382 | | Some(Self::Format10 { |
383 | 10 | value_size, |
384 | 10 | first_glyph, |
385 | 10 | glyph_count, |
386 | 10 | data: s.tail()?, |
387 | | }) |
388 | | } |
389 | 128 | _ => None, |
390 | | } |
391 | 2.14k | } |
392 | | |
393 | 1.34k | fn value(&self, glyph_id: GlyphId) -> Option<u16> { |
394 | 1.34k | match self { |
395 | 883 | Self::Format1(values) => values.get(glyph_id.0), |
396 | 434 | Self::Format2(ref bsearch) => bsearch.get(glyph_id).map(|v| v.value), |
397 | 4 | Self::Format4(ref bsearch, data) => { |
398 | | // In format 4, LookupSegment contains an offset to a list of u16 values. |
399 | | // One value for each glyph in the LookupSegment range. |
400 | 4 | let segment = bsearch.get(glyph_id)?; |
401 | 4 | let index = glyph_id.0.checked_sub(segment.first_glyph)?; |
402 | 4 | let offset = usize::from(segment.value) + u16::SIZE * usize::from(index); |
403 | 4 | Stream::read_at::<u16>(data, offset) |
404 | | } |
405 | 2 | Self::Format6(ref bsearch) => bsearch.get(glyph_id).map(|v| v.value), |
406 | 13 | Self::Format8 { |
407 | 13 | first_glyph, |
408 | 13 | values, |
409 | 13 | } => { |
410 | 13 | let idx = glyph_id.0.checked_sub(*first_glyph)?; |
411 | 12 | values.get(idx) |
412 | | } |
413 | 6 | Self::Format10 { |
414 | 6 | value_size, |
415 | 6 | first_glyph, |
416 | 6 | glyph_count, |
417 | 6 | data, |
418 | 6 | } => { |
419 | 6 | let idx = glyph_id.0.checked_sub(*first_glyph)?; |
420 | 6 | let mut s = Stream::new(data); |
421 | 6 | match value_size { |
422 | 4 | 1 => s.read_array16::<u8>(*glyph_count)?.get(idx).map(u16::from), |
423 | 0 | 2 => s.read_array16::<u16>(*glyph_count)?.get(idx), |
424 | | // TODO: we should return u32 here, but this is not supported yet |
425 | 2 | 4 => s |
426 | 2 | .read_array16::<u32>(*glyph_count)? |
427 | 2 | .get(idx) |
428 | 2 | .map(|n| n as u16), |
429 | 0 | _ => None, // 8 is also supported |
430 | | } |
431 | | } |
432 | | } |
433 | 1.34k | } |
434 | | } |
435 | | |
436 | | /// A binary searching table as defined at |
437 | | /// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html |
438 | | #[derive(Clone)] |
439 | | struct BinarySearchTable<'a, T: BinarySearchValue> { |
440 | | values: LazyArray16<'a, T>, |
441 | | len: NonZeroU16, // values length excluding termination segment |
442 | | } |
443 | | |
444 | | impl<'a, T: BinarySearchValue + core::fmt::Debug> BinarySearchTable<'a, T> { |
445 | | #[inline(never)] |
446 | 1.01k | fn parse(data: &'a [u8]) -> Option<Self> { |
447 | 1.01k | let mut s = Stream::new(data); |
448 | 1.01k | let segment_size = s.read::<u16>()?; |
449 | 1.00k | let number_of_segments = s.read::<u16>()?; |
450 | 982 | s.advance(6); // search_range + entry_selector + range_shift |
451 | 982 | |
452 | 982 | if usize::from(segment_size) != T::SIZE { |
453 | 431 | return None; |
454 | 551 | } |
455 | 551 | |
456 | 551 | if number_of_segments == 0 { |
457 | 2 | return None; |
458 | 549 | } |
459 | | |
460 | 549 | let values = s.read_array16::<T>(number_of_segments)?; |
461 | | |
462 | | // 'The number of termination values that need to be included is table-specific. |
463 | | // The value that indicates binary search termination is 0xFFFF.' |
464 | 504 | let mut len = number_of_segments; |
465 | 504 | if values.last()?.is_termination() { |
466 | 329 | len = len.checked_sub(1)?; |
467 | 175 | } |
468 | | |
469 | | Some(BinarySearchTable { |
470 | 504 | len: NonZeroU16::new(len)?, |
471 | 502 | values, |
472 | | }) |
473 | 1.01k | } <ttf_parser::aat::BinarySearchTable<ttf_parser::aat::LookupSingle>>::parse Line | Count | Source | 446 | 75 | fn parse(data: &'a [u8]) -> Option<Self> { | 447 | 75 | let mut s = Stream::new(data); | 448 | 75 | let segment_size = s.read::<u16>()?; | 449 | 73 | let number_of_segments = s.read::<u16>()?; | 450 | 49 | s.advance(6); // search_range + entry_selector + range_shift | 451 | 49 | | 452 | 49 | if usize::from(segment_size) != T::SIZE { | 453 | 13 | return None; | 454 | 36 | } | 455 | 36 | | 456 | 36 | if number_of_segments == 0 { | 457 | 1 | return None; | 458 | 35 | } | 459 | | | 460 | 35 | let values = s.read_array16::<T>(number_of_segments)?; | 461 | | | 462 | | // 'The number of termination values that need to be included is table-specific. | 463 | | // The value that indicates binary search termination is 0xFFFF.' | 464 | 17 | let mut len = number_of_segments; | 465 | 17 | if values.last()?.is_termination() { | 466 | 13 | len = len.checked_sub(1)?; | 467 | 4 | } | 468 | | | 469 | | Some(BinarySearchTable { | 470 | 17 | len: NonZeroU16::new(len)?, | 471 | 16 | values, | 472 | | }) | 473 | 75 | } |
<ttf_parser::aat::BinarySearchTable<ttf_parser::aat::LookupSegment>>::parse Line | Count | Source | 446 | 935 | fn parse(data: &'a [u8]) -> Option<Self> { | 447 | 935 | let mut s = Stream::new(data); | 448 | 935 | let segment_size = s.read::<u16>()?; | 449 | 934 | let number_of_segments = s.read::<u16>()?; | 450 | 933 | s.advance(6); // search_range + entry_selector + range_shift | 451 | 933 | | 452 | 933 | if usize::from(segment_size) != T::SIZE { | 453 | 418 | return None; | 454 | 515 | } | 455 | 515 | | 456 | 515 | if number_of_segments == 0 { | 457 | 1 | return None; | 458 | 514 | } | 459 | | | 460 | 514 | let values = s.read_array16::<T>(number_of_segments)?; | 461 | | | 462 | | // 'The number of termination values that need to be included is table-specific. | 463 | | // The value that indicates binary search termination is 0xFFFF.' | 464 | 487 | let mut len = number_of_segments; | 465 | 487 | if values.last()?.is_termination() { | 466 | 316 | len = len.checked_sub(1)?; | 467 | 171 | } | 468 | | | 469 | | Some(BinarySearchTable { | 470 | 487 | len: NonZeroU16::new(len)?, | 471 | 486 | values, | 472 | | }) | 473 | 935 | } |
|
474 | | |
475 | 440 | fn get(&self, key: GlyphId) -> Option<T> { |
476 | 440 | let mut min = 0; |
477 | 440 | let mut max = (self.len.get() as isize) - 1; |
478 | 836 | while min <= max { |
479 | 715 | let mid = (min + max) / 2; |
480 | 715 | let v = self.values.get(mid as u16)?; |
481 | 715 | match v.contains(key) { |
482 | 237 | core::cmp::Ordering::Less => max = mid - 1, |
483 | 159 | core::cmp::Ordering::Greater => min = mid + 1, |
484 | 319 | core::cmp::Ordering::Equal => return Some(v), |
485 | | } |
486 | | } |
487 | | |
488 | 121 | None |
489 | 440 | } <ttf_parser::aat::BinarySearchTable<ttf_parser::aat::LookupSingle>>::get Line | Count | Source | 475 | 2 | fn get(&self, key: GlyphId) -> Option<T> { | 476 | 2 | let mut min = 0; | 477 | 2 | let mut max = (self.len.get() as isize) - 1; | 478 | 5 | while min <= max { | 479 | 4 | let mid = (min + max) / 2; | 480 | 4 | let v = self.values.get(mid as u16)?; | 481 | 4 | match v.contains(key) { | 482 | 3 | core::cmp::Ordering::Less => max = mid - 1, | 483 | 0 | core::cmp::Ordering::Greater => min = mid + 1, | 484 | 1 | core::cmp::Ordering::Equal => return Some(v), | 485 | | } | 486 | | } | 487 | | | 488 | 1 | None | 489 | 2 | } |
<ttf_parser::aat::BinarySearchTable<ttf_parser::aat::LookupSegment>>::get Line | Count | Source | 475 | 438 | fn get(&self, key: GlyphId) -> Option<T> { | 476 | 438 | let mut min = 0; | 477 | 438 | let mut max = (self.len.get() as isize) - 1; | 478 | 831 | while min <= max { | 479 | 711 | let mid = (min + max) / 2; | 480 | 711 | let v = self.values.get(mid as u16)?; | 481 | 711 | match v.contains(key) { | 482 | 234 | core::cmp::Ordering::Less => max = mid - 1, | 483 | 159 | core::cmp::Ordering::Greater => min = mid + 1, | 484 | 318 | core::cmp::Ordering::Equal => return Some(v), | 485 | | } | 486 | | } | 487 | | | 488 | 120 | None | 489 | 438 | } |
|
490 | | } |
491 | | |
492 | | trait BinarySearchValue: FromData { |
493 | | fn is_termination(&self) -> bool; |
494 | | fn contains(&self, glyph_id: GlyphId) -> core::cmp::Ordering; |
495 | | } |
496 | | |
497 | | #[derive(Clone, Copy, Debug)] |
498 | | struct LookupSegment { |
499 | | last_glyph: u16, |
500 | | first_glyph: u16, |
501 | | value: u16, |
502 | | } |
503 | | |
504 | | impl FromData for LookupSegment { |
505 | | const SIZE: usize = 6; |
506 | | |
507 | | #[inline] |
508 | 1.19k | fn parse(data: &[u8]) -> Option<Self> { |
509 | 1.19k | let mut s = Stream::new(data); |
510 | 1.19k | Some(LookupSegment { |
511 | 1.19k | last_glyph: s.read::<u16>()?, |
512 | 1.19k | first_glyph: s.read::<u16>()?, |
513 | 1.19k | value: s.read::<u16>()?, |
514 | | }) |
515 | 1.19k | } |
516 | | } |
517 | | |
518 | | impl BinarySearchValue for LookupSegment { |
519 | | #[inline] |
520 | 487 | fn is_termination(&self) -> bool { |
521 | 487 | self.last_glyph == 0xFFFF && self.first_glyph == 0xFFFF |
522 | 487 | } |
523 | | |
524 | | #[inline] |
525 | 711 | fn contains(&self, id: GlyphId) -> core::cmp::Ordering { |
526 | 711 | if id.0 < self.first_glyph { |
527 | 234 | core::cmp::Ordering::Less |
528 | 477 | } else if id.0 <= self.last_glyph { |
529 | 318 | core::cmp::Ordering::Equal |
530 | | } else { |
531 | 159 | core::cmp::Ordering::Greater |
532 | | } |
533 | 711 | } |
534 | | } |
535 | | |
536 | | #[derive(Clone, Copy, Debug)] |
537 | | struct LookupSingle { |
538 | | glyph: u16, |
539 | | value: u16, |
540 | | } |
541 | | |
542 | | impl FromData for LookupSingle { |
543 | | const SIZE: usize = 4; |
544 | | |
545 | | #[inline] |
546 | 21 | fn parse(data: &[u8]) -> Option<Self> { |
547 | 21 | let mut s = Stream::new(data); |
548 | 21 | Some(LookupSingle { |
549 | 21 | glyph: s.read::<u16>()?, |
550 | 21 | value: s.read::<u16>()?, |
551 | | }) |
552 | 21 | } |
553 | | } |
554 | | |
555 | | impl BinarySearchValue for LookupSingle { |
556 | | #[inline] |
557 | 17 | fn is_termination(&self) -> bool { |
558 | 17 | self.glyph == 0xFFFF |
559 | 17 | } |
560 | | |
561 | | #[inline] |
562 | 4 | fn contains(&self, id: GlyphId) -> core::cmp::Ordering { |
563 | 4 | id.0.cmp(&self.glyph) |
564 | 4 | } |
565 | | } |