font_map_core/raw/ttf.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
//! This module contains the TTF parser underlying the crate
//!
//! The parser is designed to be fast, and minimal. Supporting only a subset of the TTF spec
//!
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;
/// The raw data from a TrueType font
/// Contains only the subset of the table needed for mapping unicode:
/// - Codepoints
/// - Glyph indices
/// - Glyph names
/// - Glyph outlines
#[derive(Debug)]
pub struct TrueTypeFont {
/// The glyph outlines in the font, indexed by `glyph_id`
pub glyf_table: Vec<GlyfOutline>,
/// The CMAP table of the font
pub cmap_table: CmapTable,
/// The Post table of the font
pub post_table: PostTable,
/// The Name table of the font
pub name_table: NameTable,
}
impl TrueTypeFont {
/// Creates a new TrueType font from the given font data
///
/// # Errors
/// Returns an error if the font data is invalid or cannot be parsed
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;
//
// Offset Table
reader.skip_u32()?; // Scaler type
let num_tables = reader.read_u16()?;
reader.skip_u16()?; // Search range
reader.skip_u16()?; // Entry selector
reader.skip_u16()?; // Range shift
let mut loca_is_long = false;
let mut glyf_offsets = vec![];
let mut glyf_table: Vec<_> = vec![];
//
// Table directory
for _ in 0..num_tables {
let tag = reader.read_string(4)?;
reader.skip_u32()?; // checksum
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()?; // version
table_reader.skip_u32()?; // font_revision
table_reader.skip_u32()?; // checksum_adjustment
table_reader.skip_u32()?; // magic_number
table_reader.skip_u16()?; // flags
table_reader.skip_u16()?; // units_per_em
table_reader.skip_u64()?; // created
table_reader.skip_u64()?; // modified
table_reader.skip_u64()?; // x_min-ymax
table_reader.skip_u16()?; // mac_style
table_reader.skip_u16()?; // lowest_rec_ppem
table_reader.skip_u16()?; // font_direction_hint
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");
}
}
}
//
// Grab completed tables
let cmap = cmap.unwrap_or_default();
let post = post.unwrap_or_default();
let name = name.unwrap_or_default();
//
// Parse glyf table
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,
})
}
}
/// The platform types supported by some tables
#[derive(Debug, Clone, Copy, Default)]
#[repr(u16)]
pub enum PlatformType {
/// Unicode platform
Unicode = 0,
/// Macintosh platform
Macintosh = 1,
/// ISO platform
Iso = 2,
/// Microsoft platform
Microsoft = 3,
/// Invalid platform
#[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,
}
}
}