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