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