ttf_view/tables/cmap/
mod.rs1use crate::{
2 tables::{Table, TableDirectory, TableError},
3 types::{Offset32, Tag, tags, uint16, uint32},
4 util::custom_iterator,
5};
6use std::mem::ManuallyDrop;
7
8mod codepoint;
9mod glyph_id;
10
11pub use codepoint::*;
12pub use glyph_id::*;
13
14pub mod format0;
15pub mod format2;
16pub mod format4;
17pub mod format6;
18pub mod format8;
19
20pub mod format10;
21pub mod format12;
22pub mod format13;
23pub mod format14;
24
25#[repr(C)]
26pub struct CmapV0 {
27 _exhaustive_but_dont_instantiate: (),
28 pub version: uint16,
30 pub num_tables: uint16,
31 encoding_records: [EncodingRecordRaw; 0],
32}
33#[repr(C)]
34pub struct EncodingRecordRaw {
35 pub platform_id: uint16,
36 pub encoding_id: uint16,
37 pub subtable_offset: Offset32,
38}
39
40impl<'a> Table<'a> for Cmap<'a> {
41 const TAG: Tag = tags::cmap;
42 fn new_in(dir: &'a TableDirectory) -> Result<Self, TableError> {
43 let rec = dir.table_record(Self::TAG).ok_or(TableError::NotFound)?;
44 let v0 = rec.raw_as::<CmapV0>().ok_or(TableError::InvalidLen)?;
45
46 let required_len =
47 size_of::<CmapV0>() + v0.num_tables.get() as usize * size_of::<EncodingRecordRaw>();
48 if (rec.length.get() as usize) < required_len {
50 return Err(TableError::InvalidLen);
51 }
52
53 for encoding in v0.encodings() {
54 let subtable_offset = encoding.subtable_offset.get() as usize;
56 if (rec.length.get() as usize) < subtable_offset + size_of::<uint16>() {
57 return Err(TableError::InvalidLen);
58 }
59 let subtable = encoding.subtable();
61 let header_len = match subtable.format() {
62 0 | 2 | 4 | 6 => size_of::<ShortMeta>(),
63 8 | 10 | 12 | 13 => size_of::<LongMeta>(),
64 14 => size_of::<LenOnlyMeta>(),
65 _ => 2,
66 };
67 if (rec.length.get() as usize) < subtable_offset + header_len {
69 return Err(TableError::InvalidLen);
70 }
71 if (rec.length.get() as usize) < subtable_offset + subtable.length().unwrap() as usize {
73 return Err(TableError::InvalidLen);
74 }
75 }
76
77 Ok(Self { cmap: v0 })
78 }
79}
80
81#[derive(Copy)]
82#[derive_const(Clone)]
83pub struct Cmap<'a> {
84 cmap: &'a CmapV0,
85}
86
87const impl<'a> std::ops::Deref for Cmap<'a> {
88 type Target = &'a CmapV0;
89 fn deref(&self) -> &Self::Target {
90 &self.cmap
91 }
92}
93
94impl CmapV0 {
95 pub const fn encoding_records(&self) -> &[EncodingRecordRaw] {
96 let len = self.num_tables.get() as usize;
97 unsafe { std::slice::from_raw_parts(self.encoding_records.as_ptr(), len) }
98 }
99 pub const fn encodings(&self) -> EncodingsIter<'_> {
100 EncodingsIter::new(self)
101 }
102}
103
104#[derive(Copy)]
105#[derive_const(Clone)]
106pub struct EncodingRecord<'a>(&'a CmapV0, &'a EncodingRecordRaw);
107
108const impl<'a> std::ops::Deref for EncodingRecord<'a> {
109 type Target = &'a EncodingRecordRaw;
110 fn deref(&self) -> &Self::Target {
111 &self.1
112 }
113}
114impl<'a> EncodingRecord<'a> {
115 pub const fn subtable(&self) -> &'a CmapSubtable {
116 let offset = self.1.subtable_offset.get() as _;
117 unsafe { &*std::ptr::from_ref(self.0).cast::<u8>().byte_add(offset).cast() }
118 }
119}
120
121pub struct EncodingsIter<'a> {
123 cmap: &'a CmapV0,
124 inner: std::slice::Iter<'a, EncodingRecordRaw>,
125}
126impl<'a> EncodingsIter<'a> {
127 pub const fn new(cmap: &'a CmapV0) -> Self {
128 Self { cmap, inner: cmap.encoding_records().iter() }
129 }
130 pub fn as_records(&self) -> &'a [EncodingRecordRaw] {
132 self.inner.as_slice()
133 }
134}
135custom_iterator!(EncodingsIter<'a> as this {
136 type Item = EncodingRecord<'a>;
137 map: |x| EncodingRecord(this.cmap, x);
138});
139
140#[repr(C)]
141pub struct CmapSubtable {
142 meta: SubtableMeta,
143 }
145
146#[repr(C)]
165union SubtableMeta {
166 short: ManuallyDrop<ShortMeta>,
167 long: ManuallyDrop<LongMeta>,
168 len_only: ManuallyDrop<LenOnlyMeta>,
169}
170
171#[repr(C)]
172struct ShortMeta {
173 format: uint16,
174 length: uint16,
175 language: uint16,
176 data: [u8; 0],
177}
178#[repr(C)]
179struct LongMeta {
180 format: uint16,
181 reserved: uint16,
182 length: uint32,
183 language: uint32,
184 data: [u8; 0],
185}
186#[repr(C)]
187struct LenOnlyMeta {
188 format: uint16,
189 length: uint32,
190 data: [u8; 0],
191}
192
193impl CmapSubtable {
194 pub const fn format(&self) -> u16 {
195 unsafe { self.meta.short.format.get() }
196 }
197 pub const fn length(&self) -> Option<u32> {
198 Some(match self.format() {
199 0 | 2 | 4 | 6 => unsafe { self.meta.short.length.get() as _ },
200 8 | 10 | 12 | 13 => unsafe { self.meta.long.length.get() },
201 14 => unsafe { self.meta.len_only.length.get() },
202 _ => return None,
203 })
204 }
205 pub const fn language(&self) -> Option<u32> {
206 Some(match self.format() {
207 0 | 2 | 4 | 6 => unsafe { self.meta.short.language.get() as _ },
208 8 | 10 | 12 | 13 => unsafe { self.meta.long.language.get() },
209 _ => return None,
210 })
211 }
212 const fn data_ptr(&self) -> Option<&u8> {
213 Some(match self.format() {
214 0 | 2 | 4 | 6 => unsafe { &*self.meta.short.data.as_ptr() },
215 8 | 10 | 12 | 13 => unsafe { &*self.meta.long.data.as_ptr() },
216 14 => unsafe { &*self.meta.len_only.data.as_ptr() },
217 _ => return None,
218 })
219 }
220
221 pub const fn get_as_bytes(&self) -> Option<&[u8]> {
222 match self.format() {
223 0 | 2 | 4 | 6 => unsafe {
224 let size = self.meta.short.length.get() as usize - size_of::<ShortMeta>();
225 Some(std::slice::from_raw_parts(self.meta.short.data.as_ptr(), size))
226 },
227 8 | 10 | 12 | 13 => unsafe {
228 let size = self.meta.long.length.get() as usize - size_of::<LongMeta>();
229 Some(std::slice::from_raw_parts(self.meta.long.data.as_ptr(), size))
230 },
231 14 => unsafe {
232 let size = self.meta.len_only.length.get() as usize - size_of::<LenOnlyMeta>();
233 Some(std::slice::from_raw_parts(self.meta.len_only.data.as_ptr(), size))
234 },
235 _ => None,
236 }
237 }
238 pub const fn get_as<T: CmapSubtableTrait>(&self) -> Option<&T> {
239 if self.format() == T::FORMAT { Some(unsafe { self.get_as_unchecked() }) } else { None }
240 }
241 pub const unsafe fn get_as_unchecked<T: CmapSubtableTrait>(&self) -> &T {
242 debug_assert!(self.format() == T::FORMAT);
243 unsafe { &*std::ptr::from_ref(self.data_ptr().unwrap()).cast() }
244 }
245
246 pub const fn as_format0(&self) -> Option<&format0::Format0> {
247 self.get_as()
248 }
249 pub const fn as_format2(&self) -> Option<&format2::Format2> {
250 self.get_as()
251 }
252 pub const fn as_format4(&self) -> Option<&format4::Format4> {
253 self.get_as()
254 }
255 pub const fn as_format6(&self) -> Option<&format6::Format6> {
256 self.get_as()
257 }
258 pub const fn as_format8(&self) -> Option<&format8::Format8> {
259 self.get_as()
260 }
261 pub const fn as_format10(&self) -> Option<&format10::Format10> {
262 self.get_as()
263 }
264 pub const fn as_format12(&self) -> Option<&format12::Format12> {
265 self.get_as()
266 }
267 pub const fn as_format13(&self) -> Option<&format13::Format13> {
268 self.get_as()
269 }
270 pub const fn as_format14(&self) -> Option<&format14::Format14> {
271 self.get_as()
272 }
273}
274
275pub trait CmapSubtableTrait {
276 const FORMAT: u16;
277}