use crate::error::ParseResult;
use crate::reader::{BinaryReader, Parse};
mod post;
pub use post::PostTable;
mod cmap;
pub use cmap::*;
mod glyf;
pub use glyf::*;
mod name;
pub use name::NameKind;
pub use name::NameTable;
#[derive(Debug)]
pub struct TrueTypeFont {
pub glyf_table: Vec<GlyfOutline>,
pub cmap_table: CmapTable,
pub post_table: PostTable,
pub name_table: NameTable,
}
impl TrueTypeFont {
pub fn new(font_data: &[u8]) -> ParseResult<Self> {
Self::from_data(font_data)
}
}
fn parse_table<T: Parse>(reader: &mut BinaryReader, offset: u32, len: u32) -> ParseResult<T> {
let table = reader.read_from(offset as usize, len as usize)?;
let mut table_reader = BinaryReader::new(table);
T::parse(&mut table_reader)
}
impl Parse for TrueTypeFont {
fn parse(reader: &mut BinaryReader) -> ParseResult<Self> {
let mut cmap = None;
let mut post = None;
let mut name = None;
reader.skip_u32()?; let num_tables = reader.read_u16()?;
reader.skip_u16()?; reader.skip_u16()?; reader.skip_u16()?;
let mut loca_is_long = false;
let mut glyf_offsets = vec![];
let mut glyf_table: Vec<_> = vec![];
for _ in 0..num_tables {
let tag = reader.read_string(4)?;
reader.skip_u32()?; let offset = reader.read_u32()?;
let length = reader.read_u32()?;
debug_msg!("Found the {tag} table at {offset} with length {length}");
match tag.as_str() {
"cmap" => {
cmap = Some(parse_table(reader, offset, length)?);
}
"post" => {
post = Some(parse_table(reader, offset, length)?);
}
"name" => {
name = Some(parse_table(reader, offset, length)?);
}
"glyf" => {
let table = reader.read_from(offset as usize, length as usize)?;
glyf_table = table.to_vec();
}
"head" => {
let table = reader.read_from(offset as usize, length as usize)?;
let mut table_reader = BinaryReader::new(table);
table_reader.skip_u32()?; table_reader.skip_u32()?; table_reader.skip_u32()?; table_reader.skip_u32()?; table_reader.skip_u16()?; table_reader.skip_u16()?; table_reader.skip_u64()?; table_reader.skip_u64()?; table_reader.skip_u64()?; table_reader.skip_u16()?; table_reader.skip_u16()?; table_reader.skip_u16()?;
loca_is_long = table_reader.read_u16()? != 0;
debug_msg!(" loca is long: {loca_is_long}");
}
"loca" => {
let table = reader.read_from(offset as usize, length as usize)?;
let mut table_reader = BinaryReader::new(table);
while !table_reader.is_eof() {
let offset = if loca_is_long {
table_reader.read_u32()?
} else {
u32::from(table_reader.read_u16()?) * 2
};
glyf_offsets.push(offset);
}
debug_msg!(" Found {} glyf offsets", glyf_offsets.len());
}
_ => {
debug_msg!(" Ignoring table");
}
}
}
let cmap = cmap.unwrap_or_default();
let post = post.unwrap_or_default();
let name = name.unwrap_or_default();
let mut glyphs = vec![];
let mut glyf_offsets = glyf_offsets.into_iter().peekable();
while let Some(offset) = glyf_offsets.next() {
let Some(next_offset) = glyf_offsets.peek().copied().map(|o| o as usize) else {
break;
};
let length = next_offset - offset as usize;
let data = &glyf_table[offset as usize..next_offset];
if length > 0 {
let mut glyf_reader = BinaryReader::new(data);
let glyph = GlyfOutline::parse(&mut glyf_reader)?;
glyphs.push(glyph);
} else {
debug_msg!("No outline for glyph_id {}", glyphs.len());
let glyph = GlyfOutline::default();
glyphs.push(glyph);
}
}
Ok(Self {
cmap_table: cmap,
post_table: post,
glyf_table: glyphs,
name_table: name,
})
}
}
#[derive(Debug, Clone, Copy, Default)]
#[repr(u16)]
pub enum PlatformType {
Unicode = 0,
Macintosh = 1,
Iso = 2,
Microsoft = 3,
#[default]
Invalid = 0xFFFF,
}
impl From<u16> for PlatformType {
fn from(value: u16) -> Self {
match value {
0 => Self::Unicode,
1 => Self::Macintosh,
2 => Self::Iso,
3 => Self::Microsoft,
_ => Self::Invalid,
}
}
}