use crate::{
tables::{Table, TableDirectory, TableError},
types::{Offset32, Tag, tags, uint16, uint32},
util::custom_iterator,
};
use std::mem::ManuallyDrop;
mod codepoint;
mod glyph_id;
pub use codepoint::*;
pub use glyph_id::*;
pub mod format0;
pub mod format2;
pub mod format4;
pub mod format6;
pub mod format8;
pub mod format10;
pub mod format12;
pub mod format13;
pub mod format14;
#[repr(C)]
pub struct CmapV0 {
_exhaustive_but_dont_instantiate: (),
pub version: uint16,
pub num_tables: uint16,
encoding_records: [EncodingRecordRaw; 0],
}
#[repr(C)]
pub struct EncodingRecordRaw {
pub platform_id: uint16,
pub encoding_id: uint16,
pub subtable_offset: Offset32,
}
impl<'a> Table<'a> for Cmap<'a> {
const TAG: Tag = tags::cmap;
fn new_in(dir: &'a TableDirectory) -> Result<Self, TableError> {
let rec = dir.table_record(Self::TAG).ok_or(TableError::NotFound)?;
let v0 = rec.raw_as::<CmapV0>().ok_or(TableError::InvalidLen)?;
let required_len =
size_of::<CmapV0>() + v0.num_tables.get() as usize * size_of::<EncodingRecordRaw>();
if (rec.length.get() as usize) < required_len {
return Err(TableError::InvalidLen);
}
for encoding in v0.encodings() {
let subtable_offset = encoding.subtable_offset.get() as usize;
if (rec.length.get() as usize) < subtable_offset + size_of::<uint16>() {
return Err(TableError::InvalidLen);
}
let subtable = encoding.subtable();
let header_len = match subtable.format() {
0 | 2 | 4 | 6 => size_of::<ShortMeta>(),
8 | 10 | 12 | 13 => size_of::<LongMeta>(),
14 => size_of::<LenOnlyMeta>(),
_ => 2,
};
if (rec.length.get() as usize) < subtable_offset + header_len {
return Err(TableError::InvalidLen);
}
if (rec.length.get() as usize) < subtable_offset + subtable.length().unwrap() as usize {
return Err(TableError::InvalidLen);
}
}
Ok(Self { cmap: v0 })
}
}
#[derive(Copy)]
#[derive_const(Clone)]
pub struct Cmap<'a> {
cmap: &'a CmapV0,
}
const impl<'a> std::ops::Deref for Cmap<'a> {
type Target = &'a CmapV0;
fn deref(&self) -> &Self::Target {
&self.cmap
}
}
impl CmapV0 {
pub const fn encoding_records(&self) -> &[EncodingRecordRaw] {
let len = self.num_tables.get() as usize;
unsafe { std::slice::from_raw_parts(self.encoding_records.as_ptr(), len) }
}
pub const fn encodings(&self) -> EncodingsIter<'_> {
EncodingsIter::new(self)
}
}
#[derive(Copy)]
#[derive_const(Clone)]
pub struct EncodingRecord<'a>(&'a CmapV0, &'a EncodingRecordRaw);
const impl<'a> std::ops::Deref for EncodingRecord<'a> {
type Target = &'a EncodingRecordRaw;
fn deref(&self) -> &Self::Target {
&self.1
}
}
impl<'a> EncodingRecord<'a> {
pub const fn subtable(&self) -> &'a CmapSubtable {
let offset = self.1.subtable_offset.get() as _;
unsafe { &*std::ptr::from_ref(self.0).cast::<u8>().byte_add(offset).cast() }
}
}
pub struct EncodingsIter<'a> {
cmap: &'a CmapV0,
inner: std::slice::Iter<'a, EncodingRecordRaw>,
}
impl<'a> EncodingsIter<'a> {
pub const fn new(cmap: &'a CmapV0) -> Self {
Self { cmap, inner: cmap.encoding_records().iter() }
}
pub fn as_records(&self) -> &'a [EncodingRecordRaw] {
self.inner.as_slice()
}
}
custom_iterator!(EncodingsIter<'a> as this {
type Item = EncodingRecord<'a>;
map: |x| EncodingRecord(this.cmap, x);
});
#[repr(C)]
pub struct CmapSubtable {
meta: SubtableMeta,
}
#[repr(C)]
union SubtableMeta {
short: ManuallyDrop<ShortMeta>,
long: ManuallyDrop<LongMeta>,
len_only: ManuallyDrop<LenOnlyMeta>,
}
#[repr(C)]
struct ShortMeta {
format: uint16,
length: uint16,
language: uint16,
data: [u8; 0],
}
#[repr(C)]
struct LongMeta {
format: uint16,
reserved: uint16,
length: uint32,
language: uint32,
data: [u8; 0],
}
#[repr(C)]
struct LenOnlyMeta {
format: uint16,
length: uint32,
data: [u8; 0],
}
impl CmapSubtable {
pub const fn format(&self) -> u16 {
unsafe { self.meta.short.format.get() }
}
pub const fn length(&self) -> Option<u32> {
Some(match self.format() {
0 | 2 | 4 | 6 => unsafe { self.meta.short.length.get() as _ },
8 | 10 | 12 | 13 => unsafe { self.meta.long.length.get() },
14 => unsafe { self.meta.len_only.length.get() },
_ => return None,
})
}
pub const fn language(&self) -> Option<u32> {
Some(match self.format() {
0 | 2 | 4 | 6 => unsafe { self.meta.short.language.get() as _ },
8 | 10 | 12 | 13 => unsafe { self.meta.long.language.get() },
_ => return None,
})
}
const fn data_ptr(&self) -> Option<&u8> {
Some(match self.format() {
0 | 2 | 4 | 6 => unsafe { &*self.meta.short.data.as_ptr() },
8 | 10 | 12 | 13 => unsafe { &*self.meta.long.data.as_ptr() },
14 => unsafe { &*self.meta.len_only.data.as_ptr() },
_ => return None,
})
}
pub const fn get_as_bytes(&self) -> Option<&[u8]> {
match self.format() {
0 | 2 | 4 | 6 => unsafe {
let size = self.meta.short.length.get() as usize - size_of::<ShortMeta>();
Some(std::slice::from_raw_parts(self.meta.short.data.as_ptr(), size))
},
8 | 10 | 12 | 13 => unsafe {
let size = self.meta.long.length.get() as usize - size_of::<LongMeta>();
Some(std::slice::from_raw_parts(self.meta.long.data.as_ptr(), size))
},
14 => unsafe {
let size = self.meta.len_only.length.get() as usize - size_of::<LenOnlyMeta>();
Some(std::slice::from_raw_parts(self.meta.len_only.data.as_ptr(), size))
},
_ => None,
}
}
pub const fn get_as<T: CmapSubtableTrait>(&self) -> Option<&T> {
if self.format() == T::FORMAT { Some(unsafe { self.get_as_unchecked() }) } else { None }
}
pub const unsafe fn get_as_unchecked<T: CmapSubtableTrait>(&self) -> &T {
debug_assert!(self.format() == T::FORMAT);
unsafe { &*std::ptr::from_ref(self.data_ptr().unwrap()).cast() }
}
pub const fn as_format0(&self) -> Option<&format0::Format0> {
self.get_as()
}
pub const fn as_format2(&self) -> Option<&format2::Format2> {
self.get_as()
}
pub const fn as_format4(&self) -> Option<&format4::Format4> {
self.get_as()
}
pub const fn as_format6(&self) -> Option<&format6::Format6> {
self.get_as()
}
pub const fn as_format8(&self) -> Option<&format8::Format8> {
self.get_as()
}
pub const fn as_format10(&self) -> Option<&format10::Format10> {
self.get_as()
}
pub const fn as_format12(&self) -> Option<&format12::Format12> {
self.get_as()
}
pub const fn as_format13(&self) -> Option<&format13::Format13> {
self.get_as()
}
pub const fn as_format14(&self) -> Option<&format14::Format14> {
self.get_as()
}
}
pub trait CmapSubtableTrait {
const FORMAT: u16;
}