1#[allow(unused_imports)]
6use crate::codegen_prelude::*;
7
8impl<'a> MinByteRange<'a> for Gsub<'a> {
9 fn min_byte_range(&self) -> Range<usize> {
10 0..self.lookup_list_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 Gsub<'_> {
19 const TAG: Tag = Tag::new(b"GSUB");
21}
22
23impl ReadArgs for Gsub<'_> {
24 type Args = ();
25}
26
27impl<'a> FontRead<'a> for Gsub<'a> {
28 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
29 #[allow(clippy::absurd_extreme_comparisons)]
30 if data.len() < Self::MIN_SIZE {
31 return Err(ReadError::OutOfBounds);
32 }
33 Ok(Self { data })
34 }
35}
36
37#[derive(Clone)]
39pub struct Gsub<'a> {
40 data: FontData<'a>,
41}
42
43#[allow(clippy::needless_lifetimes)]
44impl<'a> Gsub<'a> {
45 pub const MIN_SIZE: usize = (MajorMinor::RAW_BYTE_LEN
46 + Offset16::RAW_BYTE_LEN
47 + Offset16::RAW_BYTE_LEN
48 + Offset16::RAW_BYTE_LEN);
49 basic_table_impls!(impl_the_methods);
50
51 pub fn version(&self) -> MajorMinor {
53 let range = self.version_byte_range();
54 self.data.read_at(range.start).ok().unwrap()
55 }
56
57 pub fn script_list_offset(&self) -> Offset16 {
59 let range = self.script_list_offset_byte_range();
60 self.data.read_at(range.start).ok().unwrap()
61 }
62
63 pub fn script_list(&self) -> Result<ScriptList<'a>, ReadError> {
65 let data = self.data;
66 self.script_list_offset().resolve(data)
67 }
68
69 pub fn feature_list_offset(&self) -> Offset16 {
71 let range = self.feature_list_offset_byte_range();
72 self.data.read_at(range.start).ok().unwrap()
73 }
74
75 pub fn feature_list(&self) -> Result<FeatureList<'a>, ReadError> {
77 let data = self.data;
78 self.feature_list_offset().resolve(data)
79 }
80
81 pub fn lookup_list_offset(&self) -> Offset16 {
83 let range = self.lookup_list_offset_byte_range();
84 self.data.read_at(range.start).ok().unwrap()
85 }
86
87 pub fn lookup_list(&self) -> Result<SubstitutionLookupList<'a>, ReadError> {
89 let data = self.data;
90 self.lookup_list_offset().resolve(data)
91 }
92
93 pub fn feature_variations_offset(&self) -> Option<Nullable<Offset32>> {
96 let range = self.feature_variations_offset_byte_range();
97 (!range.is_empty())
98 .then(|| self.data.read_at(range.start).ok())
99 .flatten()
100 }
101
102 pub fn feature_variations(&self) -> Option<Result<FeatureVariations<'a>, ReadError>> {
104 let data = self.data;
105 self.feature_variations_offset().map(|x| x.resolve(data))?
106 }
107
108 pub fn version_byte_range(&self) -> Range<usize> {
109 let start = 0;
110 let end = start + MajorMinor::RAW_BYTE_LEN;
111 start..end
112 }
113
114 pub fn script_list_offset_byte_range(&self) -> Range<usize> {
115 let start = self.version_byte_range().end;
116 let end = start + Offset16::RAW_BYTE_LEN;
117 start..end
118 }
119
120 pub fn feature_list_offset_byte_range(&self) -> Range<usize> {
121 let start = self.script_list_offset_byte_range().end;
122 let end = start + Offset16::RAW_BYTE_LEN;
123 start..end
124 }
125
126 pub fn lookup_list_offset_byte_range(&self) -> Range<usize> {
127 let start = self.feature_list_offset_byte_range().end;
128 let end = start + Offset16::RAW_BYTE_LEN;
129 start..end
130 }
131
132 pub fn feature_variations_offset_byte_range(&self) -> Range<usize> {
133 let start = self.lookup_list_offset_byte_range().end;
134 let end = if self.version().compatible((1u16, 1u16)) {
135 start + Offset32::RAW_BYTE_LEN
136 } else {
137 start
138 };
139 start..end
140 }
141}
142
143const _: () = assert!(FontData::default_data_long_enough(Gsub::MIN_SIZE));
144
145impl Default for Gsub<'_> {
146 fn default() -> Self {
147 Self {
148 data: FontData::default_table_data(),
149 }
150 }
151}
152
153#[cfg(feature = "experimental_traverse")]
154impl<'a> SomeTable<'a> for Gsub<'a> {
155 fn type_name(&self) -> &str {
156 "Gsub"
157 }
158 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
159 match idx {
160 0usize => Some(Field::new("version", self.version())),
161 1usize => Some(Field::new(
162 "script_list_offset",
163 FieldType::offset(self.script_list_offset(), self.script_list()),
164 )),
165 2usize => Some(Field::new(
166 "feature_list_offset",
167 FieldType::offset(self.feature_list_offset(), self.feature_list()),
168 )),
169 3usize => Some(Field::new(
170 "lookup_list_offset",
171 FieldType::offset(self.lookup_list_offset(), self.lookup_list()),
172 )),
173 4usize if self.version().compatible((1u16, 1u16)) => Some(Field::new(
174 "feature_variations_offset",
175 FieldType::offset(
176 self.feature_variations_offset().unwrap(),
177 self.feature_variations(),
178 ),
179 )),
180 _ => None,
181 }
182 }
183}
184
185#[cfg(feature = "experimental_traverse")]
186#[allow(clippy::needless_lifetimes)]
187impl<'a> std::fmt::Debug for Gsub<'a> {
188 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189 (self as &dyn SomeTable<'a>).fmt(f)
190 }
191}
192
193pub enum SubstitutionLookup<'a> {
195 Single(Lookup<'a, SingleSubst<'a>>),
196 Multiple(Lookup<'a, MultipleSubstFormat1<'a>>),
197 Alternate(Lookup<'a, AlternateSubstFormat1<'a>>),
198 Ligature(Lookup<'a, LigatureSubstFormat1<'a>>),
199 Contextual(Lookup<'a, SubstitutionSequenceContext<'a>>),
200 ChainContextual(Lookup<'a, SubstitutionChainContext<'a>>),
201 Extension(Lookup<'a, ExtensionSubtable<'a>>),
202 Reverse(Lookup<'a, ReverseChainSingleSubstFormat1<'a>>),
203}
204
205impl Default for SubstitutionLookup<'_> {
206 fn default() -> Self {
207 Self::Single(Default::default())
208 }
209}
210
211impl ReadArgs for SubstitutionLookup<'_> {
212 type Args = ();
213}
214
215impl<'a> FontRead<'a> for SubstitutionLookup<'a> {
216 fn read_with_args(bytes: FontData<'a>, _: ()) -> Result<Self, ReadError> {
217 let discriminant = Lookup::read_discriminant(bytes)?;
218 match discriminant {
219 1 => Ok(SubstitutionLookup::Single(FontRead::read(bytes)?)),
220 2 => Ok(SubstitutionLookup::Multiple(FontRead::read(bytes)?)),
221 3 => Ok(SubstitutionLookup::Alternate(FontRead::read(bytes)?)),
222 4 => Ok(SubstitutionLookup::Ligature(FontRead::read(bytes)?)),
223 5 => Ok(SubstitutionLookup::Contextual(FontRead::read(bytes)?)),
224 6 => Ok(SubstitutionLookup::ChainContextual(FontRead::read(bytes)?)),
225 7 => Ok(SubstitutionLookup::Extension(FontRead::read(bytes)?)),
226 8 => Ok(SubstitutionLookup::Reverse(FontRead::read(bytes)?)),
227 other => Err(ReadError::InvalidFormat(other.into())),
228 }
229 }
230}
231
232impl<'a> SubstitutionLookup<'a> {
233 #[allow(dead_code)]
234 pub(crate) fn of_unit_type(&self) -> Lookup<'a, ()> {
238 match self {
239 SubstitutionLookup::Single(inner) => inner.of_unit_type(),
240 SubstitutionLookup::Multiple(inner) => inner.of_unit_type(),
241 SubstitutionLookup::Alternate(inner) => inner.of_unit_type(),
242 SubstitutionLookup::Ligature(inner) => inner.of_unit_type(),
243 SubstitutionLookup::Contextual(inner) => inner.of_unit_type(),
244 SubstitutionLookup::ChainContextual(inner) => inner.of_unit_type(),
245 SubstitutionLookup::Extension(inner) => inner.of_unit_type(),
246 SubstitutionLookup::Reverse(inner) => inner.of_unit_type(),
247 }
248 }
249}
250
251#[cfg(feature = "experimental_traverse")]
252impl<'a> SubstitutionLookup<'a> {
253 fn dyn_inner(&self) -> &(dyn SomeTable<'a> + 'a) {
254 match self {
255 SubstitutionLookup::Single(table) => table,
256 SubstitutionLookup::Multiple(table) => table,
257 SubstitutionLookup::Alternate(table) => table,
258 SubstitutionLookup::Ligature(table) => table,
259 SubstitutionLookup::Contextual(table) => table,
260 SubstitutionLookup::ChainContextual(table) => table,
261 SubstitutionLookup::Extension(table) => table,
262 SubstitutionLookup::Reverse(table) => table,
263 }
264 }
265}
266
267#[cfg(feature = "experimental_traverse")]
268impl<'a> SomeTable<'a> for SubstitutionLookup<'a> {
269 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
270 self.dyn_inner().get_field(idx)
271 }
272 fn type_name(&self) -> &str {
273 self.dyn_inner().type_name()
274 }
275}
276
277#[cfg(feature = "experimental_traverse")]
278impl std::fmt::Debug for SubstitutionLookup<'_> {
279 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
280 self.dyn_inner().fmt(f)
281 }
282}
283
284#[derive(Clone)]
286pub enum SingleSubst<'a> {
287 Format1(SingleSubstFormat1<'a>),
288 Format2(SingleSubstFormat2<'a>),
289}
290
291impl Default for SingleSubst<'_> {
292 fn default() -> Self {
293 Self::Format1(Default::default())
294 }
295}
296
297impl<'a> SingleSubst<'a> {
298 pub fn offset_data(&self) -> FontData<'a> {
300 match self {
301 Self::Format1(item) => item.offset_data(),
302 Self::Format2(item) => item.offset_data(),
303 }
304 }
305
306 pub fn subst_format(&self) -> u16 {
308 match self {
309 Self::Format1(item) => item.subst_format(),
310 Self::Format2(item) => item.subst_format(),
311 }
312 }
313
314 pub fn coverage_offset(&self) -> Offset16 {
317 match self {
318 Self::Format1(item) => item.coverage_offset(),
319 Self::Format2(item) => item.coverage_offset(),
320 }
321 }
322}
323
324impl ReadArgs for SingleSubst<'_> {
325 type Args = ();
326}
327
328impl<'a> FontRead<'a> for SingleSubst<'a> {
329 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
330 let format: u16 = data.read_at(0usize)?;
331 match format {
332 SingleSubstFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
333 SingleSubstFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
334 other => Err(ReadError::InvalidFormat(other.into())),
335 }
336 }
337}
338
339impl<'a> MinByteRange<'a> for SingleSubst<'a> {
340 fn min_byte_range(&self) -> Range<usize> {
341 match self {
342 Self::Format1(item) => item.min_byte_range(),
343 Self::Format2(item) => item.min_byte_range(),
344 }
345 }
346 fn min_table_bytes(&self) -> &'a [u8] {
347 match self {
348 Self::Format1(item) => item.min_table_bytes(),
349 Self::Format2(item) => item.min_table_bytes(),
350 }
351 }
352}
353
354#[cfg(feature = "experimental_traverse")]
355impl<'a> SingleSubst<'a> {
356 fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
357 match self {
358 Self::Format1(table) => table,
359 Self::Format2(table) => table,
360 }
361 }
362}
363
364#[cfg(feature = "experimental_traverse")]
365impl std::fmt::Debug for SingleSubst<'_> {
366 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
367 self.dyn_inner().fmt(f)
368 }
369}
370
371#[cfg(feature = "experimental_traverse")]
372impl<'a> SomeTable<'a> for SingleSubst<'a> {
373 fn type_name(&self) -> &str {
374 self.dyn_inner().type_name()
375 }
376 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
377 self.dyn_inner().get_field(idx)
378 }
379}
380
381impl Format<u16> for SingleSubstFormat1<'_> {
382 const FORMAT: u16 = 1;
383}
384
385impl<'a> MinByteRange<'a> for SingleSubstFormat1<'a> {
386 fn min_byte_range(&self) -> Range<usize> {
387 0..self.delta_glyph_id_byte_range().end
388 }
389 fn min_table_bytes(&self) -> &'a [u8] {
390 let range = self.min_byte_range();
391 self.data.as_bytes().get(range).unwrap_or_default()
392 }
393}
394
395impl ReadArgs for SingleSubstFormat1<'_> {
396 type Args = ();
397}
398
399impl<'a> FontRead<'a> for SingleSubstFormat1<'a> {
400 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
401 #[allow(clippy::absurd_extreme_comparisons)]
402 if data.len() < Self::MIN_SIZE {
403 return Err(ReadError::OutOfBounds);
404 }
405 Ok(Self { data })
406 }
407}
408
409#[derive(Clone)]
411pub struct SingleSubstFormat1<'a> {
412 data: FontData<'a>,
413}
414
415#[allow(clippy::needless_lifetimes)]
416impl<'a> SingleSubstFormat1<'a> {
417 pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN);
418 basic_table_impls!(impl_the_methods);
419
420 pub fn subst_format(&self) -> u16 {
422 let range = self.subst_format_byte_range();
423 self.data.read_at(range.start).ok().unwrap()
424 }
425
426 pub fn coverage_offset(&self) -> Offset16 {
429 let range = self.coverage_offset_byte_range();
430 self.data.read_at(range.start).ok().unwrap()
431 }
432
433 pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
435 let data = self.data;
436 self.coverage_offset().resolve(data)
437 }
438
439 pub fn delta_glyph_id(&self) -> i16 {
441 let range = self.delta_glyph_id_byte_range();
442 self.data.read_at(range.start).ok().unwrap()
443 }
444
445 pub fn subst_format_byte_range(&self) -> Range<usize> {
446 let start = 0;
447 let end = start + u16::RAW_BYTE_LEN;
448 start..end
449 }
450
451 pub fn coverage_offset_byte_range(&self) -> Range<usize> {
452 let start = self.subst_format_byte_range().end;
453 let end = start + Offset16::RAW_BYTE_LEN;
454 start..end
455 }
456
457 pub fn delta_glyph_id_byte_range(&self) -> Range<usize> {
458 let start = self.coverage_offset_byte_range().end;
459 let end = start + i16::RAW_BYTE_LEN;
460 start..end
461 }
462}
463
464const _: () = assert!(FontData::default_data_long_enough(
465 SingleSubstFormat1::MIN_SIZE
466));
467
468impl Default for SingleSubstFormat1<'_> {
469 fn default() -> Self {
470 Self {
471 data: FontData::default_format_1_u16_table_data(),
472 }
473 }
474}
475
476#[cfg(feature = "experimental_traverse")]
477impl<'a> SomeTable<'a> for SingleSubstFormat1<'a> {
478 fn type_name(&self) -> &str {
479 "SingleSubstFormat1"
480 }
481 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
482 match idx {
483 0usize => Some(Field::new("subst_format", self.subst_format())),
484 1usize => Some(Field::new(
485 "coverage_offset",
486 FieldType::offset(self.coverage_offset(), self.coverage()),
487 )),
488 2usize => Some(Field::new("delta_glyph_id", self.delta_glyph_id())),
489 _ => None,
490 }
491 }
492}
493
494#[cfg(feature = "experimental_traverse")]
495#[allow(clippy::needless_lifetimes)]
496impl<'a> std::fmt::Debug for SingleSubstFormat1<'a> {
497 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
498 (self as &dyn SomeTable<'a>).fmt(f)
499 }
500}
501
502impl Format<u16> for SingleSubstFormat2<'_> {
503 const FORMAT: u16 = 2;
504}
505
506impl<'a> MinByteRange<'a> for SingleSubstFormat2<'a> {
507 fn min_byte_range(&self) -> Range<usize> {
508 0..self.substitute_glyph_ids_byte_range().end
509 }
510 fn min_table_bytes(&self) -> &'a [u8] {
511 let range = self.min_byte_range();
512 self.data.as_bytes().get(range).unwrap_or_default()
513 }
514}
515
516impl ReadArgs for SingleSubstFormat2<'_> {
517 type Args = ();
518}
519
520impl<'a> FontRead<'a> for SingleSubstFormat2<'a> {
521 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
522 #[allow(clippy::absurd_extreme_comparisons)]
523 if data.len() < Self::MIN_SIZE {
524 return Err(ReadError::OutOfBounds);
525 }
526 Ok(Self { data })
527 }
528}
529
530#[derive(Clone)]
532pub struct SingleSubstFormat2<'a> {
533 data: FontData<'a>,
534}
535
536#[allow(clippy::needless_lifetimes)]
537impl<'a> SingleSubstFormat2<'a> {
538 pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
539 basic_table_impls!(impl_the_methods);
540
541 pub fn subst_format(&self) -> u16 {
543 let range = self.subst_format_byte_range();
544 self.data.read_at(range.start).ok().unwrap()
545 }
546
547 pub fn coverage_offset(&self) -> Offset16 {
550 let range = self.coverage_offset_byte_range();
551 self.data.read_at(range.start).ok().unwrap()
552 }
553
554 pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
556 let data = self.data;
557 self.coverage_offset().resolve(data)
558 }
559
560 pub fn glyph_count(&self) -> u16 {
562 let range = self.glyph_count_byte_range();
563 self.data.read_at(range.start).ok().unwrap()
564 }
565
566 pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
568 let range = self.substitute_glyph_ids_byte_range();
569 self.data.read_array(range).ok().unwrap_or_default()
570 }
571
572 pub fn subst_format_byte_range(&self) -> Range<usize> {
573 let start = 0;
574 let end = start + u16::RAW_BYTE_LEN;
575 start..end
576 }
577
578 pub fn coverage_offset_byte_range(&self) -> Range<usize> {
579 let start = self.subst_format_byte_range().end;
580 let end = start + Offset16::RAW_BYTE_LEN;
581 start..end
582 }
583
584 pub fn glyph_count_byte_range(&self) -> Range<usize> {
585 let start = self.coverage_offset_byte_range().end;
586 let end = start + u16::RAW_BYTE_LEN;
587 start..end
588 }
589
590 pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
591 let glyph_count = self.glyph_count();
592 let start = self.glyph_count_byte_range().end;
593 let end =
594 start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN);
595 start..end
596 }
597}
598
599#[cfg(feature = "experimental_traverse")]
600impl<'a> SomeTable<'a> for SingleSubstFormat2<'a> {
601 fn type_name(&self) -> &str {
602 "SingleSubstFormat2"
603 }
604 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
605 match idx {
606 0usize => Some(Field::new("subst_format", self.subst_format())),
607 1usize => Some(Field::new(
608 "coverage_offset",
609 FieldType::offset(self.coverage_offset(), self.coverage()),
610 )),
611 2usize => Some(Field::new("glyph_count", self.glyph_count())),
612 3usize => Some(Field::new(
613 "substitute_glyph_ids",
614 self.substitute_glyph_ids(),
615 )),
616 _ => None,
617 }
618 }
619}
620
621#[cfg(feature = "experimental_traverse")]
622#[allow(clippy::needless_lifetimes)]
623impl<'a> std::fmt::Debug for SingleSubstFormat2<'a> {
624 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
625 (self as &dyn SomeTable<'a>).fmt(f)
626 }
627}
628
629impl Format<u16> for MultipleSubstFormat1<'_> {
630 const FORMAT: u16 = 1;
631}
632
633impl<'a> MinByteRange<'a> for MultipleSubstFormat1<'a> {
634 fn min_byte_range(&self) -> Range<usize> {
635 0..self.sequence_offsets_byte_range().end
636 }
637 fn min_table_bytes(&self) -> &'a [u8] {
638 let range = self.min_byte_range();
639 self.data.as_bytes().get(range).unwrap_or_default()
640 }
641}
642
643impl ReadArgs for MultipleSubstFormat1<'_> {
644 type Args = ();
645}
646
647impl<'a> FontRead<'a> for MultipleSubstFormat1<'a> {
648 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
649 #[allow(clippy::absurd_extreme_comparisons)]
650 if data.len() < Self::MIN_SIZE {
651 return Err(ReadError::OutOfBounds);
652 }
653 Ok(Self { data })
654 }
655}
656
657#[derive(Clone)]
659pub struct MultipleSubstFormat1<'a> {
660 data: FontData<'a>,
661}
662
663#[allow(clippy::needless_lifetimes)]
664impl<'a> MultipleSubstFormat1<'a> {
665 pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
666 basic_table_impls!(impl_the_methods);
667
668 pub fn subst_format(&self) -> u16 {
670 let range = self.subst_format_byte_range();
671 self.data.read_at(range.start).ok().unwrap()
672 }
673
674 pub fn coverage_offset(&self) -> Offset16 {
677 let range = self.coverage_offset_byte_range();
678 self.data.read_at(range.start).ok().unwrap()
679 }
680
681 pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
683 let data = self.data;
684 self.coverage_offset().resolve(data)
685 }
686
687 pub fn sequence_count(&self) -> u16 {
689 let range = self.sequence_count_byte_range();
690 self.data.read_at(range.start).ok().unwrap()
691 }
692
693 pub fn sequence_offsets(&self) -> &'a [BigEndian<Offset16>] {
696 let range = self.sequence_offsets_byte_range();
697 self.data.read_array(range).ok().unwrap_or_default()
698 }
699
700 pub fn sequences(&self) -> ArrayOfOffsets<'a, Sequence<'a>, Offset16> {
702 let data = self.data;
703 let offsets = self.sequence_offsets();
704 ArrayOfOffsets::new(offsets, data, ())
705 }
706
707 pub fn subst_format_byte_range(&self) -> Range<usize> {
708 let start = 0;
709 let end = start + u16::RAW_BYTE_LEN;
710 start..end
711 }
712
713 pub fn coverage_offset_byte_range(&self) -> Range<usize> {
714 let start = self.subst_format_byte_range().end;
715 let end = start + Offset16::RAW_BYTE_LEN;
716 start..end
717 }
718
719 pub fn sequence_count_byte_range(&self) -> Range<usize> {
720 let start = self.coverage_offset_byte_range().end;
721 let end = start + u16::RAW_BYTE_LEN;
722 start..end
723 }
724
725 pub fn sequence_offsets_byte_range(&self) -> Range<usize> {
726 let sequence_count = self.sequence_count();
727 let start = self.sequence_count_byte_range().end;
728 let end =
729 start + (transforms::to_usize(sequence_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
730 start..end
731 }
732}
733
734const _: () = assert!(FontData::default_data_long_enough(
735 MultipleSubstFormat1::MIN_SIZE
736));
737
738impl Default for MultipleSubstFormat1<'_> {
739 fn default() -> Self {
740 Self {
741 data: FontData::default_format_1_u16_table_data(),
742 }
743 }
744}
745
746#[cfg(feature = "experimental_traverse")]
747impl<'a> SomeTable<'a> for MultipleSubstFormat1<'a> {
748 fn type_name(&self) -> &str {
749 "MultipleSubstFormat1"
750 }
751 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
752 match idx {
753 0usize => Some(Field::new("subst_format", self.subst_format())),
754 1usize => Some(Field::new(
755 "coverage_offset",
756 FieldType::offset(self.coverage_offset(), self.coverage()),
757 )),
758 2usize => Some(Field::new("sequence_count", self.sequence_count())),
759 3usize => Some(Field::new(
760 "sequence_offsets",
761 FieldType::from(self.sequences()),
762 )),
763 _ => None,
764 }
765 }
766}
767
768#[cfg(feature = "experimental_traverse")]
769#[allow(clippy::needless_lifetimes)]
770impl<'a> std::fmt::Debug for MultipleSubstFormat1<'a> {
771 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
772 (self as &dyn SomeTable<'a>).fmt(f)
773 }
774}
775
776impl<'a> MinByteRange<'a> for Sequence<'a> {
777 fn min_byte_range(&self) -> Range<usize> {
778 0..self.substitute_glyph_ids_byte_range().end
779 }
780 fn min_table_bytes(&self) -> &'a [u8] {
781 let range = self.min_byte_range();
782 self.data.as_bytes().get(range).unwrap_or_default()
783 }
784}
785
786impl ReadArgs for Sequence<'_> {
787 type Args = ();
788}
789
790impl<'a> FontRead<'a> for Sequence<'a> {
791 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
792 #[allow(clippy::absurd_extreme_comparisons)]
793 if data.len() < Self::MIN_SIZE {
794 return Err(ReadError::OutOfBounds);
795 }
796 Ok(Self { data })
797 }
798}
799
800#[derive(Clone)]
802pub struct Sequence<'a> {
803 data: FontData<'a>,
804}
805
806#[allow(clippy::needless_lifetimes)]
807impl<'a> Sequence<'a> {
808 pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
809 basic_table_impls!(impl_the_methods);
810
811 pub fn glyph_count(&self) -> u16 {
814 let range = self.glyph_count_byte_range();
815 self.data.read_at(range.start).ok().unwrap()
816 }
817
818 pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
820 let range = self.substitute_glyph_ids_byte_range();
821 self.data.read_array(range).ok().unwrap_or_default()
822 }
823
824 pub fn glyph_count_byte_range(&self) -> Range<usize> {
825 let start = 0;
826 let end = start + u16::RAW_BYTE_LEN;
827 start..end
828 }
829
830 pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
831 let glyph_count = self.glyph_count();
832 let start = self.glyph_count_byte_range().end;
833 let end =
834 start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN);
835 start..end
836 }
837}
838
839const _: () = assert!(FontData::default_data_long_enough(Sequence::MIN_SIZE));
840
841impl Default for Sequence<'_> {
842 fn default() -> Self {
843 Self {
844 data: FontData::default_table_data(),
845 }
846 }
847}
848
849#[cfg(feature = "experimental_traverse")]
850impl<'a> SomeTable<'a> for Sequence<'a> {
851 fn type_name(&self) -> &str {
852 "Sequence"
853 }
854 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
855 match idx {
856 0usize => Some(Field::new("glyph_count", self.glyph_count())),
857 1usize => Some(Field::new(
858 "substitute_glyph_ids",
859 self.substitute_glyph_ids(),
860 )),
861 _ => None,
862 }
863 }
864}
865
866#[cfg(feature = "experimental_traverse")]
867#[allow(clippy::needless_lifetimes)]
868impl<'a> std::fmt::Debug for Sequence<'a> {
869 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
870 (self as &dyn SomeTable<'a>).fmt(f)
871 }
872}
873
874impl Format<u16> for AlternateSubstFormat1<'_> {
875 const FORMAT: u16 = 1;
876}
877
878impl<'a> MinByteRange<'a> for AlternateSubstFormat1<'a> {
879 fn min_byte_range(&self) -> Range<usize> {
880 0..self.alternate_set_offsets_byte_range().end
881 }
882 fn min_table_bytes(&self) -> &'a [u8] {
883 let range = self.min_byte_range();
884 self.data.as_bytes().get(range).unwrap_or_default()
885 }
886}
887
888impl ReadArgs for AlternateSubstFormat1<'_> {
889 type Args = ();
890}
891
892impl<'a> FontRead<'a> for AlternateSubstFormat1<'a> {
893 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
894 #[allow(clippy::absurd_extreme_comparisons)]
895 if data.len() < Self::MIN_SIZE {
896 return Err(ReadError::OutOfBounds);
897 }
898 Ok(Self { data })
899 }
900}
901
902#[derive(Clone)]
904pub struct AlternateSubstFormat1<'a> {
905 data: FontData<'a>,
906}
907
908#[allow(clippy::needless_lifetimes)]
909impl<'a> AlternateSubstFormat1<'a> {
910 pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
911 basic_table_impls!(impl_the_methods);
912
913 pub fn subst_format(&self) -> u16 {
915 let range = self.subst_format_byte_range();
916 self.data.read_at(range.start).ok().unwrap()
917 }
918
919 pub fn coverage_offset(&self) -> Offset16 {
922 let range = self.coverage_offset_byte_range();
923 self.data.read_at(range.start).ok().unwrap()
924 }
925
926 pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
928 let data = self.data;
929 self.coverage_offset().resolve(data)
930 }
931
932 pub fn alternate_set_count(&self) -> u16 {
934 let range = self.alternate_set_count_byte_range();
935 self.data.read_at(range.start).ok().unwrap()
936 }
937
938 pub fn alternate_set_offsets(&self) -> &'a [BigEndian<Offset16>] {
941 let range = self.alternate_set_offsets_byte_range();
942 self.data.read_array(range).ok().unwrap_or_default()
943 }
944
945 pub fn alternate_sets(&self) -> ArrayOfOffsets<'a, AlternateSet<'a>, Offset16> {
947 let data = self.data;
948 let offsets = self.alternate_set_offsets();
949 ArrayOfOffsets::new(offsets, data, ())
950 }
951
952 pub fn subst_format_byte_range(&self) -> Range<usize> {
953 let start = 0;
954 let end = start + u16::RAW_BYTE_LEN;
955 start..end
956 }
957
958 pub fn coverage_offset_byte_range(&self) -> Range<usize> {
959 let start = self.subst_format_byte_range().end;
960 let end = start + Offset16::RAW_BYTE_LEN;
961 start..end
962 }
963
964 pub fn alternate_set_count_byte_range(&self) -> Range<usize> {
965 let start = self.coverage_offset_byte_range().end;
966 let end = start + u16::RAW_BYTE_LEN;
967 start..end
968 }
969
970 pub fn alternate_set_offsets_byte_range(&self) -> Range<usize> {
971 let alternate_set_count = self.alternate_set_count();
972 let start = self.alternate_set_count_byte_range().end;
973 let end = start
974 + (transforms::to_usize(alternate_set_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
975 start..end
976 }
977}
978
979const _: () = assert!(FontData::default_data_long_enough(
980 AlternateSubstFormat1::MIN_SIZE
981));
982
983impl Default for AlternateSubstFormat1<'_> {
984 fn default() -> Self {
985 Self {
986 data: FontData::default_format_1_u16_table_data(),
987 }
988 }
989}
990
991#[cfg(feature = "experimental_traverse")]
992impl<'a> SomeTable<'a> for AlternateSubstFormat1<'a> {
993 fn type_name(&self) -> &str {
994 "AlternateSubstFormat1"
995 }
996 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
997 match idx {
998 0usize => Some(Field::new("subst_format", self.subst_format())),
999 1usize => Some(Field::new(
1000 "coverage_offset",
1001 FieldType::offset(self.coverage_offset(), self.coverage()),
1002 )),
1003 2usize => Some(Field::new(
1004 "alternate_set_count",
1005 self.alternate_set_count(),
1006 )),
1007 3usize => Some(Field::new(
1008 "alternate_set_offsets",
1009 FieldType::from(self.alternate_sets()),
1010 )),
1011 _ => None,
1012 }
1013 }
1014}
1015
1016#[cfg(feature = "experimental_traverse")]
1017#[allow(clippy::needless_lifetimes)]
1018impl<'a> std::fmt::Debug for AlternateSubstFormat1<'a> {
1019 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1020 (self as &dyn SomeTable<'a>).fmt(f)
1021 }
1022}
1023
1024impl<'a> MinByteRange<'a> for AlternateSet<'a> {
1025 fn min_byte_range(&self) -> Range<usize> {
1026 0..self.alternate_glyph_ids_byte_range().end
1027 }
1028 fn min_table_bytes(&self) -> &'a [u8] {
1029 let range = self.min_byte_range();
1030 self.data.as_bytes().get(range).unwrap_or_default()
1031 }
1032}
1033
1034impl ReadArgs for AlternateSet<'_> {
1035 type Args = ();
1036}
1037
1038impl<'a> FontRead<'a> for AlternateSet<'a> {
1039 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1040 #[allow(clippy::absurd_extreme_comparisons)]
1041 if data.len() < Self::MIN_SIZE {
1042 return Err(ReadError::OutOfBounds);
1043 }
1044 Ok(Self { data })
1045 }
1046}
1047
1048#[derive(Clone)]
1050pub struct AlternateSet<'a> {
1051 data: FontData<'a>,
1052}
1053
1054#[allow(clippy::needless_lifetimes)]
1055impl<'a> AlternateSet<'a> {
1056 pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
1057 basic_table_impls!(impl_the_methods);
1058
1059 pub fn glyph_count(&self) -> u16 {
1061 let range = self.glyph_count_byte_range();
1062 self.data.read_at(range.start).ok().unwrap()
1063 }
1064
1065 pub fn alternate_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
1067 let range = self.alternate_glyph_ids_byte_range();
1068 self.data.read_array(range).ok().unwrap_or_default()
1069 }
1070
1071 pub fn glyph_count_byte_range(&self) -> Range<usize> {
1072 let start = 0;
1073 let end = start + u16::RAW_BYTE_LEN;
1074 start..end
1075 }
1076
1077 pub fn alternate_glyph_ids_byte_range(&self) -> Range<usize> {
1078 let glyph_count = self.glyph_count();
1079 let start = self.glyph_count_byte_range().end;
1080 let end =
1081 start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN);
1082 start..end
1083 }
1084}
1085
1086const _: () = assert!(FontData::default_data_long_enough(AlternateSet::MIN_SIZE));
1087
1088impl Default for AlternateSet<'_> {
1089 fn default() -> Self {
1090 Self {
1091 data: FontData::default_table_data(),
1092 }
1093 }
1094}
1095
1096#[cfg(feature = "experimental_traverse")]
1097impl<'a> SomeTable<'a> for AlternateSet<'a> {
1098 fn type_name(&self) -> &str {
1099 "AlternateSet"
1100 }
1101 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1102 match idx {
1103 0usize => Some(Field::new("glyph_count", self.glyph_count())),
1104 1usize => Some(Field::new(
1105 "alternate_glyph_ids",
1106 self.alternate_glyph_ids(),
1107 )),
1108 _ => None,
1109 }
1110 }
1111}
1112
1113#[cfg(feature = "experimental_traverse")]
1114#[allow(clippy::needless_lifetimes)]
1115impl<'a> std::fmt::Debug for AlternateSet<'a> {
1116 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1117 (self as &dyn SomeTable<'a>).fmt(f)
1118 }
1119}
1120
1121impl Format<u16> for LigatureSubstFormat1<'_> {
1122 const FORMAT: u16 = 1;
1123}
1124
1125impl<'a> MinByteRange<'a> for LigatureSubstFormat1<'a> {
1126 fn min_byte_range(&self) -> Range<usize> {
1127 0..self.ligature_set_offsets_byte_range().end
1128 }
1129 fn min_table_bytes(&self) -> &'a [u8] {
1130 let range = self.min_byte_range();
1131 self.data.as_bytes().get(range).unwrap_or_default()
1132 }
1133}
1134
1135impl ReadArgs for LigatureSubstFormat1<'_> {
1136 type Args = ();
1137}
1138
1139impl<'a> FontRead<'a> for LigatureSubstFormat1<'a> {
1140 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1141 #[allow(clippy::absurd_extreme_comparisons)]
1142 if data.len() < Self::MIN_SIZE {
1143 return Err(ReadError::OutOfBounds);
1144 }
1145 Ok(Self { data })
1146 }
1147}
1148
1149#[derive(Clone)]
1151pub struct LigatureSubstFormat1<'a> {
1152 data: FontData<'a>,
1153}
1154
1155#[allow(clippy::needless_lifetimes)]
1156impl<'a> LigatureSubstFormat1<'a> {
1157 pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1158 basic_table_impls!(impl_the_methods);
1159
1160 pub fn subst_format(&self) -> u16 {
1162 let range = self.subst_format_byte_range();
1163 self.data.read_at(range.start).ok().unwrap()
1164 }
1165
1166 pub fn coverage_offset(&self) -> Offset16 {
1169 let range = self.coverage_offset_byte_range();
1170 self.data.read_at(range.start).ok().unwrap()
1171 }
1172
1173 pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1175 let data = self.data;
1176 self.coverage_offset().resolve(data)
1177 }
1178
1179 pub fn ligature_set_count(&self) -> u16 {
1181 let range = self.ligature_set_count_byte_range();
1182 self.data.read_at(range.start).ok().unwrap()
1183 }
1184
1185 pub fn ligature_set_offsets(&self) -> &'a [BigEndian<Offset16>] {
1188 let range = self.ligature_set_offsets_byte_range();
1189 self.data.read_array(range).ok().unwrap_or_default()
1190 }
1191
1192 pub fn ligature_sets(&self) -> ArrayOfOffsets<'a, LigatureSet<'a>, Offset16> {
1194 let data = self.data;
1195 let offsets = self.ligature_set_offsets();
1196 ArrayOfOffsets::new(offsets, data, ())
1197 }
1198
1199 pub fn subst_format_byte_range(&self) -> Range<usize> {
1200 let start = 0;
1201 let end = start + u16::RAW_BYTE_LEN;
1202 start..end
1203 }
1204
1205 pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1206 let start = self.subst_format_byte_range().end;
1207 let end = start + Offset16::RAW_BYTE_LEN;
1208 start..end
1209 }
1210
1211 pub fn ligature_set_count_byte_range(&self) -> Range<usize> {
1212 let start = self.coverage_offset_byte_range().end;
1213 let end = start + u16::RAW_BYTE_LEN;
1214 start..end
1215 }
1216
1217 pub fn ligature_set_offsets_byte_range(&self) -> Range<usize> {
1218 let ligature_set_count = self.ligature_set_count();
1219 let start = self.ligature_set_count_byte_range().end;
1220 let end = start
1221 + (transforms::to_usize(ligature_set_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
1222 start..end
1223 }
1224}
1225
1226const _: () = assert!(FontData::default_data_long_enough(
1227 LigatureSubstFormat1::MIN_SIZE
1228));
1229
1230impl Default for LigatureSubstFormat1<'_> {
1231 fn default() -> Self {
1232 Self {
1233 data: FontData::default_format_1_u16_table_data(),
1234 }
1235 }
1236}
1237
1238#[cfg(feature = "experimental_traverse")]
1239impl<'a> SomeTable<'a> for LigatureSubstFormat1<'a> {
1240 fn type_name(&self) -> &str {
1241 "LigatureSubstFormat1"
1242 }
1243 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1244 match idx {
1245 0usize => Some(Field::new("subst_format", self.subst_format())),
1246 1usize => Some(Field::new(
1247 "coverage_offset",
1248 FieldType::offset(self.coverage_offset(), self.coverage()),
1249 )),
1250 2usize => Some(Field::new("ligature_set_count", self.ligature_set_count())),
1251 3usize => Some(Field::new(
1252 "ligature_set_offsets",
1253 FieldType::from(self.ligature_sets()),
1254 )),
1255 _ => None,
1256 }
1257 }
1258}
1259
1260#[cfg(feature = "experimental_traverse")]
1261#[allow(clippy::needless_lifetimes)]
1262impl<'a> std::fmt::Debug for LigatureSubstFormat1<'a> {
1263 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1264 (self as &dyn SomeTable<'a>).fmt(f)
1265 }
1266}
1267
1268impl<'a> MinByteRange<'a> for LigatureSet<'a> {
1269 fn min_byte_range(&self) -> Range<usize> {
1270 0..self.ligature_offsets_byte_range().end
1271 }
1272 fn min_table_bytes(&self) -> &'a [u8] {
1273 let range = self.min_byte_range();
1274 self.data.as_bytes().get(range).unwrap_or_default()
1275 }
1276}
1277
1278impl ReadArgs for LigatureSet<'_> {
1279 type Args = ();
1280}
1281
1282impl<'a> FontRead<'a> for LigatureSet<'a> {
1283 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1284 #[allow(clippy::absurd_extreme_comparisons)]
1285 if data.len() < Self::MIN_SIZE {
1286 return Err(ReadError::OutOfBounds);
1287 }
1288 Ok(Self { data })
1289 }
1290}
1291
1292#[derive(Clone)]
1294pub struct LigatureSet<'a> {
1295 data: FontData<'a>,
1296}
1297
1298#[allow(clippy::needless_lifetimes)]
1299impl<'a> LigatureSet<'a> {
1300 pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
1301 basic_table_impls!(impl_the_methods);
1302
1303 pub fn ligature_count(&self) -> u16 {
1305 let range = self.ligature_count_byte_range();
1306 self.data.read_at(range.start).ok().unwrap()
1307 }
1308
1309 pub fn ligature_offsets(&self) -> &'a [BigEndian<Offset16>] {
1312 let range = self.ligature_offsets_byte_range();
1313 self.data.read_array(range).ok().unwrap_or_default()
1314 }
1315
1316 pub fn ligatures(&self) -> ArrayOfOffsets<'a, Ligature<'a>, Offset16> {
1318 let data = self.data;
1319 let offsets = self.ligature_offsets();
1320 ArrayOfOffsets::new(offsets, data, ())
1321 }
1322
1323 pub fn ligature_count_byte_range(&self) -> Range<usize> {
1324 let start = 0;
1325 let end = start + u16::RAW_BYTE_LEN;
1326 start..end
1327 }
1328
1329 pub fn ligature_offsets_byte_range(&self) -> Range<usize> {
1330 let ligature_count = self.ligature_count();
1331 let start = self.ligature_count_byte_range().end;
1332 let end =
1333 start + (transforms::to_usize(ligature_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
1334 start..end
1335 }
1336}
1337
1338const _: () = assert!(FontData::default_data_long_enough(LigatureSet::MIN_SIZE));
1339
1340impl Default for LigatureSet<'_> {
1341 fn default() -> Self {
1342 Self {
1343 data: FontData::default_table_data(),
1344 }
1345 }
1346}
1347
1348#[cfg(feature = "experimental_traverse")]
1349impl<'a> SomeTable<'a> for LigatureSet<'a> {
1350 fn type_name(&self) -> &str {
1351 "LigatureSet"
1352 }
1353 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1354 match idx {
1355 0usize => Some(Field::new("ligature_count", self.ligature_count())),
1356 1usize => Some(Field::new(
1357 "ligature_offsets",
1358 FieldType::from(self.ligatures()),
1359 )),
1360 _ => None,
1361 }
1362 }
1363}
1364
1365#[cfg(feature = "experimental_traverse")]
1366#[allow(clippy::needless_lifetimes)]
1367impl<'a> std::fmt::Debug for LigatureSet<'a> {
1368 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1369 (self as &dyn SomeTable<'a>).fmt(f)
1370 }
1371}
1372
1373impl<'a> MinByteRange<'a> for Ligature<'a> {
1374 fn min_byte_range(&self) -> Range<usize> {
1375 0..self.component_glyph_ids_byte_range().end
1376 }
1377 fn min_table_bytes(&self) -> &'a [u8] {
1378 let range = self.min_byte_range();
1379 self.data.as_bytes().get(range).unwrap_or_default()
1380 }
1381}
1382
1383impl ReadArgs for Ligature<'_> {
1384 type Args = ();
1385}
1386
1387impl<'a> FontRead<'a> for Ligature<'a> {
1388 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1389 #[allow(clippy::absurd_extreme_comparisons)]
1390 if data.len() < Self::MIN_SIZE {
1391 return Err(ReadError::OutOfBounds);
1392 }
1393 Ok(Self { data })
1394 }
1395}
1396
1397#[derive(Clone)]
1399pub struct Ligature<'a> {
1400 data: FontData<'a>,
1401}
1402
1403#[allow(clippy::needless_lifetimes)]
1404impl<'a> Ligature<'a> {
1405 pub const MIN_SIZE: usize = (GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1406 basic_table_impls!(impl_the_methods);
1407
1408 pub fn ligature_glyph(&self) -> GlyphId16 {
1410 let range = self.ligature_glyph_byte_range();
1411 self.data.read_at(range.start).ok().unwrap()
1412 }
1413
1414 pub fn component_count(&self) -> u16 {
1416 let range = self.component_count_byte_range();
1417 self.data.read_at(range.start).ok().unwrap()
1418 }
1419
1420 pub fn component_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
1423 let range = self.component_glyph_ids_byte_range();
1424 self.data.read_array(range).ok().unwrap_or_default()
1425 }
1426
1427 pub fn ligature_glyph_byte_range(&self) -> Range<usize> {
1428 let start = 0;
1429 let end = start + GlyphId16::RAW_BYTE_LEN;
1430 start..end
1431 }
1432
1433 pub fn component_count_byte_range(&self) -> Range<usize> {
1434 let start = self.ligature_glyph_byte_range().end;
1435 let end = start + u16::RAW_BYTE_LEN;
1436 start..end
1437 }
1438
1439 pub fn component_glyph_ids_byte_range(&self) -> Range<usize> {
1440 let component_count = self.component_count();
1441 let start = self.component_count_byte_range().end;
1442 let end = start
1443 + (transforms::subtract(component_count, 1_usize))
1444 .saturating_mul(GlyphId16::RAW_BYTE_LEN);
1445 start..end
1446 }
1447}
1448
1449const _: () = assert!(FontData::default_data_long_enough(Ligature::MIN_SIZE));
1450
1451impl Default for Ligature<'_> {
1452 fn default() -> Self {
1453 Self {
1454 data: FontData::default_table_data(),
1455 }
1456 }
1457}
1458
1459#[cfg(feature = "experimental_traverse")]
1460impl<'a> SomeTable<'a> for Ligature<'a> {
1461 fn type_name(&self) -> &str {
1462 "Ligature"
1463 }
1464 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1465 match idx {
1466 0usize => Some(Field::new("ligature_glyph", self.ligature_glyph())),
1467 1usize => Some(Field::new("component_count", self.component_count())),
1468 2usize => Some(Field::new(
1469 "component_glyph_ids",
1470 self.component_glyph_ids(),
1471 )),
1472 _ => None,
1473 }
1474 }
1475}
1476
1477#[cfg(feature = "experimental_traverse")]
1478#[allow(clippy::needless_lifetimes)]
1479impl<'a> std::fmt::Debug for Ligature<'a> {
1480 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1481 (self as &dyn SomeTable<'a>).fmt(f)
1482 }
1483}
1484
1485impl Format<u16> for ExtensionSubstFormat1<'_> {
1486 const FORMAT: u16 = 1;
1487}
1488
1489impl Discriminant for ExtensionSubstFormat1<'_, ()> {
1490 fn read_discriminant(data: FontData<'_>) -> Result<u16, ReadError> {
1491 data.read_at(u16::RAW_BYTE_LEN)
1492 }
1493}
1494
1495impl<'a, T> MinByteRange<'a> for ExtensionSubstFormat1<'a, T> {
1496 fn min_byte_range(&self) -> Range<usize> {
1497 0..self.extension_offset_byte_range().end
1498 }
1499 fn min_table_bytes(&self) -> &'a [u8] {
1500 let range = self.min_byte_range();
1501 self.data.as_bytes().get(range).unwrap_or_default()
1502 }
1503}
1504
1505impl<T> ReadArgs for ExtensionSubstFormat1<'_, T> {
1506 type Args = ();
1507}
1508
1509impl<'a, T> FontRead<'a> for ExtensionSubstFormat1<'a, T> {
1510 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1511 #[allow(clippy::absurd_extreme_comparisons)]
1512 if data.len() < Self::MIN_SIZE {
1513 return Err(ReadError::OutOfBounds);
1514 }
1515 Ok(Self {
1516 data,
1517 offset_type: std::marker::PhantomData,
1518 })
1519 }
1520}
1521
1522impl<'a, T> ExtensionSubstFormat1<'a, T> {
1523 #[allow(dead_code)]
1524 pub(crate) fn of_unit_type(&self) -> ExtensionSubstFormat1<'a, ()> {
1526 ExtensionSubstFormat1 {
1527 data: self.data,
1528 offset_type: std::marker::PhantomData,
1529 }
1530 }
1531}
1532
1533#[derive(Clone)]
1535pub struct ExtensionSubstFormat1<'a, T = ()> {
1536 data: FontData<'a>,
1537 offset_type: std::marker::PhantomData<*const T>,
1538}
1539
1540#[allow(clippy::needless_lifetimes)]
1541impl<'a, T> ExtensionSubstFormat1<'a, T> {
1542 pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN);
1543 basic_table_impls!(impl_the_methods);
1544
1545 pub fn subst_format(&self) -> u16 {
1547 let range = self.subst_format_byte_range();
1548 self.data.read_at(range.start).ok().unwrap()
1549 }
1550
1551 pub fn extension_lookup_type(&self) -> u16 {
1554 let range = self.extension_lookup_type_byte_range();
1555 self.data.read_at(range.start).ok().unwrap()
1556 }
1557
1558 pub fn extension_offset(&self) -> Offset32 {
1562 let range = self.extension_offset_byte_range();
1563 self.data.read_at(range.start).ok().unwrap()
1564 }
1565
1566 pub fn extension(&self) -> Result<T, ReadError>
1568 where
1569 T: FontRead<'a, Args = ()>,
1570 {
1571 let data = self.data;
1572 self.extension_offset().resolve(data)
1573 }
1574
1575 pub fn subst_format_byte_range(&self) -> Range<usize> {
1576 let start = 0;
1577 let end = start + u16::RAW_BYTE_LEN;
1578 start..end
1579 }
1580
1581 pub fn extension_lookup_type_byte_range(&self) -> Range<usize> {
1582 let start = self.subst_format_byte_range().end;
1583 let end = start + u16::RAW_BYTE_LEN;
1584 start..end
1585 }
1586
1587 pub fn extension_offset_byte_range(&self) -> Range<usize> {
1588 let start = self.extension_lookup_type_byte_range().end;
1589 let end = start + Offset32::RAW_BYTE_LEN;
1590 start..end
1591 }
1592}
1593
1594const _: () = assert!(FontData::default_data_long_enough(
1595 ExtensionSubstFormat1::<()>::MIN_SIZE
1596));
1597
1598impl<T> Default for ExtensionSubstFormat1<'_, T> {
1599 fn default() -> Self {
1600 Self {
1601 data: FontData::default_format_1_u16_table_data(),
1602 offset_type: std::marker::PhantomData,
1603 }
1604 }
1605}
1606
1607#[cfg(feature = "experimental_traverse")]
1608impl<'a, T: FontRead<'a, Args = ()> + SomeTable<'a> + 'a> SomeTable<'a>
1609 for ExtensionSubstFormat1<'a, T>
1610{
1611 fn type_name(&self) -> &str {
1612 "ExtensionSubstFormat1"
1613 }
1614 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1615 match idx {
1616 0usize => Some(Field::new("subst_format", self.subst_format())),
1617 1usize => Some(Field::new(
1618 "extension_lookup_type",
1619 self.extension_lookup_type(),
1620 )),
1621 2usize => Some(Field::new(
1622 "extension_offset",
1623 FieldType::offset(self.extension_offset(), self.extension()),
1624 )),
1625 _ => None,
1626 }
1627 }
1628}
1629
1630#[cfg(feature = "experimental_traverse")]
1631#[allow(clippy::needless_lifetimes)]
1632impl<'a, T: FontRead<'a, Args = ()> + SomeTable<'a> + 'a> std::fmt::Debug
1633 for ExtensionSubstFormat1<'a, T>
1634{
1635 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1636 (self as &dyn SomeTable<'a>).fmt(f)
1637 }
1638}
1639
1640pub enum ExtensionSubtable<'a> {
1642 Single(ExtensionSubstFormat1<'a, SingleSubst<'a>>),
1643 Multiple(ExtensionSubstFormat1<'a, MultipleSubstFormat1<'a>>),
1644 Alternate(ExtensionSubstFormat1<'a, AlternateSubstFormat1<'a>>),
1645 Ligature(ExtensionSubstFormat1<'a, LigatureSubstFormat1<'a>>),
1646 Contextual(ExtensionSubstFormat1<'a, SubstitutionSequenceContext<'a>>),
1647 ChainContextual(ExtensionSubstFormat1<'a, SubstitutionChainContext<'a>>),
1648 Reverse(ExtensionSubstFormat1<'a, ReverseChainSingleSubstFormat1<'a>>),
1649}
1650
1651impl Default for ExtensionSubtable<'_> {
1652 fn default() -> Self {
1653 Self::Single(Default::default())
1654 }
1655}
1656
1657impl ReadArgs for ExtensionSubtable<'_> {
1658 type Args = ();
1659}
1660
1661impl<'a> FontRead<'a> for ExtensionSubtable<'a> {
1662 fn read_with_args(bytes: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1663 let discriminant = ExtensionSubstFormat1::read_discriminant(bytes)?;
1664 match discriminant {
1665 1 => Ok(ExtensionSubtable::Single(FontRead::read(bytes)?)),
1666 2 => Ok(ExtensionSubtable::Multiple(FontRead::read(bytes)?)),
1667 3 => Ok(ExtensionSubtable::Alternate(FontRead::read(bytes)?)),
1668 4 => Ok(ExtensionSubtable::Ligature(FontRead::read(bytes)?)),
1669 5 => Ok(ExtensionSubtable::Contextual(FontRead::read(bytes)?)),
1670 6 => Ok(ExtensionSubtable::ChainContextual(FontRead::read(bytes)?)),
1671 8 => Ok(ExtensionSubtable::Reverse(FontRead::read(bytes)?)),
1672 other => Err(ReadError::InvalidFormat(other.into())),
1673 }
1674 }
1675}
1676
1677impl<'a> ExtensionSubtable<'a> {
1678 #[allow(dead_code)]
1679 pub(crate) fn of_unit_type(&self) -> ExtensionSubstFormat1<'a, ()> {
1683 match self {
1684 ExtensionSubtable::Single(inner) => inner.of_unit_type(),
1685 ExtensionSubtable::Multiple(inner) => inner.of_unit_type(),
1686 ExtensionSubtable::Alternate(inner) => inner.of_unit_type(),
1687 ExtensionSubtable::Ligature(inner) => inner.of_unit_type(),
1688 ExtensionSubtable::Contextual(inner) => inner.of_unit_type(),
1689 ExtensionSubtable::ChainContextual(inner) => inner.of_unit_type(),
1690 ExtensionSubtable::Reverse(inner) => inner.of_unit_type(),
1691 }
1692 }
1693}
1694
1695#[cfg(feature = "experimental_traverse")]
1696impl<'a> ExtensionSubtable<'a> {
1697 fn dyn_inner(&self) -> &(dyn SomeTable<'a> + 'a) {
1698 match self {
1699 ExtensionSubtable::Single(table) => table,
1700 ExtensionSubtable::Multiple(table) => table,
1701 ExtensionSubtable::Alternate(table) => table,
1702 ExtensionSubtable::Ligature(table) => table,
1703 ExtensionSubtable::Contextual(table) => table,
1704 ExtensionSubtable::ChainContextual(table) => table,
1705 ExtensionSubtable::Reverse(table) => table,
1706 }
1707 }
1708}
1709
1710#[cfg(feature = "experimental_traverse")]
1711impl<'a> SomeTable<'a> for ExtensionSubtable<'a> {
1712 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1713 self.dyn_inner().get_field(idx)
1714 }
1715 fn type_name(&self) -> &str {
1716 self.dyn_inner().type_name()
1717 }
1718}
1719
1720#[cfg(feature = "experimental_traverse")]
1721impl std::fmt::Debug for ExtensionSubtable<'_> {
1722 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1723 self.dyn_inner().fmt(f)
1724 }
1725}
1726
1727impl Format<u16> for ReverseChainSingleSubstFormat1<'_> {
1728 const FORMAT: u16 = 1;
1729}
1730
1731impl<'a> MinByteRange<'a> for ReverseChainSingleSubstFormat1<'a> {
1732 fn min_byte_range(&self) -> Range<usize> {
1733 0..self.substitute_glyph_ids_byte_range().end
1734 }
1735 fn min_table_bytes(&self) -> &'a [u8] {
1736 let range = self.min_byte_range();
1737 self.data.as_bytes().get(range).unwrap_or_default()
1738 }
1739}
1740
1741impl ReadArgs for ReverseChainSingleSubstFormat1<'_> {
1742 type Args = ();
1743}
1744
1745impl<'a> FontRead<'a> for ReverseChainSingleSubstFormat1<'a> {
1746 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1747 #[allow(clippy::absurd_extreme_comparisons)]
1748 if data.len() < Self::MIN_SIZE {
1749 return Err(ReadError::OutOfBounds);
1750 }
1751 Ok(Self { data })
1752 }
1753}
1754
1755#[derive(Clone)]
1757pub struct ReverseChainSingleSubstFormat1<'a> {
1758 data: FontData<'a>,
1759}
1760
1761#[allow(clippy::needless_lifetimes)]
1762impl<'a> ReverseChainSingleSubstFormat1<'a> {
1763 pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
1764 + Offset16::RAW_BYTE_LEN
1765 + u16::RAW_BYTE_LEN
1766 + u16::RAW_BYTE_LEN
1767 + u16::RAW_BYTE_LEN);
1768 basic_table_impls!(impl_the_methods);
1769
1770 pub fn subst_format(&self) -> u16 {
1772 let range = self.subst_format_byte_range();
1773 self.data.read_at(range.start).ok().unwrap()
1774 }
1775
1776 pub fn coverage_offset(&self) -> Offset16 {
1779 let range = self.coverage_offset_byte_range();
1780 self.data.read_at(range.start).ok().unwrap()
1781 }
1782
1783 pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1785 let data = self.data;
1786 self.coverage_offset().resolve(data)
1787 }
1788
1789 pub fn backtrack_glyph_count(&self) -> u16 {
1791 let range = self.backtrack_glyph_count_byte_range();
1792 self.data.read_at(range.start).ok().unwrap()
1793 }
1794
1795 pub fn backtrack_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
1798 let range = self.backtrack_coverage_offsets_byte_range();
1799 self.data.read_array(range).ok().unwrap_or_default()
1800 }
1801
1802 pub fn backtrack_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
1804 let data = self.data;
1805 let offsets = self.backtrack_coverage_offsets();
1806 ArrayOfOffsets::new(offsets, data, ())
1807 }
1808
1809 pub fn lookahead_glyph_count(&self) -> u16 {
1811 let range = self.lookahead_glyph_count_byte_range();
1812 self.data.read_at(range.start).ok().unwrap_or_default()
1813 }
1814
1815 pub fn lookahead_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
1818 let range = self.lookahead_coverage_offsets_byte_range();
1819 self.data.read_array(range).ok().unwrap_or_default()
1820 }
1821
1822 pub fn lookahead_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
1824 let data = self.data;
1825 let offsets = self.lookahead_coverage_offsets();
1826 ArrayOfOffsets::new(offsets, data, ())
1827 }
1828
1829 pub fn glyph_count(&self) -> u16 {
1831 let range = self.glyph_count_byte_range();
1832 self.data.read_at(range.start).ok().unwrap_or_default()
1833 }
1834
1835 pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
1837 let range = self.substitute_glyph_ids_byte_range();
1838 self.data.read_array(range).ok().unwrap_or_default()
1839 }
1840
1841 pub fn subst_format_byte_range(&self) -> Range<usize> {
1842 let start = 0;
1843 let end = start + u16::RAW_BYTE_LEN;
1844 start..end
1845 }
1846
1847 pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1848 let start = self.subst_format_byte_range().end;
1849 let end = start + Offset16::RAW_BYTE_LEN;
1850 start..end
1851 }
1852
1853 pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
1854 let start = self.coverage_offset_byte_range().end;
1855 let end = start + u16::RAW_BYTE_LEN;
1856 start..end
1857 }
1858
1859 pub fn backtrack_coverage_offsets_byte_range(&self) -> Range<usize> {
1860 let backtrack_glyph_count = self.backtrack_glyph_count();
1861 let start = self.backtrack_glyph_count_byte_range().end;
1862 let end = start
1863 + (transforms::to_usize(backtrack_glyph_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
1864 start..end
1865 }
1866
1867 pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
1868 let start = self.backtrack_coverage_offsets_byte_range().end;
1869 let end = start + u16::RAW_BYTE_LEN;
1870 start..end
1871 }
1872
1873 pub fn lookahead_coverage_offsets_byte_range(&self) -> Range<usize> {
1874 let lookahead_glyph_count = self.lookahead_glyph_count();
1875 let start = self.lookahead_glyph_count_byte_range().end;
1876 let end = start
1877 + (transforms::to_usize(lookahead_glyph_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
1878 start..end
1879 }
1880
1881 pub fn glyph_count_byte_range(&self) -> Range<usize> {
1882 let start = self.lookahead_coverage_offsets_byte_range().end;
1883 let end = start + u16::RAW_BYTE_LEN;
1884 start..end
1885 }
1886
1887 pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
1888 let glyph_count = self.glyph_count();
1889 let start = self.glyph_count_byte_range().end;
1890 let end =
1891 start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN);
1892 start..end
1893 }
1894}
1895
1896const _: () = assert!(FontData::default_data_long_enough(
1897 ReverseChainSingleSubstFormat1::MIN_SIZE
1898));
1899
1900impl Default for ReverseChainSingleSubstFormat1<'_> {
1901 fn default() -> Self {
1902 Self {
1903 data: FontData::default_format_1_u16_table_data(),
1904 }
1905 }
1906}
1907
1908#[cfg(feature = "experimental_traverse")]
1909impl<'a> SomeTable<'a> for ReverseChainSingleSubstFormat1<'a> {
1910 fn type_name(&self) -> &str {
1911 "ReverseChainSingleSubstFormat1"
1912 }
1913 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1914 match idx {
1915 0usize => Some(Field::new("subst_format", self.subst_format())),
1916 1usize => Some(Field::new(
1917 "coverage_offset",
1918 FieldType::offset(self.coverage_offset(), self.coverage()),
1919 )),
1920 2usize => Some(Field::new(
1921 "backtrack_glyph_count",
1922 self.backtrack_glyph_count(),
1923 )),
1924 3usize => Some(Field::new(
1925 "backtrack_coverage_offsets",
1926 FieldType::from(self.backtrack_coverages()),
1927 )),
1928 4usize => Some(Field::new(
1929 "lookahead_glyph_count",
1930 self.lookahead_glyph_count(),
1931 )),
1932 5usize => Some(Field::new(
1933 "lookahead_coverage_offsets",
1934 FieldType::from(self.lookahead_coverages()),
1935 )),
1936 6usize => Some(Field::new("glyph_count", self.glyph_count())),
1937 7usize => Some(Field::new(
1938 "substitute_glyph_ids",
1939 self.substitute_glyph_ids(),
1940 )),
1941 _ => None,
1942 }
1943 }
1944}
1945
1946#[cfg(feature = "experimental_traverse")]
1947#[allow(clippy::needless_lifetimes)]
1948impl<'a> std::fmt::Debug for ReverseChainSingleSubstFormat1<'a> {
1949 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1950 (self as &dyn SomeTable<'a>).fmt(f)
1951 }
1952}