Coverage Report

Created: 2026-05-24 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fontations/read-fonts/generated/generated_kerx.rs
Line
Count
Source
1
// THIS FILE IS AUTOGENERATED.
2
// Any changes to this file will be overwritten.
3
// For more information about how codegen works, see font-codegen/README.md
4
5
#[allow(unused_imports)]
6
use crate::codegen_prelude::*;
7
8
impl<'a> MinByteRange<'a> for Kerx<'a> {
9
0
    fn min_byte_range(&self) -> Range<usize> {
10
0
        0..self.subtables_byte_range().end
11
0
    }
12
0
    fn min_table_bytes(&self) -> &'a [u8] {
13
0
        let range = self.min_byte_range();
14
0
        self.data.as_bytes().get(range).unwrap_or_default()
15
0
    }
16
}
17
18
impl TopLevelTable for Kerx<'_> {
19
    /// `kerx`
20
    const TAG: Tag = Tag::new(b"kerx");
21
}
22
23
impl<'a> FontRead<'a> for Kerx<'a> {
24
0
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
25
        #[allow(clippy::absurd_extreme_comparisons)]
26
0
        if data.len() < Self::MIN_SIZE {
27
0
            return Err(ReadError::OutOfBounds);
28
0
        }
29
0
        Ok(Self { data })
30
0
    }
31
}
32
33
/// The [kerx (Extended Kerning)](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html) table.
34
#[derive(Clone)]
35
pub struct Kerx<'a> {
36
    data: FontData<'a>,
37
}
38
39
#[allow(clippy::needless_lifetimes)]
40
impl<'a> Kerx<'a> {
41
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u32::RAW_BYTE_LEN);
42
    basic_table_impls!(impl_the_methods);
43
44
    /// The version number of the extended kerning table (currently 2, 3, or 4)
45
0
    pub fn version(&self) -> u16 {
46
0
        let range = self.version_byte_range();
47
0
        self.data.read_at(range.start).ok().unwrap()
48
0
    }
49
50
    /// The number of subtables included in the extended kerning table.
51
0
    pub fn n_tables(&self) -> u32 {
52
0
        let range = self.n_tables_byte_range();
53
0
        self.data.read_at(range.start).ok().unwrap()
54
0
    }
55
56
0
    pub fn subtables(&self) -> VarLenArray<'a, Subtable<'a>> {
57
0
        let range = self.subtables_byte_range();
58
0
        self.data
59
0
            .split_off(range.start)
60
0
            .and_then(|d| VarLenArray::read(d).ok())
61
0
            .unwrap_or_default()
62
0
    }
63
64
0
    pub fn version_byte_range(&self) -> Range<usize> {
65
0
        let start = 0;
66
0
        start..start + u16::RAW_BYTE_LEN
67
0
    }
68
69
0
    pub fn padding_byte_range(&self) -> Range<usize> {
70
0
        let start = self.version_byte_range().end;
71
0
        start..start + u16::RAW_BYTE_LEN
72
0
    }
73
74
0
    pub fn n_tables_byte_range(&self) -> Range<usize> {
75
0
        let start = self.padding_byte_range().end;
76
0
        start..start + u32::RAW_BYTE_LEN
77
0
    }
78
79
0
    pub fn subtables_byte_range(&self) -> Range<usize> {
80
0
        let n_tables = self.n_tables();
81
0
        let start = self.n_tables_byte_range().end;
82
0
        start..start + {
83
0
            let data = self.data.split_off(start).unwrap_or_default();
84
0
            <Subtable as VarSize>::total_len_for_count(data, n_tables as usize).unwrap_or(0)
85
0
        }
86
0
    }
87
}
88
89
#[cfg(feature = "experimental_traverse")]
90
impl<'a> SomeTable<'a> for Kerx<'a> {
91
    fn type_name(&self) -> &str {
92
        "Kerx"
93
    }
94
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
95
        match idx {
96
            0usize => Some(Field::new("version", self.version())),
97
            1usize => Some(Field::new("n_tables", self.n_tables())),
98
            2usize => Some(Field::new(
99
                "subtables",
100
                traversal::FieldType::var_array("Subtable", self.subtables(), self.offset_data()),
101
            )),
102
            _ => None,
103
        }
104
    }
105
}
106
107
#[cfg(feature = "experimental_traverse")]
108
#[allow(clippy::needless_lifetimes)]
109
impl<'a> std::fmt::Debug for Kerx<'a> {
110
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111
        (self as &dyn SomeTable<'a>).fmt(f)
112
    }
113
}
114
115
impl<'a> MinByteRange<'a> for Subtable<'a> {
116
0
    fn min_byte_range(&self) -> Range<usize> {
117
0
        0..self.data_byte_range().end
118
0
    }
119
0
    fn min_table_bytes(&self) -> &'a [u8] {
120
0
        let range = self.min_byte_range();
121
0
        self.data.as_bytes().get(range).unwrap_or_default()
122
0
    }
123
}
124
125
impl<'a> FontRead<'a> for Subtable<'a> {
126
0
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
127
        #[allow(clippy::absurd_extreme_comparisons)]
128
0
        if data.len() < Self::MIN_SIZE {
129
0
            return Err(ReadError::OutOfBounds);
130
0
        }
131
0
        Ok(Self { data })
132
0
    }
133
}
134
135
/// A subtable in a `kerx` table.
136
#[derive(Clone)]
137
pub struct Subtable<'a> {
138
    data: FontData<'a>,
139
}
140
141
#[allow(clippy::needless_lifetimes)]
142
impl<'a> Subtable<'a> {
143
    pub const MIN_SIZE: usize = (u32::RAW_BYTE_LEN + u32::RAW_BYTE_LEN + u32::RAW_BYTE_LEN);
144
    basic_table_impls!(impl_the_methods);
145
146
    /// The length of this subtable in bytes, including this header.
147
0
    pub fn length(&self) -> u32 {
148
0
        let range = self.length_byte_range();
149
0
        self.data.read_at(range.start).ok().unwrap()
150
0
    }
151
152
    /// Circumstances under which this table is used.
153
0
    pub fn coverage(&self) -> u32 {
154
0
        let range = self.coverage_byte_range();
155
0
        self.data.read_at(range.start).ok().unwrap()
156
0
    }
157
158
    /// The tuple count. This value is only used with variation fonts and should be 0 for all other fonts. The subtable's tupleCount will be ignored if the 'kerx' table version is less than 4.
159
0
    pub fn tuple_count(&self) -> u32 {
160
0
        let range = self.tuple_count_byte_range();
161
0
        self.data.read_at(range.start).ok().unwrap()
162
0
    }
163
164
    /// Subtable specific data.
165
0
    pub fn data(&self) -> &'a [u8] {
166
0
        let range = self.data_byte_range();
167
0
        self.data.read_array(range).ok().unwrap_or_default()
168
0
    }
169
170
0
    pub fn length_byte_range(&self) -> Range<usize> {
171
0
        let start = 0;
172
0
        start..start + u32::RAW_BYTE_LEN
173
0
    }
174
175
0
    pub fn coverage_byte_range(&self) -> Range<usize> {
176
0
        let start = self.length_byte_range().end;
177
0
        start..start + u32::RAW_BYTE_LEN
178
0
    }
179
180
0
    pub fn tuple_count_byte_range(&self) -> Range<usize> {
181
0
        let start = self.coverage_byte_range().end;
182
0
        start..start + u32::RAW_BYTE_LEN
183
0
    }
184
185
0
    pub fn data_byte_range(&self) -> Range<usize> {
186
0
        let start = self.tuple_count_byte_range().end;
187
0
        start..start + self.data.len().saturating_sub(start) / u8::RAW_BYTE_LEN * u8::RAW_BYTE_LEN
188
0
    }
189
}
190
191
#[cfg(feature = "experimental_traverse")]
192
impl<'a> SomeTable<'a> for Subtable<'a> {
193
    fn type_name(&self) -> &str {
194
        "Subtable"
195
    }
196
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
197
        match idx {
198
            0usize => Some(Field::new("length", self.length())),
199
            1usize => Some(Field::new("coverage", self.coverage())),
200
            2usize => Some(Field::new("tuple_count", self.tuple_count())),
201
            3usize => Some(Field::new("data", self.data())),
202
            _ => None,
203
        }
204
    }
205
}
206
207
#[cfg(feature = "experimental_traverse")]
208
#[allow(clippy::needless_lifetimes)]
209
impl<'a> std::fmt::Debug for Subtable<'a> {
210
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211
        (self as &dyn SomeTable<'a>).fmt(f)
212
    }
213
}
214
215
impl<'a> MinByteRange<'a> for Subtable0<'a> {
216
0
    fn min_byte_range(&self) -> Range<usize> {
217
0
        0..self.pairs_byte_range().end
218
0
    }
219
0
    fn min_table_bytes(&self) -> &'a [u8] {
220
0
        let range = self.min_byte_range();
221
0
        self.data.as_bytes().get(range).unwrap_or_default()
222
0
    }
223
}
224
225
impl<'a> FontRead<'a> for Subtable0<'a> {
226
0
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
227
        #[allow(clippy::absurd_extreme_comparisons)]
228
0
        if data.len() < Self::MIN_SIZE {
229
0
            return Err(ReadError::OutOfBounds);
230
0
        }
231
0
        Ok(Self { data })
232
0
    }
233
}
234
235
/// The type 0 `kerx` subtable.
236
#[derive(Clone)]
237
pub struct Subtable0<'a> {
238
    data: FontData<'a>,
239
}
240
241
#[allow(clippy::needless_lifetimes)]
242
impl<'a> Subtable0<'a> {
243
    pub const MIN_SIZE: usize =
244
        (u32::RAW_BYTE_LEN + u32::RAW_BYTE_LEN + u32::RAW_BYTE_LEN + u32::RAW_BYTE_LEN);
245
    basic_table_impls!(impl_the_methods);
246
247
    /// The number of kerning pairs in this subtable.
248
0
    pub fn n_pairs(&self) -> u32 {
249
0
        let range = self.n_pairs_byte_range();
250
0
        self.data.read_at(range.start).ok().unwrap()
251
0
    }
252
253
    /// The largest power of two less than or equal to the value of nPairs, multiplied by the size in bytes of an entry in the subtable.
254
0
    pub fn search_range(&self) -> u32 {
255
0
        let range = self.search_range_byte_range();
256
0
        self.data.read_at(range.start).ok().unwrap()
257
0
    }
258
259
    /// This is calculated as log2 of the largest power of two less than or equal to the value of nPairs. This value indicates how many iterations of the search loop have to be made. For example, in a list of eight items, there would be three iterations of the loop.
260
0
    pub fn entry_selector(&self) -> u32 {
261
0
        let range = self.entry_selector_byte_range();
262
0
        self.data.read_at(range.start).ok().unwrap()
263
0
    }
264
265
    /// The value of nPairs minus the largest power of two less than or equal to nPairs. This is multiplied by the size in bytes of an entry in the table.
266
0
    pub fn range_shift(&self) -> u32 {
267
0
        let range = self.range_shift_byte_range();
268
0
        self.data.read_at(range.start).ok().unwrap()
269
0
    }
270
271
    /// Kerning records.
272
0
    pub fn pairs(&self) -> &'a [Subtable0Pair] {
273
0
        let range = self.pairs_byte_range();
274
0
        self.data.read_array(range).ok().unwrap_or_default()
275
0
    }
276
277
0
    pub fn n_pairs_byte_range(&self) -> Range<usize> {
278
0
        let start = 0;
279
0
        start..start + u32::RAW_BYTE_LEN
280
0
    }
281
282
0
    pub fn search_range_byte_range(&self) -> Range<usize> {
283
0
        let start = self.n_pairs_byte_range().end;
284
0
        start..start + u32::RAW_BYTE_LEN
285
0
    }
286
287
0
    pub fn entry_selector_byte_range(&self) -> Range<usize> {
288
0
        let start = self.search_range_byte_range().end;
289
0
        start..start + u32::RAW_BYTE_LEN
290
0
    }
291
292
0
    pub fn range_shift_byte_range(&self) -> Range<usize> {
293
0
        let start = self.entry_selector_byte_range().end;
294
0
        start..start + u32::RAW_BYTE_LEN
295
0
    }
296
297
0
    pub fn pairs_byte_range(&self) -> Range<usize> {
298
0
        let n_pairs = self.n_pairs();
299
0
        let start = self.range_shift_byte_range().end;
300
0
        start..start + (n_pairs as usize).saturating_mul(Subtable0Pair::RAW_BYTE_LEN)
301
0
    }
302
}
303
304
#[cfg(feature = "experimental_traverse")]
305
impl<'a> SomeTable<'a> for Subtable0<'a> {
306
    fn type_name(&self) -> &str {
307
        "Subtable0"
308
    }
309
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
310
        match idx {
311
            0usize => Some(Field::new("n_pairs", self.n_pairs())),
312
            1usize => Some(Field::new("search_range", self.search_range())),
313
            2usize => Some(Field::new("entry_selector", self.entry_selector())),
314
            3usize => Some(Field::new("range_shift", self.range_shift())),
315
            4usize => Some(Field::new(
316
                "pairs",
317
                traversal::FieldType::array_of_records(
318
                    stringify!(Subtable0Pair),
319
                    self.pairs(),
320
                    self.offset_data(),
321
                ),
322
            )),
323
            _ => None,
324
        }
325
    }
326
}
327
328
#[cfg(feature = "experimental_traverse")]
329
#[allow(clippy::needless_lifetimes)]
330
impl<'a> std::fmt::Debug for Subtable0<'a> {
331
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
332
        (self as &dyn SomeTable<'a>).fmt(f)
333
    }
334
}
335
336
/// The type 0 `kerx` subtable kerning record.
337
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
338
#[repr(C)]
339
#[repr(packed)]
340
pub struct Subtable0Pair {
341
    /// The glyph index for the lefthand glyph in the kerning pair.
342
    pub left: BigEndian<GlyphId16>,
343
    /// The glyph index for the righthand glyph in the kerning pair.
344
    pub right: BigEndian<GlyphId16>,
345
    /// Kerning value.
346
    pub value: BigEndian<i16>,
347
}
348
349
impl Subtable0Pair {
350
    /// The glyph index for the lefthand glyph in the kerning pair.
351
0
    pub fn left(&self) -> GlyphId16 {
352
0
        self.left.get()
353
0
    }
354
355
    /// The glyph index for the righthand glyph in the kerning pair.
356
0
    pub fn right(&self) -> GlyphId16 {
357
0
        self.right.get()
358
0
    }
359
360
    /// Kerning value.
361
0
    pub fn value(&self) -> i16 {
362
0
        self.value.get()
363
0
    }
364
}
365
366
impl FixedSize for Subtable0Pair {
367
    const RAW_BYTE_LEN: usize =
368
        GlyphId16::RAW_BYTE_LEN + GlyphId16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN;
369
}
370
371
#[cfg(feature = "experimental_traverse")]
372
impl<'a> SomeRecord<'a> for Subtable0Pair {
373
    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
374
        RecordResolver {
375
            name: "Subtable0Pair",
376
            get_field: Box::new(move |idx, _data| match idx {
377
                0usize => Some(Field::new("left", self.left())),
378
                1usize => Some(Field::new("right", self.right())),
379
                2usize => Some(Field::new("value", self.value())),
380
                _ => None,
381
            }),
382
            data,
383
        }
384
    }
385
}