add maxp table decoding
This commit is contained in:
parent
db07f3772b
commit
a11df167de
23
src/font.rs
23
src/font.rs
|
@ -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");
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
54
src/font/maximum_profile.rs
Normal file
54
src/font/maximum_profile.rs
Normal 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()); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue