ttf_parser/tables/cmap/
format2.rs1use core::convert::TryFrom;
8
9use crate::parser::{FromData, LazyArray16, Stream};
10use crate::GlyphId;
11
12#[derive(Clone, Copy)]
13struct SubHeaderRecord {
14 first_code: u16,
15 entry_count: u16,
16 id_delta: i16,
17 id_range_offset: u16,
18}
19
20impl FromData for SubHeaderRecord {
21 const SIZE: usize = 8;
22
23 #[inline]
24 fn parse(data: &[u8]) -> Option<Self> {
25 let mut s = Stream::new(data);
26 Some(SubHeaderRecord {
27 first_code: s.read::<u16>()?,
28 entry_count: s.read::<u16>()?,
29 id_delta: s.read::<i16>()?,
30 id_range_offset: s.read::<u16>()?,
31 })
32 }
33}
34
35#[derive(Clone, Copy)]
38pub struct Subtable2<'a> {
39 sub_header_keys: LazyArray16<'a, u16>,
40 sub_headers_offset: usize,
41 sub_headers: LazyArray16<'a, SubHeaderRecord>,
42 data: &'a [u8],
44}
45
46impl<'a> Subtable2<'a> {
47 pub fn parse(data: &'a [u8]) -> Option<Self> {
49 let mut s = Stream::new(data);
50 s.skip::<u16>(); s.skip::<u16>(); s.skip::<u16>(); let sub_header_keys = s.read_array16::<u16>(256)?;
54 let sub_headers_count = sub_header_keys.into_iter().map(|n| n / 8).max()? + 1;
56
57 let sub_headers_offset = s.offset();
59 let sub_headers = s.read_array16::<SubHeaderRecord>(sub_headers_count)?;
60
61 Some(Self {
62 sub_header_keys,
63 sub_headers_offset,
64 sub_headers,
65 data,
66 })
67 }
68
69 pub fn glyph_index(&self, code_point: u32) -> Option<GlyphId> {
73 let code_point = u16::try_from(code_point).ok()?;
75
76 let code_point = code_point;
77 let high_byte = code_point >> 8;
78 let low_byte = code_point & 0x00FF;
79
80 let i = if code_point < 0xff {
81 0
83 } else {
84 self.sub_header_keys.get(high_byte)? / 8
86 };
87
88 let sub_header = self.sub_headers.get(i)?;
89
90 let first_code = sub_header.first_code;
91 let range_end = first_code.checked_add(sub_header.entry_count)?;
92 if low_byte < first_code || low_byte >= range_end {
93 return None;
94 }
95
96 let index_offset = usize::from(low_byte.checked_sub(first_code)?) * u16::SIZE;
99
100 let offset =
103 self.sub_headers_offset
104 + SubHeaderRecord::SIZE * usize::from(i + 1)
106 - u16::SIZE
108 + usize::from(sub_header.id_range_offset)
110 + index_offset;
112
113 let glyph: u16 = Stream::read_at(self.data, offset)?;
114 if glyph == 0 {
115 return None;
116 }
117
118 u16::try_from((i32::from(glyph) + i32::from(sub_header.id_delta)) % 65536).ok().map(GlyphId)
119 }
120
121 pub fn codepoints(&self, f: impl FnMut(u32)) {
123 let _ = self.codepoints_inner(f);
124 }
125
126 #[inline]
127 fn codepoints_inner(&self, mut f: impl FnMut(u32)) -> Option<()> {
128 for first_byte in 0u16..256 {
129 let i = self.sub_header_keys.get(first_byte)? / 8;
130 let sub_header = self.sub_headers.get(i)?;
131 let first_code = sub_header.first_code;
132
133 if i == 0 {
134 let range_end = first_code.checked_add(sub_header.entry_count)?;
136 if first_byte >= first_code && first_byte < range_end {
137 f(u32::from(first_byte));
138 }
139 } else {
140 let base = first_code.checked_add(first_byte << 8)?;
142 for k in 0..sub_header.entry_count {
143 let code_point = base.checked_add(k)?;
144 f(u32::from(code_point));
145 }
146 }
147 }
148
149 Some(())
150 }
151}
152
153impl core::fmt::Debug for Subtable2<'_> {
154 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
155 write!(f, "Subtable2 {{ ... }}")
156 }
157}