1#[cfg(feature = "std")]
4mod closure;
5
6mod feature;
7mod lookup_flag;
8mod script;
9
10use core::cmp::Ordering;
11
12pub use lookup_flag::LookupFlag;
13pub use script::{ScriptTags, SelectedScript, UNICODE_TO_NEW_OPENTYPE_SCRIPT_TAGS};
14
15use super::variations::DeltaSetIndex;
16
17#[cfg(feature = "std")]
18use crate::collections::IntSet;
19
20#[cfg(feature = "std")]
21pub(crate) use closure::{
22 ContextFormat1, ContextFormat2, ContextFormat3, LayoutLookupList, LookupClosure,
23 LookupClosureCtx, MAX_LOOKUP_VISIT_COUNT, MAX_NESTING_LEVEL,
24};
25
26#[cfg(feature = "std")]
27pub use closure::Intersect;
28
29#[cfg(test)]
30mod spec_tests;
31
32include!("../../generated/generated_layout.rs");
33
34impl<'a, T: FontRead<'a>> Lookup<'a, T> {
35 pub fn get_subtable(&self, offset: Offset16) -> Result<T, ReadError> {
36 self.resolve_offset(offset)
37 }
38
39 #[cfg(feature = "experimental_traverse")]
40 fn traverse_lookup_flag(&self) -> traversal::FieldType<'a> {
41 self.lookup_flag().to_bits().into()
42 }
43}
44
45pub trait ExtensionLookup<'a, T: FontRead<'a>>: FontRead<'a> {
50 fn extension(&self) -> Result<T, ReadError>;
51}
52
53pub enum Subtables<'a, T: FontRead<'a>, Ext: ExtensionLookup<'a, T>> {
58 Subtable(ArrayOfOffsets<'a, T>),
59 Extension(ArrayOfOffsets<'a, Ext>),
60}
61
62impl<'a, T: FontRead<'a> + 'a, Ext: ExtensionLookup<'a, T> + 'a> Subtables<'a, T, Ext> {
63 pub(crate) fn new(offsets: &'a [BigEndian<Offset16>], data: FontData<'a>) -> Self {
65 Subtables::Subtable(ArrayOfOffsets::new(offsets, data, ()))
66 }
67
68 pub(crate) fn new_ext(offsets: &'a [BigEndian<Offset16>], data: FontData<'a>) -> Self {
70 Subtables::Extension(ArrayOfOffsets::new(offsets, data, ()))
71 }
72
73 pub fn len(&self) -> usize {
75 match self {
76 Subtables::Subtable(inner) => inner.len(),
77 Subtables::Extension(inner) => inner.len(),
78 }
79 }
80
81 pub fn is_empty(&self) -> bool {
82 self.len() == 0
83 }
84
85 pub fn get(&self, idx: usize) -> Result<T, ReadError> {
87 match self {
88 Subtables::Subtable(inner) => inner.get(idx),
89 Subtables::Extension(inner) => inner.get(idx).and_then(|ext| ext.extension()),
90 }
91 }
92
93 pub fn iter(&self) -> impl Iterator<Item = Result<T, ReadError>> + 'a {
95 let (left, right) = match self {
96 Subtables::Subtable(inner) => (Some(inner.iter()), None),
97 Subtables::Extension(inner) => (
98 None,
99 Some(inner.iter().map(|ext| ext.and_then(|ext| ext.extension()))),
100 ),
101 };
102 left.into_iter()
103 .flatten()
104 .chain(right.into_iter().flatten())
105 }
106}
107
108pub enum FeatureParams<'a> {
110 StylisticSet(StylisticSetParams<'a>),
111 Size(SizeParams<'a>),
112 CharacterVariant(CharacterVariantParams<'a>),
113}
114
115impl ReadArgs for FeatureParams<'_> {
116 type Args = Tag;
117}
118
119impl<'a> FontReadWithArgs<'a> for FeatureParams<'a> {
120 fn read_with_args(bytes: FontData<'a>, args: &Tag) -> Result<FeatureParams<'a>, ReadError> {
121 match *args {
122 t if t == Tag::new(b"size") => SizeParams::read(bytes).map(Self::Size),
123 t if &t.to_raw()[..2] == b"ss" => {
125 StylisticSetParams::read(bytes).map(Self::StylisticSet)
126 }
127 t if &t.to_raw()[..2] == b"cv" => {
128 CharacterVariantParams::read(bytes).map(Self::CharacterVariant)
129 }
130 _ => Err(ReadError::InvalidFormat(0xdead)),
133 }
134 }
135}
136
137#[cfg(feature = "experimental_traverse")]
138impl<'a> SomeTable<'a> for FeatureParams<'a> {
139 fn type_name(&self) -> &str {
140 match self {
141 FeatureParams::StylisticSet(table) => table.type_name(),
142 FeatureParams::Size(table) => table.type_name(),
143 FeatureParams::CharacterVariant(table) => table.type_name(),
144 }
145 }
146
147 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
148 match self {
149 FeatureParams::StylisticSet(table) => table.get_field(idx),
150 FeatureParams::Size(table) => table.get_field(idx),
151 FeatureParams::CharacterVariant(table) => table.get_field(idx),
152 }
153 }
154}
155
156impl FeatureTableSubstitutionRecord {
157 pub fn alternate_feature<'a>(&self, data: FontData<'a>) -> Result<Feature<'a>, ReadError> {
158 self.alternate_feature_offset()
159 .resolve_with_args(data, &Tag::new(b"NULL"))
160 }
161}
162
163impl<'a> CoverageTable<'a> {
164 pub fn iter(&self) -> impl Iterator<Item = GlyphId16> + 'a {
165 let (iter1, iter2) = match self {
167 CoverageTable::Format1(t) => (Some(t.glyph_array().iter().map(|g| g.get())), None),
168 CoverageTable::Format2(t) => {
169 let iter = t.range_records().iter().flat_map(RangeRecord::iter);
170 (None, Some(iter))
171 }
172 };
173
174 iter1
175 .into_iter()
176 .flatten()
177 .chain(iter2.into_iter().flatten())
178 }
179
180 #[inline]
182 pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u16> {
183 match self {
184 CoverageTable::Format1(sub) => sub.get(gid),
185 CoverageTable::Format2(sub) => sub.get(gid),
186 }
187 }
188
189 #[cfg(feature = "std")]
191 pub fn intersects(&self, glyphs: &IntSet<GlyphId>) -> bool {
192 match self {
193 CoverageTable::Format1(sub) => sub.intersects(glyphs),
194 CoverageTable::Format2(sub) => sub.intersects(glyphs),
195 }
196 }
197
198 #[cfg(feature = "std")]
200 pub fn intersect_set(&self, glyphs: &IntSet<GlyphId>) -> IntSet<GlyphId> {
201 match self {
202 CoverageTable::Format1(sub) => sub.intersect_set(glyphs),
203 CoverageTable::Format2(sub) => sub.intersect_set(glyphs),
204 }
205 }
206
207 pub fn population(&self) -> usize {
209 match self {
210 CoverageTable::Format1(sub) => sub.population(),
211 CoverageTable::Format2(sub) => sub.population(),
212 }
213 }
214}
215
216impl CoverageFormat1<'_> {
217 #[inline]
219 pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u16> {
220 let gid16: GlyphId16 = gid.into().try_into().ok()?;
221 let be_glyph: BigEndian<GlyphId16> = gid16.into();
222 self.glyph_array()
223 .binary_search(&be_glyph)
224 .ok()
225 .map(|idx| idx as _)
226 }
227
228 #[cfg(feature = "std")]
230 fn intersects(&self, glyphs: &IntSet<GlyphId>) -> bool {
231 let glyph_count = self.glyph_count() as u32;
232 let num_bits = 32 - glyph_count.leading_zeros();
233 if glyph_count > (glyphs.len() as u32) * num_bits {
234 glyphs.iter().any(|g| self.get(g).is_some())
235 } else {
236 self.glyph_array()
237 .iter()
238 .any(|g| glyphs.contains(GlyphId::from(g.get())))
239 }
240 }
241
242 #[cfg(feature = "std")]
244 fn intersect_set(&self, glyphs: &IntSet<GlyphId>) -> IntSet<GlyphId> {
245 let glyph_count = self.glyph_count() as u32;
246 let num_bits = 32 - glyph_count.leading_zeros();
247 if glyph_count > (glyphs.len() as u32) * num_bits {
248 glyphs
249 .iter()
250 .filter_map(|g| self.get(g).map(|_| g))
251 .collect()
252 } else {
253 self.glyph_array()
254 .iter()
255 .filter(|g| glyphs.contains(GlyphId::from(g.get())))
256 .map(|g| GlyphId::from(g.get()))
257 .collect()
258 }
259 }
260
261 pub fn population(&self) -> usize {
263 self.glyph_count() as usize
264 }
265}
266
267impl CoverageFormat2<'_> {
268 #[inline]
270 pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u16> {
271 let gid: GlyphId16 = gid.into().try_into().ok()?;
272 self.range_records()
273 .binary_search_by(|rec| {
274 if rec.end_glyph_id() < gid {
275 Ordering::Less
276 } else if rec.start_glyph_id() > gid {
277 Ordering::Greater
278 } else {
279 Ordering::Equal
280 }
281 })
282 .ok()
283 .map(|idx| {
284 let rec = &self.range_records()[idx];
285 rec.start_coverage_index() + gid.to_u16() - rec.start_glyph_id().to_u16()
286 })
287 }
288
289 #[cfg(feature = "std")]
291 fn intersects(&self, glyphs: &IntSet<GlyphId>) -> bool {
292 let range_count = self.range_count() as u32;
293 let num_bits = 32 - range_count.leading_zeros();
294 if range_count > (glyphs.len() as u32) * num_bits {
295 glyphs.iter().any(|g| self.get(g).is_some())
296 } else {
297 self.range_records()
298 .iter()
299 .any(|record| record.intersects(glyphs))
300 }
301 }
302
303 #[cfg(feature = "std")]
305 fn intersect_set(&self, glyphs: &IntSet<GlyphId>) -> IntSet<GlyphId> {
306 let range_count = self.range_count() as u32;
307 let num_bits = 32 - range_count.leading_zeros();
308 if range_count > (glyphs.len() as u32) * num_bits {
309 glyphs
310 .iter()
311 .filter_map(|g| self.get(g).map(|_| g))
312 .collect()
313 } else {
314 let mut out = IntSet::empty();
315 let mut last = GlyphId16::from(0);
316 for record in self.range_records() {
317 let start_glyph = record.start_glyph_id();
319 if start_glyph < last {
320 break;
321 }
322 let end = record.end_glyph_id();
323 last = end;
324
325 let start = GlyphId::from(start_glyph);
326 if glyphs.contains(start) {
327 out.insert(start);
328 }
329
330 for g in glyphs.iter_after(start) {
331 if g.to_u32() > end.to_u32() {
332 break;
333 }
334 out.insert(g);
335 }
336 }
337 out
338 }
339 }
340
341 pub fn population(&self) -> usize {
343 self.range_records()
344 .iter()
345 .fold(0, |acc, record| acc + record.population())
346 }
347}
348
349impl RangeRecord {
350 pub fn iter(&self) -> impl Iterator<Item = GlyphId16> + '_ {
351 (self.start_glyph_id().to_u16()..=self.end_glyph_id().to_u16()).map(GlyphId16::new)
352 }
353
354 #[cfg(feature = "std")]
356 pub fn intersects(&self, glyphs: &IntSet<GlyphId>) -> bool {
357 glyphs.intersects_range(
358 GlyphId::from(self.start_glyph_id())..=GlyphId::from(self.end_glyph_id()),
359 )
360 }
361
362 pub fn population(&self) -> usize {
364 let start = self.start_glyph_id().to_u32() as usize;
365 let end = self.end_glyph_id().to_u32() as usize;
366 if start > end {
367 0
368 } else {
369 end - start + 1
370 }
371 }
372}
373
374impl DeltaFormat {
375 pub(crate) fn value_count(self, start_size: u16, end_size: u16) -> usize {
376 let range_len = end_size.saturating_add(1).saturating_sub(start_size) as usize;
377 let val_per_word = match self {
378 DeltaFormat::Local2BitDeltas => 8,
379 DeltaFormat::Local4BitDeltas => 4,
380 DeltaFormat::Local8BitDeltas => 2,
381 _ => return 0,
382 };
383
384 let count = range_len / val_per_word;
385 let extra = (range_len % val_per_word).min(1);
386 count + extra
387 }
388}
389
390impl From<DeltaFormat> for i64 {
393 fn from(value: DeltaFormat) -> Self {
394 value as u16 as _
395 }
396}
397
398impl<'a> ClassDefFormat1<'a> {
399 #[inline]
401 pub fn get(&self, gid: GlyphId16) -> u16 {
402 if gid < self.start_glyph_id() {
403 return 0;
404 }
405 let idx = gid.to_u16() - self.start_glyph_id().to_u16();
406 self.class_value_array()
407 .get(idx as usize)
408 .map(|x| x.get())
409 .unwrap_or(0)
410 }
411
412 pub fn iter(&self) -> impl Iterator<Item = (GlyphId16, u16)> + 'a {
414 let start = self.start_glyph_id();
415 self.class_value_array()
416 .iter()
417 .enumerate()
418 .map(move |(i, val)| {
419 let gid = start.to_u16().saturating_add(i as u16);
420 (GlyphId16::new(gid), val.get())
421 })
422 }
423
424 pub fn population(&self) -> usize {
426 self.glyph_count() as usize
427 }
428
429 #[cfg(feature = "std")]
431 fn intersect_classes(&self, glyphs: &IntSet<GlyphId>) -> IntSet<u16> {
432 let mut out = IntSet::empty();
433 if glyphs.is_empty() {
434 return out;
435 }
436
437 let start_glyph = self.start_glyph_id().to_u32();
438 let glyph_count = self.glyph_count();
439 let end_glyph = start_glyph + glyph_count as u32 - 1;
440 if glyphs.first().unwrap().to_u32() < start_glyph
441 || glyphs.last().unwrap().to_u32() > end_glyph
442 {
443 out.insert(0);
444 }
445
446 let class_values = self.class_value_array();
447 if glyphs.contains(GlyphId::from(start_glyph)) {
448 let Some(start_glyph_class) = class_values.first() else {
449 return out;
450 };
451 out.insert(start_glyph_class.get());
452 }
453
454 for g in glyphs.iter_after(GlyphId::from(start_glyph)) {
455 let g = g.to_u32();
456 if g > end_glyph {
457 break;
458 }
459
460 let idx = g - start_glyph;
461 let Some(class) = class_values.get(idx as usize) else {
462 break;
463 };
464 out.insert(class.get());
465 }
466 out
467 }
468
469 #[cfg(feature = "std")]
471 fn intersected_class_glyphs(&self, glyphs: &IntSet<GlyphId>, class: u16) -> IntSet<GlyphId> {
472 let mut out = IntSet::empty();
473 if glyphs.is_empty() {
474 return out;
475 }
476
477 let start_glyph = self.start_glyph_id().to_u32();
478 let glyph_count = self.glyph_count();
479 let end_glyph = start_glyph + glyph_count as u32 - 1;
480 if class == 0 {
481 let first = glyphs.first().unwrap();
482 if first.to_u32() < start_glyph {
483 out.extend(glyphs.range(first..GlyphId::from(start_glyph)));
484 }
485
486 let last = glyphs.last().unwrap();
487 if last.to_u32() > end_glyph {
488 out.extend(glyphs.range(GlyphId::from(end_glyph + 1)..=last));
489 }
490 return out;
491 }
492
493 let class_values = self.class_value_array();
494 for g in glyphs.range(GlyphId::from(start_glyph)..=GlyphId::from(end_glyph)) {
495 let idx = g.to_u32() - start_glyph;
496 let Some(c) = class_values.get(idx as usize) else {
497 break;
498 };
499 if c.get() == class {
500 out.insert(g);
501 }
502 }
503 out
504 }
505}
506
507impl<'a> ClassDefFormat2<'a> {
508 #[inline]
510 pub fn get(&self, gid: GlyphId16) -> u16 {
511 let records = self.class_range_records();
512 let ix = match records.binary_search_by(|rec| rec.start_glyph_id().cmp(&gid)) {
513 Ok(ix) => ix,
514 Err(ix) => ix.saturating_sub(1),
515 };
516 if let Some(record) = records.get(ix) {
517 if (record.start_glyph_id()..=record.end_glyph_id()).contains(&gid) {
518 return record.class();
519 }
520 }
521 0
522 }
523
524 pub fn iter(&self) -> impl Iterator<Item = (GlyphId16, u16)> + 'a {
526 self.class_range_records().iter().flat_map(|range| {
527 let start = range.start_glyph_id().to_u16();
528 let end = range.end_glyph_id().to_u16();
529 (start..=end).map(|gid| (GlyphId16::new(gid), range.class()))
530 })
531 }
532
533 pub fn population(&self) -> usize {
535 self.class_range_records()
536 .iter()
537 .fold(0, |acc, record| acc + record.population())
538 }
539
540 #[cfg(feature = "std")]
542 fn intersect_classes(&self, glyphs: &IntSet<GlyphId>) -> IntSet<u16> {
543 let mut out = IntSet::empty();
544 if glyphs.is_empty() {
545 return out;
546 }
547
548 if self.class_range_count() == 0 {
549 out.insert(0);
550 return out;
551 }
552
553 let range_records = self.class_range_records();
554 let first_record = range_records[0];
555
556 if glyphs.first().unwrap() < first_record.start_glyph_id() {
557 out.insert(0);
558 } else {
559 let mut glyph = GlyphId::from(first_record.end_glyph_id());
560 for record in range_records.iter().skip(1) {
561 let Some(g) = glyphs.iter_after(glyph).next() else {
562 break;
563 };
564
565 if g < record.start_glyph_id() {
566 out.insert(0);
567 break;
568 }
569 glyph = GlyphId::from(record.end_glyph_id());
570 }
571 if glyphs.iter_after(glyph).next().is_some() {
572 out.insert(0);
573 }
574 }
575
576 let num_ranges = self.class_range_count();
577 let num_bits = 16 - num_ranges.leading_zeros();
578 if num_ranges as u64 > glyphs.len() * num_bits as u64 {
579 for g in glyphs.iter() {
580 let class = self.get(GlyphId16::from(g.to_u32() as u16));
581 if class != 0 {
582 out.insert(class);
583 }
584 }
585 } else {
586 for record in range_records {
587 if glyphs.intersects_range(
588 GlyphId::from(record.start_glyph_id())..=GlyphId::from(record.end_glyph_id()),
589 ) {
590 out.insert(record.class());
591 }
592 }
593 }
594 out
595 }
596
597 #[cfg(feature = "std")]
599 fn intersected_class_glyphs(&self, glyphs: &IntSet<GlyphId>, class: u16) -> IntSet<GlyphId> {
600 let mut out = IntSet::empty();
601 if glyphs.is_empty() {
602 return out;
603 }
604
605 let first = glyphs.first().unwrap().to_u32();
606 let last = glyphs.last().unwrap().to_u32();
607 if class == 0 {
608 let mut start = first;
609 for range in self.class_range_records() {
610 let range_start = range.start_glyph_id().to_u32();
611 if start < range_start {
612 out.extend(glyphs.range(GlyphId::from(start)..GlyphId::from(range_start)));
613 }
614
615 let range_end = range.end_glyph_id().to_u32();
616 if range_end >= last {
617 break;
618 }
619 start = range_end + 1;
620 }
621 return out;
622 }
623
624 let num_ranges = self.class_range_count();
625 let num_bits = 16 - num_ranges.leading_zeros();
626 if num_ranges as u64 > glyphs.len() * num_bits as u64 {
627 for g in glyphs.iter() {
628 let c = self.get(GlyphId16::from(g.to_u32() as u16));
629 if c == class {
630 out.insert(g);
631 }
632 }
633 } else {
634 for range in self.class_range_records() {
635 let range_start = range.start_glyph_id().to_u32();
636 let range_end = range.end_glyph_id().to_u32();
637 if range_start > last || range.end_glyph_id().to_u32() < first {
638 break;
639 }
640 if range.class() != class {
641 continue;
642 }
643 out.extend(glyphs.range(GlyphId::from(range_start)..=GlyphId::from(range_end)));
644 }
645 }
646 out
647 }
648}
649
650impl ClassRangeRecord {
651 pub fn population(&self) -> usize {
653 let start = self.start_glyph_id().to_u32() as usize;
654 let end = self.end_glyph_id().to_u32() as usize;
655 if start > end {
656 0
657 } else {
658 end - start + 1
659 }
660 }
661}
662
663impl ClassDef<'_> {
664 #[inline]
666 pub fn get(&self, gid: GlyphId16) -> u16 {
667 match self {
668 ClassDef::Format1(table) => table.get(gid),
669 ClassDef::Format2(table) => table.get(gid),
670 }
671 }
672
673 pub fn iter(&self) -> impl Iterator<Item = (GlyphId16, u16)> + '_ {
677 let (one, two) = match self {
678 ClassDef::Format1(inner) => (Some(inner.iter()), None),
679 ClassDef::Format2(inner) => (None, Some(inner.iter())),
680 };
681 one.into_iter().flatten().chain(two.into_iter().flatten())
682 }
683
684 pub fn population(&self) -> usize {
686 match self {
687 ClassDef::Format1(table) => table.population(),
688 ClassDef::Format2(table) => table.population(),
689 }
690 }
691
692 #[cfg(feature = "std")]
694 pub fn intersect_classes(&self, glyphs: &IntSet<GlyphId>) -> IntSet<u16> {
695 match self {
696 ClassDef::Format1(table) => table.intersect_classes(glyphs),
697 ClassDef::Format2(table) => table.intersect_classes(glyphs),
698 }
699 }
700
701 #[cfg(feature = "std")]
703 pub fn intersected_class_glyphs(
704 &self,
705 glyphs: &IntSet<GlyphId>,
706 class: u16,
707 ) -> IntSet<GlyphId> {
708 match self {
709 ClassDef::Format1(table) => table.intersected_class_glyphs(glyphs, class),
710 ClassDef::Format2(table) => table.intersected_class_glyphs(glyphs, class),
711 }
712 }
713}
714
715impl<'a> Device<'a> {
716 pub fn iter(&self) -> impl Iterator<Item = i8> + 'a {
718 let format = self.delta_format();
719 let mut n = (self.end_size() - self.start_size()) as usize + 1;
720 let deltas_per_word = match format {
721 DeltaFormat::Local2BitDeltas => 8,
722 DeltaFormat::Local4BitDeltas => 4,
723 DeltaFormat::Local8BitDeltas => 2,
724 _ => 0,
725 };
726
727 self.delta_value().iter().flat_map(move |val| {
728 let iter = iter_packed_values(val.get(), format, n);
729 n = n.saturating_sub(deltas_per_word);
730 iter
731 })
732 }
733}
734
735fn iter_packed_values(raw: u16, format: DeltaFormat, n: usize) -> impl Iterator<Item = i8> {
736 let mut decoded = [None; 8];
737 let (mask, sign_mask, bits) = match format {
738 DeltaFormat::Local2BitDeltas => (0b11, 0b10, 2usize),
739 DeltaFormat::Local4BitDeltas => (0b1111, 0b1000, 4),
740 DeltaFormat::Local8BitDeltas => (0b1111_1111, 0b1000_0000, 8),
741 _ => (0, 0, 0),
742 };
743
744 let max_per_word = 16 / bits;
745 #[allow(clippy::needless_range_loop)] for i in 0..n.min(max_per_word) {
747 let mask = mask << ((16 - bits) - i * bits);
748 let val = (raw & mask) >> ((16 - bits) - i * bits);
749 let sign = val & sign_mask != 0;
750
751 let val = if sign {
752 -((((!val) & mask) + 1) as i8)
754 } else {
755 val as i8
756 };
757 decoded[i] = Some(val)
758 }
759 decoded.into_iter().flatten()
760}
761
762impl From<VariationIndex<'_>> for DeltaSetIndex {
763 fn from(src: VariationIndex) -> DeltaSetIndex {
764 DeltaSetIndex {
765 outer: src.delta_set_outer_index(),
766 inner: src.delta_set_inner_index(),
767 }
768 }
769}
770
771#[derive(Clone)]
777pub struct TaggedElement<T> {
778 pub tag: Tag,
779 pub element: T,
780}
781
782impl<T> TaggedElement<T> {
783 pub fn new(tag: Tag, element: T) -> Self {
784 Self { tag, element }
785 }
786}
787
788impl<T> std::ops::Deref for TaggedElement<T> {
789 type Target = T;
790
791 fn deref(&self) -> &Self::Target {
792 &self.element
793 }
794}
795
796#[cfg(test)]
797mod tests {
798 use super::*;
799
800 #[test]
801 fn coverage_get_format1() {
802 const COV1_DATA: FontData = FontData::new(&[0, 1, 0, 5, 0, 1, 0, 7, 0, 13, 0, 27, 0, 44]);
804
805 let coverage = CoverageFormat1::read(COV1_DATA).unwrap();
806 assert_eq!(coverage.get(GlyphId::new(1)), Some(0));
807 assert_eq!(coverage.get(GlyphId::new(2)), None);
808 assert_eq!(coverage.get(GlyphId::new(7)), Some(1));
809 assert_eq!(coverage.get(GlyphId::new(27)), Some(3));
810 assert_eq!(coverage.get(GlyphId::new(45)), None);
811 }
812
813 #[test]
814 fn coverage_get_format2() {
815 const COV2_DATA: FontData =
817 FontData::new(&[0, 2, 0, 2, 0, 5, 0, 9, 0, 0, 0, 30, 0, 39, 0, 5]);
818 let coverage = CoverageFormat2::read(COV2_DATA).unwrap();
819 assert_eq!(coverage.get(GlyphId::new(2)), None);
820 assert_eq!(coverage.get(GlyphId::new(7)), Some(2));
821 assert_eq!(coverage.get(GlyphId::new(9)), Some(4));
822 assert_eq!(coverage.get(GlyphId::new(10)), None);
823 assert_eq!(coverage.get(GlyphId::new(32)), Some(7));
824 assert_eq!(coverage.get(GlyphId::new(39)), Some(14));
825 assert_eq!(coverage.get(GlyphId::new(40)), None);
826 }
827
828 #[test]
829 fn classdef_get_format2() {
830 let classdef = ClassDef::read(FontData::new(
831 font_test_data::gdef::MARKATTACHCLASSDEF_TABLE,
832 ))
833 .unwrap();
834 assert!(matches!(classdef, ClassDef::Format2(..)));
835 let gid_class_pairs = [
836 (616, 1),
837 (617, 1),
838 (618, 1),
839 (624, 1),
840 (625, 1),
841 (626, 1),
842 (652, 2),
843 (653, 2),
844 (654, 2),
845 (655, 2),
846 (661, 2),
847 ];
848 for (gid, class) in gid_class_pairs {
849 assert_eq!(classdef.get(GlyphId16::new(gid)), class);
850 }
851 for (gid, class) in classdef.iter() {
852 assert_eq!(classdef.get(gid), class);
853 }
854 }
855
856 #[test]
857 fn delta_decode() {
858 assert_eq!(
860 iter_packed_values(0x123f, DeltaFormat::Local4BitDeltas, 4).collect::<Vec<_>>(),
861 &[1, 2, 3, -1]
862 );
863
864 assert_eq!(
865 iter_packed_values(0x5540, DeltaFormat::Local2BitDeltas, 5).collect::<Vec<_>>(),
866 &[1, 1, 1, 1, 1]
867 );
868 }
869
870 #[test]
871 fn delta_decode_all() {
872 let bytes: &[u8] = &[0, 7, 0, 13, 0, 3, 1, 244, 30, 245, 101, 8, 42, 0];
874 let device = Device::read(bytes.into()).unwrap();
875 assert_eq!(
876 device.iter().collect::<Vec<_>>(),
877 &[1i8, -12, 30, -11, 101, 8, 42]
878 );
879 }
880}