1use crate::error::ParseResult;
6use crate::reader::{BinaryReader, Parse};
7
8mod post;
9pub use post::PostTable;
10
11mod cmap;
12pub use cmap::*;
13
14mod glyf;
15pub use glyf::*;
16
17mod name;
18pub use name::NameKind;
19pub use name::NameTable;
20
21#[derive(Debug)]
28pub struct TrueTypeFont {
29 pub glyf_table: Vec<GlyfOutline>,
31
32 pub cmap_table: CmapTable,
34
35 pub post_table: PostTable,
37
38 pub name_table: NameTable,
40}
41
42impl TrueTypeFont {
43 pub fn new(font_data: &[u8]) -> ParseResult<Self> {
48 Self::from_data(font_data)
49 }
50}
51
52fn parse_table<T: Parse>(reader: &mut BinaryReader, offset: u32, len: u32) -> ParseResult<T> {
53 let table = reader.read_from(offset as usize, len as usize)?;
54 let mut table_reader = BinaryReader::new(table);
55 T::parse(&mut table_reader)
56}
57
58impl Parse for TrueTypeFont {
59 fn parse(reader: &mut BinaryReader) -> ParseResult<Self> {
60 let mut cmap = None;
61 let mut post = None;
62 let mut name = None;
63
64 reader.skip_u32()?; let num_tables = reader.read_u16()?;
68 reader.skip_u16()?; reader.skip_u16()?; reader.skip_u16()?; let mut loca_is_long = false;
73 let mut glyf_offsets = vec![];
74 let mut glyf_table: Vec<_> = vec![];
75
76 for _ in 0..num_tables {
79 let tag = reader.read_string(4)?;
80 reader.skip_u32()?; let offset = reader.read_u32()?;
82 let length = reader.read_u32()?;
83
84 debug_msg!("Found the {tag} table at {offset} with length {length}");
85
86 match tag.as_str() {
87 "cmap" => {
88 cmap = Some(parse_table(reader, offset, length)?);
89 }
90
91 "post" => {
92 post = Some(parse_table(reader, offset, length)?);
93 }
94
95 "name" => {
96 name = Some(parse_table(reader, offset, length)?);
97 }
98
99 "glyf" => {
100 let table = reader.read_from(offset as usize, length as usize)?;
101 glyf_table = table.to_vec();
102 }
103
104 "head" => {
105 let table = reader.read_from(offset as usize, length as usize)?;
106 let mut table_reader = BinaryReader::new(table);
107
108 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;
122 debug_msg!(" loca is long: {loca_is_long}");
123 }
124
125 "loca" => {
126 let table = reader.read_from(offset as usize, length as usize)?;
127 let mut table_reader = BinaryReader::new(table);
128
129 while !table_reader.is_eof() {
130 let offset = if loca_is_long {
131 table_reader.read_u32()?
132 } else {
133 u32::from(table_reader.read_u16()?) * 2
134 };
135
136 glyf_offsets.push(offset);
137 }
138
139 debug_msg!(" Found {} glyf offsets", glyf_offsets.len());
140 }
141
142 _ => {
143 debug_msg!(" Ignoring table");
144 }
145 }
146 }
147
148 let cmap = cmap.unwrap_or_default();
151 let post = post.unwrap_or_default();
152 let name = name.unwrap_or_default();
153
154 let mut glyphs = vec![];
157 let mut glyf_offsets = glyf_offsets.into_iter().peekable();
158 while let Some(offset) = glyf_offsets.next() {
159 let Some(next_offset) = glyf_offsets.peek().copied().map(|o| o as usize) else {
160 break;
161 };
162
163 let length = next_offset - offset as usize;
164 let data = &glyf_table[offset as usize..next_offset];
165
166 if length > 0 {
167 let mut glyf_reader = BinaryReader::new(data);
168 let glyph = GlyfOutline::parse(&mut glyf_reader)?;
169 glyphs.push(glyph);
170 } else {
171 debug_msg!("No outline for glyph_id {}", glyphs.len());
172 let glyph = GlyfOutline::default();
173 glyphs.push(glyph);
174 }
175 }
176
177 Ok(Self {
178 cmap_table: cmap,
179 post_table: post,
180 glyf_table: glyphs,
181 name_table: name,
182 })
183 }
184}
185
186#[derive(Debug, Clone, Copy, Default)]
188#[repr(u16)]
189pub enum PlatformType {
190 Unicode = 0,
192
193 Macintosh = 1,
195
196 Iso = 2,
198
199 Microsoft = 3,
201
202 #[default]
204 Invalid = 0xFFFF,
205}
206impl From<u16> for PlatformType {
207 fn from(value: u16) -> Self {
208 match value {
209 0 => Self::Unicode,
210 1 => Self::Macintosh,
211 2 => Self::Iso,
212 3 => Self::Microsoft,
213 _ => Self::Invalid,
214 }
215 }
216}