1#[cfg(feature = "std")]
4mod closure;
5
6mod condition;
7mod feature;
8mod feature_variations;
9pub(crate) mod header;
10mod lookup_flag;
11mod script;
12
13use core::cmp::Ordering;
14
15pub use condition::store_deltas;
16pub use lookup_flag::LookupFlag;
17pub use script::{ScriptTags, SelectedScript, UNICODE_TO_NEW_OPENTYPE_SCRIPT_TAGS};
18
19use super::variations::DeltaSetIndex;
20
21#[cfg(feature = "std")]
22use crate::collections::IntSet;
23use crate::TableProvider;
24
25#[cfg(feature = "std")]
26pub(crate) use closure::{
27 ContextFormat1, ContextFormat2, ContextFormat3, LayoutLookupList, LookupClosure,
28 LookupClosureCtx, SeqCache,
29};
30
31#[cfg(feature = "std")]
32pub use closure::Intersect;
33
34#[cfg(test)]
35mod spec_tests;
36
37include!("../../generated/generated_layout.rs");
38
39pub use feature_variations::LookupVariations;
40pub use header::{LayoutOffsetArray, SelectedLookupList};
41
42impl<'a, T: FontRead<'a, Args = ()>> Lookup<'a, T> {
43 pub fn get_subtable(&self, offset: Offset16) -> Result<T, ReadError> {
44 self.resolve_offset(offset)
45 }
46}
47
48pub trait ExtensionLookup<'a, T: FontRead<'a, Args = ()>>: FontRead<'a, Args = ()> {
53 fn extension(&self) -> Result<T, ReadError>;
54}
55
56pub enum Subtables<'a, T: FontRead<'a, Args = ()>, Ext: ExtensionLookup<'a, T>> {
61 Subtable(ArrayOfOffsets<'a, T>),
62 Extension(ArrayOfOffsets<'a, Ext>),
63}
64
65impl<'a, T: FontRead<'a, Args = ()> + 'a, Ext: ExtensionLookup<'a, T> + 'a> Subtables<'a, T, Ext> {
66 pub(crate) fn new(offsets: &'a [BigEndian<Offset16>], data: FontData<'a>) -> Self {
68 Subtables::Subtable(ArrayOfOffsets::new(offsets, data, ()))
69 }
70
71 pub(crate) fn new_ext(offsets: &'a [BigEndian<Offset16>], data: FontData<'a>) -> Self {
73 Subtables::Extension(ArrayOfOffsets::new(offsets, data, ()))
74 }
75
76 pub fn len(&self) -> usize {
78 match self {
79 Subtables::Subtable(inner) => inner.len(),
80 Subtables::Extension(inner) => inner.len(),
81 }
82 }
83
84 pub fn is_empty(&self) -> bool {
85 self.len() == 0
86 }
87
88 pub fn get(&self, idx: usize) -> Result<T, ReadError> {
90 match self {
91 Subtables::Subtable(inner) => inner.get(idx),
92 Subtables::Extension(inner) => inner.get(idx).and_then(|ext| ext.extension()),
93 }
94 }
95
96 pub fn iter(&self) -> impl Iterator<Item = Result<T, ReadError>> + 'a {
98 let (left, right) = match self {
99 Subtables::Subtable(inner) => (Some(inner.iter()), None),
100 Subtables::Extension(inner) => (
101 None,
102 Some(inner.iter().map(|ext| ext.and_then(|ext| ext.extension()))),
103 ),
104 };
105 left.into_iter()
106 .flatten()
107 .chain(right.into_iter().flatten())
108 }
109}
110
111pub enum FeatureParams<'a> {
113 StylisticSet(StylisticSetParams<'a>),
114 Size(SizeParams<'a>),
115 CharacterVariant(CharacterVariantParams<'a>),
116}
117
118impl ReadArgs for FeatureParams<'_> {
119 type Args = Tag;
120}
121
122impl<'a> FontRead<'a> for FeatureParams<'a> {
123 fn read_with_args(bytes: FontData<'a>, args: Tag) -> Result<FeatureParams<'a>, ReadError> {
124 match args {
125 t if t == Tag::new(b"size") => SizeParams::read(bytes).map(Self::Size),
126 t if &t.to_raw()[..2] == b"ss" => {
128 StylisticSetParams::read(bytes).map(Self::StylisticSet)
129 }
130 t if &t.to_raw()[..2] == b"cv" => {
131 CharacterVariantParams::read(bytes).map(Self::CharacterVariant)
132 }
133 _ => Err(ReadError::InvalidFormat(0xdead)),
136 }
137 }
138}
139
140impl FeatureVariations<'_> {
141 pub fn index_for_coords(&self, coords: &[F2Dot14]) -> Option<u32> {
145 self.index_for_coords_with_delta(coords, |_| Ok::<_, core::convert::Infallible>(0.0))
146 }
147
148 pub fn index_for_coords_with_delta<E>(
152 &self,
153 coords: &[F2Dot14],
154 mut delta: impl FnMut(u32) -> Result<f64, E>,
155 ) -> Option<u32> {
156 if self.version().major != 1 {
157 return None;
158 }
159 for (index, rec) in self.feature_variation_records().iter().enumerate() {
160 if rec.condition_set_offset().is_null() {
163 return Some(index as u32);
164 }
165 let Some(Ok(condition_set)) = rec.condition_set(self.offset_data()) else {
166 continue;
167 };
168 let delta = |index| delta(index).map_err(|_| crate::model::Error::Malformed);
169 if condition_set.evaluate(coords, delta).ok() == Some(true) {
170 return Some(index as u32);
171 }
172 }
173 None
174 }
175}
176
177#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
182pub struct SelectedFeatureVariations {
183 pub gsub: Option<u32>,
185 pub gpos: Option<u32>,
187 pub has_lookup_variations: bool,
190}
191
192impl SelectedFeatureVariations {
193 pub fn new<'a>(tables: &impl TableProvider<'a>, coords: &[F2Dot14]) -> Self {
196 let feature_var_tables = [
197 tables
198 .gsub()
199 .ok()
200 .and_then(|gsub| gsub.feature_variations()),
201 tables
202 .gpos()
203 .ok()
204 .and_then(|gpos| gpos.feature_variations()),
205 ];
206 let store = feature_var_tables
207 .iter()
208 .any(|table| matches!(table, Some(Ok(_))))
209 .then(|| {
210 tables
211 .gdef()
212 .ok()?
213 .item_var_store()
214 .transpose()
215 .ok()
216 .flatten()
217 })
218 .flatten();
219 let has_lookup_variations = feature_var_tables.iter().any(|table| {
220 table.as_ref().is_some_and(|table| {
221 table
222 .as_ref()
223 .is_ok_and(|table| table.lookup_variations().is_some())
224 })
225 });
226 let [gsub, gpos] = feature_var_tables.map(|feature_vars| {
227 feature_vars
228 .transpose()
229 .ok()
230 .flatten()
231 .and_then(|feature_vars| {
232 feature_vars
233 .index_for_coords_with_delta(coords, store_deltas(store.as_ref(), coords))
234 })
235 });
236 Self {
237 gsub,
238 gpos,
239 has_lookup_variations,
240 }
241 }
242}
243
244impl FeatureTableSubstitutionRecord {
245 pub fn alternate_feature<'a>(&self, data: FontData<'a>) -> Result<Feature<'a>, ReadError> {
246 self.alternate_feature_offset()
247 .resolve_with_args(data, Tag::new(b"NULL"))
248 }
249}
250
251fn bit_storage(v: u32) -> u32 {
252 u32::BITS - v.leading_zeros()
253}
254
255impl<'a> CoverageTable<'a> {
256 pub fn iter(&self) -> impl Iterator<Item = GlyphId> + 'a {
258 let (one, two, three, four) = match self {
259 Self::Format1(t) => (
260 Some(t.glyph_array().iter().map(|g| GlyphId::from(g.get()))),
261 None,
262 None,
263 None,
264 ),
265 Self::Format2(t) => (
266 None,
267 Some(
268 t.range_records()
269 .iter()
270 .flat_map(|r| r.iter().map(GlyphId::from)),
271 ),
272 None,
273 None,
274 ),
275 Self::Format3(t) => (
276 None,
277 None,
278 Some(t.glyph_array().iter().map(|g| GlyphId::from(g.get()))),
279 None,
280 ),
281 Self::Format4(t) => (
282 None,
283 None,
284 None,
285 Some(t.range_records().iter().flat_map(RangeRecord2::iter)),
286 ),
287 };
288 one.into_iter()
289 .flatten()
290 .chain(two.into_iter().flatten())
291 .chain(three.into_iter().flatten())
292 .chain(four.into_iter().flatten())
293 }
294
295 #[inline(always)]
299 pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u32> {
300 let gid = gid.into();
301 match self {
302 Self::Format1(t) => t.get(gid).map(u32::from),
303 Self::Format2(t) => t.get(gid).map(u32::from),
304 _ => self.get_wide(gid),
305 }
306 }
307
308 #[inline(never)]
310 fn get_wide(&self, gid: GlyphId) -> Option<u32> {
311 match self {
312 Self::Format3(t) => t.get(gid),
313 Self::Format4(t) => t.get(gid),
314 _ => None,
315 }
316 }
317
318 #[cfg(feature = "std")]
320 pub fn intersects(&self, glyphs: &IntSet<GlyphId>) -> bool {
321 match self {
322 Self::Format1(t) => t.intersects(glyphs),
323 Self::Format2(t) => t.intersects(glyphs),
324 Self::Format3(t) => t
325 .glyph_array()
326 .iter()
327 .any(|g| glyphs.contains(GlyphId::from(g.get()))),
328 Self::Format4(t) => t.range_records().iter().any(|r| {
329 glyphs.intersects_range(
330 GlyphId::from(r.start_glyph_id())..=GlyphId::from(r.end_glyph_id()),
331 )
332 }),
333 }
334 }
335
336 #[cfg(feature = "std")]
338 pub fn intersect_set(&self, glyphs: &IntSet<GlyphId>) -> IntSet<GlyphId> {
339 match self {
340 Self::Format1(t) => t.intersect_set(glyphs),
341 Self::Format2(t) => t.intersect_set(glyphs),
342 Self::Format3(t) => t
343 .glyph_array()
344 .iter()
345 .map(|g| GlyphId::from(g.get()))
346 .filter(|g| glyphs.contains(*g))
347 .collect(),
348 Self::Format4(t) => {
349 let mut covered = IntSet::empty();
350 for range in t.range_records() {
351 if range.start_glyph_id() <= range.end_glyph_id() {
352 covered.insert_range(
353 GlyphId::from(range.start_glyph_id())
354 ..=GlyphId::from(range.end_glyph_id()),
355 );
356 }
357 }
358 covered.intersect(glyphs);
359 covered
360 }
361 }
362 }
363
364 pub fn population(&self) -> usize {
366 match self {
367 Self::Format1(t) => t.population(),
368 Self::Format2(t) => t.population(),
369 Self::Format3(t) => t.glyph_count().to_u32() as usize,
370 Self::Format4(t) => t.range_records().iter().fold(0, |acc: usize, record| {
371 acc.saturating_add(record.population())
372 }),
373 }
374 }
375
376 pub fn cost(&self) -> u32 {
378 match self {
379 Self::Format1(t) => t.cost(),
380 Self::Format2(t) => t.cost(),
381 Self::Format3(t) => bit_storage(t.glyph_count().to_u32()),
382 Self::Format4(t) => bit_storage(t.range_count().to_u32()),
383 }
384 }
385}
386
387impl CoverageFormat1<'_> {
388 #[inline]
390 pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u16> {
391 let gid16: GlyphId16 = gid.into().try_into().ok()?;
392 let be_glyph: BigEndian<GlyphId16> = gid16.into();
393 self.glyph_array()
394 .binary_search(&be_glyph)
395 .ok()
396 .map(|idx| idx as _)
397 }
398
399 #[cfg(feature = "std")]
401 fn intersects(&self, glyphs: &IntSet<GlyphId>) -> bool {
402 let glyph_count = self.glyph_count() as u32;
403 if glyph_count > (glyphs.len() as u32) * self.cost() {
404 glyphs.iter().any(|g| self.get(g).is_some())
405 } else {
406 self.glyph_array()
407 .iter()
408 .any(|g| glyphs.contains(GlyphId::from(g.get())))
409 }
410 }
411
412 #[cfg(feature = "std")]
414 fn intersect_set(&self, glyphs: &IntSet<GlyphId>) -> IntSet<GlyphId> {
415 let glyph_count = self.glyph_count() as u32;
416 if glyph_count > (glyphs.len() as u32) * self.cost() {
417 glyphs
418 .iter()
419 .filter_map(|g| self.get(g).map(|_| g))
420 .collect()
421 } else {
422 self.glyph_array()
423 .iter()
424 .filter(|g| glyphs.contains(GlyphId::from(g.get())))
425 .map(|g| GlyphId::from(g.get()))
426 .collect()
427 }
428 }
429
430 pub fn population(&self) -> usize {
432 self.glyph_count() as usize
433 }
434
435 pub fn cost(&self) -> u32 {
437 bit_storage(self.glyph_count() as u32)
438 }
439}
440
441impl CoverageFormat2<'_> {
442 #[inline]
444 pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u16> {
445 let gid: GlyphId16 = gid.into().try_into().ok()?;
446 self.range_records()
447 .binary_search_by(|rec| {
448 if rec.end_glyph_id() < gid {
449 Ordering::Less
450 } else if rec.start_glyph_id() > gid {
451 Ordering::Greater
452 } else {
453 Ordering::Equal
454 }
455 })
456 .ok()
457 .and_then(|idx| {
458 let rec = &self.range_records()[idx];
459 rec.start_coverage_index()
461 .checked_add(gid.to_u16() - rec.start_glyph_id().to_u16())
462 })
463 }
464
465 #[cfg(feature = "std")]
467 fn intersects(&self, glyphs: &IntSet<GlyphId>) -> bool {
468 let range_count = self.range_count() as u32;
469 if range_count > (glyphs.len() as u32) * self.cost() {
470 glyphs.iter().any(|g| self.get(g).is_some())
471 } else {
472 self.range_records()
473 .iter()
474 .any(|record| record.intersects(glyphs))
475 }
476 }
477
478 #[cfg(feature = "std")]
480 fn intersect_set(&self, glyphs: &IntSet<GlyphId>) -> IntSet<GlyphId> {
481 let range_count = self.range_count() as u32;
482 if range_count > (glyphs.len() as u32) * self.cost() {
483 glyphs
484 .iter()
485 .filter_map(|g| self.get(g).map(|_| g))
486 .collect()
487 } else {
488 let mut out = IntSet::empty();
489 let mut last = GlyphId16::from(0);
490 for record in self.range_records() {
491 let start_glyph = record.start_glyph_id();
493 if start_glyph < last {
494 break;
495 }
496 let end = record.end_glyph_id();
497 last = end;
498
499 let start = GlyphId::from(start_glyph);
500 if glyphs.contains(start) {
501 out.insert(start);
502 }
503
504 for g in glyphs.iter_after(start) {
505 if g.to_u32() > end.to_u32() {
506 break;
507 }
508 out.insert(g);
509 }
510 }
511 out
512 }
513 }
514
515 pub fn population(&self) -> usize {
517 self.range_records().iter().fold(0, |acc: usize, record| {
518 acc.saturating_add(record.population())
519 })
520 }
521
522 pub fn cost(&self) -> u32 {
524 bit_storage(self.range_count() as u32)
525 }
526}
527
528impl RangeRecord {
529 pub fn iter(&self) -> impl Iterator<Item = GlyphId16> + '_ {
530 (self.start_glyph_id().to_u16()..=self.end_glyph_id().to_u16()).map(GlyphId16::new)
531 }
532
533 #[cfg(feature = "std")]
535 pub fn intersects(&self, glyphs: &IntSet<GlyphId>) -> bool {
536 glyphs.intersects_range(
537 GlyphId::from(self.start_glyph_id())..=GlyphId::from(self.end_glyph_id()),
538 )
539 }
540
541 pub fn population(&self) -> usize {
543 let start = self.start_glyph_id().to_u32() as usize;
544 let end = self.end_glyph_id().to_u32() as usize;
545 if start > end {
546 0
547 } else {
548 end - start + 1
549 }
550 }
551}
552
553impl CoverageFormat3<'_> {
554 pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u32> {
556 let gid = GlyphId24::checked_new(gid.into().to_u32())?;
557 self.glyph_array()
558 .binary_search(&BigEndian::from(gid))
559 .ok()
560 .map(|index| index as u32)
561 }
562}
563
564impl CoverageFormat4<'_> {
565 pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u32> {
567 let gid = gid.into().to_u32();
568 let records = self.range_records();
569 let index = records
570 .binary_search_by(|r| {
571 if r.end_glyph_id().to_u32() < gid {
572 Ordering::Less
573 } else if r.start_glyph_id().to_u32() > gid {
574 Ordering::Greater
575 } else {
576 Ordering::Equal
577 }
578 })
579 .ok()?;
580 let record = &records[index];
581 record
582 .start_coverage_index()
583 .to_u32()
584 .checked_add(gid.checked_sub(record.start_glyph_id().to_u32())?)
585 .filter(|index| *index <= Uint24::MAX.to_u32())
586 }
587}
588
589impl RangeRecord2 {
590 pub fn iter(&self) -> impl Iterator<Item = GlyphId> + '_ {
592 (self.start_glyph_id().to_u32()..=self.end_glyph_id().to_u32()).map(GlyphId::new)
593 }
594
595 pub fn population(&self) -> usize {
597 self.end_glyph_id()
598 .to_u32()
599 .checked_sub(self.start_glyph_id().to_u32())
600 .map_or(0, |n| n as usize + 1)
601 }
602}
603
604impl DeltaFormat {
605 pub(crate) fn value_count(self, start_size: u16, end_size: u16) -> usize {
606 let range_len = end_size.saturating_add(1).saturating_sub(start_size) as usize;
607 let val_per_word = match self {
608 DeltaFormat::Local2BitDeltas => 8,
609 DeltaFormat::Local4BitDeltas => 4,
610 DeltaFormat::Local8BitDeltas => 2,
611 _ => return 0,
612 };
613
614 let count = range_len / val_per_word;
615 let extra = (range_len % val_per_word).min(1);
616 count + extra
617 }
618}
619
620impl From<DeltaFormat> for i64 {
623 fn from(value: DeltaFormat) -> Self {
624 value as u16 as _
625 }
626}
627
628impl<'a> ClassDefFormat1<'a> {
629 #[inline]
631 pub fn get(&self, gid: impl Into<GlyphId>) -> u16 {
632 let Some(idx) = gid
633 .into()
634 .to_u32()
635 .checked_sub(self.start_glyph_id().to_u32())
636 else {
637 return 0;
638 };
639 self.class_value_array()
640 .get(idx as usize)
641 .map(|x| x.get())
642 .unwrap_or(0)
643 }
644
645 pub fn iter(&self) -> impl Iterator<Item = (GlyphId16, u16)> + 'a {
647 let start = self.start_glyph_id();
648 self.class_value_array()
649 .iter()
650 .enumerate()
651 .map(move |(i, val)| {
652 let gid = start.to_u16().saturating_add(i as u16);
653 (GlyphId16::new(gid), val.get())
654 })
655 }
656
657 pub fn population(&self) -> usize {
659 self.glyph_count() as usize
660 }
661
662 pub fn cost(&self) -> u32 {
664 1
665 }
666
667 #[cfg(feature = "std")]
669 fn intersect_classes(&self, glyphs: &IntSet<GlyphId>) -> IntSet<u16> {
670 let mut out = IntSet::empty();
671 if glyphs.is_empty() {
672 return out;
673 }
674
675 let start_glyph = self.start_glyph_id().to_u32();
676 let class_values = self.class_value_array();
677 if class_values.is_empty() {
678 out.insert(0);
679 return out;
680 }
681 let end_glyph = start_glyph + class_values.len() as u32 - 1;
682 if glyphs.first().unwrap().to_u32() < start_glyph
683 || glyphs.last().unwrap().to_u32() > end_glyph
684 {
685 out.insert(0);
686 }
687
688 if glyphs.contains(GlyphId::from(start_glyph)) {
689 let Some(start_glyph_class) = class_values.first() else {
690 return out;
691 };
692 out.insert(start_glyph_class.get());
693 }
694
695 for g in glyphs.iter_after(GlyphId::from(start_glyph)) {
696 let g = g.to_u32();
697 if g > end_glyph {
698 break;
699 }
700
701 let idx = g - start_glyph;
702 let Some(class) = class_values.get(idx as usize) else {
703 break;
704 };
705 out.insert(class.get());
706 }
707 out
708 }
709
710 #[cfg(feature = "std")]
712 fn intersected_class_glyphs(&self, glyphs: &IntSet<GlyphId>, class: u16) -> IntSet<GlyphId> {
713 let mut out = IntSet::empty();
714 if glyphs.is_empty() {
715 return out;
716 }
717
718 let start_glyph = self.start_glyph_id().to_u32();
719 let glyph_count = self.glyph_count();
720 if glyph_count == 0 {
724 if class == 0 {
725 out.extend(glyphs.iter());
726 }
727 return out;
728 }
729 let end_glyph = start_glyph + glyph_count as u32 - 1;
730 if class == 0 {
731 let first = glyphs.first().unwrap();
732 if first.to_u32() < start_glyph {
733 out.extend(glyphs.range(first..GlyphId::from(start_glyph)));
734 }
735
736 let last = glyphs.last().unwrap();
737 if last.to_u32() > end_glyph {
738 out.extend(glyphs.range(GlyphId::from(end_glyph + 1)..=last));
739 }
740 return out;
741 }
742
743 let class_values = self.class_value_array();
744 for g in glyphs.range(GlyphId::from(start_glyph)..=GlyphId::from(end_glyph)) {
745 let idx = g.to_u32() - start_glyph;
746 let Some(c) = class_values.get(idx as usize) else {
747 break;
748 };
749 if c.get() == class {
750 out.insert(g);
751 }
752 }
753 out
754 }
755
756 #[cfg(feature = "std")]
758 fn intersects_class_glyphs(&self, glyphs: &IntSet<GlyphId>, class: u16) -> bool {
759 if glyphs.is_empty() {
760 return false;
761 }
762
763 let start_glyph = self.start_glyph_id().to_u32();
764 let glyph_count = self.glyph_count();
765 if glyph_count == 0 {
769 return class == 0;
770 }
771 let end_glyph = start_glyph + glyph_count as u32 - 1;
772 if class == 0 {
773 let first = glyphs.first().unwrap();
774 if first.to_u32() < start_glyph {
775 return true;
776 }
777
778 let last = glyphs.last().unwrap();
779 if last.to_u32() > end_glyph {
780 return true;
781 }
782 }
783
784 let class_values = self.class_value_array();
785 for g in glyphs.range(GlyphId::from(start_glyph)..=GlyphId::from(end_glyph)) {
786 let idx = g.to_u32() - start_glyph;
787 let Some(c) = class_values.get(idx as usize) else {
788 return false;
789 };
790 if c.get() == class {
791 return true;
792 }
793 }
794 false
795 }
796}
797
798impl<'a> ClassDefFormat2<'a> {
799 #[inline]
801 pub fn get(&self, gid: impl Into<GlyphId>) -> u16 {
802 let gid = gid.into().to_u32();
803 let records = self.class_range_records();
804 let ix = match records.binary_search_by(|rec| rec.start_glyph_id().to_u32().cmp(&gid)) {
805 Ok(ix) => ix,
806 Err(ix) => ix.saturating_sub(1),
807 };
808 if let Some(record) = records.get(ix) {
809 if (record.start_glyph_id().to_u32()..=record.end_glyph_id().to_u32()).contains(&gid) {
810 return record.class();
811 }
812 }
813 0
814 }
815
816 pub fn iter(&self) -> impl Iterator<Item = (GlyphId16, u16)> + 'a {
818 self.class_range_records().iter().flat_map(|range| {
819 let start = range.start_glyph_id().to_u16();
820 let end = range.end_glyph_id().to_u16();
821 (start..=end).map(|gid| (GlyphId16::new(gid), range.class()))
822 })
823 }
824
825 pub fn population(&self) -> usize {
827 self.class_range_records()
828 .iter()
829 .fold(0, |acc: usize, record| {
830 acc.saturating_add(record.population())
831 })
832 }
833
834 pub fn cost(&self) -> u32 {
836 bit_storage(self.class_range_count() as u32)
837 }
838
839 #[cfg(feature = "std")]
841 fn intersect_classes(&self, glyphs: &IntSet<GlyphId>) -> IntSet<u16> {
842 let mut out = IntSet::empty();
843 if glyphs.is_empty() {
844 return out;
845 }
846
847 let range_records = self.class_range_records();
848 let Some(first_record) = range_records.first() else {
849 out.insert(0);
850 return out;
851 };
852
853 if glyphs.first().unwrap() < first_record.start_glyph_id() {
854 out.insert(0);
855 } else {
856 let mut glyph = GlyphId::from(first_record.end_glyph_id());
857 for record in range_records.iter().skip(1) {
858 let Some(g) = glyphs.iter_after(glyph).next() else {
859 break;
860 };
861
862 if g < record.start_glyph_id() {
863 out.insert(0);
864 break;
865 }
866 glyph = GlyphId::from(record.end_glyph_id());
867 }
868 if glyphs.iter_after(glyph).next().is_some() {
869 out.insert(0);
870 }
871 }
872
873 let num_ranges = self.class_range_count();
874 if num_ranges as u64 > glyphs.len() * self.cost() as u64 {
875 for g in glyphs.iter() {
876 let class = self.get(g);
877 if class != 0 {
878 out.insert(class);
879 }
880 }
881 } else {
882 for record in range_records {
883 if glyphs.intersects_range(
884 GlyphId::from(record.start_glyph_id())..=GlyphId::from(record.end_glyph_id()),
885 ) {
886 out.insert(record.class());
887 }
888 }
889 }
890 out
891 }
892
893 #[cfg(feature = "std")]
895 fn intersected_class_glyphs(&self, glyphs: &IntSet<GlyphId>, class: u16) -> IntSet<GlyphId> {
896 let mut out = IntSet::empty();
897 if glyphs.is_empty() {
898 return out;
899 }
900
901 let first = glyphs.first().unwrap().to_u32();
902 let last = glyphs.last().unwrap().to_u32();
903 if class == 0 {
904 let mut start = first;
905 for range in self.class_range_records() {
906 let range_start = range.start_glyph_id().to_u32();
907 if start < range_start {
908 out.extend(glyphs.range(GlyphId::from(start)..GlyphId::from(range_start)));
909 }
910
911 let range_end = range.end_glyph_id().to_u32();
912 if range_end >= last {
913 break;
914 }
915 start = range_end + 1;
916 }
917
918 if start <= last {
919 out.extend(glyphs.range(GlyphId::from(start)..=GlyphId::from(last)));
920 }
921 return out;
922 }
923
924 let num_ranges = self.class_range_count();
925 if num_ranges as u64 > glyphs.len() * self.cost() as u64 {
926 for g in glyphs.iter() {
927 let c = self.get(g);
928 if c == class {
929 out.insert(g);
930 }
931 }
932 } else {
933 for range in self.class_range_records() {
934 let range_start = range.start_glyph_id().to_u32();
935 let range_end = range.end_glyph_id().to_u32();
936 if range_start > last {
937 break;
938 }
939 if range.class() != class || range.end_glyph_id().to_u32() < first {
940 continue;
941 }
942 out.extend(glyphs.range(GlyphId::from(range_start)..=GlyphId::from(range_end)));
943 }
944 }
945 out
946 }
947
948 #[cfg(feature = "std")]
950 fn intersects_class_glyphs(&self, glyphs: &IntSet<GlyphId>, class: u16) -> bool {
951 if glyphs.is_empty() {
952 return false;
953 }
954
955 if self.class_range_count() == 0 {
959 return class == 0;
960 }
961
962 let first = glyphs.first().unwrap().to_u32();
963 if class == 0 {
964 let mut last_end = first;
965 for (i, range) in self.class_range_records().iter().enumerate() {
966 let range_start = range.start_glyph_id().to_u32();
967 let range_end = range.end_glyph_id().to_u32();
968 if i == 0 {
969 if first < range_start {
970 return true;
971 }
972 last_end = range_end;
973 continue;
974 }
975
976 if range_start == last_end + 1 {
977 last_end = range_end;
978 continue;
979 }
980
981 if glyphs
982 .intersects_range(GlyphId::from(last_end + 1)..=GlyphId::from(range_start - 1))
983 {
984 return true;
985 };
986 last_end = range_end + 1;
987 }
988 if glyphs
989 .iter_after(GlyphId::from(last_end + 1))
990 .next()
991 .is_some()
992 {
993 return true;
994 }
995 }
996
997 let num_ranges = self.class_range_count();
998 if num_ranges as u64 > glyphs.len() * self.cost() as u64 {
999 for g in glyphs.iter() {
1000 let c = self.get(g);
1001 if c == class {
1002 return true;
1003 }
1004 }
1005 } else {
1006 let last = glyphs.last().unwrap().to_u32();
1007 for range in self.class_range_records() {
1008 let range_start = range.start_glyph_id().to_u32();
1009 let range_end = range.end_glyph_id().to_u32();
1010 if range_start > last {
1011 break;
1012 }
1013 if range_end < first {
1014 continue;
1015 }
1016 if range.class() == class
1017 && glyphs
1018 .intersects_range(GlyphId::from(range_start)..=GlyphId::from(range_end))
1019 {
1020 return true;
1021 }
1022 }
1023 }
1024 false
1025 }
1026}
1027
1028impl ClassRangeRecord {
1029 pub fn population(&self) -> usize {
1031 let start = self.start_glyph_id().to_u32() as usize;
1032 let end = self.end_glyph_id().to_u32() as usize;
1033 if start > end {
1034 0
1035 } else {
1036 end - start + 1
1037 }
1038 }
1039}
1040
1041impl ClassDefFormat3<'_> {
1042 pub fn get(&self, gid: impl Into<GlyphId>) -> u32 {
1044 let gid = gid.into().to_u32();
1045 if gid > Uint24::MAX.to_u32() {
1046 return 0;
1047 }
1048 gid.checked_sub(self.start_glyph_id().to_u32())
1049 .and_then(|index| self.class_value_array().get(index as usize))
1050 .map_or(0, |class| class.get().to_u32())
1051 }
1052}
1053
1054impl ClassDefFormat4<'_> {
1055 pub fn get(&self, gid: impl Into<GlyphId>) -> u32 {
1057 let gid = gid.into().to_u32();
1058 let records = self.class_range_records();
1059 let index = records
1060 .binary_search_by(|r| r.start_glyph_id().to_u32().cmp(&gid))
1061 .unwrap_or_else(|i| i.saturating_sub(1));
1062 records
1063 .get(index)
1064 .filter(|r| (r.start_glyph_id().to_u32()..=r.end_glyph_id().to_u32()).contains(&gid))
1065 .map_or(0, |r| u32::from(r.class()))
1066 }
1067}
1068
1069impl ClassRangeRecord2 {
1070 pub fn population(&self) -> usize {
1072 self.end_glyph_id()
1073 .to_u32()
1074 .checked_sub(self.start_glyph_id().to_u32())
1075 .map_or(0, |n| n as usize + 1)
1076 }
1077}
1078
1079impl ClassDef<'_> {
1080 #[inline(always)]
1083 pub fn get(&self, gid: impl Into<GlyphId>) -> u32 {
1084 let gid = gid.into();
1085 match self {
1086 Self::Format1(t) => u32::from(t.get(gid)),
1087 Self::Format2(t) => u32::from(t.get(gid)),
1088 _ => self.get_wide(gid),
1089 }
1090 }
1091
1092 #[inline(never)]
1094 fn get_wide(&self, gid: GlyphId) -> u32 {
1095 match self {
1096 Self::Format3(t) => t.get(gid),
1097 Self::Format4(t) => t.get(gid),
1098 _ => 0,
1099 }
1100 }
1101
1102 pub fn iter(&self) -> impl Iterator<Item = (GlyphId, u32)> + '_ {
1104 let (one, two) = match self {
1105 Self::Format1(t) => (
1106 Some(t.iter().map(|(g, c)| (GlyphId::from(g), u32::from(c)))),
1107 None,
1108 ),
1109 Self::Format2(t) => (
1110 None,
1111 Some(t.iter().map(|(g, c)| (GlyphId::from(g), u32::from(c)))),
1112 ),
1113 _ => (None, None),
1114 };
1115 one.into_iter()
1116 .flatten()
1117 .chain(two.into_iter().flatten())
1118 .chain(self.wide_class_ranges().flat_map(|(r, c)| {
1119 (r.start().to_u32()..=r.end().to_u32()).map(move |g| (GlyphId::new(g), c))
1120 }))
1121 }
1122
1123 pub fn population(&self) -> usize {
1125 match self {
1126 Self::Format1(t) => t.population(),
1127 Self::Format2(t) => t.population(),
1128 Self::Format3(t) => t.glyph_count().to_u32() as usize,
1129 Self::Format4(t) => t
1130 .class_range_records()
1131 .iter()
1132 .fold(0, |acc: usize, record| {
1133 acc.saturating_add(record.population())
1134 }),
1135 }
1136 }
1137
1138 pub fn cost(&self) -> u32 {
1140 match self {
1141 Self::Format1(t) => t.cost(),
1142 Self::Format2(t) => t.cost(),
1143 Self::Format3(_) => 1,
1144 Self::Format4(t) => bit_storage(t.class_range_count().to_u32()),
1145 }
1146 }
1147
1148 #[cfg(feature = "std")]
1150 pub fn intersect_classes(&self, glyphs: &IntSet<GlyphId>) -> IntSet<u32> {
1151 match self {
1152 Self::Format1(t) => t.intersect_classes(glyphs).iter().map(u32::from).collect(),
1153 Self::Format2(t) => t.intersect_classes(glyphs).iter().map(u32::from).collect(),
1154 _ => {
1155 let mut classes = IntSet::empty();
1156 let mut assigned = IntSet::empty();
1157 for (range, class) in self.wide_class_ranges() {
1158 if glyphs.intersects_range(range.clone()) {
1159 classes.insert(class);
1160 }
1161 assigned.insert_range(range);
1162 }
1163 if !glyphs.is_subset(&assigned) {
1164 classes.insert(0);
1165 }
1166 classes
1167 }
1168 }
1169 }
1170
1171 #[cfg(feature = "std")]
1173 pub fn intersected_class_glyphs(
1174 &self,
1175 glyphs: &IntSet<GlyphId>,
1176 class: u32,
1177 ) -> IntSet<GlyphId> {
1178 match self {
1179 Self::Format1(t) => u16::try_from(class)
1180 .map(|c| t.intersected_class_glyphs(glyphs, c))
1181 .unwrap_or_default(),
1182 Self::Format2(t) => u16::try_from(class)
1183 .map(|c| t.intersected_class_glyphs(glyphs, c))
1184 .unwrap_or_default(),
1185 _ => {
1186 let mut out = IntSet::empty();
1187 let mut assigned = IntSet::empty();
1188 for (range, c) in self.wide_class_ranges() {
1189 if c == class {
1190 out.insert_range(range.clone());
1191 }
1192 if class == 0 {
1193 assigned.insert_range(range);
1194 }
1195 }
1196 if class == 0 {
1197 assigned.invert();
1198 out.union(&assigned);
1199 }
1200 out.intersect(glyphs);
1201 out
1202 }
1203 }
1204 }
1205
1206 #[cfg(feature = "std")]
1208 pub fn intersects_class_glyphs(&self, glyphs: &IntSet<GlyphId>, class: u32) -> bool {
1209 match self {
1210 Self::Format1(t) => {
1211 u16::try_from(class).is_ok_and(|c| t.intersects_class_glyphs(glyphs, c))
1212 }
1213 Self::Format2(t) => {
1214 u16::try_from(class).is_ok_and(|c| t.intersects_class_glyphs(glyphs, c))
1215 }
1216 _ => !self.intersected_class_glyphs(glyphs, class).is_empty(),
1217 }
1218 }
1219
1220 fn wide_class_ranges(
1221 &self,
1222 ) -> impl Iterator<Item = (core::ops::RangeInclusive<GlyphId>, u32)> + '_ {
1223 let (one, two) = match self {
1224 Self::Format3(t) => (
1225 Some(
1226 t.class_value_array()
1227 .iter()
1228 .enumerate()
1229 .filter_map(move |(i, c)| {
1230 let g = t.start_glyph_id().to_u32().checked_add(i as u32)?;
1231 (g <= Uint24::MAX.to_u32())
1232 .then_some((GlyphId::new(g)..=GlyphId::new(g), c.get().to_u32()))
1233 }),
1234 ),
1235 None,
1236 ),
1237 Self::Format4(t) => (
1238 None,
1239 Some(t.class_range_records().iter().filter_map(|r| {
1240 (r.start_glyph_id() <= r.end_glyph_id()).then_some((
1241 GlyphId::from(r.start_glyph_id())..=GlyphId::from(r.end_glyph_id()),
1242 u32::from(r.class()),
1243 ))
1244 })),
1245 ),
1246 _ => (None, None),
1247 };
1248 one.into_iter().flatten().chain(two.into_iter().flatten())
1249 }
1250}
1251
1252impl<'a> Device<'a> {
1253 pub fn delta_for_ppem(&self, ppem: u16) -> i32 {
1259 let start = self.start_size();
1260 if ppem == 0 || ppem < start || ppem > self.end_size() {
1261 return 0;
1262 }
1263 self.iter()
1264 .nth((ppem - start) as usize)
1265 .map_or(0, |delta| delta as i32)
1266 }
1267
1268 pub fn iter(&self) -> impl Iterator<Item = i8> + 'a {
1270 let format = self.delta_format();
1271 let mut n = self
1272 .end_size()
1273 .checked_sub(self.start_size())
1274 .map(|x| x as usize + 1)
1275 .unwrap_or(0);
1276 let deltas_per_word = match format {
1277 DeltaFormat::Local2BitDeltas => 8,
1278 DeltaFormat::Local4BitDeltas => 4,
1279 DeltaFormat::Local8BitDeltas => 2,
1280 _ => 0,
1281 };
1282
1283 self.delta_value().iter().flat_map(move |val| {
1284 let iter = iter_packed_values(val.get(), format, n);
1285 n = n.saturating_sub(deltas_per_word);
1286 iter
1287 })
1288 }
1289}
1290
1291fn iter_packed_values(raw: u16, format: DeltaFormat, n: usize) -> impl Iterator<Item = i8> {
1292 let mut decoded = [None; 8];
1293 let (mask, sign_mask, bits) = match format {
1294 DeltaFormat::Local2BitDeltas => (0b11, 0b10, 2usize),
1295 DeltaFormat::Local4BitDeltas => (0b1111, 0b1000, 4),
1296 DeltaFormat::Local8BitDeltas => (0b1111_1111, 0b1000_0000, 8),
1297 _ => (0, 0, 0),
1298 };
1299
1300 let max_per_word = 16 / bits;
1301 #[allow(clippy::needless_range_loop)] for i in 0..n.min(max_per_word) {
1303 let shift = (16 - bits) - i * bits;
1304 let val = (raw >> shift) & mask;
1307 let val = if val & sign_mask != 0 {
1308 (val | !mask) as i8
1310 } else {
1311 val as i8
1312 };
1313 decoded[i] = Some(val)
1314 }
1315 decoded.into_iter().flatten()
1316}
1317
1318impl From<VariationIndex<'_>> for DeltaSetIndex {
1319 fn from(src: VariationIndex) -> DeltaSetIndex {
1320 DeltaSetIndex {
1321 outer: src.delta_set_outer_index(),
1322 inner: src.delta_set_inner_index(),
1323 }
1324 }
1325}
1326
1327#[derive(Clone)]
1333pub struct TaggedElement<T> {
1334 pub tag: Tag,
1335 pub element: T,
1336}
1337
1338impl<T> TaggedElement<T> {
1339 pub fn new(tag: Tag, element: T) -> Self {
1340 Self { tag, element }
1341 }
1342}
1343
1344impl<T> std::ops::Deref for TaggedElement<T> {
1345 type Target = T;
1346
1347 fn deref(&self) -> &Self::Target {
1348 &self.element
1349 }
1350}
1351
1352#[cfg(test)]
1353mod tests {
1354 use super::*;
1355 use crate::FontRef;
1356 use types::Fixed;
1357
1358 #[test]
1359 fn coverage_24bit_glyphs_and_indices() {
1360 let bytes = [0, 3, 0, 0, 3, 0, 0xFF, 0xFF, 1, 0, 0, 0xFF, 0xFF, 0xFF];
1362 let coverage = CoverageTable::read(FontData::new(&bytes)).unwrap();
1363 assert_eq!(
1364 coverage.iter().map(GlyphId::to_u32).collect::<Vec<_>>(),
1365 [65535, 65536, 0xFFFFFF]
1366 );
1367 assert_eq!(coverage.get(GlyphId::new(65536)), Some(1));
1368 assert_eq!(coverage.get(GlyphId::new(0x1000000)), None);
1369
1370 let bytes = [0, 4, 0, 0, 1, 1, 0, 0, 1, 0, 2, 1, 0, 0];
1372 let coverage = CoverageTable::read(FontData::new(&bytes)).unwrap();
1373 assert_eq!(coverage.get(GlyphId::new(65538)), Some(65538));
1374 assert_eq!(coverage.get(GlyphId::new(65539)), None);
1375 assert_eq!(coverage.population(), 3);
1376 }
1377
1378 #[test]
1379 fn class_widths_follow_the_format() {
1380 let bytes = [0, 3, 1, 0, 0, 0, 0, 2, 1, 0, 0, 0xFF, 0xFF, 0xFF];
1382 let class = ClassDef::read(FontData::new(&bytes)).unwrap();
1383 assert_eq!(class.get(GlyphId::new(65536)), 65536);
1384 assert_eq!(class.get(GlyphId::new(65537)), 0xFFFFFF);
1385 assert_eq!(class.get(GlyphId::new(65538)), 0);
1386
1387 let bytes = [0, 4, 0, 0, 1, 1, 0, 0, 1, 0, 2, 0xFF, 0xFF];
1389 let class = ClassDef::read(FontData::new(&bytes)).unwrap();
1390 assert_eq!(class.get(GlyphId::new(65538)), 65535);
1391 assert_eq!(class.get(GlyphId::new(65539)), 0);
1392 assert_eq!(class.iter().count(), 3);
1393 }
1394
1395 #[test]
1396 fn truncated_wide_arrays_do_not_match() {
1397 for bytes in [&[0, 3, 0, 0, 1][..], &[0, 4, 0, 0, 1][..]] {
1398 let coverage = CoverageTable::read(FontData::new(bytes)).unwrap();
1399 assert_eq!(coverage.get(GlyphId::new(0)), None);
1400 }
1401 for bytes in [&[0, 3, 1, 0, 0, 0, 0, 1][..], &[0, 4, 0, 0, 1][..]] {
1402 let class = ClassDef::read(FontData::new(bytes)).unwrap();
1403 assert_eq!(class.get(GlyphId::new(65536)), 0);
1404 }
1405 }
1406
1407 #[cfg(feature = "std")]
1408 #[test]
1409 fn wide_class_zero_includes_unassigned_glyphs() {
1410 let bytes = [0, 4, 0, 0, 1, 1, 0, 0, 1, 0, 2, 0, 1];
1411 let class = ClassDef::read(FontData::new(&bytes)).unwrap();
1412 let glyphs: IntSet<GlyphId> = [65535, 65536, 65538, 65539]
1413 .map(GlyphId::new)
1414 .into_iter()
1415 .collect();
1416 assert_eq!(
1417 class.intersect_classes(&glyphs).iter().collect::<Vec<_>>(),
1418 [0, 1]
1419 );
1420 assert_eq!(
1421 class
1422 .intersected_class_glyphs(&glyphs, 0)
1423 .iter()
1424 .map(GlyphId::to_u32)
1425 .collect::<Vec<_>>(),
1426 [65535, 65539]
1427 );
1428 }
1429
1430 #[test]
1431 fn coverage_get_format1() {
1432 const COV1_DATA: FontData = FontData::new(&[0, 1, 0, 5, 0, 1, 0, 7, 0, 13, 0, 27, 0, 44]);
1434
1435 let coverage = CoverageFormat1::read(COV1_DATA).unwrap();
1436 assert_eq!(coverage.get(GlyphId::new(1)), Some(0));
1437 assert_eq!(coverage.get(GlyphId::new(2)), None);
1438 assert_eq!(coverage.get(GlyphId::new(7)), Some(1));
1439 assert_eq!(coverage.get(GlyphId::new(27)), Some(3));
1440 assert_eq!(coverage.get(GlyphId::new(45)), None);
1441 }
1442
1443 #[test]
1444 fn coverage_get_format2() {
1445 const COV2_DATA: FontData =
1447 FontData::new(&[0, 2, 0, 2, 0, 5, 0, 9, 0, 0, 0, 30, 0, 39, 0, 5]);
1448 let coverage = CoverageFormat2::read(COV2_DATA).unwrap();
1449 assert_eq!(coverage.get(GlyphId::new(2)), None);
1450 assert_eq!(coverage.get(GlyphId::new(7)), Some(2));
1451 assert_eq!(coverage.get(GlyphId::new(9)), Some(4));
1452 assert_eq!(coverage.get(GlyphId::new(10)), None);
1453 assert_eq!(coverage.get(GlyphId::new(32)), Some(7));
1454 assert_eq!(coverage.get(GlyphId::new(39)), Some(14));
1455 assert_eq!(coverage.get(GlyphId::new(40)), None);
1456 }
1457
1458 #[test]
1460 fn coverage_get_format2_no_u16_overflow() {
1461 const COV2_DATA: FontData =
1465 FontData::new(&[0, 2, 0, 1, 0x9c, 0x40, 0x9c, 0x4a, 0x9c, 0x40]);
1466 let coverage = CoverageFormat2::read(COV2_DATA).unwrap();
1467 assert_eq!(coverage.get(GlyphId::new(40000)), Some(40000));
1468 assert_eq!(coverage.get(GlyphId::new(40005)), Some(40005));
1469 assert_eq!(coverage.get(GlyphId::new(40010)), Some(40010));
1470 assert_eq!(coverage.get(GlyphId::new(40011)), None);
1471 }
1472
1473 #[test]
1474 fn coverage_get_format2_rejects_overflowing_coverage_index() {
1475 const COV2_DATA: FontData = FontData::new(&[0, 2, 0, 1, 0, 1, 0, 2, 0xff, 0xff]);
1477 let coverage = CoverageFormat2::read(COV2_DATA).unwrap();
1478 assert_eq!(coverage.get(GlyphId::new(1)), Some(u16::MAX));
1479 assert_eq!(coverage.get(GlyphId::new(2)), None);
1480 }
1481
1482 #[test]
1483 fn classdef_get_format2() {
1484 let classdef = ClassDef::read(FontData::new(
1485 font_test_data::gdef::MARKATTACHCLASSDEF_TABLE,
1486 ))
1487 .unwrap();
1488 assert!(matches!(classdef, ClassDef::Format2(..)));
1489 let gid_class_pairs = [
1490 (616, 1),
1491 (617, 1),
1492 (618, 1),
1493 (624, 1),
1494 (625, 1),
1495 (626, 1),
1496 (652, 2),
1497 (653, 2),
1498 (654, 2),
1499 (655, 2),
1500 (661, 2),
1501 ];
1502 for (gid, class) in gid_class_pairs {
1503 assert_eq!(classdef.get(GlyphId16::new(gid)), class);
1504 }
1505 for (gid, class) in classdef.iter() {
1506 assert_eq!(classdef.get(gid), class);
1507 }
1508 }
1509
1510 #[test]
1511 fn classdef_format1_short_read_no_panic() {
1512 let classdef = ClassDefFormat1::read(FontData::new(&[0, 1, 0, 10, 0, 5, 0, 1])).unwrap();
1514 let glyphs: IntSet<GlyphId> = [GlyphId::new(10), GlyphId::new(11), GlyphId::new(14)]
1515 .into_iter()
1516 .collect();
1517
1518 assert_eq!(classdef.get(GlyphId::new(10)), 0);
1519 assert_eq!(classdef.get(GlyphId::new(11)), 0);
1520 assert!(!classdef.intersects_class_glyphs(&glyphs, 2));
1521
1522 let class_ones = classdef.intersected_class_glyphs(&glyphs, 1);
1523 assert!(class_ones.is_empty());
1524 }
1525
1526 #[test]
1528 fn classdef_format1_empty() {
1529 let classdef = ClassDefFormat1::read(FontData::new(&[0, 1, 0, 0, 0, 0])).unwrap();
1532 let glyphs: IntSet<GlyphId> = [GlyphId::new(48), GlyphId::new(49), GlyphId::new(50)]
1533 .into_iter()
1534 .collect();
1535
1536 assert!(classdef.intersects_class_glyphs(&glyphs, 0));
1537 assert!(!classdef.intersects_class_glyphs(&glyphs, 1));
1538
1539 assert!(classdef
1540 .intersected_class_glyphs(&glyphs, 0)
1541 .iter()
1542 .eq(glyphs.iter()));
1543 assert!(classdef.intersected_class_glyphs(&glyphs, 1).is_empty());
1544
1545 assert_eq!(
1546 classdef
1547 .intersect_classes(&glyphs)
1548 .iter()
1549 .collect::<Vec<_>>(),
1550 vec![0]
1551 );
1552
1553 let one: IntSet<GlyphId> = [GlyphId::new(48)].into_iter().collect();
1555 assert!(classdef.intersects_class_glyphs(&one, 0));
1556
1557 let none = IntSet::<GlyphId>::empty();
1559 assert!(!classdef.intersects_class_glyphs(&none, 0));
1560 assert!(classdef.intersected_class_glyphs(&none, 0).is_empty());
1561 }
1562
1563 #[test]
1566 fn classdef_format1_empty_nonzero_start_glyph() {
1567 let classdef = ClassDefFormat1::read(FontData::new(&[0, 1, 0, 60, 0, 0])).unwrap();
1568
1569 for gids in [vec![48u16, 49], vec![70, 71], vec![48, 70]] {
1571 let glyphs: IntSet<GlyphId> = gids.iter().map(|g| GlyphId::new(*g as u32)).collect();
1572 assert!(classdef.intersects_class_glyphs(&glyphs, 0), "{gids:?}");
1573 assert!(!classdef.intersects_class_glyphs(&glyphs, 1), "{gids:?}");
1574 assert_eq!(
1575 classdef
1576 .intersected_class_glyphs(&glyphs, 0)
1577 .iter()
1578 .collect::<Vec<_>>(),
1579 glyphs.iter().collect::<Vec<_>>(),
1580 "{gids:?}"
1581 );
1582 }
1583 }
1584
1585 #[test]
1586 fn classdef_format2_empty() {
1587 let classdef = ClassDefFormat2::read(FontData::new(&[0, 2, 0, 0])).unwrap();
1589 let glyphs: IntSet<GlyphId> = [GlyphId::new(48), GlyphId::new(49), GlyphId::new(50)]
1590 .into_iter()
1591 .collect();
1592
1593 assert!(classdef.intersects_class_glyphs(&glyphs, 0));
1594 assert!(!classdef.intersects_class_glyphs(&glyphs, 1));
1595
1596 let one: IntSet<GlyphId> = [GlyphId::new(48)].into_iter().collect();
1599 assert!(classdef.intersects_class_glyphs(&one, 0));
1600
1601 assert_eq!(
1602 classdef
1603 .intersected_class_glyphs(&glyphs, 0)
1604 .iter()
1605 .collect::<Vec<_>>(),
1606 glyphs.iter().collect::<Vec<_>>()
1607 );
1608 assert!(classdef.intersected_class_glyphs(&glyphs, 1).is_empty());
1609
1610 assert_eq!(
1611 classdef
1612 .intersect_classes(&glyphs)
1613 .iter()
1614 .collect::<Vec<_>>(),
1615 vec![0]
1616 );
1617 }
1618
1619 #[test]
1621 fn classdef_empty_formats_agree() {
1622 let f1 = ClassDef::read(FontData::new(&[0, 1, 0, 0, 0, 0])).unwrap();
1623 let f2 = ClassDef::read(FontData::new(&[0, 2, 0, 0])).unwrap();
1624 assert!(matches!(f1, ClassDef::Format1(..)));
1625 assert!(matches!(f2, ClassDef::Format2(..)));
1626
1627 for gids in [vec![0u32], vec![48], vec![48, 49, 50]] {
1628 let glyphs: IntSet<GlyphId> = gids.iter().copied().map(GlyphId::new).collect();
1629 for class in [0u32, 1] {
1630 assert_eq!(
1631 f1.intersects_class_glyphs(&glyphs, class),
1632 f2.intersects_class_glyphs(&glyphs, class),
1633 "{gids:?} class {class}"
1634 );
1635 assert_eq!(
1636 f1.intersected_class_glyphs(&glyphs, class)
1637 .iter()
1638 .collect::<Vec<_>>(),
1639 f2.intersected_class_glyphs(&glyphs, class)
1640 .iter()
1641 .collect::<Vec<_>>(),
1642 "{gids:?} class {class}"
1643 );
1644 }
1645 assert_eq!(
1646 f1.intersect_classes(&glyphs).iter().collect::<Vec<_>>(),
1647 f2.intersect_classes(&glyphs).iter().collect::<Vec<_>>(),
1648 "{gids:?}"
1649 );
1650 }
1651 }
1652
1653 #[test]
1654 fn delta_decode() {
1655 assert_eq!(
1657 iter_packed_values(0x123f, DeltaFormat::Local4BitDeltas, 4).collect::<Vec<_>>(),
1658 &[1, 2, 3, -1]
1659 );
1660
1661 assert_eq!(
1662 iter_packed_values(0x5540, DeltaFormat::Local2BitDeltas, 5).collect::<Vec<_>>(),
1663 &[1, 1, 1, 1, 1]
1664 );
1665 }
1666
1667 #[test]
1668 fn delta_decode_negative_not_in_last_slot() {
1669 assert_eq!(
1673 iter_packed_values(0xf401, DeltaFormat::Local8BitDeltas, 2).collect::<Vec<_>>(),
1674 &[-12, 1]
1675 );
1676 assert_eq!(
1678 iter_packed_values(0x8123, DeltaFormat::Local4BitDeltas, 4).collect::<Vec<_>>(),
1679 &[-8, 1, 2, 3]
1680 );
1681 assert_eq!(
1683 iter_packed_values(0x9540, DeltaFormat::Local2BitDeltas, 4).collect::<Vec<_>>(),
1684 &[-2, 1, 1, 1]
1685 );
1686 assert_eq!(
1688 iter_packed_values(0x8000, DeltaFormat::Local8BitDeltas, 2).collect::<Vec<_>>(),
1689 &[-128, 0]
1690 );
1691 }
1692
1693 #[test]
1694 fn delta_decode_all() {
1695 let bytes: &[u8] = &[0, 7, 0, 13, 0, 3, 1, 244, 30, 245, 101, 8, 42, 0];
1697 let device = Device::read(bytes.into()).unwrap();
1698 assert_eq!(
1699 device.iter().collect::<Vec<_>>(),
1700 &[1i8, -12, 30, -11, 101, 8, 42]
1701 );
1702 }
1703
1704 #[test]
1705 fn device_decode_does_not_overflow() {
1706 let bytes: &[u8] = &[0, 0xA, 0, 1, 0, 1];
1708 Device::read(bytes.into()).unwrap().iter().count();
1710 }
1711
1712 #[test]
1713 fn bit_storage_tests() {
1714 assert_eq!(bit_storage(0), 0);
1715 assert_eq!(bit_storage(1), 1);
1716 assert_eq!(bit_storage(2), 2);
1717 assert_eq!(bit_storage(4), 3);
1718 assert_eq!(bit_storage(9), 4);
1719 assert_eq!(bit_storage(0x123), 9);
1720 assert_eq!(bit_storage(0x1234), 13);
1721 assert_eq!(bit_storage(0xffff), 16);
1722 assert_eq!(bit_storage(0xffff_ffff), 32);
1723 }
1724
1725 #[test]
1726 fn default_coverage() {
1727 let coverage = CoverageTable::default();
1728 assert_eq!(coverage.iter().count(), 0)
1729 }
1730
1731 fn coords_at(font: &FontRef, tag: Tag, value: f32) -> Vec<F2Dot14> {
1733 let fvar = font.fvar().unwrap();
1734 let mut coords = vec![F2Dot14::ZERO; fvar.axis_count() as usize];
1735 fvar.user_to_normalized(
1736 font.avar().ok().as_ref(),
1737 [(tag, Fixed::from_f64(value as f64))],
1738 &mut coords,
1739 );
1740 coords
1741 }
1742
1743 #[test]
1744 fn index_for_coords_honors_the_condition_set() {
1745 let font = FontRef::new(font_test_data::MATERIAL_SYMBOLS_SUBSET).unwrap();
1746 let feature_vars = font.gsub().unwrap().feature_variations().unwrap().unwrap();
1747 let fill = Tag::new(b"FILL");
1748 assert_eq!(
1749 feature_vars.index_for_coords(&coords_at(&font, fill, 1.0)),
1750 Some(0)
1751 );
1752 assert_eq!(
1753 feature_vars.index_for_coords(&coords_at(&font, fill, 0.5)),
1754 None
1755 );
1756 assert_eq!(feature_vars.index_for_coords(&[]), None);
1759 }
1760
1761 #[test]
1762 fn selected_feature_variations() {
1763 let font = FontRef::new(font_test_data::MATERIAL_SYMBOLS_SUBSET).unwrap();
1764 let cases = [
1765 (0.0, [None, None]),
1767 (0.5, [None, None]),
1768 (0.98, [None, None]),
1769 (0.99, [Some(0), None]),
1770 (1.0, [Some(0), None]),
1771 ];
1772 for (fill, expected) in cases {
1773 let coords = coords_at(&font, Tag::new(b"FILL"), fill);
1774 let selected = SelectedFeatureVariations::new(&font, &coords);
1775 assert_eq!([selected.gsub, selected.gpos], expected, "fill={fill}");
1776 }
1777 }
1778
1779 #[test]
1780 fn a_font_without_the_layout_tables_selects_nothing() {
1781 let font = FontRef::new(font_test_data::NAMES_ONLY).unwrap();
1782 assert_eq!(
1783 SelectedFeatureVariations::new(&font, &[]),
1784 SelectedFeatureVariations::default()
1785 );
1786 }
1787
1788 #[test]
1789 fn default_classdef() {
1790 let classdef = ClassDef::default();
1791 assert_eq!(classdef.population(), 0);
1792 assert_eq!(classdef.iter().count(), 0);
1793 }
1794}