Skip to main content

ttf_view/tables/cmap/
mod.rs

1use 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    // version ≥ 0:
29    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        // Validate that all encoding records are in range
49        if (rec.length.get() as usize) < required_len {
50            return Err(TableError::InvalidLen);
51        }
52
53        for encoding in v0.encodings() {
54            // Validate that this subtable's format is in range
55            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            // Calculate this subtable format's header length
60            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            // Validate that this subtable's header is in range
68            if (rec.length.get() as usize) < subtable_offset + header_len {
69                return Err(TableError::InvalidLen);
70            }
71            // Validate that all of this subtable's data is in range
72            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
121// TODO: When std::slice::Iter's Clone is constified, make the derive const
122pub 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    // TODO: When std::slice::Iter's as_slice() is constified, constify as_records()
131    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    // : <format-specific data: use meta.*.data to point to>,
144}
145
146/// The offsets and sizes of `length` and `language` depend on the subtable's format:
147///
148/// ```text
149///                                        SubtableMeta
150///                ┌─────────────────────────────┐─────────────────────────────┐
151///                 0    1    2    3    4    5    6
152///  ShortMeta     ┌────┬────┬────┬────┬────┬────┐
153///  f 0,2,4,6     │ format  │ length  │language │
154///                └─────────┴─────────┴─────────┘
155///                 0    1    2    3    4    5    6    7    8    9    10   11   12
156///  LongMeta      ┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
157///  f 8,10,12,13  │ format  │reserved │      length       │     language      │
158///                └─────────┴─────────┴───────────────────┴───────────────────┘
159///                 0    1    2    3    4    5    6
160///  LenOnlyMeta   ┌────┬────┬────┬────┬────┬────┐
161///  f 14          │ format  │      length       │
162///                └─────────┴───────────────────┘
163/// ```
164#[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}