use crate::parser::{FromData, LazyArray16, Offset, Offset32, Stream};
use crate::{name::PlatformId, GlyphId};
mod format0;
mod format10;
mod format12;
mod format13;
mod format14;
mod format2;
mod format4;
mod format6;
pub use format0::Subtable0;
pub use format10::Subtable10;
pub use format12::Subtable12;
pub use format13::Subtable13;
pub use format14::{GlyphVariationResult, Subtable14};
pub use format2::Subtable2;
pub use format4::Subtable4;
pub use format6::Subtable6;
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug)]
pub enum Format<'a> {
ByteEncodingTable(Subtable0<'a>),
HighByteMappingThroughTable(Subtable2<'a>),
SegmentMappingToDeltaValues(Subtable4<'a>),
TrimmedTableMapping(Subtable6<'a>),
MixedCoverage, TrimmedArray(Subtable10<'a>),
SegmentedCoverage(Subtable12<'a>),
ManyToOneRangeMappings(Subtable13<'a>),
UnicodeVariationSequences(Subtable14<'a>),
}
#[derive(Clone, Copy, Debug)]
pub struct Subtable<'a> {
pub platform_id: PlatformId,
pub encoding_id: u16,
pub format: Format<'a>,
}
impl<'a> Subtable<'a> {
#[inline]
pub fn is_unicode(&self) -> bool {
const WINDOWS_UNICODE_BMP_ENCODING_ID: u16 = 1;
const WINDOWS_UNICODE_FULL_REPERTOIRE_ENCODING_ID: u16 = 10;
match self.platform_id {
PlatformId::Unicode => true,
PlatformId::Windows if self.encoding_id == WINDOWS_UNICODE_BMP_ENCODING_ID => true,
PlatformId::Windows => {
let is_format_12_compatible = matches!(
self.format,
Format::SegmentedCoverage(..) | Format::ManyToOneRangeMappings(..)
);
self.encoding_id == WINDOWS_UNICODE_FULL_REPERTOIRE_ENCODING_ID
&& is_format_12_compatible
}
_ => false,
}
}
#[inline]
pub fn glyph_index(&self, code_point: u32) -> Option<GlyphId> {
match self.format {
Format::ByteEncodingTable(ref subtable) => subtable.glyph_index(code_point),
Format::HighByteMappingThroughTable(ref subtable) => subtable.glyph_index(code_point),
Format::SegmentMappingToDeltaValues(ref subtable) => subtable.glyph_index(code_point),
Format::TrimmedTableMapping(ref subtable) => subtable.glyph_index(code_point),
Format::MixedCoverage => None,
Format::TrimmedArray(ref subtable) => subtable.glyph_index(code_point),
Format::SegmentedCoverage(ref subtable) => subtable.glyph_index(code_point),
Format::ManyToOneRangeMappings(ref subtable) => subtable.glyph_index(code_point),
Format::UnicodeVariationSequences(_) => None,
}
}
#[inline]
pub fn glyph_variation_index(
&self,
code_point: u32,
variation: u32,
) -> Option<GlyphVariationResult> {
match self.format {
Format::UnicodeVariationSequences(ref subtable) => {
subtable.glyph_index(code_point, variation)
}
_ => None,
}
}
pub fn codepoints<F: FnMut(u32)>(&self, f: F) {
match self.format {
Format::ByteEncodingTable(ref subtable) => subtable.codepoints(f),
Format::HighByteMappingThroughTable(ref subtable) => subtable.codepoints(f),
Format::SegmentMappingToDeltaValues(ref subtable) => subtable.codepoints(f),
Format::TrimmedTableMapping(ref subtable) => subtable.codepoints(f),
Format::MixedCoverage => {} Format::TrimmedArray(ref subtable) => subtable.codepoints(f),
Format::SegmentedCoverage(ref subtable) => subtable.codepoints(f),
Format::ManyToOneRangeMappings(ref subtable) => subtable.codepoints(f),
Format::UnicodeVariationSequences(_) => {} };
}
}
#[derive(Clone, Copy)]
struct EncodingRecord {
platform_id: PlatformId,
encoding_id: u16,
offset: Offset32,
}
impl FromData for EncodingRecord {
const SIZE: usize = 8;
#[inline]
fn parse(data: &[u8]) -> Option<Self> {
let mut s = Stream::new(data);
Some(EncodingRecord {
platform_id: s.read::<PlatformId>()?,
encoding_id: s.read::<u16>()?,
offset: s.read::<Offset32>()?,
})
}
}
#[derive(Clone, Copy, Default)]
pub struct Subtables<'a> {
data: &'a [u8],
records: LazyArray16<'a, EncodingRecord>,
}
impl core::fmt::Debug for Subtables<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "Subtables {{ ... }}")
}
}
impl<'a> Subtables<'a> {
pub fn get(&self, index: u16) -> Option<Subtable<'a>> {
let record = self.records.get(index)?;
let data = self.data.get(record.offset.to_usize()..)?;
let format = match Stream::read_at::<u16>(data, 0)? {
0 => Format::ByteEncodingTable(Subtable0::parse(data)?),
2 => Format::HighByteMappingThroughTable(Subtable2::parse(data)?),
4 => Format::SegmentMappingToDeltaValues(Subtable4::parse(data)?),
6 => Format::TrimmedTableMapping(Subtable6::parse(data)?),
8 => Format::MixedCoverage, 10 => Format::TrimmedArray(Subtable10::parse(data)?),
12 => Format::SegmentedCoverage(Subtable12::parse(data)?),
13 => Format::ManyToOneRangeMappings(Subtable13::parse(data)?),
14 => Format::UnicodeVariationSequences(Subtable14::parse(data)?),
_ => return None,
};
Some(Subtable {
platform_id: record.platform_id,
encoding_id: record.encoding_id,
format,
})
}
#[inline]
pub fn len(&self) -> u16 {
self.records.len()
}
pub fn is_empty(&self) -> bool {
self.records.is_empty()
}
}
impl<'a> IntoIterator for Subtables<'a> {
type Item = Subtable<'a>;
type IntoIter = SubtablesIter<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
SubtablesIter {
subtables: self,
index: 0,
}
}
}
#[allow(missing_debug_implementations)]
pub struct SubtablesIter<'a> {
subtables: Subtables<'a>,
index: u16,
}
impl<'a> Iterator for SubtablesIter<'a> {
type Item = Subtable<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.subtables.len() {
self.index += 1;
self.subtables.get(self.index - 1)
} else {
None
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct Table<'a> {
pub subtables: Subtables<'a>,
}
impl<'a> Table<'a> {
pub fn parse(data: &'a [u8]) -> Option<Self> {
let mut s = Stream::new(data);
s.skip::<u16>(); let count = s.read::<u16>()?;
let records = s.read_array16::<EncodingRecord>(count)?;
Some(Table {
subtables: Subtables { data, records },
})
}
}