add maxp table decoding

This commit is contained in:
lauchmelder 2025-03-02 19:27:05 +01:00
parent db07f3772b
commit a11df167de
4 changed files with 125 additions and 52 deletions

View file

@ -1,5 +1,6 @@
mod table_directory; mod table_directory;
mod character_map; mod character_map;
mod maximum_profile;
mod search; mod search;
use std::{fs::File, io::{BufReader, Read}}; use std::{fs::File, io::{BufReader, Read}};
@ -7,6 +8,7 @@ use std::{fs::File, io::{BufReader, Read}};
use bincode::Options; use bincode::Options;
use character_map::CharacterMap; use character_map::CharacterMap;
use log::{debug, info}; use log::{debug, info};
use maximum_profile::MaximumProfile;
use table_directory::TableDirectory; use table_directory::TableDirectory;
fn deserialize<R: Read, T: serde::de::DeserializeOwned>(reader: R) -> bincode::Result<T> { fn deserialize<R: Read, T: serde::de::DeserializeOwned>(reader: R) -> bincode::Result<T> {
@ -15,9 +17,9 @@ fn deserialize<R: Read, T: serde::de::DeserializeOwned>(reader: R) -> bincode::R
#[derive(Debug)] #[derive(Debug)]
pub struct Font { pub struct Font {
data_source: BufReader<File>,
table_directory: TableDirectory, table_directory: TableDirectory,
character_map: CharacterMap character_map: CharacterMap,
maximum_profile: MaximumProfile
} }
impl Font { impl Font {
@ -25,25 +27,20 @@ impl Font {
let mut buf_reader = BufReader::new(file.try_clone()?); let mut buf_reader = BufReader::new(file.try_clone()?);
let table_directory = TableDirectory::new(&mut buf_reader)?; let table_directory = TableDirectory::new(&mut buf_reader)?;
let Some(cmap) = table_directory.get_table("cmap") else { let character_map = table_directory.get_table(&file)?;
return Err(Box::new(bincode::ErrorKind::Custom("Missing table 'cmap'".into())));
};
let character_map = CharacterMap::new(file.try_clone()?, cmap)?;
debug!("{character_map:#?}"); debug!("{character_map:#?}");
let maximum_profile = table_directory.get_table(&file)?;
debug!("{maximum_profile:#?}");
Ok(Font { Ok(Font {
data_source: buf_reader,
table_directory, table_directory,
character_map character_map,
maximum_profile
}) })
} }
pub fn get_data_for(&mut self, glyph: char) -> Result<(), &'static str> { pub fn get_data_for(&mut self, glyph: char) -> Result<(), &'static str> {
let Some(table) = self.table_directory.get_table("glyf") else {
return Err("Failed to get table. It doesn't exist");
};
let Some(glyph_id) = self.character_map.get_char(glyph) else { let Some(glyph_id) = self.character_map.get_char(glyph) else {
return Err("Failed to get glyph id for char"); return Err("Failed to get glyph id for char");
}; };

View file

@ -4,7 +4,7 @@ use bincode::{ErrorKind, Result};
use log::{debug, error, warn}; use log::{debug, error, warn};
use serde::{de::DeserializeOwned, Deserialize}; use serde::{de::DeserializeOwned, Deserialize};
use super::{deserialize, table_directory::TableDirectoryRecord, search::SearchParameters}; use super::{deserialize, search::SearchParameters, table_directory::{FontTable, TableDirectoryRecord}};
#[derive(Debug, Deserialize, Copy, Clone)] #[derive(Debug, Deserialize, Copy, Clone)]
struct SegmentToDeltaHeader { struct SegmentToDeltaHeader {
@ -37,19 +37,21 @@ impl SegmentToDelta {
Ok(values) Ok(values)
} }
fn new<R: Read + Seek>(reader: &mut R, file: File) -> Result<SegmentToDelta> { fn new(mut reader: BufReader<File>) -> Result<SegmentToDelta> {
let header: SegmentToDeltaHeader = deserialize(reader.by_ref())?; let header: SegmentToDeltaHeader = deserialize(reader.by_ref())?;
let search_params: SearchParameters = deserialize(reader.by_ref())?; let search_params: SearchParameters = deserialize(reader.by_ref())?;
let end_code = Self::get_next_vec(reader, search_params.seg_count_x2 >> 1)?; let end_code = Self::get_next_vec(&mut reader, search_params.seg_count_x2 >> 1)?;
reader.seek_relative(2)?; reader.seek_relative(2)?;
let start_code = Self::get_next_vec(reader, search_params.seg_count_x2 >> 1)?; let start_code = Self::get_next_vec(&mut reader, search_params.seg_count_x2 >> 1)?;
let id_delta = Self::get_next_vec(reader, search_params.seg_count_x2 >> 1)?; let id_delta = Self::get_next_vec(&mut reader, search_params.seg_count_x2 >> 1)?;
let id_range_offset = Self::get_next_vec(reader, search_params.seg_count_x2 >> 1)?; let id_range_offset = Self::get_next_vec(&mut reader, search_params.seg_count_x2 >> 1)?;
let glyph_table_pos = reader.stream_position()? as u32;
Ok(SegmentToDelta { Ok(SegmentToDelta {
data_source: BufReader::new(file), data_source: reader,
glyph_table: reader.stream_position()? as u32, glyph_table: glyph_table_pos,
header, header,
search_params, search_params,
end_code, end_code,
@ -119,33 +121,6 @@ impl CharacterMap {
None None
} }
pub fn new(file: File, cmap: TableDirectoryRecord) -> Result<CharacterMap> {
let mut reader = BufReader::new(file.try_clone()?);
reader.seek(SeekFrom::Start(cmap.offset as u64))?;
reader.seek_relative(2)?; // Skip version because i dont care
let num_tables: u16 = deserialize(reader.by_ref())?;
let Some(table) = Self::find_unicode_table(&mut reader, num_tables) else {
return Err(Box::new(ErrorKind::Custom("No encoding for Unicode 2.0 BMP".into())));
};
reader.seek(SeekFrom::Start((cmap.offset + table.offset) as u64))?;
let format: u16 = deserialize(reader.by_ref())?;
if format != 4 {
todo!();
}
Ok(CharacterMap {
mapping_table: SegmentToDelta::new(reader.by_ref(), file)?,
_table_start: cmap.offset,
_length: cmap.length,
})
}
pub fn get_char(&mut self, c: char) -> Option<u16> { pub fn get_char(&mut self, c: char) -> Option<u16> {
let code = c as u32; let code = c as u32;
@ -159,3 +134,33 @@ impl CharacterMap {
return self.mapping_table.get_glyph_id(code); return self.mapping_table.get_glyph_id(code);
} }
} }
impl FontTable for CharacterMap {
const TAG: &'static str = "cmap";
fn decode(mut reader: BufReader<File>, table: &TableDirectoryRecord) -> Result<Self>
where Self: Sized
{
reader.seek_relative(2)?; // Skip version because i dont care
let num_tables: u16 = deserialize(reader.by_ref())?;
let Some(record) = Self::find_unicode_table(&mut reader, num_tables) else {
return Err(Box::new(ErrorKind::Custom("No encoding for Unicode 2.0 BMP".into())));
};
reader.seek(SeekFrom::Start((table.offset + record.offset) as u64))?;
let format: u16 = deserialize(reader.by_ref())?;
if format != 4 {
todo!();
}
Ok(CharacterMap {
mapping_table: SegmentToDelta::new(reader)?,
_table_start: table.offset,
_length: table.length,
})
}
}

View file

@ -0,0 +1,54 @@
use std::{fs::File, io::{BufReader, Read, Seek, SeekFrom}};
use bincode::{de::read, Result};
use log::debug;
use serde::Deserialize;
use super::{deserialize, table_directory::{FontTable, TableDirectoryRecord}};
#[derive(Debug, Deserialize)]
pub struct MaximumProfileV05 {
num_glyphs: u16
}
#[derive(Debug, Deserialize)]
pub struct MaximumProfileV10 {
num_glyphs: u16,
max_points: u16,
max_countours: u16,
max_composite_points: u16,
max_composite_countours: u16,
max_zones: u16,
max_twilight_points: u16,
max_storage: u16,
max_function_defs: u16,
max_instruction_defs: u16,
max_stack_elements: u16,
max_size_of_instructions: u16,
max_component_elements: u16,
max_component_depth: u16
}
#[derive(Debug)]
pub enum MaximumProfile {
V0_5(MaximumProfileV05),
V1_0(MaximumProfileV10)
}
impl FontTable for MaximumProfile {
const TAG: &'static str = "maxp";
fn decode(mut reader: BufReader<File>, _: &TableDirectoryRecord) -> Result<Self>
where Self: Sized
{
let version: u32 = deserialize(reader.by_ref())?;
debug!("maxp table version: {:#08x}", version);
match version {
0x00005000 => todo!(),
0x00010000 => Ok(MaximumProfile::V1_0(deserialize(reader.by_ref())?)),
_ => { return Err(bincode::ErrorKind::Custom("Invalid maxp table version".into()).into()); }
}
}
}

View file

@ -1,4 +1,4 @@
use std::{collections::HashMap, io::{Read, Seek}}; use std::{collections::HashMap, fs::File, io::{BufReader, Read, Seek, SeekFrom}};
use bincode::Result; use bincode::Result;
use serde::Deserialize; use serde::Deserialize;
@ -37,12 +37,29 @@ impl TableDirectory {
Ok(TableDirectory(tables)) Ok(TableDirectory(tables))
} }
pub fn get_table(&self, table: &str) -> Option<TableDirectoryRecord> { pub fn get_table<T: FontTable>(&self, file: &File) -> Result<T> {
let table_id = table.as_bytes().first_chunk::<4>().unwrap().iter().fold(0u32, |running, &val| { let table_id = T::TAG.as_bytes().first_chunk::<4>().unwrap().iter().fold(0u32, |running, &val| {
(running << 8) | (val as u32) (running << 8) | (val as u32)
}); });
self.0.get(&table_id).copied() T::find_and_decode(file, self.0.get(&table_id).ok_or(bincode::ErrorKind::Custom(format!("No table with id '{}' found", T::TAG)))?)
} }
} }
pub trait FontTable {
const TAG: &'static str;
fn decode(reader: BufReader<File>, table: &TableDirectoryRecord) -> Result<Self>
where Self: Sized;
fn find_and_decode(file: &File, table: &TableDirectoryRecord) -> Result<Self>
where Self: Sized
{
let file = file.try_clone()?;
let mut reader = BufReader::new(file);
reader.seek(SeekFrom::Start(table.offset as u64))?;
Self::decode(reader, table)
}
}