Coverage Report

Created: 2026-05-24 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fontations/read-fonts/generated/generated_layout.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 ScriptList<'a> {
9
    fn min_byte_range(&self) -> Range<usize> {
10
        0..self.script_records_byte_range().end
11
    }
12
    fn min_table_bytes(&self) -> &'a [u8] {
13
        let range = self.min_byte_range();
14
        self.data.as_bytes().get(range).unwrap_or_default()
15
    }
16
}
17
18
impl<'a> FontRead<'a> for ScriptList<'a> {
19
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
20
        #[allow(clippy::absurd_extreme_comparisons)]
21
        if data.len() < Self::MIN_SIZE {
22
            return Err(ReadError::OutOfBounds);
23
        }
24
        Ok(Self { data })
25
    }
26
}
27
28
/// [Script List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-list-table-and-script-record)
29
#[derive(Clone)]
30
pub struct ScriptList<'a> {
31
    data: FontData<'a>,
32
}
33
34
#[allow(clippy::needless_lifetimes)]
35
impl<'a> ScriptList<'a> {
36
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
37
    basic_table_impls!(impl_the_methods);
38
39
    /// Number of ScriptRecords
40
    pub fn script_count(&self) -> u16 {
41
        let range = self.script_count_byte_range();
42
        self.data.read_at(range.start).ok().unwrap()
43
    }
44
45
    /// Array of ScriptRecords, listed alphabetically by script tag
46
    pub fn script_records(&self) -> &'a [ScriptRecord] {
47
        let range = self.script_records_byte_range();
48
        self.data.read_array(range).ok().unwrap_or_default()
49
    }
50
51
    pub fn script_count_byte_range(&self) -> Range<usize> {
52
        let start = 0;
53
        start..start + u16::RAW_BYTE_LEN
54
    }
55
56
    pub fn script_records_byte_range(&self) -> Range<usize> {
57
        let script_count = self.script_count();
58
        let start = self.script_count_byte_range().end;
59
        start..start + (script_count as usize).saturating_mul(ScriptRecord::RAW_BYTE_LEN)
60
    }
61
}
62
63
#[cfg(feature = "experimental_traverse")]
64
impl<'a> SomeTable<'a> for ScriptList<'a> {
65
    fn type_name(&self) -> &str {
66
        "ScriptList"
67
    }
68
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
69
        match idx {
70
            0usize => Some(Field::new("script_count", self.script_count())),
71
            1usize => Some(Field::new(
72
                "script_records",
73
                traversal::FieldType::array_of_records(
74
                    stringify!(ScriptRecord),
75
                    self.script_records(),
76
                    self.offset_data(),
77
                ),
78
            )),
79
            _ => None,
80
        }
81
    }
82
}
83
84
#[cfg(feature = "experimental_traverse")]
85
#[allow(clippy::needless_lifetimes)]
86
impl<'a> std::fmt::Debug for ScriptList<'a> {
87
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88
        (self as &dyn SomeTable<'a>).fmt(f)
89
    }
90
}
91
92
/// [Script Record](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-list-table-and-script-record)
93
#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
94
#[repr(C)]
95
#[repr(packed)]
96
pub struct ScriptRecord {
97
    /// 4-byte script tag identifier
98
    pub script_tag: BigEndian<Tag>,
99
    /// Offset to Script table, from beginning of ScriptList
100
    pub script_offset: BigEndian<Offset16>,
101
}
102
103
impl ScriptRecord {
104
    /// 4-byte script tag identifier
105
    pub fn script_tag(&self) -> Tag {
106
        self.script_tag.get()
107
    }
108
109
    /// Offset to Script table, from beginning of ScriptList
110
    pub fn script_offset(&self) -> Offset16 {
111
        self.script_offset.get()
112
    }
113
114
    /// Offset to Script table, from beginning of ScriptList
115
    ///
116
    /// The `data` argument should be retrieved from the parent table
117
    /// By calling its `offset_data` method.
118
    pub fn script<'a>(&self, data: FontData<'a>) -> Result<Script<'a>, ReadError> {
119
        self.script_offset().resolve(data)
120
    }
121
}
122
123
impl FixedSize for ScriptRecord {
124
    const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
125
}
126
127
#[cfg(feature = "experimental_traverse")]
128
impl<'a> SomeRecord<'a> for ScriptRecord {
129
    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
130
        RecordResolver {
131
            name: "ScriptRecord",
132
            get_field: Box::new(move |idx, _data| match idx {
133
                0usize => Some(Field::new("script_tag", self.script_tag())),
134
                1usize => Some(Field::new(
135
                    "script_offset",
136
                    FieldType::offset(self.script_offset(), self.script(_data)),
137
                )),
138
                _ => None,
139
            }),
140
            data,
141
        }
142
    }
143
}
144
145
impl<'a> MinByteRange<'a> for Script<'a> {
146
    fn min_byte_range(&self) -> Range<usize> {
147
        0..self.lang_sys_records_byte_range().end
148
    }
149
    fn min_table_bytes(&self) -> &'a [u8] {
150
        let range = self.min_byte_range();
151
        self.data.as_bytes().get(range).unwrap_or_default()
152
    }
153
}
154
155
impl<'a> FontRead<'a> for Script<'a> {
156
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
157
        #[allow(clippy::absurd_extreme_comparisons)]
158
        if data.len() < Self::MIN_SIZE {
159
            return Err(ReadError::OutOfBounds);
160
        }
161
        Ok(Self { data })
162
    }
163
}
164
165
/// [Script Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-table-and-language-system-record)
166
#[derive(Clone)]
167
pub struct Script<'a> {
168
    data: FontData<'a>,
169
}
170
171
#[allow(clippy::needless_lifetimes)]
172
impl<'a> Script<'a> {
173
    pub const MIN_SIZE: usize = (Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
174
    basic_table_impls!(impl_the_methods);
175
176
    /// Offset to default LangSys table, from beginning of Script table
177
    /// — may be NULL
178
    pub fn default_lang_sys_offset(&self) -> Nullable<Offset16> {
179
        let range = self.default_lang_sys_offset_byte_range();
180
        self.data.read_at(range.start).ok().unwrap()
181
    }
182
183
    /// Attempt to resolve [`default_lang_sys_offset`][Self::default_lang_sys_offset].
184
    pub fn default_lang_sys(&self) -> Option<Result<LangSys<'a>, ReadError>> {
185
        let data = self.data;
186
        self.default_lang_sys_offset().resolve(data)
187
    }
188
189
    /// Number of LangSysRecords for this script — excluding the
190
    /// default LangSys
191
    pub fn lang_sys_count(&self) -> u16 {
192
        let range = self.lang_sys_count_byte_range();
193
        self.data.read_at(range.start).ok().unwrap()
194
    }
195
196
    /// Array of LangSysRecords, listed alphabetically by LangSys tag
197
    pub fn lang_sys_records(&self) -> &'a [LangSysRecord] {
198
        let range = self.lang_sys_records_byte_range();
199
        self.data.read_array(range).ok().unwrap_or_default()
200
    }
201
202
    pub fn default_lang_sys_offset_byte_range(&self) -> Range<usize> {
203
        let start = 0;
204
        start..start + Offset16::RAW_BYTE_LEN
205
    }
206
207
    pub fn lang_sys_count_byte_range(&self) -> Range<usize> {
208
        let start = self.default_lang_sys_offset_byte_range().end;
209
        start..start + u16::RAW_BYTE_LEN
210
    }
211
212
    pub fn lang_sys_records_byte_range(&self) -> Range<usize> {
213
        let lang_sys_count = self.lang_sys_count();
214
        let start = self.lang_sys_count_byte_range().end;
215
        start..start + (lang_sys_count as usize).saturating_mul(LangSysRecord::RAW_BYTE_LEN)
216
    }
217
}
218
219
#[cfg(feature = "experimental_traverse")]
220
impl<'a> SomeTable<'a> for Script<'a> {
221
    fn type_name(&self) -> &str {
222
        "Script"
223
    }
224
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
225
        match idx {
226
            0usize => Some(Field::new(
227
                "default_lang_sys_offset",
228
                FieldType::offset(self.default_lang_sys_offset(), self.default_lang_sys()),
229
            )),
230
            1usize => Some(Field::new("lang_sys_count", self.lang_sys_count())),
231
            2usize => Some(Field::new(
232
                "lang_sys_records",
233
                traversal::FieldType::array_of_records(
234
                    stringify!(LangSysRecord),
235
                    self.lang_sys_records(),
236
                    self.offset_data(),
237
                ),
238
            )),
239
            _ => None,
240
        }
241
    }
242
}
243
244
#[cfg(feature = "experimental_traverse")]
245
#[allow(clippy::needless_lifetimes)]
246
impl<'a> std::fmt::Debug for Script<'a> {
247
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
248
        (self as &dyn SomeTable<'a>).fmt(f)
249
    }
250
}
251
252
#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
253
#[repr(C)]
254
#[repr(packed)]
255
pub struct LangSysRecord {
256
    /// 4-byte LangSysTag identifier
257
    pub lang_sys_tag: BigEndian<Tag>,
258
    /// Offset to LangSys table, from beginning of Script table
259
    pub lang_sys_offset: BigEndian<Offset16>,
260
}
261
262
impl LangSysRecord {
263
    /// 4-byte LangSysTag identifier
264
    pub fn lang_sys_tag(&self) -> Tag {
265
        self.lang_sys_tag.get()
266
    }
267
268
    /// Offset to LangSys table, from beginning of Script table
269
    pub fn lang_sys_offset(&self) -> Offset16 {
270
        self.lang_sys_offset.get()
271
    }
272
273
    /// Offset to LangSys table, from beginning of Script table
274
    ///
275
    /// The `data` argument should be retrieved from the parent table
276
    /// By calling its `offset_data` method.
277
    pub fn lang_sys<'a>(&self, data: FontData<'a>) -> Result<LangSys<'a>, ReadError> {
278
        self.lang_sys_offset().resolve(data)
279
    }
280
}
281
282
impl FixedSize for LangSysRecord {
283
    const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
284
}
285
286
#[cfg(feature = "experimental_traverse")]
287
impl<'a> SomeRecord<'a> for LangSysRecord {
288
    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
289
        RecordResolver {
290
            name: "LangSysRecord",
291
            get_field: Box::new(move |idx, _data| match idx {
292
                0usize => Some(Field::new("lang_sys_tag", self.lang_sys_tag())),
293
                1usize => Some(Field::new(
294
                    "lang_sys_offset",
295
                    FieldType::offset(self.lang_sys_offset(), self.lang_sys(_data)),
296
                )),
297
                _ => None,
298
            }),
299
            data,
300
        }
301
    }
302
}
303
304
impl<'a> MinByteRange<'a> for LangSys<'a> {
305
    fn min_byte_range(&self) -> Range<usize> {
306
        0..self.feature_indices_byte_range().end
307
    }
308
    fn min_table_bytes(&self) -> &'a [u8] {
309
        let range = self.min_byte_range();
310
        self.data.as_bytes().get(range).unwrap_or_default()
311
    }
312
}
313
314
impl<'a> FontRead<'a> for LangSys<'a> {
315
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
316
        #[allow(clippy::absurd_extreme_comparisons)]
317
        if data.len() < Self::MIN_SIZE {
318
            return Err(ReadError::OutOfBounds);
319
        }
320
        Ok(Self { data })
321
    }
322
}
323
324
/// [Language System Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#language-system-table)
325
#[derive(Clone)]
326
pub struct LangSys<'a> {
327
    data: FontData<'a>,
328
}
329
330
#[allow(clippy::needless_lifetimes)]
331
impl<'a> LangSys<'a> {
332
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
333
    basic_table_impls!(impl_the_methods);
334
335
    /// Index of a feature required for this language system; if no
336
    /// required features = 0xFFFF
337
    pub fn required_feature_index(&self) -> u16 {
338
        let range = self.required_feature_index_byte_range();
339
        self.data.read_at(range.start).ok().unwrap()
340
    }
341
342
    /// Number of feature index values for this language system —
343
    /// excludes the required feature
344
    pub fn feature_index_count(&self) -> u16 {
345
        let range = self.feature_index_count_byte_range();
346
        self.data.read_at(range.start).ok().unwrap()
347
    }
348
349
    /// Array of indices into the FeatureList, in arbitrary order
350
    pub fn feature_indices(&self) -> &'a [BigEndian<u16>] {
351
        let range = self.feature_indices_byte_range();
352
        self.data.read_array(range).ok().unwrap_or_default()
353
    }
354
355
    pub fn lookup_order_offset_byte_range(&self) -> Range<usize> {
356
        let start = 0;
357
        start..start + u16::RAW_BYTE_LEN
358
    }
359
360
    pub fn required_feature_index_byte_range(&self) -> Range<usize> {
361
        let start = self.lookup_order_offset_byte_range().end;
362
        start..start + u16::RAW_BYTE_LEN
363
    }
364
365
    pub fn feature_index_count_byte_range(&self) -> Range<usize> {
366
        let start = self.required_feature_index_byte_range().end;
367
        start..start + u16::RAW_BYTE_LEN
368
    }
369
370
    pub fn feature_indices_byte_range(&self) -> Range<usize> {
371
        let feature_index_count = self.feature_index_count();
372
        let start = self.feature_index_count_byte_range().end;
373
        start..start + (feature_index_count as usize).saturating_mul(u16::RAW_BYTE_LEN)
374
    }
375
}
376
377
#[cfg(feature = "experimental_traverse")]
378
impl<'a> SomeTable<'a> for LangSys<'a> {
379
    fn type_name(&self) -> &str {
380
        "LangSys"
381
    }
382
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
383
        match idx {
384
            0usize => Some(Field::new(
385
                "required_feature_index",
386
                self.required_feature_index(),
387
            )),
388
            1usize => Some(Field::new(
389
                "feature_index_count",
390
                self.feature_index_count(),
391
            )),
392
            2usize => Some(Field::new("feature_indices", self.feature_indices())),
393
            _ => None,
394
        }
395
    }
396
}
397
398
#[cfg(feature = "experimental_traverse")]
399
#[allow(clippy::needless_lifetimes)]
400
impl<'a> std::fmt::Debug for LangSys<'a> {
401
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
402
        (self as &dyn SomeTable<'a>).fmt(f)
403
    }
404
}
405
406
impl<'a> MinByteRange<'a> for FeatureList<'a> {
407
    fn min_byte_range(&self) -> Range<usize> {
408
        0..self.feature_records_byte_range().end
409
    }
410
    fn min_table_bytes(&self) -> &'a [u8] {
411
        let range = self.min_byte_range();
412
        self.data.as_bytes().get(range).unwrap_or_default()
413
    }
414
}
415
416
impl<'a> FontRead<'a> for FeatureList<'a> {
417
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
418
        #[allow(clippy::absurd_extreme_comparisons)]
419
        if data.len() < Self::MIN_SIZE {
420
            return Err(ReadError::OutOfBounds);
421
        }
422
        Ok(Self { data })
423
    }
424
}
425
426
/// [Feature List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-list-table)
427
#[derive(Clone)]
428
pub struct FeatureList<'a> {
429
    data: FontData<'a>,
430
}
431
432
#[allow(clippy::needless_lifetimes)]
433
impl<'a> FeatureList<'a> {
434
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
435
    basic_table_impls!(impl_the_methods);
436
437
    /// Number of FeatureRecords in this table
438
    pub fn feature_count(&self) -> u16 {
439
        let range = self.feature_count_byte_range();
440
        self.data.read_at(range.start).ok().unwrap()
441
    }
442
443
    /// Array of FeatureRecords — zero-based (first feature has
444
    /// FeatureIndex = 0), listed alphabetically by feature tag
445
    pub fn feature_records(&self) -> &'a [FeatureRecord] {
446
        let range = self.feature_records_byte_range();
447
        self.data.read_array(range).ok().unwrap_or_default()
448
    }
449
450
    pub fn feature_count_byte_range(&self) -> Range<usize> {
451
        let start = 0;
452
        start..start + u16::RAW_BYTE_LEN
453
    }
454
455
    pub fn feature_records_byte_range(&self) -> Range<usize> {
456
        let feature_count = self.feature_count();
457
        let start = self.feature_count_byte_range().end;
458
        start..start + (feature_count as usize).saturating_mul(FeatureRecord::RAW_BYTE_LEN)
459
    }
460
}
461
462
#[cfg(feature = "experimental_traverse")]
463
impl<'a> SomeTable<'a> for FeatureList<'a> {
464
    fn type_name(&self) -> &str {
465
        "FeatureList"
466
    }
467
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
468
        match idx {
469
            0usize => Some(Field::new("feature_count", self.feature_count())),
470
            1usize => Some(Field::new(
471
                "feature_records",
472
                traversal::FieldType::array_of_records(
473
                    stringify!(FeatureRecord),
474
                    self.feature_records(),
475
                    self.offset_data(),
476
                ),
477
            )),
478
            _ => None,
479
        }
480
    }
481
}
482
483
#[cfg(feature = "experimental_traverse")]
484
#[allow(clippy::needless_lifetimes)]
485
impl<'a> std::fmt::Debug for FeatureList<'a> {
486
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
487
        (self as &dyn SomeTable<'a>).fmt(f)
488
    }
489
}
490
491
/// Part of [FeatureList]
492
#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
493
#[repr(C)]
494
#[repr(packed)]
495
pub struct FeatureRecord {
496
    /// 4-byte feature identification tag
497
    pub feature_tag: BigEndian<Tag>,
498
    /// Offset to Feature table, from beginning of FeatureList
499
    pub feature_offset: BigEndian<Offset16>,
500
}
501
502
impl FeatureRecord {
503
    /// 4-byte feature identification tag
504
    pub fn feature_tag(&self) -> Tag {
505
        self.feature_tag.get()
506
    }
507
508
    /// Offset to Feature table, from beginning of FeatureList
509
    pub fn feature_offset(&self) -> Offset16 {
510
        self.feature_offset.get()
511
    }
512
513
    /// Offset to Feature table, from beginning of FeatureList
514
    ///
515
    /// The `data` argument should be retrieved from the parent table
516
    /// By calling its `offset_data` method.
517
    pub fn feature<'a>(&self, data: FontData<'a>) -> Result<Feature<'a>, ReadError> {
518
        let args = self.feature_tag();
519
        self.feature_offset().resolve_with_args(data, &args)
520
    }
521
}
522
523
impl FixedSize for FeatureRecord {
524
    const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
525
}
526
527
#[cfg(feature = "experimental_traverse")]
528
impl<'a> SomeRecord<'a> for FeatureRecord {
529
    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
530
        RecordResolver {
531
            name: "FeatureRecord",
532
            get_field: Box::new(move |idx, _data| match idx {
533
                0usize => Some(Field::new("feature_tag", self.feature_tag())),
534
                1usize => Some(Field::new(
535
                    "feature_offset",
536
                    FieldType::offset(self.feature_offset(), self.feature(_data)),
537
                )),
538
                _ => None,
539
            }),
540
            data,
541
        }
542
    }
543
}
544
545
impl<'a> MinByteRange<'a> for Feature<'a> {
546
    fn min_byte_range(&self) -> Range<usize> {
547
        0..self.lookup_list_indices_byte_range().end
548
    }
549
    fn min_table_bytes(&self) -> &'a [u8] {
550
        let range = self.min_byte_range();
551
        self.data.as_bytes().get(range).unwrap_or_default()
552
    }
553
}
554
555
impl ReadArgs for Feature<'_> {
556
    type Args = Tag;
557
}
558
559
impl<'a> FontReadWithArgs<'a> for Feature<'a> {
560
    fn read_with_args(data: FontData<'a>, args: &Tag) -> Result<Self, ReadError> {
561
        let feature_tag = *args;
562
563
        #[allow(clippy::absurd_extreme_comparisons)]
564
        if data.len() < Self::MIN_SIZE {
565
            return Err(ReadError::OutOfBounds);
566
        }
567
        Ok(Self { data, feature_tag })
568
    }
569
}
570
571
impl<'a> Feature<'a> {
572
    /// A constructor that requires additional arguments.
573
    ///
574
    /// This type requires some external state in order to be
575
    /// parsed.
576
    pub fn read(data: FontData<'a>, feature_tag: Tag) -> Result<Self, ReadError> {
577
        let args = feature_tag;
578
        Self::read_with_args(data, &args)
579
    }
580
}
581
582
/// [Feature Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-table)
583
#[derive(Clone)]
584
pub struct Feature<'a> {
585
    data: FontData<'a>,
586
    feature_tag: Tag,
587
}
588
589
#[allow(clippy::needless_lifetimes)]
590
impl<'a> Feature<'a> {
591
    pub const MIN_SIZE: usize = (Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
592
    basic_table_impls!(impl_the_methods);
593
594
    /// Offset from start of Feature table to FeatureParams table, if defined for the feature and present, else NULL
595
    pub fn feature_params_offset(&self) -> Nullable<Offset16> {
596
        let range = self.feature_params_offset_byte_range();
597
        self.data.read_at(range.start).ok().unwrap()
598
    }
599
600
    /// Attempt to resolve [`feature_params_offset`][Self::feature_params_offset].
601
    pub fn feature_params(&self) -> Option<Result<FeatureParams<'a>, ReadError>> {
602
        let data = self.data;
603
        let args = self.feature_tag();
604
        self.feature_params_offset().resolve_with_args(data, &args)
605
    }
606
607
    /// Number of LookupList indices for this feature
608
    pub fn lookup_index_count(&self) -> u16 {
609
        let range = self.lookup_index_count_byte_range();
610
        self.data.read_at(range.start).ok().unwrap()
611
    }
612
613
    /// Array of indices into the LookupList — zero-based (first
614
    /// lookup is LookupListIndex = 0)
615
    pub fn lookup_list_indices(&self) -> &'a [BigEndian<u16>] {
616
        let range = self.lookup_list_indices_byte_range();
617
        self.data.read_array(range).ok().unwrap_or_default()
618
    }
619
620
    pub(crate) fn feature_tag(&self) -> Tag {
621
        self.feature_tag
622
    }
623
624
    pub fn feature_params_offset_byte_range(&self) -> Range<usize> {
625
        let start = 0;
626
        start..start + Offset16::RAW_BYTE_LEN
627
    }
628
629
    pub fn lookup_index_count_byte_range(&self) -> Range<usize> {
630
        let start = self.feature_params_offset_byte_range().end;
631
        start..start + u16::RAW_BYTE_LEN
632
    }
633
634
    pub fn lookup_list_indices_byte_range(&self) -> Range<usize> {
635
        let lookup_index_count = self.lookup_index_count();
636
        let start = self.lookup_index_count_byte_range().end;
637
        start..start + (lookup_index_count as usize).saturating_mul(u16::RAW_BYTE_LEN)
638
    }
639
}
640
641
#[cfg(feature = "experimental_traverse")]
642
impl<'a> SomeTable<'a> for Feature<'a> {
643
    fn type_name(&self) -> &str {
644
        "Feature"
645
    }
646
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
647
        match idx {
648
            0usize => Some(Field::new(
649
                "feature_params_offset",
650
                FieldType::offset(self.feature_params_offset(), self.feature_params()),
651
            )),
652
            1usize => Some(Field::new("lookup_index_count", self.lookup_index_count())),
653
            2usize => Some(Field::new(
654
                "lookup_list_indices",
655
                self.lookup_list_indices(),
656
            )),
657
            _ => None,
658
        }
659
    }
660
}
661
662
#[cfg(feature = "experimental_traverse")]
663
#[allow(clippy::needless_lifetimes)]
664
impl<'a> std::fmt::Debug for Feature<'a> {
665
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
666
        (self as &dyn SomeTable<'a>).fmt(f)
667
    }
668
}
669
670
impl<'a, T> MinByteRange<'a> for LookupList<'a, T> {
671
    fn min_byte_range(&self) -> Range<usize> {
672
        0..self.lookup_offsets_byte_range().end
673
    }
674
    fn min_table_bytes(&self) -> &'a [u8] {
675
        let range = self.min_byte_range();
676
        self.data.as_bytes().get(range).unwrap_or_default()
677
    }
678
}
679
680
impl<'a, T> FontRead<'a> for LookupList<'a, T> {
681
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
682
        #[allow(clippy::absurd_extreme_comparisons)]
683
        if data.len() < Self::MIN_SIZE {
684
            return Err(ReadError::OutOfBounds);
685
        }
686
        Ok(Self {
687
            data,
688
            offset_type: std::marker::PhantomData,
689
        })
690
    }
691
}
692
693
impl<'a> LookupList<'a, ()> {
694
    #[allow(dead_code)]
695
    pub(crate) fn into_concrete<T>(self) -> LookupList<'a, T> {
696
        LookupList {
697
            data: self.data,
698
            offset_type: std::marker::PhantomData,
699
        }
700
    }
701
}
702
703
impl<'a, T> LookupList<'a, T> {
704
    #[allow(dead_code)]
705
    /// Replace the specific generic type on this implementation with `()`
706
    pub(crate) fn of_unit_type(&self) -> LookupList<'a, ()> {
707
        LookupList {
708
            data: self.data,
709
            offset_type: std::marker::PhantomData,
710
        }
711
    }
712
}
713
714
/// [Lookup List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#lookup-list-table)
715
#[derive(Clone)]
716
pub struct LookupList<'a, T = ()> {
717
    data: FontData<'a>,
718
    offset_type: std::marker::PhantomData<*const T>,
719
}
720
721
#[allow(clippy::needless_lifetimes)]
722
impl<'a, T> LookupList<'a, T> {
723
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
724
    basic_table_impls!(impl_the_methods);
725
726
    /// Number of lookups in this table
727
    pub fn lookup_count(&self) -> u16 {
728
        let range = self.lookup_count_byte_range();
729
        self.data.read_at(range.start).ok().unwrap()
730
    }
731
732
    /// Array of offsets to Lookup tables, from beginning of LookupList
733
    /// — zero based (first lookup is Lookup index = 0)
734
    pub fn lookup_offsets(&self) -> &'a [BigEndian<Offset16>] {
735
        let range = self.lookup_offsets_byte_range();
736
        self.data.read_array(range).ok().unwrap_or_default()
737
    }
738
739
    /// A dynamically resolving wrapper for [`lookup_offsets`][Self::lookup_offsets].
740
    pub fn lookups(&self) -> ArrayOfOffsets<'a, T, Offset16>
741
    where
742
        T: FontRead<'a>,
743
    {
744
        let data = self.data;
745
        let offsets = self.lookup_offsets();
746
        ArrayOfOffsets::new(offsets, data, ())
747
    }
748
749
    pub fn lookup_count_byte_range(&self) -> Range<usize> {
750
        let start = 0;
751
        start..start + u16::RAW_BYTE_LEN
752
    }
753
754
    pub fn lookup_offsets_byte_range(&self) -> Range<usize> {
755
        let lookup_count = self.lookup_count();
756
        let start = self.lookup_count_byte_range().end;
757
        start..start + (lookup_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
758
    }
759
}
760
761
#[cfg(feature = "experimental_traverse")]
762
impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> SomeTable<'a> for LookupList<'a, T> {
763
    fn type_name(&self) -> &str {
764
        "LookupList"
765
    }
766
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
767
        match idx {
768
            0usize => Some(Field::new("lookup_count", self.lookup_count())),
769
            1usize => Some({
770
                let data = self.data;
771
                Field::new(
772
                    "lookup_offsets",
773
                    FieldType::array_of_offsets(
774
                        better_type_name::<T>(),
775
                        self.lookup_offsets(),
776
                        move |off| {
777
                            let target = off.get().resolve::<T>(data);
778
                            FieldType::offset(off.get(), target)
779
                        },
780
                    ),
781
                )
782
            }),
783
            _ => None,
784
        }
785
    }
786
}
787
788
#[cfg(feature = "experimental_traverse")]
789
#[allow(clippy::needless_lifetimes)]
790
impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> std::fmt::Debug for LookupList<'a, T> {
791
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
792
        (self as &dyn SomeTable<'a>).fmt(f)
793
    }
794
}
795
796
impl<'a, T> MinByteRange<'a> for Lookup<'a, T> {
797
    fn min_byte_range(&self) -> Range<usize> {
798
        0..self.subtable_offsets_byte_range().end
799
    }
800
    fn min_table_bytes(&self) -> &'a [u8] {
801
        let range = self.min_byte_range();
802
        self.data.as_bytes().get(range).unwrap_or_default()
803
    }
804
}
805
806
impl<'a, T> FontRead<'a> for Lookup<'a, T> {
807
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
808
        #[allow(clippy::absurd_extreme_comparisons)]
809
        if data.len() < Self::MIN_SIZE {
810
            return Err(ReadError::OutOfBounds);
811
        }
812
        Ok(Self {
813
            data,
814
            offset_type: std::marker::PhantomData,
815
        })
816
    }
817
}
818
819
impl<'a> Lookup<'a, ()> {
820
    #[allow(dead_code)]
821
    pub(crate) fn into_concrete<T>(self) -> Lookup<'a, T> {
822
        Lookup {
823
            data: self.data,
824
            offset_type: std::marker::PhantomData,
825
        }
826
    }
827
}
828
829
impl<'a, T> Lookup<'a, T> {
830
    #[allow(dead_code)]
831
    /// Replace the specific generic type on this implementation with `()`
832
    pub(crate) fn of_unit_type(&self) -> Lookup<'a, ()> {
833
        Lookup {
834
            data: self.data,
835
            offset_type: std::marker::PhantomData,
836
        }
837
    }
838
}
839
840
/// [Lookup Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#lookup-table)
841
#[derive(Clone)]
842
pub struct Lookup<'a, T = ()> {
843
    data: FontData<'a>,
844
    offset_type: std::marker::PhantomData<*const T>,
845
}
846
847
#[allow(clippy::needless_lifetimes)]
848
impl<'a, T> Lookup<'a, T> {
849
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + LookupFlag::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
850
    basic_table_impls!(impl_the_methods);
851
852
    /// Different enumerations for GSUB and GPOS
853
    pub fn lookup_type(&self) -> u16 {
854
        let range = self.lookup_type_byte_range();
855
        self.data.read_at(range.start).ok().unwrap()
856
    }
857
858
    /// Lookup qualifiers
859
0
    pub fn lookup_flag(&self) -> LookupFlag {
860
0
        let range = self.lookup_flag_byte_range();
861
0
        self.data.read_at(range.start).ok().unwrap()
862
0
    }
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::SequenceContext>>::lookup_flag
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::ChainedSequenceContext>>::lookup_flag
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::CursivePosFormat1>>::lookup_flag
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::ExtensionSubtable>>::lookup_flag
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkLigPosFormat1>>::lookup_flag
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkBasePosFormat1>>::lookup_flag
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkMarkPosFormat1>>::lookup_flag
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::PairPos>>::lookup_flag
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::SinglePos>>::lookup_flag
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::SingleSubst>>::lookup_flag
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ExtensionSubtable>>::lookup_flag
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::LigatureSubstFormat1>>::lookup_flag
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::MultipleSubstFormat1>>::lookup_flag
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::AlternateSubstFormat1>>::lookup_flag
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1>>::lookup_flag
863
864
    /// Number of subtables for this lookup
865
0
    pub fn sub_table_count(&self) -> u16 {
866
0
        let range = self.sub_table_count_byte_range();
867
0
        self.data.read_at(range.start).ok().unwrap()
868
0
    }
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::SequenceContext>>::sub_table_count
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::ChainedSequenceContext>>::sub_table_count
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::CursivePosFormat1>>::sub_table_count
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::ExtensionSubtable>>::sub_table_count
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkLigPosFormat1>>::sub_table_count
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkBasePosFormat1>>::sub_table_count
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkMarkPosFormat1>>::sub_table_count
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::PairPos>>::sub_table_count
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::SinglePos>>::sub_table_count
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::SingleSubst>>::sub_table_count
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ExtensionSubtable>>::sub_table_count
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::LigatureSubstFormat1>>::sub_table_count
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::MultipleSubstFormat1>>::sub_table_count
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::AlternateSubstFormat1>>::sub_table_count
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1>>::sub_table_count
869
870
    /// Array of offsets to lookup subtables, from beginning of Lookup
871
    /// table
872
0
    pub fn subtable_offsets(&self) -> &'a [BigEndian<Offset16>] {
873
0
        let range = self.subtable_offsets_byte_range();
874
0
        self.data.read_array(range).ok().unwrap_or_default()
875
0
    }
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::SequenceContext>>::subtable_offsets
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::ChainedSequenceContext>>::subtable_offsets
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::CursivePosFormat1>>::subtable_offsets
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::ExtensionSubtable>>::subtable_offsets
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkLigPosFormat1>>::subtable_offsets
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkBasePosFormat1>>::subtable_offsets
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkMarkPosFormat1>>::subtable_offsets
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::PairPos>>::subtable_offsets
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::SinglePos>>::subtable_offsets
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::SingleSubst>>::subtable_offsets
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ExtensionSubtable>>::subtable_offsets
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::LigatureSubstFormat1>>::subtable_offsets
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::MultipleSubstFormat1>>::subtable_offsets
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::AlternateSubstFormat1>>::subtable_offsets
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1>>::subtable_offsets
876
877
    /// A dynamically resolving wrapper for [`subtable_offsets`][Self::subtable_offsets].
878
0
    pub fn subtables(&self) -> ArrayOfOffsets<'a, T, Offset16>
879
0
    where
880
0
        T: FontRead<'a>,
881
    {
882
0
        let data = self.data;
883
0
        let offsets = self.subtable_offsets();
884
0
        ArrayOfOffsets::new(offsets, data, ())
885
0
    }
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::SequenceContext>>::subtables
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::ChainedSequenceContext>>::subtables
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::CursivePosFormat1>>::subtables
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::ExtensionSubtable>>::subtables
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkLigPosFormat1>>::subtables
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkBasePosFormat1>>::subtables
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkMarkPosFormat1>>::subtables
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::PairPos>>::subtables
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::SinglePos>>::subtables
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::SingleSubst>>::subtables
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ExtensionSubtable>>::subtables
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::LigatureSubstFormat1>>::subtables
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::MultipleSubstFormat1>>::subtables
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::AlternateSubstFormat1>>::subtables
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1>>::subtables
886
887
    /// Index (base 0) into GDEF mark glyph sets structure. This field
888
    /// is only present if the USE_MARK_FILTERING_SET lookup flag is
889
    /// set.
890
0
    pub fn mark_filtering_set(&self) -> Option<u16> {
891
0
        let range = self.mark_filtering_set_byte_range();
892
0
        (!range.is_empty())
893
0
            .then(|| self.data.read_at(range.start).ok())
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::SequenceContext>>::mark_filtering_set::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::ChainedSequenceContext>>::mark_filtering_set::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::CursivePosFormat1>>::mark_filtering_set::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::ExtensionSubtable>>::mark_filtering_set::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkLigPosFormat1>>::mark_filtering_set::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkBasePosFormat1>>::mark_filtering_set::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkMarkPosFormat1>>::mark_filtering_set::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::PairPos>>::mark_filtering_set::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::SinglePos>>::mark_filtering_set::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::SingleSubst>>::mark_filtering_set::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ExtensionSubtable>>::mark_filtering_set::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::LigatureSubstFormat1>>::mark_filtering_set::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::MultipleSubstFormat1>>::mark_filtering_set::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::AlternateSubstFormat1>>::mark_filtering_set::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1>>::mark_filtering_set::{closure#0}
894
0
            .flatten()
895
0
    }
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::SequenceContext>>::mark_filtering_set
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::ChainedSequenceContext>>::mark_filtering_set
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::CursivePosFormat1>>::mark_filtering_set
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::ExtensionSubtable>>::mark_filtering_set
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkLigPosFormat1>>::mark_filtering_set
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkBasePosFormat1>>::mark_filtering_set
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkMarkPosFormat1>>::mark_filtering_set
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::PairPos>>::mark_filtering_set
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::SinglePos>>::mark_filtering_set
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::SingleSubst>>::mark_filtering_set
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ExtensionSubtable>>::mark_filtering_set
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::LigatureSubstFormat1>>::mark_filtering_set
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::MultipleSubstFormat1>>::mark_filtering_set
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::AlternateSubstFormat1>>::mark_filtering_set
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1>>::mark_filtering_set
896
897
0
    pub fn lookup_type_byte_range(&self) -> Range<usize> {
898
0
        let start = 0;
899
0
        start..start + u16::RAW_BYTE_LEN
900
0
    }
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::SequenceContext>>::lookup_type_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::ChainedSequenceContext>>::lookup_type_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::CursivePosFormat1>>::lookup_type_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::ExtensionSubtable>>::lookup_type_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkLigPosFormat1>>::lookup_type_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkBasePosFormat1>>::lookup_type_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkMarkPosFormat1>>::lookup_type_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::PairPos>>::lookup_type_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::SinglePos>>::lookup_type_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::SingleSubst>>::lookup_type_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ExtensionSubtable>>::lookup_type_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::LigatureSubstFormat1>>::lookup_type_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::MultipleSubstFormat1>>::lookup_type_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::AlternateSubstFormat1>>::lookup_type_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1>>::lookup_type_byte_range
901
902
0
    pub fn lookup_flag_byte_range(&self) -> Range<usize> {
903
0
        let start = self.lookup_type_byte_range().end;
904
0
        start..start + LookupFlag::RAW_BYTE_LEN
905
0
    }
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::SequenceContext>>::lookup_flag_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::ChainedSequenceContext>>::lookup_flag_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::CursivePosFormat1>>::lookup_flag_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::ExtensionSubtable>>::lookup_flag_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkLigPosFormat1>>::lookup_flag_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkBasePosFormat1>>::lookup_flag_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkMarkPosFormat1>>::lookup_flag_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::PairPos>>::lookup_flag_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::SinglePos>>::lookup_flag_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::SingleSubst>>::lookup_flag_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ExtensionSubtable>>::lookup_flag_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::LigatureSubstFormat1>>::lookup_flag_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::MultipleSubstFormat1>>::lookup_flag_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::AlternateSubstFormat1>>::lookup_flag_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1>>::lookup_flag_byte_range
906
907
0
    pub fn sub_table_count_byte_range(&self) -> Range<usize> {
908
0
        let start = self.lookup_flag_byte_range().end;
909
0
        start..start + u16::RAW_BYTE_LEN
910
0
    }
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::SequenceContext>>::sub_table_count_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::ChainedSequenceContext>>::sub_table_count_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::CursivePosFormat1>>::sub_table_count_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::ExtensionSubtable>>::sub_table_count_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkLigPosFormat1>>::sub_table_count_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkBasePosFormat1>>::sub_table_count_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkMarkPosFormat1>>::sub_table_count_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::PairPos>>::sub_table_count_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::SinglePos>>::sub_table_count_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::SingleSubst>>::sub_table_count_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ExtensionSubtable>>::sub_table_count_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::LigatureSubstFormat1>>::sub_table_count_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::MultipleSubstFormat1>>::sub_table_count_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::AlternateSubstFormat1>>::sub_table_count_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1>>::sub_table_count_byte_range
911
912
0
    pub fn subtable_offsets_byte_range(&self) -> Range<usize> {
913
0
        let sub_table_count = self.sub_table_count();
914
0
        let start = self.sub_table_count_byte_range().end;
915
0
        start..start + (sub_table_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
916
0
    }
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::SequenceContext>>::subtable_offsets_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::ChainedSequenceContext>>::subtable_offsets_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::CursivePosFormat1>>::subtable_offsets_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::ExtensionSubtable>>::subtable_offsets_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkLigPosFormat1>>::subtable_offsets_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkBasePosFormat1>>::subtable_offsets_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkMarkPosFormat1>>::subtable_offsets_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::PairPos>>::subtable_offsets_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::SinglePos>>::subtable_offsets_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::SingleSubst>>::subtable_offsets_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ExtensionSubtable>>::subtable_offsets_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::LigatureSubstFormat1>>::subtable_offsets_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::MultipleSubstFormat1>>::subtable_offsets_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::AlternateSubstFormat1>>::subtable_offsets_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1>>::subtable_offsets_byte_range
917
918
0
    pub fn mark_filtering_set_byte_range(&self) -> Range<usize> {
919
0
        let start = self.subtable_offsets_byte_range().end;
920
0
        start
921
0
            ..(self
922
0
                .lookup_flag()
923
0
                .contains(LookupFlag::USE_MARK_FILTERING_SET))
924
0
            .then(|| start + u16::RAW_BYTE_LEN)
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::SequenceContext>>::mark_filtering_set_byte_range::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::ChainedSequenceContext>>::mark_filtering_set_byte_range::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::CursivePosFormat1>>::mark_filtering_set_byte_range::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::ExtensionSubtable>>::mark_filtering_set_byte_range::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkLigPosFormat1>>::mark_filtering_set_byte_range::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkBasePosFormat1>>::mark_filtering_set_byte_range::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkMarkPosFormat1>>::mark_filtering_set_byte_range::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::PairPos>>::mark_filtering_set_byte_range::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::SinglePos>>::mark_filtering_set_byte_range::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::SingleSubst>>::mark_filtering_set_byte_range::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ExtensionSubtable>>::mark_filtering_set_byte_range::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::LigatureSubstFormat1>>::mark_filtering_set_byte_range::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::MultipleSubstFormat1>>::mark_filtering_set_byte_range::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::AlternateSubstFormat1>>::mark_filtering_set_byte_range::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1>>::mark_filtering_set_byte_range::{closure#0}
925
0
            .unwrap_or(start)
926
0
    }
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::SequenceContext>>::mark_filtering_set_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::layout::ChainedSequenceContext>>::mark_filtering_set_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::CursivePosFormat1>>::mark_filtering_set_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::ExtensionSubtable>>::mark_filtering_set_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkLigPosFormat1>>::mark_filtering_set_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkBasePosFormat1>>::mark_filtering_set_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::MarkMarkPosFormat1>>::mark_filtering_set_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::PairPos>>::mark_filtering_set_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gpos::SinglePos>>::mark_filtering_set_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::SingleSubst>>::mark_filtering_set_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ExtensionSubtable>>::mark_filtering_set_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::LigatureSubstFormat1>>::mark_filtering_set_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::MultipleSubstFormat1>>::mark_filtering_set_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::AlternateSubstFormat1>>::mark_filtering_set_byte_range
Unexecuted instantiation: <read_fonts::tables::layout::Lookup<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1>>::mark_filtering_set_byte_range
927
}
928
929
#[cfg(feature = "experimental_traverse")]
930
impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> SomeTable<'a> for Lookup<'a, T> {
931
    fn type_name(&self) -> &str {
932
        "Lookup"
933
    }
934
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
935
        match idx {
936
            0usize => Some(Field::new("lookup_type", self.lookup_type())),
937
            1usize => Some(Field::new("lookup_flag", self.traverse_lookup_flag())),
938
            2usize => Some(Field::new("sub_table_count", self.sub_table_count())),
939
            3usize => Some({
940
                let data = self.data;
941
                Field::new(
942
                    "subtable_offsets",
943
                    FieldType::array_of_offsets(
944
                        better_type_name::<T>(),
945
                        self.subtable_offsets(),
946
                        move |off| {
947
                            let target = off.get().resolve::<T>(data);
948
                            FieldType::offset(off.get(), target)
949
                        },
950
                    ),
951
                )
952
            }),
953
            4usize
954
                if self
955
                    .lookup_flag()
956
                    .contains(LookupFlag::USE_MARK_FILTERING_SET) =>
957
            {
958
                Some(Field::new(
959
                    "mark_filtering_set",
960
                    self.mark_filtering_set().unwrap(),
961
                ))
962
            }
963
            _ => None,
964
        }
965
    }
966
}
967
968
#[cfg(feature = "experimental_traverse")]
969
#[allow(clippy::needless_lifetimes)]
970
impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> std::fmt::Debug for Lookup<'a, T> {
971
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
972
        (self as &dyn SomeTable<'a>).fmt(f)
973
    }
974
}
975
976
impl Format<u16> for CoverageFormat1<'_> {
977
    const FORMAT: u16 = 1;
978
}
979
980
impl<'a> MinByteRange<'a> for CoverageFormat1<'a> {
981
    fn min_byte_range(&self) -> Range<usize> {
982
        0..self.glyph_array_byte_range().end
983
    }
984
    fn min_table_bytes(&self) -> &'a [u8] {
985
        let range = self.min_byte_range();
986
        self.data.as_bytes().get(range).unwrap_or_default()
987
    }
988
}
989
990
impl<'a> FontRead<'a> for CoverageFormat1<'a> {
991
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
992
        #[allow(clippy::absurd_extreme_comparisons)]
993
        if data.len() < Self::MIN_SIZE {
994
            return Err(ReadError::OutOfBounds);
995
        }
996
        Ok(Self { data })
997
    }
998
}
999
1000
/// [Coverage Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-1)
1001
#[derive(Clone)]
1002
pub struct CoverageFormat1<'a> {
1003
    data: FontData<'a>,
1004
}
1005
1006
#[allow(clippy::needless_lifetimes)]
1007
impl<'a> CoverageFormat1<'a> {
1008
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1009
    basic_table_impls!(impl_the_methods);
1010
1011
    /// Format identifier — format = 1
1012
    pub fn coverage_format(&self) -> u16 {
1013
        let range = self.coverage_format_byte_range();
1014
        self.data.read_at(range.start).ok().unwrap()
1015
    }
1016
1017
    /// Number of glyphs in the glyph array
1018
    pub fn glyph_count(&self) -> u16 {
1019
        let range = self.glyph_count_byte_range();
1020
        self.data.read_at(range.start).ok().unwrap()
1021
    }
1022
1023
    /// Array of glyph IDs — in numerical order
1024
    pub fn glyph_array(&self) -> &'a [BigEndian<GlyphId16>] {
1025
        let range = self.glyph_array_byte_range();
1026
        self.data.read_array(range).ok().unwrap_or_default()
1027
    }
1028
1029
    pub fn coverage_format_byte_range(&self) -> Range<usize> {
1030
        let start = 0;
1031
        start..start + u16::RAW_BYTE_LEN
1032
    }
1033
1034
    pub fn glyph_count_byte_range(&self) -> Range<usize> {
1035
        let start = self.coverage_format_byte_range().end;
1036
        start..start + u16::RAW_BYTE_LEN
1037
    }
1038
1039
    pub fn glyph_array_byte_range(&self) -> Range<usize> {
1040
        let glyph_count = self.glyph_count();
1041
        let start = self.glyph_count_byte_range().end;
1042
        start..start + (glyph_count as usize).saturating_mul(GlyphId16::RAW_BYTE_LEN)
1043
    }
1044
}
1045
1046
#[cfg(feature = "experimental_traverse")]
1047
impl<'a> SomeTable<'a> for CoverageFormat1<'a> {
1048
    fn type_name(&self) -> &str {
1049
        "CoverageFormat1"
1050
    }
1051
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1052
        match idx {
1053
            0usize => Some(Field::new("coverage_format", self.coverage_format())),
1054
            1usize => Some(Field::new("glyph_count", self.glyph_count())),
1055
            2usize => Some(Field::new("glyph_array", self.glyph_array())),
1056
            _ => None,
1057
        }
1058
    }
1059
}
1060
1061
#[cfg(feature = "experimental_traverse")]
1062
#[allow(clippy::needless_lifetimes)]
1063
impl<'a> std::fmt::Debug for CoverageFormat1<'a> {
1064
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1065
        (self as &dyn SomeTable<'a>).fmt(f)
1066
    }
1067
}
1068
1069
impl Format<u16> for CoverageFormat2<'_> {
1070
    const FORMAT: u16 = 2;
1071
}
1072
1073
impl<'a> MinByteRange<'a> for CoverageFormat2<'a> {
1074
    fn min_byte_range(&self) -> Range<usize> {
1075
        0..self.range_records_byte_range().end
1076
    }
1077
    fn min_table_bytes(&self) -> &'a [u8] {
1078
        let range = self.min_byte_range();
1079
        self.data.as_bytes().get(range).unwrap_or_default()
1080
    }
1081
}
1082
1083
impl<'a> FontRead<'a> for CoverageFormat2<'a> {
1084
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1085
        #[allow(clippy::absurd_extreme_comparisons)]
1086
        if data.len() < Self::MIN_SIZE {
1087
            return Err(ReadError::OutOfBounds);
1088
        }
1089
        Ok(Self { data })
1090
    }
1091
}
1092
1093
/// [Coverage Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-2)
1094
#[derive(Clone)]
1095
pub struct CoverageFormat2<'a> {
1096
    data: FontData<'a>,
1097
}
1098
1099
#[allow(clippy::needless_lifetimes)]
1100
impl<'a> CoverageFormat2<'a> {
1101
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1102
    basic_table_impls!(impl_the_methods);
1103
1104
    /// Format identifier — format = 2
1105
    pub fn coverage_format(&self) -> u16 {
1106
        let range = self.coverage_format_byte_range();
1107
        self.data.read_at(range.start).ok().unwrap()
1108
    }
1109
1110
    /// Number of RangeRecords
1111
    pub fn range_count(&self) -> u16 {
1112
        let range = self.range_count_byte_range();
1113
        self.data.read_at(range.start).ok().unwrap()
1114
    }
1115
1116
    /// Array of glyph ranges — ordered by startGlyphID.
1117
    pub fn range_records(&self) -> &'a [RangeRecord] {
1118
        let range = self.range_records_byte_range();
1119
        self.data.read_array(range).ok().unwrap_or_default()
1120
    }
1121
1122
    pub fn coverage_format_byte_range(&self) -> Range<usize> {
1123
        let start = 0;
1124
        start..start + u16::RAW_BYTE_LEN
1125
    }
1126
1127
    pub fn range_count_byte_range(&self) -> Range<usize> {
1128
        let start = self.coverage_format_byte_range().end;
1129
        start..start + u16::RAW_BYTE_LEN
1130
    }
1131
1132
    pub fn range_records_byte_range(&self) -> Range<usize> {
1133
        let range_count = self.range_count();
1134
        let start = self.range_count_byte_range().end;
1135
        start..start + (range_count as usize).saturating_mul(RangeRecord::RAW_BYTE_LEN)
1136
    }
1137
}
1138
1139
#[cfg(feature = "experimental_traverse")]
1140
impl<'a> SomeTable<'a> for CoverageFormat2<'a> {
1141
    fn type_name(&self) -> &str {
1142
        "CoverageFormat2"
1143
    }
1144
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1145
        match idx {
1146
            0usize => Some(Field::new("coverage_format", self.coverage_format())),
1147
            1usize => Some(Field::new("range_count", self.range_count())),
1148
            2usize => Some(Field::new(
1149
                "range_records",
1150
                traversal::FieldType::array_of_records(
1151
                    stringify!(RangeRecord),
1152
                    self.range_records(),
1153
                    self.offset_data(),
1154
                ),
1155
            )),
1156
            _ => None,
1157
        }
1158
    }
1159
}
1160
1161
#[cfg(feature = "experimental_traverse")]
1162
#[allow(clippy::needless_lifetimes)]
1163
impl<'a> std::fmt::Debug for CoverageFormat2<'a> {
1164
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1165
        (self as &dyn SomeTable<'a>).fmt(f)
1166
    }
1167
}
1168
1169
/// Used in [CoverageFormat2]
1170
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
1171
#[repr(C)]
1172
#[repr(packed)]
1173
pub struct RangeRecord {
1174
    /// First glyph ID in the range
1175
    pub start_glyph_id: BigEndian<GlyphId16>,
1176
    /// Last glyph ID in the range
1177
    pub end_glyph_id: BigEndian<GlyphId16>,
1178
    /// Coverage Index of first glyph ID in range
1179
    pub start_coverage_index: BigEndian<u16>,
1180
}
1181
1182
impl RangeRecord {
1183
    /// First glyph ID in the range
1184
    pub fn start_glyph_id(&self) -> GlyphId16 {
1185
        self.start_glyph_id.get()
1186
    }
1187
1188
    /// Last glyph ID in the range
1189
    pub fn end_glyph_id(&self) -> GlyphId16 {
1190
        self.end_glyph_id.get()
1191
    }
1192
1193
    /// Coverage Index of first glyph ID in range
1194
    pub fn start_coverage_index(&self) -> u16 {
1195
        self.start_coverage_index.get()
1196
    }
1197
}
1198
1199
impl FixedSize for RangeRecord {
1200
    const RAW_BYTE_LEN: usize =
1201
        GlyphId16::RAW_BYTE_LEN + GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
1202
}
1203
1204
#[cfg(feature = "experimental_traverse")]
1205
impl<'a> SomeRecord<'a> for RangeRecord {
1206
    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
1207
        RecordResolver {
1208
            name: "RangeRecord",
1209
            get_field: Box::new(move |idx, _data| match idx {
1210
                0usize => Some(Field::new("start_glyph_id", self.start_glyph_id())),
1211
                1usize => Some(Field::new("end_glyph_id", self.end_glyph_id())),
1212
                2usize => Some(Field::new(
1213
                    "start_coverage_index",
1214
                    self.start_coverage_index(),
1215
                )),
1216
                _ => None,
1217
            }),
1218
            data,
1219
        }
1220
    }
1221
}
1222
1223
/// [Coverage Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-table)
1224
#[derive(Clone)]
1225
pub enum CoverageTable<'a> {
1226
    Format1(CoverageFormat1<'a>),
1227
    Format2(CoverageFormat2<'a>),
1228
}
1229
1230
impl<'a> CoverageTable<'a> {
1231
    ///Return the `FontData` used to resolve offsets for this table.
1232
    pub fn offset_data(&self) -> FontData<'a> {
1233
        match self {
1234
            Self::Format1(item) => item.offset_data(),
1235
            Self::Format2(item) => item.offset_data(),
1236
        }
1237
    }
1238
1239
    /// Format identifier — format = 1
1240
    pub fn coverage_format(&self) -> u16 {
1241
        match self {
1242
            Self::Format1(item) => item.coverage_format(),
1243
            Self::Format2(item) => item.coverage_format(),
1244
        }
1245
    }
1246
}
1247
1248
impl<'a> FontRead<'a> for CoverageTable<'a> {
1249
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1250
        let format: u16 = data.read_at(0usize)?;
1251
        match format {
1252
            CoverageFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
1253
            CoverageFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
1254
            other => Err(ReadError::InvalidFormat(other.into())),
1255
        }
1256
    }
1257
}
1258
1259
impl<'a> MinByteRange<'a> for CoverageTable<'a> {
1260
    fn min_byte_range(&self) -> Range<usize> {
1261
        match self {
1262
            Self::Format1(item) => item.min_byte_range(),
1263
            Self::Format2(item) => item.min_byte_range(),
1264
        }
1265
    }
1266
    fn min_table_bytes(&self) -> &'a [u8] {
1267
        match self {
1268
            Self::Format1(item) => item.min_table_bytes(),
1269
            Self::Format2(item) => item.min_table_bytes(),
1270
        }
1271
    }
1272
}
1273
1274
#[cfg(feature = "experimental_traverse")]
1275
impl<'a> CoverageTable<'a> {
1276
    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
1277
        match self {
1278
            Self::Format1(table) => table,
1279
            Self::Format2(table) => table,
1280
        }
1281
    }
1282
}
1283
1284
#[cfg(feature = "experimental_traverse")]
1285
impl std::fmt::Debug for CoverageTable<'_> {
1286
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1287
        self.dyn_inner().fmt(f)
1288
    }
1289
}
1290
1291
#[cfg(feature = "experimental_traverse")]
1292
impl<'a> SomeTable<'a> for CoverageTable<'a> {
1293
    fn type_name(&self) -> &str {
1294
        self.dyn_inner().type_name()
1295
    }
1296
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1297
        self.dyn_inner().get_field(idx)
1298
    }
1299
}
1300
1301
impl Format<u16> for ClassDefFormat1<'_> {
1302
    const FORMAT: u16 = 1;
1303
}
1304
1305
impl<'a> MinByteRange<'a> for ClassDefFormat1<'a> {
1306
    fn min_byte_range(&self) -> Range<usize> {
1307
        0..self.class_value_array_byte_range().end
1308
    }
1309
    fn min_table_bytes(&self) -> &'a [u8] {
1310
        let range = self.min_byte_range();
1311
        self.data.as_bytes().get(range).unwrap_or_default()
1312
    }
1313
}
1314
1315
impl<'a> FontRead<'a> for ClassDefFormat1<'a> {
1316
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1317
        #[allow(clippy::absurd_extreme_comparisons)]
1318
        if data.len() < Self::MIN_SIZE {
1319
            return Err(ReadError::OutOfBounds);
1320
        }
1321
        Ok(Self { data })
1322
    }
1323
}
1324
1325
/// [Class Definition Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-1)
1326
#[derive(Clone)]
1327
pub struct ClassDefFormat1<'a> {
1328
    data: FontData<'a>,
1329
}
1330
1331
#[allow(clippy::needless_lifetimes)]
1332
impl<'a> ClassDefFormat1<'a> {
1333
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1334
    basic_table_impls!(impl_the_methods);
1335
1336
    /// Format identifier — format = 1
1337
    pub fn class_format(&self) -> u16 {
1338
        let range = self.class_format_byte_range();
1339
        self.data.read_at(range.start).ok().unwrap()
1340
    }
1341
1342
    /// First glyph ID of the classValueArray
1343
    pub fn start_glyph_id(&self) -> GlyphId16 {
1344
        let range = self.start_glyph_id_byte_range();
1345
        self.data.read_at(range.start).ok().unwrap()
1346
    }
1347
1348
    /// Size of the classValueArray
1349
    pub fn glyph_count(&self) -> u16 {
1350
        let range = self.glyph_count_byte_range();
1351
        self.data.read_at(range.start).ok().unwrap()
1352
    }
1353
1354
    /// Array of Class Values — one per glyph ID
1355
    pub fn class_value_array(&self) -> &'a [BigEndian<u16>] {
1356
        let range = self.class_value_array_byte_range();
1357
        self.data.read_array(range).ok().unwrap_or_default()
1358
    }
1359
1360
    pub fn class_format_byte_range(&self) -> Range<usize> {
1361
        let start = 0;
1362
        start..start + u16::RAW_BYTE_LEN
1363
    }
1364
1365
    pub fn start_glyph_id_byte_range(&self) -> Range<usize> {
1366
        let start = self.class_format_byte_range().end;
1367
        start..start + GlyphId16::RAW_BYTE_LEN
1368
    }
1369
1370
    pub fn glyph_count_byte_range(&self) -> Range<usize> {
1371
        let start = self.start_glyph_id_byte_range().end;
1372
        start..start + u16::RAW_BYTE_LEN
1373
    }
1374
1375
    pub fn class_value_array_byte_range(&self) -> Range<usize> {
1376
        let glyph_count = self.glyph_count();
1377
        let start = self.glyph_count_byte_range().end;
1378
        start..start + (glyph_count as usize).saturating_mul(u16::RAW_BYTE_LEN)
1379
    }
1380
}
1381
1382
#[cfg(feature = "experimental_traverse")]
1383
impl<'a> SomeTable<'a> for ClassDefFormat1<'a> {
1384
    fn type_name(&self) -> &str {
1385
        "ClassDefFormat1"
1386
    }
1387
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1388
        match idx {
1389
            0usize => Some(Field::new("class_format", self.class_format())),
1390
            1usize => Some(Field::new("start_glyph_id", self.start_glyph_id())),
1391
            2usize => Some(Field::new("glyph_count", self.glyph_count())),
1392
            3usize => Some(Field::new("class_value_array", self.class_value_array())),
1393
            _ => None,
1394
        }
1395
    }
1396
}
1397
1398
#[cfg(feature = "experimental_traverse")]
1399
#[allow(clippy::needless_lifetimes)]
1400
impl<'a> std::fmt::Debug for ClassDefFormat1<'a> {
1401
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1402
        (self as &dyn SomeTable<'a>).fmt(f)
1403
    }
1404
}
1405
1406
impl Format<u16> for ClassDefFormat2<'_> {
1407
    const FORMAT: u16 = 2;
1408
}
1409
1410
impl<'a> MinByteRange<'a> for ClassDefFormat2<'a> {
1411
    fn min_byte_range(&self) -> Range<usize> {
1412
        0..self.class_range_records_byte_range().end
1413
    }
1414
    fn min_table_bytes(&self) -> &'a [u8] {
1415
        let range = self.min_byte_range();
1416
        self.data.as_bytes().get(range).unwrap_or_default()
1417
    }
1418
}
1419
1420
impl<'a> FontRead<'a> for ClassDefFormat2<'a> {
1421
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1422
        #[allow(clippy::absurd_extreme_comparisons)]
1423
        if data.len() < Self::MIN_SIZE {
1424
            return Err(ReadError::OutOfBounds);
1425
        }
1426
        Ok(Self { data })
1427
    }
1428
}
1429
1430
/// [Class Definition Table Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-2)
1431
#[derive(Clone)]
1432
pub struct ClassDefFormat2<'a> {
1433
    data: FontData<'a>,
1434
}
1435
1436
#[allow(clippy::needless_lifetimes)]
1437
impl<'a> ClassDefFormat2<'a> {
1438
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1439
    basic_table_impls!(impl_the_methods);
1440
1441
    /// Format identifier — format = 2
1442
    pub fn class_format(&self) -> u16 {
1443
        let range = self.class_format_byte_range();
1444
        self.data.read_at(range.start).ok().unwrap()
1445
    }
1446
1447
    /// Number of ClassRangeRecords
1448
    pub fn class_range_count(&self) -> u16 {
1449
        let range = self.class_range_count_byte_range();
1450
        self.data.read_at(range.start).ok().unwrap()
1451
    }
1452
1453
    /// Array of ClassRangeRecords — ordered by startGlyphID
1454
    pub fn class_range_records(&self) -> &'a [ClassRangeRecord] {
1455
        let range = self.class_range_records_byte_range();
1456
        self.data.read_array(range).ok().unwrap_or_default()
1457
    }
1458
1459
    pub fn class_format_byte_range(&self) -> Range<usize> {
1460
        let start = 0;
1461
        start..start + u16::RAW_BYTE_LEN
1462
    }
1463
1464
    pub fn class_range_count_byte_range(&self) -> Range<usize> {
1465
        let start = self.class_format_byte_range().end;
1466
        start..start + u16::RAW_BYTE_LEN
1467
    }
1468
1469
    pub fn class_range_records_byte_range(&self) -> Range<usize> {
1470
        let class_range_count = self.class_range_count();
1471
        let start = self.class_range_count_byte_range().end;
1472
        start..start + (class_range_count as usize).saturating_mul(ClassRangeRecord::RAW_BYTE_LEN)
1473
    }
1474
}
1475
1476
#[cfg(feature = "experimental_traverse")]
1477
impl<'a> SomeTable<'a> for ClassDefFormat2<'a> {
1478
    fn type_name(&self) -> &str {
1479
        "ClassDefFormat2"
1480
    }
1481
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1482
        match idx {
1483
            0usize => Some(Field::new("class_format", self.class_format())),
1484
            1usize => Some(Field::new("class_range_count", self.class_range_count())),
1485
            2usize => Some(Field::new(
1486
                "class_range_records",
1487
                traversal::FieldType::array_of_records(
1488
                    stringify!(ClassRangeRecord),
1489
                    self.class_range_records(),
1490
                    self.offset_data(),
1491
                ),
1492
            )),
1493
            _ => None,
1494
        }
1495
    }
1496
}
1497
1498
#[cfg(feature = "experimental_traverse")]
1499
#[allow(clippy::needless_lifetimes)]
1500
impl<'a> std::fmt::Debug for ClassDefFormat2<'a> {
1501
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1502
        (self as &dyn SomeTable<'a>).fmt(f)
1503
    }
1504
}
1505
1506
/// Used in [ClassDefFormat2]
1507
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
1508
#[repr(C)]
1509
#[repr(packed)]
1510
pub struct ClassRangeRecord {
1511
    /// First glyph ID in the range
1512
    pub start_glyph_id: BigEndian<GlyphId16>,
1513
    /// Last glyph ID in the range
1514
    pub end_glyph_id: BigEndian<GlyphId16>,
1515
    /// Applied to all glyphs in the range
1516
    pub class: BigEndian<u16>,
1517
}
1518
1519
impl ClassRangeRecord {
1520
    /// First glyph ID in the range
1521
    pub fn start_glyph_id(&self) -> GlyphId16 {
1522
        self.start_glyph_id.get()
1523
    }
1524
1525
    /// Last glyph ID in the range
1526
    pub fn end_glyph_id(&self) -> GlyphId16 {
1527
        self.end_glyph_id.get()
1528
    }
1529
1530
    /// Applied to all glyphs in the range
1531
    pub fn class(&self) -> u16 {
1532
        self.class.get()
1533
    }
1534
}
1535
1536
impl FixedSize for ClassRangeRecord {
1537
    const RAW_BYTE_LEN: usize =
1538
        GlyphId16::RAW_BYTE_LEN + GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
1539
}
1540
1541
#[cfg(feature = "experimental_traverse")]
1542
impl<'a> SomeRecord<'a> for ClassRangeRecord {
1543
    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
1544
        RecordResolver {
1545
            name: "ClassRangeRecord",
1546
            get_field: Box::new(move |idx, _data| match idx {
1547
                0usize => Some(Field::new("start_glyph_id", self.start_glyph_id())),
1548
                1usize => Some(Field::new("end_glyph_id", self.end_glyph_id())),
1549
                2usize => Some(Field::new("class", self.class())),
1550
                _ => None,
1551
            }),
1552
            data,
1553
        }
1554
    }
1555
}
1556
1557
/// A [Class Definition Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table)
1558
#[derive(Clone)]
1559
pub enum ClassDef<'a> {
1560
    Format1(ClassDefFormat1<'a>),
1561
    Format2(ClassDefFormat2<'a>),
1562
}
1563
1564
impl<'a> ClassDef<'a> {
1565
    ///Return the `FontData` used to resolve offsets for this table.
1566
    pub fn offset_data(&self) -> FontData<'a> {
1567
        match self {
1568
            Self::Format1(item) => item.offset_data(),
1569
            Self::Format2(item) => item.offset_data(),
1570
        }
1571
    }
1572
1573
    /// Format identifier — format = 1
1574
    pub fn class_format(&self) -> u16 {
1575
        match self {
1576
            Self::Format1(item) => item.class_format(),
1577
            Self::Format2(item) => item.class_format(),
1578
        }
1579
    }
1580
}
1581
1582
impl<'a> FontRead<'a> for ClassDef<'a> {
1583
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1584
        let format: u16 = data.read_at(0usize)?;
1585
        match format {
1586
            ClassDefFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
1587
            ClassDefFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
1588
            other => Err(ReadError::InvalidFormat(other.into())),
1589
        }
1590
    }
1591
}
1592
1593
impl<'a> MinByteRange<'a> for ClassDef<'a> {
1594
    fn min_byte_range(&self) -> Range<usize> {
1595
        match self {
1596
            Self::Format1(item) => item.min_byte_range(),
1597
            Self::Format2(item) => item.min_byte_range(),
1598
        }
1599
    }
1600
    fn min_table_bytes(&self) -> &'a [u8] {
1601
        match self {
1602
            Self::Format1(item) => item.min_table_bytes(),
1603
            Self::Format2(item) => item.min_table_bytes(),
1604
        }
1605
    }
1606
}
1607
1608
#[cfg(feature = "experimental_traverse")]
1609
impl<'a> ClassDef<'a> {
1610
    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
1611
        match self {
1612
            Self::Format1(table) => table,
1613
            Self::Format2(table) => table,
1614
        }
1615
    }
1616
}
1617
1618
#[cfg(feature = "experimental_traverse")]
1619
impl std::fmt::Debug for ClassDef<'_> {
1620
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1621
        self.dyn_inner().fmt(f)
1622
    }
1623
}
1624
1625
#[cfg(feature = "experimental_traverse")]
1626
impl<'a> SomeTable<'a> for ClassDef<'a> {
1627
    fn type_name(&self) -> &str {
1628
        self.dyn_inner().type_name()
1629
    }
1630
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1631
        self.dyn_inner().get_field(idx)
1632
    }
1633
}
1634
1635
/// [Sequence Lookup Record](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-lookup-record)
1636
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
1637
#[repr(C)]
1638
#[repr(packed)]
1639
pub struct SequenceLookupRecord {
1640
    /// Index (zero-based) into the input glyph sequence
1641
    pub sequence_index: BigEndian<u16>,
1642
    /// Index (zero-based) into the LookupList
1643
    pub lookup_list_index: BigEndian<u16>,
1644
}
1645
1646
impl SequenceLookupRecord {
1647
    /// Index (zero-based) into the input glyph sequence
1648
    pub fn sequence_index(&self) -> u16 {
1649
        self.sequence_index.get()
1650
    }
1651
1652
    /// Index (zero-based) into the LookupList
1653
    pub fn lookup_list_index(&self) -> u16 {
1654
        self.lookup_list_index.get()
1655
    }
1656
}
1657
1658
impl FixedSize for SequenceLookupRecord {
1659
    const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
1660
}
1661
1662
#[cfg(feature = "experimental_traverse")]
1663
impl<'a> SomeRecord<'a> for SequenceLookupRecord {
1664
    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
1665
        RecordResolver {
1666
            name: "SequenceLookupRecord",
1667
            get_field: Box::new(move |idx, _data| match idx {
1668
                0usize => Some(Field::new("sequence_index", self.sequence_index())),
1669
                1usize => Some(Field::new("lookup_list_index", self.lookup_list_index())),
1670
                _ => None,
1671
            }),
1672
            data,
1673
        }
1674
    }
1675
}
1676
1677
impl Format<u16> for SequenceContextFormat1<'_> {
1678
    const FORMAT: u16 = 1;
1679
}
1680
1681
impl<'a> MinByteRange<'a> for SequenceContextFormat1<'a> {
1682
    fn min_byte_range(&self) -> Range<usize> {
1683
        0..self.seq_rule_set_offsets_byte_range().end
1684
    }
1685
    fn min_table_bytes(&self) -> &'a [u8] {
1686
        let range = self.min_byte_range();
1687
        self.data.as_bytes().get(range).unwrap_or_default()
1688
    }
1689
}
1690
1691
impl<'a> FontRead<'a> for SequenceContextFormat1<'a> {
1692
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1693
        #[allow(clippy::absurd_extreme_comparisons)]
1694
        if data.len() < Self::MIN_SIZE {
1695
            return Err(ReadError::OutOfBounds);
1696
        }
1697
        Ok(Self { data })
1698
    }
1699
}
1700
1701
/// [Sequence Context Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-1-simple-glyph-contexts)
1702
#[derive(Clone)]
1703
pub struct SequenceContextFormat1<'a> {
1704
    data: FontData<'a>,
1705
}
1706
1707
#[allow(clippy::needless_lifetimes)]
1708
impl<'a> SequenceContextFormat1<'a> {
1709
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1710
    basic_table_impls!(impl_the_methods);
1711
1712
    /// Format identifier: format = 1
1713
    pub fn format(&self) -> u16 {
1714
        let range = self.format_byte_range();
1715
        self.data.read_at(range.start).ok().unwrap()
1716
    }
1717
1718
    /// Offset to Coverage table, from beginning of
1719
    /// SequenceContextFormat1 table
1720
    pub fn coverage_offset(&self) -> Offset16 {
1721
        let range = self.coverage_offset_byte_range();
1722
        self.data.read_at(range.start).ok().unwrap()
1723
    }
1724
1725
    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1726
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1727
        let data = self.data;
1728
        self.coverage_offset().resolve(data)
1729
    }
1730
1731
    /// Number of SequenceRuleSet tables
1732
    pub fn seq_rule_set_count(&self) -> u16 {
1733
        let range = self.seq_rule_set_count_byte_range();
1734
        self.data.read_at(range.start).ok().unwrap()
1735
    }
1736
1737
    /// Array of offsets to SequenceRuleSet tables, from beginning of
1738
    /// SequenceContextFormat1 table (offsets may be NULL)
1739
    pub fn seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
1740
        let range = self.seq_rule_set_offsets_byte_range();
1741
        self.data.read_array(range).ok().unwrap_or_default()
1742
    }
1743
1744
    /// A dynamically resolving wrapper for [`seq_rule_set_offsets`][Self::seq_rule_set_offsets].
1745
    pub fn seq_rule_sets(&self) -> ArrayOfNullableOffsets<'a, SequenceRuleSet<'a>, Offset16> {
1746
        let data = self.data;
1747
        let offsets = self.seq_rule_set_offsets();
1748
        ArrayOfNullableOffsets::new(offsets, data, ())
1749
    }
1750
1751
    pub fn format_byte_range(&self) -> Range<usize> {
1752
        let start = 0;
1753
        start..start + u16::RAW_BYTE_LEN
1754
    }
1755
1756
    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1757
        let start = self.format_byte_range().end;
1758
        start..start + Offset16::RAW_BYTE_LEN
1759
    }
1760
1761
    pub fn seq_rule_set_count_byte_range(&self) -> Range<usize> {
1762
        let start = self.coverage_offset_byte_range().end;
1763
        start..start + u16::RAW_BYTE_LEN
1764
    }
1765
1766
    pub fn seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
1767
        let seq_rule_set_count = self.seq_rule_set_count();
1768
        let start = self.seq_rule_set_count_byte_range().end;
1769
        start..start + (seq_rule_set_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
1770
    }
1771
}
1772
1773
#[cfg(feature = "experimental_traverse")]
1774
impl<'a> SomeTable<'a> for SequenceContextFormat1<'a> {
1775
    fn type_name(&self) -> &str {
1776
        "SequenceContextFormat1"
1777
    }
1778
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1779
        match idx {
1780
            0usize => Some(Field::new("format", self.format())),
1781
            1usize => Some(Field::new(
1782
                "coverage_offset",
1783
                FieldType::offset(self.coverage_offset(), self.coverage()),
1784
            )),
1785
            2usize => Some(Field::new("seq_rule_set_count", self.seq_rule_set_count())),
1786
            3usize => Some({
1787
                let data = self.data;
1788
                Field::new(
1789
                    "seq_rule_set_offsets",
1790
                    FieldType::array_of_offsets(
1791
                        better_type_name::<SequenceRuleSet>(),
1792
                        self.seq_rule_set_offsets(),
1793
                        move |off| {
1794
                            let target = off.get().resolve::<SequenceRuleSet>(data);
1795
                            FieldType::offset(off.get(), target)
1796
                        },
1797
                    ),
1798
                )
1799
            }),
1800
            _ => None,
1801
        }
1802
    }
1803
}
1804
1805
#[cfg(feature = "experimental_traverse")]
1806
#[allow(clippy::needless_lifetimes)]
1807
impl<'a> std::fmt::Debug for SequenceContextFormat1<'a> {
1808
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1809
        (self as &dyn SomeTable<'a>).fmt(f)
1810
    }
1811
}
1812
1813
impl<'a> MinByteRange<'a> for SequenceRuleSet<'a> {
1814
    fn min_byte_range(&self) -> Range<usize> {
1815
        0..self.seq_rule_offsets_byte_range().end
1816
    }
1817
    fn min_table_bytes(&self) -> &'a [u8] {
1818
        let range = self.min_byte_range();
1819
        self.data.as_bytes().get(range).unwrap_or_default()
1820
    }
1821
}
1822
1823
impl<'a> FontRead<'a> for SequenceRuleSet<'a> {
1824
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1825
        #[allow(clippy::absurd_extreme_comparisons)]
1826
        if data.len() < Self::MIN_SIZE {
1827
            return Err(ReadError::OutOfBounds);
1828
        }
1829
        Ok(Self { data })
1830
    }
1831
}
1832
1833
/// Part of [SequenceContextFormat1]
1834
#[derive(Clone)]
1835
pub struct SequenceRuleSet<'a> {
1836
    data: FontData<'a>,
1837
}
1838
1839
#[allow(clippy::needless_lifetimes)]
1840
impl<'a> SequenceRuleSet<'a> {
1841
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
1842
    basic_table_impls!(impl_the_methods);
1843
1844
    /// Number of SequenceRule tables
1845
    pub fn seq_rule_count(&self) -> u16 {
1846
        let range = self.seq_rule_count_byte_range();
1847
        self.data.read_at(range.start).ok().unwrap()
1848
    }
1849
1850
    /// Array of offsets to SequenceRule tables, from beginning of the
1851
    /// SequenceRuleSet table
1852
    pub fn seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
1853
        let range = self.seq_rule_offsets_byte_range();
1854
        self.data.read_array(range).ok().unwrap_or_default()
1855
    }
1856
1857
    /// A dynamically resolving wrapper for [`seq_rule_offsets`][Self::seq_rule_offsets].
1858
    pub fn seq_rules(&self) -> ArrayOfOffsets<'a, SequenceRule<'a>, Offset16> {
1859
        let data = self.data;
1860
        let offsets = self.seq_rule_offsets();
1861
        ArrayOfOffsets::new(offsets, data, ())
1862
    }
1863
1864
    pub fn seq_rule_count_byte_range(&self) -> Range<usize> {
1865
        let start = 0;
1866
        start..start + u16::RAW_BYTE_LEN
1867
    }
1868
1869
    pub fn seq_rule_offsets_byte_range(&self) -> Range<usize> {
1870
        let seq_rule_count = self.seq_rule_count();
1871
        let start = self.seq_rule_count_byte_range().end;
1872
        start..start + (seq_rule_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
1873
    }
1874
}
1875
1876
#[cfg(feature = "experimental_traverse")]
1877
impl<'a> SomeTable<'a> for SequenceRuleSet<'a> {
1878
    fn type_name(&self) -> &str {
1879
        "SequenceRuleSet"
1880
    }
1881
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1882
        match idx {
1883
            0usize => Some(Field::new("seq_rule_count", self.seq_rule_count())),
1884
            1usize => Some({
1885
                let data = self.data;
1886
                Field::new(
1887
                    "seq_rule_offsets",
1888
                    FieldType::array_of_offsets(
1889
                        better_type_name::<SequenceRule>(),
1890
                        self.seq_rule_offsets(),
1891
                        move |off| {
1892
                            let target = off.get().resolve::<SequenceRule>(data);
1893
                            FieldType::offset(off.get(), target)
1894
                        },
1895
                    ),
1896
                )
1897
            }),
1898
            _ => None,
1899
        }
1900
    }
1901
}
1902
1903
#[cfg(feature = "experimental_traverse")]
1904
#[allow(clippy::needless_lifetimes)]
1905
impl<'a> std::fmt::Debug for SequenceRuleSet<'a> {
1906
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1907
        (self as &dyn SomeTable<'a>).fmt(f)
1908
    }
1909
}
1910
1911
impl<'a> MinByteRange<'a> for SequenceRule<'a> {
1912
    fn min_byte_range(&self) -> Range<usize> {
1913
        0..self.seq_lookup_records_byte_range().end
1914
    }
1915
    fn min_table_bytes(&self) -> &'a [u8] {
1916
        let range = self.min_byte_range();
1917
        self.data.as_bytes().get(range).unwrap_or_default()
1918
    }
1919
}
1920
1921
impl<'a> FontRead<'a> for SequenceRule<'a> {
1922
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1923
        #[allow(clippy::absurd_extreme_comparisons)]
1924
        if data.len() < Self::MIN_SIZE {
1925
            return Err(ReadError::OutOfBounds);
1926
        }
1927
        Ok(Self { data })
1928
    }
1929
}
1930
1931
/// Part of [SequenceContextFormat1]
1932
#[derive(Clone)]
1933
pub struct SequenceRule<'a> {
1934
    data: FontData<'a>,
1935
}
1936
1937
#[allow(clippy::needless_lifetimes)]
1938
impl<'a> SequenceRule<'a> {
1939
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1940
    basic_table_impls!(impl_the_methods);
1941
1942
    /// Number of glyphs in the input glyph sequence
1943
    pub fn glyph_count(&self) -> u16 {
1944
        let range = self.glyph_count_byte_range();
1945
        self.data.read_at(range.start).ok().unwrap()
1946
    }
1947
1948
    /// Number of SequenceLookupRecords
1949
    pub fn seq_lookup_count(&self) -> u16 {
1950
        let range = self.seq_lookup_count_byte_range();
1951
        self.data.read_at(range.start).ok().unwrap()
1952
    }
1953
1954
    /// Array of input glyph IDs—starting with the second glyph
1955
    pub fn input_sequence(&self) -> &'a [BigEndian<GlyphId16>] {
1956
        let range = self.input_sequence_byte_range();
1957
        self.data.read_array(range).ok().unwrap_or_default()
1958
    }
1959
1960
    /// Array of Sequence lookup records
1961
    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
1962
        let range = self.seq_lookup_records_byte_range();
1963
        self.data.read_array(range).ok().unwrap_or_default()
1964
    }
1965
1966
    pub fn glyph_count_byte_range(&self) -> Range<usize> {
1967
        let start = 0;
1968
        start..start + u16::RAW_BYTE_LEN
1969
    }
1970
1971
    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
1972
        let start = self.glyph_count_byte_range().end;
1973
        start..start + u16::RAW_BYTE_LEN
1974
    }
1975
1976
    pub fn input_sequence_byte_range(&self) -> Range<usize> {
1977
        let glyph_count = self.glyph_count();
1978
        let start = self.seq_lookup_count_byte_range().end;
1979
        start
1980
            ..start
1981
                + (transforms::subtract(glyph_count, 1_usize))
1982
                    .saturating_mul(GlyphId16::RAW_BYTE_LEN)
1983
    }
1984
1985
    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
1986
        let seq_lookup_count = self.seq_lookup_count();
1987
        let start = self.input_sequence_byte_range().end;
1988
        start
1989
            ..start + (seq_lookup_count as usize).saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN)
1990
    }
1991
}
1992
1993
#[cfg(feature = "experimental_traverse")]
1994
impl<'a> SomeTable<'a> for SequenceRule<'a> {
1995
    fn type_name(&self) -> &str {
1996
        "SequenceRule"
1997
    }
1998
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1999
        match idx {
2000
            0usize => Some(Field::new("glyph_count", self.glyph_count())),
2001
            1usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
2002
            2usize => Some(Field::new("input_sequence", self.input_sequence())),
2003
            3usize => Some(Field::new(
2004
                "seq_lookup_records",
2005
                traversal::FieldType::array_of_records(
2006
                    stringify!(SequenceLookupRecord),
2007
                    self.seq_lookup_records(),
2008
                    self.offset_data(),
2009
                ),
2010
            )),
2011
            _ => None,
2012
        }
2013
    }
2014
}
2015
2016
#[cfg(feature = "experimental_traverse")]
2017
#[allow(clippy::needless_lifetimes)]
2018
impl<'a> std::fmt::Debug for SequenceRule<'a> {
2019
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2020
        (self as &dyn SomeTable<'a>).fmt(f)
2021
    }
2022
}
2023
2024
impl Format<u16> for SequenceContextFormat2<'_> {
2025
    const FORMAT: u16 = 2;
2026
}
2027
2028
impl<'a> MinByteRange<'a> for SequenceContextFormat2<'a> {
2029
    fn min_byte_range(&self) -> Range<usize> {
2030
        0..self.class_seq_rule_set_offsets_byte_range().end
2031
    }
2032
    fn min_table_bytes(&self) -> &'a [u8] {
2033
        let range = self.min_byte_range();
2034
        self.data.as_bytes().get(range).unwrap_or_default()
2035
    }
2036
}
2037
2038
impl<'a> FontRead<'a> for SequenceContextFormat2<'a> {
2039
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2040
        #[allow(clippy::absurd_extreme_comparisons)]
2041
        if data.len() < Self::MIN_SIZE {
2042
            return Err(ReadError::OutOfBounds);
2043
        }
2044
        Ok(Self { data })
2045
    }
2046
}
2047
2048
/// [Sequence Context Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-2-class-based-glyph-contexts)
2049
#[derive(Clone)]
2050
pub struct SequenceContextFormat2<'a> {
2051
    data: FontData<'a>,
2052
}
2053
2054
#[allow(clippy::needless_lifetimes)]
2055
impl<'a> SequenceContextFormat2<'a> {
2056
    pub const MIN_SIZE: usize =
2057
        (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2058
    basic_table_impls!(impl_the_methods);
2059
2060
    /// Format identifier: format = 2
2061
    pub fn format(&self) -> u16 {
2062
        let range = self.format_byte_range();
2063
        self.data.read_at(range.start).ok().unwrap()
2064
    }
2065
2066
    /// Offset to Coverage table, from beginning of
2067
    /// SequenceContextFormat2 table
2068
    pub fn coverage_offset(&self) -> Offset16 {
2069
        let range = self.coverage_offset_byte_range();
2070
        self.data.read_at(range.start).ok().unwrap()
2071
    }
2072
2073
    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
2074
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2075
        let data = self.data;
2076
        self.coverage_offset().resolve(data)
2077
    }
2078
2079
    /// Offset to ClassDef table, from beginning of
2080
    /// SequenceContextFormat2 table
2081
    pub fn class_def_offset(&self) -> Offset16 {
2082
        let range = self.class_def_offset_byte_range();
2083
        self.data.read_at(range.start).ok().unwrap()
2084
    }
2085
2086
    /// Attempt to resolve [`class_def_offset`][Self::class_def_offset].
2087
    pub fn class_def(&self) -> Result<ClassDef<'a>, ReadError> {
2088
        let data = self.data;
2089
        self.class_def_offset().resolve(data)
2090
    }
2091
2092
    /// Number of ClassSequenceRuleSet tables
2093
    pub fn class_seq_rule_set_count(&self) -> u16 {
2094
        let range = self.class_seq_rule_set_count_byte_range();
2095
        self.data.read_at(range.start).ok().unwrap()
2096
    }
2097
2098
    /// Array of offsets to ClassSequenceRuleSet tables, from beginning
2099
    /// of SequenceContextFormat2 table (may be NULL)
2100
    pub fn class_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
2101
        let range = self.class_seq_rule_set_offsets_byte_range();
2102
        self.data.read_array(range).ok().unwrap_or_default()
2103
    }
2104
2105
    /// A dynamically resolving wrapper for [`class_seq_rule_set_offsets`][Self::class_seq_rule_set_offsets].
2106
    pub fn class_seq_rule_sets(
2107
        &self,
2108
    ) -> ArrayOfNullableOffsets<'a, ClassSequenceRuleSet<'a>, Offset16> {
2109
        let data = self.data;
2110
        let offsets = self.class_seq_rule_set_offsets();
2111
        ArrayOfNullableOffsets::new(offsets, data, ())
2112
    }
2113
2114
    pub fn format_byte_range(&self) -> Range<usize> {
2115
        let start = 0;
2116
        start..start + u16::RAW_BYTE_LEN
2117
    }
2118
2119
    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
2120
        let start = self.format_byte_range().end;
2121
        start..start + Offset16::RAW_BYTE_LEN
2122
    }
2123
2124
    pub fn class_def_offset_byte_range(&self) -> Range<usize> {
2125
        let start = self.coverage_offset_byte_range().end;
2126
        start..start + Offset16::RAW_BYTE_LEN
2127
    }
2128
2129
    pub fn class_seq_rule_set_count_byte_range(&self) -> Range<usize> {
2130
        let start = self.class_def_offset_byte_range().end;
2131
        start..start + u16::RAW_BYTE_LEN
2132
    }
2133
2134
    pub fn class_seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
2135
        let class_seq_rule_set_count = self.class_seq_rule_set_count();
2136
        let start = self.class_seq_rule_set_count_byte_range().end;
2137
        start..start + (class_seq_rule_set_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
2138
    }
2139
}
2140
2141
#[cfg(feature = "experimental_traverse")]
2142
impl<'a> SomeTable<'a> for SequenceContextFormat2<'a> {
2143
    fn type_name(&self) -> &str {
2144
        "SequenceContextFormat2"
2145
    }
2146
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2147
        match idx {
2148
            0usize => Some(Field::new("format", self.format())),
2149
            1usize => Some(Field::new(
2150
                "coverage_offset",
2151
                FieldType::offset(self.coverage_offset(), self.coverage()),
2152
            )),
2153
            2usize => Some(Field::new(
2154
                "class_def_offset",
2155
                FieldType::offset(self.class_def_offset(), self.class_def()),
2156
            )),
2157
            3usize => Some(Field::new(
2158
                "class_seq_rule_set_count",
2159
                self.class_seq_rule_set_count(),
2160
            )),
2161
            4usize => Some({
2162
                let data = self.data;
2163
                Field::new(
2164
                    "class_seq_rule_set_offsets",
2165
                    FieldType::array_of_offsets(
2166
                        better_type_name::<ClassSequenceRuleSet>(),
2167
                        self.class_seq_rule_set_offsets(),
2168
                        move |off| {
2169
                            let target = off.get().resolve::<ClassSequenceRuleSet>(data);
2170
                            FieldType::offset(off.get(), target)
2171
                        },
2172
                    ),
2173
                )
2174
            }),
2175
            _ => None,
2176
        }
2177
    }
2178
}
2179
2180
#[cfg(feature = "experimental_traverse")]
2181
#[allow(clippy::needless_lifetimes)]
2182
impl<'a> std::fmt::Debug for SequenceContextFormat2<'a> {
2183
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2184
        (self as &dyn SomeTable<'a>).fmt(f)
2185
    }
2186
}
2187
2188
impl<'a> MinByteRange<'a> for ClassSequenceRuleSet<'a> {
2189
    fn min_byte_range(&self) -> Range<usize> {
2190
        0..self.class_seq_rule_offsets_byte_range().end
2191
    }
2192
    fn min_table_bytes(&self) -> &'a [u8] {
2193
        let range = self.min_byte_range();
2194
        self.data.as_bytes().get(range).unwrap_or_default()
2195
    }
2196
}
2197
2198
impl<'a> FontRead<'a> for ClassSequenceRuleSet<'a> {
2199
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2200
        #[allow(clippy::absurd_extreme_comparisons)]
2201
        if data.len() < Self::MIN_SIZE {
2202
            return Err(ReadError::OutOfBounds);
2203
        }
2204
        Ok(Self { data })
2205
    }
2206
}
2207
2208
/// Part of [SequenceContextFormat2]
2209
#[derive(Clone)]
2210
pub struct ClassSequenceRuleSet<'a> {
2211
    data: FontData<'a>,
2212
}
2213
2214
#[allow(clippy::needless_lifetimes)]
2215
impl<'a> ClassSequenceRuleSet<'a> {
2216
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
2217
    basic_table_impls!(impl_the_methods);
2218
2219
    /// Number of ClassSequenceRule tables
2220
    pub fn class_seq_rule_count(&self) -> u16 {
2221
        let range = self.class_seq_rule_count_byte_range();
2222
        self.data.read_at(range.start).ok().unwrap()
2223
    }
2224
2225
    /// Array of offsets to ClassSequenceRule tables, from beginning of
2226
    /// ClassSequenceRuleSet table
2227
    pub fn class_seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
2228
        let range = self.class_seq_rule_offsets_byte_range();
2229
        self.data.read_array(range).ok().unwrap_or_default()
2230
    }
2231
2232
    /// A dynamically resolving wrapper for [`class_seq_rule_offsets`][Self::class_seq_rule_offsets].
2233
    pub fn class_seq_rules(&self) -> ArrayOfOffsets<'a, ClassSequenceRule<'a>, Offset16> {
2234
        let data = self.data;
2235
        let offsets = self.class_seq_rule_offsets();
2236
        ArrayOfOffsets::new(offsets, data, ())
2237
    }
2238
2239
    pub fn class_seq_rule_count_byte_range(&self) -> Range<usize> {
2240
        let start = 0;
2241
        start..start + u16::RAW_BYTE_LEN
2242
    }
2243
2244
    pub fn class_seq_rule_offsets_byte_range(&self) -> Range<usize> {
2245
        let class_seq_rule_count = self.class_seq_rule_count();
2246
        let start = self.class_seq_rule_count_byte_range().end;
2247
        start..start + (class_seq_rule_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
2248
    }
2249
}
2250
2251
#[cfg(feature = "experimental_traverse")]
2252
impl<'a> SomeTable<'a> for ClassSequenceRuleSet<'a> {
2253
    fn type_name(&self) -> &str {
2254
        "ClassSequenceRuleSet"
2255
    }
2256
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2257
        match idx {
2258
            0usize => Some(Field::new(
2259
                "class_seq_rule_count",
2260
                self.class_seq_rule_count(),
2261
            )),
2262
            1usize => Some({
2263
                let data = self.data;
2264
                Field::new(
2265
                    "class_seq_rule_offsets",
2266
                    FieldType::array_of_offsets(
2267
                        better_type_name::<ClassSequenceRule>(),
2268
                        self.class_seq_rule_offsets(),
2269
                        move |off| {
2270
                            let target = off.get().resolve::<ClassSequenceRule>(data);
2271
                            FieldType::offset(off.get(), target)
2272
                        },
2273
                    ),
2274
                )
2275
            }),
2276
            _ => None,
2277
        }
2278
    }
2279
}
2280
2281
#[cfg(feature = "experimental_traverse")]
2282
#[allow(clippy::needless_lifetimes)]
2283
impl<'a> std::fmt::Debug for ClassSequenceRuleSet<'a> {
2284
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2285
        (self as &dyn SomeTable<'a>).fmt(f)
2286
    }
2287
}
2288
2289
impl<'a> MinByteRange<'a> for ClassSequenceRule<'a> {
2290
    fn min_byte_range(&self) -> Range<usize> {
2291
        0..self.seq_lookup_records_byte_range().end
2292
    }
2293
    fn min_table_bytes(&self) -> &'a [u8] {
2294
        let range = self.min_byte_range();
2295
        self.data.as_bytes().get(range).unwrap_or_default()
2296
    }
2297
}
2298
2299
impl<'a> FontRead<'a> for ClassSequenceRule<'a> {
2300
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2301
        #[allow(clippy::absurd_extreme_comparisons)]
2302
        if data.len() < Self::MIN_SIZE {
2303
            return Err(ReadError::OutOfBounds);
2304
        }
2305
        Ok(Self { data })
2306
    }
2307
}
2308
2309
/// Part of [SequenceContextFormat2]
2310
#[derive(Clone)]
2311
pub struct ClassSequenceRule<'a> {
2312
    data: FontData<'a>,
2313
}
2314
2315
#[allow(clippy::needless_lifetimes)]
2316
impl<'a> ClassSequenceRule<'a> {
2317
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2318
    basic_table_impls!(impl_the_methods);
2319
2320
    /// Number of glyphs to be matched
2321
    pub fn glyph_count(&self) -> u16 {
2322
        let range = self.glyph_count_byte_range();
2323
        self.data.read_at(range.start).ok().unwrap()
2324
    }
2325
2326
    /// Number of SequenceLookupRecords
2327
    pub fn seq_lookup_count(&self) -> u16 {
2328
        let range = self.seq_lookup_count_byte_range();
2329
        self.data.read_at(range.start).ok().unwrap()
2330
    }
2331
2332
    /// Sequence of classes to be matched to the input glyph sequence,
2333
    /// beginning with the second glyph position
2334
    pub fn input_sequence(&self) -> &'a [BigEndian<u16>] {
2335
        let range = self.input_sequence_byte_range();
2336
        self.data.read_array(range).ok().unwrap_or_default()
2337
    }
2338
2339
    /// Array of SequenceLookupRecords
2340
    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
2341
        let range = self.seq_lookup_records_byte_range();
2342
        self.data.read_array(range).ok().unwrap_or_default()
2343
    }
2344
2345
    pub fn glyph_count_byte_range(&self) -> Range<usize> {
2346
        let start = 0;
2347
        start..start + u16::RAW_BYTE_LEN
2348
    }
2349
2350
    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
2351
        let start = self.glyph_count_byte_range().end;
2352
        start..start + u16::RAW_BYTE_LEN
2353
    }
2354
2355
    pub fn input_sequence_byte_range(&self) -> Range<usize> {
2356
        let glyph_count = self.glyph_count();
2357
        let start = self.seq_lookup_count_byte_range().end;
2358
        start
2359
            ..start + (transforms::subtract(glyph_count, 1_usize)).saturating_mul(u16::RAW_BYTE_LEN)
2360
    }
2361
2362
    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
2363
        let seq_lookup_count = self.seq_lookup_count();
2364
        let start = self.input_sequence_byte_range().end;
2365
        start
2366
            ..start + (seq_lookup_count as usize).saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN)
2367
    }
2368
}
2369
2370
#[cfg(feature = "experimental_traverse")]
2371
impl<'a> SomeTable<'a> for ClassSequenceRule<'a> {
2372
    fn type_name(&self) -> &str {
2373
        "ClassSequenceRule"
2374
    }
2375
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2376
        match idx {
2377
            0usize => Some(Field::new("glyph_count", self.glyph_count())),
2378
            1usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
2379
            2usize => Some(Field::new("input_sequence", self.input_sequence())),
2380
            3usize => Some(Field::new(
2381
                "seq_lookup_records",
2382
                traversal::FieldType::array_of_records(
2383
                    stringify!(SequenceLookupRecord),
2384
                    self.seq_lookup_records(),
2385
                    self.offset_data(),
2386
                ),
2387
            )),
2388
            _ => None,
2389
        }
2390
    }
2391
}
2392
2393
#[cfg(feature = "experimental_traverse")]
2394
#[allow(clippy::needless_lifetimes)]
2395
impl<'a> std::fmt::Debug for ClassSequenceRule<'a> {
2396
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2397
        (self as &dyn SomeTable<'a>).fmt(f)
2398
    }
2399
}
2400
2401
impl Format<u16> for SequenceContextFormat3<'_> {
2402
    const FORMAT: u16 = 3;
2403
}
2404
2405
impl<'a> MinByteRange<'a> for SequenceContextFormat3<'a> {
2406
    fn min_byte_range(&self) -> Range<usize> {
2407
        0..self.seq_lookup_records_byte_range().end
2408
    }
2409
    fn min_table_bytes(&self) -> &'a [u8] {
2410
        let range = self.min_byte_range();
2411
        self.data.as_bytes().get(range).unwrap_or_default()
2412
    }
2413
}
2414
2415
impl<'a> FontRead<'a> for SequenceContextFormat3<'a> {
2416
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2417
        #[allow(clippy::absurd_extreme_comparisons)]
2418
        if data.len() < Self::MIN_SIZE {
2419
            return Err(ReadError::OutOfBounds);
2420
        }
2421
        Ok(Self { data })
2422
    }
2423
}
2424
2425
/// [Sequence Context Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-3-coverage-based-glyph-contexts)
2426
#[derive(Clone)]
2427
pub struct SequenceContextFormat3<'a> {
2428
    data: FontData<'a>,
2429
}
2430
2431
#[allow(clippy::needless_lifetimes)]
2432
impl<'a> SequenceContextFormat3<'a> {
2433
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2434
    basic_table_impls!(impl_the_methods);
2435
2436
    /// Format identifier: format = 3
2437
    pub fn format(&self) -> u16 {
2438
        let range = self.format_byte_range();
2439
        self.data.read_at(range.start).ok().unwrap()
2440
    }
2441
2442
    /// Number of glyphs in the input sequence
2443
    pub fn glyph_count(&self) -> u16 {
2444
        let range = self.glyph_count_byte_range();
2445
        self.data.read_at(range.start).ok().unwrap()
2446
    }
2447
2448
    /// Number of SequenceLookupRecords
2449
    pub fn seq_lookup_count(&self) -> u16 {
2450
        let range = self.seq_lookup_count_byte_range();
2451
        self.data.read_at(range.start).ok().unwrap()
2452
    }
2453
2454
    /// Array of offsets to Coverage tables, from beginning of
2455
    /// SequenceContextFormat3 subtable
2456
    pub fn coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
2457
        let range = self.coverage_offsets_byte_range();
2458
        self.data.read_array(range).ok().unwrap_or_default()
2459
    }
2460
2461
    /// A dynamically resolving wrapper for [`coverage_offsets`][Self::coverage_offsets].
2462
    pub fn coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
2463
        let data = self.data;
2464
        let offsets = self.coverage_offsets();
2465
        ArrayOfOffsets::new(offsets, data, ())
2466
    }
2467
2468
    /// Array of SequenceLookupRecords
2469
    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
2470
        let range = self.seq_lookup_records_byte_range();
2471
        self.data.read_array(range).ok().unwrap_or_default()
2472
    }
2473
2474
    pub fn format_byte_range(&self) -> Range<usize> {
2475
        let start = 0;
2476
        start..start + u16::RAW_BYTE_LEN
2477
    }
2478
2479
    pub fn glyph_count_byte_range(&self) -> Range<usize> {
2480
        let start = self.format_byte_range().end;
2481
        start..start + u16::RAW_BYTE_LEN
2482
    }
2483
2484
    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
2485
        let start = self.glyph_count_byte_range().end;
2486
        start..start + u16::RAW_BYTE_LEN
2487
    }
2488
2489
    pub fn coverage_offsets_byte_range(&self) -> Range<usize> {
2490
        let glyph_count = self.glyph_count();
2491
        let start = self.seq_lookup_count_byte_range().end;
2492
        start..start + (glyph_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
2493
    }
2494
2495
    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
2496
        let seq_lookup_count = self.seq_lookup_count();
2497
        let start = self.coverage_offsets_byte_range().end;
2498
        start
2499
            ..start + (seq_lookup_count as usize).saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN)
2500
    }
2501
}
2502
2503
#[cfg(feature = "experimental_traverse")]
2504
impl<'a> SomeTable<'a> for SequenceContextFormat3<'a> {
2505
    fn type_name(&self) -> &str {
2506
        "SequenceContextFormat3"
2507
    }
2508
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2509
        match idx {
2510
            0usize => Some(Field::new("format", self.format())),
2511
            1usize => Some(Field::new("glyph_count", self.glyph_count())),
2512
            2usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
2513
            3usize => Some({
2514
                let data = self.data;
2515
                Field::new(
2516
                    "coverage_offsets",
2517
                    FieldType::array_of_offsets(
2518
                        better_type_name::<CoverageTable>(),
2519
                        self.coverage_offsets(),
2520
                        move |off| {
2521
                            let target = off.get().resolve::<CoverageTable>(data);
2522
                            FieldType::offset(off.get(), target)
2523
                        },
2524
                    ),
2525
                )
2526
            }),
2527
            4usize => Some(Field::new(
2528
                "seq_lookup_records",
2529
                traversal::FieldType::array_of_records(
2530
                    stringify!(SequenceLookupRecord),
2531
                    self.seq_lookup_records(),
2532
                    self.offset_data(),
2533
                ),
2534
            )),
2535
            _ => None,
2536
        }
2537
    }
2538
}
2539
2540
#[cfg(feature = "experimental_traverse")]
2541
#[allow(clippy::needless_lifetimes)]
2542
impl<'a> std::fmt::Debug for SequenceContextFormat3<'a> {
2543
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2544
        (self as &dyn SomeTable<'a>).fmt(f)
2545
    }
2546
}
2547
2548
#[derive(Clone)]
2549
pub enum SequenceContext<'a> {
2550
    Format1(SequenceContextFormat1<'a>),
2551
    Format2(SequenceContextFormat2<'a>),
2552
    Format3(SequenceContextFormat3<'a>),
2553
}
2554
2555
impl<'a> SequenceContext<'a> {
2556
    ///Return the `FontData` used to resolve offsets for this table.
2557
    pub fn offset_data(&self) -> FontData<'a> {
2558
        match self {
2559
            Self::Format1(item) => item.offset_data(),
2560
            Self::Format2(item) => item.offset_data(),
2561
            Self::Format3(item) => item.offset_data(),
2562
        }
2563
    }
2564
2565
    /// Format identifier: format = 1
2566
    pub fn format(&self) -> u16 {
2567
        match self {
2568
            Self::Format1(item) => item.format(),
2569
            Self::Format2(item) => item.format(),
2570
            Self::Format3(item) => item.format(),
2571
        }
2572
    }
2573
}
2574
2575
impl<'a> FontRead<'a> for SequenceContext<'a> {
2576
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2577
        let format: u16 = data.read_at(0usize)?;
2578
        match format {
2579
            SequenceContextFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
2580
            SequenceContextFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
2581
            SequenceContextFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
2582
            other => Err(ReadError::InvalidFormat(other.into())),
2583
        }
2584
    }
2585
}
2586
2587
impl<'a> MinByteRange<'a> for SequenceContext<'a> {
2588
    fn min_byte_range(&self) -> Range<usize> {
2589
        match self {
2590
            Self::Format1(item) => item.min_byte_range(),
2591
            Self::Format2(item) => item.min_byte_range(),
2592
            Self::Format3(item) => item.min_byte_range(),
2593
        }
2594
    }
2595
    fn min_table_bytes(&self) -> &'a [u8] {
2596
        match self {
2597
            Self::Format1(item) => item.min_table_bytes(),
2598
            Self::Format2(item) => item.min_table_bytes(),
2599
            Self::Format3(item) => item.min_table_bytes(),
2600
        }
2601
    }
2602
}
2603
2604
#[cfg(feature = "experimental_traverse")]
2605
impl<'a> SequenceContext<'a> {
2606
    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
2607
        match self {
2608
            Self::Format1(table) => table,
2609
            Self::Format2(table) => table,
2610
            Self::Format3(table) => table,
2611
        }
2612
    }
2613
}
2614
2615
#[cfg(feature = "experimental_traverse")]
2616
impl std::fmt::Debug for SequenceContext<'_> {
2617
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2618
        self.dyn_inner().fmt(f)
2619
    }
2620
}
2621
2622
#[cfg(feature = "experimental_traverse")]
2623
impl<'a> SomeTable<'a> for SequenceContext<'a> {
2624
    fn type_name(&self) -> &str {
2625
        self.dyn_inner().type_name()
2626
    }
2627
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2628
        self.dyn_inner().get_field(idx)
2629
    }
2630
}
2631
2632
impl Format<u16> for ChainedSequenceContextFormat1<'_> {
2633
    const FORMAT: u16 = 1;
2634
}
2635
2636
impl<'a> MinByteRange<'a> for ChainedSequenceContextFormat1<'a> {
2637
    fn min_byte_range(&self) -> Range<usize> {
2638
        0..self.chained_seq_rule_set_offsets_byte_range().end
2639
    }
2640
    fn min_table_bytes(&self) -> &'a [u8] {
2641
        let range = self.min_byte_range();
2642
        self.data.as_bytes().get(range).unwrap_or_default()
2643
    }
2644
}
2645
2646
impl<'a> FontRead<'a> for ChainedSequenceContextFormat1<'a> {
2647
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2648
        #[allow(clippy::absurd_extreme_comparisons)]
2649
        if data.len() < Self::MIN_SIZE {
2650
            return Err(ReadError::OutOfBounds);
2651
        }
2652
        Ok(Self { data })
2653
    }
2654
}
2655
2656
/// [Chained Sequence Context Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-1-simple-glyph-contexts)
2657
#[derive(Clone)]
2658
pub struct ChainedSequenceContextFormat1<'a> {
2659
    data: FontData<'a>,
2660
}
2661
2662
#[allow(clippy::needless_lifetimes)]
2663
impl<'a> ChainedSequenceContextFormat1<'a> {
2664
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2665
    basic_table_impls!(impl_the_methods);
2666
2667
    /// Format identifier: format = 1
2668
    pub fn format(&self) -> u16 {
2669
        let range = self.format_byte_range();
2670
        self.data.read_at(range.start).ok().unwrap()
2671
    }
2672
2673
    /// Offset to Coverage table, from beginning of
2674
    /// ChainSequenceContextFormat1 table
2675
    pub fn coverage_offset(&self) -> Offset16 {
2676
        let range = self.coverage_offset_byte_range();
2677
        self.data.read_at(range.start).ok().unwrap()
2678
    }
2679
2680
    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
2681
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2682
        let data = self.data;
2683
        self.coverage_offset().resolve(data)
2684
    }
2685
2686
    /// Number of ChainedSequenceRuleSet tables
2687
    pub fn chained_seq_rule_set_count(&self) -> u16 {
2688
        let range = self.chained_seq_rule_set_count_byte_range();
2689
        self.data.read_at(range.start).ok().unwrap()
2690
    }
2691
2692
    /// Array of offsets to ChainedSeqRuleSet tables, from beginning of
2693
    /// ChainedSequenceContextFormat1 table (may be NULL)
2694
    pub fn chained_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
2695
        let range = self.chained_seq_rule_set_offsets_byte_range();
2696
        self.data.read_array(range).ok().unwrap_or_default()
2697
    }
2698
2699
    /// A dynamically resolving wrapper for [`chained_seq_rule_set_offsets`][Self::chained_seq_rule_set_offsets].
2700
    pub fn chained_seq_rule_sets(
2701
        &self,
2702
    ) -> ArrayOfNullableOffsets<'a, ChainedSequenceRuleSet<'a>, Offset16> {
2703
        let data = self.data;
2704
        let offsets = self.chained_seq_rule_set_offsets();
2705
        ArrayOfNullableOffsets::new(offsets, data, ())
2706
    }
2707
2708
    pub fn format_byte_range(&self) -> Range<usize> {
2709
        let start = 0;
2710
        start..start + u16::RAW_BYTE_LEN
2711
    }
2712
2713
    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
2714
        let start = self.format_byte_range().end;
2715
        start..start + Offset16::RAW_BYTE_LEN
2716
    }
2717
2718
    pub fn chained_seq_rule_set_count_byte_range(&self) -> Range<usize> {
2719
        let start = self.coverage_offset_byte_range().end;
2720
        start..start + u16::RAW_BYTE_LEN
2721
    }
2722
2723
    pub fn chained_seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
2724
        let chained_seq_rule_set_count = self.chained_seq_rule_set_count();
2725
        let start = self.chained_seq_rule_set_count_byte_range().end;
2726
        start..start + (chained_seq_rule_set_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
2727
    }
2728
}
2729
2730
#[cfg(feature = "experimental_traverse")]
2731
impl<'a> SomeTable<'a> for ChainedSequenceContextFormat1<'a> {
2732
    fn type_name(&self) -> &str {
2733
        "ChainedSequenceContextFormat1"
2734
    }
2735
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2736
        match idx {
2737
            0usize => Some(Field::new("format", self.format())),
2738
            1usize => Some(Field::new(
2739
                "coverage_offset",
2740
                FieldType::offset(self.coverage_offset(), self.coverage()),
2741
            )),
2742
            2usize => Some(Field::new(
2743
                "chained_seq_rule_set_count",
2744
                self.chained_seq_rule_set_count(),
2745
            )),
2746
            3usize => Some({
2747
                let data = self.data;
2748
                Field::new(
2749
                    "chained_seq_rule_set_offsets",
2750
                    FieldType::array_of_offsets(
2751
                        better_type_name::<ChainedSequenceRuleSet>(),
2752
                        self.chained_seq_rule_set_offsets(),
2753
                        move |off| {
2754
                            let target = off.get().resolve::<ChainedSequenceRuleSet>(data);
2755
                            FieldType::offset(off.get(), target)
2756
                        },
2757
                    ),
2758
                )
2759
            }),
2760
            _ => None,
2761
        }
2762
    }
2763
}
2764
2765
#[cfg(feature = "experimental_traverse")]
2766
#[allow(clippy::needless_lifetimes)]
2767
impl<'a> std::fmt::Debug for ChainedSequenceContextFormat1<'a> {
2768
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2769
        (self as &dyn SomeTable<'a>).fmt(f)
2770
    }
2771
}
2772
2773
impl<'a> MinByteRange<'a> for ChainedSequenceRuleSet<'a> {
2774
    fn min_byte_range(&self) -> Range<usize> {
2775
        0..self.chained_seq_rule_offsets_byte_range().end
2776
    }
2777
    fn min_table_bytes(&self) -> &'a [u8] {
2778
        let range = self.min_byte_range();
2779
        self.data.as_bytes().get(range).unwrap_or_default()
2780
    }
2781
}
2782
2783
impl<'a> FontRead<'a> for ChainedSequenceRuleSet<'a> {
2784
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2785
        #[allow(clippy::absurd_extreme_comparisons)]
2786
        if data.len() < Self::MIN_SIZE {
2787
            return Err(ReadError::OutOfBounds);
2788
        }
2789
        Ok(Self { data })
2790
    }
2791
}
2792
2793
/// Part of [ChainedSequenceContextFormat1]
2794
#[derive(Clone)]
2795
pub struct ChainedSequenceRuleSet<'a> {
2796
    data: FontData<'a>,
2797
}
2798
2799
#[allow(clippy::needless_lifetimes)]
2800
impl<'a> ChainedSequenceRuleSet<'a> {
2801
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
2802
    basic_table_impls!(impl_the_methods);
2803
2804
    /// Number of ChainedSequenceRule tables
2805
    pub fn chained_seq_rule_count(&self) -> u16 {
2806
        let range = self.chained_seq_rule_count_byte_range();
2807
        self.data.read_at(range.start).ok().unwrap()
2808
    }
2809
2810
    /// Array of offsets to ChainedSequenceRule tables, from beginning
2811
    /// of ChainedSequenceRuleSet table
2812
    pub fn chained_seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
2813
        let range = self.chained_seq_rule_offsets_byte_range();
2814
        self.data.read_array(range).ok().unwrap_or_default()
2815
    }
2816
2817
    /// A dynamically resolving wrapper for [`chained_seq_rule_offsets`][Self::chained_seq_rule_offsets].
2818
    pub fn chained_seq_rules(&self) -> ArrayOfOffsets<'a, ChainedSequenceRule<'a>, Offset16> {
2819
        let data = self.data;
2820
        let offsets = self.chained_seq_rule_offsets();
2821
        ArrayOfOffsets::new(offsets, data, ())
2822
    }
2823
2824
    pub fn chained_seq_rule_count_byte_range(&self) -> Range<usize> {
2825
        let start = 0;
2826
        start..start + u16::RAW_BYTE_LEN
2827
    }
2828
2829
    pub fn chained_seq_rule_offsets_byte_range(&self) -> Range<usize> {
2830
        let chained_seq_rule_count = self.chained_seq_rule_count();
2831
        let start = self.chained_seq_rule_count_byte_range().end;
2832
        start..start + (chained_seq_rule_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
2833
    }
2834
}
2835
2836
#[cfg(feature = "experimental_traverse")]
2837
impl<'a> SomeTable<'a> for ChainedSequenceRuleSet<'a> {
2838
    fn type_name(&self) -> &str {
2839
        "ChainedSequenceRuleSet"
2840
    }
2841
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2842
        match idx {
2843
            0usize => Some(Field::new(
2844
                "chained_seq_rule_count",
2845
                self.chained_seq_rule_count(),
2846
            )),
2847
            1usize => Some({
2848
                let data = self.data;
2849
                Field::new(
2850
                    "chained_seq_rule_offsets",
2851
                    FieldType::array_of_offsets(
2852
                        better_type_name::<ChainedSequenceRule>(),
2853
                        self.chained_seq_rule_offsets(),
2854
                        move |off| {
2855
                            let target = off.get().resolve::<ChainedSequenceRule>(data);
2856
                            FieldType::offset(off.get(), target)
2857
                        },
2858
                    ),
2859
                )
2860
            }),
2861
            _ => None,
2862
        }
2863
    }
2864
}
2865
2866
#[cfg(feature = "experimental_traverse")]
2867
#[allow(clippy::needless_lifetimes)]
2868
impl<'a> std::fmt::Debug for ChainedSequenceRuleSet<'a> {
2869
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2870
        (self as &dyn SomeTable<'a>).fmt(f)
2871
    }
2872
}
2873
2874
impl<'a> MinByteRange<'a> for ChainedSequenceRule<'a> {
2875
    fn min_byte_range(&self) -> Range<usize> {
2876
        0..self.seq_lookup_records_byte_range().end
2877
    }
2878
    fn min_table_bytes(&self) -> &'a [u8] {
2879
        let range = self.min_byte_range();
2880
        self.data.as_bytes().get(range).unwrap_or_default()
2881
    }
2882
}
2883
2884
impl<'a> FontRead<'a> for ChainedSequenceRule<'a> {
2885
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2886
        #[allow(clippy::absurd_extreme_comparisons)]
2887
        if data.len() < Self::MIN_SIZE {
2888
            return Err(ReadError::OutOfBounds);
2889
        }
2890
        Ok(Self { data })
2891
    }
2892
}
2893
2894
/// Part of [ChainedSequenceContextFormat1]
2895
#[derive(Clone)]
2896
pub struct ChainedSequenceRule<'a> {
2897
    data: FontData<'a>,
2898
}
2899
2900
#[allow(clippy::needless_lifetimes)]
2901
impl<'a> ChainedSequenceRule<'a> {
2902
    pub const MIN_SIZE: usize =
2903
        (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2904
    basic_table_impls!(impl_the_methods);
2905
2906
    /// Number of glyphs in the backtrack sequence
2907
    pub fn backtrack_glyph_count(&self) -> u16 {
2908
        let range = self.backtrack_glyph_count_byte_range();
2909
        self.data.read_at(range.start).ok().unwrap()
2910
    }
2911
2912
    /// Array of backtrack glyph IDs
2913
    pub fn backtrack_sequence(&self) -> &'a [BigEndian<GlyphId16>] {
2914
        let range = self.backtrack_sequence_byte_range();
2915
        self.data.read_array(range).ok().unwrap_or_default()
2916
    }
2917
2918
    /// Number of glyphs in the input sequence
2919
    pub fn input_glyph_count(&self) -> u16 {
2920
        let range = self.input_glyph_count_byte_range();
2921
        self.data.read_at(range.start).ok().unwrap_or_default()
2922
    }
2923
2924
    /// Array of input glyph IDs—start with second glyph
2925
    pub fn input_sequence(&self) -> &'a [BigEndian<GlyphId16>] {
2926
        let range = self.input_sequence_byte_range();
2927
        self.data.read_array(range).ok().unwrap_or_default()
2928
    }
2929
2930
    /// Number of glyphs in the lookahead sequence
2931
    pub fn lookahead_glyph_count(&self) -> u16 {
2932
        let range = self.lookahead_glyph_count_byte_range();
2933
        self.data.read_at(range.start).ok().unwrap_or_default()
2934
    }
2935
2936
    /// Array of lookahead glyph IDs
2937
    pub fn lookahead_sequence(&self) -> &'a [BigEndian<GlyphId16>] {
2938
        let range = self.lookahead_sequence_byte_range();
2939
        self.data.read_array(range).ok().unwrap_or_default()
2940
    }
2941
2942
    /// Number of SequenceLookupRecords
2943
    pub fn seq_lookup_count(&self) -> u16 {
2944
        let range = self.seq_lookup_count_byte_range();
2945
        self.data.read_at(range.start).ok().unwrap_or_default()
2946
    }
2947
2948
    /// Array of SequenceLookupRecords
2949
    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
2950
        let range = self.seq_lookup_records_byte_range();
2951
        self.data.read_array(range).ok().unwrap_or_default()
2952
    }
2953
2954
    pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
2955
        let start = 0;
2956
        start..start + u16::RAW_BYTE_LEN
2957
    }
2958
2959
    pub fn backtrack_sequence_byte_range(&self) -> Range<usize> {
2960
        let backtrack_glyph_count = self.backtrack_glyph_count();
2961
        let start = self.backtrack_glyph_count_byte_range().end;
2962
        start..start + (backtrack_glyph_count as usize).saturating_mul(GlyphId16::RAW_BYTE_LEN)
2963
    }
2964
2965
    pub fn input_glyph_count_byte_range(&self) -> Range<usize> {
2966
        let start = self.backtrack_sequence_byte_range().end;
2967
        start..start + u16::RAW_BYTE_LEN
2968
    }
2969
2970
    pub fn input_sequence_byte_range(&self) -> Range<usize> {
2971
        let input_glyph_count = self.input_glyph_count();
2972
        let start = self.input_glyph_count_byte_range().end;
2973
        start
2974
            ..start
2975
                + (transforms::subtract(input_glyph_count, 1_usize))
2976
                    .saturating_mul(GlyphId16::RAW_BYTE_LEN)
2977
    }
2978
2979
    pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
2980
        let start = self.input_sequence_byte_range().end;
2981
        start..start + u16::RAW_BYTE_LEN
2982
    }
2983
2984
    pub fn lookahead_sequence_byte_range(&self) -> Range<usize> {
2985
        let lookahead_glyph_count = self.lookahead_glyph_count();
2986
        let start = self.lookahead_glyph_count_byte_range().end;
2987
        start..start + (lookahead_glyph_count as usize).saturating_mul(GlyphId16::RAW_BYTE_LEN)
2988
    }
2989
2990
    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
2991
        let start = self.lookahead_sequence_byte_range().end;
2992
        start..start + u16::RAW_BYTE_LEN
2993
    }
2994
2995
    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
2996
        let seq_lookup_count = self.seq_lookup_count();
2997
        let start = self.seq_lookup_count_byte_range().end;
2998
        start
2999
            ..start + (seq_lookup_count as usize).saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN)
3000
    }
3001
}
3002
3003
#[cfg(feature = "experimental_traverse")]
3004
impl<'a> SomeTable<'a> for ChainedSequenceRule<'a> {
3005
    fn type_name(&self) -> &str {
3006
        "ChainedSequenceRule"
3007
    }
3008
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3009
        match idx {
3010
            0usize => Some(Field::new(
3011
                "backtrack_glyph_count",
3012
                self.backtrack_glyph_count(),
3013
            )),
3014
            1usize => Some(Field::new("backtrack_sequence", self.backtrack_sequence())),
3015
            2usize => Some(Field::new("input_glyph_count", self.input_glyph_count())),
3016
            3usize => Some(Field::new("input_sequence", self.input_sequence())),
3017
            4usize => Some(Field::new(
3018
                "lookahead_glyph_count",
3019
                self.lookahead_glyph_count(),
3020
            )),
3021
            5usize => Some(Field::new("lookahead_sequence", self.lookahead_sequence())),
3022
            6usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
3023
            7usize => Some(Field::new(
3024
                "seq_lookup_records",
3025
                traversal::FieldType::array_of_records(
3026
                    stringify!(SequenceLookupRecord),
3027
                    self.seq_lookup_records(),
3028
                    self.offset_data(),
3029
                ),
3030
            )),
3031
            _ => None,
3032
        }
3033
    }
3034
}
3035
3036
#[cfg(feature = "experimental_traverse")]
3037
#[allow(clippy::needless_lifetimes)]
3038
impl<'a> std::fmt::Debug for ChainedSequenceRule<'a> {
3039
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3040
        (self as &dyn SomeTable<'a>).fmt(f)
3041
    }
3042
}
3043
3044
impl Format<u16> for ChainedSequenceContextFormat2<'_> {
3045
    const FORMAT: u16 = 2;
3046
}
3047
3048
impl<'a> MinByteRange<'a> for ChainedSequenceContextFormat2<'a> {
3049
    fn min_byte_range(&self) -> Range<usize> {
3050
        0..self.chained_class_seq_rule_set_offsets_byte_range().end
3051
    }
3052
    fn min_table_bytes(&self) -> &'a [u8] {
3053
        let range = self.min_byte_range();
3054
        self.data.as_bytes().get(range).unwrap_or_default()
3055
    }
3056
}
3057
3058
impl<'a> FontRead<'a> for ChainedSequenceContextFormat2<'a> {
3059
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
3060
        #[allow(clippy::absurd_extreme_comparisons)]
3061
        if data.len() < Self::MIN_SIZE {
3062
            return Err(ReadError::OutOfBounds);
3063
        }
3064
        Ok(Self { data })
3065
    }
3066
}
3067
3068
/// [Chained Sequence Context Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-2-class-based-glyph-contexts)
3069
#[derive(Clone)]
3070
pub struct ChainedSequenceContextFormat2<'a> {
3071
    data: FontData<'a>,
3072
}
3073
3074
#[allow(clippy::needless_lifetimes)]
3075
impl<'a> ChainedSequenceContextFormat2<'a> {
3076
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
3077
        + Offset16::RAW_BYTE_LEN
3078
        + Offset16::RAW_BYTE_LEN
3079
        + Offset16::RAW_BYTE_LEN
3080
        + Offset16::RAW_BYTE_LEN
3081
        + u16::RAW_BYTE_LEN);
3082
    basic_table_impls!(impl_the_methods);
3083
3084
    /// Format identifier: format = 2
3085
    pub fn format(&self) -> u16 {
3086
        let range = self.format_byte_range();
3087
        self.data.read_at(range.start).ok().unwrap()
3088
    }
3089
3090
    /// Offset to Coverage table, from beginning of
3091
    /// ChainedSequenceContextFormat2 table
3092
    pub fn coverage_offset(&self) -> Offset16 {
3093
        let range = self.coverage_offset_byte_range();
3094
        self.data.read_at(range.start).ok().unwrap()
3095
    }
3096
3097
    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
3098
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
3099
        let data = self.data;
3100
        self.coverage_offset().resolve(data)
3101
    }
3102
3103
    /// Offset to ClassDef table containing backtrack sequence context,
3104
    /// from beginning of ChainedSequenceContextFormat2 table
3105
    pub fn backtrack_class_def_offset(&self) -> Offset16 {
3106
        let range = self.backtrack_class_def_offset_byte_range();
3107
        self.data.read_at(range.start).ok().unwrap()
3108
    }
3109
3110
    /// Attempt to resolve [`backtrack_class_def_offset`][Self::backtrack_class_def_offset].
3111
    pub fn backtrack_class_def(&self) -> Result<ClassDef<'a>, ReadError> {
3112
        let data = self.data;
3113
        self.backtrack_class_def_offset().resolve(data)
3114
    }
3115
3116
    /// Offset to ClassDef table containing input sequence context,
3117
    /// from beginning of ChainedSequenceContextFormat2 table
3118
    pub fn input_class_def_offset(&self) -> Offset16 {
3119
        let range = self.input_class_def_offset_byte_range();
3120
        self.data.read_at(range.start).ok().unwrap()
3121
    }
3122
3123
    /// Attempt to resolve [`input_class_def_offset`][Self::input_class_def_offset].
3124
    pub fn input_class_def(&self) -> Result<ClassDef<'a>, ReadError> {
3125
        let data = self.data;
3126
        self.input_class_def_offset().resolve(data)
3127
    }
3128
3129
    /// Offset to ClassDef table containing lookahead sequence context,
3130
    /// from beginning of ChainedSequenceContextFormat2 table
3131
    pub fn lookahead_class_def_offset(&self) -> Offset16 {
3132
        let range = self.lookahead_class_def_offset_byte_range();
3133
        self.data.read_at(range.start).ok().unwrap()
3134
    }
3135
3136
    /// Attempt to resolve [`lookahead_class_def_offset`][Self::lookahead_class_def_offset].
3137
    pub fn lookahead_class_def(&self) -> Result<ClassDef<'a>, ReadError> {
3138
        let data = self.data;
3139
        self.lookahead_class_def_offset().resolve(data)
3140
    }
3141
3142
    /// Number of ChainedClassSequenceRuleSet tables
3143
    pub fn chained_class_seq_rule_set_count(&self) -> u16 {
3144
        let range = self.chained_class_seq_rule_set_count_byte_range();
3145
        self.data.read_at(range.start).ok().unwrap()
3146
    }
3147
3148
    /// Array of offsets to ChainedClassSequenceRuleSet tables, from
3149
    /// beginning of ChainedSequenceContextFormat2 table (may be NULL)
3150
    pub fn chained_class_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
3151
        let range = self.chained_class_seq_rule_set_offsets_byte_range();
3152
        self.data.read_array(range).ok().unwrap_or_default()
3153
    }
3154
3155
    /// A dynamically resolving wrapper for [`chained_class_seq_rule_set_offsets`][Self::chained_class_seq_rule_set_offsets].
3156
    pub fn chained_class_seq_rule_sets(
3157
        &self,
3158
    ) -> ArrayOfNullableOffsets<'a, ChainedClassSequenceRuleSet<'a>, Offset16> {
3159
        let data = self.data;
3160
        let offsets = self.chained_class_seq_rule_set_offsets();
3161
        ArrayOfNullableOffsets::new(offsets, data, ())
3162
    }
3163
3164
    pub fn format_byte_range(&self) -> Range<usize> {
3165
        let start = 0;
3166
        start..start + u16::RAW_BYTE_LEN
3167
    }
3168
3169
    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
3170
        let start = self.format_byte_range().end;
3171
        start..start + Offset16::RAW_BYTE_LEN
3172
    }
3173
3174
    pub fn backtrack_class_def_offset_byte_range(&self) -> Range<usize> {
3175
        let start = self.coverage_offset_byte_range().end;
3176
        start..start + Offset16::RAW_BYTE_LEN
3177
    }
3178
3179
    pub fn input_class_def_offset_byte_range(&self) -> Range<usize> {
3180
        let start = self.backtrack_class_def_offset_byte_range().end;
3181
        start..start + Offset16::RAW_BYTE_LEN
3182
    }
3183
3184
    pub fn lookahead_class_def_offset_byte_range(&self) -> Range<usize> {
3185
        let start = self.input_class_def_offset_byte_range().end;
3186
        start..start + Offset16::RAW_BYTE_LEN
3187
    }
3188
3189
    pub fn chained_class_seq_rule_set_count_byte_range(&self) -> Range<usize> {
3190
        let start = self.lookahead_class_def_offset_byte_range().end;
3191
        start..start + u16::RAW_BYTE_LEN
3192
    }
3193
3194
    pub fn chained_class_seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
3195
        let chained_class_seq_rule_set_count = self.chained_class_seq_rule_set_count();
3196
        let start = self.chained_class_seq_rule_set_count_byte_range().end;
3197
        start
3198
            ..start
3199
                + (chained_class_seq_rule_set_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
3200
    }
3201
}
3202
3203
#[cfg(feature = "experimental_traverse")]
3204
impl<'a> SomeTable<'a> for ChainedSequenceContextFormat2<'a> {
3205
    fn type_name(&self) -> &str {
3206
        "ChainedSequenceContextFormat2"
3207
    }
3208
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3209
        match idx {
3210
            0usize => Some(Field::new("format", self.format())),
3211
            1usize => Some(Field::new(
3212
                "coverage_offset",
3213
                FieldType::offset(self.coverage_offset(), self.coverage()),
3214
            )),
3215
            2usize => Some(Field::new(
3216
                "backtrack_class_def_offset",
3217
                FieldType::offset(
3218
                    self.backtrack_class_def_offset(),
3219
                    self.backtrack_class_def(),
3220
                ),
3221
            )),
3222
            3usize => Some(Field::new(
3223
                "input_class_def_offset",
3224
                FieldType::offset(self.input_class_def_offset(), self.input_class_def()),
3225
            )),
3226
            4usize => Some(Field::new(
3227
                "lookahead_class_def_offset",
3228
                FieldType::offset(
3229
                    self.lookahead_class_def_offset(),
3230
                    self.lookahead_class_def(),
3231
                ),
3232
            )),
3233
            5usize => Some(Field::new(
3234
                "chained_class_seq_rule_set_count",
3235
                self.chained_class_seq_rule_set_count(),
3236
            )),
3237
            6usize => Some({
3238
                let data = self.data;
3239
                Field::new(
3240
                    "chained_class_seq_rule_set_offsets",
3241
                    FieldType::array_of_offsets(
3242
                        better_type_name::<ChainedClassSequenceRuleSet>(),
3243
                        self.chained_class_seq_rule_set_offsets(),
3244
                        move |off| {
3245
                            let target = off.get().resolve::<ChainedClassSequenceRuleSet>(data);
3246
                            FieldType::offset(off.get(), target)
3247
                        },
3248
                    ),
3249
                )
3250
            }),
3251
            _ => None,
3252
        }
3253
    }
3254
}
3255
3256
#[cfg(feature = "experimental_traverse")]
3257
#[allow(clippy::needless_lifetimes)]
3258
impl<'a> std::fmt::Debug for ChainedSequenceContextFormat2<'a> {
3259
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3260
        (self as &dyn SomeTable<'a>).fmt(f)
3261
    }
3262
}
3263
3264
impl<'a> MinByteRange<'a> for ChainedClassSequenceRuleSet<'a> {
3265
    fn min_byte_range(&self) -> Range<usize> {
3266
        0..self.chained_class_seq_rule_offsets_byte_range().end
3267
    }
3268
    fn min_table_bytes(&self) -> &'a [u8] {
3269
        let range = self.min_byte_range();
3270
        self.data.as_bytes().get(range).unwrap_or_default()
3271
    }
3272
}
3273
3274
impl<'a> FontRead<'a> for ChainedClassSequenceRuleSet<'a> {
3275
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
3276
        #[allow(clippy::absurd_extreme_comparisons)]
3277
        if data.len() < Self::MIN_SIZE {
3278
            return Err(ReadError::OutOfBounds);
3279
        }
3280
        Ok(Self { data })
3281
    }
3282
}
3283
3284
/// Part of [ChainedSequenceContextFormat2]
3285
#[derive(Clone)]
3286
pub struct ChainedClassSequenceRuleSet<'a> {
3287
    data: FontData<'a>,
3288
}
3289
3290
#[allow(clippy::needless_lifetimes)]
3291
impl<'a> ChainedClassSequenceRuleSet<'a> {
3292
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
3293
    basic_table_impls!(impl_the_methods);
3294
3295
    /// Number of ChainedClassSequenceRule tables
3296
    pub fn chained_class_seq_rule_count(&self) -> u16 {
3297
        let range = self.chained_class_seq_rule_count_byte_range();
3298
        self.data.read_at(range.start).ok().unwrap()
3299
    }
3300
3301
    /// Array of offsets to ChainedClassSequenceRule tables, from
3302
    /// beginning of ChainedClassSequenceRuleSet
3303
    pub fn chained_class_seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
3304
        let range = self.chained_class_seq_rule_offsets_byte_range();
3305
        self.data.read_array(range).ok().unwrap_or_default()
3306
    }
3307
3308
    /// A dynamically resolving wrapper for [`chained_class_seq_rule_offsets`][Self::chained_class_seq_rule_offsets].
3309
    pub fn chained_class_seq_rules(
3310
        &self,
3311
    ) -> ArrayOfOffsets<'a, ChainedClassSequenceRule<'a>, Offset16> {
3312
        let data = self.data;
3313
        let offsets = self.chained_class_seq_rule_offsets();
3314
        ArrayOfOffsets::new(offsets, data, ())
3315
    }
3316
3317
    pub fn chained_class_seq_rule_count_byte_range(&self) -> Range<usize> {
3318
        let start = 0;
3319
        start..start + u16::RAW_BYTE_LEN
3320
    }
3321
3322
    pub fn chained_class_seq_rule_offsets_byte_range(&self) -> Range<usize> {
3323
        let chained_class_seq_rule_count = self.chained_class_seq_rule_count();
3324
        let start = self.chained_class_seq_rule_count_byte_range().end;
3325
        start
3326
            ..start + (chained_class_seq_rule_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
3327
    }
3328
}
3329
3330
#[cfg(feature = "experimental_traverse")]
3331
impl<'a> SomeTable<'a> for ChainedClassSequenceRuleSet<'a> {
3332
    fn type_name(&self) -> &str {
3333
        "ChainedClassSequenceRuleSet"
3334
    }
3335
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3336
        match idx {
3337
            0usize => Some(Field::new(
3338
                "chained_class_seq_rule_count",
3339
                self.chained_class_seq_rule_count(),
3340
            )),
3341
            1usize => Some({
3342
                let data = self.data;
3343
                Field::new(
3344
                    "chained_class_seq_rule_offsets",
3345
                    FieldType::array_of_offsets(
3346
                        better_type_name::<ChainedClassSequenceRule>(),
3347
                        self.chained_class_seq_rule_offsets(),
3348
                        move |off| {
3349
                            let target = off.get().resolve::<ChainedClassSequenceRule>(data);
3350
                            FieldType::offset(off.get(), target)
3351
                        },
3352
                    ),
3353
                )
3354
            }),
3355
            _ => None,
3356
        }
3357
    }
3358
}
3359
3360
#[cfg(feature = "experimental_traverse")]
3361
#[allow(clippy::needless_lifetimes)]
3362
impl<'a> std::fmt::Debug for ChainedClassSequenceRuleSet<'a> {
3363
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3364
        (self as &dyn SomeTable<'a>).fmt(f)
3365
    }
3366
}
3367
3368
impl<'a> MinByteRange<'a> for ChainedClassSequenceRule<'a> {
3369
    fn min_byte_range(&self) -> Range<usize> {
3370
        0..self.seq_lookup_records_byte_range().end
3371
    }
3372
    fn min_table_bytes(&self) -> &'a [u8] {
3373
        let range = self.min_byte_range();
3374
        self.data.as_bytes().get(range).unwrap_or_default()
3375
    }
3376
}
3377
3378
impl<'a> FontRead<'a> for ChainedClassSequenceRule<'a> {
3379
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
3380
        #[allow(clippy::absurd_extreme_comparisons)]
3381
        if data.len() < Self::MIN_SIZE {
3382
            return Err(ReadError::OutOfBounds);
3383
        }
3384
        Ok(Self { data })
3385
    }
3386
}
3387
3388
/// Part of [ChainedSequenceContextFormat2]
3389
#[derive(Clone)]
3390
pub struct ChainedClassSequenceRule<'a> {
3391
    data: FontData<'a>,
3392
}
3393
3394
#[allow(clippy::needless_lifetimes)]
3395
impl<'a> ChainedClassSequenceRule<'a> {
3396
    pub const MIN_SIZE: usize =
3397
        (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
3398
    basic_table_impls!(impl_the_methods);
3399
3400
    /// Number of glyphs in the backtrack sequence
3401
    pub fn backtrack_glyph_count(&self) -> u16 {
3402
        let range = self.backtrack_glyph_count_byte_range();
3403
        self.data.read_at(range.start).ok().unwrap()
3404
    }
3405
3406
    /// Array of backtrack-sequence classes
3407
    pub fn backtrack_sequence(&self) -> &'a [BigEndian<u16>] {
3408
        let range = self.backtrack_sequence_byte_range();
3409
        self.data.read_array(range).ok().unwrap_or_default()
3410
    }
3411
3412
    /// Total number of glyphs in the input sequence
3413
    pub fn input_glyph_count(&self) -> u16 {
3414
        let range = self.input_glyph_count_byte_range();
3415
        self.data.read_at(range.start).ok().unwrap_or_default()
3416
    }
3417
3418
    /// Array of input sequence classes, beginning with the second
3419
    /// glyph position
3420
    pub fn input_sequence(&self) -> &'a [BigEndian<u16>] {
3421
        let range = self.input_sequence_byte_range();
3422
        self.data.read_array(range).ok().unwrap_or_default()
3423
    }
3424
3425
    /// Number of glyphs in the lookahead sequence
3426
    pub fn lookahead_glyph_count(&self) -> u16 {
3427
        let range = self.lookahead_glyph_count_byte_range();
3428
        self.data.read_at(range.start).ok().unwrap_or_default()
3429
    }
3430
3431
    /// Array of lookahead-sequence classes
3432
    pub fn lookahead_sequence(&self) -> &'a [BigEndian<u16>] {
3433
        let range = self.lookahead_sequence_byte_range();
3434
        self.data.read_array(range).ok().unwrap_or_default()
3435
    }
3436
3437
    /// Number of SequenceLookupRecords
3438
    pub fn seq_lookup_count(&self) -> u16 {
3439
        let range = self.seq_lookup_count_byte_range();
3440
        self.data.read_at(range.start).ok().unwrap_or_default()
3441
    }
3442
3443
    /// Array of SequenceLookupRecords
3444
    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
3445
        let range = self.seq_lookup_records_byte_range();
3446
        self.data.read_array(range).ok().unwrap_or_default()
3447
    }
3448
3449
    pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
3450
        let start = 0;
3451
        start..start + u16::RAW_BYTE_LEN
3452
    }
3453
3454
    pub fn backtrack_sequence_byte_range(&self) -> Range<usize> {
3455
        let backtrack_glyph_count = self.backtrack_glyph_count();
3456
        let start = self.backtrack_glyph_count_byte_range().end;
3457
        start..start + (backtrack_glyph_count as usize).saturating_mul(u16::RAW_BYTE_LEN)
3458
    }
3459
3460
    pub fn input_glyph_count_byte_range(&self) -> Range<usize> {
3461
        let start = self.backtrack_sequence_byte_range().end;
3462
        start..start + u16::RAW_BYTE_LEN
3463
    }
3464
3465
    pub fn input_sequence_byte_range(&self) -> Range<usize> {
3466
        let input_glyph_count = self.input_glyph_count();
3467
        let start = self.input_glyph_count_byte_range().end;
3468
        start
3469
            ..start
3470
                + (transforms::subtract(input_glyph_count, 1_usize))
3471
                    .saturating_mul(u16::RAW_BYTE_LEN)
3472
    }
3473
3474
    pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
3475
        let start = self.input_sequence_byte_range().end;
3476
        start..start + u16::RAW_BYTE_LEN
3477
    }
3478
3479
    pub fn lookahead_sequence_byte_range(&self) -> Range<usize> {
3480
        let lookahead_glyph_count = self.lookahead_glyph_count();
3481
        let start = self.lookahead_glyph_count_byte_range().end;
3482
        start..start + (lookahead_glyph_count as usize).saturating_mul(u16::RAW_BYTE_LEN)
3483
    }
3484
3485
    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
3486
        let start = self.lookahead_sequence_byte_range().end;
3487
        start..start + u16::RAW_BYTE_LEN
3488
    }
3489
3490
    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
3491
        let seq_lookup_count = self.seq_lookup_count();
3492
        let start = self.seq_lookup_count_byte_range().end;
3493
        start
3494
            ..start + (seq_lookup_count as usize).saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN)
3495
    }
3496
}
3497
3498
#[cfg(feature = "experimental_traverse")]
3499
impl<'a> SomeTable<'a> for ChainedClassSequenceRule<'a> {
3500
    fn type_name(&self) -> &str {
3501
        "ChainedClassSequenceRule"
3502
    }
3503
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3504
        match idx {
3505
            0usize => Some(Field::new(
3506
                "backtrack_glyph_count",
3507
                self.backtrack_glyph_count(),
3508
            )),
3509
            1usize => Some(Field::new("backtrack_sequence", self.backtrack_sequence())),
3510
            2usize => Some(Field::new("input_glyph_count", self.input_glyph_count())),
3511
            3usize => Some(Field::new("input_sequence", self.input_sequence())),
3512
            4usize => Some(Field::new(
3513
                "lookahead_glyph_count",
3514
                self.lookahead_glyph_count(),
3515
            )),
3516
            5usize => Some(Field::new("lookahead_sequence", self.lookahead_sequence())),
3517
            6usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
3518
            7usize => Some(Field::new(
3519
                "seq_lookup_records",
3520
                traversal::FieldType::array_of_records(
3521
                    stringify!(SequenceLookupRecord),
3522
                    self.seq_lookup_records(),
3523
                    self.offset_data(),
3524
                ),
3525
            )),
3526
            _ => None,
3527
        }
3528
    }
3529
}
3530
3531
#[cfg(feature = "experimental_traverse")]
3532
#[allow(clippy::needless_lifetimes)]
3533
impl<'a> std::fmt::Debug for ChainedClassSequenceRule<'a> {
3534
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3535
        (self as &dyn SomeTable<'a>).fmt(f)
3536
    }
3537
}
3538
3539
impl Format<u16> for ChainedSequenceContextFormat3<'_> {
3540
    const FORMAT: u16 = 3;
3541
}
3542
3543
impl<'a> MinByteRange<'a> for ChainedSequenceContextFormat3<'a> {
3544
    fn min_byte_range(&self) -> Range<usize> {
3545
        0..self.seq_lookup_records_byte_range().end
3546
    }
3547
    fn min_table_bytes(&self) -> &'a [u8] {
3548
        let range = self.min_byte_range();
3549
        self.data.as_bytes().get(range).unwrap_or_default()
3550
    }
3551
}
3552
3553
impl<'a> FontRead<'a> for ChainedSequenceContextFormat3<'a> {
3554
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
3555
        #[allow(clippy::absurd_extreme_comparisons)]
3556
        if data.len() < Self::MIN_SIZE {
3557
            return Err(ReadError::OutOfBounds);
3558
        }
3559
        Ok(Self { data })
3560
    }
3561
}
3562
3563
/// [Chained Sequence Context Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-3-coverage-based-glyph-contexts)
3564
#[derive(Clone)]
3565
pub struct ChainedSequenceContextFormat3<'a> {
3566
    data: FontData<'a>,
3567
}
3568
3569
#[allow(clippy::needless_lifetimes)]
3570
impl<'a> ChainedSequenceContextFormat3<'a> {
3571
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
3572
        + u16::RAW_BYTE_LEN
3573
        + u16::RAW_BYTE_LEN
3574
        + u16::RAW_BYTE_LEN
3575
        + u16::RAW_BYTE_LEN);
3576
    basic_table_impls!(impl_the_methods);
3577
3578
    /// Format identifier: format = 3
3579
    pub fn format(&self) -> u16 {
3580
        let range = self.format_byte_range();
3581
        self.data.read_at(range.start).ok().unwrap()
3582
    }
3583
3584
    /// Number of glyphs in the backtrack sequence
3585
    pub fn backtrack_glyph_count(&self) -> u16 {
3586
        let range = self.backtrack_glyph_count_byte_range();
3587
        self.data.read_at(range.start).ok().unwrap()
3588
    }
3589
3590
    /// Array of offsets to coverage tables for the backtrack sequence
3591
    pub fn backtrack_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
3592
        let range = self.backtrack_coverage_offsets_byte_range();
3593
        self.data.read_array(range).ok().unwrap_or_default()
3594
    }
3595
3596
    /// A dynamically resolving wrapper for [`backtrack_coverage_offsets`][Self::backtrack_coverage_offsets].
3597
    pub fn backtrack_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
3598
        let data = self.data;
3599
        let offsets = self.backtrack_coverage_offsets();
3600
        ArrayOfOffsets::new(offsets, data, ())
3601
    }
3602
3603
    /// Number of glyphs in the input sequence
3604
    pub fn input_glyph_count(&self) -> u16 {
3605
        let range = self.input_glyph_count_byte_range();
3606
        self.data.read_at(range.start).ok().unwrap_or_default()
3607
    }
3608
3609
    /// Array of offsets to coverage tables for the input sequence
3610
    pub fn input_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
3611
        let range = self.input_coverage_offsets_byte_range();
3612
        self.data.read_array(range).ok().unwrap_or_default()
3613
    }
3614
3615
    /// A dynamically resolving wrapper for [`input_coverage_offsets`][Self::input_coverage_offsets].
3616
    pub fn input_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
3617
        let data = self.data;
3618
        let offsets = self.input_coverage_offsets();
3619
        ArrayOfOffsets::new(offsets, data, ())
3620
    }
3621
3622
    /// Number of glyphs in the lookahead sequence
3623
    pub fn lookahead_glyph_count(&self) -> u16 {
3624
        let range = self.lookahead_glyph_count_byte_range();
3625
        self.data.read_at(range.start).ok().unwrap_or_default()
3626
    }
3627
3628
    /// Array of offsets to coverage tables for the lookahead sequence
3629
    pub fn lookahead_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
3630
        let range = self.lookahead_coverage_offsets_byte_range();
3631
        self.data.read_array(range).ok().unwrap_or_default()
3632
    }
3633
3634
    /// A dynamically resolving wrapper for [`lookahead_coverage_offsets`][Self::lookahead_coverage_offsets].
3635
    pub fn lookahead_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
3636
        let data = self.data;
3637
        let offsets = self.lookahead_coverage_offsets();
3638
        ArrayOfOffsets::new(offsets, data, ())
3639
    }
3640
3641
    /// Number of SequenceLookupRecords
3642
    pub fn seq_lookup_count(&self) -> u16 {
3643
        let range = self.seq_lookup_count_byte_range();
3644
        self.data.read_at(range.start).ok().unwrap_or_default()
3645
    }
3646
3647
    /// Array of SequenceLookupRecords
3648
    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
3649
        let range = self.seq_lookup_records_byte_range();
3650
        self.data.read_array(range).ok().unwrap_or_default()
3651
    }
3652
3653
    pub fn format_byte_range(&self) -> Range<usize> {
3654
        let start = 0;
3655
        start..start + u16::RAW_BYTE_LEN
3656
    }
3657
3658
    pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
3659
        let start = self.format_byte_range().end;
3660
        start..start + u16::RAW_BYTE_LEN
3661
    }
3662
3663
    pub fn backtrack_coverage_offsets_byte_range(&self) -> Range<usize> {
3664
        let backtrack_glyph_count = self.backtrack_glyph_count();
3665
        let start = self.backtrack_glyph_count_byte_range().end;
3666
        start..start + (backtrack_glyph_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
3667
    }
3668
3669
    pub fn input_glyph_count_byte_range(&self) -> Range<usize> {
3670
        let start = self.backtrack_coverage_offsets_byte_range().end;
3671
        start..start + u16::RAW_BYTE_LEN
3672
    }
3673
3674
    pub fn input_coverage_offsets_byte_range(&self) -> Range<usize> {
3675
        let input_glyph_count = self.input_glyph_count();
3676
        let start = self.input_glyph_count_byte_range().end;
3677
        start..start + (input_glyph_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
3678
    }
3679
3680
    pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
3681
        let start = self.input_coverage_offsets_byte_range().end;
3682
        start..start + u16::RAW_BYTE_LEN
3683
    }
3684
3685
    pub fn lookahead_coverage_offsets_byte_range(&self) -> Range<usize> {
3686
        let lookahead_glyph_count = self.lookahead_glyph_count();
3687
        let start = self.lookahead_glyph_count_byte_range().end;
3688
        start..start + (lookahead_glyph_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
3689
    }
3690
3691
    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
3692
        let start = self.lookahead_coverage_offsets_byte_range().end;
3693
        start..start + u16::RAW_BYTE_LEN
3694
    }
3695
3696
    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
3697
        let seq_lookup_count = self.seq_lookup_count();
3698
        let start = self.seq_lookup_count_byte_range().end;
3699
        start
3700
            ..start + (seq_lookup_count as usize).saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN)
3701
    }
3702
}
3703
3704
#[cfg(feature = "experimental_traverse")]
3705
impl<'a> SomeTable<'a> for ChainedSequenceContextFormat3<'a> {
3706
    fn type_name(&self) -> &str {
3707
        "ChainedSequenceContextFormat3"
3708
    }
3709
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3710
        match idx {
3711
            0usize => Some(Field::new("format", self.format())),
3712
            1usize => Some(Field::new(
3713
                "backtrack_glyph_count",
3714
                self.backtrack_glyph_count(),
3715
            )),
3716
            2usize => Some({
3717
                let data = self.data;
3718
                Field::new(
3719
                    "backtrack_coverage_offsets",
3720
                    FieldType::array_of_offsets(
3721
                        better_type_name::<CoverageTable>(),
3722
                        self.backtrack_coverage_offsets(),
3723
                        move |off| {
3724
                            let target = off.get().resolve::<CoverageTable>(data);
3725
                            FieldType::offset(off.get(), target)
3726
                        },
3727
                    ),
3728
                )
3729
            }),
3730
            3usize => Some(Field::new("input_glyph_count", self.input_glyph_count())),
3731
            4usize => Some({
3732
                let data = self.data;
3733
                Field::new(
3734
                    "input_coverage_offsets",
3735
                    FieldType::array_of_offsets(
3736
                        better_type_name::<CoverageTable>(),
3737
                        self.input_coverage_offsets(),
3738
                        move |off| {
3739
                            let target = off.get().resolve::<CoverageTable>(data);
3740
                            FieldType::offset(off.get(), target)
3741
                        },
3742
                    ),
3743
                )
3744
            }),
3745
            5usize => Some(Field::new(
3746
                "lookahead_glyph_count",
3747
                self.lookahead_glyph_count(),
3748
            )),
3749
            6usize => Some({
3750
                let data = self.data;
3751
                Field::new(
3752
                    "lookahead_coverage_offsets",
3753
                    FieldType::array_of_offsets(
3754
                        better_type_name::<CoverageTable>(),
3755
                        self.lookahead_coverage_offsets(),
3756
                        move |off| {
3757
                            let target = off.get().resolve::<CoverageTable>(data);
3758
                            FieldType::offset(off.get(), target)
3759
                        },
3760
                    ),
3761
                )
3762
            }),
3763
            7usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
3764
            8usize => Some(Field::new(
3765
                "seq_lookup_records",
3766
                traversal::FieldType::array_of_records(
3767
                    stringify!(SequenceLookupRecord),
3768
                    self.seq_lookup_records(),
3769
                    self.offset_data(),
3770
                ),
3771
            )),
3772
            _ => None,
3773
        }
3774
    }
3775
}
3776
3777
#[cfg(feature = "experimental_traverse")]
3778
#[allow(clippy::needless_lifetimes)]
3779
impl<'a> std::fmt::Debug for ChainedSequenceContextFormat3<'a> {
3780
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3781
        (self as &dyn SomeTable<'a>).fmt(f)
3782
    }
3783
}
3784
3785
#[derive(Clone)]
3786
pub enum ChainedSequenceContext<'a> {
3787
    Format1(ChainedSequenceContextFormat1<'a>),
3788
    Format2(ChainedSequenceContextFormat2<'a>),
3789
    Format3(ChainedSequenceContextFormat3<'a>),
3790
}
3791
3792
impl<'a> ChainedSequenceContext<'a> {
3793
    ///Return the `FontData` used to resolve offsets for this table.
3794
    pub fn offset_data(&self) -> FontData<'a> {
3795
        match self {
3796
            Self::Format1(item) => item.offset_data(),
3797
            Self::Format2(item) => item.offset_data(),
3798
            Self::Format3(item) => item.offset_data(),
3799
        }
3800
    }
3801
3802
    /// Format identifier: format = 1
3803
    pub fn format(&self) -> u16 {
3804
        match self {
3805
            Self::Format1(item) => item.format(),
3806
            Self::Format2(item) => item.format(),
3807
            Self::Format3(item) => item.format(),
3808
        }
3809
    }
3810
}
3811
3812
impl<'a> FontRead<'a> for ChainedSequenceContext<'a> {
3813
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
3814
        let format: u16 = data.read_at(0usize)?;
3815
        match format {
3816
            ChainedSequenceContextFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
3817
            ChainedSequenceContextFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
3818
            ChainedSequenceContextFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
3819
            other => Err(ReadError::InvalidFormat(other.into())),
3820
        }
3821
    }
3822
}
3823
3824
impl<'a> MinByteRange<'a> for ChainedSequenceContext<'a> {
3825
    fn min_byte_range(&self) -> Range<usize> {
3826
        match self {
3827
            Self::Format1(item) => item.min_byte_range(),
3828
            Self::Format2(item) => item.min_byte_range(),
3829
            Self::Format3(item) => item.min_byte_range(),
3830
        }
3831
    }
3832
    fn min_table_bytes(&self) -> &'a [u8] {
3833
        match self {
3834
            Self::Format1(item) => item.min_table_bytes(),
3835
            Self::Format2(item) => item.min_table_bytes(),
3836
            Self::Format3(item) => item.min_table_bytes(),
3837
        }
3838
    }
3839
}
3840
3841
#[cfg(feature = "experimental_traverse")]
3842
impl<'a> ChainedSequenceContext<'a> {
3843
    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
3844
        match self {
3845
            Self::Format1(table) => table,
3846
            Self::Format2(table) => table,
3847
            Self::Format3(table) => table,
3848
        }
3849
    }
3850
}
3851
3852
#[cfg(feature = "experimental_traverse")]
3853
impl std::fmt::Debug for ChainedSequenceContext<'_> {
3854
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3855
        self.dyn_inner().fmt(f)
3856
    }
3857
}
3858
3859
#[cfg(feature = "experimental_traverse")]
3860
impl<'a> SomeTable<'a> for ChainedSequenceContext<'a> {
3861
    fn type_name(&self) -> &str {
3862
        self.dyn_inner().type_name()
3863
    }
3864
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3865
        self.dyn_inner().get_field(idx)
3866
    }
3867
}
3868
3869
/// [Device](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#device-and-variationindex-tables)
3870
/// delta formats
3871
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
3872
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3873
#[repr(u16)]
3874
#[allow(clippy::manual_non_exhaustive)]
3875
pub enum DeltaFormat {
3876
    /// Signed 2-bit value, 8 values per uint16
3877
    #[default]
3878
    Local2BitDeltas = 0x0001,
3879
    /// Signed 4-bit value, 4 values per uint16
3880
    Local4BitDeltas = 0x0002,
3881
    /// Signed 8-bit value, 2 values per uint16
3882
    Local8BitDeltas = 0x0003,
3883
    /// VariationIndex table, contains a delta-set index pair.
3884
    VariationIndex = 0x8000,
3885
    #[doc(hidden)]
3886
    /// If font data is malformed we will map unknown values to this variant
3887
    Unknown,
3888
}
3889
3890
impl DeltaFormat {
3891
    /// Create from a raw scalar.
3892
    ///
3893
    /// This will never fail; unknown values will be mapped to the `Unknown` variant
3894
    pub fn new(raw: u16) -> Self {
3895
        match raw {
3896
            0x0001 => Self::Local2BitDeltas,
3897
            0x0002 => Self::Local4BitDeltas,
3898
            0x0003 => Self::Local8BitDeltas,
3899
            0x8000 => Self::VariationIndex,
3900
            _ => Self::Unknown,
3901
        }
3902
    }
3903
}
3904
3905
impl font_types::Scalar for DeltaFormat {
3906
    type Raw = <u16 as font_types::Scalar>::Raw;
3907
    fn to_raw(self) -> Self::Raw {
3908
        (self as u16).to_raw()
3909
    }
3910
    fn from_raw(raw: Self::Raw) -> Self {
3911
        let t = <u16>::from_raw(raw);
3912
        Self::new(t)
3913
    }
3914
}
3915
3916
#[cfg(feature = "experimental_traverse")]
3917
impl<'a> From<DeltaFormat> for FieldType<'a> {
3918
    fn from(src: DeltaFormat) -> FieldType<'a> {
3919
        (src as u16).into()
3920
    }
3921
}
3922
3923
impl<'a> MinByteRange<'a> for Device<'a> {
3924
    fn min_byte_range(&self) -> Range<usize> {
3925
        0..self.delta_value_byte_range().end
3926
    }
3927
    fn min_table_bytes(&self) -> &'a [u8] {
3928
        let range = self.min_byte_range();
3929
        self.data.as_bytes().get(range).unwrap_or_default()
3930
    }
3931
}
3932
3933
impl<'a> FontRead<'a> for Device<'a> {
3934
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
3935
        #[allow(clippy::absurd_extreme_comparisons)]
3936
        if data.len() < Self::MIN_SIZE {
3937
            return Err(ReadError::OutOfBounds);
3938
        }
3939
        Ok(Self { data })
3940
    }
3941
}
3942
3943
/// [Device Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#device-and-variationindex-tables)
3944
#[derive(Clone)]
3945
pub struct Device<'a> {
3946
    data: FontData<'a>,
3947
}
3948
3949
#[allow(clippy::needless_lifetimes)]
3950
impl<'a> Device<'a> {
3951
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + DeltaFormat::RAW_BYTE_LEN);
3952
    basic_table_impls!(impl_the_methods);
3953
3954
    /// Smallest size to correct, in ppem
3955
    pub fn start_size(&self) -> u16 {
3956
        let range = self.start_size_byte_range();
3957
        self.data.read_at(range.start).ok().unwrap()
3958
    }
3959
3960
    /// Largest size to correct, in ppem
3961
    pub fn end_size(&self) -> u16 {
3962
        let range = self.end_size_byte_range();
3963
        self.data.read_at(range.start).ok().unwrap()
3964
    }
3965
3966
    /// Format of deltaValue array data: 0x0001, 0x0002, or 0x0003
3967
    pub fn delta_format(&self) -> DeltaFormat {
3968
        let range = self.delta_format_byte_range();
3969
        self.data.read_at(range.start).ok().unwrap()
3970
    }
3971
3972
    /// Array of compressed data
3973
    pub fn delta_value(&self) -> &'a [BigEndian<u16>] {
3974
        let range = self.delta_value_byte_range();
3975
        self.data.read_array(range).ok().unwrap_or_default()
3976
    }
3977
3978
    pub fn start_size_byte_range(&self) -> Range<usize> {
3979
        let start = 0;
3980
        start..start + u16::RAW_BYTE_LEN
3981
    }
3982
3983
    pub fn end_size_byte_range(&self) -> Range<usize> {
3984
        let start = self.start_size_byte_range().end;
3985
        start..start + u16::RAW_BYTE_LEN
3986
    }
3987
3988
    pub fn delta_format_byte_range(&self) -> Range<usize> {
3989
        let start = self.end_size_byte_range().end;
3990
        start..start + DeltaFormat::RAW_BYTE_LEN
3991
    }
3992
3993
    pub fn delta_value_byte_range(&self) -> Range<usize> {
3994
        let delta_format = self.delta_format();
3995
        let start_size = self.start_size();
3996
        let end_size = self.end_size();
3997
        let start = self.delta_format_byte_range().end;
3998
        start
3999
            ..start
4000
                + (DeltaFormat::value_count(delta_format, start_size, end_size))
4001
                    .saturating_mul(u16::RAW_BYTE_LEN)
4002
    }
4003
}
4004
4005
#[cfg(feature = "experimental_traverse")]
4006
impl<'a> SomeTable<'a> for Device<'a> {
4007
    fn type_name(&self) -> &str {
4008
        "Device"
4009
    }
4010
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4011
        match idx {
4012
            0usize => Some(Field::new("start_size", self.start_size())),
4013
            1usize => Some(Field::new("end_size", self.end_size())),
4014
            2usize => Some(Field::new("delta_format", self.delta_format())),
4015
            3usize => Some(Field::new("delta_value", self.delta_value())),
4016
            _ => None,
4017
        }
4018
    }
4019
}
4020
4021
#[cfg(feature = "experimental_traverse")]
4022
#[allow(clippy::needless_lifetimes)]
4023
impl<'a> std::fmt::Debug for Device<'a> {
4024
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4025
        (self as &dyn SomeTable<'a>).fmt(f)
4026
    }
4027
}
4028
4029
impl<'a> MinByteRange<'a> for VariationIndex<'a> {
4030
    fn min_byte_range(&self) -> Range<usize> {
4031
        0..self.delta_format_byte_range().end
4032
    }
4033
    fn min_table_bytes(&self) -> &'a [u8] {
4034
        let range = self.min_byte_range();
4035
        self.data.as_bytes().get(range).unwrap_or_default()
4036
    }
4037
}
4038
4039
impl<'a> FontRead<'a> for VariationIndex<'a> {
4040
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4041
        #[allow(clippy::absurd_extreme_comparisons)]
4042
        if data.len() < Self::MIN_SIZE {
4043
            return Err(ReadError::OutOfBounds);
4044
        }
4045
        Ok(Self { data })
4046
    }
4047
}
4048
4049
/// Variation index table
4050
#[derive(Clone)]
4051
pub struct VariationIndex<'a> {
4052
    data: FontData<'a>,
4053
}
4054
4055
#[allow(clippy::needless_lifetimes)]
4056
impl<'a> VariationIndex<'a> {
4057
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + DeltaFormat::RAW_BYTE_LEN);
4058
    basic_table_impls!(impl_the_methods);
4059
4060
    /// A delta-set outer index — used to select an item variation
4061
    /// data subtable within the item variation store.
4062
    pub fn delta_set_outer_index(&self) -> u16 {
4063
        let range = self.delta_set_outer_index_byte_range();
4064
        self.data.read_at(range.start).ok().unwrap()
4065
    }
4066
4067
    /// A delta-set inner index — used to select a delta-set row
4068
    /// within an item variation data subtable.
4069
    pub fn delta_set_inner_index(&self) -> u16 {
4070
        let range = self.delta_set_inner_index_byte_range();
4071
        self.data.read_at(range.start).ok().unwrap()
4072
    }
4073
4074
    /// Format, = 0x8000
4075
    pub fn delta_format(&self) -> DeltaFormat {
4076
        let range = self.delta_format_byte_range();
4077
        self.data.read_at(range.start).ok().unwrap()
4078
    }
4079
4080
    pub fn delta_set_outer_index_byte_range(&self) -> Range<usize> {
4081
        let start = 0;
4082
        start..start + u16::RAW_BYTE_LEN
4083
    }
4084
4085
    pub fn delta_set_inner_index_byte_range(&self) -> Range<usize> {
4086
        let start = self.delta_set_outer_index_byte_range().end;
4087
        start..start + u16::RAW_BYTE_LEN
4088
    }
4089
4090
    pub fn delta_format_byte_range(&self) -> Range<usize> {
4091
        let start = self.delta_set_inner_index_byte_range().end;
4092
        start..start + DeltaFormat::RAW_BYTE_LEN
4093
    }
4094
}
4095
4096
#[cfg(feature = "experimental_traverse")]
4097
impl<'a> SomeTable<'a> for VariationIndex<'a> {
4098
    fn type_name(&self) -> &str {
4099
        "VariationIndex"
4100
    }
4101
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4102
        match idx {
4103
            0usize => Some(Field::new(
4104
                "delta_set_outer_index",
4105
                self.delta_set_outer_index(),
4106
            )),
4107
            1usize => Some(Field::new(
4108
                "delta_set_inner_index",
4109
                self.delta_set_inner_index(),
4110
            )),
4111
            2usize => Some(Field::new("delta_format", self.delta_format())),
4112
            _ => None,
4113
        }
4114
    }
4115
}
4116
4117
#[cfg(feature = "experimental_traverse")]
4118
#[allow(clippy::needless_lifetimes)]
4119
impl<'a> std::fmt::Debug for VariationIndex<'a> {
4120
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4121
        (self as &dyn SomeTable<'a>).fmt(f)
4122
    }
4123
}
4124
4125
/// Either a [Device] table (in a non-variable font) or a [VariationIndex] table (in a variable font)
4126
#[derive(Clone)]
4127
pub enum DeviceOrVariationIndex<'a> {
4128
    Device(Device<'a>),
4129
    VariationIndex(VariationIndex<'a>),
4130
}
4131
4132
impl<'a> DeviceOrVariationIndex<'a> {
4133
    ///Return the `FontData` used to resolve offsets for this table.
4134
    pub fn offset_data(&self) -> FontData<'a> {
4135
        match self {
4136
            Self::Device(item) => item.offset_data(),
4137
            Self::VariationIndex(item) => item.offset_data(),
4138
        }
4139
    }
4140
}
4141
4142
impl<'a> FontRead<'a> for DeviceOrVariationIndex<'a> {
4143
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4144
        let format: DeltaFormat = data.read_at(4usize)?;
4145
4146
        #[allow(clippy::redundant_guards)]
4147
        match format {
4148
            format if format != DeltaFormat::VariationIndex => {
4149
                Ok(Self::Device(FontRead::read(data)?))
4150
            }
4151
            format if format == DeltaFormat::VariationIndex => {
4152
                Ok(Self::VariationIndex(FontRead::read(data)?))
4153
            }
4154
            other => Err(ReadError::InvalidFormat(other.into())),
4155
        }
4156
    }
4157
}
4158
4159
impl<'a> MinByteRange<'a> for DeviceOrVariationIndex<'a> {
4160
    fn min_byte_range(&self) -> Range<usize> {
4161
        match self {
4162
            Self::Device(item) => item.min_byte_range(),
4163
            Self::VariationIndex(item) => item.min_byte_range(),
4164
        }
4165
    }
4166
    fn min_table_bytes(&self) -> &'a [u8] {
4167
        match self {
4168
            Self::Device(item) => item.min_table_bytes(),
4169
            Self::VariationIndex(item) => item.min_table_bytes(),
4170
        }
4171
    }
4172
}
4173
4174
#[cfg(feature = "experimental_traverse")]
4175
impl<'a> DeviceOrVariationIndex<'a> {
4176
    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
4177
        match self {
4178
            Self::Device(table) => table,
4179
            Self::VariationIndex(table) => table,
4180
        }
4181
    }
4182
}
4183
4184
#[cfg(feature = "experimental_traverse")]
4185
impl std::fmt::Debug for DeviceOrVariationIndex<'_> {
4186
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4187
        self.dyn_inner().fmt(f)
4188
    }
4189
}
4190
4191
#[cfg(feature = "experimental_traverse")]
4192
impl<'a> SomeTable<'a> for DeviceOrVariationIndex<'a> {
4193
    fn type_name(&self) -> &str {
4194
        self.dyn_inner().type_name()
4195
    }
4196
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4197
        self.dyn_inner().get_field(idx)
4198
    }
4199
}
4200
4201
impl<'a> MinByteRange<'a> for FeatureVariations<'a> {
4202
    fn min_byte_range(&self) -> Range<usize> {
4203
        0..self.feature_variation_records_byte_range().end
4204
    }
4205
    fn min_table_bytes(&self) -> &'a [u8] {
4206
        let range = self.min_byte_range();
4207
        self.data.as_bytes().get(range).unwrap_or_default()
4208
    }
4209
}
4210
4211
impl<'a> FontRead<'a> for FeatureVariations<'a> {
4212
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4213
        #[allow(clippy::absurd_extreme_comparisons)]
4214
        if data.len() < Self::MIN_SIZE {
4215
            return Err(ReadError::OutOfBounds);
4216
        }
4217
        Ok(Self { data })
4218
    }
4219
}
4220
4221
/// [FeatureVariations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featurevariations-table)
4222
#[derive(Clone)]
4223
pub struct FeatureVariations<'a> {
4224
    data: FontData<'a>,
4225
}
4226
4227
#[allow(clippy::needless_lifetimes)]
4228
impl<'a> FeatureVariations<'a> {
4229
    pub const MIN_SIZE: usize = (MajorMinor::RAW_BYTE_LEN + u32::RAW_BYTE_LEN);
4230
    basic_table_impls!(impl_the_methods);
4231
4232
    pub fn version(&self) -> MajorMinor {
4233
        let range = self.version_byte_range();
4234
        self.data.read_at(range.start).ok().unwrap()
4235
    }
4236
4237
    /// Number of feature variation records.
4238
    pub fn feature_variation_record_count(&self) -> u32 {
4239
        let range = self.feature_variation_record_count_byte_range();
4240
        self.data.read_at(range.start).ok().unwrap()
4241
    }
4242
4243
    /// Array of feature variation records.
4244
    pub fn feature_variation_records(&self) -> &'a [FeatureVariationRecord] {
4245
        let range = self.feature_variation_records_byte_range();
4246
        self.data.read_array(range).ok().unwrap_or_default()
4247
    }
4248
4249
    pub fn version_byte_range(&self) -> Range<usize> {
4250
        let start = 0;
4251
        start..start + MajorMinor::RAW_BYTE_LEN
4252
    }
4253
4254
    pub fn feature_variation_record_count_byte_range(&self) -> Range<usize> {
4255
        let start = self.version_byte_range().end;
4256
        start..start + u32::RAW_BYTE_LEN
4257
    }
4258
4259
    pub fn feature_variation_records_byte_range(&self) -> Range<usize> {
4260
        let feature_variation_record_count = self.feature_variation_record_count();
4261
        let start = self.feature_variation_record_count_byte_range().end;
4262
        start
4263
            ..start
4264
                + (feature_variation_record_count as usize)
4265
                    .saturating_mul(FeatureVariationRecord::RAW_BYTE_LEN)
4266
    }
4267
}
4268
4269
#[cfg(feature = "experimental_traverse")]
4270
impl<'a> SomeTable<'a> for FeatureVariations<'a> {
4271
    fn type_name(&self) -> &str {
4272
        "FeatureVariations"
4273
    }
4274
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4275
        match idx {
4276
            0usize => Some(Field::new("version", self.version())),
4277
            1usize => Some(Field::new(
4278
                "feature_variation_record_count",
4279
                self.feature_variation_record_count(),
4280
            )),
4281
            2usize => Some(Field::new(
4282
                "feature_variation_records",
4283
                traversal::FieldType::array_of_records(
4284
                    stringify!(FeatureVariationRecord),
4285
                    self.feature_variation_records(),
4286
                    self.offset_data(),
4287
                ),
4288
            )),
4289
            _ => None,
4290
        }
4291
    }
4292
}
4293
4294
#[cfg(feature = "experimental_traverse")]
4295
#[allow(clippy::needless_lifetimes)]
4296
impl<'a> std::fmt::Debug for FeatureVariations<'a> {
4297
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4298
        (self as &dyn SomeTable<'a>).fmt(f)
4299
    }
4300
}
4301
4302
/// Part of [FeatureVariations]
4303
#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
4304
#[repr(C)]
4305
#[repr(packed)]
4306
pub struct FeatureVariationRecord {
4307
    /// Offset to a condition set table, from beginning of
4308
    /// FeatureVariations table.
4309
    pub condition_set_offset: BigEndian<Nullable<Offset32>>,
4310
    /// Offset to a feature table substitution table, from beginning of
4311
    /// the FeatureVariations table.
4312
    pub feature_table_substitution_offset: BigEndian<Nullable<Offset32>>,
4313
}
4314
4315
impl FeatureVariationRecord {
4316
    /// Offset to a condition set table, from beginning of
4317
    /// FeatureVariations table.
4318
    pub fn condition_set_offset(&self) -> Nullable<Offset32> {
4319
        self.condition_set_offset.get()
4320
    }
4321
4322
    /// Offset to a condition set table, from beginning of
4323
    /// FeatureVariations table.
4324
    ///
4325
    /// The `data` argument should be retrieved from the parent table
4326
    /// By calling its `offset_data` method.
4327
    pub fn condition_set<'a>(
4328
        &self,
4329
        data: FontData<'a>,
4330
    ) -> Option<Result<ConditionSet<'a>, ReadError>> {
4331
        self.condition_set_offset().resolve(data)
4332
    }
4333
4334
    /// Offset to a feature table substitution table, from beginning of
4335
    /// the FeatureVariations table.
4336
    pub fn feature_table_substitution_offset(&self) -> Nullable<Offset32> {
4337
        self.feature_table_substitution_offset.get()
4338
    }
4339
4340
    /// Offset to a feature table substitution table, from beginning of
4341
    /// the FeatureVariations table.
4342
    ///
4343
    /// The `data` argument should be retrieved from the parent table
4344
    /// By calling its `offset_data` method.
4345
    pub fn feature_table_substitution<'a>(
4346
        &self,
4347
        data: FontData<'a>,
4348
    ) -> Option<Result<FeatureTableSubstitution<'a>, ReadError>> {
4349
        self.feature_table_substitution_offset().resolve(data)
4350
    }
4351
}
4352
4353
impl FixedSize for FeatureVariationRecord {
4354
    const RAW_BYTE_LEN: usize = Offset32::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN;
4355
}
4356
4357
#[cfg(feature = "experimental_traverse")]
4358
impl<'a> SomeRecord<'a> for FeatureVariationRecord {
4359
    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
4360
        RecordResolver {
4361
            name: "FeatureVariationRecord",
4362
            get_field: Box::new(move |idx, _data| match idx {
4363
                0usize => Some(Field::new(
4364
                    "condition_set_offset",
4365
                    FieldType::offset(self.condition_set_offset(), self.condition_set(_data)),
4366
                )),
4367
                1usize => Some(Field::new(
4368
                    "feature_table_substitution_offset",
4369
                    FieldType::offset(
4370
                        self.feature_table_substitution_offset(),
4371
                        self.feature_table_substitution(_data),
4372
                    ),
4373
                )),
4374
                _ => None,
4375
            }),
4376
            data,
4377
        }
4378
    }
4379
}
4380
4381
impl<'a> MinByteRange<'a> for ConditionSet<'a> {
4382
    fn min_byte_range(&self) -> Range<usize> {
4383
        0..self.condition_offsets_byte_range().end
4384
    }
4385
    fn min_table_bytes(&self) -> &'a [u8] {
4386
        let range = self.min_byte_range();
4387
        self.data.as_bytes().get(range).unwrap_or_default()
4388
    }
4389
}
4390
4391
impl<'a> FontRead<'a> for ConditionSet<'a> {
4392
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4393
        #[allow(clippy::absurd_extreme_comparisons)]
4394
        if data.len() < Self::MIN_SIZE {
4395
            return Err(ReadError::OutOfBounds);
4396
        }
4397
        Ok(Self { data })
4398
    }
4399
}
4400
4401
/// [ConditionSet Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#conditionset-table)
4402
#[derive(Clone)]
4403
pub struct ConditionSet<'a> {
4404
    data: FontData<'a>,
4405
}
4406
4407
#[allow(clippy::needless_lifetimes)]
4408
impl<'a> ConditionSet<'a> {
4409
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
4410
    basic_table_impls!(impl_the_methods);
4411
4412
    /// Number of conditions for this condition set.
4413
    pub fn condition_count(&self) -> u16 {
4414
        let range = self.condition_count_byte_range();
4415
        self.data.read_at(range.start).ok().unwrap()
4416
    }
4417
4418
    /// Array of offsets to condition tables, from beginning of the
4419
    /// ConditionSet table.
4420
    pub fn condition_offsets(&self) -> &'a [BigEndian<Offset32>] {
4421
        let range = self.condition_offsets_byte_range();
4422
        self.data.read_array(range).ok().unwrap_or_default()
4423
    }
4424
4425
    /// A dynamically resolving wrapper for [`condition_offsets`][Self::condition_offsets].
4426
    pub fn conditions(&self) -> ArrayOfOffsets<'a, Condition<'a>, Offset32> {
4427
        let data = self.data;
4428
        let offsets = self.condition_offsets();
4429
        ArrayOfOffsets::new(offsets, data, ())
4430
    }
4431
4432
    pub fn condition_count_byte_range(&self) -> Range<usize> {
4433
        let start = 0;
4434
        start..start + u16::RAW_BYTE_LEN
4435
    }
4436
4437
    pub fn condition_offsets_byte_range(&self) -> Range<usize> {
4438
        let condition_count = self.condition_count();
4439
        let start = self.condition_count_byte_range().end;
4440
        start..start + (condition_count as usize).saturating_mul(Offset32::RAW_BYTE_LEN)
4441
    }
4442
}
4443
4444
#[cfg(feature = "experimental_traverse")]
4445
impl<'a> SomeTable<'a> for ConditionSet<'a> {
4446
    fn type_name(&self) -> &str {
4447
        "ConditionSet"
4448
    }
4449
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4450
        match idx {
4451
            0usize => Some(Field::new("condition_count", self.condition_count())),
4452
            1usize => Some({
4453
                let data = self.data;
4454
                Field::new(
4455
                    "condition_offsets",
4456
                    FieldType::array_of_offsets(
4457
                        better_type_name::<Condition>(),
4458
                        self.condition_offsets(),
4459
                        move |off| {
4460
                            let target = off.get().resolve::<Condition>(data);
4461
                            FieldType::offset(off.get(), target)
4462
                        },
4463
                    ),
4464
                )
4465
            }),
4466
            _ => None,
4467
        }
4468
    }
4469
}
4470
4471
#[cfg(feature = "experimental_traverse")]
4472
#[allow(clippy::needless_lifetimes)]
4473
impl<'a> std::fmt::Debug for ConditionSet<'a> {
4474
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4475
        (self as &dyn SomeTable<'a>).fmt(f)
4476
    }
4477
}
4478
4479
/// [Condition Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#condition-table)
4480
///
4481
/// Formats 2..5 are implementations of specification changes currently under debate at ISO for an OFF
4482
/// update. For the time being the specification is <https://github.com/harfbuzz/boring-expansion-spec/blob/main/ConditionTree.md>.
4483
#[derive(Clone)]
4484
pub enum Condition<'a> {
4485
    Format1AxisRange(ConditionFormat1<'a>),
4486
    Format2VariableValue(ConditionFormat2<'a>),
4487
    Format3And(ConditionFormat3<'a>),
4488
    Format4Or(ConditionFormat4<'a>),
4489
    Format5Negate(ConditionFormat5<'a>),
4490
}
4491
4492
impl<'a> Condition<'a> {
4493
    ///Return the `FontData` used to resolve offsets for this table.
4494
    pub fn offset_data(&self) -> FontData<'a> {
4495
        match self {
4496
            Self::Format1AxisRange(item) => item.offset_data(),
4497
            Self::Format2VariableValue(item) => item.offset_data(),
4498
            Self::Format3And(item) => item.offset_data(),
4499
            Self::Format4Or(item) => item.offset_data(),
4500
            Self::Format5Negate(item) => item.offset_data(),
4501
        }
4502
    }
4503
4504
    /// Format, = 1
4505
    pub fn format(&self) -> u16 {
4506
        match self {
4507
            Self::Format1AxisRange(item) => item.format(),
4508
            Self::Format2VariableValue(item) => item.format(),
4509
            Self::Format3And(item) => item.format(),
4510
            Self::Format4Or(item) => item.format(),
4511
            Self::Format5Negate(item) => item.format(),
4512
        }
4513
    }
4514
}
4515
4516
impl<'a> FontRead<'a> for Condition<'a> {
4517
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4518
        let format: u16 = data.read_at(0usize)?;
4519
        match format {
4520
            ConditionFormat1::FORMAT => Ok(Self::Format1AxisRange(FontRead::read(data)?)),
4521
            ConditionFormat2::FORMAT => Ok(Self::Format2VariableValue(FontRead::read(data)?)),
4522
            ConditionFormat3::FORMAT => Ok(Self::Format3And(FontRead::read(data)?)),
4523
            ConditionFormat4::FORMAT => Ok(Self::Format4Or(FontRead::read(data)?)),
4524
            ConditionFormat5::FORMAT => Ok(Self::Format5Negate(FontRead::read(data)?)),
4525
            other => Err(ReadError::InvalidFormat(other.into())),
4526
        }
4527
    }
4528
}
4529
4530
impl<'a> MinByteRange<'a> for Condition<'a> {
4531
    fn min_byte_range(&self) -> Range<usize> {
4532
        match self {
4533
            Self::Format1AxisRange(item) => item.min_byte_range(),
4534
            Self::Format2VariableValue(item) => item.min_byte_range(),
4535
            Self::Format3And(item) => item.min_byte_range(),
4536
            Self::Format4Or(item) => item.min_byte_range(),
4537
            Self::Format5Negate(item) => item.min_byte_range(),
4538
        }
4539
    }
4540
    fn min_table_bytes(&self) -> &'a [u8] {
4541
        match self {
4542
            Self::Format1AxisRange(item) => item.min_table_bytes(),
4543
            Self::Format2VariableValue(item) => item.min_table_bytes(),
4544
            Self::Format3And(item) => item.min_table_bytes(),
4545
            Self::Format4Or(item) => item.min_table_bytes(),
4546
            Self::Format5Negate(item) => item.min_table_bytes(),
4547
        }
4548
    }
4549
}
4550
4551
#[cfg(feature = "experimental_traverse")]
4552
impl<'a> Condition<'a> {
4553
    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
4554
        match self {
4555
            Self::Format1AxisRange(table) => table,
4556
            Self::Format2VariableValue(table) => table,
4557
            Self::Format3And(table) => table,
4558
            Self::Format4Or(table) => table,
4559
            Self::Format5Negate(table) => table,
4560
        }
4561
    }
4562
}
4563
4564
#[cfg(feature = "experimental_traverse")]
4565
impl std::fmt::Debug for Condition<'_> {
4566
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4567
        self.dyn_inner().fmt(f)
4568
    }
4569
}
4570
4571
#[cfg(feature = "experimental_traverse")]
4572
impl<'a> SomeTable<'a> for Condition<'a> {
4573
    fn type_name(&self) -> &str {
4574
        self.dyn_inner().type_name()
4575
    }
4576
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4577
        self.dyn_inner().get_field(idx)
4578
    }
4579
}
4580
4581
impl Format<u16> for ConditionFormat1<'_> {
4582
    const FORMAT: u16 = 1;
4583
}
4584
4585
impl<'a> MinByteRange<'a> for ConditionFormat1<'a> {
4586
    fn min_byte_range(&self) -> Range<usize> {
4587
        0..self.filter_range_max_value_byte_range().end
4588
    }
4589
    fn min_table_bytes(&self) -> &'a [u8] {
4590
        let range = self.min_byte_range();
4591
        self.data.as_bytes().get(range).unwrap_or_default()
4592
    }
4593
}
4594
4595
impl<'a> FontRead<'a> for ConditionFormat1<'a> {
4596
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4597
        #[allow(clippy::absurd_extreme_comparisons)]
4598
        if data.len() < Self::MIN_SIZE {
4599
            return Err(ReadError::OutOfBounds);
4600
        }
4601
        Ok(Self { data })
4602
    }
4603
}
4604
4605
/// [Condition Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#condition-table-format-1-font-variation-axis-range): Font Variation Axis Range
4606
#[derive(Clone)]
4607
pub struct ConditionFormat1<'a> {
4608
    data: FontData<'a>,
4609
}
4610
4611
#[allow(clippy::needless_lifetimes)]
4612
impl<'a> ConditionFormat1<'a> {
4613
    pub const MIN_SIZE: usize =
4614
        (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + F2Dot14::RAW_BYTE_LEN + F2Dot14::RAW_BYTE_LEN);
4615
    basic_table_impls!(impl_the_methods);
4616
4617
    /// Format, = 1
4618
    pub fn format(&self) -> u16 {
4619
        let range = self.format_byte_range();
4620
        self.data.read_at(range.start).ok().unwrap()
4621
    }
4622
4623
    /// Index (zero-based) for the variation axis within the 'fvar'
4624
    /// table.
4625
    pub fn axis_index(&self) -> u16 {
4626
        let range = self.axis_index_byte_range();
4627
        self.data.read_at(range.start).ok().unwrap()
4628
    }
4629
4630
    /// Minimum value of the font variation instances that satisfy this
4631
    /// condition.
4632
    pub fn filter_range_min_value(&self) -> F2Dot14 {
4633
        let range = self.filter_range_min_value_byte_range();
4634
        self.data.read_at(range.start).ok().unwrap()
4635
    }
4636
4637
    /// Maximum value of the font variation instances that satisfy this
4638
    /// condition.
4639
    pub fn filter_range_max_value(&self) -> F2Dot14 {
4640
        let range = self.filter_range_max_value_byte_range();
4641
        self.data.read_at(range.start).ok().unwrap()
4642
    }
4643
4644
    pub fn format_byte_range(&self) -> Range<usize> {
4645
        let start = 0;
4646
        start..start + u16::RAW_BYTE_LEN
4647
    }
4648
4649
    pub fn axis_index_byte_range(&self) -> Range<usize> {
4650
        let start = self.format_byte_range().end;
4651
        start..start + u16::RAW_BYTE_LEN
4652
    }
4653
4654
    pub fn filter_range_min_value_byte_range(&self) -> Range<usize> {
4655
        let start = self.axis_index_byte_range().end;
4656
        start..start + F2Dot14::RAW_BYTE_LEN
4657
    }
4658
4659
    pub fn filter_range_max_value_byte_range(&self) -> Range<usize> {
4660
        let start = self.filter_range_min_value_byte_range().end;
4661
        start..start + F2Dot14::RAW_BYTE_LEN
4662
    }
4663
}
4664
4665
#[cfg(feature = "experimental_traverse")]
4666
impl<'a> SomeTable<'a> for ConditionFormat1<'a> {
4667
    fn type_name(&self) -> &str {
4668
        "ConditionFormat1"
4669
    }
4670
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4671
        match idx {
4672
            0usize => Some(Field::new("format", self.format())),
4673
            1usize => Some(Field::new("axis_index", self.axis_index())),
4674
            2usize => Some(Field::new(
4675
                "filter_range_min_value",
4676
                self.filter_range_min_value(),
4677
            )),
4678
            3usize => Some(Field::new(
4679
                "filter_range_max_value",
4680
                self.filter_range_max_value(),
4681
            )),
4682
            _ => None,
4683
        }
4684
    }
4685
}
4686
4687
#[cfg(feature = "experimental_traverse")]
4688
#[allow(clippy::needless_lifetimes)]
4689
impl<'a> std::fmt::Debug for ConditionFormat1<'a> {
4690
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4691
        (self as &dyn SomeTable<'a>).fmt(f)
4692
    }
4693
}
4694
4695
impl Format<u16> for ConditionFormat2<'_> {
4696
    const FORMAT: u16 = 2;
4697
}
4698
4699
impl<'a> MinByteRange<'a> for ConditionFormat2<'a> {
4700
    fn min_byte_range(&self) -> Range<usize> {
4701
        0..self.var_index_byte_range().end
4702
    }
4703
    fn min_table_bytes(&self) -> &'a [u8] {
4704
        let range = self.min_byte_range();
4705
        self.data.as_bytes().get(range).unwrap_or_default()
4706
    }
4707
}
4708
4709
impl<'a> FontRead<'a> for ConditionFormat2<'a> {
4710
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4711
        #[allow(clippy::absurd_extreme_comparisons)]
4712
        if data.len() < Self::MIN_SIZE {
4713
            return Err(ReadError::OutOfBounds);
4714
        }
4715
        Ok(Self { data })
4716
    }
4717
}
4718
4719
/// [Condition Table Format 2](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3237-L3255): Variation index
4720
#[derive(Clone)]
4721
pub struct ConditionFormat2<'a> {
4722
    data: FontData<'a>,
4723
}
4724
4725
#[allow(clippy::needless_lifetimes)]
4726
impl<'a> ConditionFormat2<'a> {
4727
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN + u32::RAW_BYTE_LEN);
4728
    basic_table_impls!(impl_the_methods);
4729
4730
    /// Format, = 2
4731
    pub fn format(&self) -> u16 {
4732
        let range = self.format_byte_range();
4733
        self.data.read_at(range.start).ok().unwrap()
4734
    }
4735
4736
    /// Value at default instance.
4737
    pub fn default_value(&self) -> i16 {
4738
        let range = self.default_value_byte_range();
4739
        self.data.read_at(range.start).ok().unwrap()
4740
    }
4741
4742
    /// Variation index to vary the value based on current designspace location.
4743
    pub fn var_index(&self) -> u32 {
4744
        let range = self.var_index_byte_range();
4745
        self.data.read_at(range.start).ok().unwrap()
4746
    }
4747
4748
    pub fn format_byte_range(&self) -> Range<usize> {
4749
        let start = 0;
4750
        start..start + u16::RAW_BYTE_LEN
4751
    }
4752
4753
    pub fn default_value_byte_range(&self) -> Range<usize> {
4754
        let start = self.format_byte_range().end;
4755
        start..start + i16::RAW_BYTE_LEN
4756
    }
4757
4758
    pub fn var_index_byte_range(&self) -> Range<usize> {
4759
        let start = self.default_value_byte_range().end;
4760
        start..start + u32::RAW_BYTE_LEN
4761
    }
4762
}
4763
4764
#[cfg(feature = "experimental_traverse")]
4765
impl<'a> SomeTable<'a> for ConditionFormat2<'a> {
4766
    fn type_name(&self) -> &str {
4767
        "ConditionFormat2"
4768
    }
4769
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4770
        match idx {
4771
            0usize => Some(Field::new("format", self.format())),
4772
            1usize => Some(Field::new("default_value", self.default_value())),
4773
            2usize => Some(Field::new("var_index", self.var_index())),
4774
            _ => None,
4775
        }
4776
    }
4777
}
4778
4779
#[cfg(feature = "experimental_traverse")]
4780
#[allow(clippy::needless_lifetimes)]
4781
impl<'a> std::fmt::Debug for ConditionFormat2<'a> {
4782
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4783
        (self as &dyn SomeTable<'a>).fmt(f)
4784
    }
4785
}
4786
4787
impl Format<u16> for ConditionFormat3<'_> {
4788
    const FORMAT: u16 = 3;
4789
}
4790
4791
impl<'a> MinByteRange<'a> for ConditionFormat3<'a> {
4792
    fn min_byte_range(&self) -> Range<usize> {
4793
        0..self.condition_offsets_byte_range().end
4794
    }
4795
    fn min_table_bytes(&self) -> &'a [u8] {
4796
        let range = self.min_byte_range();
4797
        self.data.as_bytes().get(range).unwrap_or_default()
4798
    }
4799
}
4800
4801
impl<'a> FontRead<'a> for ConditionFormat3<'a> {
4802
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4803
        #[allow(clippy::absurd_extreme_comparisons)]
4804
        if data.len() < Self::MIN_SIZE {
4805
            return Err(ReadError::OutOfBounds);
4806
        }
4807
        Ok(Self { data })
4808
    }
4809
}
4810
4811
/// [Condition Table Format 3](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3257-L3275): AND
4812
#[derive(Clone)]
4813
pub struct ConditionFormat3<'a> {
4814
    data: FontData<'a>,
4815
}
4816
4817
#[allow(clippy::needless_lifetimes)]
4818
impl<'a> ConditionFormat3<'a> {
4819
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u8::RAW_BYTE_LEN);
4820
    basic_table_impls!(impl_the_methods);
4821
4822
    /// Format, = 3
4823
    pub fn format(&self) -> u16 {
4824
        let range = self.format_byte_range();
4825
        self.data.read_at(range.start).ok().unwrap()
4826
    }
4827
4828
    /// Number of conditions.
4829
    pub fn condition_count(&self) -> u8 {
4830
        let range = self.condition_count_byte_range();
4831
        self.data.read_at(range.start).ok().unwrap()
4832
    }
4833
4834
    /// Array of condition tables for this conjunction (AND) expression.
4835
    pub fn condition_offsets(&self) -> &'a [BigEndian<Offset24>] {
4836
        let range = self.condition_offsets_byte_range();
4837
        self.data.read_array(range).ok().unwrap_or_default()
4838
    }
4839
4840
    /// A dynamically resolving wrapper for [`condition_offsets`][Self::condition_offsets].
4841
    pub fn conditions(&self) -> ArrayOfOffsets<'a, Condition<'a>, Offset24> {
4842
        let data = self.data;
4843
        let offsets = self.condition_offsets();
4844
        ArrayOfOffsets::new(offsets, data, ())
4845
    }
4846
4847
    pub fn format_byte_range(&self) -> Range<usize> {
4848
        let start = 0;
4849
        start..start + u16::RAW_BYTE_LEN
4850
    }
4851
4852
    pub fn condition_count_byte_range(&self) -> Range<usize> {
4853
        let start = self.format_byte_range().end;
4854
        start..start + u8::RAW_BYTE_LEN
4855
    }
4856
4857
    pub fn condition_offsets_byte_range(&self) -> Range<usize> {
4858
        let condition_count = self.condition_count();
4859
        let start = self.condition_count_byte_range().end;
4860
        start..start + (condition_count as usize).saturating_mul(Offset24::RAW_BYTE_LEN)
4861
    }
4862
}
4863
4864
#[cfg(feature = "experimental_traverse")]
4865
impl<'a> SomeTable<'a> for ConditionFormat3<'a> {
4866
    fn type_name(&self) -> &str {
4867
        "ConditionFormat3"
4868
    }
4869
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4870
        match idx {
4871
            0usize => Some(Field::new("format", self.format())),
4872
            1usize => Some(Field::new("condition_count", self.condition_count())),
4873
            2usize => Some({
4874
                let data = self.data;
4875
                Field::new(
4876
                    "condition_offsets",
4877
                    FieldType::array_of_offsets(
4878
                        better_type_name::<Condition>(),
4879
                        self.condition_offsets(),
4880
                        move |off| {
4881
                            let target = off.get().resolve::<Condition>(data);
4882
                            FieldType::offset(off.get(), target)
4883
                        },
4884
                    ),
4885
                )
4886
            }),
4887
            _ => None,
4888
        }
4889
    }
4890
}
4891
4892
#[cfg(feature = "experimental_traverse")]
4893
#[allow(clippy::needless_lifetimes)]
4894
impl<'a> std::fmt::Debug for ConditionFormat3<'a> {
4895
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4896
        (self as &dyn SomeTable<'a>).fmt(f)
4897
    }
4898
}
4899
4900
impl Format<u16> for ConditionFormat4<'_> {
4901
    const FORMAT: u16 = 4;
4902
}
4903
4904
impl<'a> MinByteRange<'a> for ConditionFormat4<'a> {
4905
    fn min_byte_range(&self) -> Range<usize> {
4906
        0..self.condition_offsets_byte_range().end
4907
    }
4908
    fn min_table_bytes(&self) -> &'a [u8] {
4909
        let range = self.min_byte_range();
4910
        self.data.as_bytes().get(range).unwrap_or_default()
4911
    }
4912
}
4913
4914
impl<'a> FontRead<'a> for ConditionFormat4<'a> {
4915
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4916
        #[allow(clippy::absurd_extreme_comparisons)]
4917
        if data.len() < Self::MIN_SIZE {
4918
            return Err(ReadError::OutOfBounds);
4919
        }
4920
        Ok(Self { data })
4921
    }
4922
}
4923
4924
/// [Condition Table Format 4](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3276-L3295): OR
4925
#[derive(Clone)]
4926
pub struct ConditionFormat4<'a> {
4927
    data: FontData<'a>,
4928
}
4929
4930
#[allow(clippy::needless_lifetimes)]
4931
impl<'a> ConditionFormat4<'a> {
4932
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u8::RAW_BYTE_LEN);
4933
    basic_table_impls!(impl_the_methods);
4934
4935
    /// Format, = 4
4936
    pub fn format(&self) -> u16 {
4937
        let range = self.format_byte_range();
4938
        self.data.read_at(range.start).ok().unwrap()
4939
    }
4940
4941
    /// Number of conditions.
4942
    pub fn condition_count(&self) -> u8 {
4943
        let range = self.condition_count_byte_range();
4944
        self.data.read_at(range.start).ok().unwrap()
4945
    }
4946
4947
    /// Array of condition tables for this disjunction (OR) expression.
4948
    pub fn condition_offsets(&self) -> &'a [BigEndian<Offset24>] {
4949
        let range = self.condition_offsets_byte_range();
4950
        self.data.read_array(range).ok().unwrap_or_default()
4951
    }
4952
4953
    /// A dynamically resolving wrapper for [`condition_offsets`][Self::condition_offsets].
4954
    pub fn conditions(&self) -> ArrayOfOffsets<'a, Condition<'a>, Offset24> {
4955
        let data = self.data;
4956
        let offsets = self.condition_offsets();
4957
        ArrayOfOffsets::new(offsets, data, ())
4958
    }
4959
4960
    pub fn format_byte_range(&self) -> Range<usize> {
4961
        let start = 0;
4962
        start..start + u16::RAW_BYTE_LEN
4963
    }
4964
4965
    pub fn condition_count_byte_range(&self) -> Range<usize> {
4966
        let start = self.format_byte_range().end;
4967
        start..start + u8::RAW_BYTE_LEN
4968
    }
4969
4970
    pub fn condition_offsets_byte_range(&self) -> Range<usize> {
4971
        let condition_count = self.condition_count();
4972
        let start = self.condition_count_byte_range().end;
4973
        start..start + (condition_count as usize).saturating_mul(Offset24::RAW_BYTE_LEN)
4974
    }
4975
}
4976
4977
#[cfg(feature = "experimental_traverse")]
4978
impl<'a> SomeTable<'a> for ConditionFormat4<'a> {
4979
    fn type_name(&self) -> &str {
4980
        "ConditionFormat4"
4981
    }
4982
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4983
        match idx {
4984
            0usize => Some(Field::new("format", self.format())),
4985
            1usize => Some(Field::new("condition_count", self.condition_count())),
4986
            2usize => Some({
4987
                let data = self.data;
4988
                Field::new(
4989
                    "condition_offsets",
4990
                    FieldType::array_of_offsets(
4991
                        better_type_name::<Condition>(),
4992
                        self.condition_offsets(),
4993
                        move |off| {
4994
                            let target = off.get().resolve::<Condition>(data);
4995
                            FieldType::offset(off.get(), target)
4996
                        },
4997
                    ),
4998
                )
4999
            }),
5000
            _ => None,
5001
        }
5002
    }
5003
}
5004
5005
#[cfg(feature = "experimental_traverse")]
5006
#[allow(clippy::needless_lifetimes)]
5007
impl<'a> std::fmt::Debug for ConditionFormat4<'a> {
5008
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5009
        (self as &dyn SomeTable<'a>).fmt(f)
5010
    }
5011
}
5012
5013
impl Format<u16> for ConditionFormat5<'_> {
5014
    const FORMAT: u16 = 5;
5015
}
5016
5017
impl<'a> MinByteRange<'a> for ConditionFormat5<'a> {
5018
    fn min_byte_range(&self) -> Range<usize> {
5019
        0..self.condition_offset_byte_range().end
5020
    }
5021
    fn min_table_bytes(&self) -> &'a [u8] {
5022
        let range = self.min_byte_range();
5023
        self.data.as_bytes().get(range).unwrap_or_default()
5024
    }
5025
}
5026
5027
impl<'a> FontRead<'a> for ConditionFormat5<'a> {
5028
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
5029
        #[allow(clippy::absurd_extreme_comparisons)]
5030
        if data.len() < Self::MIN_SIZE {
5031
            return Err(ReadError::OutOfBounds);
5032
        }
5033
        Ok(Self { data })
5034
    }
5035
}
5036
5037
/// [Condition Table Format 5](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3296-L3308): NOT
5038
#[derive(Clone)]
5039
pub struct ConditionFormat5<'a> {
5040
    data: FontData<'a>,
5041
}
5042
5043
#[allow(clippy::needless_lifetimes)]
5044
impl<'a> ConditionFormat5<'a> {
5045
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset24::RAW_BYTE_LEN);
5046
    basic_table_impls!(impl_the_methods);
5047
5048
    /// Format, = 5
5049
    pub fn format(&self) -> u16 {
5050
        let range = self.format_byte_range();
5051
        self.data.read_at(range.start).ok().unwrap()
5052
    }
5053
5054
    /// Condition to negate.
5055
    pub fn condition_offset(&self) -> Offset24 {
5056
        let range = self.condition_offset_byte_range();
5057
        self.data.read_at(range.start).ok().unwrap()
5058
    }
5059
5060
    /// Attempt to resolve [`condition_offset`][Self::condition_offset].
5061
    pub fn condition(&self) -> Result<Condition<'a>, ReadError> {
5062
        let data = self.data;
5063
        self.condition_offset().resolve(data)
5064
    }
5065
5066
    pub fn format_byte_range(&self) -> Range<usize> {
5067
        let start = 0;
5068
        start..start + u16::RAW_BYTE_LEN
5069
    }
5070
5071
    pub fn condition_offset_byte_range(&self) -> Range<usize> {
5072
        let start = self.format_byte_range().end;
5073
        start..start + Offset24::RAW_BYTE_LEN
5074
    }
5075
}
5076
5077
#[cfg(feature = "experimental_traverse")]
5078
impl<'a> SomeTable<'a> for ConditionFormat5<'a> {
5079
    fn type_name(&self) -> &str {
5080
        "ConditionFormat5"
5081
    }
5082
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
5083
        match idx {
5084
            0usize => Some(Field::new("format", self.format())),
5085
            1usize => Some(Field::new(
5086
                "condition_offset",
5087
                FieldType::offset(self.condition_offset(), self.condition()),
5088
            )),
5089
            _ => None,
5090
        }
5091
    }
5092
}
5093
5094
#[cfg(feature = "experimental_traverse")]
5095
#[allow(clippy::needless_lifetimes)]
5096
impl<'a> std::fmt::Debug for ConditionFormat5<'a> {
5097
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5098
        (self as &dyn SomeTable<'a>).fmt(f)
5099
    }
5100
}
5101
5102
impl<'a> MinByteRange<'a> for FeatureTableSubstitution<'a> {
5103
    fn min_byte_range(&self) -> Range<usize> {
5104
        0..self.substitutions_byte_range().end
5105
    }
5106
    fn min_table_bytes(&self) -> &'a [u8] {
5107
        let range = self.min_byte_range();
5108
        self.data.as_bytes().get(range).unwrap_or_default()
5109
    }
5110
}
5111
5112
impl<'a> FontRead<'a> for FeatureTableSubstitution<'a> {
5113
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
5114
        #[allow(clippy::absurd_extreme_comparisons)]
5115
        if data.len() < Self::MIN_SIZE {
5116
            return Err(ReadError::OutOfBounds);
5117
        }
5118
        Ok(Self { data })
5119
    }
5120
}
5121
5122
/// [FeatureTableSubstitution Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featuretablesubstitution-table)
5123
#[derive(Clone)]
5124
pub struct FeatureTableSubstitution<'a> {
5125
    data: FontData<'a>,
5126
}
5127
5128
#[allow(clippy::needless_lifetimes)]
5129
impl<'a> FeatureTableSubstitution<'a> {
5130
    pub const MIN_SIZE: usize = (MajorMinor::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
5131
    basic_table_impls!(impl_the_methods);
5132
5133
    /// Major & minor version of the table: (1, 0)
5134
    pub fn version(&self) -> MajorMinor {
5135
        let range = self.version_byte_range();
5136
        self.data.read_at(range.start).ok().unwrap()
5137
    }
5138
5139
    /// Number of feature table substitution records.
5140
    pub fn substitution_count(&self) -> u16 {
5141
        let range = self.substitution_count_byte_range();
5142
        self.data.read_at(range.start).ok().unwrap()
5143
    }
5144
5145
    /// Array of feature table substitution records.
5146
    pub fn substitutions(&self) -> &'a [FeatureTableSubstitutionRecord] {
5147
        let range = self.substitutions_byte_range();
5148
        self.data.read_array(range).ok().unwrap_or_default()
5149
    }
5150
5151
    pub fn version_byte_range(&self) -> Range<usize> {
5152
        let start = 0;
5153
        start..start + MajorMinor::RAW_BYTE_LEN
5154
    }
5155
5156
    pub fn substitution_count_byte_range(&self) -> Range<usize> {
5157
        let start = self.version_byte_range().end;
5158
        start..start + u16::RAW_BYTE_LEN
5159
    }
5160
5161
    pub fn substitutions_byte_range(&self) -> Range<usize> {
5162
        let substitution_count = self.substitution_count();
5163
        let start = self.substitution_count_byte_range().end;
5164
        start
5165
            ..start
5166
                + (substitution_count as usize)
5167
                    .saturating_mul(FeatureTableSubstitutionRecord::RAW_BYTE_LEN)
5168
    }
5169
}
5170
5171
#[cfg(feature = "experimental_traverse")]
5172
impl<'a> SomeTable<'a> for FeatureTableSubstitution<'a> {
5173
    fn type_name(&self) -> &str {
5174
        "FeatureTableSubstitution"
5175
    }
5176
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
5177
        match idx {
5178
            0usize => Some(Field::new("version", self.version())),
5179
            1usize => Some(Field::new("substitution_count", self.substitution_count())),
5180
            2usize => Some(Field::new(
5181
                "substitutions",
5182
                traversal::FieldType::array_of_records(
5183
                    stringify!(FeatureTableSubstitutionRecord),
5184
                    self.substitutions(),
5185
                    self.offset_data(),
5186
                ),
5187
            )),
5188
            _ => None,
5189
        }
5190
    }
5191
}
5192
5193
#[cfg(feature = "experimental_traverse")]
5194
#[allow(clippy::needless_lifetimes)]
5195
impl<'a> std::fmt::Debug for FeatureTableSubstitution<'a> {
5196
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5197
        (self as &dyn SomeTable<'a>).fmt(f)
5198
    }
5199
}
5200
5201
/// Used in [FeatureTableSubstitution]
5202
#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
5203
#[repr(C)]
5204
#[repr(packed)]
5205
pub struct FeatureTableSubstitutionRecord {
5206
    /// The feature table index to match.
5207
    pub feature_index: BigEndian<u16>,
5208
    /// Offset to an alternate feature table, from start of the
5209
    /// FeatureTableSubstitution table.
5210
    pub alternate_feature_offset: BigEndian<Offset32>,
5211
}
5212
5213
impl FeatureTableSubstitutionRecord {
5214
    /// The feature table index to match.
5215
    pub fn feature_index(&self) -> u16 {
5216
        self.feature_index.get()
5217
    }
5218
5219
    /// Offset to an alternate feature table, from start of the
5220
    /// FeatureTableSubstitution table.
5221
    pub fn alternate_feature_offset(&self) -> Offset32 {
5222
        self.alternate_feature_offset.get()
5223
    }
5224
}
5225
5226
impl FixedSize for FeatureTableSubstitutionRecord {
5227
    const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN;
5228
}
5229
5230
#[cfg(feature = "experimental_traverse")]
5231
impl<'a> SomeRecord<'a> for FeatureTableSubstitutionRecord {
5232
    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
5233
        RecordResolver {
5234
            name: "FeatureTableSubstitutionRecord",
5235
            get_field: Box::new(move |idx, _data| match idx {
5236
                0usize => Some(Field::new("feature_index", self.feature_index())),
5237
                1usize => Some(Field::new(
5238
                    "alternate_feature_offset",
5239
                    FieldType::offset(
5240
                        self.alternate_feature_offset(),
5241
                        self.alternate_feature(_data),
5242
                    ),
5243
                )),
5244
                _ => None,
5245
            }),
5246
            data,
5247
        }
5248
    }
5249
}
5250
5251
impl<'a> MinByteRange<'a> for SizeParams<'a> {
5252
    fn min_byte_range(&self) -> Range<usize> {
5253
        0..self.range_end_byte_range().end
5254
    }
5255
    fn min_table_bytes(&self) -> &'a [u8] {
5256
        let range = self.min_byte_range();
5257
        self.data.as_bytes().get(range).unwrap_or_default()
5258
    }
5259
}
5260
5261
impl<'a> FontRead<'a> for SizeParams<'a> {
5262
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
5263
        #[allow(clippy::absurd_extreme_comparisons)]
5264
        if data.len() < Self::MIN_SIZE {
5265
            return Err(ReadError::OutOfBounds);
5266
        }
5267
        Ok(Self { data })
5268
    }
5269
}
5270
5271
#[derive(Clone)]
5272
pub struct SizeParams<'a> {
5273
    data: FontData<'a>,
5274
}
5275
5276
#[allow(clippy::needless_lifetimes)]
5277
impl<'a> SizeParams<'a> {
5278
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
5279
        + u16::RAW_BYTE_LEN
5280
        + u16::RAW_BYTE_LEN
5281
        + u16::RAW_BYTE_LEN
5282
        + u16::RAW_BYTE_LEN);
5283
    basic_table_impls!(impl_the_methods);
5284
5285
    /// The first value represents the design size in 720/inch units (decipoints).
5286
    ///
5287
    /// The design size entry must be non-zero. When there is a design size but
5288
    /// no recommended size range, the rest of the array will consist of zeros.
5289
    pub fn design_size(&self) -> u16 {
5290
        let range = self.design_size_byte_range();
5291
        self.data.read_at(range.start).ok().unwrap()
5292
    }
5293
5294
    /// The second value has no independent meaning, but serves as an identifier that associates fonts in a subfamily.
5295
    ///
5296
    /// All fonts which share a Typographic or Font Family name and which differ
5297
    /// only by size range shall have the same subfamily value, and no fonts
5298
    /// which differ in weight or style shall have the same subfamily value.
5299
    /// If this value is zero, the remaining fields in the array will be ignored.
5300
    pub fn identifier(&self) -> u16 {
5301
        let range = self.identifier_byte_range();
5302
        self.data.read_at(range.start).ok().unwrap()
5303
    }
5304
5305
    /// The third value enables applications to use a single name for the subfamily identified by the second value.
5306
    ///
5307
    /// If the preceding value is non-zero, this value must be set in the range
5308
    /// 256 – 32767 (inclusive). It records the value of a field in the 'name'
5309
    /// table, which must contain English-language strings encoded in Windows
5310
    /// Unicode and Macintosh Roman, and may contain additional strings localized
5311
    /// to other scripts and languages. Each of these strings is the name
5312
    /// an application should use, in combination with the family name, to
5313
    /// represent the subfamily in a menu. Applications will choose the
5314
    /// appropriate version based on their selection criteria.
5315
    pub fn name_entry(&self) -> u16 {
5316
        let range = self.name_entry_byte_range();
5317
        self.data.read_at(range.start).ok().unwrap()
5318
    }
5319
5320
    /// The fourth and fifth values represent the small end of the recommended
5321
    /// usage range (exclusive) and the large end of the recommended usage range
5322
    /// (inclusive), stored in 720/inch units (decipoints).
5323
    ///
5324
    /// Ranges must not overlap, and should generally be contiguous.
5325
    pub fn range_start(&self) -> u16 {
5326
        let range = self.range_start_byte_range();
5327
        self.data.read_at(range.start).ok().unwrap()
5328
    }
5329
5330
    pub fn range_end(&self) -> u16 {
5331
        let range = self.range_end_byte_range();
5332
        self.data.read_at(range.start).ok().unwrap()
5333
    }
5334
5335
    pub fn design_size_byte_range(&self) -> Range<usize> {
5336
        let start = 0;
5337
        start..start + u16::RAW_BYTE_LEN
5338
    }
5339
5340
    pub fn identifier_byte_range(&self) -> Range<usize> {
5341
        let start = self.design_size_byte_range().end;
5342
        start..start + u16::RAW_BYTE_LEN
5343
    }
5344
5345
    pub fn name_entry_byte_range(&self) -> Range<usize> {
5346
        let start = self.identifier_byte_range().end;
5347
        start..start + u16::RAW_BYTE_LEN
5348
    }
5349
5350
    pub fn range_start_byte_range(&self) -> Range<usize> {
5351
        let start = self.name_entry_byte_range().end;
5352
        start..start + u16::RAW_BYTE_LEN
5353
    }
5354
5355
    pub fn range_end_byte_range(&self) -> Range<usize> {
5356
        let start = self.range_start_byte_range().end;
5357
        start..start + u16::RAW_BYTE_LEN
5358
    }
5359
}
5360
5361
#[cfg(feature = "experimental_traverse")]
5362
impl<'a> SomeTable<'a> for SizeParams<'a> {
5363
    fn type_name(&self) -> &str {
5364
        "SizeParams"
5365
    }
5366
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
5367
        match idx {
5368
            0usize => Some(Field::new("design_size", self.design_size())),
5369
            1usize => Some(Field::new("identifier", self.identifier())),
5370
            2usize => Some(Field::new("name_entry", self.name_entry())),
5371
            3usize => Some(Field::new("range_start", self.range_start())),
5372
            4usize => Some(Field::new("range_end", self.range_end())),
5373
            _ => None,
5374
        }
5375
    }
5376
}
5377
5378
#[cfg(feature = "experimental_traverse")]
5379
#[allow(clippy::needless_lifetimes)]
5380
impl<'a> std::fmt::Debug for SizeParams<'a> {
5381
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5382
        (self as &dyn SomeTable<'a>).fmt(f)
5383
    }
5384
}
5385
5386
impl<'a> MinByteRange<'a> for StylisticSetParams<'a> {
5387
    fn min_byte_range(&self) -> Range<usize> {
5388
        0..self.ui_name_id_byte_range().end
5389
    }
5390
    fn min_table_bytes(&self) -> &'a [u8] {
5391
        let range = self.min_byte_range();
5392
        self.data.as_bytes().get(range).unwrap_or_default()
5393
    }
5394
}
5395
5396
impl<'a> FontRead<'a> for StylisticSetParams<'a> {
5397
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
5398
        #[allow(clippy::absurd_extreme_comparisons)]
5399
        if data.len() < Self::MIN_SIZE {
5400
            return Err(ReadError::OutOfBounds);
5401
        }
5402
        Ok(Self { data })
5403
    }
5404
}
5405
5406
#[derive(Clone)]
5407
pub struct StylisticSetParams<'a> {
5408
    data: FontData<'a>,
5409
}
5410
5411
#[allow(clippy::needless_lifetimes)]
5412
impl<'a> StylisticSetParams<'a> {
5413
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + NameId::RAW_BYTE_LEN);
5414
    basic_table_impls!(impl_the_methods);
5415
5416
    pub fn version(&self) -> u16 {
5417
        let range = self.version_byte_range();
5418
        self.data.read_at(range.start).ok().unwrap()
5419
    }
5420
5421
    /// The 'name' table name ID that specifies a string (or strings, for
5422
    /// multiple languages) for a user-interface label for this feature.
5423
    ///
5424
    /// The value of uiLabelNameId is expected to be in the font-specific name
5425
    /// ID range (256-32767), though that is not a requirement in this Feature
5426
    /// Parameters specification. The user-interface label for the feature can
5427
    /// be provided in multiple languages. An English string should be included
5428
    /// as a fallback. The string should be kept to a minimal length to fit
5429
    /// comfortably with different application interfaces.
5430
    pub fn ui_name_id(&self) -> NameId {
5431
        let range = self.ui_name_id_byte_range();
5432
        self.data.read_at(range.start).ok().unwrap()
5433
    }
5434
5435
    pub fn version_byte_range(&self) -> Range<usize> {
5436
        let start = 0;
5437
        start..start + u16::RAW_BYTE_LEN
5438
    }
5439
5440
    pub fn ui_name_id_byte_range(&self) -> Range<usize> {
5441
        let start = self.version_byte_range().end;
5442
        start..start + NameId::RAW_BYTE_LEN
5443
    }
5444
}
5445
5446
#[cfg(feature = "experimental_traverse")]
5447
impl<'a> SomeTable<'a> for StylisticSetParams<'a> {
5448
    fn type_name(&self) -> &str {
5449
        "StylisticSetParams"
5450
    }
5451
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
5452
        match idx {
5453
            0usize => Some(Field::new("version", self.version())),
5454
            1usize => Some(Field::new("ui_name_id", self.ui_name_id())),
5455
            _ => None,
5456
        }
5457
    }
5458
}
5459
5460
#[cfg(feature = "experimental_traverse")]
5461
#[allow(clippy::needless_lifetimes)]
5462
impl<'a> std::fmt::Debug for StylisticSetParams<'a> {
5463
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5464
        (self as &dyn SomeTable<'a>).fmt(f)
5465
    }
5466
}
5467
5468
impl Format<u16> for CharacterVariantParams<'_> {
5469
    const FORMAT: u16 = 0;
5470
}
5471
5472
impl<'a> MinByteRange<'a> for CharacterVariantParams<'a> {
5473
    fn min_byte_range(&self) -> Range<usize> {
5474
        0..self.character_byte_range().end
5475
    }
5476
    fn min_table_bytes(&self) -> &'a [u8] {
5477
        let range = self.min_byte_range();
5478
        self.data.as_bytes().get(range).unwrap_or_default()
5479
    }
5480
}
5481
5482
impl<'a> FontRead<'a> for CharacterVariantParams<'a> {
5483
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
5484
        #[allow(clippy::absurd_extreme_comparisons)]
5485
        if data.len() < Self::MIN_SIZE {
5486
            return Err(ReadError::OutOfBounds);
5487
        }
5488
        Ok(Self { data })
5489
    }
5490
}
5491
5492
/// featureParams for ['cv01'-'cv99'](https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae#cv01-cv99)
5493
#[derive(Clone)]
5494
pub struct CharacterVariantParams<'a> {
5495
    data: FontData<'a>,
5496
}
5497
5498
#[allow(clippy::needless_lifetimes)]
5499
impl<'a> CharacterVariantParams<'a> {
5500
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
5501
        + NameId::RAW_BYTE_LEN
5502
        + NameId::RAW_BYTE_LEN
5503
        + NameId::RAW_BYTE_LEN
5504
        + u16::RAW_BYTE_LEN
5505
        + NameId::RAW_BYTE_LEN
5506
        + u16::RAW_BYTE_LEN);
5507
    basic_table_impls!(impl_the_methods);
5508
5509
    /// Format number is set to 0.
5510
    pub fn format(&self) -> u16 {
5511
        let range = self.format_byte_range();
5512
        self.data.read_at(range.start).ok().unwrap()
5513
    }
5514
5515
    /// The 'name' table name ID that specifies a string (or strings,
5516
    /// for multiple languages) for a user-interface label for this
5517
    /// feature. (May be NULL.)
5518
    pub fn feat_ui_label_name_id(&self) -> NameId {
5519
        let range = self.feat_ui_label_name_id_byte_range();
5520
        self.data.read_at(range.start).ok().unwrap()
5521
    }
5522
5523
    /// The 'name' table name ID that specifies a string (or strings,
5524
    /// for multiple languages) that an application can use for tooltip
5525
    /// text for this feature. (May be NULL.)
5526
    pub fn feat_ui_tooltip_text_name_id(&self) -> NameId {
5527
        let range = self.feat_ui_tooltip_text_name_id_byte_range();
5528
        self.data.read_at(range.start).ok().unwrap()
5529
    }
5530
5531
    /// The 'name' table name ID that specifies sample text that
5532
    /// illustrates the effect of this feature. (May be NULL.)
5533
    pub fn sample_text_name_id(&self) -> NameId {
5534
        let range = self.sample_text_name_id_byte_range();
5535
        self.data.read_at(range.start).ok().unwrap()
5536
    }
5537
5538
    /// Number of named parameters. (May be zero.)
5539
    pub fn num_named_parameters(&self) -> u16 {
5540
        let range = self.num_named_parameters_byte_range();
5541
        self.data.read_at(range.start).ok().unwrap()
5542
    }
5543
5544
    /// The first 'name' table name ID used to specify strings for
5545
    /// user-interface labels for the feature parameters. (Must be zero
5546
    /// if numParameters is zero.)
5547
    pub fn first_param_ui_label_name_id(&self) -> NameId {
5548
        let range = self.first_param_ui_label_name_id_byte_range();
5549
        self.data.read_at(range.start).ok().unwrap()
5550
    }
5551
5552
    /// The count of characters for which this feature provides glyph
5553
    /// variants. (May be zero.)
5554
    pub fn char_count(&self) -> u16 {
5555
        let range = self.char_count_byte_range();
5556
        self.data.read_at(range.start).ok().unwrap()
5557
    }
5558
5559
    /// The Unicode Scalar Value of the characters for which this
5560
    /// feature provides glyph variants.
5561
    pub fn character(&self) -> &'a [BigEndian<Uint24>] {
5562
        let range = self.character_byte_range();
5563
        self.data.read_array(range).ok().unwrap_or_default()
5564
    }
5565
5566
    pub fn format_byte_range(&self) -> Range<usize> {
5567
        let start = 0;
5568
        start..start + u16::RAW_BYTE_LEN
5569
    }
5570
5571
    pub fn feat_ui_label_name_id_byte_range(&self) -> Range<usize> {
5572
        let start = self.format_byte_range().end;
5573
        start..start + NameId::RAW_BYTE_LEN
5574
    }
5575
5576
    pub fn feat_ui_tooltip_text_name_id_byte_range(&self) -> Range<usize> {
5577
        let start = self.feat_ui_label_name_id_byte_range().end;
5578
        start..start + NameId::RAW_BYTE_LEN
5579
    }
5580
5581
    pub fn sample_text_name_id_byte_range(&self) -> Range<usize> {
5582
        let start = self.feat_ui_tooltip_text_name_id_byte_range().end;
5583
        start..start + NameId::RAW_BYTE_LEN
5584
    }
5585
5586
    pub fn num_named_parameters_byte_range(&self) -> Range<usize> {
5587
        let start = self.sample_text_name_id_byte_range().end;
5588
        start..start + u16::RAW_BYTE_LEN
5589
    }
5590
5591
    pub fn first_param_ui_label_name_id_byte_range(&self) -> Range<usize> {
5592
        let start = self.num_named_parameters_byte_range().end;
5593
        start..start + NameId::RAW_BYTE_LEN
5594
    }
5595
5596
    pub fn char_count_byte_range(&self) -> Range<usize> {
5597
        let start = self.first_param_ui_label_name_id_byte_range().end;
5598
        start..start + u16::RAW_BYTE_LEN
5599
    }
5600
5601
    pub fn character_byte_range(&self) -> Range<usize> {
5602
        let char_count = self.char_count();
5603
        let start = self.char_count_byte_range().end;
5604
        start..start + (char_count as usize).saturating_mul(Uint24::RAW_BYTE_LEN)
5605
    }
5606
}
5607
5608
#[cfg(feature = "experimental_traverse")]
5609
impl<'a> SomeTable<'a> for CharacterVariantParams<'a> {
5610
    fn type_name(&self) -> &str {
5611
        "CharacterVariantParams"
5612
    }
5613
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
5614
        match idx {
5615
            0usize => Some(Field::new("format", self.format())),
5616
            1usize => Some(Field::new(
5617
                "feat_ui_label_name_id",
5618
                self.feat_ui_label_name_id(),
5619
            )),
5620
            2usize => Some(Field::new(
5621
                "feat_ui_tooltip_text_name_id",
5622
                self.feat_ui_tooltip_text_name_id(),
5623
            )),
5624
            3usize => Some(Field::new(
5625
                "sample_text_name_id",
5626
                self.sample_text_name_id(),
5627
            )),
5628
            4usize => Some(Field::new(
5629
                "num_named_parameters",
5630
                self.num_named_parameters(),
5631
            )),
5632
            5usize => Some(Field::new(
5633
                "first_param_ui_label_name_id",
5634
                self.first_param_ui_label_name_id(),
5635
            )),
5636
            6usize => Some(Field::new("char_count", self.char_count())),
5637
            7usize => Some(Field::new("character", self.character())),
5638
            _ => None,
5639
        }
5640
    }
5641
}
5642
5643
#[cfg(feature = "experimental_traverse")]
5644
#[allow(clippy::needless_lifetimes)]
5645
impl<'a> std::fmt::Debug for CharacterVariantParams<'a> {
5646
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5647
        (self as &dyn SomeTable<'a>).fmt(f)
5648
    }
5649
}