From a11df167de1bae0f9e94d2c5050962b3449f0e2a Mon Sep 17 00:00:00 2001 From: lauchmelder Date: Sun, 2 Mar 2025 19:27:05 +0100 Subject: [PATCH] add maxp table decoding --- src/font.rs | 23 +++++------- src/font/character_map.rs | 75 ++++++++++++++++++++----------------- src/font/maximum_profile.rs | 54 ++++++++++++++++++++++++++ src/font/table_directory.rs | 25 +++++++++++-- 4 files changed, 125 insertions(+), 52 deletions(-) create mode 100644 src/font/maximum_profile.rs diff --git a/src/font.rs b/src/font.rs index ae32898..82bb366 100644 --- a/src/font.rs +++ b/src/font.rs @@ -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(reader: R) -> bincode::Result { @@ -15,9 +17,9 @@ fn deserialize(reader: R) -> bincode::R #[derive(Debug)] pub struct Font { - data_source: BufReader, 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"); }; diff --git a/src/font/character_map.rs b/src/font/character_map.rs index 8ce4a69..6b93101 100644 --- a/src/font/character_map.rs +++ b/src/font/character_map.rs @@ -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(reader: &mut R, file: File) -> Result { + fn new(mut reader: BufReader) -> Result { 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 { - 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 { 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, table: &TableDirectoryRecord) -> Result + 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, + + }) + } +} diff --git a/src/font/maximum_profile.rs b/src/font/maximum_profile.rs new file mode 100644 index 0000000..4f2416e --- /dev/null +++ b/src/font/maximum_profile.rs @@ -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, _: &TableDirectoryRecord) -> Result + 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()); } + } + } + +} diff --git a/src/font/table_directory.rs b/src/font/table_directory.rs index 68eb82f..318b060 100644 --- a/src/font/table_directory.rs +++ b/src/font/table_directory.rs @@ -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 { - let table_id = table.as_bytes().first_chunk::<4>().unwrap().iter().fold(0u32, |running, &val| { + pub fn get_table(&self, file: &File) -> Result { + 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, table: &TableDirectoryRecord) -> Result + where Self: Sized; + + fn find_and_decode(file: &File, table: &TableDirectoryRecord) -> Result + 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) + } +}