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