1use super::{raw_tag, Bytes, RawTag};
4
5pub const GDEF: RawTag = raw_tag(b"GDEF");
6pub const GSUB: RawTag = raw_tag(b"GSUB");
7pub const GPOS: RawTag = raw_tag(b"GPOS");
8
9pub const DFLT: RawTag = raw_tag(b"DFLT");
10
11#[derive(Copy, Clone)]
13pub struct Gdef<'a> {
14 data: Bytes<'a>,
15 classes: u16,
16 mark_classes: u16,
17 mark_sets: u16,
18 var_store: u32,
19}
20
21impl<'a> Gdef<'a> {
22 pub fn new(data: &'a [u8]) -> Option<Self> {
23 let b = Bytes::new(data);
24 let major = b.read::<u16>(0)?;
25 let minor = b.read::<u16>(2)?;
26 let classes = b.read::<u16>(4)?;
27 let mark_classes = b.read::<u16>(10)?;
28 let mark_sets = if major > 1 || minor >= 2 {
29 b.read_or_default::<u16>(12)
30 } else {
31 0
32 };
33 let var_store = if major > 1 || minor >= 3 {
34 b.read_or_default::<u32>(14)
35 } else {
36 0
37 };
38 Some(Self {
39 data: b,
40 classes,
41 mark_classes,
42 mark_sets,
43 var_store,
44 })
45 }
46
47 pub fn from_offset(data: &'a [u8], offset: u32) -> Option<Self> {
48 if offset == 0 {
49 return None;
50 }
51 Self::new(data.get(offset as usize..)?)
52 }
53
54 pub fn empty() -> Self {
55 Self {
56 data: Bytes::new(&[]),
57 classes: 0,
58 mark_classes: 0,
59 mark_sets: 0,
60 var_store: 0,
61 }
62 }
63
64 pub fn ok(&self) -> bool {
65 self.data.len() != 0
66 }
67
68 pub fn has_classes(&self) -> bool {
70 self.classes != 0
71 }
72
73 pub fn class(&self, glyph_id: u16) -> u16 {
75 classdef(&self.data, self.classes as u32, glyph_id)
76 }
77
78 pub fn has_mark_classes(&self) -> bool {
80 self.mark_classes != 0
81 }
82
83 pub fn mark_class(&self, glyph_id: u16) -> u16 {
85 classdef(&self.data, self.mark_classes as u32, glyph_id)
86 }
87
88 pub fn mark_set_coverage(&self, set_offset: u32, glyph_id: u16) -> Option<u16> {
89 if set_offset == 0 {
90 return None;
91 }
92 unsafe { fast_coverage(&self.data, set_offset, glyph_id) }
94 }
95
96 pub fn mark_set_offset(&self, set_index: u16) -> Option<u32> {
97 if self.mark_sets == 0 {
98 return None;
99 }
100 let set = set_index as usize;
101 let b = &self.data;
102 let sets_base = self.mark_sets as usize;
103 let len = b.read::<u16>(sets_base + 2)? as usize;
104 if set >= len {
105 return None;
106 }
107 let offset = b.read::<u32>(sets_base + 4 + set * 4)?;
108 let set_offset = sets_base as u32 + offset;
109 (offset != 0 && validate_coverage(b, set_offset)).then_some(set_offset)
110 }
111
112 pub fn has_var_store(&self) -> bool {
113 self.var_store != 0
114 }
115
116 pub fn delta(&self, outer: u16, inner: u16, coords: &[i16]) -> f32 {
117 if self.var_store != 0 {
118 super::var::item_delta(self.data.data(), self.var_store, outer, inner, coords)
119 .map(|d| d.to_f32())
120 .unwrap_or(0.)
121 } else {
122 0.
123 }
124 }
125}
126
127#[derive(Copy, Clone, PartialEq, Debug)]
129#[repr(u8)]
130pub enum LookupKind {
131 SingleSub,
132 MultiSub,
133 AltSub,
134 LigSub,
135 SingleAdj,
136 PairAdj,
137 Cursive,
138 MarkToBase,
139 MarkToLig,
140 MarkToMark,
141 Context,
142 ChainContext,
143 RevChainContext,
144}
145
146#[derive(Copy, Clone)]
148pub struct LookupData {
149 pub index: u16,
150 pub stage: u8,
151 pub kind: LookupKind,
152 pub feature: u16,
153 pub mask: u8,
154 pub ignored: u8,
155 pub is_ext: bool,
156 pub offset: u32,
157 pub coverage: u32,
158 pub count: u16,
159 pub subtables: (u16, u16),
160 pub mark_set: u32,
161 pub mark_check: u8,
162 pub mark_class: u8,
163}
164
165impl LookupData {
166 pub fn subtable_data(&self, b: &Bytes, index: u16) -> Option<SubtableData> {
167 let base = self.offset as usize;
168 let subtable_base = base + 6;
169 let mut offset = base + b.read::<u16>(subtable_base + index as usize * 2)? as usize;
170 if self.is_ext {
171 offset = offset + b.read::<u32>(offset + 4)? as usize;
172 }
173 let fmt = b.read::<u16>(offset)?;
174 subtable_data(b, offset as u32, self.kind, fmt)
175 }
176}
177
178#[derive(Copy, Clone, PartialEq, Debug)]
180#[repr(u8)]
181pub enum SubtableKind {
182 SingleSub1,
183 SingleSub2,
184 MultiSub1,
185 AltSub1,
186 LigSub1,
187 SingleAdj1,
188 SingleAdj2,
189 PairAdj1,
190 PairAdj2,
191 Cursive1,
192 MarkToBase1,
193 MarkToLig1,
194 MarkToMark1,
195 Context1,
196 Context2,
197 Context3,
198 ChainContext1,
199 ChainContext2,
200 ChainContext3,
201 RevChainContext1,
202}
203
204#[derive(Copy, Clone)]
206pub struct SubtableData {
207 pub offset: u32,
208 pub kind: SubtableKind,
209 pub coverage: u16,
210}
211
212impl SubtableData {
213 pub fn coverage(&self, b: &Bytes, glyph_id: u16) -> Option<u16> {
214 unsafe { fast_coverage(b, self.offset + self.coverage as u32, glyph_id) }
215 }
216}
217
218#[derive(Copy, Clone)]
220pub struct FeatureSubsts(u32);
221
222impl FeatureSubsts {
223 pub fn new(b: &Bytes, offset: u32, coords: &[i16]) -> Option<Self> {
224 if offset == 0 || coords.is_empty() {
225 return None;
226 }
227 let base = offset as usize;
228 let count = b.read::<u32>(base + 4)? as usize;
229 for i in 0..count {
230 let rec = base + 8 + i * 8;
231 let condset_table = base + b.read::<u32>(rec)? as usize;
232 let condset_count = b.read::<u16>(condset_table)? as usize;
233 let mut matched = 0;
234 for j in 0..condset_count {
235 let cond_table = condset_table + b.read::<u32>(condset_table + 2 + j * 4)? as usize;
236 let format = b.read::<u16>(cond_table)?;
237 if format != 1 {
238 break;
239 }
240 let axis = b.read::<u16>(cond_table + 2)? as usize;
241 if axis >= coords.len() {
242 break;
243 }
244 let coord = coords[axis];
245 let min = b.read::<i16>(cond_table + 4)?;
246 if coord < min {
247 break;
248 }
249 let max = b.read::<i16>(cond_table + 6)?;
250 if coord > max {
251 break;
252 }
253 matched += 1;
254 }
255 if matched == condset_count {
256 return Some(Self(offset + b.read::<u32>(rec + 4)?));
257 }
258 }
259 None
260 }
261
262 pub fn apply(self, b: &Bytes, index: u16) -> Option<usize> {
263 let mut base = self.0 as usize;
264 let count = b.read::<u16>(base + 4)? as usize;
265 base += 6;
266 let mut l = 0;
267 let mut h = count;
268 while l < h {
269 use core::cmp::Ordering::*;
270 let i = (l + h) / 2;
271 let rec = base + i * 6;
272 let idx = b.read::<u16>(rec)?;
273 match index.cmp(&idx) {
274 Less => h = i,
275 Greater => l = i + 1,
276 Equal => return Some((self.0 + b.read::<u32>(rec + 2)?) as usize),
277 }
278 }
279 None
280 }
281}
282
283pub fn script_count(b: &Bytes, gsubgpos_offset: u32) -> u16 {
284 if gsubgpos_offset == 0 {
285 return 0;
286 }
287 let base = gsubgpos_offset as usize;
288 let offset = b.read_or_default::<u16>(base + 4) as usize;
289 if offset == 0 {
290 return 0;
291 }
292 b.read_or_default::<u16>(base + offset)
293}
294
295pub fn script_at(b: &Bytes, gsubgpos_offset: u32, index: u16) -> Option<(RawTag, u32)> {
296 if gsubgpos_offset == 0 {
297 return None;
298 }
299 let base = gsubgpos_offset as usize;
300 let sbase = base + b.read::<u16>(base + 4)? as usize;
301 let rec = sbase + 2 + index as usize * 6;
302 let tag = b.read::<u32>(rec)?;
303 let offset = sbase as u32 + b.read::<u16>(rec + 4)? as u32;
304 Some((tag, offset))
305}
306
307pub fn script_by_tag(b: &Bytes, gsubgpos_offset: u32, script: RawTag) -> Option<u32> {
308 if gsubgpos_offset == 0 {
309 return None;
310 }
311 let base = gsubgpos_offset as usize;
312 let sbase = base + b.read::<u16>(base + 4)? as usize;
313 let mut l = 0;
314 let mut h = b.read::<u16>(sbase)? as usize;
315 while l < h {
316 use core::cmp::Ordering::*;
317 let i = l + (h - l) / 2;
318 let rec = sbase + 2 + i * 6;
319 let t = b.read::<u32>(rec)?;
320 match script.cmp(&t) {
321 Less => h = i,
322 Greater => l = i + 1,
323 Equal => return Some(sbase as u32 + b.read::<u16>(rec + 4)? as u32),
324 }
325 }
326 None
327}
328
329pub fn script_language_count(b: &Bytes, script_offset: u32) -> u16 {
330 if script_offset == 0 {
331 return 0;
332 }
333 b.read::<u16>(script_offset as usize + 2)
334 .map(|n| n + 1)
335 .unwrap_or(0)
336}
337
338pub fn script_default_language(b: &Bytes, script_offset: u32) -> Option<u32> {
339 if script_offset == 0 {
340 return None;
341 }
342 let offset = b.read::<u16>(script_offset as usize)? as u32;
343 if offset == 0 {
344 None
345 } else {
346 Some(script_offset + offset)
347 }
348}
349
350pub fn script_language_at(b: &Bytes, script_offset: u32, index: u16) -> Option<(RawTag, u32)> {
351 if script_offset == 0 {
352 return None;
353 }
354 let index = if index == 0 {
355 return Some((DFLT, script_default_language(b, script_offset)?));
356 } else {
357 index - 1
358 };
359 let rec = script_offset as usize + 4 + index as usize * 6;
360 let tag = b.read::<u32>(rec)?;
361 let offset = b.read::<u16>(rec + 4)? as u32;
362 if offset == 0 {
363 return None;
364 }
365 Some((tag, script_offset + offset))
366}
367
368pub fn script_language_by_tag(
369 b: &Bytes,
370 script_offset: u32,
371 language: Option<RawTag>,
372) -> Option<(u32, bool)> {
373 if script_offset == 0 {
374 return None;
375 }
376 let base = script_offset as usize;
377 if let Some(lang) = language {
378 let mut l = 0;
379 let mut h = b.read::<u16>(base + 2)? as usize;
380 while l < h {
381 use core::cmp::Ordering::*;
382 let i = (l + h) / 2;
383 let rec = base + 4 + i * 6;
384 let t = b.read::<u32>(rec)?;
385 match lang.cmp(&t) {
386 Less => h = i,
387 Greater => l = i + 1,
388 Equal => {
389 let lang_offset = b.read::<u16>(rec + 4)? as usize;
390 if lang_offset == 0 {
391 return None;
392 }
393 return Some((script_offset + lang_offset as u32, false));
394 }
395 }
396 }
397 }
398 let default = b.read::<u16>(base)? as usize;
399 if default == 0 {
400 return None;
401 }
402 Some(((base + default) as u32, true))
403}
404
405pub fn language_or_default_by_tags(
406 b: &Bytes,
407 gsubgpos_offset: u32,
408 script: RawTag,
409 lang: Option<RawTag>,
410) -> Option<(u32, [RawTag; 2])> {
411 if let Some(script_offset) = script_by_tag(b, gsubgpos_offset, script) {
412 let (lang_offset, is_default) = script_language_by_tag(b, script_offset, lang)?;
413 Some((
414 lang_offset,
415 [
416 script,
417 if is_default {
418 DFLT
419 } else {
420 lang.unwrap_or(DFLT)
421 },
422 ],
423 ))
424 } else {
425 let (lang_offset, is_default) = language_by_tags(b, gsubgpos_offset, DFLT, lang)?;
426 Some((
427 lang_offset,
428 [
429 DFLT,
430 if is_default {
431 DFLT
432 } else {
433 lang.unwrap_or(DFLT)
434 },
435 ],
436 ))
437 }
438}
439
440pub fn language_by_tags(
441 b: &Bytes,
442 gsubgpos_offset: u32,
443 script: RawTag,
444 language: Option<RawTag>,
445) -> Option<(u32, bool)> {
446 script_language_by_tag(b, script_by_tag(b, gsubgpos_offset, script)?, language)
447}
448
449pub fn language_feature_count(b: &Bytes, language_offset: u32) -> u16 {
450 if language_offset == 0 {
451 return 0;
452 }
453 b.read_or_default(language_offset as usize + 4)
454}
455
456pub fn language_feature_at(b: &Bytes, language_offset: u32, index: u16) -> Option<u16> {
457 b.read(language_offset as usize + 6 + index as usize * 2)
458}
459
460pub fn language_features<'a>(
461 b: Bytes<'a>,
462 gsubgpos_offset: u32,
463 language_offset: u32,
464) -> impl Iterator<Item = (RawTag, u32)> + 'a + Clone {
465 let mut count = language_feature_count(&b, language_offset);
466 if gsubgpos_offset == 0 {
467 count = 0;
468 }
469 let base = gsubgpos_offset as usize;
470 let fbase = b.read_or_default::<u16>(base + 6) as usize;
471 if fbase == 0 {
472 count = 0;
473 }
474 let fbase = base + fbase;
475 (0..count).filter_map(move |i| {
476 let index = language_feature_at(&b, language_offset, i)?;
477 let rec = fbase + 2 + index as usize * 6;
478 let tag = b.read::<u32>(rec)?;
479 let offset = b.read::<u16>(rec + 4)?;
480 if offset == 0 {
481 return None;
482 }
483 Some((tag, fbase as u32 + offset as u32))
484 })
485}
486
487pub fn feature_count(b: &Bytes, gsubgpos_offset: u32) -> u16 {
488 if gsubgpos_offset == 0 {
489 return 0;
490 }
491 let base = gsubgpos_offset as usize;
492 let fbase = b.read_or_default::<u16>(base + 6) as usize;
493 if fbase == 0 {
494 return 0;
495 }
496 b.read_or_default::<u16>(base + fbase)
497}
498
499pub fn feature_at(b: &Bytes, gsubgpos_offset: u32, index: u16) -> Option<(RawTag, u32)> {
500 if gsubgpos_offset == 0 {
501 return None;
502 }
503 let base = gsubgpos_offset as usize;
504 let fbase = b.read::<u16>(base + 6)? as usize;
505 if fbase == 0 {
506 return None;
507 }
508 let fbase = base + fbase;
509 let rec = fbase + 2 + index as usize * 6;
510 let tag = b.read::<u32>(rec)?;
511 let offset = b.read::<u16>(rec + 4)?;
512 if offset == 0 {
513 return None;
514 }
515 Some((tag, fbase as u32 + offset as u32))
516}
517
518pub fn feature_var_offset(b: &Bytes, gsubgpos_offset: u32) -> u32 {
519 if gsubgpos_offset == 0 {
520 return 0;
521 }
522 let base = gsubgpos_offset as usize;
523 let major = b.read_or_default::<u16>(base);
524 if major > 1 || (major == 1 && b.read_or_default::<u16>(base + 2) >= 1) {
525 let offset = b.read_or_default::<u32>(base + 10);
526 if offset != 0 {
527 gsubgpos_offset + offset
528 } else {
529 0
530 }
531 } else {
532 0
533 }
534}
535
536pub fn lookup_data(
537 b: &Bytes,
538 stage: u8,
539 list_base: u32,
540 index: u16,
541 mask: u8,
542 gdef: Option<&Gdef>,
543) -> Option<LookupData> {
544 if list_base == 0 {
545 return None;
546 }
547 let base = list_base as usize;
548 let rec = base + 2 + index as usize * 2;
549 let offset = b.read::<u16>(rec)?;
550 let base = base + offset as usize;
551 let mut kind = b.read::<u16>(base)? as u8;
552 let flag = b.read::<u16>(base + 2)?;
553 let f = flag as u8;
554 let count = b.read::<u16>(base + 4)?;
555 let mark_class = (flag >> 8) as u8;
556 let ignore_marks = f & (1 << 3) != 0;
557 let mut mark_check = 0;
558 let mut mark_set = 0;
559 if !ignore_marks {
560 if let Some(gdef) = gdef {
561 mark_check = (mark_class != 0 && gdef.has_mark_classes()) as u8;
562 mark_set = if gdef.ok() && flag & 0x10 != 0 {
563 let idx = b.read::<u16>(base + 6 + count as usize * 2)?;
564 mark_check = 1;
565 gdef.mark_set_offset(idx).unwrap_or(0)
566 } else {
567 0
568 };
569 }
570 }
571 let is_sub = stage == 0;
572 let subtables = base + 6;
573 let is_ext = (is_sub && kind == 7) || (!is_sub && kind == 9);
574 if is_ext && count > 0 {
575 let s = base + b.read::<u16>(subtables)? as usize;
576 kind = b.read::<u16>(s + 2)? as u8;
577 }
578 use LookupKind::*;
579 let kind = if stage == 0 {
580 match kind {
581 1 => SingleSub,
582 2 => MultiSub,
583 3 => AltSub,
584 4 => LigSub,
585 5 => Context,
586 6 => ChainContext,
587 8 => RevChainContext,
588 _ => return None,
589 }
590 } else {
591 match kind {
592 1 => SingleAdj,
593 2 => PairAdj,
594 3 => Cursive,
595 4 => MarkToBase,
596 5 => MarkToLig,
597 6 => MarkToMark,
598 7 => Context,
599 8 => ChainContext,
600 _ => return None,
601 }
602 };
603 let ignored = (f & 0b1110) | 1 << 5;
604 Some(LookupData {
605 index,
606 stage,
607 kind,
608 feature: 0,
609 mask,
610 ignored,
611 is_ext,
612 offset: base as u32,
613 count,
614 coverage: !0,
615 subtables: (0, 0),
616 mark_class,
617 mark_set,
618 mark_check,
619 })
620}
621
622pub fn subtable_data(b: &Bytes, offset: u32, kind: LookupKind, fmt: u16) -> Option<SubtableData> {
623 let base = offset as usize;
624 fn cov(b: &Bytes, base: usize, offset: usize) -> Option<u16> {
625 let c = b.read::<u16>(base + offset)?;
626 validate_coverage(b, base as u32 + c as u32).then_some(c)
627 }
628 use LookupKind::*;
629 match kind {
630 SingleSub => {
631 let kind = match fmt {
632 1 => SubtableKind::SingleSub1,
633 2 => SubtableKind::SingleSub2,
634 _ => return None,
635 };
636 let coverage = cov(b, base, 2)?;
637 Some(SubtableData {
638 offset,
639 kind,
640 coverage,
641 })
642 }
643 MultiSub => {
644 let kind = match fmt {
645 1 => SubtableKind::MultiSub1,
646 _ => return None,
647 };
648 let coverage = cov(b, base, 2)?;
649 Some(SubtableData {
650 offset,
651 kind,
652 coverage,
653 })
654 }
655 AltSub => {
656 let kind = match fmt {
657 1 => SubtableKind::AltSub1,
658 _ => return None,
659 };
660 let coverage = cov(b, base, 2)?;
661 Some(SubtableData {
662 offset,
663 kind,
664 coverage,
665 })
666 }
667 LigSub => {
668 let kind = match fmt {
669 1 => SubtableKind::LigSub1,
670 _ => return None,
671 };
672 let coverage = cov(b, base, 2)?;
673 Some(SubtableData {
674 offset,
675 kind,
676 coverage,
677 })
678 }
679 SingleAdj => {
680 let kind = match fmt {
681 1 => SubtableKind::SingleAdj1,
682 2 => SubtableKind::SingleAdj2,
683 _ => return None,
684 };
685 let coverage = cov(b, base, 2)?;
686 Some(SubtableData {
687 offset,
688 kind,
689 coverage,
690 })
691 }
692 PairAdj => {
693 let kind = match fmt {
694 1 => SubtableKind::PairAdj1,
695 2 => SubtableKind::PairAdj2,
696 _ => return None,
697 };
698 let coverage = cov(b, base, 2)?;
699 Some(SubtableData {
700 offset,
701 kind,
702 coverage,
703 })
704 }
705 Cursive => {
706 let kind = match fmt {
707 1 => SubtableKind::Cursive1,
708 _ => return None,
709 };
710 let coverage = cov(b, base, 2)?;
711 Some(SubtableData {
712 offset,
713 kind,
714 coverage,
715 })
716 }
717 MarkToBase => {
718 let kind = match fmt {
719 1 => SubtableKind::MarkToBase1,
720 _ => return None,
721 };
722 let coverage = cov(b, base, 2)?;
723 Some(SubtableData {
724 offset,
725 kind,
726 coverage,
727 })
728 }
729 MarkToLig => {
730 let kind = match fmt {
731 1 => SubtableKind::MarkToLig1,
732 _ => return None,
733 };
734 let coverage = cov(b, base, 2)?;
735 Some(SubtableData {
736 offset,
737 kind,
738 coverage,
739 })
740 }
741 MarkToMark => {
742 let kind = match fmt {
743 1 => SubtableKind::MarkToMark1,
744 _ => return None,
745 };
746 let coverage = cov(b, base, 2)?;
747 Some(SubtableData {
748 offset,
749 kind,
750 coverage,
751 })
752 }
753 Context => match fmt {
754 1 | 2 => {
755 let kind = if fmt == 1 {
756 SubtableKind::Context1
757 } else {
758 SubtableKind::Context2
759 };
760 let coverage = cov(b, base, 2)?;
761 Some(SubtableData {
762 offset,
763 kind,
764 coverage,
765 })
766 }
767 3 => {
768 let coverage = cov(b, base, 6)?;
769 Some(SubtableData {
770 kind: SubtableKind::Context3,
771 offset,
772 coverage,
773 })
774 }
775 _ => None,
776 },
777 ChainContext => match fmt {
778 1 | 2 => {
779 let kind = if fmt == 1 {
780 SubtableKind::ChainContext1
781 } else {
782 SubtableKind::ChainContext2
783 };
784 let coverage = cov(b, base, 2)?;
785 Some(SubtableData {
786 offset,
787 kind,
788 coverage,
789 })
790 }
791 3 => {
792 let backtrack_len = b.read::<u16>(base + 2)? as usize * 2;
793 let input_len = b.read::<u16>(base + backtrack_len + 4)?;
794 if input_len == 0 {
795 return None;
796 }
797 let coverage = cov(b, base, backtrack_len + 6)?;
798 Some(SubtableData {
799 kind: SubtableKind::ChainContext3,
800 offset,
801 coverage,
802 })
803 }
804 _ => None,
805 },
806 RevChainContext => {
807 let kind = match fmt {
808 1 => SubtableKind::RevChainContext1,
809 _ => return None,
810 };
811 let coverage = cov(b, base, 2)?;
812 Some(SubtableData {
813 offset,
814 kind,
815 coverage,
816 })
817 }
818 }
819}
820
821fn validate_coverage(b: &Bytes, coverage_offset: u32) -> bool {
822 if coverage_offset == 0 {
823 return false;
824 }
825 let base = coverage_offset as usize;
826 let arr = base + 4;
827 match (b.read::<u16>(base), b.read::<u16>(base + 2)) {
828 (Some(_), Some(0)) => false,
830 (Some(1), Some(len)) => b.check_range(arr, len as usize * 2),
831 (Some(2), Some(len)) => b.check_range(arr, len as usize * 6),
832 _ => false,
833 }
834}
835
836pub unsafe fn fast_coverage(b: &Bytes, coverage_offset: u32, glyph_id: u16) -> Option<u16> {
837 let base = coverage_offset as usize;
838 let fmt = b.read_unchecked::<u16>(base);
839 let len = b.read_unchecked::<u16>(base + 2) as usize;
840 let arr = base + 4;
841 if fmt == 1 {
842 let mut l = 0;
843 let mut h = len;
844 while l < h {
845 use core::cmp::Ordering::*;
846 let i = (l + h) / 2;
847 let g = b.read_unchecked::<u16>(arr + i * 2);
848 match glyph_id.cmp(&g) {
849 Less => h = i,
850 Greater => l = i + 1,
851 Equal => return Some(i as u16),
852 }
853 }
854 } else if fmt == 2 {
855 let mut l = 0;
856 let mut h = len;
857 while l < h {
858 let i = (l + h) / 2;
859 let rec = arr + i * 6;
860 let start = b.read_unchecked::<u16>(rec);
861 if glyph_id < start {
862 h = i;
863 } else if glyph_id > b.read_unchecked::<u16>(rec + 2) {
864 l = i + 1;
865 } else {
866 let base = b.read_unchecked::<u16>(rec + 4);
867 return Some(base + glyph_id - start);
868 }
869 }
870 }
871 None
872}
873
874pub fn coverage(b: &Bytes, coverage_offset: u32, glyph_id: u16) -> Option<u16> {
875 if coverage_offset == 0 {
876 return None;
877 }
878 let base = coverage_offset as usize;
879 let fmt = b.read::<u16>(base)?;
880 let len = b.read::<u16>(base + 2)? as usize;
881 let arr = base + 4;
882 if fmt == 1 {
883 if !b.check_range(arr, len * 2) {
884 return None;
885 }
886 let mut l = 0;
887 let mut h = len;
888 while l < h {
889 use core::cmp::Ordering::*;
890 let i = (l + h) / 2;
891 let g = unsafe { b.read_unchecked::<u16>(arr + i * 2) };
892 match glyph_id.cmp(&g) {
893 Less => h = i,
894 Greater => l = i + 1,
895 Equal => return Some(i as u16),
896 }
897 }
898 } else if fmt == 2 {
899 if !b.check_range(arr, len * 6) {
900 return None;
901 }
902 let mut l = 0;
903 let mut h = len;
904 while l < h {
905 let i = (l + h) / 2;
906 let rec = arr + i * 6;
907 let start = unsafe { b.read_unchecked::<u16>(rec) };
908 if glyph_id < start {
909 h = i;
910 } else if glyph_id > unsafe { b.read_unchecked::<u16>(rec + 2) } {
911 l = i + 1;
912 } else {
913 let base = unsafe { b.read_unchecked::<u16>(rec + 4) };
914 return Some(base + (glyph_id - start));
915 }
916 }
917 }
918 None
919}
920
921pub fn classdef(b: &Bytes, classdef_offset: u32, glyph_id: u16) -> u16 {
922 if classdef_offset == 0 {
923 return 0;
924 }
925 let base = classdef_offset as usize;
926 let fmt = b.read_or_default::<u16>(base);
927 if fmt == 1 {
928 let start = b.read_or_default::<u16>(base + 2);
929 let len = b.read_or_default::<u16>(base + 4);
930 let end = start + len - 1;
931 let arr = base + 6;
932 if glyph_id >= start && glyph_id <= end {
933 return b.read_or_default::<u16>(arr + (glyph_id - start) as usize * 2);
934 }
935 return 0;
936 } else if fmt == 2 {
937 let len = b.read_or_default::<u16>(base + 2) as usize;
938 let arr = base + 4;
939 if !b.check_range(arr, len * 6) {
940 return 0;
941 }
942 let mut l = 0;
943 let mut h = len;
944 while l < h {
945 let i = (l + h) / 2;
946 let rec = arr + i * 6;
947 let start = unsafe { b.read_unchecked::<u16>(rec) };
948 if glyph_id < start {
949 h = i;
950 } else if glyph_id > unsafe { b.read_unchecked::<u16>(rec + 2) } {
951 l = i + 1;
952 } else {
953 return unsafe { b.read_unchecked::<u16>(rec + 4) };
954 }
955 }
956 }
957 0
958}