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