1#[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 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#[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 pub fn version(&self) -> MajorMinor {
47 let range = self.version_byte_range();
48 self.data.read_at(range.start).ok().unwrap()
49 }
50
51 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 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 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 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 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 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#[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 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 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 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 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#[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 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 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#[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 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 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#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
407#[repr(C)]
408#[repr(packed)]
409pub struct BaseScriptRecord {
410 pub base_script_tag: BigEndian<Tag>,
412 pub base_script_offset: BigEndian<Offset16>,
414}
415
416impl BaseScriptRecord {
417 pub fn base_script_tag(&self) -> Tag {
419 self.base_script_tag.get()
420 }
421
422 pub fn base_script_offset(&self) -> Offset16 {
424 self.base_script_offset.get()
425 }
426
427 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#[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 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 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 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 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 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 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#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
592#[repr(C)]
593#[repr(packed)]
594pub struct BaseLangSysRecord {
595 pub base_lang_sys_tag: BigEndian<Tag>,
597 pub min_max_offset: BigEndian<Offset16>,
599}
600
601impl BaseLangSysRecord {
602 pub fn base_lang_sys_tag(&self) -> Tag {
604 self.base_lang_sys_tag.get()
605 }
606
607 pub fn min_max_offset(&self) -> Offset16 {
609 self.min_max_offset.get()
610 }
611
612 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#[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 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 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 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 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#[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 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 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 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 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 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 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#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
892#[repr(C)]
893#[repr(packed)]
894pub struct FeatMinMaxRecord {
895 pub feature_table_tag: BigEndian<Tag>,
898 pub min_coord_offset: BigEndian<Nullable<Offset16>>,
901 pub max_coord_offset: BigEndian<Nullable<Offset16>>,
904}
905
906impl FeatMinMaxRecord {
907 pub fn feature_table_tag(&self) -> Tag {
910 self.feature_table_tag.get()
911 }
912
913 pub fn min_coord_offset(&self) -> Nullable<Offset16> {
916 self.min_coord_offset.get()
917 }
918
919 pub fn min_coord<'a>(&self, data: FontData<'a>) -> Option<Result<BaseCoord<'a>, ReadError>> {
925 self.min_coord_offset().resolve(data)
926 }
927
928 pub fn max_coord_offset(&self) -> Nullable<Offset16> {
931 self.max_coord_offset.get()
932 }
933
934 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 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 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 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#[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 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 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#[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 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 pub fn coordinate(&self) -> i16 {
1187 let range = self.coordinate_byte_range();
1188 self.data.read_at(range.start).ok().unwrap()
1189 }
1190
1191 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 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#[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 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 pub fn coordinate(&self) -> i16 {
1291 let range = self.coordinate_byte_range();
1292 self.data.read_at(range.start).ok().unwrap()
1293 }
1294
1295 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 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}