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