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 let high_byte = code_point >> 8;
76 let low_byte = code_point & 0x00FF;
77
78 let i = if code_point < 0xff {
79 0
81 } else {
82 self.sub_header_keys.get(high_byte)? / 8
84 };
85
86 let sub_header = self.sub_headers.get(i)?;
87
88 let first_code = sub_header.first_code;
89 let range_end = first_code.checked_add(sub_header.entry_count)?;
90 if low_byte < first_code || low_byte >= range_end {
91 return None;
92 }
93
94 let index_offset = usize::from(low_byte.checked_sub(first_code)?) * u16::SIZE;
97
98 let offset = self.sub_headers_offset
101 + SubHeaderRecord::SIZE * usize::from(i + 1)
103 - u16::SIZE
105 + usize::from(sub_header.id_range_offset)
107 + index_offset;
109
110 let glyph: u16 = Stream::read_at(self.data, offset)?;
111 if glyph == 0 {
112 return None;
113 }
114
115 u16::try_from((i32::from(glyph) + i32::from(sub_header.id_delta)) % 65536)
116 .ok()
117 .map(GlyphId)
118 }
119
120 pub fn codepoints(&self, f: impl FnMut(u32)) {
122 let _ = self.codepoints_inner(f);
123 }
124
125 #[inline]
126 fn codepoints_inner(&self, mut f: impl FnMut(u32)) -> Option<()> {
127 for first_byte in 0u16..256 {
128 let i = self.sub_header_keys.get(first_byte)? / 8;
129 let sub_header = self.sub_headers.get(i)?;
130 let first_code = sub_header.first_code;
131
132 if i == 0 {
133 let range_end = first_code.checked_add(sub_header.entry_count)?;
135 if first_byte >= first_code && first_byte < range_end {
136 f(u32::from(first_byte));
137 }
138 } else {
139 let base = first_code.checked_add(first_byte << 8)?;
141 for k in 0..sub_header.entry_count {
142 let code_point = base.checked_add(k)?;
143 f(u32::from(code_point));
144 }
145 }
146 }
147
148 Some(())
149 }
150}
151
152impl core::fmt::Debug for Subtable2<'_> {
153 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
154 write!(f, "Subtable2 {{ ... }}")
155 }
156}