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 + (base_tag_count as usize).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..start + (base_script_count as usize).saturating_mul(BaseScriptRecord::RAW_BYTE_LEN)
403    }
404}
405
406const _: () = assert!(FontData::default_data_long_enough(BaseScriptList::MIN_SIZE));
407
408impl Default for BaseScriptList<'_> {
409    fn default() -> Self {
410        Self {
411            data: FontData::default_table_data(),
412        }
413    }
414}
415
416#[cfg(feature = "experimental_traverse")]
417impl<'a> SomeTable<'a> for BaseScriptList<'a> {
418    fn type_name(&self) -> &str {
419        "BaseScriptList"
420    }
421    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
422        match idx {
423            0usize => Some(Field::new("base_script_count", self.base_script_count())),
424            1usize => Some(Field::new(
425                "base_script_records",
426                traversal::FieldType::array_of_records(
427                    stringify!(BaseScriptRecord),
428                    self.base_script_records(),
429                    self.offset_data(),
430                ),
431            )),
432            _ => None,
433        }
434    }
435}
436
437#[cfg(feature = "experimental_traverse")]
438#[allow(clippy::needless_lifetimes)]
439impl<'a> std::fmt::Debug for BaseScriptList<'a> {
440    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
441        (self as &dyn SomeTable<'a>).fmt(f)
442    }
443}
444
445/// [BaseScriptRecord](https://learn.microsoft.com/en-us/typography/opentype/spec/base#basescriptrecord)
446#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
447#[repr(C)]
448#[repr(packed)]
449pub struct BaseScriptRecord {
450    /// 4-byte script identification tag
451    pub base_script_tag: BigEndian<Tag>,
452    /// Offset to BaseScript table, from beginning of BaseScriptList
453    pub base_script_offset: BigEndian<Offset16>,
454}
455
456impl BaseScriptRecord {
457    /// 4-byte script identification tag
458    pub fn base_script_tag(&self) -> Tag {
459        self.base_script_tag.get()
460    }
461
462    /// Offset to BaseScript table, from beginning of BaseScriptList
463    pub fn base_script_offset(&self) -> Offset16 {
464        self.base_script_offset.get()
465    }
466
467    /// Offset to BaseScript table, from beginning of BaseScriptList
468    ///
469    /// The `data` argument should be retrieved from the parent table
470    /// By calling its `offset_data` method.
471    pub fn base_script<'a>(&self, data: FontData<'a>) -> Result<BaseScript<'a>, ReadError> {
472        self.base_script_offset().resolve(data)
473    }
474}
475
476impl FixedSize for BaseScriptRecord {
477    const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
478}
479
480#[cfg(feature = "experimental_traverse")]
481impl<'a> SomeRecord<'a> for BaseScriptRecord {
482    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
483        RecordResolver {
484            name: "BaseScriptRecord",
485            get_field: Box::new(move |idx, _data| match idx {
486                0usize => Some(Field::new("base_script_tag", self.base_script_tag())),
487                1usize => Some(Field::new(
488                    "base_script_offset",
489                    FieldType::offset(self.base_script_offset(), self.base_script(_data)),
490                )),
491                _ => None,
492            }),
493            data,
494        }
495    }
496}
497
498impl<'a> MinByteRange<'a> for BaseScript<'a> {
499    fn min_byte_range(&self) -> Range<usize> {
500        0..self.base_lang_sys_records_byte_range().end
501    }
502    fn min_table_bytes(&self) -> &'a [u8] {
503        let range = self.min_byte_range();
504        self.data.as_bytes().get(range).unwrap_or_default()
505    }
506}
507
508impl<'a> FontRead<'a> for BaseScript<'a> {
509    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
510        #[allow(clippy::absurd_extreme_comparisons)]
511        if data.len() < Self::MIN_SIZE {
512            return Err(ReadError::OutOfBounds);
513        }
514        Ok(Self { data })
515    }
516}
517
518/// [BaseScript Table](https://learn.microsoft.com/en-us/typography/opentype/spec/base#basescript-table)
519#[derive(Clone)]
520pub struct BaseScript<'a> {
521    data: FontData<'a>,
522}
523
524#[allow(clippy::needless_lifetimes)]
525impl<'a> BaseScript<'a> {
526    pub const MIN_SIZE: usize =
527        (Offset16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
528    basic_table_impls!(impl_the_methods);
529
530    /// Offset to BaseValues table, from beginning of BaseScript table (may be NULL)
531    pub fn base_values_offset(&self) -> Nullable<Offset16> {
532        let range = self.base_values_offset_byte_range();
533        self.data.read_at(range.start).ok().unwrap()
534    }
535
536    /// Attempt to resolve [`base_values_offset`][Self::base_values_offset].
537    pub fn base_values(&self) -> Option<Result<BaseValues<'a>, ReadError>> {
538        let data = self.data;
539        self.base_values_offset().resolve(data)
540    }
541
542    /// Offset to MinMax table, from beginning of BaseScript table (may be NULL)
543    pub fn default_min_max_offset(&self) -> Nullable<Offset16> {
544        let range = self.default_min_max_offset_byte_range();
545        self.data.read_at(range.start).ok().unwrap()
546    }
547
548    /// Attempt to resolve [`default_min_max_offset`][Self::default_min_max_offset].
549    pub fn default_min_max(&self) -> Option<Result<MinMax<'a>, ReadError>> {
550        let data = self.data;
551        self.default_min_max_offset().resolve(data)
552    }
553
554    /// Number of BaseLangSysRecords defined — may be zero (0)
555    pub fn base_lang_sys_count(&self) -> u16 {
556        let range = self.base_lang_sys_count_byte_range();
557        self.data.read_at(range.start).ok().unwrap()
558    }
559
560    /// Array of BaseLangSysRecords, in alphabetical order by
561    /// BaseLangSysTag
562    pub fn base_lang_sys_records(&self) -> &'a [BaseLangSysRecord] {
563        let range = self.base_lang_sys_records_byte_range();
564        self.data.read_array(range).ok().unwrap_or_default()
565    }
566
567    pub fn base_values_offset_byte_range(&self) -> Range<usize> {
568        let start = 0;
569        start..start + Offset16::RAW_BYTE_LEN
570    }
571
572    pub fn default_min_max_offset_byte_range(&self) -> Range<usize> {
573        let start = self.base_values_offset_byte_range().end;
574        start..start + Offset16::RAW_BYTE_LEN
575    }
576
577    pub fn base_lang_sys_count_byte_range(&self) -> Range<usize> {
578        let start = self.default_min_max_offset_byte_range().end;
579        start..start + u16::RAW_BYTE_LEN
580    }
581
582    pub fn base_lang_sys_records_byte_range(&self) -> Range<usize> {
583        let base_lang_sys_count = self.base_lang_sys_count();
584        let start = self.base_lang_sys_count_byte_range().end;
585        start
586            ..start + (base_lang_sys_count as usize).saturating_mul(BaseLangSysRecord::RAW_BYTE_LEN)
587    }
588}
589
590const _: () = assert!(FontData::default_data_long_enough(BaseScript::MIN_SIZE));
591
592impl Default for BaseScript<'_> {
593    fn default() -> Self {
594        Self {
595            data: FontData::default_table_data(),
596        }
597    }
598}
599
600#[cfg(feature = "experimental_traverse")]
601impl<'a> SomeTable<'a> for BaseScript<'a> {
602    fn type_name(&self) -> &str {
603        "BaseScript"
604    }
605    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
606        match idx {
607            0usize => Some(Field::new(
608                "base_values_offset",
609                FieldType::offset(self.base_values_offset(), self.base_values()),
610            )),
611            1usize => Some(Field::new(
612                "default_min_max_offset",
613                FieldType::offset(self.default_min_max_offset(), self.default_min_max()),
614            )),
615            2usize => Some(Field::new(
616                "base_lang_sys_count",
617                self.base_lang_sys_count(),
618            )),
619            3usize => Some(Field::new(
620                "base_lang_sys_records",
621                traversal::FieldType::array_of_records(
622                    stringify!(BaseLangSysRecord),
623                    self.base_lang_sys_records(),
624                    self.offset_data(),
625                ),
626            )),
627            _ => None,
628        }
629    }
630}
631
632#[cfg(feature = "experimental_traverse")]
633#[allow(clippy::needless_lifetimes)]
634impl<'a> std::fmt::Debug for BaseScript<'a> {
635    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
636        (self as &dyn SomeTable<'a>).fmt(f)
637    }
638}
639
640/// [BaseLangSysRecord](https://learn.microsoft.com/en-us/typography/opentype/spec/base#baselangsysrecord)
641#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
642#[repr(C)]
643#[repr(packed)]
644pub struct BaseLangSysRecord {
645    /// 4-byte language system identification tag
646    pub base_lang_sys_tag: BigEndian<Tag>,
647    /// Offset to MinMax table, from beginning of BaseScript table
648    pub min_max_offset: BigEndian<Offset16>,
649}
650
651impl BaseLangSysRecord {
652    /// 4-byte language system identification tag
653    pub fn base_lang_sys_tag(&self) -> Tag {
654        self.base_lang_sys_tag.get()
655    }
656
657    /// Offset to MinMax table, from beginning of BaseScript table
658    pub fn min_max_offset(&self) -> Offset16 {
659        self.min_max_offset.get()
660    }
661
662    /// Offset to MinMax table, from beginning of BaseScript table
663    ///
664    /// The `data` argument should be retrieved from the parent table
665    /// By calling its `offset_data` method.
666    pub fn min_max<'a>(&self, data: FontData<'a>) -> Result<MinMax<'a>, ReadError> {
667        self.min_max_offset().resolve(data)
668    }
669}
670
671impl FixedSize for BaseLangSysRecord {
672    const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
673}
674
675#[cfg(feature = "experimental_traverse")]
676impl<'a> SomeRecord<'a> for BaseLangSysRecord {
677    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
678        RecordResolver {
679            name: "BaseLangSysRecord",
680            get_field: Box::new(move |idx, _data| match idx {
681                0usize => Some(Field::new("base_lang_sys_tag", self.base_lang_sys_tag())),
682                1usize => Some(Field::new(
683                    "min_max_offset",
684                    FieldType::offset(self.min_max_offset(), self.min_max(_data)),
685                )),
686                _ => None,
687            }),
688            data,
689        }
690    }
691}
692
693impl<'a> MinByteRange<'a> for BaseValues<'a> {
694    fn min_byte_range(&self) -> Range<usize> {
695        0..self.base_coord_offsets_byte_range().end
696    }
697    fn min_table_bytes(&self) -> &'a [u8] {
698        let range = self.min_byte_range();
699        self.data.as_bytes().get(range).unwrap_or_default()
700    }
701}
702
703impl<'a> FontRead<'a> for BaseValues<'a> {
704    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
705        #[allow(clippy::absurd_extreme_comparisons)]
706        if data.len() < Self::MIN_SIZE {
707            return Err(ReadError::OutOfBounds);
708        }
709        Ok(Self { data })
710    }
711}
712
713/// [BaseValues](https://learn.microsoft.com/en-us/typography/opentype/spec/base#basevalues-table) table
714#[derive(Clone)]
715pub struct BaseValues<'a> {
716    data: FontData<'a>,
717}
718
719#[allow(clippy::needless_lifetimes)]
720impl<'a> BaseValues<'a> {
721    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
722    basic_table_impls!(impl_the_methods);
723
724    /// Index number of default baseline for this script — equals
725    /// index position of baseline tag in baselineTags array of the
726    /// BaseTagList
727    pub fn default_baseline_index(&self) -> u16 {
728        let range = self.default_baseline_index_byte_range();
729        self.data.read_at(range.start).ok().unwrap()
730    }
731
732    /// Number of BaseCoord tables defined — should equal
733    /// baseTagCount in the BaseTagList
734    pub fn base_coord_count(&self) -> u16 {
735        let range = self.base_coord_count_byte_range();
736        self.data.read_at(range.start).ok().unwrap()
737    }
738
739    /// Array of offsets to BaseCoord tables, from beginning of
740    /// BaseValues table — order matches baselineTags array in the
741    /// BaseTagList
742    pub fn base_coord_offsets(&self) -> &'a [BigEndian<Offset16>] {
743        let range = self.base_coord_offsets_byte_range();
744        self.data.read_array(range).ok().unwrap_or_default()
745    }
746
747    /// A dynamically resolving wrapper for [`base_coord_offsets`][Self::base_coord_offsets].
748    pub fn base_coords(&self) -> ArrayOfOffsets<'a, BaseCoord<'a>, Offset16> {
749        let data = self.data;
750        let offsets = self.base_coord_offsets();
751        ArrayOfOffsets::new(offsets, data, ())
752    }
753
754    pub fn default_baseline_index_byte_range(&self) -> Range<usize> {
755        let start = 0;
756        start..start + u16::RAW_BYTE_LEN
757    }
758
759    pub fn base_coord_count_byte_range(&self) -> Range<usize> {
760        let start = self.default_baseline_index_byte_range().end;
761        start..start + u16::RAW_BYTE_LEN
762    }
763
764    pub fn base_coord_offsets_byte_range(&self) -> Range<usize> {
765        let base_coord_count = self.base_coord_count();
766        let start = self.base_coord_count_byte_range().end;
767        start..start + (base_coord_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
768    }
769}
770
771const _: () = assert!(FontData::default_data_long_enough(BaseValues::MIN_SIZE));
772
773impl Default for BaseValues<'_> {
774    fn default() -> Self {
775        Self {
776            data: FontData::default_table_data(),
777        }
778    }
779}
780
781#[cfg(feature = "experimental_traverse")]
782impl<'a> SomeTable<'a> for BaseValues<'a> {
783    fn type_name(&self) -> &str {
784        "BaseValues"
785    }
786    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
787        match idx {
788            0usize => Some(Field::new(
789                "default_baseline_index",
790                self.default_baseline_index(),
791            )),
792            1usize => Some(Field::new("base_coord_count", self.base_coord_count())),
793            2usize => Some(Field::new(
794                "base_coord_offsets",
795                FieldType::from(self.base_coords()),
796            )),
797            _ => None,
798        }
799    }
800}
801
802#[cfg(feature = "experimental_traverse")]
803#[allow(clippy::needless_lifetimes)]
804impl<'a> std::fmt::Debug for BaseValues<'a> {
805    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
806        (self as &dyn SomeTable<'a>).fmt(f)
807    }
808}
809
810impl<'a> MinByteRange<'a> for MinMax<'a> {
811    fn min_byte_range(&self) -> Range<usize> {
812        0..self.feat_min_max_records_byte_range().end
813    }
814    fn min_table_bytes(&self) -> &'a [u8] {
815        let range = self.min_byte_range();
816        self.data.as_bytes().get(range).unwrap_or_default()
817    }
818}
819
820impl<'a> FontRead<'a> for MinMax<'a> {
821    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
822        #[allow(clippy::absurd_extreme_comparisons)]
823        if data.len() < Self::MIN_SIZE {
824            return Err(ReadError::OutOfBounds);
825        }
826        Ok(Self { data })
827    }
828}
829
830/// [MinMax](https://learn.microsoft.com/en-us/typography/opentype/spec/base#minmax-table) table
831#[derive(Clone)]
832pub struct MinMax<'a> {
833    data: FontData<'a>,
834}
835
836#[allow(clippy::needless_lifetimes)]
837impl<'a> MinMax<'a> {
838    pub const MIN_SIZE: usize =
839        (Offset16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
840    basic_table_impls!(impl_the_methods);
841
842    /// Offset to BaseCoord table that defines the minimum extent
843    /// value, from the beginning of MinMax table (may be NULL)
844    pub fn min_coord_offset(&self) -> Nullable<Offset16> {
845        let range = self.min_coord_offset_byte_range();
846        self.data.read_at(range.start).ok().unwrap()
847    }
848
849    /// Attempt to resolve [`min_coord_offset`][Self::min_coord_offset].
850    pub fn min_coord(&self) -> Option<Result<BaseCoord<'a>, ReadError>> {
851        let data = self.data;
852        self.min_coord_offset().resolve(data)
853    }
854
855    /// Offset to BaseCoord table that defines maximum extent value,
856    /// from the beginning of MinMax table (may be NULL)
857    pub fn max_coord_offset(&self) -> Nullable<Offset16> {
858        let range = self.max_coord_offset_byte_range();
859        self.data.read_at(range.start).ok().unwrap()
860    }
861
862    /// Attempt to resolve [`max_coord_offset`][Self::max_coord_offset].
863    pub fn max_coord(&self) -> Option<Result<BaseCoord<'a>, ReadError>> {
864        let data = self.data;
865        self.max_coord_offset().resolve(data)
866    }
867
868    /// Number of FeatMinMaxRecords — may be zero (0)
869    pub fn feat_min_max_count(&self) -> u16 {
870        let range = self.feat_min_max_count_byte_range();
871        self.data.read_at(range.start).ok().unwrap()
872    }
873
874    /// Array of FeatMinMaxRecords, in alphabetical order by
875    /// featureTableTag
876    pub fn feat_min_max_records(&self) -> &'a [FeatMinMaxRecord] {
877        let range = self.feat_min_max_records_byte_range();
878        self.data.read_array(range).ok().unwrap_or_default()
879    }
880
881    pub fn min_coord_offset_byte_range(&self) -> Range<usize> {
882        let start = 0;
883        start..start + Offset16::RAW_BYTE_LEN
884    }
885
886    pub fn max_coord_offset_byte_range(&self) -> Range<usize> {
887        let start = self.min_coord_offset_byte_range().end;
888        start..start + Offset16::RAW_BYTE_LEN
889    }
890
891    pub fn feat_min_max_count_byte_range(&self) -> Range<usize> {
892        let start = self.max_coord_offset_byte_range().end;
893        start..start + u16::RAW_BYTE_LEN
894    }
895
896    pub fn feat_min_max_records_byte_range(&self) -> Range<usize> {
897        let feat_min_max_count = self.feat_min_max_count();
898        let start = self.feat_min_max_count_byte_range().end;
899        start..start + (feat_min_max_count as usize).saturating_mul(FeatMinMaxRecord::RAW_BYTE_LEN)
900    }
901}
902
903const _: () = assert!(FontData::default_data_long_enough(MinMax::MIN_SIZE));
904
905impl Default for MinMax<'_> {
906    fn default() -> Self {
907        Self {
908            data: FontData::default_table_data(),
909        }
910    }
911}
912
913#[cfg(feature = "experimental_traverse")]
914impl<'a> SomeTable<'a> for MinMax<'a> {
915    fn type_name(&self) -> &str {
916        "MinMax"
917    }
918    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
919        match idx {
920            0usize => Some(Field::new(
921                "min_coord_offset",
922                FieldType::offset(self.min_coord_offset(), self.min_coord()),
923            )),
924            1usize => Some(Field::new(
925                "max_coord_offset",
926                FieldType::offset(self.max_coord_offset(), self.max_coord()),
927            )),
928            2usize => Some(Field::new("feat_min_max_count", self.feat_min_max_count())),
929            3usize => Some(Field::new(
930                "feat_min_max_records",
931                traversal::FieldType::array_of_records(
932                    stringify!(FeatMinMaxRecord),
933                    self.feat_min_max_records(),
934                    self.offset_data(),
935                ),
936            )),
937            _ => None,
938        }
939    }
940}
941
942#[cfg(feature = "experimental_traverse")]
943#[allow(clippy::needless_lifetimes)]
944impl<'a> std::fmt::Debug for MinMax<'a> {
945    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
946        (self as &dyn SomeTable<'a>).fmt(f)
947    }
948}
949
950/// [FeatMinMaxRecord](https://learn.microsoft.com/en-us/typography/opentype/spec/base#baselangsysrecord)
951#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
952#[repr(C)]
953#[repr(packed)]
954pub struct FeatMinMaxRecord {
955    /// 4-byte feature identification tag — must match feature tag in
956    /// FeatureList
957    pub feature_table_tag: BigEndian<Tag>,
958    /// Offset to BaseCoord table that defines the minimum extent
959    /// value, from beginning of MinMax table (may be NULL)
960    pub min_coord_offset: BigEndian<Nullable<Offset16>>,
961    /// Offset to BaseCoord table that defines the maximum extent
962    /// value, from beginning of MinMax table (may be NULL)
963    pub max_coord_offset: BigEndian<Nullable<Offset16>>,
964}
965
966impl FeatMinMaxRecord {
967    /// 4-byte feature identification tag — must match feature tag in
968    /// FeatureList
969    pub fn feature_table_tag(&self) -> Tag {
970        self.feature_table_tag.get()
971    }
972
973    /// Offset to BaseCoord table that defines the minimum extent
974    /// value, from beginning of MinMax table (may be NULL)
975    pub fn min_coord_offset(&self) -> Nullable<Offset16> {
976        self.min_coord_offset.get()
977    }
978
979    /// Offset to BaseCoord table that defines the minimum extent
980    /// value, from beginning of MinMax table (may be NULL)
981    ///
982    /// The `data` argument should be retrieved from the parent table
983    /// By calling its `offset_data` method.
984    pub fn min_coord<'a>(&self, data: FontData<'a>) -> Option<Result<BaseCoord<'a>, ReadError>> {
985        self.min_coord_offset().resolve(data)
986    }
987
988    /// Offset to BaseCoord table that defines the maximum extent
989    /// value, from beginning of MinMax table (may be NULL)
990    pub fn max_coord_offset(&self) -> Nullable<Offset16> {
991        self.max_coord_offset.get()
992    }
993
994    /// Offset to BaseCoord table that defines the maximum extent
995    /// value, from beginning of MinMax table (may be NULL)
996    ///
997    /// The `data` argument should be retrieved from the parent table
998    /// By calling its `offset_data` method.
999    pub fn max_coord<'a>(&self, data: FontData<'a>) -> Option<Result<BaseCoord<'a>, ReadError>> {
1000        self.max_coord_offset().resolve(data)
1001    }
1002}
1003
1004impl FixedSize for FeatMinMaxRecord {
1005    const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
1006}
1007
1008#[cfg(feature = "experimental_traverse")]
1009impl<'a> SomeRecord<'a> for FeatMinMaxRecord {
1010    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
1011        RecordResolver {
1012            name: "FeatMinMaxRecord",
1013            get_field: Box::new(move |idx, _data| match idx {
1014                0usize => Some(Field::new("feature_table_tag", self.feature_table_tag())),
1015                1usize => Some(Field::new(
1016                    "min_coord_offset",
1017                    FieldType::offset(self.min_coord_offset(), self.min_coord(_data)),
1018                )),
1019                2usize => Some(Field::new(
1020                    "max_coord_offset",
1021                    FieldType::offset(self.max_coord_offset(), self.max_coord(_data)),
1022                )),
1023                _ => None,
1024            }),
1025            data,
1026        }
1027    }
1028}
1029
1030#[derive(Clone)]
1031pub enum BaseCoord<'a> {
1032    Format1(BaseCoordFormat1<'a>),
1033    Format2(BaseCoordFormat2<'a>),
1034    Format3(BaseCoordFormat3<'a>),
1035}
1036
1037impl Default for BaseCoord<'_> {
1038    fn default() -> Self {
1039        Self::Format1(Default::default())
1040    }
1041}
1042
1043impl<'a> BaseCoord<'a> {
1044    ///Return the `FontData` used to resolve offsets for this table.
1045    pub fn offset_data(&self) -> FontData<'a> {
1046        match self {
1047            Self::Format1(item) => item.offset_data(),
1048            Self::Format2(item) => item.offset_data(),
1049            Self::Format3(item) => item.offset_data(),
1050        }
1051    }
1052
1053    /// Format identifier — format = 1
1054    pub fn base_coord_format(&self) -> u16 {
1055        match self {
1056            Self::Format1(item) => item.base_coord_format(),
1057            Self::Format2(item) => item.base_coord_format(),
1058            Self::Format3(item) => item.base_coord_format(),
1059        }
1060    }
1061
1062    /// X or Y value, in design units
1063    pub fn coordinate(&self) -> i16 {
1064        match self {
1065            Self::Format1(item) => item.coordinate(),
1066            Self::Format2(item) => item.coordinate(),
1067            Self::Format3(item) => item.coordinate(),
1068        }
1069    }
1070}
1071
1072impl<'a> FontRead<'a> for BaseCoord<'a> {
1073    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1074        let format: u16 = data.read_at(0usize)?;
1075        match format {
1076            BaseCoordFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
1077            BaseCoordFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
1078            BaseCoordFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
1079            other => Err(ReadError::InvalidFormat(other.into())),
1080        }
1081    }
1082}
1083
1084impl<'a> MinByteRange<'a> for BaseCoord<'a> {
1085    fn min_byte_range(&self) -> Range<usize> {
1086        match self {
1087            Self::Format1(item) => item.min_byte_range(),
1088            Self::Format2(item) => item.min_byte_range(),
1089            Self::Format3(item) => item.min_byte_range(),
1090        }
1091    }
1092    fn min_table_bytes(&self) -> &'a [u8] {
1093        match self {
1094            Self::Format1(item) => item.min_table_bytes(),
1095            Self::Format2(item) => item.min_table_bytes(),
1096            Self::Format3(item) => item.min_table_bytes(),
1097        }
1098    }
1099}
1100
1101#[cfg(feature = "experimental_traverse")]
1102impl<'a> BaseCoord<'a> {
1103    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
1104        match self {
1105            Self::Format1(table) => table,
1106            Self::Format2(table) => table,
1107            Self::Format3(table) => table,
1108        }
1109    }
1110}
1111
1112#[cfg(feature = "experimental_traverse")]
1113impl std::fmt::Debug for BaseCoord<'_> {
1114    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1115        self.dyn_inner().fmt(f)
1116    }
1117}
1118
1119#[cfg(feature = "experimental_traverse")]
1120impl<'a> SomeTable<'a> for BaseCoord<'a> {
1121    fn type_name(&self) -> &str {
1122        self.dyn_inner().type_name()
1123    }
1124    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1125        self.dyn_inner().get_field(idx)
1126    }
1127}
1128
1129impl Format<u16> for BaseCoordFormat1<'_> {
1130    const FORMAT: u16 = 1;
1131}
1132
1133impl<'a> MinByteRange<'a> for BaseCoordFormat1<'a> {
1134    fn min_byte_range(&self) -> Range<usize> {
1135        0..self.coordinate_byte_range().end
1136    }
1137    fn min_table_bytes(&self) -> &'a [u8] {
1138        let range = self.min_byte_range();
1139        self.data.as_bytes().get(range).unwrap_or_default()
1140    }
1141}
1142
1143impl<'a> FontRead<'a> for BaseCoordFormat1<'a> {
1144    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1145        #[allow(clippy::absurd_extreme_comparisons)]
1146        if data.len() < Self::MIN_SIZE {
1147            return Err(ReadError::OutOfBounds);
1148        }
1149        Ok(Self { data })
1150    }
1151}
1152
1153/// [BaseCoordFormat1](https://learn.microsoft.com/en-us/typography/opentype/spec/base#basecoord-format-1)
1154#[derive(Clone)]
1155pub struct BaseCoordFormat1<'a> {
1156    data: FontData<'a>,
1157}
1158
1159#[allow(clippy::needless_lifetimes)]
1160impl<'a> BaseCoordFormat1<'a> {
1161    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN);
1162    basic_table_impls!(impl_the_methods);
1163
1164    /// Format identifier — format = 1
1165    pub fn base_coord_format(&self) -> u16 {
1166        let range = self.base_coord_format_byte_range();
1167        self.data.read_at(range.start).ok().unwrap()
1168    }
1169
1170    /// X or Y value, in design units
1171    pub fn coordinate(&self) -> i16 {
1172        let range = self.coordinate_byte_range();
1173        self.data.read_at(range.start).ok().unwrap()
1174    }
1175
1176    pub fn base_coord_format_byte_range(&self) -> Range<usize> {
1177        let start = 0;
1178        start..start + u16::RAW_BYTE_LEN
1179    }
1180
1181    pub fn coordinate_byte_range(&self) -> Range<usize> {
1182        let start = self.base_coord_format_byte_range().end;
1183        start..start + i16::RAW_BYTE_LEN
1184    }
1185}
1186
1187const _: () = assert!(FontData::default_data_long_enough(
1188    BaseCoordFormat1::MIN_SIZE
1189));
1190
1191impl Default for BaseCoordFormat1<'_> {
1192    fn default() -> Self {
1193        Self {
1194            data: FontData::default_format_1_u16_table_data(),
1195        }
1196    }
1197}
1198
1199#[cfg(feature = "experimental_traverse")]
1200impl<'a> SomeTable<'a> for BaseCoordFormat1<'a> {
1201    fn type_name(&self) -> &str {
1202        "BaseCoordFormat1"
1203    }
1204    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1205        match idx {
1206            0usize => Some(Field::new("base_coord_format", self.base_coord_format())),
1207            1usize => Some(Field::new("coordinate", self.coordinate())),
1208            _ => None,
1209        }
1210    }
1211}
1212
1213#[cfg(feature = "experimental_traverse")]
1214#[allow(clippy::needless_lifetimes)]
1215impl<'a> std::fmt::Debug for BaseCoordFormat1<'a> {
1216    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1217        (self as &dyn SomeTable<'a>).fmt(f)
1218    }
1219}
1220
1221impl Format<u16> for BaseCoordFormat2<'_> {
1222    const FORMAT: u16 = 2;
1223}
1224
1225impl<'a> MinByteRange<'a> for BaseCoordFormat2<'a> {
1226    fn min_byte_range(&self) -> Range<usize> {
1227        0..self.base_coord_point_byte_range().end
1228    }
1229    fn min_table_bytes(&self) -> &'a [u8] {
1230        let range = self.min_byte_range();
1231        self.data.as_bytes().get(range).unwrap_or_default()
1232    }
1233}
1234
1235impl<'a> FontRead<'a> for BaseCoordFormat2<'a> {
1236    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1237        #[allow(clippy::absurd_extreme_comparisons)]
1238        if data.len() < Self::MIN_SIZE {
1239            return Err(ReadError::OutOfBounds);
1240        }
1241        Ok(Self { data })
1242    }
1243}
1244
1245/// [BaseCoordFormat2](https://learn.microsoft.com/en-us/typography/opentype/spec/base#basecoord-format-2)
1246#[derive(Clone)]
1247pub struct BaseCoordFormat2<'a> {
1248    data: FontData<'a>,
1249}
1250
1251#[allow(clippy::needless_lifetimes)]
1252impl<'a> BaseCoordFormat2<'a> {
1253    pub const MIN_SIZE: usize =
1254        (u16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1255    basic_table_impls!(impl_the_methods);
1256
1257    /// Format identifier — format = 2
1258    pub fn base_coord_format(&self) -> u16 {
1259        let range = self.base_coord_format_byte_range();
1260        self.data.read_at(range.start).ok().unwrap()
1261    }
1262
1263    /// X or Y value, in design units
1264    pub fn coordinate(&self) -> i16 {
1265        let range = self.coordinate_byte_range();
1266        self.data.read_at(range.start).ok().unwrap()
1267    }
1268
1269    /// Glyph ID of control glyph
1270    pub fn reference_glyph(&self) -> u16 {
1271        let range = self.reference_glyph_byte_range();
1272        self.data.read_at(range.start).ok().unwrap()
1273    }
1274
1275    /// Index of contour point on the reference glyph
1276    pub fn base_coord_point(&self) -> u16 {
1277        let range = self.base_coord_point_byte_range();
1278        self.data.read_at(range.start).ok().unwrap()
1279    }
1280
1281    pub fn base_coord_format_byte_range(&self) -> Range<usize> {
1282        let start = 0;
1283        start..start + u16::RAW_BYTE_LEN
1284    }
1285
1286    pub fn coordinate_byte_range(&self) -> Range<usize> {
1287        let start = self.base_coord_format_byte_range().end;
1288        start..start + i16::RAW_BYTE_LEN
1289    }
1290
1291    pub fn reference_glyph_byte_range(&self) -> Range<usize> {
1292        let start = self.coordinate_byte_range().end;
1293        start..start + u16::RAW_BYTE_LEN
1294    }
1295
1296    pub fn base_coord_point_byte_range(&self) -> Range<usize> {
1297        let start = self.reference_glyph_byte_range().end;
1298        start..start + u16::RAW_BYTE_LEN
1299    }
1300}
1301
1302#[cfg(feature = "experimental_traverse")]
1303impl<'a> SomeTable<'a> for BaseCoordFormat2<'a> {
1304    fn type_name(&self) -> &str {
1305        "BaseCoordFormat2"
1306    }
1307    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1308        match idx {
1309            0usize => Some(Field::new("base_coord_format", self.base_coord_format())),
1310            1usize => Some(Field::new("coordinate", self.coordinate())),
1311            2usize => Some(Field::new("reference_glyph", self.reference_glyph())),
1312            3usize => Some(Field::new("base_coord_point", self.base_coord_point())),
1313            _ => None,
1314        }
1315    }
1316}
1317
1318#[cfg(feature = "experimental_traverse")]
1319#[allow(clippy::needless_lifetimes)]
1320impl<'a> std::fmt::Debug for BaseCoordFormat2<'a> {
1321    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1322        (self as &dyn SomeTable<'a>).fmt(f)
1323    }
1324}
1325
1326impl Format<u16> for BaseCoordFormat3<'_> {
1327    const FORMAT: u16 = 3;
1328}
1329
1330impl<'a> MinByteRange<'a> for BaseCoordFormat3<'a> {
1331    fn min_byte_range(&self) -> Range<usize> {
1332        0..self.device_offset_byte_range().end
1333    }
1334    fn min_table_bytes(&self) -> &'a [u8] {
1335        let range = self.min_byte_range();
1336        self.data.as_bytes().get(range).unwrap_or_default()
1337    }
1338}
1339
1340impl<'a> FontRead<'a> for BaseCoordFormat3<'a> {
1341    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1342        #[allow(clippy::absurd_extreme_comparisons)]
1343        if data.len() < Self::MIN_SIZE {
1344            return Err(ReadError::OutOfBounds);
1345        }
1346        Ok(Self { data })
1347    }
1348}
1349
1350/// [BaseCoordFormat3](https://learn.microsoft.com/en-us/typography/opentype/spec/base#basecoord-format-3)
1351#[derive(Clone)]
1352pub struct BaseCoordFormat3<'a> {
1353    data: FontData<'a>,
1354}
1355
1356#[allow(clippy::needless_lifetimes)]
1357impl<'a> BaseCoordFormat3<'a> {
1358    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN);
1359    basic_table_impls!(impl_the_methods);
1360
1361    /// Format identifier — format = 3
1362    pub fn base_coord_format(&self) -> u16 {
1363        let range = self.base_coord_format_byte_range();
1364        self.data.read_at(range.start).ok().unwrap()
1365    }
1366
1367    /// X or Y value, in design units
1368    pub fn coordinate(&self) -> i16 {
1369        let range = self.coordinate_byte_range();
1370        self.data.read_at(range.start).ok().unwrap()
1371    }
1372
1373    /// Offset to Device table (non-variable font) / Variation Index
1374    /// table (variable font) for X or Y value, from beginning of
1375    /// BaseCoord table (may be NULL).
1376    pub fn device_offset(&self) -> Nullable<Offset16> {
1377        let range = self.device_offset_byte_range();
1378        self.data.read_at(range.start).ok().unwrap()
1379    }
1380
1381    /// Attempt to resolve [`device_offset`][Self::device_offset].
1382    pub fn device(&self) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
1383        let data = self.data;
1384        self.device_offset().resolve(data)
1385    }
1386
1387    pub fn base_coord_format_byte_range(&self) -> Range<usize> {
1388        let start = 0;
1389        start..start + u16::RAW_BYTE_LEN
1390    }
1391
1392    pub fn coordinate_byte_range(&self) -> Range<usize> {
1393        let start = self.base_coord_format_byte_range().end;
1394        start..start + i16::RAW_BYTE_LEN
1395    }
1396
1397    pub fn device_offset_byte_range(&self) -> Range<usize> {
1398        let start = self.coordinate_byte_range().end;
1399        start..start + Offset16::RAW_BYTE_LEN
1400    }
1401}
1402
1403#[cfg(feature = "experimental_traverse")]
1404impl<'a> SomeTable<'a> for BaseCoordFormat3<'a> {
1405    fn type_name(&self) -> &str {
1406        "BaseCoordFormat3"
1407    }
1408    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1409        match idx {
1410            0usize => Some(Field::new("base_coord_format", self.base_coord_format())),
1411            1usize => Some(Field::new("coordinate", self.coordinate())),
1412            2usize => Some(Field::new(
1413                "device_offset",
1414                FieldType::offset(self.device_offset(), self.device()),
1415            )),
1416            _ => None,
1417        }
1418    }
1419}
1420
1421#[cfg(feature = "experimental_traverse")]
1422#[allow(clippy::needless_lifetimes)]
1423impl<'a> std::fmt::Debug for BaseCoordFormat3<'a> {
1424    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1425        (self as &dyn SomeTable<'a>).fmt(f)
1426    }
1427}