Skip to main content

read_fonts/generated/
generated_base.rs

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)]
6use crate::codegen_prelude::*;
7
8impl<'a> MinByteRange<'a> for Base<'a> {
9    fn min_byte_range(&self) -> Range<usize> {
10        0..self.vert_axis_offset_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
18impl TopLevelTable for Base<'_> {
19    /// `BASE`
20    const TAG: Tag = Tag::new(b"BASE");
21}
22
23impl<'a> FontRead<'a> for Base<'a> {
24    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
25        #[allow(clippy::absurd_extreme_comparisons)]
26        if data.len() < Self::MIN_SIZE {
27            return Err(ReadError::OutOfBounds);
28        }
29        Ok(Self { data })
30    }
31}
32
33/// The [BASE](https://learn.microsoft.com/en-us/typography/opentype/spec/base) (Baseline) table
34#[derive(Clone)]
35pub struct Base<'a> {
36    data: FontData<'a>,
37}
38
39#[allow(clippy::needless_lifetimes)]
40impl<'a> Base<'a> {
41    pub const MIN_SIZE: usize =
42        (MajorMinor::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN);
43    basic_table_impls!(impl_the_methods);
44
45    /// (major, minor) Version for the BASE table (1,0) or (1,1)
46    pub fn version(&self) -> MajorMinor {
47        let range = self.version_byte_range();
48        self.data.read_at(range.start).ok().unwrap()
49    }
50
51    /// Offset to horizontal Axis table, from beginning of BASE table (may be NULL)
52    pub fn horiz_axis_offset(&self) -> Nullable<Offset16> {
53        let range = self.horiz_axis_offset_byte_range();
54        self.data.read_at(range.start).ok().unwrap()
55    }
56
57    /// Attempt to resolve [`horiz_axis_offset`][Self::horiz_axis_offset].
58    pub fn horiz_axis(&self) -> Option<Result<Axis<'a>, ReadError>> {
59        let data = self.data;
60        self.horiz_axis_offset().resolve(data)
61    }
62
63    /// Offset to vertical Axis table, from beginning of BASE table (may be NULL)
64    pub fn vert_axis_offset(&self) -> Nullable<Offset16> {
65        let range = self.vert_axis_offset_byte_range();
66        self.data.read_at(range.start).ok().unwrap()
67    }
68
69    /// Attempt to resolve [`vert_axis_offset`][Self::vert_axis_offset].
70    pub fn vert_axis(&self) -> Option<Result<Axis<'a>, ReadError>> {
71        let data = self.data;
72        self.vert_axis_offset().resolve(data)
73    }
74
75    /// Offset to Item Variation Store table, from beginning of BASE table (may be null)
76    pub fn item_var_store_offset(&self) -> Option<Nullable<Offset32>> {
77        let range = self.item_var_store_offset_byte_range();
78        (!range.is_empty())
79            .then(|| self.data.read_at(range.start).ok())
80            .flatten()
81    }
82
83    /// Attempt to resolve [`item_var_store_offset`][Self::item_var_store_offset].
84    pub fn item_var_store(&self) -> Option<Result<ItemVariationStore<'a>, ReadError>> {
85        let data = self.data;
86        self.item_var_store_offset().map(|x| x.resolve(data))?
87    }
88
89    pub fn version_byte_range(&self) -> Range<usize> {
90        let start = 0;
91        start..start + MajorMinor::RAW_BYTE_LEN
92    }
93
94    pub fn horiz_axis_offset_byte_range(&self) -> Range<usize> {
95        let start = self.version_byte_range().end;
96        start..start + Offset16::RAW_BYTE_LEN
97    }
98
99    pub fn vert_axis_offset_byte_range(&self) -> Range<usize> {
100        let start = self.horiz_axis_offset_byte_range().end;
101        start..start + Offset16::RAW_BYTE_LEN
102    }
103
104    pub fn item_var_store_offset_byte_range(&self) -> Range<usize> {
105        let start = self.vert_axis_offset_byte_range().end;
106        start
107            ..(self.version().compatible((1u16, 1u16)))
108                .then(|| start + Offset32::RAW_BYTE_LEN)
109                .unwrap_or(start)
110    }
111}
112
113const _: () = assert!(FontData::default_data_long_enough(Base::MIN_SIZE));
114
115impl Default for Base<'_> {
116    fn default() -> Self {
117        Self {
118            data: FontData::default_table_data(),
119        }
120    }
121}
122
123#[cfg(feature = "experimental_traverse")]
124impl<'a> SomeTable<'a> for Base<'a> {
125    fn type_name(&self) -> &str {
126        "Base"
127    }
128    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
129        match idx {
130            0usize => Some(Field::new("version", self.version())),
131            1usize => Some(Field::new(
132                "horiz_axis_offset",
133                FieldType::offset(self.horiz_axis_offset(), self.horiz_axis()),
134            )),
135            2usize => Some(Field::new(
136                "vert_axis_offset",
137                FieldType::offset(self.vert_axis_offset(), self.vert_axis()),
138            )),
139            3usize if self.version().compatible((1u16, 1u16)) => Some(Field::new(
140                "item_var_store_offset",
141                FieldType::offset(self.item_var_store_offset().unwrap(), self.item_var_store()),
142            )),
143            _ => None,
144        }
145    }
146}
147
148#[cfg(feature = "experimental_traverse")]
149#[allow(clippy::needless_lifetimes)]
150impl<'a> std::fmt::Debug for Base<'a> {
151    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152        (self as &dyn SomeTable<'a>).fmt(f)
153    }
154}
155
156impl<'a> MinByteRange<'a> for Axis<'a> {
157    fn min_byte_range(&self) -> Range<usize> {
158        0..self.base_script_list_offset_byte_range().end
159    }
160    fn min_table_bytes(&self) -> &'a [u8] {
161        let range = self.min_byte_range();
162        self.data.as_bytes().get(range).unwrap_or_default()
163    }
164}
165
166impl<'a> FontRead<'a> for Axis<'a> {
167    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
168        #[allow(clippy::absurd_extreme_comparisons)]
169        if data.len() < Self::MIN_SIZE {
170            return Err(ReadError::OutOfBounds);
171        }
172        Ok(Self { data })
173    }
174}
175
176/// [Axis Table](https://learn.microsoft.com/en-us/typography/opentype/spec/base#axis-tables-horizaxis-and-vertaxis)
177#[derive(Clone)]
178pub struct Axis<'a> {
179    data: FontData<'a>,
180}
181
182#[allow(clippy::needless_lifetimes)]
183impl<'a> Axis<'a> {
184    pub const MIN_SIZE: usize = (Offset16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN);
185    basic_table_impls!(impl_the_methods);
186
187    /// Offset to BaseTagList table, from beginning of Axis table (may
188    /// be NULL)
189    pub fn base_tag_list_offset(&self) -> Nullable<Offset16> {
190        let range = self.base_tag_list_offset_byte_range();
191        self.data.read_at(range.start).ok().unwrap()
192    }
193
194    /// Attempt to resolve [`base_tag_list_offset`][Self::base_tag_list_offset].
195    pub fn base_tag_list(&self) -> Option<Result<BaseTagList<'a>, ReadError>> {
196        let data = self.data;
197        self.base_tag_list_offset().resolve(data)
198    }
199
200    /// Offset to BaseScriptList table, from beginning of Axis table
201    pub fn base_script_list_offset(&self) -> Offset16 {
202        let range = self.base_script_list_offset_byte_range();
203        self.data.read_at(range.start).ok().unwrap()
204    }
205
206    /// Attempt to resolve [`base_script_list_offset`][Self::base_script_list_offset].
207    pub fn base_script_list(&self) -> Result<BaseScriptList<'a>, ReadError> {
208        let data = self.data;
209        self.base_script_list_offset().resolve(data)
210    }
211
212    pub fn base_tag_list_offset_byte_range(&self) -> Range<usize> {
213        let start = 0;
214        start..start + Offset16::RAW_BYTE_LEN
215    }
216
217    pub fn base_script_list_offset_byte_range(&self) -> Range<usize> {
218        let start = self.base_tag_list_offset_byte_range().end;
219        start..start + Offset16::RAW_BYTE_LEN
220    }
221}
222
223const _: () = assert!(FontData::default_data_long_enough(Axis::MIN_SIZE));
224
225impl Default for Axis<'_> {
226    fn default() -> Self {
227        Self {
228            data: FontData::default_table_data(),
229        }
230    }
231}
232
233#[cfg(feature = "experimental_traverse")]
234impl<'a> SomeTable<'a> for Axis<'a> {
235    fn type_name(&self) -> &str {
236        "Axis"
237    }
238    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
239        match idx {
240            0usize => Some(Field::new(
241                "base_tag_list_offset",
242                FieldType::offset(self.base_tag_list_offset(), self.base_tag_list()),
243            )),
244            1usize => Some(Field::new(
245                "base_script_list_offset",
246                FieldType::offset(self.base_script_list_offset(), self.base_script_list()),
247            )),
248            _ => None,
249        }
250    }
251}
252
253#[cfg(feature = "experimental_traverse")]
254#[allow(clippy::needless_lifetimes)]
255impl<'a> std::fmt::Debug for Axis<'a> {
256    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
257        (self as &dyn SomeTable<'a>).fmt(f)
258    }
259}
260
261impl<'a> MinByteRange<'a> for BaseTagList<'a> {
262    fn min_byte_range(&self) -> Range<usize> {
263        0..self.baseline_tags_byte_range().end
264    }
265    fn min_table_bytes(&self) -> &'a [u8] {
266        let range = self.min_byte_range();
267        self.data.as_bytes().get(range).unwrap_or_default()
268    }
269}
270
271impl<'a> FontRead<'a> for BaseTagList<'a> {
272    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
273        #[allow(clippy::absurd_extreme_comparisons)]
274        if data.len() < Self::MIN_SIZE {
275            return Err(ReadError::OutOfBounds);
276        }
277        Ok(Self { data })
278    }
279}
280
281/// [BaseTagList Table](https://learn.microsoft.com/en-us/typography/opentype/spec/base#basetaglist-table)
282#[derive(Clone)]
283pub struct BaseTagList<'a> {
284    data: FontData<'a>,
285}
286
287#[allow(clippy::needless_lifetimes)]
288impl<'a> BaseTagList<'a> {
289    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
290    basic_table_impls!(impl_the_methods);
291
292    /// Number of baseline identification tags in this text direction
293    /// — may be zero (0)
294    pub fn base_tag_count(&self) -> u16 {
295        let range = self.base_tag_count_byte_range();
296        self.data.read_at(range.start).ok().unwrap()
297    }
298
299    /// Array of 4-byte baseline identification tags — must be in
300    /// alphabetical order
301    pub fn baseline_tags(&self) -> &'a [BigEndian<Tag>] {
302        let range = self.baseline_tags_byte_range();
303        self.data.read_array(range).ok().unwrap_or_default()
304    }
305
306    pub fn base_tag_count_byte_range(&self) -> Range<usize> {
307        let start = 0;
308        start..start + u16::RAW_BYTE_LEN
309    }
310
311    pub fn baseline_tags_byte_range(&self) -> Range<usize> {
312        let base_tag_count = self.base_tag_count();
313        let start = self.base_tag_count_byte_range().end;
314        start..start + (transforms::to_usize(base_tag_count)).saturating_mul(Tag::RAW_BYTE_LEN)
315    }
316}
317
318const _: () = assert!(FontData::default_data_long_enough(BaseTagList::MIN_SIZE));
319
320impl Default for BaseTagList<'_> {
321    fn default() -> Self {
322        Self {
323            data: FontData::default_table_data(),
324        }
325    }
326}
327
328#[cfg(feature = "experimental_traverse")]
329impl<'a> SomeTable<'a> for BaseTagList<'a> {
330    fn type_name(&self) -> &str {
331        "BaseTagList"
332    }
333    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
334        match idx {
335            0usize => Some(Field::new("base_tag_count", self.base_tag_count())),
336            1usize => Some(Field::new("baseline_tags", self.baseline_tags())),
337            _ => None,
338        }
339    }
340}
341
342#[cfg(feature = "experimental_traverse")]
343#[allow(clippy::needless_lifetimes)]
344impl<'a> std::fmt::Debug for BaseTagList<'a> {
345    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
346        (self as &dyn SomeTable<'a>).fmt(f)
347    }
348}
349
350impl<'a> MinByteRange<'a> for BaseScriptList<'a> {
351    fn min_byte_range(&self) -> Range<usize> {
352        0..self.base_script_records_byte_range().end
353    }
354    fn min_table_bytes(&self) -> &'a [u8] {
355        let range = self.min_byte_range();
356        self.data.as_bytes().get(range).unwrap_or_default()
357    }
358}
359
360impl<'a> FontRead<'a> for BaseScriptList<'a> {
361    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
362        #[allow(clippy::absurd_extreme_comparisons)]
363        if data.len() < Self::MIN_SIZE {
364            return Err(ReadError::OutOfBounds);
365        }
366        Ok(Self { data })
367    }
368}
369
370/// [BaseScriptList Table](https://learn.microsoft.com/en-us/typography/opentype/spec/base#basescriptlist-table)
371#[derive(Clone)]
372pub struct BaseScriptList<'a> {
373    data: FontData<'a>,
374}
375
376#[allow(clippy::needless_lifetimes)]
377impl<'a> BaseScriptList<'a> {
378    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
379    basic_table_impls!(impl_the_methods);
380
381    /// Number of BaseScriptRecords defined
382    pub fn base_script_count(&self) -> u16 {
383        let range = self.base_script_count_byte_range();
384        self.data.read_at(range.start).ok().unwrap()
385    }
386
387    /// Array of BaseScriptRecords, in alphabetical order by
388    /// baseScriptTag
389    pub fn base_script_records(&self) -> &'a [BaseScriptRecord] {
390        let range = self.base_script_records_byte_range();
391        self.data.read_array(range).ok().unwrap_or_default()
392    }
393
394    pub fn base_script_count_byte_range(&self) -> Range<usize> {
395        let start = 0;
396        start..start + u16::RAW_BYTE_LEN
397    }
398
399    pub fn base_script_records_byte_range(&self) -> Range<usize> {
400        let base_script_count = self.base_script_count();
401        let start = self.base_script_count_byte_range().end;
402        start
403            ..start
404                + (transforms::to_usize(base_script_count))
405                    .saturating_mul(BaseScriptRecord::RAW_BYTE_LEN)
406    }
407}
408
409const _: () = assert!(FontData::default_data_long_enough(BaseScriptList::MIN_SIZE));
410
411impl Default for BaseScriptList<'_> {
412    fn default() -> Self {
413        Self {
414            data: FontData::default_table_data(),
415        }
416    }
417}
418
419#[cfg(feature = "experimental_traverse")]
420impl<'a> SomeTable<'a> for BaseScriptList<'a> {
421    fn type_name(&self) -> &str {
422        "BaseScriptList"
423    }
424    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
425        match idx {
426            0usize => Some(Field::new("base_script_count", self.base_script_count())),
427            1usize => Some(Field::new(
428                "base_script_records",
429                traversal::FieldType::array_of_records(
430                    stringify!(BaseScriptRecord),
431                    self.base_script_records(),
432                    self.offset_data(),
433                ),
434            )),
435            _ => None,
436        }
437    }
438}
439
440#[cfg(feature = "experimental_traverse")]
441#[allow(clippy::needless_lifetimes)]
442impl<'a> std::fmt::Debug for BaseScriptList<'a> {
443    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
444        (self as &dyn SomeTable<'a>).fmt(f)
445    }
446}
447
448/// [BaseScriptRecord](https://learn.microsoft.com/en-us/typography/opentype/spec/base#basescriptrecord)
449#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
450#[repr(C)]
451#[repr(packed)]
452pub struct BaseScriptRecord {
453    /// 4-byte script identification tag
454    pub base_script_tag: BigEndian<Tag>,
455    /// Offset to BaseScript table, from beginning of BaseScriptList
456    pub base_script_offset: BigEndian<Offset16>,
457}
458
459impl BaseScriptRecord {
460    /// 4-byte script identification tag
461    pub fn base_script_tag(&self) -> Tag {
462        self.base_script_tag.get()
463    }
464
465    /// Offset to BaseScript table, from beginning of BaseScriptList
466    pub fn base_script_offset(&self) -> Offset16 {
467        self.base_script_offset.get()
468    }
469
470    /// Offset to BaseScript table, from beginning of BaseScriptList
471    ///
472    /// The `data` argument should be retrieved from the parent table
473    /// By calling its `offset_data` method.
474    pub fn base_script<'a>(&self, data: FontData<'a>) -> Result<BaseScript<'a>, ReadError> {
475        self.base_script_offset().resolve(data)
476    }
477}
478
479impl FixedSize for BaseScriptRecord {
480    const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
481}
482
483#[cfg(feature = "experimental_traverse")]
484impl<'a> SomeRecord<'a> for BaseScriptRecord {
485    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
486        RecordResolver {
487            name: "BaseScriptRecord",
488            get_field: Box::new(move |idx, _data| match idx {
489                0usize => Some(Field::new("base_script_tag", self.base_script_tag())),
490                1usize => Some(Field::new(
491                    "base_script_offset",
492                    FieldType::offset(self.base_script_offset(), self.base_script(_data)),
493                )),
494                _ => None,
495            }),
496            data,
497        }
498    }
499}
500
501impl<'a> MinByteRange<'a> for BaseScript<'a> {
502    fn min_byte_range(&self) -> Range<usize> {
503        0..self.base_lang_sys_records_byte_range().end
504    }
505    fn min_table_bytes(&self) -> &'a [u8] {
506        let range = self.min_byte_range();
507        self.data.as_bytes().get(range).unwrap_or_default()
508    }
509}
510
511impl<'a> FontRead<'a> for BaseScript<'a> {
512    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
513        #[allow(clippy::absurd_extreme_comparisons)]
514        if data.len() < Self::MIN_SIZE {
515            return Err(ReadError::OutOfBounds);
516        }
517        Ok(Self { data })
518    }
519}
520
521/// [BaseScript Table](https://learn.microsoft.com/en-us/typography/opentype/spec/base#basescript-table)
522#[derive(Clone)]
523pub struct BaseScript<'a> {
524    data: FontData<'a>,
525}
526
527#[allow(clippy::needless_lifetimes)]
528impl<'a> BaseScript<'a> {
529    pub const MIN_SIZE: usize =
530        (Offset16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
531    basic_table_impls!(impl_the_methods);
532
533    /// Offset to BaseValues table, from beginning of BaseScript table (may be NULL)
534    pub fn base_values_offset(&self) -> Nullable<Offset16> {
535        let range = self.base_values_offset_byte_range();
536        self.data.read_at(range.start).ok().unwrap()
537    }
538
539    /// Attempt to resolve [`base_values_offset`][Self::base_values_offset].
540    pub fn base_values(&self) -> Option<Result<BaseValues<'a>, ReadError>> {
541        let data = self.data;
542        self.base_values_offset().resolve(data)
543    }
544
545    /// Offset to MinMax table, from beginning of BaseScript table (may be NULL)
546    pub fn default_min_max_offset(&self) -> Nullable<Offset16> {
547        let range = self.default_min_max_offset_byte_range();
548        self.data.read_at(range.start).ok().unwrap()
549    }
550
551    /// Attempt to resolve [`default_min_max_offset`][Self::default_min_max_offset].
552    pub fn default_min_max(&self) -> Option<Result<MinMax<'a>, ReadError>> {
553        let data = self.data;
554        self.default_min_max_offset().resolve(data)
555    }
556
557    /// Number of BaseLangSysRecords defined — may be zero (0)
558    pub fn base_lang_sys_count(&self) -> u16 {
559        let range = self.base_lang_sys_count_byte_range();
560        self.data.read_at(range.start).ok().unwrap()
561    }
562
563    /// Array of BaseLangSysRecords, in alphabetical order by
564    /// BaseLangSysTag
565    pub fn base_lang_sys_records(&self) -> &'a [BaseLangSysRecord] {
566        let range = self.base_lang_sys_records_byte_range();
567        self.data.read_array(range).ok().unwrap_or_default()
568    }
569
570    pub fn base_values_offset_byte_range(&self) -> Range<usize> {
571        let start = 0;
572        start..start + Offset16::RAW_BYTE_LEN
573    }
574
575    pub fn default_min_max_offset_byte_range(&self) -> Range<usize> {
576        let start = self.base_values_offset_byte_range().end;
577        start..start + Offset16::RAW_BYTE_LEN
578    }
579
580    pub fn base_lang_sys_count_byte_range(&self) -> Range<usize> {
581        let start = self.default_min_max_offset_byte_range().end;
582        start..start + u16::RAW_BYTE_LEN
583    }
584
585    pub fn base_lang_sys_records_byte_range(&self) -> Range<usize> {
586        let base_lang_sys_count = self.base_lang_sys_count();
587        let start = self.base_lang_sys_count_byte_range().end;
588        start
589            ..start
590                + (transforms::to_usize(base_lang_sys_count))
591                    .saturating_mul(BaseLangSysRecord::RAW_BYTE_LEN)
592    }
593}
594
595const _: () = assert!(FontData::default_data_long_enough(BaseScript::MIN_SIZE));
596
597impl Default for BaseScript<'_> {
598    fn default() -> Self {
599        Self {
600            data: FontData::default_table_data(),
601        }
602    }
603}
604
605#[cfg(feature = "experimental_traverse")]
606impl<'a> SomeTable<'a> for BaseScript<'a> {
607    fn type_name(&self) -> &str {
608        "BaseScript"
609    }
610    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
611        match idx {
612            0usize => Some(Field::new(
613                "base_values_offset",
614                FieldType::offset(self.base_values_offset(), self.base_values()),
615            )),
616            1usize => Some(Field::new(
617                "default_min_max_offset",
618                FieldType::offset(self.default_min_max_offset(), self.default_min_max()),
619            )),
620            2usize => Some(Field::new(
621                "base_lang_sys_count",
622                self.base_lang_sys_count(),
623            )),
624            3usize => Some(Field::new(
625                "base_lang_sys_records",
626                traversal::FieldType::array_of_records(
627                    stringify!(BaseLangSysRecord),
628                    self.base_lang_sys_records(),
629                    self.offset_data(),
630                ),
631            )),
632            _ => None,
633        }
634    }
635}
636
637#[cfg(feature = "experimental_traverse")]
638#[allow(clippy::needless_lifetimes)]
639impl<'a> std::fmt::Debug for BaseScript<'a> {
640    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
641        (self as &dyn SomeTable<'a>).fmt(f)
642    }
643}
644
645/// [BaseLangSysRecord](https://learn.microsoft.com/en-us/typography/opentype/spec/base#baselangsysrecord)
646#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
647#[repr(C)]
648#[repr(packed)]
649pub struct BaseLangSysRecord {
650    /// 4-byte language system identification tag
651    pub base_lang_sys_tag: BigEndian<Tag>,
652    /// Offset to MinMax table, from beginning of BaseScript table
653    pub min_max_offset: BigEndian<Offset16>,
654}
655
656impl BaseLangSysRecord {
657    /// 4-byte language system identification tag
658    pub fn base_lang_sys_tag(&self) -> Tag {
659        self.base_lang_sys_tag.get()
660    }
661
662    /// Offset to MinMax table, from beginning of BaseScript table
663    pub fn min_max_offset(&self) -> Offset16 {
664        self.min_max_offset.get()
665    }
666
667    /// Offset to MinMax table, from beginning of BaseScript table
668    ///
669    /// The `data` argument should be retrieved from the parent table
670    /// By calling its `offset_data` method.
671    pub fn min_max<'a>(&self, data: FontData<'a>) -> Result<MinMax<'a>, ReadError> {
672        self.min_max_offset().resolve(data)
673    }
674}
675
676impl FixedSize for BaseLangSysRecord {
677    const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
678}
679
680#[cfg(feature = "experimental_traverse")]
681impl<'a> SomeRecord<'a> for BaseLangSysRecord {
682    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
683        RecordResolver {
684            name: "BaseLangSysRecord",
685            get_field: Box::new(move |idx, _data| match idx {
686                0usize => Some(Field::new("base_lang_sys_tag", self.base_lang_sys_tag())),
687                1usize => Some(Field::new(
688                    "min_max_offset",
689                    FieldType::offset(self.min_max_offset(), self.min_max(_data)),
690                )),
691                _ => None,
692            }),
693            data,
694        }
695    }
696}
697
698impl<'a> MinByteRange<'a> for BaseValues<'a> {
699    fn min_byte_range(&self) -> Range<usize> {
700        0..self.base_coord_offsets_byte_range().end
701    }
702    fn min_table_bytes(&self) -> &'a [u8] {
703        let range = self.min_byte_range();
704        self.data.as_bytes().get(range).unwrap_or_default()
705    }
706}
707
708impl<'a> FontRead<'a> for BaseValues<'a> {
709    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
710        #[allow(clippy::absurd_extreme_comparisons)]
711        if data.len() < Self::MIN_SIZE {
712            return Err(ReadError::OutOfBounds);
713        }
714        Ok(Self { data })
715    }
716}
717
718/// [BaseValues](https://learn.microsoft.com/en-us/typography/opentype/spec/base#basevalues-table) table
719#[derive(Clone)]
720pub struct BaseValues<'a> {
721    data: FontData<'a>,
722}
723
724#[allow(clippy::needless_lifetimes)]
725impl<'a> BaseValues<'a> {
726    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
727    basic_table_impls!(impl_the_methods);
728
729    /// Index number of default baseline for this script — equals
730    /// index position of baseline tag in baselineTags array of the
731    /// BaseTagList
732    pub fn default_baseline_index(&self) -> u16 {
733        let range = self.default_baseline_index_byte_range();
734        self.data.read_at(range.start).ok().unwrap()
735    }
736
737    /// Number of BaseCoord tables defined — should equal
738    /// baseTagCount in the BaseTagList
739    pub fn base_coord_count(&self) -> u16 {
740        let range = self.base_coord_count_byte_range();
741        self.data.read_at(range.start).ok().unwrap()
742    }
743
744    /// Array of offsets to BaseCoord tables, from beginning of
745    /// BaseValues table — order matches baselineTags array in the
746    /// BaseTagList
747    pub fn base_coord_offsets(&self) -> &'a [BigEndian<Offset16>] {
748        let range = self.base_coord_offsets_byte_range();
749        self.data.read_array(range).ok().unwrap_or_default()
750    }
751
752    /// A dynamically resolving wrapper for [`base_coord_offsets`][Self::base_coord_offsets].
753    pub fn base_coords(&self) -> ArrayOfOffsets<'a, BaseCoord<'a>, Offset16> {
754        let data = self.data;
755        let offsets = self.base_coord_offsets();
756        ArrayOfOffsets::new(offsets, data, ())
757    }
758
759    pub fn default_baseline_index_byte_range(&self) -> Range<usize> {
760        let start = 0;
761        start..start + u16::RAW_BYTE_LEN
762    }
763
764    pub fn base_coord_count_byte_range(&self) -> Range<usize> {
765        let start = self.default_baseline_index_byte_range().end;
766        start..start + u16::RAW_BYTE_LEN
767    }
768
769    pub fn base_coord_offsets_byte_range(&self) -> Range<usize> {
770        let base_coord_count = self.base_coord_count();
771        let start = self.base_coord_count_byte_range().end;
772        start
773            ..start
774                + (transforms::to_usize(base_coord_count)).saturating_mul(Offset16::RAW_BYTE_LEN)
775    }
776}
777
778const _: () = assert!(FontData::default_data_long_enough(BaseValues::MIN_SIZE));
779
780impl Default for BaseValues<'_> {
781    fn default() -> Self {
782        Self {
783            data: FontData::default_table_data(),
784        }
785    }
786}
787
788#[cfg(feature = "experimental_traverse")]
789impl<'a> SomeTable<'a> for BaseValues<'a> {
790    fn type_name(&self) -> &str {
791        "BaseValues"
792    }
793    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
794        match idx {
795            0usize => Some(Field::new(
796                "default_baseline_index",
797                self.default_baseline_index(),
798            )),
799            1usize => Some(Field::new("base_coord_count", self.base_coord_count())),
800            2usize => Some(Field::new(
801                "base_coord_offsets",
802                FieldType::from(self.base_coords()),
803            )),
804            _ => None,
805        }
806    }
807}
808
809#[cfg(feature = "experimental_traverse")]
810#[allow(clippy::needless_lifetimes)]
811impl<'a> std::fmt::Debug for BaseValues<'a> {
812    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
813        (self as &dyn SomeTable<'a>).fmt(f)
814    }
815}
816
817impl<'a> MinByteRange<'a> for MinMax<'a> {
818    fn min_byte_range(&self) -> Range<usize> {
819        0..self.feat_min_max_records_byte_range().end
820    }
821    fn min_table_bytes(&self) -> &'a [u8] {
822        let range = self.min_byte_range();
823        self.data.as_bytes().get(range).unwrap_or_default()
824    }
825}
826
827impl<'a> FontRead<'a> for MinMax<'a> {
828    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
829        #[allow(clippy::absurd_extreme_comparisons)]
830        if data.len() < Self::MIN_SIZE {
831            return Err(ReadError::OutOfBounds);
832        }
833        Ok(Self { data })
834    }
835}
836
837/// [MinMax](https://learn.microsoft.com/en-us/typography/opentype/spec/base#minmax-table) table
838#[derive(Clone)]
839pub struct MinMax<'a> {
840    data: FontData<'a>,
841}
842
843#[allow(clippy::needless_lifetimes)]
844impl<'a> MinMax<'a> {
845    pub const MIN_SIZE: usize =
846        (Offset16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
847    basic_table_impls!(impl_the_methods);
848
849    /// Offset to BaseCoord table that defines the minimum extent
850    /// value, from the beginning of MinMax table (may be NULL)
851    pub fn min_coord_offset(&self) -> Nullable<Offset16> {
852        let range = self.min_coord_offset_byte_range();
853        self.data.read_at(range.start).ok().unwrap()
854    }
855
856    /// Attempt to resolve [`min_coord_offset`][Self::min_coord_offset].
857    pub fn min_coord(&self) -> Option<Result<BaseCoord<'a>, ReadError>> {
858        let data = self.data;
859        self.min_coord_offset().resolve(data)
860    }
861
862    /// Offset to BaseCoord table that defines maximum extent value,
863    /// from the beginning of MinMax table (may be NULL)
864    pub fn max_coord_offset(&self) -> Nullable<Offset16> {
865        let range = self.max_coord_offset_byte_range();
866        self.data.read_at(range.start).ok().unwrap()
867    }
868
869    /// Attempt to resolve [`max_coord_offset`][Self::max_coord_offset].
870    pub fn max_coord(&self) -> Option<Result<BaseCoord<'a>, ReadError>> {
871        let data = self.data;
872        self.max_coord_offset().resolve(data)
873    }
874
875    /// Number of FeatMinMaxRecords — may be zero (0)
876    pub fn feat_min_max_count(&self) -> u16 {
877        let range = self.feat_min_max_count_byte_range();
878        self.data.read_at(range.start).ok().unwrap()
879    }
880
881    /// Array of FeatMinMaxRecords, in alphabetical order by
882    /// featureTableTag
883    pub fn feat_min_max_records(&self) -> &'a [FeatMinMaxRecord] {
884        let range = self.feat_min_max_records_byte_range();
885        self.data.read_array(range).ok().unwrap_or_default()
886    }
887
888    pub fn min_coord_offset_byte_range(&self) -> Range<usize> {
889        let start = 0;
890        start..start + Offset16::RAW_BYTE_LEN
891    }
892
893    pub fn max_coord_offset_byte_range(&self) -> Range<usize> {
894        let start = self.min_coord_offset_byte_range().end;
895        start..start + Offset16::RAW_BYTE_LEN
896    }
897
898    pub fn feat_min_max_count_byte_range(&self) -> Range<usize> {
899        let start = self.max_coord_offset_byte_range().end;
900        start..start + u16::RAW_BYTE_LEN
901    }
902
903    pub fn feat_min_max_records_byte_range(&self) -> Range<usize> {
904        let feat_min_max_count = self.feat_min_max_count();
905        let start = self.feat_min_max_count_byte_range().end;
906        start
907            ..start
908                + (transforms::to_usize(feat_min_max_count))
909                    .saturating_mul(FeatMinMaxRecord::RAW_BYTE_LEN)
910    }
911}
912
913const _: () = assert!(FontData::default_data_long_enough(MinMax::MIN_SIZE));
914
915impl Default for MinMax<'_> {
916    fn default() -> Self {
917        Self {
918            data: FontData::default_table_data(),
919        }
920    }
921}
922
923#[cfg(feature = "experimental_traverse")]
924impl<'a> SomeTable<'a> for MinMax<'a> {
925    fn type_name(&self) -> &str {
926        "MinMax"
927    }
928    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
929        match idx {
930            0usize => Some(Field::new(
931                "min_coord_offset",
932                FieldType::offset(self.min_coord_offset(), self.min_coord()),
933            )),
934            1usize => Some(Field::new(
935                "max_coord_offset",
936                FieldType::offset(self.max_coord_offset(), self.max_coord()),
937            )),
938            2usize => Some(Field::new("feat_min_max_count", self.feat_min_max_count())),
939            3usize => Some(Field::new(
940                "feat_min_max_records",
941                traversal::FieldType::array_of_records(
942                    stringify!(FeatMinMaxRecord),
943                    self.feat_min_max_records(),
944                    self.offset_data(),
945                ),
946            )),
947            _ => None,
948        }
949    }
950}
951
952#[cfg(feature = "experimental_traverse")]
953#[allow(clippy::needless_lifetimes)]
954impl<'a> std::fmt::Debug for MinMax<'a> {
955    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
956        (self as &dyn SomeTable<'a>).fmt(f)
957    }
958}
959
960/// [FeatMinMaxRecord](https://learn.microsoft.com/en-us/typography/opentype/spec/base#baselangsysrecord)
961#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
962#[repr(C)]
963#[repr(packed)]
964pub struct FeatMinMaxRecord {
965    /// 4-byte feature identification tag — must match feature tag in
966    /// FeatureList
967    pub feature_table_tag: BigEndian<Tag>,
968    /// Offset to BaseCoord table that defines the minimum extent
969    /// value, from beginning of MinMax table (may be NULL)
970    pub min_coord_offset: BigEndian<Nullable<Offset16>>,
971    /// Offset to BaseCoord table that defines the maximum extent
972    /// value, from beginning of MinMax table (may be NULL)
973    pub max_coord_offset: BigEndian<Nullable<Offset16>>,
974}
975
976impl FeatMinMaxRecord {
977    /// 4-byte feature identification tag — must match feature tag in
978    /// FeatureList
979    pub fn feature_table_tag(&self) -> Tag {
980        self.feature_table_tag.get()
981    }
982
983    /// Offset to BaseCoord table that defines the minimum extent
984    /// value, from beginning of MinMax table (may be NULL)
985    pub fn min_coord_offset(&self) -> Nullable<Offset16> {
986        self.min_coord_offset.get()
987    }
988
989    /// Offset to BaseCoord table that defines the minimum extent
990    /// value, from beginning of MinMax table (may be NULL)
991    ///
992    /// The `data` argument should be retrieved from the parent table
993    /// By calling its `offset_data` method.
994    pub fn min_coord<'a>(&self, data: FontData<'a>) -> Option<Result<BaseCoord<'a>, ReadError>> {
995        self.min_coord_offset().resolve(data)
996    }
997
998    /// Offset to BaseCoord table that defines the maximum extent
999    /// value, from beginning of MinMax table (may be NULL)
1000    pub fn max_coord_offset(&self) -> Nullable<Offset16> {
1001        self.max_coord_offset.get()
1002    }
1003
1004    /// Offset to BaseCoord table that defines the maximum extent
1005    /// value, from beginning of MinMax table (may be NULL)
1006    ///
1007    /// The `data` argument should be retrieved from the parent table
1008    /// By calling its `offset_data` method.
1009    pub fn max_coord<'a>(&self, data: FontData<'a>) -> Option<Result<BaseCoord<'a>, ReadError>> {
1010        self.max_coord_offset().resolve(data)
1011    }
1012}
1013
1014impl FixedSize for FeatMinMaxRecord {
1015    const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
1016}
1017
1018#[cfg(feature = "experimental_traverse")]
1019impl<'a> SomeRecord<'a> for FeatMinMaxRecord {
1020    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
1021        RecordResolver {
1022            name: "FeatMinMaxRecord",
1023            get_field: Box::new(move |idx, _data| match idx {
1024                0usize => Some(Field::new("feature_table_tag", self.feature_table_tag())),
1025                1usize => Some(Field::new(
1026                    "min_coord_offset",
1027                    FieldType::offset(self.min_coord_offset(), self.min_coord(_data)),
1028                )),
1029                2usize => Some(Field::new(
1030                    "max_coord_offset",
1031                    FieldType::offset(self.max_coord_offset(), self.max_coord(_data)),
1032                )),
1033                _ => None,
1034            }),
1035            data,
1036        }
1037    }
1038}
1039
1040#[derive(Clone)]
1041pub enum BaseCoord<'a> {
1042    Format1(BaseCoordFormat1<'a>),
1043    Format2(BaseCoordFormat2<'a>),
1044    Format3(BaseCoordFormat3<'a>),
1045}
1046
1047impl Default for BaseCoord<'_> {
1048    fn default() -> Self {
1049        Self::Format1(Default::default())
1050    }
1051}
1052
1053impl<'a> BaseCoord<'a> {
1054    ///Return the `FontData` used to resolve offsets for this table.
1055    pub fn offset_data(&self) -> FontData<'a> {
1056        match self {
1057            Self::Format1(item) => item.offset_data(),
1058            Self::Format2(item) => item.offset_data(),
1059            Self::Format3(item) => item.offset_data(),
1060        }
1061    }
1062
1063    /// Format identifier — format = 1
1064    pub fn base_coord_format(&self) -> u16 {
1065        match self {
1066            Self::Format1(item) => item.base_coord_format(),
1067            Self::Format2(item) => item.base_coord_format(),
1068            Self::Format3(item) => item.base_coord_format(),
1069        }
1070    }
1071
1072    /// X or Y value, in design units
1073    pub fn coordinate(&self) -> i16 {
1074        match self {
1075            Self::Format1(item) => item.coordinate(),
1076            Self::Format2(item) => item.coordinate(),
1077            Self::Format3(item) => item.coordinate(),
1078        }
1079    }
1080}
1081
1082impl<'a> FontRead<'a> for BaseCoord<'a> {
1083    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1084        let format: u16 = data.read_at(0usize)?;
1085        match format {
1086            BaseCoordFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
1087            BaseCoordFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
1088            BaseCoordFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
1089            other => Err(ReadError::InvalidFormat(other.into())),
1090        }
1091    }
1092}
1093
1094impl<'a> MinByteRange<'a> for BaseCoord<'a> {
1095    fn min_byte_range(&self) -> Range<usize> {
1096        match self {
1097            Self::Format1(item) => item.min_byte_range(),
1098            Self::Format2(item) => item.min_byte_range(),
1099            Self::Format3(item) => item.min_byte_range(),
1100        }
1101    }
1102    fn min_table_bytes(&self) -> &'a [u8] {
1103        match self {
1104            Self::Format1(item) => item.min_table_bytes(),
1105            Self::Format2(item) => item.min_table_bytes(),
1106            Self::Format3(item) => item.min_table_bytes(),
1107        }
1108    }
1109}
1110
1111#[cfg(feature = "experimental_traverse")]
1112impl<'a> BaseCoord<'a> {
1113    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
1114        match self {
1115            Self::Format1(table) => table,
1116            Self::Format2(table) => table,
1117            Self::Format3(table) => table,
1118        }
1119    }
1120}
1121
1122#[cfg(feature = "experimental_traverse")]
1123impl std::fmt::Debug for BaseCoord<'_> {
1124    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1125        self.dyn_inner().fmt(f)
1126    }
1127}
1128
1129#[cfg(feature = "experimental_traverse")]
1130impl<'a> SomeTable<'a> for BaseCoord<'a> {
1131    fn type_name(&self) -> &str {
1132        self.dyn_inner().type_name()
1133    }
1134    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1135        self.dyn_inner().get_field(idx)
1136    }
1137}
1138
1139impl Format<u16> for BaseCoordFormat1<'_> {
1140    const FORMAT: u16 = 1;
1141}
1142
1143impl<'a> MinByteRange<'a> for BaseCoordFormat1<'a> {
1144    fn min_byte_range(&self) -> Range<usize> {
1145        0..self.coordinate_byte_range().end
1146    }
1147    fn min_table_bytes(&self) -> &'a [u8] {
1148        let range = self.min_byte_range();
1149        self.data.as_bytes().get(range).unwrap_or_default()
1150    }
1151}
1152
1153impl<'a> FontRead<'a> for BaseCoordFormat1<'a> {
1154    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1155        #[allow(clippy::absurd_extreme_comparisons)]
1156        if data.len() < Self::MIN_SIZE {
1157            return Err(ReadError::OutOfBounds);
1158        }
1159        Ok(Self { data })
1160    }
1161}
1162
1163/// [BaseCoordFormat1](https://learn.microsoft.com/en-us/typography/opentype/spec/base#basecoord-format-1)
1164#[derive(Clone)]
1165pub struct BaseCoordFormat1<'a> {
1166    data: FontData<'a>,
1167}
1168
1169#[allow(clippy::needless_lifetimes)]
1170impl<'a> BaseCoordFormat1<'a> {
1171    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN);
1172    basic_table_impls!(impl_the_methods);
1173
1174    /// Format identifier — format = 1
1175    pub fn base_coord_format(&self) -> u16 {
1176        let range = self.base_coord_format_byte_range();
1177        self.data.read_at(range.start).ok().unwrap()
1178    }
1179
1180    /// X or Y value, in design units
1181    pub fn coordinate(&self) -> i16 {
1182        let range = self.coordinate_byte_range();
1183        self.data.read_at(range.start).ok().unwrap()
1184    }
1185
1186    pub fn base_coord_format_byte_range(&self) -> Range<usize> {
1187        let start = 0;
1188        start..start + u16::RAW_BYTE_LEN
1189    }
1190
1191    pub fn coordinate_byte_range(&self) -> Range<usize> {
1192        let start = self.base_coord_format_byte_range().end;
1193        start..start + i16::RAW_BYTE_LEN
1194    }
1195}
1196
1197const _: () = assert!(FontData::default_data_long_enough(
1198    BaseCoordFormat1::MIN_SIZE
1199));
1200
1201impl Default for BaseCoordFormat1<'_> {
1202    fn default() -> Self {
1203        Self {
1204            data: FontData::default_format_1_u16_table_data(),
1205        }
1206    }
1207}
1208
1209#[cfg(feature = "experimental_traverse")]
1210impl<'a> SomeTable<'a> for BaseCoordFormat1<'a> {
1211    fn type_name(&self) -> &str {
1212        "BaseCoordFormat1"
1213    }
1214    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1215        match idx {
1216            0usize => Some(Field::new("base_coord_format", self.base_coord_format())),
1217            1usize => Some(Field::new("coordinate", self.coordinate())),
1218            _ => None,
1219        }
1220    }
1221}
1222
1223#[cfg(feature = "experimental_traverse")]
1224#[allow(clippy::needless_lifetimes)]
1225impl<'a> std::fmt::Debug for BaseCoordFormat1<'a> {
1226    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1227        (self as &dyn SomeTable<'a>).fmt(f)
1228    }
1229}
1230
1231impl Format<u16> for BaseCoordFormat2<'_> {
1232    const FORMAT: u16 = 2;
1233}
1234
1235impl<'a> MinByteRange<'a> for BaseCoordFormat2<'a> {
1236    fn min_byte_range(&self) -> Range<usize> {
1237        0..self.base_coord_point_byte_range().end
1238    }
1239    fn min_table_bytes(&self) -> &'a [u8] {
1240        let range = self.min_byte_range();
1241        self.data.as_bytes().get(range).unwrap_or_default()
1242    }
1243}
1244
1245impl<'a> FontRead<'a> for BaseCoordFormat2<'a> {
1246    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1247        #[allow(clippy::absurd_extreme_comparisons)]
1248        if data.len() < Self::MIN_SIZE {
1249            return Err(ReadError::OutOfBounds);
1250        }
1251        Ok(Self { data })
1252    }
1253}
1254
1255/// [BaseCoordFormat2](https://learn.microsoft.com/en-us/typography/opentype/spec/base#basecoord-format-2)
1256#[derive(Clone)]
1257pub struct BaseCoordFormat2<'a> {
1258    data: FontData<'a>,
1259}
1260
1261#[allow(clippy::needless_lifetimes)]
1262impl<'a> BaseCoordFormat2<'a> {
1263    pub const MIN_SIZE: usize =
1264        (u16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1265    basic_table_impls!(impl_the_methods);
1266
1267    /// Format identifier — format = 2
1268    pub fn base_coord_format(&self) -> u16 {
1269        let range = self.base_coord_format_byte_range();
1270        self.data.read_at(range.start).ok().unwrap()
1271    }
1272
1273    /// X or Y value, in design units
1274    pub fn coordinate(&self) -> i16 {
1275        let range = self.coordinate_byte_range();
1276        self.data.read_at(range.start).ok().unwrap()
1277    }
1278
1279    /// Glyph ID of control glyph
1280    pub fn reference_glyph(&self) -> u16 {
1281        let range = self.reference_glyph_byte_range();
1282        self.data.read_at(range.start).ok().unwrap()
1283    }
1284
1285    /// Index of contour point on the reference glyph
1286    pub fn base_coord_point(&self) -> u16 {
1287        let range = self.base_coord_point_byte_range();
1288        self.data.read_at(range.start).ok().unwrap()
1289    }
1290
1291    pub fn base_coord_format_byte_range(&self) -> Range<usize> {
1292        let start = 0;
1293        start..start + u16::RAW_BYTE_LEN
1294    }
1295
1296    pub fn coordinate_byte_range(&self) -> Range<usize> {
1297        let start = self.base_coord_format_byte_range().end;
1298        start..start + i16::RAW_BYTE_LEN
1299    }
1300
1301    pub fn reference_glyph_byte_range(&self) -> Range<usize> {
1302        let start = self.coordinate_byte_range().end;
1303        start..start + u16::RAW_BYTE_LEN
1304    }
1305
1306    pub fn base_coord_point_byte_range(&self) -> Range<usize> {
1307        let start = self.reference_glyph_byte_range().end;
1308        start..start + u16::RAW_BYTE_LEN
1309    }
1310}
1311
1312#[cfg(feature = "experimental_traverse")]
1313impl<'a> SomeTable<'a> for BaseCoordFormat2<'a> {
1314    fn type_name(&self) -> &str {
1315        "BaseCoordFormat2"
1316    }
1317    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1318        match idx {
1319            0usize => Some(Field::new("base_coord_format", self.base_coord_format())),
1320            1usize => Some(Field::new("coordinate", self.coordinate())),
1321            2usize => Some(Field::new("reference_glyph", self.reference_glyph())),
1322            3usize => Some(Field::new("base_coord_point", self.base_coord_point())),
1323            _ => None,
1324        }
1325    }
1326}
1327
1328#[cfg(feature = "experimental_traverse")]
1329#[allow(clippy::needless_lifetimes)]
1330impl<'a> std::fmt::Debug for BaseCoordFormat2<'a> {
1331    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1332        (self as &dyn SomeTable<'a>).fmt(f)
1333    }
1334}
1335
1336impl Format<u16> for BaseCoordFormat3<'_> {
1337    const FORMAT: u16 = 3;
1338}
1339
1340impl<'a> MinByteRange<'a> for BaseCoordFormat3<'a> {
1341    fn min_byte_range(&self) -> Range<usize> {
1342        0..self.device_offset_byte_range().end
1343    }
1344    fn min_table_bytes(&self) -> &'a [u8] {
1345        let range = self.min_byte_range();
1346        self.data.as_bytes().get(range).unwrap_or_default()
1347    }
1348}
1349
1350impl<'a> FontRead<'a> for BaseCoordFormat3<'a> {
1351    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1352        #[allow(clippy::absurd_extreme_comparisons)]
1353        if data.len() < Self::MIN_SIZE {
1354            return Err(ReadError::OutOfBounds);
1355        }
1356        Ok(Self { data })
1357    }
1358}
1359
1360/// [BaseCoordFormat3](https://learn.microsoft.com/en-us/typography/opentype/spec/base#basecoord-format-3)
1361#[derive(Clone)]
1362pub struct BaseCoordFormat3<'a> {
1363    data: FontData<'a>,
1364}
1365
1366#[allow(clippy::needless_lifetimes)]
1367impl<'a> BaseCoordFormat3<'a> {
1368    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN);
1369    basic_table_impls!(impl_the_methods);
1370
1371    /// Format identifier — format = 3
1372    pub fn base_coord_format(&self) -> u16 {
1373        let range = self.base_coord_format_byte_range();
1374        self.data.read_at(range.start).ok().unwrap()
1375    }
1376
1377    /// X or Y value, in design units
1378    pub fn coordinate(&self) -> i16 {
1379        let range = self.coordinate_byte_range();
1380        self.data.read_at(range.start).ok().unwrap()
1381    }
1382
1383    /// Offset to Device table (non-variable font) / Variation Index
1384    /// table (variable font) for X or Y value, from beginning of
1385    /// BaseCoord table (may be NULL).
1386    pub fn device_offset(&self) -> Nullable<Offset16> {
1387        let range = self.device_offset_byte_range();
1388        self.data.read_at(range.start).ok().unwrap()
1389    }
1390
1391    /// Attempt to resolve [`device_offset`][Self::device_offset].
1392    pub fn device(&self) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
1393        let data = self.data;
1394        self.device_offset().resolve(data)
1395    }
1396
1397    pub fn base_coord_format_byte_range(&self) -> Range<usize> {
1398        let start = 0;
1399        start..start + u16::RAW_BYTE_LEN
1400    }
1401
1402    pub fn coordinate_byte_range(&self) -> Range<usize> {
1403        let start = self.base_coord_format_byte_range().end;
1404        start..start + i16::RAW_BYTE_LEN
1405    }
1406
1407    pub fn device_offset_byte_range(&self) -> Range<usize> {
1408        let start = self.coordinate_byte_range().end;
1409        start..start + Offset16::RAW_BYTE_LEN
1410    }
1411}
1412
1413#[cfg(feature = "experimental_traverse")]
1414impl<'a> SomeTable<'a> for BaseCoordFormat3<'a> {
1415    fn type_name(&self) -> &str {
1416        "BaseCoordFormat3"
1417    }
1418    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1419        match idx {
1420            0usize => Some(Field::new("base_coord_format", self.base_coord_format())),
1421            1usize => Some(Field::new("coordinate", self.coordinate())),
1422            2usize => Some(Field::new(
1423                "device_offset",
1424                FieldType::offset(self.device_offset(), self.device()),
1425            )),
1426            _ => None,
1427        }
1428    }
1429}
1430
1431#[cfg(feature = "experimental_traverse")]
1432#[allow(clippy::needless_lifetimes)]
1433impl<'a> std::fmt::Debug for BaseCoordFormat3<'a> {
1434    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1435        (self as &dyn SomeTable<'a>).fmt(f)
1436    }
1437}