font_map_core/raw/ttf/
cmap.rs1#![allow(clippy::cast_possible_wrap)]
2use super::PlatformType;
3use crate::error::ParseResult;
4use crate::reader::{BinaryReader, Parse};
5
6#[derive(Debug, Default)]
9pub struct CmapTable {
10 pub mappings: Vec<u32>,
12
13 pub tables: Vec<CmapSubtable>,
15}
16
17impl CmapTable {
18 #[must_use]
20 pub fn get_codepoint(&self, index: u16) -> Option<u32> {
21 if index as usize >= self.mappings.len() {
22 None
23 } else {
24 Some(self.mappings[index as usize])
25 }
26 }
27}
28
29impl Parse for CmapTable {
30 fn parse(reader: &mut BinaryReader) -> ParseResult<Self> {
31 let mut table = Self::default();
32
33 reader.skip_u16()?; let num_tables = reader.read_u16()?;
37
38 for _ in 0..num_tables {
41 let platform_id = reader.read_u16()?;
42 let encoding_id = reader.read_u16()?;
43 let offset = reader.read_u32()?;
44
45 debug_msg!(
46 " CMAP subtable: platform={}, encoding={}, offset={}",
47 platform_id,
48 encoding_id,
49 offset
50 );
51
52 let mut subtable_reader = reader.clone();
53 subtable_reader.advance_to(offset as usize)?;
54 let mut subtable = CmapSubtable::parse(&mut subtable_reader)?;
55 subtable.platform = platform_id.into();
56 subtable.encoding = encoding_id;
57
58 for (idx, cde) in &subtable.mappings {
59 let idx = *idx as usize;
60 if table.mappings.len() <= idx {
61 table.mappings.resize(idx + 1, 0xFFFF);
62 }
63 table.mappings[idx] = *cde;
64 }
65 table.tables.push(subtable);
66 }
67
68 Ok(table)
69 }
70}
71
72#[derive(Debug, Default)]
74pub struct CmapSubtable {
75 pub platform: PlatformType,
77
78 pub encoding: u16,
80
81 pub mappings: Vec<(u16, u32)>,
83}
84
85impl Parse for CmapSubtable {
86 #[allow(clippy::too_many_lines)]
87 fn parse(reader: &mut BinaryReader) -> ParseResult<Self> {
88 let fmt = reader.read_u16()?;
89
90 let mut subtable = Self::default();
91 debug_msg!(" CMAP format: {}", fmt);
92
93 match fmt {
94 0 => {
95 reader.skip_u16()?; reader.skip_u16()?; for codepoint in 0u32..=0xFF {
101 let glyph_index = u16::from(reader.read_u8()?);
102 subtable.mappings.push((glyph_index, codepoint));
103 }
104 }
105
106 4 => {
107 reader.skip_u16()?; reader.skip_u16()?; let mut seg_count = reader.read_u16()?;
113 seg_count /= 2;
114
115 reader.skip_u16()?; reader.skip_u16()?; reader.skip_u16()?; let mut end_code = Vec::with_capacity(seg_count as usize);
120 for _ in 0..seg_count {
121 end_code.push(reader.read_u16()?);
122 }
123
124 reader.skip_u16()?; let mut start_code = Vec::with_capacity(seg_count as usize);
127 for _ in 0..seg_count {
128 start_code.push(reader.read_u16()?);
129 }
130
131 let mut id_delta = Vec::with_capacity(seg_count as usize);
132 for _ in 0..seg_count {
133 id_delta.push(reader.read_u16()?);
134 }
135
136 for i in 0..seg_count as usize {
137 let id_range_offset = reader.read_u16()?;
138
139 for codepoint in start_code[i]..=end_code[i] {
140 if codepoint == 0xFFFF {
141 subtable.mappings.push((0, 0xFFFF));
142 break;
143 }
144
145 let glyph_index = if id_range_offset == 0 {
146 codepoint.wrapping_add(id_delta[i])
149 } else {
150 let index_offset =
155 id_range_offset + 2 * (codepoint - start_code[i]) - 2;
156
157 let mut glyph_reader = reader.clone();
158 glyph_reader.advance_by(index_offset as isize)?;
159
160 let glyph_index = glyph_reader.read_u16()?;
161 if glyph_index != 0 {
162 glyph_index.wrapping_add(id_delta[i])
163 } else {
164 glyph_index
165 }
166 };
167
168 subtable.mappings.push((glyph_index, u32::from(codepoint)));
169 }
170 }
171 }
172
173 6 => {
174 reader.skip_u16()?; reader.skip_u16()?; let first_code = reader.read_u16()?;
178 let entry_count = reader.read_u16()?;
179
180 debug_msg!(
181 " CMAP format 6: first_code={}, entry_count={}",
182 first_code,
183 entry_count
184 );
185
186 for i in 0..u32::from(entry_count) {
187 let glyph_index = reader.read_u16()?;
188 let codepoint = u32::from(first_code) + i;
189 subtable.mappings.push((glyph_index, codepoint));
190 }
191 }
192
193 12 => {
194 reader.skip_u16()?; reader.skip_u32()?; reader.skip_u32()?; let num_groups = reader.read_u32()?;
200
201 debug_msg!(" CMAP format 12: num_groups={}", num_groups);
202
203 for _ in 0..num_groups {
204 let start = reader.read_u32()?;
205 let end = reader.read_u32()?;
206 let start_glyph = reader.read_u32()?; debug_msg!(
209 " CMAP group: start={}, end={}, start_glyph={}",
210 start,
211 end,
212 start_glyph
213 );
214
215 let adj = if start < end { 1 } else { -1 };
216
217 let n = start.abs_diff(end);
218 let mut codepoint = start;
219 for i in 0..n {
220 let index = u16::try_from(start_glyph + i).unwrap_or_default();
221 subtable.mappings.push((index, codepoint));
222 codepoint = codepoint.wrapping_add_signed(adj);
223 }
224 }
225 }
226
227 _ => return Err(reader.err(&format!("Unsupported CMAP format: {fmt}"))),
228 }
229
230 debug_msg!(" Found {} mappings", subtable.mappings.len());
231 Ok(subtable)
232 }
233}