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 character_map;
mod maximum_profile;
mod search;
use std::{fs::File, io::{BufReader, Read}};
@ -7,6 +8,7 @@ use std::{fs::File, io::{BufReader, Read}};
use bincode::Options;
use character_map::CharacterMap;
use log::{debug, info};
use maximum_profile::MaximumProfile;
use table_directory::TableDirectory;
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)]
pub struct Font {
data_source: BufReader<File>,
table_directory: TableDirectory,
character_map: CharacterMap
character_map: CharacterMap,
maximum_profile: MaximumProfile
}
impl Font {
@ -25,25 +27,20 @@ impl Font {
let mut buf_reader = BufReader::new(file.try_clone()?);
let table_directory = TableDirectory::new(&mut buf_reader)?;
let Some(cmap) = table_directory.get_table("cmap") else {
return Err(Box::new(bincode::ErrorKind::Custom("Missing table 'cmap'".into())));
};
let character_map = CharacterMap::new(file.try_clone()?, cmap)?;
let character_map = table_directory.get_table(&file)?;
debug!("{character_map:#?}");
let maximum_profile = table_directory.get_table(&file)?;
debug!("{maximum_profile:#?}");
Ok(Font {
data_source: buf_reader,
table_directory,
character_map
character_map,
maximum_profile
})
}
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 {
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 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)]
struct SegmentToDeltaHeader {
@ -37,19 +37,21 @@ impl SegmentToDelta {
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 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)?;
let start_code = Self::get_next_vec(reader, search_params.seg_count_x2 >> 1)?;
let id_delta = Self::get_next_vec(reader, search_params.seg_count_x2 >> 1)?;
let id_range_offset = 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(&mut 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 {
data_source: BufReader::new(file),
glyph_table: reader.stream_position()? as u32,
data_source: reader,
glyph_table: glyph_table_pos,
header,
search_params,
end_code,
@ -119,33 +121,6 @@ impl CharacterMap {
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> {
let code = c as u32;
@ -159,3 +134,33 @@ impl CharacterMap {
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 serde::Deserialize;
@ -37,12 +37,29 @@ impl TableDirectory {
Ok(TableDirectory(tables))
}
pub fn get_table(&self, table: &str) -> Option<TableDirectoryRecord> {
let table_id = table.as_bytes().first_chunk::<4>().unwrap().iter().fold(0u32, |running, &val| {
pub fn get_table<T: FontTable>(&self, file: &File) -> Result<T> {
let table_id = T::TAG.as_bytes().first_chunk::<4>().unwrap().iter().fold(0u32, |running, &val| {
(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)
}
}