Coverage Report

Created: 2026-10-24 07:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/read-fonts-0.46.0/generated/generated_gsub.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 Gsub<'a> {
9
0
    fn min_byte_range(&self) -> Range<usize> {
10
0
        0..self.lookup_list_offset_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.offset_data().as_bytes().get(range).unwrap_or_default()
15
0
    }
16
}
17
18
impl TopLevelTable for Gsub<'_> {
19
    /// `GSUB`
20
    const TAG: Tag = Tag::new(b"GSUB");
21
}
22
23
impl ReadArgs for Gsub<'_> {
24
    type Args = ();
25
}
26
27
impl<'a> FontRead<'a> for Gsub<'a> {
28
162k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
29
        #[allow(clippy::absurd_extreme_comparisons)]
30
162k
        if data.len() < Self::MIN_SIZE {
31
8
            return Err(ReadError::OutOfBounds);
32
162k
        }
33
162k
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
34
0
            return Err(ReadError::OutOfBounds);
35
        };
36
162k
        Ok(Self { data })
37
162k
    }
38
}
39
40
/// The fields at the start of the table, which are read directly.
41
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
42
#[repr(C, packed)]
43
#[doc(hidden)]
44
pub struct GsubFixedHeader {
45
    pub version: BigEndian<MajorMinor>,
46
    pub script_list_offset: BigEndian<Nullable<Offset16>>,
47
    pub feature_list_offset: BigEndian<Nullable<Offset16>>,
48
    pub lookup_list_offset: BigEndian<Nullable<Offset16>>,
49
}
50
51
#[allow(clippy::absurd_extreme_comparisons)]
52
const _: () = assert!(std::mem::size_of::<GsubFixedHeader>() <= Gsub::MIN_SIZE);
53
54
/// [GSUB](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#gsub-header)
55
#[derive(Clone)]
56
pub struct Gsub<'a> {
57
    data: TableDataRef<'a, GsubFixedHeader>,
58
}
59
60
#[allow(clippy::needless_lifetimes)]
61
impl<'a> Gsub<'a> {
62
    pub const MIN_SIZE: usize = (MajorMinor::RAW_BYTE_LEN
63
        + Offset16::RAW_BYTE_LEN
64
        + Offset16::RAW_BYTE_LEN
65
        + Offset16::RAW_BYTE_LEN);
66
    basic_table_impls!(impl_the_methods);
67
68
    /// The major and minor version of the GSUB table, as a tuple (u16, u16)
69
3.72M
    pub fn version(&self) -> MajorMinor {
70
3.72M
        self.data.header().version.get()
71
3.72M
    }
72
73
    /// Offset to ScriptList table, from beginning of GSUB table
74
425k
    pub fn script_list_offset(&self) -> Nullable<Offset16> {
75
425k
        self.data.header().script_list_offset.get()
76
425k
    }
77
78
    /// Offset to FeatureList table, from beginning of GSUB table
79
408k
    pub fn feature_list_offset(&self) -> Nullable<Offset16> {
80
408k
        self.data.header().feature_list_offset.get()
81
408k
    }
82
83
    /// Offset to LookupList table, from beginning of GSUB table
84
162k
    pub fn lookup_list_offset(&self) -> Nullable<Offset16> {
85
162k
        self.data.header().lookup_list_offset.get()
86
162k
    }
87
88
    /// Offset to FeatureVariations table, from beginning of the GSUB
89
    /// table (may be NULL)
90
7.93k
    pub fn feature_variations_offset(&self) -> Option<Nullable<Offset32>> {
91
7.93k
        let range = self.feature_variations_offset_byte_range();
92
7.93k
        (!range.is_empty())
93
7.93k
            .then(|| self.offset_data().read_at(range.start).ok())
94
7.93k
            .flatten()
95
7.93k
    }
96
97
    /// Attempt to resolve [`feature_variations_offset`][Self::feature_variations_offset].
98
7.93k
    pub fn feature_variations(&self) -> Option<Result<FeatureVariations<'a>, ReadError>> {
99
7.93k
        let data = self.offset_data();
100
7.93k
        self.feature_variations_offset().map(|x| x.resolve(data))?
101
7.93k
    }
102
103
    /// 32-bit offset to ScriptList, taking precedence when nonzero.
104
425k
    pub fn script_list2_offset(&self) -> Option<Nullable<Offset32>> {
105
425k
        let range = self.script_list2_offset_byte_range();
106
425k
        (!range.is_empty())
107
425k
            .then(|| self.offset_data().read_at(range.start).ok())
108
425k
            .flatten()
109
425k
    }
110
111
    /// Attempt to resolve [`script_list2_offset`][Self::script_list2_offset].
112
0
    pub fn script_list2(&self) -> Option<Result<ScriptList<'a>, ReadError>> {
113
0
        let data = self.offset_data();
114
0
        self.script_list2_offset().map(|x| x.resolve(data))?
115
0
    }
116
117
    /// 32-bit offset to FeatureList, taking precedence when nonzero.
118
408k
    pub fn feature_list2_offset(&self) -> Option<Nullable<Offset32>> {
119
408k
        let range = self.feature_list2_offset_byte_range();
120
408k
        (!range.is_empty())
121
408k
            .then(|| self.offset_data().read_at(range.start).ok())
122
408k
            .flatten()
123
408k
    }
124
125
    /// Attempt to resolve [`feature_list2_offset`][Self::feature_list2_offset].
126
0
    pub fn feature_list2(&self) -> Option<Result<FeatureList<'a>, ReadError>> {
127
0
        let data = self.offset_data();
128
0
        self.feature_list2_offset().map(|x| x.resolve(data))?
129
0
    }
130
131
    /// 32-bit offset to LookupList2, taking precedence when nonzero.
132
162k
    pub fn lookup_list2_offset(&self) -> Option<Nullable<Offset32>> {
133
162k
        let range = self.lookup_list2_offset_byte_range();
134
162k
        (!range.is_empty())
135
162k
            .then(|| self.offset_data().read_at(range.start).ok())
136
162k
            .flatten()
137
162k
    }
138
139
    /// Attempt to resolve [`lookup_list2_offset`][Self::lookup_list2_offset].
140
0
    pub fn lookup_list2(&self) -> Option<Result<SubstitutionLookupList2<'a>, ReadError>> {
141
0
        let data = self.offset_data();
142
0
        self.lookup_list2_offset().map(|x| x.resolve(data))?
143
0
    }
144
145
1.00M
    pub fn version_byte_range(&self) -> Range<usize> {
146
1.00M
        let start = 0;
147
1.00M
        let end = start + MajorMinor::RAW_BYTE_LEN;
148
1.00M
        start..end
149
1.00M
    }
150
151
1.00M
    pub fn script_list_offset_byte_range(&self) -> Range<usize> {
152
1.00M
        let start = self.version_byte_range().end;
153
1.00M
        let end = start + Offset16::RAW_BYTE_LEN;
154
1.00M
        start..end
155
1.00M
    }
156
157
1.00M
    pub fn feature_list_offset_byte_range(&self) -> Range<usize> {
158
1.00M
        let start = self.script_list_offset_byte_range().end;
159
1.00M
        let end = start + Offset16::RAW_BYTE_LEN;
160
1.00M
        start..end
161
1.00M
    }
162
163
1.00M
    pub fn lookup_list_offset_byte_range(&self) -> Range<usize> {
164
1.00M
        let start = self.feature_list_offset_byte_range().end;
165
1.00M
        let end = start + Offset16::RAW_BYTE_LEN;
166
1.00M
        start..end
167
1.00M
    }
168
169
1.00M
    pub fn feature_variations_offset_byte_range(&self) -> Range<usize> {
170
1.00M
        let start = self.lookup_list_offset_byte_range().end;
171
1.00M
        let end = if self.version().compatible((1u16, 1u16)) {
172
567
            start + Offset32::RAW_BYTE_LEN
173
        } else {
174
1.00M
            start
175
        };
176
1.00M
        start..end
177
1.00M
    }
178
179
995k
    pub fn script_list2_offset_byte_range(&self) -> Range<usize> {
180
995k
        let start = self.feature_variations_offset_byte_range().end;
181
995k
        let end = if self.version().compatible((1u16, 2u16)) {
182
513
            start + Offset32::RAW_BYTE_LEN
183
        } else {
184
995k
            start
185
        };
186
995k
        start..end
187
995k
    }
188
189
570k
    pub fn feature_list2_offset_byte_range(&self) -> Range<usize> {
190
570k
        let start = self.script_list2_offset_byte_range().end;
191
570k
        let end = if self.version().compatible((1u16, 2u16)) {
192
205
            start + Offset32::RAW_BYTE_LEN
193
        } else {
194
570k
            start
195
        };
196
570k
        start..end
197
570k
    }
198
199
162k
    pub fn lookup_list2_offset_byte_range(&self) -> Range<usize> {
200
162k
        let start = self.feature_list2_offset_byte_range().end;
201
162k
        let end = if self.version().compatible((1u16, 2u16)) {
202
43
            start + Offset32::RAW_BYTE_LEN
203
        } else {
204
162k
            start
205
        };
206
162k
        start..end
207
162k
    }
208
}
209
210
const _: () = assert!(FontData::default_data_long_enough(Gsub::MIN_SIZE));
211
212
impl Default for Gsub<'_> {
213
0
    fn default() -> Self {
214
        Self {
215
0
            data: TableDataRef::new(FontData::default_table_data().as_bytes())
216
0
                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
217
        }
218
0
    }
219
}
220
221
/// A [GSUB Lookup](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#gsubLookupTypeEnum) subtable.
222
pub enum SubstitutionLookup<'a> {
223
    Single(Lookup<'a, SingleSubst<'a>>),
224
    Multiple(Lookup<'a, MultipleSubst<'a>>),
225
    Alternate(Lookup<'a, AlternateSubst<'a>>),
226
    Ligature(Lookup<'a, LigatureSubst<'a>>),
227
    Contextual(Lookup<'a, SubstitutionSequenceContext<'a>>),
228
    ChainContextual(Lookup<'a, SubstitutionChainContext<'a>>),
229
    Extension(Lookup<'a, ExtensionSubtable<'a>>),
230
    Reverse(Lookup<'a, ReverseChainSingleSubst<'a>>),
231
}
232
233
impl Default for SubstitutionLookup<'_> {
234
0
    fn default() -> Self {
235
0
        Self::Single(Default::default())
236
0
    }
237
}
238
239
impl ReadArgs for SubstitutionLookup<'_> {
240
    type Args = ();
241
}
242
243
impl<'a> FontRead<'a> for SubstitutionLookup<'a> {
244
0
    fn read_with_args(bytes: FontData<'a>, _: ()) -> Result<Self, ReadError> {
245
0
        let discriminant = Lookup::read_discriminant(bytes)?;
246
0
        match discriminant {
247
0
            1 => Ok(SubstitutionLookup::Single(FontRead::read(bytes)?)),
248
0
            2 => Ok(SubstitutionLookup::Multiple(FontRead::read(bytes)?)),
249
0
            3 => Ok(SubstitutionLookup::Alternate(FontRead::read(bytes)?)),
250
0
            4 => Ok(SubstitutionLookup::Ligature(FontRead::read(bytes)?)),
251
0
            5 => Ok(SubstitutionLookup::Contextual(FontRead::read(bytes)?)),
252
0
            6 => Ok(SubstitutionLookup::ChainContextual(FontRead::read(bytes)?)),
253
0
            7 => Ok(SubstitutionLookup::Extension(FontRead::read(bytes)?)),
254
0
            8 => Ok(SubstitutionLookup::Reverse(FontRead::read(bytes)?)),
255
0
            other => Err(ReadError::InvalidFormat(other.into())),
256
        }
257
0
    }
258
}
259
260
impl<'a> SubstitutionLookup<'a> {
261
    #[allow(dead_code)]
262
    /// Return the inner table, removing the specific generics.
263
    ///
264
    /// This lets us return a single concrete type we can call methods on.
265
0
    pub(crate) fn of_unit_type(&self) -> Lookup<'a, ()> {
266
0
        match self {
267
0
            SubstitutionLookup::Single(inner) => inner.of_unit_type(),
268
0
            SubstitutionLookup::Multiple(inner) => inner.of_unit_type(),
269
0
            SubstitutionLookup::Alternate(inner) => inner.of_unit_type(),
270
0
            SubstitutionLookup::Ligature(inner) => inner.of_unit_type(),
271
0
            SubstitutionLookup::Contextual(inner) => inner.of_unit_type(),
272
0
            SubstitutionLookup::ChainContextual(inner) => inner.of_unit_type(),
273
0
            SubstitutionLookup::Extension(inner) => inner.of_unit_type(),
274
0
            SubstitutionLookup::Reverse(inner) => inner.of_unit_type(),
275
        }
276
0
    }
277
}
278
279
/// LookupType 1: [Single Substitution](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#lookuptype-1-single-substitution-subtable) Subtable
280
#[derive(Clone)]
281
pub enum SingleSubst<'a> {
282
    Format1(SingleSubstFormat1<'a>),
283
    Format2(SingleSubstFormat2<'a>),
284
    Format3(SingleSubstFormat3<'a>),
285
    Format4(SingleSubstFormat4<'a>),
286
}
287
288
impl Default for SingleSubst<'_> {
289
0
    fn default() -> Self {
290
0
        Self::Format1(Default::default())
291
0
    }
292
}
293
294
impl<'a> SingleSubst<'a> {
295
    ///Return the `FontData` used to resolve offsets for this table.
296
0
    pub fn offset_data(&self) -> FontData<'a> {
297
0
        match self {
298
0
            Self::Format1(item) => item.offset_data(),
299
0
            Self::Format2(item) => item.offset_data(),
300
0
            Self::Format3(item) => item.offset_data(),
301
0
            Self::Format4(item) => item.offset_data(),
302
        }
303
0
    }
304
305
    /// Format identifier: format = 1
306
0
    pub fn subst_format(&self) -> u16 {
307
0
        match self {
308
0
            Self::Format1(item) => item.subst_format(),
309
0
            Self::Format2(item) => item.subst_format(),
310
0
            Self::Format3(item) => item.subst_format(),
311
0
            Self::Format4(item) => item.subst_format(),
312
        }
313
0
    }
314
}
315
316
impl ReadArgs for SingleSubst<'_> {
317
    type Args = ();
318
}
319
320
impl<'a> FontRead<'a> for SingleSubst<'a> {
321
    #[inline]
322
71.9k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
323
71.9k
        let format: u16 = data.read_at(0usize)?;
324
71.6k
        match format {
325
23.6k
            SingleSubstFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
326
3.55k
            SingleSubstFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
327
945
            SingleSubstFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
328
983
            SingleSubstFormat4::FORMAT => Ok(Self::Format4(FontRead::read(data)?)),
329
42.5k
            other => Err(ReadError::InvalidFormat(other.into())),
330
        }
331
71.9k
    }
<read_fonts::tables::gsub::SingleSubst as read_fonts::read::FontRead>::read_with_args
Line
Count
Source
322
71.9k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
323
71.9k
        let format: u16 = data.read_at(0usize)?;
324
71.6k
        match format {
325
23.6k
            SingleSubstFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
326
3.55k
            SingleSubstFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
327
945
            SingleSubstFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
328
983
            SingleSubstFormat4::FORMAT => Ok(Self::Format4(FontRead::read(data)?)),
329
42.5k
            other => Err(ReadError::InvalidFormat(other.into())),
330
        }
331
71.9k
    }
Unexecuted instantiation: <read_fonts::tables::gsub::SingleSubst as read_fonts::read::FontRead>::read_with_args
332
}
333
334
impl<'a> MinByteRange<'a> for SingleSubst<'a> {
335
0
    fn min_byte_range(&self) -> Range<usize> {
336
0
        match self {
337
0
            Self::Format1(item) => item.min_byte_range(),
338
0
            Self::Format2(item) => item.min_byte_range(),
339
0
            Self::Format3(item) => item.min_byte_range(),
340
0
            Self::Format4(item) => item.min_byte_range(),
341
        }
342
0
    }
343
0
    fn min_table_bytes(&self) -> &'a [u8] {
344
0
        match self {
345
0
            Self::Format1(item) => item.min_table_bytes(),
346
0
            Self::Format2(item) => item.min_table_bytes(),
347
0
            Self::Format3(item) => item.min_table_bytes(),
348
0
            Self::Format4(item) => item.min_table_bytes(),
349
        }
350
0
    }
351
}
352
353
impl Format<u16> for SingleSubstFormat1<'_> {
354
    const FORMAT: u16 = 1;
355
}
356
357
impl<'a> MinByteRange<'a> for SingleSubstFormat1<'a> {
358
0
    fn min_byte_range(&self) -> Range<usize> {
359
0
        0..self.delta_glyph_id_byte_range().end
360
0
    }
361
0
    fn min_table_bytes(&self) -> &'a [u8] {
362
0
        let range = self.min_byte_range();
363
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
364
0
    }
365
}
366
367
impl ReadArgs for SingleSubstFormat1<'_> {
368
    type Args = ();
369
}
370
371
impl<'a> FontRead<'a> for SingleSubstFormat1<'a> {
372
23.6k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
373
        #[allow(clippy::absurd_extreme_comparisons)]
374
23.6k
        if data.len() < Self::MIN_SIZE {
375
595
            return Err(ReadError::OutOfBounds);
376
23.0k
        }
377
23.0k
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
378
0
            return Err(ReadError::OutOfBounds);
379
        };
380
23.0k
        Ok(Self { data })
381
23.6k
    }
382
}
383
384
/// The fields at the start of the table, which are read directly.
385
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
386
#[repr(C, packed)]
387
#[doc(hidden)]
388
pub struct SingleSubstFormat1FixedHeader {
389
    pub subst_format: BigEndian<u16>,
390
    pub coverage_offset: BigEndian<Offset16>,
391
    pub delta_glyph_id: BigEndian<i16>,
392
}
393
394
#[allow(clippy::absurd_extreme_comparisons)]
395
const _: () =
396
    assert!(std::mem::size_of::<SingleSubstFormat1FixedHeader>() <= SingleSubstFormat1::MIN_SIZE);
397
398
/// [Single Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#11-single-substitution-format-1)
399
#[derive(Clone)]
400
pub struct SingleSubstFormat1<'a> {
401
    data: TableDataRef<'a, SingleSubstFormat1FixedHeader>,
402
}
403
404
#[allow(clippy::needless_lifetimes)]
405
impl<'a> SingleSubstFormat1<'a> {
406
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN);
407
    basic_table_impls!(impl_the_methods);
408
409
    /// Format identifier: format = 1
410
0
    pub fn subst_format(&self) -> u16 {
411
0
        self.data.header().subst_format.get()
412
0
    }
413
414
    /// Offset to Coverage table, from beginning of substitution
415
    /// subtable
416
23.0k
    pub fn coverage_offset(&self) -> Offset16 {
417
23.0k
        self.data.header().coverage_offset.get()
418
23.0k
    }
419
420
    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
421
23.0k
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
422
23.0k
        let data = self.offset_data();
423
23.0k
        self.coverage_offset().resolve(data)
424
23.0k
    }
425
426
    /// Add to original glyph ID to get substitute glyph ID
427
23.0k
    pub fn delta_glyph_id(&self) -> i16 {
428
23.0k
        self.data.header().delta_glyph_id.get()
429
23.0k
    }
430
431
0
    pub fn subst_format_byte_range(&self) -> Range<usize> {
432
0
        let start = 0;
433
0
        let end = start + u16::RAW_BYTE_LEN;
434
0
        start..end
435
0
    }
436
437
0
    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
438
0
        let start = self.subst_format_byte_range().end;
439
0
        let end = start + Offset16::RAW_BYTE_LEN;
440
0
        start..end
441
0
    }
442
443
0
    pub fn delta_glyph_id_byte_range(&self) -> Range<usize> {
444
0
        let start = self.coverage_offset_byte_range().end;
445
0
        let end = start + i16::RAW_BYTE_LEN;
446
0
        start..end
447
0
    }
448
}
449
450
const _: () = assert!(FontData::default_data_long_enough(
451
    SingleSubstFormat1::MIN_SIZE
452
));
453
454
impl Default for SingleSubstFormat1<'_> {
455
0
    fn default() -> Self {
456
        Self {
457
0
            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
458
0
                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
459
        }
460
0
    }
461
}
462
463
impl Format<u16> for SingleSubstFormat2<'_> {
464
    const FORMAT: u16 = 2;
465
}
466
467
impl<'a> MinByteRange<'a> for SingleSubstFormat2<'a> {
468
0
    fn min_byte_range(&self) -> Range<usize> {
469
0
        0..self.substitute_glyph_ids_byte_range().end
470
0
    }
471
0
    fn min_table_bytes(&self) -> &'a [u8] {
472
0
        let range = self.min_byte_range();
473
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
474
0
    }
475
}
476
477
impl ReadArgs for SingleSubstFormat2<'_> {
478
    type Args = ();
479
}
480
481
impl<'a> FontRead<'a> for SingleSubstFormat2<'a> {
482
3.55k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
483
        #[allow(clippy::absurd_extreme_comparisons)]
484
3.55k
        if data.len() < Self::MIN_SIZE {
485
217
            return Err(ReadError::OutOfBounds);
486
3.33k
        }
487
3.33k
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
488
0
            return Err(ReadError::OutOfBounds);
489
        };
490
3.33k
        Ok(Self { data })
491
3.55k
    }
492
}
493
494
/// The fields at the start of the table, which are read directly.
495
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
496
#[repr(C, packed)]
497
#[doc(hidden)]
498
pub struct SingleSubstFormat2FixedHeader {
499
    pub subst_format: BigEndian<u16>,
500
    pub coverage_offset: BigEndian<Offset16>,
501
    pub glyph_count: BigEndian<u16>,
502
}
503
504
#[allow(clippy::absurd_extreme_comparisons)]
505
const _: () =
506
    assert!(std::mem::size_of::<SingleSubstFormat2FixedHeader>() <= SingleSubstFormat2::MIN_SIZE);
507
508
/// [Single Substitution Format 2](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#12-single-substitution-format-2)
509
#[derive(Clone)]
510
pub struct SingleSubstFormat2<'a> {
511
    data: TableDataRef<'a, SingleSubstFormat2FixedHeader>,
512
}
513
514
#[allow(clippy::needless_lifetimes)]
515
impl<'a> SingleSubstFormat2<'a> {
516
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
517
    basic_table_impls!(impl_the_methods);
518
519
    /// Format identifier: format = 2
520
0
    pub fn subst_format(&self) -> u16 {
521
0
        self.data.header().subst_format.get()
522
0
    }
523
524
    /// Offset to Coverage table, from beginning of substitution
525
    /// subtable
526
3.33k
    pub fn coverage_offset(&self) -> Offset16 {
527
3.33k
        self.data.header().coverage_offset.get()
528
3.33k
    }
529
530
    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
531
3.33k
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
532
3.33k
        let data = self.offset_data();
533
3.33k
        self.coverage_offset().resolve(data)
534
3.33k
    }
535
536
    /// Number of glyph IDs in the substituteGlyphIDs array
537
3.33k
    pub fn glyph_count(&self) -> u16 {
538
3.33k
        self.data.header().glyph_count.get()
539
3.33k
    }
540
541
    /// Array of substitute glyph IDs — ordered by Coverage index
542
0
    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
543
0
        let range = self.substitute_glyph_ids_byte_range();
544
0
        self.offset_data()
545
0
            .read_array(range)
546
0
            .ok()
547
0
            .unwrap_or_default()
548
0
    }
549
550
3.33k
    pub fn subst_format_byte_range(&self) -> Range<usize> {
551
3.33k
        let start = 0;
552
3.33k
        let end = start + u16::RAW_BYTE_LEN;
553
3.33k
        start..end
554
3.33k
    }
555
556
3.33k
    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
557
3.33k
        let start = self.subst_format_byte_range().end;
558
3.33k
        let end = start + Offset16::RAW_BYTE_LEN;
559
3.33k
        start..end
560
3.33k
    }
561
562
3.33k
    pub fn glyph_count_byte_range(&self) -> Range<usize> {
563
3.33k
        let start = self.coverage_offset_byte_range().end;
564
3.33k
        let end = start + u16::RAW_BYTE_LEN;
565
3.33k
        start..end
566
3.33k
    }
567
568
3.33k
    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
569
3.33k
        let glyph_count = self.glyph_count();
570
3.33k
        let start = self.glyph_count_byte_range().end;
571
3.33k
        let end =
572
3.33k
            start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN);
573
3.33k
        start..end
574
3.33k
    }
575
}
576
577
impl Format<u16> for MultipleSubstFormat1<'_> {
578
    const FORMAT: u16 = 1;
579
}
580
581
impl<'a> MinByteRange<'a> for MultipleSubstFormat1<'a> {
582
0
    fn min_byte_range(&self) -> Range<usize> {
583
0
        0..self.sequence_offsets_byte_range().end
584
0
    }
585
0
    fn min_table_bytes(&self) -> &'a [u8] {
586
0
        let range = self.min_byte_range();
587
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
588
0
    }
589
}
590
591
impl ReadArgs for MultipleSubstFormat1<'_> {
592
    type Args = ();
593
}
594
595
impl<'a> FontRead<'a> for MultipleSubstFormat1<'a> {
596
10.9k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
597
        #[allow(clippy::absurd_extreme_comparisons)]
598
10.9k
        if data.len() < Self::MIN_SIZE {
599
347
            return Err(ReadError::OutOfBounds);
600
10.6k
        }
601
10.6k
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
602
0
            return Err(ReadError::OutOfBounds);
603
        };
604
10.6k
        Ok(Self { data })
605
10.9k
    }
606
}
607
608
/// The fields at the start of the table, which are read directly.
609
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
610
#[repr(C, packed)]
611
#[doc(hidden)]
612
pub struct MultipleSubstFormat1FixedHeader {
613
    pub subst_format: BigEndian<u16>,
614
    pub coverage_offset: BigEndian<Offset16>,
615
    pub sequence_count: BigEndian<u16>,
616
}
617
618
#[allow(clippy::absurd_extreme_comparisons)]
619
const _: () = assert!(
620
    std::mem::size_of::<MultipleSubstFormat1FixedHeader>() <= MultipleSubstFormat1::MIN_SIZE
621
);
622
623
/// [Multiple Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#21-multiple-substitution-format-1)
624
#[derive(Clone)]
625
pub struct MultipleSubstFormat1<'a> {
626
    data: TableDataRef<'a, MultipleSubstFormat1FixedHeader>,
627
}
628
629
#[allow(clippy::needless_lifetimes)]
630
impl<'a> MultipleSubstFormat1<'a> {
631
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
632
    basic_table_impls!(impl_the_methods);
633
634
    /// Format identifier: format = 1
635
0
    pub fn subst_format(&self) -> u16 {
636
0
        self.data.header().subst_format.get()
637
0
    }
638
639
    /// Offset to Coverage table, from beginning of substitution
640
    /// subtable
641
10.6k
    pub fn coverage_offset(&self) -> Offset16 {
642
10.6k
        self.data.header().coverage_offset.get()
643
10.6k
    }
644
645
    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
646
10.6k
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
647
10.6k
        let data = self.offset_data();
648
10.6k
        self.coverage_offset().resolve(data)
649
10.6k
    }
650
651
    /// Number of Sequence table offsets in the sequenceOffsets array
652
10.6k
    pub fn sequence_count(&self) -> u16 {
653
10.6k
        self.data.header().sequence_count.get()
654
10.6k
    }
655
656
    /// Array of offsets to Sequence tables. Offsets are from beginning
657
    /// of substitution subtable, ordered by Coverage index
658
0
    pub fn sequence_offsets(&self) -> &'a [BigEndian<Offset16>] {
659
0
        let range = self.sequence_offsets_byte_range();
660
0
        self.offset_data()
661
0
            .read_array(range)
662
0
            .ok()
663
0
            .unwrap_or_default()
664
0
    }
665
666
    /// A dynamically resolving wrapper for [`sequence_offsets`][Self::sequence_offsets].
667
0
    pub fn sequences(&self) -> ArrayOfOffsets<'a, Sequence<'a>, Offset16> {
668
0
        let data = self.offset_data();
669
0
        let offsets = self.sequence_offsets();
670
0
        ArrayOfOffsets::new(offsets, data, ())
671
0
    }
672
673
10.6k
    pub fn subst_format_byte_range(&self) -> Range<usize> {
674
10.6k
        let start = 0;
675
10.6k
        let end = start + u16::RAW_BYTE_LEN;
676
10.6k
        start..end
677
10.6k
    }
678
679
10.6k
    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
680
10.6k
        let start = self.subst_format_byte_range().end;
681
10.6k
        let end = start + Offset16::RAW_BYTE_LEN;
682
10.6k
        start..end
683
10.6k
    }
684
685
10.6k
    pub fn sequence_count_byte_range(&self) -> Range<usize> {
686
10.6k
        let start = self.coverage_offset_byte_range().end;
687
10.6k
        let end = start + u16::RAW_BYTE_LEN;
688
10.6k
        start..end
689
10.6k
    }
690
691
10.6k
    pub fn sequence_offsets_byte_range(&self) -> Range<usize> {
692
10.6k
        let sequence_count = self.sequence_count();
693
10.6k
        let start = self.sequence_count_byte_range().end;
694
10.6k
        let end =
695
10.6k
            start + (transforms::to_usize(sequence_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
696
10.6k
        start..end
697
10.6k
    }
698
}
699
700
const _: () = assert!(FontData::default_data_long_enough(
701
    MultipleSubstFormat1::MIN_SIZE
702
));
703
704
impl Default for MultipleSubstFormat1<'_> {
705
0
    fn default() -> Self {
706
        Self {
707
0
            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
708
0
                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
709
        }
710
0
    }
711
}
712
713
impl<'a> MinByteRange<'a> for Sequence<'a> {
714
0
    fn min_byte_range(&self) -> Range<usize> {
715
0
        0..self.substitute_glyph_ids_byte_range().end
716
0
    }
717
0
    fn min_table_bytes(&self) -> &'a [u8] {
718
0
        let range = self.min_byte_range();
719
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
720
0
    }
721
}
722
723
impl ReadArgs for Sequence<'_> {
724
    type Args = ();
725
}
726
727
impl<'a> FontRead<'a> for Sequence<'a> {
728
0
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
729
        #[allow(clippy::absurd_extreme_comparisons)]
730
0
        if data.len() < Self::MIN_SIZE {
731
0
            return Err(ReadError::OutOfBounds);
732
0
        }
733
0
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
734
0
            return Err(ReadError::OutOfBounds);
735
        };
736
0
        Ok(Self { data })
737
0
    }
738
}
739
740
/// The fields at the start of the table, which are read directly.
741
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
742
#[repr(C, packed)]
743
#[doc(hidden)]
744
pub struct SequenceFixedHeader {
745
    pub glyph_count: BigEndian<u16>,
746
}
747
748
#[allow(clippy::absurd_extreme_comparisons)]
749
const _: () = assert!(std::mem::size_of::<SequenceFixedHeader>() <= Sequence::MIN_SIZE);
750
751
/// Part of [MultipleSubstFormat1]
752
#[derive(Clone)]
753
pub struct Sequence<'a> {
754
    data: TableDataRef<'a, SequenceFixedHeader>,
755
}
756
757
#[allow(clippy::needless_lifetimes)]
758
impl<'a> Sequence<'a> {
759
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
760
    basic_table_impls!(impl_the_methods);
761
762
    /// Number of glyph IDs in the substituteGlyphIDs array. This must
763
    /// always be greater than 0.
764
0
    pub fn glyph_count(&self) -> u16 {
765
0
        self.data.header().glyph_count.get()
766
0
    }
767
768
    /// String of glyph IDs to substitute
769
0
    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
770
0
        let range = self.substitute_glyph_ids_byte_range();
771
0
        self.offset_data()
772
0
            .read_array(range)
773
0
            .ok()
774
0
            .unwrap_or_default()
775
0
    }
776
777
0
    pub fn glyph_count_byte_range(&self) -> Range<usize> {
778
0
        let start = 0;
779
0
        let end = start + u16::RAW_BYTE_LEN;
780
0
        start..end
781
0
    }
782
783
0
    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
784
0
        let glyph_count = self.glyph_count();
785
0
        let start = self.glyph_count_byte_range().end;
786
0
        let end =
787
0
            start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN);
788
0
        start..end
789
0
    }
790
}
791
792
const _: () = assert!(FontData::default_data_long_enough(Sequence::MIN_SIZE));
793
794
impl Default for Sequence<'_> {
795
0
    fn default() -> Self {
796
        Self {
797
0
            data: TableDataRef::new(FontData::default_table_data().as_bytes())
798
0
                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
799
        }
800
0
    }
801
}
802
803
impl Format<u16> for AlternateSubstFormat1<'_> {
804
    const FORMAT: u16 = 1;
805
}
806
807
impl<'a> MinByteRange<'a> for AlternateSubstFormat1<'a> {
808
0
    fn min_byte_range(&self) -> Range<usize> {
809
0
        0..self.alternate_set_offsets_byte_range().end
810
0
    }
811
0
    fn min_table_bytes(&self) -> &'a [u8] {
812
0
        let range = self.min_byte_range();
813
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
814
0
    }
815
}
816
817
impl ReadArgs for AlternateSubstFormat1<'_> {
818
    type Args = ();
819
}
820
821
impl<'a> FontRead<'a> for AlternateSubstFormat1<'a> {
822
1.77k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
823
        #[allow(clippy::absurd_extreme_comparisons)]
824
1.77k
        if data.len() < Self::MIN_SIZE {
825
250
            return Err(ReadError::OutOfBounds);
826
1.52k
        }
827
1.52k
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
828
0
            return Err(ReadError::OutOfBounds);
829
        };
830
1.52k
        Ok(Self { data })
831
1.77k
    }
832
}
833
834
/// The fields at the start of the table, which are read directly.
835
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
836
#[repr(C, packed)]
837
#[doc(hidden)]
838
pub struct AlternateSubstFormat1FixedHeader {
839
    pub subst_format: BigEndian<u16>,
840
    pub coverage_offset: BigEndian<Offset16>,
841
    pub alternate_set_count: BigEndian<u16>,
842
}
843
844
#[allow(clippy::absurd_extreme_comparisons)]
845
const _: () = assert!(
846
    std::mem::size_of::<AlternateSubstFormat1FixedHeader>() <= AlternateSubstFormat1::MIN_SIZE
847
);
848
849
/// [Alternate Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#31-alternate-substitution-format-1)
850
#[derive(Clone)]
851
pub struct AlternateSubstFormat1<'a> {
852
    data: TableDataRef<'a, AlternateSubstFormat1FixedHeader>,
853
}
854
855
#[allow(clippy::needless_lifetimes)]
856
impl<'a> AlternateSubstFormat1<'a> {
857
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
858
    basic_table_impls!(impl_the_methods);
859
860
    /// Format identifier: format = 1
861
0
    pub fn subst_format(&self) -> u16 {
862
0
        self.data.header().subst_format.get()
863
0
    }
864
865
    /// Offset to Coverage table, from beginning of substitution
866
    /// subtable
867
1.52k
    pub fn coverage_offset(&self) -> Offset16 {
868
1.52k
        self.data.header().coverage_offset.get()
869
1.52k
    }
870
871
    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
872
1.52k
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
873
1.52k
        let data = self.offset_data();
874
1.52k
        self.coverage_offset().resolve(data)
875
1.52k
    }
876
877
    /// Number of AlternateSet tables
878
1.52k
    pub fn alternate_set_count(&self) -> u16 {
879
1.52k
        self.data.header().alternate_set_count.get()
880
1.52k
    }
881
882
    /// Array of offsets to AlternateSet tables. Offsets are from
883
    /// beginning of substitution subtable, ordered by Coverage index
884
0
    pub fn alternate_set_offsets(&self) -> &'a [BigEndian<Offset16>] {
885
0
        let range = self.alternate_set_offsets_byte_range();
886
0
        self.offset_data()
887
0
            .read_array(range)
888
0
            .ok()
889
0
            .unwrap_or_default()
890
0
    }
891
892
    /// A dynamically resolving wrapper for [`alternate_set_offsets`][Self::alternate_set_offsets].
893
0
    pub fn alternate_sets(&self) -> ArrayOfOffsets<'a, AlternateSet<'a>, Offset16> {
894
0
        let data = self.offset_data();
895
0
        let offsets = self.alternate_set_offsets();
896
0
        ArrayOfOffsets::new(offsets, data, ())
897
0
    }
898
899
1.52k
    pub fn subst_format_byte_range(&self) -> Range<usize> {
900
1.52k
        let start = 0;
901
1.52k
        let end = start + u16::RAW_BYTE_LEN;
902
1.52k
        start..end
903
1.52k
    }
904
905
1.52k
    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
906
1.52k
        let start = self.subst_format_byte_range().end;
907
1.52k
        let end = start + Offset16::RAW_BYTE_LEN;
908
1.52k
        start..end
909
1.52k
    }
910
911
1.52k
    pub fn alternate_set_count_byte_range(&self) -> Range<usize> {
912
1.52k
        let start = self.coverage_offset_byte_range().end;
913
1.52k
        let end = start + u16::RAW_BYTE_LEN;
914
1.52k
        start..end
915
1.52k
    }
916
917
1.52k
    pub fn alternate_set_offsets_byte_range(&self) -> Range<usize> {
918
1.52k
        let alternate_set_count = self.alternate_set_count();
919
1.52k
        let start = self.alternate_set_count_byte_range().end;
920
1.52k
        let end = start
921
1.52k
            + (transforms::to_usize(alternate_set_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
922
1.52k
        start..end
923
1.52k
    }
924
}
925
926
const _: () = assert!(FontData::default_data_long_enough(
927
    AlternateSubstFormat1::MIN_SIZE
928
));
929
930
impl Default for AlternateSubstFormat1<'_> {
931
0
    fn default() -> Self {
932
        Self {
933
0
            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
934
0
                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
935
        }
936
0
    }
937
}
938
939
impl<'a> MinByteRange<'a> for AlternateSet<'a> {
940
0
    fn min_byte_range(&self) -> Range<usize> {
941
0
        0..self.alternate_glyph_ids_byte_range().end
942
0
    }
943
0
    fn min_table_bytes(&self) -> &'a [u8] {
944
0
        let range = self.min_byte_range();
945
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
946
0
    }
947
}
948
949
impl ReadArgs for AlternateSet<'_> {
950
    type Args = ();
951
}
952
953
impl<'a> FontRead<'a> for AlternateSet<'a> {
954
0
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
955
        #[allow(clippy::absurd_extreme_comparisons)]
956
0
        if data.len() < Self::MIN_SIZE {
957
0
            return Err(ReadError::OutOfBounds);
958
0
        }
959
0
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
960
0
            return Err(ReadError::OutOfBounds);
961
        };
962
0
        Ok(Self { data })
963
0
    }
964
}
965
966
/// The fields at the start of the table, which are read directly.
967
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
968
#[repr(C, packed)]
969
#[doc(hidden)]
970
pub struct AlternateSetFixedHeader {
971
    pub glyph_count: BigEndian<u16>,
972
}
973
974
#[allow(clippy::absurd_extreme_comparisons)]
975
const _: () = assert!(std::mem::size_of::<AlternateSetFixedHeader>() <= AlternateSet::MIN_SIZE);
976
977
/// Part of [AlternateSubstFormat1]
978
#[derive(Clone)]
979
pub struct AlternateSet<'a> {
980
    data: TableDataRef<'a, AlternateSetFixedHeader>,
981
}
982
983
#[allow(clippy::needless_lifetimes)]
984
impl<'a> AlternateSet<'a> {
985
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
986
    basic_table_impls!(impl_the_methods);
987
988
    /// Number of glyph IDs in the alternateGlyphIDs array
989
0
    pub fn glyph_count(&self) -> u16 {
990
0
        self.data.header().glyph_count.get()
991
0
    }
992
993
    /// Array of alternate glyph IDs, in arbitrary order
994
0
    pub fn alternate_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
995
0
        let range = self.alternate_glyph_ids_byte_range();
996
0
        self.offset_data()
997
0
            .read_array(range)
998
0
            .ok()
999
0
            .unwrap_or_default()
1000
0
    }
1001
1002
0
    pub fn glyph_count_byte_range(&self) -> Range<usize> {
1003
0
        let start = 0;
1004
0
        let end = start + u16::RAW_BYTE_LEN;
1005
0
        start..end
1006
0
    }
1007
1008
0
    pub fn alternate_glyph_ids_byte_range(&self) -> Range<usize> {
1009
0
        let glyph_count = self.glyph_count();
1010
0
        let start = self.glyph_count_byte_range().end;
1011
0
        let end =
1012
0
            start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN);
1013
0
        start..end
1014
0
    }
1015
}
1016
1017
const _: () = assert!(FontData::default_data_long_enough(AlternateSet::MIN_SIZE));
1018
1019
impl Default for AlternateSet<'_> {
1020
0
    fn default() -> Self {
1021
        Self {
1022
0
            data: TableDataRef::new(FontData::default_table_data().as_bytes())
1023
0
                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1024
        }
1025
0
    }
1026
}
1027
1028
impl Format<u16> for LigatureSubstFormat1<'_> {
1029
    const FORMAT: u16 = 1;
1030
}
1031
1032
impl<'a> MinByteRange<'a> for LigatureSubstFormat1<'a> {
1033
0
    fn min_byte_range(&self) -> Range<usize> {
1034
0
        0..self.ligature_set_offsets_byte_range().end
1035
0
    }
1036
0
    fn min_table_bytes(&self) -> &'a [u8] {
1037
0
        let range = self.min_byte_range();
1038
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
1039
0
    }
1040
}
1041
1042
impl ReadArgs for LigatureSubstFormat1<'_> {
1043
    type Args = ();
1044
}
1045
1046
impl<'a> FontRead<'a> for LigatureSubstFormat1<'a> {
1047
20.2k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1048
        #[allow(clippy::absurd_extreme_comparisons)]
1049
20.2k
        if data.len() < Self::MIN_SIZE {
1050
80
            return Err(ReadError::OutOfBounds);
1051
20.1k
        }
1052
20.1k
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1053
0
            return Err(ReadError::OutOfBounds);
1054
        };
1055
20.1k
        Ok(Self { data })
1056
20.2k
    }
1057
}
1058
1059
/// The fields at the start of the table, which are read directly.
1060
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1061
#[repr(C, packed)]
1062
#[doc(hidden)]
1063
pub struct LigatureSubstFormat1FixedHeader {
1064
    pub subst_format: BigEndian<u16>,
1065
    pub coverage_offset: BigEndian<Offset16>,
1066
    pub ligature_set_count: BigEndian<u16>,
1067
}
1068
1069
#[allow(clippy::absurd_extreme_comparisons)]
1070
const _: () = assert!(
1071
    std::mem::size_of::<LigatureSubstFormat1FixedHeader>() <= LigatureSubstFormat1::MIN_SIZE
1072
);
1073
1074
/// [Ligature Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#41-ligature-substitution-format-1)
1075
#[derive(Clone)]
1076
pub struct LigatureSubstFormat1<'a> {
1077
    data: TableDataRef<'a, LigatureSubstFormat1FixedHeader>,
1078
}
1079
1080
#[allow(clippy::needless_lifetimes)]
1081
impl<'a> LigatureSubstFormat1<'a> {
1082
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1083
    basic_table_impls!(impl_the_methods);
1084
1085
    /// Format identifier: format = 1
1086
0
    pub fn subst_format(&self) -> u16 {
1087
0
        self.data.header().subst_format.get()
1088
0
    }
1089
1090
    /// Offset to Coverage table, from beginning of substitution
1091
    /// subtable
1092
20.1k
    pub fn coverage_offset(&self) -> Offset16 {
1093
20.1k
        self.data.header().coverage_offset.get()
1094
20.1k
    }
1095
1096
    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1097
20.1k
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1098
20.1k
        let data = self.offset_data();
1099
20.1k
        self.coverage_offset().resolve(data)
1100
20.1k
    }
1101
1102
    /// Number of LigatureSet tables
1103
20.1k
    pub fn ligature_set_count(&self) -> u16 {
1104
20.1k
        self.data.header().ligature_set_count.get()
1105
20.1k
    }
1106
1107
    /// Array of offsets to LigatureSet tables. Offsets are from
1108
    /// beginning of substitution subtable, ordered by Coverage index
1109
0
    pub fn ligature_set_offsets(&self) -> &'a [BigEndian<Offset16>] {
1110
0
        let range = self.ligature_set_offsets_byte_range();
1111
0
        self.offset_data()
1112
0
            .read_array(range)
1113
0
            .ok()
1114
0
            .unwrap_or_default()
1115
0
    }
1116
1117
    /// A dynamically resolving wrapper for [`ligature_set_offsets`][Self::ligature_set_offsets].
1118
0
    pub fn ligature_sets(&self) -> ArrayOfOffsets<'a, LigatureSet<'a>, Offset16> {
1119
0
        let data = self.offset_data();
1120
0
        let offsets = self.ligature_set_offsets();
1121
0
        ArrayOfOffsets::new(offsets, data, ())
1122
0
    }
1123
1124
20.1k
    pub fn subst_format_byte_range(&self) -> Range<usize> {
1125
20.1k
        let start = 0;
1126
20.1k
        let end = start + u16::RAW_BYTE_LEN;
1127
20.1k
        start..end
1128
20.1k
    }
1129
1130
20.1k
    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1131
20.1k
        let start = self.subst_format_byte_range().end;
1132
20.1k
        let end = start + Offset16::RAW_BYTE_LEN;
1133
20.1k
        start..end
1134
20.1k
    }
1135
1136
20.1k
    pub fn ligature_set_count_byte_range(&self) -> Range<usize> {
1137
20.1k
        let start = self.coverage_offset_byte_range().end;
1138
20.1k
        let end = start + u16::RAW_BYTE_LEN;
1139
20.1k
        start..end
1140
20.1k
    }
1141
1142
20.1k
    pub fn ligature_set_offsets_byte_range(&self) -> Range<usize> {
1143
20.1k
        let ligature_set_count = self.ligature_set_count();
1144
20.1k
        let start = self.ligature_set_count_byte_range().end;
1145
20.1k
        let end = start
1146
20.1k
            + (transforms::to_usize(ligature_set_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
1147
20.1k
        start..end
1148
20.1k
    }
1149
}
1150
1151
const _: () = assert!(FontData::default_data_long_enough(
1152
    LigatureSubstFormat1::MIN_SIZE
1153
));
1154
1155
impl Default for LigatureSubstFormat1<'_> {
1156
0
    fn default() -> Self {
1157
        Self {
1158
0
            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
1159
0
                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1160
        }
1161
0
    }
1162
}
1163
1164
impl<'a> MinByteRange<'a> for LigatureSet<'a> {
1165
0
    fn min_byte_range(&self) -> Range<usize> {
1166
0
        0..self.ligature_offsets_byte_range().end
1167
0
    }
1168
0
    fn min_table_bytes(&self) -> &'a [u8] {
1169
0
        let range = self.min_byte_range();
1170
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
1171
0
    }
1172
}
1173
1174
impl ReadArgs for LigatureSet<'_> {
1175
    type Args = ();
1176
}
1177
1178
impl<'a> FontRead<'a> for LigatureSet<'a> {
1179
0
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1180
        #[allow(clippy::absurd_extreme_comparisons)]
1181
0
        if data.len() < Self::MIN_SIZE {
1182
0
            return Err(ReadError::OutOfBounds);
1183
0
        }
1184
0
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1185
0
            return Err(ReadError::OutOfBounds);
1186
        };
1187
0
        Ok(Self { data })
1188
0
    }
1189
}
1190
1191
/// The fields at the start of the table, which are read directly.
1192
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1193
#[repr(C, packed)]
1194
#[doc(hidden)]
1195
pub struct LigatureSetFixedHeader {
1196
    pub ligature_count: BigEndian<u16>,
1197
}
1198
1199
#[allow(clippy::absurd_extreme_comparisons)]
1200
const _: () = assert!(std::mem::size_of::<LigatureSetFixedHeader>() <= LigatureSet::MIN_SIZE);
1201
1202
/// Part of [LigatureSubstFormat1]
1203
#[derive(Clone)]
1204
pub struct LigatureSet<'a> {
1205
    data: TableDataRef<'a, LigatureSetFixedHeader>,
1206
}
1207
1208
#[allow(clippy::needless_lifetimes)]
1209
impl<'a> LigatureSet<'a> {
1210
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
1211
    basic_table_impls!(impl_the_methods);
1212
1213
    /// Number of Ligature tables
1214
0
    pub fn ligature_count(&self) -> u16 {
1215
0
        self.data.header().ligature_count.get()
1216
0
    }
1217
1218
    /// Array of offsets to Ligature tables. Offsets are from beginning
1219
    /// of LigatureSet table, ordered by preference.
1220
0
    pub fn ligature_offsets(&self) -> &'a [BigEndian<Offset16>] {
1221
0
        let range = self.ligature_offsets_byte_range();
1222
0
        self.offset_data()
1223
0
            .read_array(range)
1224
0
            .ok()
1225
0
            .unwrap_or_default()
1226
0
    }
1227
1228
    /// A dynamically resolving wrapper for [`ligature_offsets`][Self::ligature_offsets].
1229
0
    pub fn ligatures(&self) -> ArrayOfOffsets<'a, Ligature<'a>, Offset16> {
1230
0
        let data = self.offset_data();
1231
0
        let offsets = self.ligature_offsets();
1232
0
        ArrayOfOffsets::new(offsets, data, ())
1233
0
    }
1234
1235
0
    pub fn ligature_count_byte_range(&self) -> Range<usize> {
1236
0
        let start = 0;
1237
0
        let end = start + u16::RAW_BYTE_LEN;
1238
0
        start..end
1239
0
    }
1240
1241
0
    pub fn ligature_offsets_byte_range(&self) -> Range<usize> {
1242
0
        let ligature_count = self.ligature_count();
1243
0
        let start = self.ligature_count_byte_range().end;
1244
0
        let end =
1245
0
            start + (transforms::to_usize(ligature_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
1246
0
        start..end
1247
0
    }
1248
}
1249
1250
const _: () = assert!(FontData::default_data_long_enough(LigatureSet::MIN_SIZE));
1251
1252
impl Default for LigatureSet<'_> {
1253
0
    fn default() -> Self {
1254
        Self {
1255
0
            data: TableDataRef::new(FontData::default_table_data().as_bytes())
1256
0
                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1257
        }
1258
0
    }
1259
}
1260
1261
impl<'a> MinByteRange<'a> for Ligature<'a> {
1262
0
    fn min_byte_range(&self) -> Range<usize> {
1263
0
        0..self.component_glyph_ids_byte_range().end
1264
0
    }
1265
0
    fn min_table_bytes(&self) -> &'a [u8] {
1266
0
        let range = self.min_byte_range();
1267
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
1268
0
    }
1269
}
1270
1271
impl ReadArgs for Ligature<'_> {
1272
    type Args = ();
1273
}
1274
1275
impl<'a> FontRead<'a> for Ligature<'a> {
1276
0
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1277
        #[allow(clippy::absurd_extreme_comparisons)]
1278
0
        if data.len() < Self::MIN_SIZE {
1279
0
            return Err(ReadError::OutOfBounds);
1280
0
        }
1281
0
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1282
0
            return Err(ReadError::OutOfBounds);
1283
        };
1284
0
        Ok(Self { data })
1285
0
    }
1286
}
1287
1288
/// The fields at the start of the table, which are read directly.
1289
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1290
#[repr(C, packed)]
1291
#[doc(hidden)]
1292
pub struct LigatureFixedHeader {
1293
    pub ligature_glyph: BigEndian<GlyphId16>,
1294
    pub component_count: BigEndian<u16>,
1295
}
1296
1297
#[allow(clippy::absurd_extreme_comparisons)]
1298
const _: () = assert!(std::mem::size_of::<LigatureFixedHeader>() <= Ligature::MIN_SIZE);
1299
1300
/// Part of [LigatureSubstFormat1]
1301
#[derive(Clone)]
1302
pub struct Ligature<'a> {
1303
    data: TableDataRef<'a, LigatureFixedHeader>,
1304
}
1305
1306
#[allow(clippy::needless_lifetimes)]
1307
impl<'a> Ligature<'a> {
1308
    pub const MIN_SIZE: usize = (GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1309
    basic_table_impls!(impl_the_methods);
1310
1311
    /// glyph ID of ligature to substitute
1312
0
    pub fn ligature_glyph(&self) -> GlyphId16 {
1313
0
        self.data.header().ligature_glyph.get()
1314
0
    }
1315
1316
    /// Number of components in the ligature
1317
0
    pub fn component_count(&self) -> u16 {
1318
0
        self.data.header().component_count.get()
1319
0
    }
1320
1321
    /// Array of component glyph IDs — start with the second
1322
    /// component, ordered in writing direction
1323
0
    pub fn component_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
1324
0
        let range = self.component_glyph_ids_byte_range();
1325
0
        self.offset_data()
1326
0
            .read_array(range)
1327
0
            .ok()
1328
0
            .unwrap_or_default()
1329
0
    }
1330
1331
0
    pub fn ligature_glyph_byte_range(&self) -> Range<usize> {
1332
0
        let start = 0;
1333
0
        let end = start + GlyphId16::RAW_BYTE_LEN;
1334
0
        start..end
1335
0
    }
1336
1337
0
    pub fn component_count_byte_range(&self) -> Range<usize> {
1338
0
        let start = self.ligature_glyph_byte_range().end;
1339
0
        let end = start + u16::RAW_BYTE_LEN;
1340
0
        start..end
1341
0
    }
1342
1343
0
    pub fn component_glyph_ids_byte_range(&self) -> Range<usize> {
1344
0
        let component_count = self.component_count();
1345
0
        let start = self.component_count_byte_range().end;
1346
0
        let end = start
1347
0
            + (transforms::subtract(component_count, 1_usize))
1348
0
                .saturating_mul(GlyphId16::RAW_BYTE_LEN);
1349
0
        start..end
1350
0
    }
1351
}
1352
1353
const _: () = assert!(FontData::default_data_long_enough(Ligature::MIN_SIZE));
1354
1355
impl Default for Ligature<'_> {
1356
0
    fn default() -> Self {
1357
        Self {
1358
0
            data: TableDataRef::new(FontData::default_table_data().as_bytes())
1359
0
                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1360
        }
1361
0
    }
1362
}
1363
1364
impl Format<u16> for ExtensionSubstFormat1<'_> {
1365
    const FORMAT: u16 = 1;
1366
}
1367
1368
impl Discriminant for ExtensionSubstFormat1<'_, ()> {
1369
0
    fn read_discriminant(data: FontData<'_>) -> Result<u16, ReadError> {
1370
0
        data.read_at(u16::RAW_BYTE_LEN)
1371
0
    }
1372
}
1373
1374
impl<'a, T> MinByteRange<'a> for ExtensionSubstFormat1<'a, T> {
1375
0
    fn min_byte_range(&self) -> Range<usize> {
1376
0
        0..self.extension_offset_byte_range().end
1377
0
    }
1378
0
    fn min_table_bytes(&self) -> &'a [u8] {
1379
0
        let range = self.min_byte_range();
1380
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
1381
0
    }
1382
}
1383
1384
impl<T> ReadArgs for ExtensionSubstFormat1<'_, T> {
1385
    type Args = ();
1386
}
1387
1388
impl<'a, T> FontRead<'a> for ExtensionSubstFormat1<'a, T> {
1389
51.7k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1390
        #[allow(clippy::absurd_extreme_comparisons)]
1391
51.7k
        if data.len() < Self::MIN_SIZE {
1392
321
            return Err(ReadError::OutOfBounds);
1393
51.4k
        }
1394
51.4k
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1395
0
            return Err(ReadError::OutOfBounds);
1396
        };
1397
51.4k
        Ok(Self {
1398
51.4k
            data,
1399
51.4k
            offset_type: std::marker::PhantomData,
1400
51.4k
        })
1401
51.7k
    }
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::gsub::SingleSubst> as read_fonts::read::FontRead>::read_with_args
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::gsub::LigatureSubst> as read_fonts::read::FontRead>::read_with_args
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::gsub::MultipleSubst> as read_fonts::read::FontRead>::read_with_args
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::gsub::AlternateSubst> as read_fonts::read::FontRead>::read_with_args
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::gsub::ReverseChainSingleSubst> as read_fonts::read::FontRead>::read_with_args
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::layout::SequenceContext> as read_fonts::read::FontRead>::read_with_args
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::layout::ChainedSequenceContext> as read_fonts::read::FontRead>::read_with_args
<read_fonts::tables::gsub::ExtensionSubstFormat1 as read_fonts::read::FontRead>::read_with_args
Line
Count
Source
1389
51.7k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1390
        #[allow(clippy::absurd_extreme_comparisons)]
1391
51.7k
        if data.len() < Self::MIN_SIZE {
1392
321
            return Err(ReadError::OutOfBounds);
1393
51.4k
        }
1394
51.4k
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1395
0
            return Err(ReadError::OutOfBounds);
1396
        };
1397
51.4k
        Ok(Self {
1398
51.4k
            data,
1399
51.4k
            offset_type: std::marker::PhantomData,
1400
51.4k
        })
1401
51.7k
    }
1402
}
1403
1404
impl<'a, T> ExtensionSubstFormat1<'a, T> {
1405
    #[allow(dead_code)]
1406
    /// Replace the specific generic type on this implementation with `()`
1407
0
    pub(crate) fn of_unit_type(&self) -> ExtensionSubstFormat1<'a, ()> {
1408
0
        ExtensionSubstFormat1 {
1409
0
            data: self.data,
1410
0
            offset_type: std::marker::PhantomData,
1411
0
        }
1412
0
    }
1413
}
1414
1415
/// The fields at the start of the table, which are read directly.
1416
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1417
#[repr(C, packed)]
1418
#[doc(hidden)]
1419
pub struct ExtensionSubstFormat1FixedHeader {
1420
    pub subst_format: BigEndian<u16>,
1421
    pub extension_lookup_type: BigEndian<u16>,
1422
    pub extension_offset: BigEndian<Offset32>,
1423
}
1424
1425
#[allow(clippy::absurd_extreme_comparisons)]
1426
const _: () = assert!(
1427
    std::mem::size_of::<ExtensionSubstFormat1FixedHeader>()
1428
        <= ExtensionSubstFormat1::<()>::MIN_SIZE
1429
);
1430
1431
/// [Extension Substitution Subtable Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#71-extension-substitution-subtable-format-1)
1432
#[derive(Clone)]
1433
pub struct ExtensionSubstFormat1<'a, T = ()> {
1434
    data: TableDataRef<'a, ExtensionSubstFormat1FixedHeader>,
1435
    offset_type: std::marker::PhantomData<*const T>,
1436
}
1437
1438
#[allow(clippy::needless_lifetimes)]
1439
impl<'a, T> ExtensionSubstFormat1<'a, T> {
1440
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN);
1441
    basic_table_impls!(impl_the_methods);
1442
1443
    /// Format identifier. Set to 1.
1444
0
    pub fn subst_format(&self) -> u16 {
1445
0
        self.data.header().subst_format.get()
1446
0
    }
1447
1448
    /// Lookup type of subtable referenced by extensionOffset (that is,
1449
    /// the extension subtable).
1450
51.4k
    pub fn extension_lookup_type(&self) -> u16 {
1451
51.4k
        self.data.header().extension_lookup_type.get()
1452
51.4k
    }
1453
1454
    /// Offset to the extension subtable, of lookup type
1455
    /// extensionLookupType, relative to the start of the
1456
    /// ExtensionSubstFormat1 subtable.
1457
50.9k
    pub fn extension_offset(&self) -> Offset32 {
1458
50.9k
        self.data.header().extension_offset.get()
1459
50.9k
    }
<read_fonts::tables::gsub::ExtensionSubstFormat1>::extension_offset
Line
Count
Source
1457
50.9k
    pub fn extension_offset(&self) -> Offset32 {
1458
50.9k
        self.data.header().extension_offset.get()
1459
50.9k
    }
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::gsub::SingleSubst>>::extension_offset
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::gsub::LigatureSubst>>::extension_offset
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::gsub::MultipleSubst>>::extension_offset
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::gsub::AlternateSubst>>::extension_offset
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::gsub::ReverseChainSingleSubst>>::extension_offset
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::layout::SequenceContext>>::extension_offset
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::layout::ChainedSequenceContext>>::extension_offset
1460
1461
    /// Attempt to resolve [`extension_offset`][Self::extension_offset].
1462
0
    pub fn extension(&self) -> Result<T, ReadError>
1463
0
    where
1464
0
        T: FontRead<'a, Args = ()>,
1465
    {
1466
0
        let data = self.offset_data();
1467
0
        self.extension_offset().resolve(data)
1468
0
    }
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::gsub::SingleSubst>>::extension
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::gsub::LigatureSubst>>::extension
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::gsub::MultipleSubst>>::extension
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::gsub::AlternateSubst>>::extension
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::gsub::ReverseChainSingleSubst>>::extension
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::layout::SequenceContext>>::extension
Unexecuted instantiation: <read_fonts::tables::gsub::ExtensionSubstFormat1<read_fonts::tables::layout::ChainedSequenceContext>>::extension
1469
1470
0
    pub fn subst_format_byte_range(&self) -> Range<usize> {
1471
0
        let start = 0;
1472
0
        let end = start + u16::RAW_BYTE_LEN;
1473
0
        start..end
1474
0
    }
1475
1476
0
    pub fn extension_lookup_type_byte_range(&self) -> Range<usize> {
1477
0
        let start = self.subst_format_byte_range().end;
1478
0
        let end = start + u16::RAW_BYTE_LEN;
1479
0
        start..end
1480
0
    }
1481
1482
0
    pub fn extension_offset_byte_range(&self) -> Range<usize> {
1483
0
        let start = self.extension_lookup_type_byte_range().end;
1484
0
        let end = start + Offset32::RAW_BYTE_LEN;
1485
0
        start..end
1486
0
    }
1487
}
1488
1489
const _: () = assert!(FontData::default_data_long_enough(
1490
    ExtensionSubstFormat1::<()>::MIN_SIZE
1491
));
1492
1493
impl<T> Default for ExtensionSubstFormat1<'_, T> {
1494
0
    fn default() -> Self {
1495
        Self {
1496
0
            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
1497
0
                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1498
0
            offset_type: std::marker::PhantomData,
1499
        }
1500
0
    }
1501
}
1502
1503
/// A [GSUB Extension Substitution](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#ES) subtable
1504
pub enum ExtensionSubtable<'a> {
1505
    Single(ExtensionSubstFormat1<'a, SingleSubst<'a>>),
1506
    Multiple(ExtensionSubstFormat1<'a, MultipleSubst<'a>>),
1507
    Alternate(ExtensionSubstFormat1<'a, AlternateSubst<'a>>),
1508
    Ligature(ExtensionSubstFormat1<'a, LigatureSubst<'a>>),
1509
    Contextual(ExtensionSubstFormat1<'a, SubstitutionSequenceContext<'a>>),
1510
    ChainContextual(ExtensionSubstFormat1<'a, SubstitutionChainContext<'a>>),
1511
    Reverse(ExtensionSubstFormat1<'a, ReverseChainSingleSubst<'a>>),
1512
}
1513
1514
impl Default for ExtensionSubtable<'_> {
1515
0
    fn default() -> Self {
1516
0
        Self::Single(Default::default())
1517
0
    }
1518
}
1519
1520
impl ReadArgs for ExtensionSubtable<'_> {
1521
    type Args = ();
1522
}
1523
1524
impl<'a> FontRead<'a> for ExtensionSubtable<'a> {
1525
0
    fn read_with_args(bytes: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1526
0
        let discriminant = ExtensionSubstFormat1::read_discriminant(bytes)?;
1527
0
        match discriminant {
1528
0
            1 => Ok(ExtensionSubtable::Single(FontRead::read(bytes)?)),
1529
0
            2 => Ok(ExtensionSubtable::Multiple(FontRead::read(bytes)?)),
1530
0
            3 => Ok(ExtensionSubtable::Alternate(FontRead::read(bytes)?)),
1531
0
            4 => Ok(ExtensionSubtable::Ligature(FontRead::read(bytes)?)),
1532
0
            5 => Ok(ExtensionSubtable::Contextual(FontRead::read(bytes)?)),
1533
0
            6 => Ok(ExtensionSubtable::ChainContextual(FontRead::read(bytes)?)),
1534
0
            8 => Ok(ExtensionSubtable::Reverse(FontRead::read(bytes)?)),
1535
0
            other => Err(ReadError::InvalidFormat(other.into())),
1536
        }
1537
0
    }
1538
}
1539
1540
impl<'a> ExtensionSubtable<'a> {
1541
    #[allow(dead_code)]
1542
    /// Return the inner table, removing the specific generics.
1543
    ///
1544
    /// This lets us return a single concrete type we can call methods on.
1545
0
    pub(crate) fn of_unit_type(&self) -> ExtensionSubstFormat1<'a, ()> {
1546
0
        match self {
1547
0
            ExtensionSubtable::Single(inner) => inner.of_unit_type(),
1548
0
            ExtensionSubtable::Multiple(inner) => inner.of_unit_type(),
1549
0
            ExtensionSubtable::Alternate(inner) => inner.of_unit_type(),
1550
0
            ExtensionSubtable::Ligature(inner) => inner.of_unit_type(),
1551
0
            ExtensionSubtable::Contextual(inner) => inner.of_unit_type(),
1552
0
            ExtensionSubtable::ChainContextual(inner) => inner.of_unit_type(),
1553
0
            ExtensionSubtable::Reverse(inner) => inner.of_unit_type(),
1554
        }
1555
0
    }
1556
}
1557
1558
impl Format<u16> for ReverseChainSingleSubstFormat1<'_> {
1559
    const FORMAT: u16 = 1;
1560
}
1561
1562
impl<'a> MinByteRange<'a> for ReverseChainSingleSubstFormat1<'a> {
1563
0
    fn min_byte_range(&self) -> Range<usize> {
1564
0
        0..self.substitute_glyph_ids_byte_range().end
1565
0
    }
1566
0
    fn min_table_bytes(&self) -> &'a [u8] {
1567
0
        let range = self.min_byte_range();
1568
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
1569
0
    }
1570
}
1571
1572
impl ReadArgs for ReverseChainSingleSubstFormat1<'_> {
1573
    type Args = ();
1574
}
1575
1576
impl<'a> FontRead<'a> for ReverseChainSingleSubstFormat1<'a> {
1577
4.75k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1578
        #[allow(clippy::absurd_extreme_comparisons)]
1579
4.75k
        if data.len() < Self::MIN_SIZE {
1580
291
            return Err(ReadError::OutOfBounds);
1581
4.45k
        }
1582
4.45k
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1583
0
            return Err(ReadError::OutOfBounds);
1584
        };
1585
4.45k
        Ok(Self { data })
1586
4.75k
    }
1587
}
1588
1589
/// The fields at the start of the table, which are read directly.
1590
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1591
#[repr(C, packed)]
1592
#[doc(hidden)]
1593
pub struct ReverseChainSingleSubstFormat1FixedHeader {
1594
    pub subst_format: BigEndian<u16>,
1595
    pub coverage_offset: BigEndian<Offset16>,
1596
    pub backtrack_glyph_count: BigEndian<u16>,
1597
}
1598
1599
#[allow(clippy::absurd_extreme_comparisons)]
1600
const _: () = assert!(
1601
    std::mem::size_of::<ReverseChainSingleSubstFormat1FixedHeader>()
1602
        <= ReverseChainSingleSubstFormat1::MIN_SIZE
1603
);
1604
1605
/// [Reverse Chaining Contextual Single Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#81-reverse-chaining-contextual-single-substitution-format-1-coverage-based-glyph-contexts)
1606
#[derive(Clone)]
1607
pub struct ReverseChainSingleSubstFormat1<'a> {
1608
    data: TableDataRef<'a, ReverseChainSingleSubstFormat1FixedHeader>,
1609
}
1610
1611
#[allow(clippy::needless_lifetimes)]
1612
impl<'a> ReverseChainSingleSubstFormat1<'a> {
1613
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
1614
        + Offset16::RAW_BYTE_LEN
1615
        + u16::RAW_BYTE_LEN
1616
        + u16::RAW_BYTE_LEN
1617
        + u16::RAW_BYTE_LEN);
1618
    basic_table_impls!(impl_the_methods);
1619
1620
    /// Format identifier: format = 1
1621
0
    pub fn subst_format(&self) -> u16 {
1622
0
        self.data.header().subst_format.get()
1623
0
    }
1624
1625
    /// Offset to Coverage table, from beginning of substitution
1626
    /// subtable.
1627
4.45k
    pub fn coverage_offset(&self) -> Offset16 {
1628
4.45k
        self.data.header().coverage_offset.get()
1629
4.45k
    }
1630
1631
    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1632
4.45k
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1633
4.45k
        let data = self.offset_data();
1634
4.45k
        self.coverage_offset().resolve(data)
1635
4.45k
    }
1636
1637
    /// Number of glyphs in the backtrack sequence.
1638
24.2k
    pub fn backtrack_glyph_count(&self) -> u16 {
1639
24.2k
        self.data.header().backtrack_glyph_count.get()
1640
24.2k
    }
1641
1642
    /// Array of offsets to coverage tables in backtrack sequence, in
1643
    /// glyph sequence order.
1644
0
    pub fn backtrack_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
1645
0
        let range = self.backtrack_coverage_offsets_byte_range();
1646
0
        self.offset_data()
1647
0
            .read_array(range)
1648
0
            .ok()
1649
0
            .unwrap_or_default()
1650
0
    }
1651
1652
    /// A dynamically resolving wrapper for [`backtrack_coverage_offsets`][Self::backtrack_coverage_offsets].
1653
0
    pub fn backtrack_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
1654
0
        let data = self.offset_data();
1655
0
        let offsets = self.backtrack_coverage_offsets();
1656
0
        ArrayOfOffsets::new(offsets, data, ())
1657
0
    }
1658
1659
    /// Number of glyphs in lookahead sequence.
1660
11.0k
    pub fn lookahead_glyph_count(&self) -> u16 {
1661
11.0k
        let range = self.lookahead_glyph_count_byte_range();
1662
11.0k
        self.offset_data()
1663
11.0k
            .read_at(range.start)
1664
11.0k
            .ok()
1665
11.0k
            .unwrap_or_default()
1666
11.0k
    }
1667
1668
    /// Array of offsets to coverage tables in lookahead sequence, in
1669
    /// glyph sequence order.
1670
0
    pub fn lookahead_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
1671
0
        let range = self.lookahead_coverage_offsets_byte_range();
1672
0
        self.offset_data()
1673
0
            .read_array(range)
1674
0
            .ok()
1675
0
            .unwrap_or_default()
1676
0
    }
1677
1678
    /// A dynamically resolving wrapper for [`lookahead_coverage_offsets`][Self::lookahead_coverage_offsets].
1679
0
    pub fn lookahead_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
1680
0
        let data = self.offset_data();
1681
0
        let offsets = self.lookahead_coverage_offsets();
1682
0
        ArrayOfOffsets::new(offsets, data, ())
1683
0
    }
1684
1685
    /// Number of glyph IDs in the substituteGlyphIDs array.
1686
4.45k
    pub fn glyph_count(&self) -> u16 {
1687
4.45k
        let range = self.glyph_count_byte_range();
1688
4.45k
        self.offset_data()
1689
4.45k
            .read_at(range.start)
1690
4.45k
            .ok()
1691
4.45k
            .unwrap_or_default()
1692
4.45k
    }
1693
1694
    /// Array of substitute glyph IDs — ordered by Coverage index.
1695
0
    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
1696
0
        let range = self.substitute_glyph_ids_byte_range();
1697
0
        self.offset_data()
1698
0
            .read_array(range)
1699
0
            .ok()
1700
0
            .unwrap_or_default()
1701
0
    }
1702
1703
24.2k
    pub fn subst_format_byte_range(&self) -> Range<usize> {
1704
24.2k
        let start = 0;
1705
24.2k
        let end = start + u16::RAW_BYTE_LEN;
1706
24.2k
        start..end
1707
24.2k
    }
1708
1709
24.2k
    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1710
24.2k
        let start = self.subst_format_byte_range().end;
1711
24.2k
        let end = start + Offset16::RAW_BYTE_LEN;
1712
24.2k
        start..end
1713
24.2k
    }
1714
1715
24.2k
    pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
1716
24.2k
        let start = self.coverage_offset_byte_range().end;
1717
24.2k
        let end = start + u16::RAW_BYTE_LEN;
1718
24.2k
        start..end
1719
24.2k
    }
1720
1721
24.2k
    pub fn backtrack_coverage_offsets_byte_range(&self) -> Range<usize> {
1722
24.2k
        let backtrack_glyph_count = self.backtrack_glyph_count();
1723
24.2k
        let start = self.backtrack_glyph_count_byte_range().end;
1724
24.2k
        let end = start
1725
24.2k
            + (transforms::to_usize(backtrack_glyph_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
1726
24.2k
        start..end
1727
24.2k
    }
1728
1729
22.1k
    pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
1730
22.1k
        let start = self.backtrack_coverage_offsets_byte_range().end;
1731
22.1k
        let end = start + u16::RAW_BYTE_LEN;
1732
22.1k
        start..end
1733
22.1k
    }
1734
1735
11.0k
    pub fn lookahead_coverage_offsets_byte_range(&self) -> Range<usize> {
1736
11.0k
        let lookahead_glyph_count = self.lookahead_glyph_count();
1737
11.0k
        let start = self.lookahead_glyph_count_byte_range().end;
1738
11.0k
        let end = start
1739
11.0k
            + (transforms::to_usize(lookahead_glyph_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
1740
11.0k
        start..end
1741
11.0k
    }
1742
1743
8.91k
    pub fn glyph_count_byte_range(&self) -> Range<usize> {
1744
8.91k
        let start = self.lookahead_coverage_offsets_byte_range().end;
1745
8.91k
        let end = start + u16::RAW_BYTE_LEN;
1746
8.91k
        start..end
1747
8.91k
    }
1748
1749
4.45k
    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
1750
4.45k
        let glyph_count = self.glyph_count();
1751
4.45k
        let start = self.glyph_count_byte_range().end;
1752
4.45k
        let end =
1753
4.45k
            start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN);
1754
4.45k
        start..end
1755
4.45k
    }
1756
}
1757
1758
const _: () = assert!(FontData::default_data_long_enough(
1759
    ReverseChainSingleSubstFormat1::MIN_SIZE
1760
));
1761
1762
impl Default for ReverseChainSingleSubstFormat1<'_> {
1763
0
    fn default() -> Self {
1764
        Self {
1765
0
            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
1766
0
                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1767
        }
1768
0
    }
1769
}
1770
1771
impl Format<u16> for SingleSubstFormat3<'_> {
1772
    const FORMAT: u16 = 3;
1773
}
1774
1775
impl<'a> MinByteRange<'a> for SingleSubstFormat3<'a> {
1776
0
    fn min_byte_range(&self) -> Range<usize> {
1777
0
        0..self.delta_glyph_id_byte_range().end
1778
0
    }
1779
0
    fn min_table_bytes(&self) -> &'a [u8] {
1780
0
        let range = self.min_byte_range();
1781
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
1782
0
    }
1783
}
1784
1785
impl ReadArgs for SingleSubstFormat3<'_> {
1786
    type Args = ();
1787
}
1788
1789
impl<'a> FontRead<'a> for SingleSubstFormat3<'a> {
1790
945
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1791
        #[allow(clippy::absurd_extreme_comparisons)]
1792
945
        if data.len() < Self::MIN_SIZE {
1793
205
            return Err(ReadError::OutOfBounds);
1794
740
        }
1795
740
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1796
0
            return Err(ReadError::OutOfBounds);
1797
        };
1798
740
        Ok(Self { data })
1799
945
    }
1800
}
1801
1802
/// The fields at the start of the table, which are read directly.
1803
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1804
#[repr(C, packed)]
1805
#[doc(hidden)]
1806
pub struct SingleSubstFormat3FixedHeader {
1807
    pub subst_format: BigEndian<u16>,
1808
    pub coverage_offset: BigEndian<Offset32>,
1809
    pub delta_glyph_id: BigEndian<Int24>,
1810
}
1811
1812
#[allow(clippy::absurd_extreme_comparisons)]
1813
const _: () =
1814
    assert!(std::mem::size_of::<SingleSubstFormat3FixedHeader>() <= SingleSubstFormat3::MIN_SIZE);
1815
1816
/// SingleSubstFormat3 (ISO/IEC 14496-22:2026).
1817
#[derive(Clone)]
1818
pub struct SingleSubstFormat3<'a> {
1819
    data: TableDataRef<'a, SingleSubstFormat3FixedHeader>,
1820
}
1821
1822
#[allow(clippy::needless_lifetimes)]
1823
impl<'a> SingleSubstFormat3<'a> {
1824
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN + Int24::RAW_BYTE_LEN);
1825
    basic_table_impls!(impl_the_methods);
1826
1827
    /// Format identifier: format = 3
1828
0
    pub fn subst_format(&self) -> u16 {
1829
0
        self.data.header().subst_format.get()
1830
0
    }
1831
1832
    /// Offset to Coverage table, from beginning of substitution
1833
    /// subtable
1834
740
    pub fn coverage_offset(&self) -> Offset32 {
1835
740
        self.data.header().coverage_offset.get()
1836
740
    }
1837
1838
    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1839
740
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1840
740
        let data = self.offset_data();
1841
740
        self.coverage_offset().resolve(data)
1842
740
    }
1843
1844
    /// Add to original glyph ID to get substitute glyph ID
1845
740
    pub fn delta_glyph_id(&self) -> Int24 {
1846
740
        self.data.header().delta_glyph_id.get()
1847
740
    }
1848
1849
0
    pub fn subst_format_byte_range(&self) -> Range<usize> {
1850
0
        let start = 0;
1851
0
        let end = start + u16::RAW_BYTE_LEN;
1852
0
        start..end
1853
0
    }
1854
1855
0
    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1856
0
        let start = self.subst_format_byte_range().end;
1857
0
        let end = start + Offset32::RAW_BYTE_LEN;
1858
0
        start..end
1859
0
    }
1860
1861
0
    pub fn delta_glyph_id_byte_range(&self) -> Range<usize> {
1862
0
        let start = self.coverage_offset_byte_range().end;
1863
0
        let end = start + Int24::RAW_BYTE_LEN;
1864
0
        start..end
1865
0
    }
1866
}
1867
1868
impl Format<u16> for SingleSubstFormat4<'_> {
1869
    const FORMAT: u16 = 4;
1870
}
1871
1872
impl<'a> MinByteRange<'a> for SingleSubstFormat4<'a> {
1873
0
    fn min_byte_range(&self) -> Range<usize> {
1874
0
        0..self.substitute_glyph_ids_byte_range().end
1875
0
    }
1876
0
    fn min_table_bytes(&self) -> &'a [u8] {
1877
0
        let range = self.min_byte_range();
1878
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
1879
0
    }
1880
}
1881
1882
impl ReadArgs for SingleSubstFormat4<'_> {
1883
    type Args = ();
1884
}
1885
1886
impl<'a> FontRead<'a> for SingleSubstFormat4<'a> {
1887
983
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1888
        #[allow(clippy::absurd_extreme_comparisons)]
1889
983
        if data.len() < Self::MIN_SIZE {
1890
216
            return Err(ReadError::OutOfBounds);
1891
767
        }
1892
767
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1893
0
            return Err(ReadError::OutOfBounds);
1894
        };
1895
767
        Ok(Self { data })
1896
983
    }
1897
}
1898
1899
/// The fields at the start of the table, which are read directly.
1900
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1901
#[repr(C, packed)]
1902
#[doc(hidden)]
1903
pub struct SingleSubstFormat4FixedHeader {
1904
    pub subst_format: BigEndian<u16>,
1905
    pub coverage_offset: BigEndian<Offset32>,
1906
    pub glyph_count: BigEndian<Uint24>,
1907
}
1908
1909
#[allow(clippy::absurd_extreme_comparisons)]
1910
const _: () =
1911
    assert!(std::mem::size_of::<SingleSubstFormat4FixedHeader>() <= SingleSubstFormat4::MIN_SIZE);
1912
1913
/// SingleSubstFormat4 (ISO/IEC 14496-22:2026).
1914
#[derive(Clone)]
1915
pub struct SingleSubstFormat4<'a> {
1916
    data: TableDataRef<'a, SingleSubstFormat4FixedHeader>,
1917
}
1918
1919
#[allow(clippy::needless_lifetimes)]
1920
impl<'a> SingleSubstFormat4<'a> {
1921
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN + Uint24::RAW_BYTE_LEN);
1922
    basic_table_impls!(impl_the_methods);
1923
1924
    /// Format identifier: format = 4
1925
0
    pub fn subst_format(&self) -> u16 {
1926
0
        self.data.header().subst_format.get()
1927
0
    }
1928
1929
    /// Offset to Coverage table, from beginning of substitution
1930
    /// subtable
1931
767
    pub fn coverage_offset(&self) -> Offset32 {
1932
767
        self.data.header().coverage_offset.get()
1933
767
    }
1934
1935
    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1936
767
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1937
767
        let data = self.offset_data();
1938
767
        self.coverage_offset().resolve(data)
1939
767
    }
1940
1941
    /// Number of glyph IDs in the substituteGlyphIDs array
1942
767
    pub fn glyph_count(&self) -> Uint24 {
1943
767
        self.data.header().glyph_count.get()
1944
767
    }
1945
1946
    /// Array of substitute glyph IDs — ordered by Coverage index
1947
0
    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId24>] {
1948
0
        let range = self.substitute_glyph_ids_byte_range();
1949
0
        self.offset_data()
1950
0
            .read_array(range)
1951
0
            .ok()
1952
0
            .unwrap_or_default()
1953
0
    }
1954
1955
767
    pub fn subst_format_byte_range(&self) -> Range<usize> {
1956
767
        let start = 0;
1957
767
        let end = start + u16::RAW_BYTE_LEN;
1958
767
        start..end
1959
767
    }
1960
1961
767
    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1962
767
        let start = self.subst_format_byte_range().end;
1963
767
        let end = start + Offset32::RAW_BYTE_LEN;
1964
767
        start..end
1965
767
    }
1966
1967
767
    pub fn glyph_count_byte_range(&self) -> Range<usize> {
1968
767
        let start = self.coverage_offset_byte_range().end;
1969
767
        let end = start + Uint24::RAW_BYTE_LEN;
1970
767
        start..end
1971
767
    }
1972
1973
767
    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
1974
767
        let glyph_count = self.glyph_count();
1975
767
        let start = self.glyph_count_byte_range().end;
1976
767
        let end =
1977
767
            start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId24::RAW_BYTE_LEN);
1978
767
        start..end
1979
767
    }
1980
}
1981
1982
impl Format<u16> for MultipleSubstFormat2<'_> {
1983
    const FORMAT: u16 = 2;
1984
}
1985
1986
impl<'a> MinByteRange<'a> for MultipleSubstFormat2<'a> {
1987
0
    fn min_byte_range(&self) -> Range<usize> {
1988
0
        0..self.sequence_offsets_byte_range().end
1989
0
    }
1990
0
    fn min_table_bytes(&self) -> &'a [u8] {
1991
0
        let range = self.min_byte_range();
1992
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
1993
0
    }
1994
}
1995
1996
impl ReadArgs for MultipleSubstFormat2<'_> {
1997
    type Args = ();
1998
}
1999
2000
impl<'a> FontRead<'a> for MultipleSubstFormat2<'a> {
2001
110k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2002
        #[allow(clippy::absurd_extreme_comparisons)]
2003
110k
        if data.len() < Self::MIN_SIZE {
2004
322
            return Err(ReadError::OutOfBounds);
2005
110k
        }
2006
110k
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2007
0
            return Err(ReadError::OutOfBounds);
2008
        };
2009
110k
        Ok(Self { data })
2010
110k
    }
2011
}
2012
2013
/// The fields at the start of the table, which are read directly.
2014
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2015
#[repr(C, packed)]
2016
#[doc(hidden)]
2017
pub struct MultipleSubstFormat2FixedHeader {
2018
    pub subst_format: BigEndian<u16>,
2019
    pub coverage_offset: BigEndian<Offset32>,
2020
    pub sequence_count: BigEndian<Uint24>,
2021
}
2022
2023
#[allow(clippy::absurd_extreme_comparisons)]
2024
const _: () = assert!(
2025
    std::mem::size_of::<MultipleSubstFormat2FixedHeader>() <= MultipleSubstFormat2::MIN_SIZE
2026
);
2027
2028
/// MultipleSubstFormat2 (ISO/IEC 14496-22:2026).
2029
#[derive(Clone)]
2030
pub struct MultipleSubstFormat2<'a> {
2031
    data: TableDataRef<'a, MultipleSubstFormat2FixedHeader>,
2032
}
2033
2034
#[allow(clippy::needless_lifetimes)]
2035
impl<'a> MultipleSubstFormat2<'a> {
2036
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN + Uint24::RAW_BYTE_LEN);
2037
    basic_table_impls!(impl_the_methods);
2038
2039
    /// Format identifier: format = 2
2040
0
    pub fn subst_format(&self) -> u16 {
2041
0
        self.data.header().subst_format.get()
2042
0
    }
2043
2044
    /// Offset to Coverage table, from beginning of substitution
2045
    /// subtable
2046
110k
    pub fn coverage_offset(&self) -> Offset32 {
2047
110k
        self.data.header().coverage_offset.get()
2048
110k
    }
2049
2050
    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
2051
110k
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2052
110k
        let data = self.offset_data();
2053
110k
        self.coverage_offset().resolve(data)
2054
110k
    }
2055
2056
    /// Number of Sequence table offsets in the sequenceOffsets array
2057
110k
    pub fn sequence_count(&self) -> Uint24 {
2058
110k
        self.data.header().sequence_count.get()
2059
110k
    }
2060
2061
    /// Array of offsets to Sequence tables. Offsets are from beginning
2062
    /// of substitution subtable, ordered by Coverage index
2063
0
    pub fn sequence_offsets(&self) -> &'a [BigEndian<Offset24>] {
2064
0
        let range = self.sequence_offsets_byte_range();
2065
0
        self.offset_data()
2066
0
            .read_array(range)
2067
0
            .ok()
2068
0
            .unwrap_or_default()
2069
0
    }
2070
2071
    /// A dynamically resolving wrapper for [`sequence_offsets`][Self::sequence_offsets].
2072
0
    pub fn sequences(&self) -> ArrayOfOffsets<'a, Sequence2<'a>, Offset24> {
2073
0
        let data = self.offset_data();
2074
0
        let offsets = self.sequence_offsets();
2075
0
        ArrayOfOffsets::new(offsets, data, ())
2076
0
    }
2077
2078
110k
    pub fn subst_format_byte_range(&self) -> Range<usize> {
2079
110k
        let start = 0;
2080
110k
        let end = start + u16::RAW_BYTE_LEN;
2081
110k
        start..end
2082
110k
    }
2083
2084
110k
    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
2085
110k
        let start = self.subst_format_byte_range().end;
2086
110k
        let end = start + Offset32::RAW_BYTE_LEN;
2087
110k
        start..end
2088
110k
    }
2089
2090
110k
    pub fn sequence_count_byte_range(&self) -> Range<usize> {
2091
110k
        let start = self.coverage_offset_byte_range().end;
2092
110k
        let end = start + Uint24::RAW_BYTE_LEN;
2093
110k
        start..end
2094
110k
    }
2095
2096
110k
    pub fn sequence_offsets_byte_range(&self) -> Range<usize> {
2097
110k
        let sequence_count = self.sequence_count();
2098
110k
        let start = self.sequence_count_byte_range().end;
2099
110k
        let end =
2100
110k
            start + (transforms::to_usize(sequence_count)).saturating_mul(Offset24::RAW_BYTE_LEN);
2101
110k
        start..end
2102
110k
    }
2103
}
2104
2105
impl Format<u16> for AlternateSubstFormat2<'_> {
2106
    const FORMAT: u16 = 2;
2107
}
2108
2109
impl<'a> MinByteRange<'a> for AlternateSubstFormat2<'a> {
2110
0
    fn min_byte_range(&self) -> Range<usize> {
2111
0
        0..self.alternate_set_offsets_byte_range().end
2112
0
    }
2113
0
    fn min_table_bytes(&self) -> &'a [u8] {
2114
0
        let range = self.min_byte_range();
2115
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
2116
0
    }
2117
}
2118
2119
impl ReadArgs for AlternateSubstFormat2<'_> {
2120
    type Args = ();
2121
}
2122
2123
impl<'a> FontRead<'a> for AlternateSubstFormat2<'a> {
2124
2.44k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2125
        #[allow(clippy::absurd_extreme_comparisons)]
2126
2.44k
        if data.len() < Self::MIN_SIZE {
2127
383
            return Err(ReadError::OutOfBounds);
2128
2.06k
        }
2129
2.06k
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2130
0
            return Err(ReadError::OutOfBounds);
2131
        };
2132
2.06k
        Ok(Self { data })
2133
2.44k
    }
2134
}
2135
2136
/// The fields at the start of the table, which are read directly.
2137
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2138
#[repr(C, packed)]
2139
#[doc(hidden)]
2140
pub struct AlternateSubstFormat2FixedHeader {
2141
    pub subst_format: BigEndian<u16>,
2142
    pub coverage_offset: BigEndian<Offset32>,
2143
    pub alternate_set_count: BigEndian<Uint24>,
2144
}
2145
2146
#[allow(clippy::absurd_extreme_comparisons)]
2147
const _: () = assert!(
2148
    std::mem::size_of::<AlternateSubstFormat2FixedHeader>() <= AlternateSubstFormat2::MIN_SIZE
2149
);
2150
2151
/// AlternateSubstFormat2 (ISO/IEC 14496-22:2026).
2152
#[derive(Clone)]
2153
pub struct AlternateSubstFormat2<'a> {
2154
    data: TableDataRef<'a, AlternateSubstFormat2FixedHeader>,
2155
}
2156
2157
#[allow(clippy::needless_lifetimes)]
2158
impl<'a> AlternateSubstFormat2<'a> {
2159
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN + Uint24::RAW_BYTE_LEN);
2160
    basic_table_impls!(impl_the_methods);
2161
2162
    /// Format identifier: format = 2
2163
0
    pub fn subst_format(&self) -> u16 {
2164
0
        self.data.header().subst_format.get()
2165
0
    }
2166
2167
    /// Offset to Coverage table, from beginning of substitution
2168
    /// subtable
2169
2.06k
    pub fn coverage_offset(&self) -> Offset32 {
2170
2.06k
        self.data.header().coverage_offset.get()
2171
2.06k
    }
2172
2173
    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
2174
2.06k
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2175
2.06k
        let data = self.offset_data();
2176
2.06k
        self.coverage_offset().resolve(data)
2177
2.06k
    }
2178
2179
    /// Number of AlternateSet tables
2180
2.06k
    pub fn alternate_set_count(&self) -> Uint24 {
2181
2.06k
        self.data.header().alternate_set_count.get()
2182
2.06k
    }
2183
2184
    /// Array of offsets to AlternateSet tables. Offsets are from
2185
    /// beginning of substitution subtable, ordered by Coverage index
2186
0
    pub fn alternate_set_offsets(&self) -> &'a [BigEndian<Offset24>] {
2187
0
        let range = self.alternate_set_offsets_byte_range();
2188
0
        self.offset_data()
2189
0
            .read_array(range)
2190
0
            .ok()
2191
0
            .unwrap_or_default()
2192
0
    }
2193
2194
    /// A dynamically resolving wrapper for [`alternate_set_offsets`][Self::alternate_set_offsets].
2195
0
    pub fn alternate_sets(&self) -> ArrayOfOffsets<'a, AlternateSet2<'a>, Offset24> {
2196
0
        let data = self.offset_data();
2197
0
        let offsets = self.alternate_set_offsets();
2198
0
        ArrayOfOffsets::new(offsets, data, ())
2199
0
    }
2200
2201
2.06k
    pub fn subst_format_byte_range(&self) -> Range<usize> {
2202
2.06k
        let start = 0;
2203
2.06k
        let end = start + u16::RAW_BYTE_LEN;
2204
2.06k
        start..end
2205
2.06k
    }
2206
2207
2.06k
    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
2208
2.06k
        let start = self.subst_format_byte_range().end;
2209
2.06k
        let end = start + Offset32::RAW_BYTE_LEN;
2210
2.06k
        start..end
2211
2.06k
    }
2212
2213
2.06k
    pub fn alternate_set_count_byte_range(&self) -> Range<usize> {
2214
2.06k
        let start = self.coverage_offset_byte_range().end;
2215
2.06k
        let end = start + Uint24::RAW_BYTE_LEN;
2216
2.06k
        start..end
2217
2.06k
    }
2218
2219
2.06k
    pub fn alternate_set_offsets_byte_range(&self) -> Range<usize> {
2220
2.06k
        let alternate_set_count = self.alternate_set_count();
2221
2.06k
        let start = self.alternate_set_count_byte_range().end;
2222
2.06k
        let end = start
2223
2.06k
            + (transforms::to_usize(alternate_set_count)).saturating_mul(Offset24::RAW_BYTE_LEN);
2224
2.06k
        start..end
2225
2.06k
    }
2226
}
2227
2228
impl Format<u16> for LigatureSubstFormat2<'_> {
2229
    const FORMAT: u16 = 2;
2230
}
2231
2232
impl<'a> MinByteRange<'a> for LigatureSubstFormat2<'a> {
2233
0
    fn min_byte_range(&self) -> Range<usize> {
2234
0
        0..self.ligature_set_offsets_byte_range().end
2235
0
    }
2236
0
    fn min_table_bytes(&self) -> &'a [u8] {
2237
0
        let range = self.min_byte_range();
2238
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
2239
0
    }
2240
}
2241
2242
impl ReadArgs for LigatureSubstFormat2<'_> {
2243
    type Args = ();
2244
}
2245
2246
impl<'a> FontRead<'a> for LigatureSubstFormat2<'a> {
2247
22.0k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2248
        #[allow(clippy::absurd_extreme_comparisons)]
2249
22.0k
        if data.len() < Self::MIN_SIZE {
2250
252
            return Err(ReadError::OutOfBounds);
2251
21.7k
        }
2252
21.7k
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2253
0
            return Err(ReadError::OutOfBounds);
2254
        };
2255
21.7k
        Ok(Self { data })
2256
22.0k
    }
2257
}
2258
2259
/// The fields at the start of the table, which are read directly.
2260
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2261
#[repr(C, packed)]
2262
#[doc(hidden)]
2263
pub struct LigatureSubstFormat2FixedHeader {
2264
    pub subst_format: BigEndian<u16>,
2265
    pub coverage_offset: BigEndian<Offset32>,
2266
    pub ligature_set_count: BigEndian<Uint24>,
2267
}
2268
2269
#[allow(clippy::absurd_extreme_comparisons)]
2270
const _: () = assert!(
2271
    std::mem::size_of::<LigatureSubstFormat2FixedHeader>() <= LigatureSubstFormat2::MIN_SIZE
2272
);
2273
2274
/// LigatureSubstFormat2 (ISO/IEC 14496-22:2026).
2275
#[derive(Clone)]
2276
pub struct LigatureSubstFormat2<'a> {
2277
    data: TableDataRef<'a, LigatureSubstFormat2FixedHeader>,
2278
}
2279
2280
#[allow(clippy::needless_lifetimes)]
2281
impl<'a> LigatureSubstFormat2<'a> {
2282
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN + Uint24::RAW_BYTE_LEN);
2283
    basic_table_impls!(impl_the_methods);
2284
2285
    /// Format identifier: format = 2
2286
0
    pub fn subst_format(&self) -> u16 {
2287
0
        self.data.header().subst_format.get()
2288
0
    }
2289
2290
    /// Offset to Coverage table, from beginning of substitution
2291
    /// subtable
2292
21.7k
    pub fn coverage_offset(&self) -> Offset32 {
2293
21.7k
        self.data.header().coverage_offset.get()
2294
21.7k
    }
2295
2296
    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
2297
21.7k
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2298
21.7k
        let data = self.offset_data();
2299
21.7k
        self.coverage_offset().resolve(data)
2300
21.7k
    }
2301
2302
    /// Number of LigatureSet tables
2303
21.7k
    pub fn ligature_set_count(&self) -> Uint24 {
2304
21.7k
        self.data.header().ligature_set_count.get()
2305
21.7k
    }
2306
2307
    /// Array of offsets to LigatureSet tables. Offsets are from
2308
    /// beginning of substitution subtable, ordered by Coverage index
2309
0
    pub fn ligature_set_offsets(&self) -> &'a [BigEndian<Offset24>] {
2310
0
        let range = self.ligature_set_offsets_byte_range();
2311
0
        self.offset_data()
2312
0
            .read_array(range)
2313
0
            .ok()
2314
0
            .unwrap_or_default()
2315
0
    }
2316
2317
    /// A dynamically resolving wrapper for [`ligature_set_offsets`][Self::ligature_set_offsets].
2318
0
    pub fn ligature_sets(&self) -> ArrayOfOffsets<'a, LigatureSet2<'a>, Offset24> {
2319
0
        let data = self.offset_data();
2320
0
        let offsets = self.ligature_set_offsets();
2321
0
        ArrayOfOffsets::new(offsets, data, ())
2322
0
    }
2323
2324
21.7k
    pub fn subst_format_byte_range(&self) -> Range<usize> {
2325
21.7k
        let start = 0;
2326
21.7k
        let end = start + u16::RAW_BYTE_LEN;
2327
21.7k
        start..end
2328
21.7k
    }
2329
2330
21.7k
    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
2331
21.7k
        let start = self.subst_format_byte_range().end;
2332
21.7k
        let end = start + Offset32::RAW_BYTE_LEN;
2333
21.7k
        start..end
2334
21.7k
    }
2335
2336
21.7k
    pub fn ligature_set_count_byte_range(&self) -> Range<usize> {
2337
21.7k
        let start = self.coverage_offset_byte_range().end;
2338
21.7k
        let end = start + Uint24::RAW_BYTE_LEN;
2339
21.7k
        start..end
2340
21.7k
    }
2341
2342
21.7k
    pub fn ligature_set_offsets_byte_range(&self) -> Range<usize> {
2343
21.7k
        let ligature_set_count = self.ligature_set_count();
2344
21.7k
        let start = self.ligature_set_count_byte_range().end;
2345
21.7k
        let end = start
2346
21.7k
            + (transforms::to_usize(ligature_set_count)).saturating_mul(Offset24::RAW_BYTE_LEN);
2347
21.7k
        start..end
2348
21.7k
    }
2349
}
2350
2351
impl Format<u16> for ReverseChainSingleSubstFormat2<'_> {
2352
    const FORMAT: u16 = 2;
2353
}
2354
2355
impl<'a> MinByteRange<'a> for ReverseChainSingleSubstFormat2<'a> {
2356
0
    fn min_byte_range(&self) -> Range<usize> {
2357
0
        0..self.substitute_glyph_ids_byte_range().end
2358
0
    }
2359
0
    fn min_table_bytes(&self) -> &'a [u8] {
2360
0
        let range = self.min_byte_range();
2361
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
2362
0
    }
2363
}
2364
2365
impl ReadArgs for ReverseChainSingleSubstFormat2<'_> {
2366
    type Args = ();
2367
}
2368
2369
impl<'a> FontRead<'a> for ReverseChainSingleSubstFormat2<'a> {
2370
3.98k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2371
        #[allow(clippy::absurd_extreme_comparisons)]
2372
3.98k
        if data.len() < Self::MIN_SIZE {
2373
356
            return Err(ReadError::OutOfBounds);
2374
3.62k
        }
2375
3.62k
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2376
0
            return Err(ReadError::OutOfBounds);
2377
        };
2378
3.62k
        Ok(Self { data })
2379
3.98k
    }
2380
}
2381
2382
/// The fields at the start of the table, which are read directly.
2383
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2384
#[repr(C, packed)]
2385
#[doc(hidden)]
2386
pub struct ReverseChainSingleSubstFormat2FixedHeader {
2387
    pub subst_format: BigEndian<u16>,
2388
    pub coverage_offset: BigEndian<Offset32>,
2389
    pub backtrack_glyph_count: BigEndian<u16>,
2390
}
2391
2392
#[allow(clippy::absurd_extreme_comparisons)]
2393
const _: () = assert!(
2394
    std::mem::size_of::<ReverseChainSingleSubstFormat2FixedHeader>()
2395
        <= ReverseChainSingleSubstFormat2::MIN_SIZE
2396
);
2397
2398
/// ReverseChainSingleSubstFormat2 (ISO/IEC 14496-22:2026).
2399
#[derive(Clone)]
2400
pub struct ReverseChainSingleSubstFormat2<'a> {
2401
    data: TableDataRef<'a, ReverseChainSingleSubstFormat2FixedHeader>,
2402
}
2403
2404
#[allow(clippy::needless_lifetimes)]
2405
impl<'a> ReverseChainSingleSubstFormat2<'a> {
2406
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
2407
        + Offset32::RAW_BYTE_LEN
2408
        + u16::RAW_BYTE_LEN
2409
        + u16::RAW_BYTE_LEN
2410
        + Uint24::RAW_BYTE_LEN);
2411
    basic_table_impls!(impl_the_methods);
2412
2413
    /// Format identifier: format = 2
2414
0
    pub fn subst_format(&self) -> u16 {
2415
0
        self.data.header().subst_format.get()
2416
0
    }
2417
2418
    /// Offset to Coverage table, from beginning of substitution
2419
    /// subtable.
2420
3.62k
    pub fn coverage_offset(&self) -> Offset32 {
2421
3.62k
        self.data.header().coverage_offset.get()
2422
3.62k
    }
2423
2424
    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
2425
3.62k
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2426
3.62k
        let data = self.offset_data();
2427
3.62k
        self.coverage_offset().resolve(data)
2428
3.62k
    }
2429
2430
    /// Number of glyphs in the backtrack sequence.
2431
15.0k
    pub fn backtrack_glyph_count(&self) -> u16 {
2432
15.0k
        self.data.header().backtrack_glyph_count.get()
2433
15.0k
    }
2434
2435
    /// Array of offsets to coverage tables in backtrack sequence, in
2436
    /// glyph sequence order.
2437
0
    pub fn backtrack_coverage_offsets(&self) -> &'a [BigEndian<Offset24>] {
2438
0
        let range = self.backtrack_coverage_offsets_byte_range();
2439
0
        self.offset_data()
2440
0
            .read_array(range)
2441
0
            .ok()
2442
0
            .unwrap_or_default()
2443
0
    }
2444
2445
    /// A dynamically resolving wrapper for [`backtrack_coverage_offsets`][Self::backtrack_coverage_offsets].
2446
0
    pub fn backtrack_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset24> {
2447
0
        let data = self.offset_data();
2448
0
        let offsets = self.backtrack_coverage_offsets();
2449
0
        ArrayOfOffsets::new(offsets, data, ())
2450
0
    }
2451
2452
    /// Number of glyphs in lookahead sequence.
2453
7.43k
    pub fn lookahead_glyph_count(&self) -> u16 {
2454
7.43k
        let range = self.lookahead_glyph_count_byte_range();
2455
7.43k
        self.offset_data()
2456
7.43k
            .read_at(range.start)
2457
7.43k
            .ok()
2458
7.43k
            .unwrap_or_default()
2459
7.43k
    }
2460
2461
    /// Array of offsets to coverage tables in lookahead sequence, in
2462
    /// glyph sequence order.
2463
0
    pub fn lookahead_coverage_offsets(&self) -> &'a [BigEndian<Offset24>] {
2464
0
        let range = self.lookahead_coverage_offsets_byte_range();
2465
0
        self.offset_data()
2466
0
            .read_array(range)
2467
0
            .ok()
2468
0
            .unwrap_or_default()
2469
0
    }
2470
2471
    /// A dynamically resolving wrapper for [`lookahead_coverage_offsets`][Self::lookahead_coverage_offsets].
2472
0
    pub fn lookahead_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset24> {
2473
0
        let data = self.offset_data();
2474
0
        let offsets = self.lookahead_coverage_offsets();
2475
0
        ArrayOfOffsets::new(offsets, data, ())
2476
0
    }
2477
2478
    /// Number of glyph IDs in the substituteGlyphIDs array.
2479
3.62k
    pub fn glyph_count(&self) -> Uint24 {
2480
3.62k
        let range = self.glyph_count_byte_range();
2481
3.62k
        self.offset_data()
2482
3.62k
            .read_at(range.start)
2483
3.62k
            .ok()
2484
3.62k
            .unwrap_or_default()
2485
3.62k
    }
2486
2487
    /// Array of substitute glyph IDs — ordered by Coverage index.
2488
0
    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId24>] {
2489
0
        let range = self.substitute_glyph_ids_byte_range();
2490
0
        self.offset_data()
2491
0
            .read_array(range)
2492
0
            .ok()
2493
0
            .unwrap_or_default()
2494
0
    }
2495
2496
15.0k
    pub fn subst_format_byte_range(&self) -> Range<usize> {
2497
15.0k
        let start = 0;
2498
15.0k
        let end = start + u16::RAW_BYTE_LEN;
2499
15.0k
        start..end
2500
15.0k
    }
2501
2502
15.0k
    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
2503
15.0k
        let start = self.subst_format_byte_range().end;
2504
15.0k
        let end = start + Offset32::RAW_BYTE_LEN;
2505
15.0k
        start..end
2506
15.0k
    }
2507
2508
15.0k
    pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
2509
15.0k
        let start = self.coverage_offset_byte_range().end;
2510
15.0k
        let end = start + u16::RAW_BYTE_LEN;
2511
15.0k
        start..end
2512
15.0k
    }
2513
2514
15.0k
    pub fn backtrack_coverage_offsets_byte_range(&self) -> Range<usize> {
2515
15.0k
        let backtrack_glyph_count = self.backtrack_glyph_count();
2516
15.0k
        let start = self.backtrack_glyph_count_byte_range().end;
2517
15.0k
        let end = start
2518
15.0k
            + (transforms::to_usize(backtrack_glyph_count)).saturating_mul(Offset24::RAW_BYTE_LEN);
2519
15.0k
        start..end
2520
15.0k
    }
2521
2522
14.8k
    pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
2523
14.8k
        let start = self.backtrack_coverage_offsets_byte_range().end;
2524
14.8k
        let end = start + u16::RAW_BYTE_LEN;
2525
14.8k
        start..end
2526
14.8k
    }
2527
2528
7.43k
    pub fn lookahead_coverage_offsets_byte_range(&self) -> Range<usize> {
2529
7.43k
        let lookahead_glyph_count = self.lookahead_glyph_count();
2530
7.43k
        let start = self.lookahead_glyph_count_byte_range().end;
2531
7.43k
        let end = start
2532
7.43k
            + (transforms::to_usize(lookahead_glyph_count)).saturating_mul(Offset24::RAW_BYTE_LEN);
2533
7.43k
        start..end
2534
7.43k
    }
2535
2536
7.24k
    pub fn glyph_count_byte_range(&self) -> Range<usize> {
2537
7.24k
        let start = self.lookahead_coverage_offsets_byte_range().end;
2538
7.24k
        let end = start + Uint24::RAW_BYTE_LEN;
2539
7.24k
        start..end
2540
7.24k
    }
2541
2542
3.62k
    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
2543
3.62k
        let glyph_count = self.glyph_count();
2544
3.62k
        let start = self.glyph_count_byte_range().end;
2545
3.62k
        let end =
2546
3.62k
            start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId24::RAW_BYTE_LEN);
2547
3.62k
        start..end
2548
3.62k
    }
2549
}
2550
2551
impl<'a> MinByteRange<'a> for Sequence2<'a> {
2552
0
    fn min_byte_range(&self) -> Range<usize> {
2553
0
        0..self.substitute_glyph_ids_byte_range().end
2554
0
    }
2555
0
    fn min_table_bytes(&self) -> &'a [u8] {
2556
0
        let range = self.min_byte_range();
2557
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
2558
0
    }
2559
}
2560
2561
impl ReadArgs for Sequence2<'_> {
2562
    type Args = ();
2563
}
2564
2565
impl<'a> FontRead<'a> for Sequence2<'a> {
2566
0
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2567
        #[allow(clippy::absurd_extreme_comparisons)]
2568
0
        if data.len() < Self::MIN_SIZE {
2569
0
            return Err(ReadError::OutOfBounds);
2570
0
        }
2571
0
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2572
0
            return Err(ReadError::OutOfBounds);
2573
        };
2574
0
        Ok(Self { data })
2575
0
    }
2576
}
2577
2578
/// The fields at the start of the table, which are read directly.
2579
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2580
#[repr(C, packed)]
2581
#[doc(hidden)]
2582
pub struct Sequence2FixedHeader {
2583
    pub glyph_count: BigEndian<u16>,
2584
}
2585
2586
#[allow(clippy::absurd_extreme_comparisons)]
2587
const _: () = assert!(std::mem::size_of::<Sequence2FixedHeader>() <= Sequence2::MIN_SIZE);
2588
2589
/// Sequence2 (ISO/IEC 14496-22:2026).
2590
#[derive(Clone)]
2591
pub struct Sequence2<'a> {
2592
    data: TableDataRef<'a, Sequence2FixedHeader>,
2593
}
2594
2595
#[allow(clippy::needless_lifetimes)]
2596
impl<'a> Sequence2<'a> {
2597
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
2598
    basic_table_impls!(impl_the_methods);
2599
2600
    /// Number of glyph IDs in the substituteGlyphIDs array. This must
2601
    /// always be greater than 0.
2602
0
    pub fn glyph_count(&self) -> u16 {
2603
0
        self.data.header().glyph_count.get()
2604
0
    }
2605
2606
    /// String of glyph IDs to substitute
2607
0
    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId24>] {
2608
0
        let range = self.substitute_glyph_ids_byte_range();
2609
0
        self.offset_data()
2610
0
            .read_array(range)
2611
0
            .ok()
2612
0
            .unwrap_or_default()
2613
0
    }
2614
2615
0
    pub fn glyph_count_byte_range(&self) -> Range<usize> {
2616
0
        let start = 0;
2617
0
        let end = start + u16::RAW_BYTE_LEN;
2618
0
        start..end
2619
0
    }
2620
2621
0
    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
2622
0
        let glyph_count = self.glyph_count();
2623
0
        let start = self.glyph_count_byte_range().end;
2624
0
        let end =
2625
0
            start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId24::RAW_BYTE_LEN);
2626
0
        start..end
2627
0
    }
2628
}
2629
2630
const _: () = assert!(FontData::default_data_long_enough(Sequence2::MIN_SIZE));
2631
2632
impl Default for Sequence2<'_> {
2633
0
    fn default() -> Self {
2634
        Self {
2635
0
            data: TableDataRef::new(FontData::default_table_data().as_bytes())
2636
0
                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
2637
        }
2638
0
    }
2639
}
2640
2641
impl<'a> MinByteRange<'a> for AlternateSet2<'a> {
2642
0
    fn min_byte_range(&self) -> Range<usize> {
2643
0
        0..self.alternate_glyph_ids_byte_range().end
2644
0
    }
2645
0
    fn min_table_bytes(&self) -> &'a [u8] {
2646
0
        let range = self.min_byte_range();
2647
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
2648
0
    }
2649
}
2650
2651
impl ReadArgs for AlternateSet2<'_> {
2652
    type Args = ();
2653
}
2654
2655
impl<'a> FontRead<'a> for AlternateSet2<'a> {
2656
0
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2657
        #[allow(clippy::absurd_extreme_comparisons)]
2658
0
        if data.len() < Self::MIN_SIZE {
2659
0
            return Err(ReadError::OutOfBounds);
2660
0
        }
2661
0
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2662
0
            return Err(ReadError::OutOfBounds);
2663
        };
2664
0
        Ok(Self { data })
2665
0
    }
2666
}
2667
2668
/// The fields at the start of the table, which are read directly.
2669
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2670
#[repr(C, packed)]
2671
#[doc(hidden)]
2672
pub struct AlternateSet2FixedHeader {
2673
    pub glyph_count: BigEndian<u16>,
2674
}
2675
2676
#[allow(clippy::absurd_extreme_comparisons)]
2677
const _: () = assert!(std::mem::size_of::<AlternateSet2FixedHeader>() <= AlternateSet2::MIN_SIZE);
2678
2679
/// AlternateSet2 (ISO/IEC 14496-22:2026).
2680
#[derive(Clone)]
2681
pub struct AlternateSet2<'a> {
2682
    data: TableDataRef<'a, AlternateSet2FixedHeader>,
2683
}
2684
2685
#[allow(clippy::needless_lifetimes)]
2686
impl<'a> AlternateSet2<'a> {
2687
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
2688
    basic_table_impls!(impl_the_methods);
2689
2690
    /// Number of glyph IDs in the alternateGlyphIDs array
2691
0
    pub fn glyph_count(&self) -> u16 {
2692
0
        self.data.header().glyph_count.get()
2693
0
    }
2694
2695
    /// Array of alternate glyph IDs, in arbitrary order
2696
0
    pub fn alternate_glyph_ids(&self) -> &'a [BigEndian<GlyphId24>] {
2697
0
        let range = self.alternate_glyph_ids_byte_range();
2698
0
        self.offset_data()
2699
0
            .read_array(range)
2700
0
            .ok()
2701
0
            .unwrap_or_default()
2702
0
    }
2703
2704
0
    pub fn glyph_count_byte_range(&self) -> Range<usize> {
2705
0
        let start = 0;
2706
0
        let end = start + u16::RAW_BYTE_LEN;
2707
0
        start..end
2708
0
    }
2709
2710
0
    pub fn alternate_glyph_ids_byte_range(&self) -> Range<usize> {
2711
0
        let glyph_count = self.glyph_count();
2712
0
        let start = self.glyph_count_byte_range().end;
2713
0
        let end =
2714
0
            start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId24::RAW_BYTE_LEN);
2715
0
        start..end
2716
0
    }
2717
}
2718
2719
const _: () = assert!(FontData::default_data_long_enough(AlternateSet2::MIN_SIZE));
2720
2721
impl Default for AlternateSet2<'_> {
2722
0
    fn default() -> Self {
2723
        Self {
2724
0
            data: TableDataRef::new(FontData::default_table_data().as_bytes())
2725
0
                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
2726
        }
2727
0
    }
2728
}
2729
2730
impl<'a> MinByteRange<'a> for LigatureSet2<'a> {
2731
0
    fn min_byte_range(&self) -> Range<usize> {
2732
0
        0..self.ligature_offsets_byte_range().end
2733
0
    }
2734
0
    fn min_table_bytes(&self) -> &'a [u8] {
2735
0
        let range = self.min_byte_range();
2736
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
2737
0
    }
2738
}
2739
2740
impl ReadArgs for LigatureSet2<'_> {
2741
    type Args = ();
2742
}
2743
2744
impl<'a> FontRead<'a> for LigatureSet2<'a> {
2745
0
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2746
        #[allow(clippy::absurd_extreme_comparisons)]
2747
0
        if data.len() < Self::MIN_SIZE {
2748
0
            return Err(ReadError::OutOfBounds);
2749
0
        }
2750
0
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2751
0
            return Err(ReadError::OutOfBounds);
2752
        };
2753
0
        Ok(Self { data })
2754
0
    }
2755
}
2756
2757
/// The fields at the start of the table, which are read directly.
2758
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2759
#[repr(C, packed)]
2760
#[doc(hidden)]
2761
pub struct LigatureSet2FixedHeader {
2762
    pub ligature_count: BigEndian<u16>,
2763
}
2764
2765
#[allow(clippy::absurd_extreme_comparisons)]
2766
const _: () = assert!(std::mem::size_of::<LigatureSet2FixedHeader>() <= LigatureSet2::MIN_SIZE);
2767
2768
/// LigatureSet2 (ISO/IEC 14496-22:2026).
2769
#[derive(Clone)]
2770
pub struct LigatureSet2<'a> {
2771
    data: TableDataRef<'a, LigatureSet2FixedHeader>,
2772
}
2773
2774
#[allow(clippy::needless_lifetimes)]
2775
impl<'a> LigatureSet2<'a> {
2776
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
2777
    basic_table_impls!(impl_the_methods);
2778
2779
    /// Number of Ligature tables
2780
0
    pub fn ligature_count(&self) -> u16 {
2781
0
        self.data.header().ligature_count.get()
2782
0
    }
2783
2784
    /// Array of offsets to Ligature tables. Offsets are from beginning
2785
    /// of LigatureSet2 table, ordered by preference.
2786
0
    pub fn ligature_offsets(&self) -> &'a [BigEndian<Offset24>] {
2787
0
        let range = self.ligature_offsets_byte_range();
2788
0
        self.offset_data()
2789
0
            .read_array(range)
2790
0
            .ok()
2791
0
            .unwrap_or_default()
2792
0
    }
2793
2794
    /// A dynamically resolving wrapper for [`ligature_offsets`][Self::ligature_offsets].
2795
0
    pub fn ligatures(&self) -> ArrayOfOffsets<'a, Ligature2<'a>, Offset24> {
2796
0
        let data = self.offset_data();
2797
0
        let offsets = self.ligature_offsets();
2798
0
        ArrayOfOffsets::new(offsets, data, ())
2799
0
    }
2800
2801
0
    pub fn ligature_count_byte_range(&self) -> Range<usize> {
2802
0
        let start = 0;
2803
0
        let end = start + u16::RAW_BYTE_LEN;
2804
0
        start..end
2805
0
    }
2806
2807
0
    pub fn ligature_offsets_byte_range(&self) -> Range<usize> {
2808
0
        let ligature_count = self.ligature_count();
2809
0
        let start = self.ligature_count_byte_range().end;
2810
0
        let end =
2811
0
            start + (transforms::to_usize(ligature_count)).saturating_mul(Offset24::RAW_BYTE_LEN);
2812
0
        start..end
2813
0
    }
2814
}
2815
2816
const _: () = assert!(FontData::default_data_long_enough(LigatureSet2::MIN_SIZE));
2817
2818
impl Default for LigatureSet2<'_> {
2819
0
    fn default() -> Self {
2820
        Self {
2821
0
            data: TableDataRef::new(FontData::default_table_data().as_bytes())
2822
0
                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
2823
        }
2824
0
    }
2825
}
2826
2827
impl<'a> MinByteRange<'a> for Ligature2<'a> {
2828
0
    fn min_byte_range(&self) -> Range<usize> {
2829
0
        0..self.component_glyph_ids_byte_range().end
2830
0
    }
2831
0
    fn min_table_bytes(&self) -> &'a [u8] {
2832
0
        let range = self.min_byte_range();
2833
0
        self.offset_data().as_bytes().get(range).unwrap_or_default()
2834
0
    }
2835
}
2836
2837
impl ReadArgs for Ligature2<'_> {
2838
    type Args = ();
2839
}
2840
2841
impl<'a> FontRead<'a> for Ligature2<'a> {
2842
0
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2843
        #[allow(clippy::absurd_extreme_comparisons)]
2844
0
        if data.len() < Self::MIN_SIZE {
2845
0
            return Err(ReadError::OutOfBounds);
2846
0
        }
2847
0
        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2848
0
            return Err(ReadError::OutOfBounds);
2849
        };
2850
0
        Ok(Self { data })
2851
0
    }
2852
}
2853
2854
/// The fields at the start of the table, which are read directly.
2855
#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2856
#[repr(C, packed)]
2857
#[doc(hidden)]
2858
pub struct Ligature2FixedHeader {
2859
    pub ligature_glyph: BigEndian<GlyphId24>,
2860
    pub component_count: BigEndian<u16>,
2861
}
2862
2863
#[allow(clippy::absurd_extreme_comparisons)]
2864
const _: () = assert!(std::mem::size_of::<Ligature2FixedHeader>() <= Ligature2::MIN_SIZE);
2865
2866
/// Ligature2 (ISO/IEC 14496-22:2026).
2867
#[derive(Clone)]
2868
pub struct Ligature2<'a> {
2869
    data: TableDataRef<'a, Ligature2FixedHeader>,
2870
}
2871
2872
#[allow(clippy::needless_lifetimes)]
2873
impl<'a> Ligature2<'a> {
2874
    pub const MIN_SIZE: usize = (GlyphId24::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2875
    basic_table_impls!(impl_the_methods);
2876
2877
    /// glyph ID of ligature to substitute
2878
0
    pub fn ligature_glyph(&self) -> GlyphId24 {
2879
0
        self.data.header().ligature_glyph.get()
2880
0
    }
2881
2882
    /// Number of components in the ligature
2883
0
    pub fn component_count(&self) -> u16 {
2884
0
        self.data.header().component_count.get()
2885
0
    }
2886
2887
    /// Array of component glyph IDs — start with the second
2888
    /// component, ordered in writing direction
2889
0
    pub fn component_glyph_ids(&self) -> &'a [BigEndian<GlyphId24>] {
2890
0
        let range = self.component_glyph_ids_byte_range();
2891
0
        self.offset_data()
2892
0
            .read_array(range)
2893
0
            .ok()
2894
0
            .unwrap_or_default()
2895
0
    }
2896
2897
0
    pub fn ligature_glyph_byte_range(&self) -> Range<usize> {
2898
0
        let start = 0;
2899
0
        let end = start + GlyphId24::RAW_BYTE_LEN;
2900
0
        start..end
2901
0
    }
2902
2903
0
    pub fn component_count_byte_range(&self) -> Range<usize> {
2904
0
        let start = self.ligature_glyph_byte_range().end;
2905
0
        let end = start + u16::RAW_BYTE_LEN;
2906
0
        start..end
2907
0
    }
2908
2909
0
    pub fn component_glyph_ids_byte_range(&self) -> Range<usize> {
2910
0
        let component_count = self.component_count();
2911
0
        let start = self.component_count_byte_range().end;
2912
0
        let end = start
2913
0
            + (transforms::subtract(component_count, 1_usize))
2914
0
                .saturating_mul(GlyphId24::RAW_BYTE_LEN);
2915
0
        start..end
2916
0
    }
2917
}
2918
2919
const _: () = assert!(FontData::default_data_long_enough(Ligature2::MIN_SIZE));
2920
2921
impl Default for Ligature2<'_> {
2922
0
    fn default() -> Self {
2923
        Self {
2924
0
            data: TableDataRef::new(FontData::default_table_data().as_bytes())
2925
0
                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
2926
        }
2927
0
    }
2928
}
2929
2930
/// MultipleSubst (ISO/IEC 14496-22:2026).
2931
#[derive(Clone)]
2932
pub enum MultipleSubst<'a> {
2933
    Format1(MultipleSubstFormat1<'a>),
2934
    Format2(MultipleSubstFormat2<'a>),
2935
}
2936
2937
impl Default for MultipleSubst<'_> {
2938
0
    fn default() -> Self {
2939
0
        Self::Format1(Default::default())
2940
0
    }
2941
}
2942
2943
impl<'a> MultipleSubst<'a> {
2944
    ///Return the `FontData` used to resolve offsets for this table.
2945
0
    pub fn offset_data(&self) -> FontData<'a> {
2946
0
        match self {
2947
0
            Self::Format1(item) => item.offset_data(),
2948
0
            Self::Format2(item) => item.offset_data(),
2949
        }
2950
0
    }
2951
2952
    /// Format identifier: format = 1
2953
0
    pub fn subst_format(&self) -> u16 {
2954
0
        match self {
2955
0
            Self::Format1(item) => item.subst_format(),
2956
0
            Self::Format2(item) => item.subst_format(),
2957
        }
2958
0
    }
2959
}
2960
2961
impl ReadArgs for MultipleSubst<'_> {
2962
    type Args = ();
2963
}
2964
2965
impl<'a> FontRead<'a> for MultipleSubst<'a> {
2966
    #[inline]
2967
313k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2968
313k
        let format: u16 = data.read_at(0usize)?;
2969
312k
        match format {
2970
10.9k
            MultipleSubstFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
2971
110k
            MultipleSubstFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
2972
190k
            other => Err(ReadError::InvalidFormat(other.into())),
2973
        }
2974
313k
    }
<read_fonts::tables::gsub::MultipleSubst as read_fonts::read::FontRead>::read_with_args
Line
Count
Source
2967
313k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2968
313k
        let format: u16 = data.read_at(0usize)?;
2969
312k
        match format {
2970
10.9k
            MultipleSubstFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
2971
110k
            MultipleSubstFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
2972
190k
            other => Err(ReadError::InvalidFormat(other.into())),
2973
        }
2974
313k
    }
Unexecuted instantiation: <read_fonts::tables::gsub::MultipleSubst as read_fonts::read::FontRead>::read_with_args
2975
}
2976
2977
impl<'a> MinByteRange<'a> for MultipleSubst<'a> {
2978
0
    fn min_byte_range(&self) -> Range<usize> {
2979
0
        match self {
2980
0
            Self::Format1(item) => item.min_byte_range(),
2981
0
            Self::Format2(item) => item.min_byte_range(),
2982
        }
2983
0
    }
2984
0
    fn min_table_bytes(&self) -> &'a [u8] {
2985
0
        match self {
2986
0
            Self::Format1(item) => item.min_table_bytes(),
2987
0
            Self::Format2(item) => item.min_table_bytes(),
2988
        }
2989
0
    }
2990
}
2991
2992
/// AlternateSubst (ISO/IEC 14496-22:2026).
2993
#[derive(Clone)]
2994
pub enum AlternateSubst<'a> {
2995
    Format1(AlternateSubstFormat1<'a>),
2996
    Format2(AlternateSubstFormat2<'a>),
2997
}
2998
2999
impl Default for AlternateSubst<'_> {
3000
0
    fn default() -> Self {
3001
0
        Self::Format1(Default::default())
3002
0
    }
3003
}
3004
3005
impl<'a> AlternateSubst<'a> {
3006
    ///Return the `FontData` used to resolve offsets for this table.
3007
0
    pub fn offset_data(&self) -> FontData<'a> {
3008
0
        match self {
3009
0
            Self::Format1(item) => item.offset_data(),
3010
0
            Self::Format2(item) => item.offset_data(),
3011
        }
3012
0
    }
3013
3014
    /// Format identifier: format = 1
3015
0
    pub fn subst_format(&self) -> u16 {
3016
0
        match self {
3017
0
            Self::Format1(item) => item.subst_format(),
3018
0
            Self::Format2(item) => item.subst_format(),
3019
        }
3020
0
    }
3021
}
3022
3023
impl ReadArgs for AlternateSubst<'_> {
3024
    type Args = ();
3025
}
3026
3027
impl<'a> FontRead<'a> for AlternateSubst<'a> {
3028
    #[inline]
3029
38.0k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3030
38.0k
        let format: u16 = data.read_at(0usize)?;
3031
37.6k
        match format {
3032
1.77k
            AlternateSubstFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
3033
2.44k
            AlternateSubstFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
3034
33.4k
            other => Err(ReadError::InvalidFormat(other.into())),
3035
        }
3036
38.0k
    }
<read_fonts::tables::gsub::AlternateSubst as read_fonts::read::FontRead>::read_with_args
Line
Count
Source
3029
38.0k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3030
38.0k
        let format: u16 = data.read_at(0usize)?;
3031
37.6k
        match format {
3032
1.77k
            AlternateSubstFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
3033
2.44k
            AlternateSubstFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
3034
33.4k
            other => Err(ReadError::InvalidFormat(other.into())),
3035
        }
3036
38.0k
    }
Unexecuted instantiation: <read_fonts::tables::gsub::AlternateSubst as read_fonts::read::FontRead>::read_with_args
3037
}
3038
3039
impl<'a> MinByteRange<'a> for AlternateSubst<'a> {
3040
0
    fn min_byte_range(&self) -> Range<usize> {
3041
0
        match self {
3042
0
            Self::Format1(item) => item.min_byte_range(),
3043
0
            Self::Format2(item) => item.min_byte_range(),
3044
        }
3045
0
    }
3046
0
    fn min_table_bytes(&self) -> &'a [u8] {
3047
0
        match self {
3048
0
            Self::Format1(item) => item.min_table_bytes(),
3049
0
            Self::Format2(item) => item.min_table_bytes(),
3050
        }
3051
0
    }
3052
}
3053
3054
/// LigatureSubst (ISO/IEC 14496-22:2026).
3055
#[derive(Clone)]
3056
pub enum LigatureSubst<'a> {
3057
    Format1(LigatureSubstFormat1<'a>),
3058
    Format2(LigatureSubstFormat2<'a>),
3059
}
3060
3061
impl Default for LigatureSubst<'_> {
3062
0
    fn default() -> Self {
3063
0
        Self::Format1(Default::default())
3064
0
    }
3065
}
3066
3067
impl<'a> LigatureSubst<'a> {
3068
    ///Return the `FontData` used to resolve offsets for this table.
3069
0
    pub fn offset_data(&self) -> FontData<'a> {
3070
0
        match self {
3071
0
            Self::Format1(item) => item.offset_data(),
3072
0
            Self::Format2(item) => item.offset_data(),
3073
        }
3074
0
    }
3075
3076
    /// Format identifier: format = 1
3077
0
    pub fn subst_format(&self) -> u16 {
3078
0
        match self {
3079
0
            Self::Format1(item) => item.subst_format(),
3080
0
            Self::Format2(item) => item.subst_format(),
3081
        }
3082
0
    }
3083
}
3084
3085
impl ReadArgs for LigatureSubst<'_> {
3086
    type Args = ();
3087
}
3088
3089
impl<'a> FontRead<'a> for LigatureSubst<'a> {
3090
    #[inline]
3091
921k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3092
921k
        let format: u16 = data.read_at(0usize)?;
3093
921k
        match format {
3094
20.2k
            LigatureSubstFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
3095
22.0k
            LigatureSubstFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
3096
879k
            other => Err(ReadError::InvalidFormat(other.into())),
3097
        }
3098
921k
    }
<read_fonts::tables::gsub::LigatureSubst as read_fonts::read::FontRead>::read_with_args
Line
Count
Source
3091
921k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3092
921k
        let format: u16 = data.read_at(0usize)?;
3093
921k
        match format {
3094
20.2k
            LigatureSubstFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
3095
22.0k
            LigatureSubstFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
3096
879k
            other => Err(ReadError::InvalidFormat(other.into())),
3097
        }
3098
921k
    }
Unexecuted instantiation: <read_fonts::tables::gsub::LigatureSubst as read_fonts::read::FontRead>::read_with_args
3099
}
3100
3101
impl<'a> MinByteRange<'a> for LigatureSubst<'a> {
3102
0
    fn min_byte_range(&self) -> Range<usize> {
3103
0
        match self {
3104
0
            Self::Format1(item) => item.min_byte_range(),
3105
0
            Self::Format2(item) => item.min_byte_range(),
3106
        }
3107
0
    }
3108
0
    fn min_table_bytes(&self) -> &'a [u8] {
3109
0
        match self {
3110
0
            Self::Format1(item) => item.min_table_bytes(),
3111
0
            Self::Format2(item) => item.min_table_bytes(),
3112
        }
3113
0
    }
3114
}
3115
3116
/// ReverseChainSingleSubst (ISO/IEC 14496-22:2026).
3117
#[derive(Clone)]
3118
pub enum ReverseChainSingleSubst<'a> {
3119
    Format1(ReverseChainSingleSubstFormat1<'a>),
3120
    Format2(ReverseChainSingleSubstFormat2<'a>),
3121
}
3122
3123
impl Default for ReverseChainSingleSubst<'_> {
3124
0
    fn default() -> Self {
3125
0
        Self::Format1(Default::default())
3126
0
    }
3127
}
3128
3129
impl<'a> ReverseChainSingleSubst<'a> {
3130
    ///Return the `FontData` used to resolve offsets for this table.
3131
0
    pub fn offset_data(&self) -> FontData<'a> {
3132
0
        match self {
3133
0
            Self::Format1(item) => item.offset_data(),
3134
0
            Self::Format2(item) => item.offset_data(),
3135
        }
3136
0
    }
3137
3138
    /// Format identifier: format = 1
3139
0
    pub fn subst_format(&self) -> u16 {
3140
0
        match self {
3141
0
            Self::Format1(item) => item.subst_format(),
3142
0
            Self::Format2(item) => item.subst_format(),
3143
        }
3144
0
    }
3145
3146
    /// Number of glyphs in the backtrack sequence.
3147
0
    pub fn backtrack_glyph_count(&self) -> u16 {
3148
0
        match self {
3149
0
            Self::Format1(item) => item.backtrack_glyph_count(),
3150
0
            Self::Format2(item) => item.backtrack_glyph_count(),
3151
        }
3152
0
    }
3153
3154
    /// Number of glyphs in lookahead sequence.
3155
0
    pub fn lookahead_glyph_count(&self) -> u16 {
3156
0
        match self {
3157
0
            Self::Format1(item) => item.lookahead_glyph_count(),
3158
0
            Self::Format2(item) => item.lookahead_glyph_count(),
3159
        }
3160
0
    }
3161
}
3162
3163
impl ReadArgs for ReverseChainSingleSubst<'_> {
3164
    type Args = ();
3165
}
3166
3167
impl<'a> FontRead<'a> for ReverseChainSingleSubst<'a> {
3168
    #[inline]
3169
103k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3170
103k
        let format: u16 = data.read_at(0usize)?;
3171
103k
        match format {
3172
4.75k
            ReverseChainSingleSubstFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
3173
3.98k
            ReverseChainSingleSubstFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
3174
94.6k
            other => Err(ReadError::InvalidFormat(other.into())),
3175
        }
3176
103k
    }
<read_fonts::tables::gsub::ReverseChainSingleSubst as read_fonts::read::FontRead>::read_with_args
Line
Count
Source
3169
103k
    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3170
103k
        let format: u16 = data.read_at(0usize)?;
3171
103k
        match format {
3172
4.75k
            ReverseChainSingleSubstFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
3173
3.98k
            ReverseChainSingleSubstFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
3174
94.6k
            other => Err(ReadError::InvalidFormat(other.into())),
3175
        }
3176
103k
    }
Unexecuted instantiation: <read_fonts::tables::gsub::ReverseChainSingleSubst as read_fonts::read::FontRead>::read_with_args
3177
}
3178
3179
impl<'a> MinByteRange<'a> for ReverseChainSingleSubst<'a> {
3180
0
    fn min_byte_range(&self) -> Range<usize> {
3181
0
        match self {
3182
0
            Self::Format1(item) => item.min_byte_range(),
3183
0
            Self::Format2(item) => item.min_byte_range(),
3184
        }
3185
0
    }
3186
0
    fn min_table_bytes(&self) -> &'a [u8] {
3187
0
        match self {
3188
0
            Self::Format1(item) => item.min_table_bytes(),
3189
0
            Self::Format2(item) => item.min_table_bytes(),
3190
        }
3191
0
    }
3192
}