add index to location table
This commit is contained in:
parent
d77396a6ec
commit
b3f9246e99
23
src/font.rs
23
src/font.rs
|
@ -2,6 +2,7 @@ mod table_directory;
|
|||
mod character_map;
|
||||
mod maximum_profile;
|
||||
mod font_header;
|
||||
mod index_to_location;
|
||||
mod search;
|
||||
|
||||
use std::{fs::File, io::{BufReader, Read}};
|
||||
|
@ -9,6 +10,7 @@ use std::{fs::File, io::{BufReader, Read}};
|
|||
use bincode::Options;
|
||||
use character_map::CharacterMap;
|
||||
use font_header::FontHeader;
|
||||
use index_to_location::{IndexToLocation, IndexToLocationArgs};
|
||||
use log::{debug, info};
|
||||
use maximum_profile::MaximumProfile;
|
||||
use table_directory::TableDirectory;
|
||||
|
@ -22,7 +24,8 @@ pub struct Font {
|
|||
table_directory: TableDirectory,
|
||||
font_header: FontHeader,
|
||||
character_map: CharacterMap,
|
||||
maximum_profile: MaximumProfile
|
||||
maximum_profile: MaximumProfile,
|
||||
index_to_location: IndexToLocation
|
||||
}
|
||||
|
||||
impl Font {
|
||||
|
@ -30,20 +33,26 @@ impl Font {
|
|||
let mut buf_reader = BufReader::new(file.try_clone()?);
|
||||
let table_directory = TableDirectory::new(&mut buf_reader)?;
|
||||
|
||||
let font_header = table_directory.get_table(&file)?;
|
||||
let font_header: FontHeader = table_directory.get_table(&file)?;
|
||||
debug!("{font_header:#?}");
|
||||
|
||||
let character_map = table_directory.get_table(&file)?;
|
||||
debug!("{character_map:#?}");
|
||||
|
||||
let maximum_profile = table_directory.get_table(&file)?;
|
||||
let maximum_profile: MaximumProfile = table_directory.get_table(&file)?;
|
||||
debug!("{maximum_profile:#?}");
|
||||
|
||||
let index_to_location = table_directory.get_table_with_args(&file, Some(IndexToLocationArgs {
|
||||
format: font_header.index_to_loc_format,
|
||||
num_glyphs: maximum_profile.num_glyphs()
|
||||
}))?;
|
||||
|
||||
Ok(Font {
|
||||
table_directory,
|
||||
font_header,
|
||||
character_map,
|
||||
maximum_profile
|
||||
maximum_profile,
|
||||
index_to_location
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -54,6 +63,12 @@ impl Font {
|
|||
|
||||
info!("glyph id for char '{glyph}' is {glyph_id}");
|
||||
|
||||
let Some(offset) = self.index_to_location.get_offset(glyph_id) else {
|
||||
return Err("Failed to get glyph offset for char");
|
||||
};
|
||||
|
||||
info!("glyph has offset {offset}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,8 +137,9 @@ impl CharacterMap {
|
|||
|
||||
impl FontTable for CharacterMap {
|
||||
const TAG: &'static str = "cmap";
|
||||
type InitData = ();
|
||||
|
||||
fn decode(mut reader: BufReader<File>, table: &TableDirectoryRecord) -> Result<Self>
|
||||
fn decode(mut reader: BufReader<File>, table: &TableDirectoryRecord, _: Option<Self::InitData>) -> Result<Self>
|
||||
where Self: Sized
|
||||
{
|
||||
reader.seek_relative(2)?; // Skip version because i dont care
|
||||
|
|
|
@ -14,36 +14,37 @@ pub struct Version {
|
|||
#[derive(Debug, Deserialize)]
|
||||
pub struct FontHeader {
|
||||
#[serde(skip)]
|
||||
version: Version,
|
||||
pub version: Version,
|
||||
|
||||
#[serde(skip)]
|
||||
font_revision: Version,
|
||||
pub font_revision: Version,
|
||||
|
||||
checksum_adjust: u32,
|
||||
magic_number: u32,
|
||||
flags: u16,
|
||||
units_per_em: u16,
|
||||
pub flags: u16,
|
||||
pub units_per_em: u16,
|
||||
|
||||
created: i64,
|
||||
modified: i64,
|
||||
pub created: i64,
|
||||
pub modified: i64,
|
||||
|
||||
xmin: i16,
|
||||
ymin: i16,
|
||||
xmax: i16,
|
||||
ymax: i16,
|
||||
pub xmin: i16,
|
||||
pub ymin: i16,
|
||||
pub xmax: i16,
|
||||
pub ymax: i16,
|
||||
|
||||
mac_style: u16,
|
||||
lowest_rec_ppem: u16,
|
||||
pub mac_style: u16,
|
||||
pub lowest_rec_ppem: u16,
|
||||
|
||||
font_direction_hint: i16,
|
||||
index_to_loc_hint: i16,
|
||||
glyph_data_format: i16
|
||||
pub font_direction_hint: i16,
|
||||
pub index_to_loc_format: i16,
|
||||
pub glyph_data_format: i16
|
||||
}
|
||||
|
||||
impl FontTable for FontHeader {
|
||||
const TAG: &'static str = "head";
|
||||
type InitData = ();
|
||||
|
||||
fn decode(reader: BufReader<File>, _: &TableDirectoryRecord) -> bincode::Result<Self>
|
||||
fn decode(reader: BufReader<File>, _: &TableDirectoryRecord, _: Option<Self::InitData>) -> bincode::Result<Self>
|
||||
where Self: Sized
|
||||
{
|
||||
let version = deserialize::<_, Version>(reader.get_ref())?;
|
||||
|
|
95
src/font/index_to_location.rs
Normal file
95
src/font/index_to_location.rs
Normal file
|
@ -0,0 +1,95 @@
|
|||
use std::{collections::HashMap, fs::File, io::{BufReader, Seek, SeekFrom}};
|
||||
|
||||
use log::{debug, error};
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use crate::font::deserialize;
|
||||
|
||||
use super::table_directory::{FontTable, TableDirectoryRecord};
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IndexToLocation {
|
||||
data_source: BufReader<File>,
|
||||
offset: u32,
|
||||
|
||||
num_glyphs: u16,
|
||||
stride: u16,
|
||||
|
||||
cache: HashMap<u16, u32>
|
||||
}
|
||||
|
||||
impl IndexToLocation {
|
||||
fn read_offset_with_stride<T: DeserializeOwned>(&self) -> Option<u32>
|
||||
where u32: From<T>
|
||||
{
|
||||
match deserialize::<_, T>(self.data_source.get_ref()) {
|
||||
Ok(val) => Some(u32::from(val)),
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_offset(&self) -> Option<u32> {
|
||||
match self.stride {
|
||||
2 => self.read_offset_with_stride::<u16>(),
|
||||
4 => self.read_offset_with_stride::<u32>(),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_offset(&mut self, glyph_id: u16) -> Option<u32> {
|
||||
if glyph_id > self.num_glyphs {
|
||||
error!("glyph id {glyph_id} out of range");
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(value) = self.cache.get(&glyph_id) {
|
||||
debug!("Found glyph {glyph_id} in cache");
|
||||
return Some(*value);
|
||||
}
|
||||
|
||||
if let Err(err) = self.data_source.seek(SeekFrom::Start(self.offset as u64 + (glyph_id * self.stride) as u64)) {
|
||||
error!("{err}");
|
||||
return None;
|
||||
}
|
||||
|
||||
debug!("Reading glyph offset from file");
|
||||
|
||||
let offset = self.read_offset()?;
|
||||
self.cache.insert(glyph_id, offset);
|
||||
|
||||
Some(offset)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IndexToLocationArgs {
|
||||
pub format: i16,
|
||||
pub num_glyphs: u16
|
||||
}
|
||||
|
||||
impl FontTable for IndexToLocation {
|
||||
const TAG: &'static str = "loca";
|
||||
type InitData = IndexToLocationArgs;
|
||||
|
||||
fn decode(reader: BufReader<File>, table: &TableDirectoryRecord, args: Option<Self::InitData>) -> bincode::Result<Self>
|
||||
where Self: Sized
|
||||
{
|
||||
let args = args.unwrap();
|
||||
if args.format > 1 {
|
||||
return Err(bincode::ErrorKind::Custom(format!("Invalid index to location format: '{}'", args.format)).into());
|
||||
}
|
||||
|
||||
Ok(IndexToLocation {
|
||||
data_source: reader,
|
||||
offset: table.offset,
|
||||
|
||||
stride: 2 + (args.format as u16) * 2,
|
||||
num_glyphs: args.num_glyphs,
|
||||
|
||||
cache: HashMap::new()
|
||||
})
|
||||
}
|
||||
}
|
|
@ -8,25 +8,25 @@ use super::{deserialize, table_directory::{FontTable, TableDirectoryRecord}};
|
|||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct MaximumProfileV05 {
|
||||
num_glyphs: u16
|
||||
pub 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
|
||||
pub num_glyphs: u16,
|
||||
pub max_points: u16,
|
||||
pub max_countours: u16,
|
||||
pub max_composite_points: u16,
|
||||
pub max_composite_countours: u16,
|
||||
pub max_zones: u16,
|
||||
pub max_twilight_points: u16,
|
||||
pub max_storage: u16,
|
||||
pub max_function_defs: u16,
|
||||
pub max_instruction_defs: u16,
|
||||
pub max_stack_elements: u16,
|
||||
pub max_size_of_instructions: u16,
|
||||
pub max_component_elements: u16,
|
||||
pub max_component_depth: u16
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -35,10 +35,20 @@ pub enum MaximumProfile {
|
|||
V1_0(MaximumProfileV10)
|
||||
}
|
||||
|
||||
impl MaximumProfile {
|
||||
pub fn num_glyphs(&self) -> u16 {
|
||||
match self {
|
||||
Self::V0_5(profile) => profile.num_glyphs,
|
||||
Self::V1_0(profile) => profile.num_glyphs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FontTable for MaximumProfile {
|
||||
const TAG: &'static str = "maxp";
|
||||
type InitData = ();
|
||||
|
||||
fn decode(mut reader: BufReader<File>, _: &TableDirectoryRecord) -> Result<Self>
|
||||
fn decode(mut reader: BufReader<File>, _: &TableDirectoryRecord, _: Option<Self::InitData>) -> Result<Self>
|
||||
where Self: Sized
|
||||
{
|
||||
let version: u32 = deserialize(reader.by_ref())?;
|
||||
|
|
|
@ -38,28 +38,33 @@ impl TableDirectory {
|
|||
}
|
||||
|
||||
pub fn get_table<T: FontTable>(&self, file: &File) -> Result<T> {
|
||||
self.get_table_with_args(file, None)
|
||||
}
|
||||
|
||||
pub fn get_table_with_args<T: FontTable>(&self, file: &File, init_data: Option<T::InitData>) -> Result<T> {
|
||||
let table_id = T::TAG.as_bytes().first_chunk::<4>().unwrap().iter().fold(0u32, |running, &val| {
|
||||
(running << 8) | (val as u32)
|
||||
});
|
||||
|
||||
T::find_and_decode(file, self.0.get(&table_id).ok_or(bincode::ErrorKind::Custom(format!("No table with id '{}' found", T::TAG)))?)
|
||||
T::find_and_decode(file, self.0.get(&table_id).ok_or(bincode::ErrorKind::Custom(format!("No table with id '{}' found", T::TAG)))?, init_data)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub trait FontTable {
|
||||
const TAG: &'static str;
|
||||
type InitData;
|
||||
|
||||
fn decode(reader: BufReader<File>, table: &TableDirectoryRecord) -> Result<Self>
|
||||
fn decode(reader: BufReader<File>, table: &TableDirectoryRecord, init_data: Option<Self::InitData>) -> Result<Self>
|
||||
where Self: Sized;
|
||||
|
||||
fn find_and_decode(file: &File, table: &TableDirectoryRecord) -> Result<Self>
|
||||
fn find_and_decode(file: &File, table: &TableDirectoryRecord, init_data: Option<Self::InitData>) -> 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)
|
||||
Self::decode(reader, table, init_data)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue