finish glyph parsing

This commit is contained in:
lauchmelder 2025-03-03 17:21:55 +01:00
parent c994f3ec02
commit 7cbb264410
4 changed files with 133 additions and 33 deletions

View file

@ -51,12 +51,13 @@ impl Font {
pub fn new(file: File) -> bincode::Result<Self> {
let mut buf_reader = BufReader::new(file.try_clone()?);
let table_directory = TableDirectory::new(&mut buf_reader)?;
debug!("{table_directory:#?}");
let font_header: FontHeader = table_directory.get_table(&file)?;
debug!("{font_header:#?}");
let character_map = table_directory.get_table(&file)?;
debug!("{character_map:#?}");
// debug!("{character_map:#?}");
let maximum_profile: MaximumProfile = table_directory.get_table(&file)?;
debug!("{maximum_profile:#?}");

View file

@ -1,8 +1,8 @@
use std::{collections::HashMap, fs::File, io::{BufReader, Read, Seek, SeekFrom}, iter::zip, slice::Iter};
use std::{collections::HashMap, fs::File, io::{BufReader, Read, Seek, SeekFrom}, iter::{zip, Peekable}, slice::Iter};
use bincode::{ErrorKind, Result};
use bitfield::bitfield;
use log::{debug, info};
use log::{debug, info, warn};
use serde::Deserialize;
use super::{deserialize, deserialize_vec, index_to_location::IndexToLocation, table_directory::{FontTable, TableDirectoryRecord}};
@ -56,35 +56,37 @@ impl<'a> Iterator for GlyphFlags<'a> {
fn next(&mut self) -> Option<Self::Item> {
if self.current_item >= self.flags.len() {
debug!("no more logical flags");
return None;
}
let current = &self.flags[self.current_item];
if self.repetitions < current.repeat as u16 {
if self.repetitions <= current.repeat as u16 {
if self.repetitions > 0 {
debug!("flag #{} repeated {} times", self.current_item, self.repetitions);
} else {
debug!("yield flag #{}", self.current_item);
}
self.repetitions += 1;
return Some(current.flags);
}
self.current_item += 1;
if self.current_item >= self.flags.len() {
return None;
}
let current = &self.flags[self.current_item];
self.repetitions = 0;
Some(current.flags)
self.current_item += 1;
self.next()
}
}
#[derive(Debug)]
#[derive(Debug, Copy, Clone)]
pub struct GlyphPoint {
pub x: i32,
pub y: i32,
pub on_curve: bool
pub on_curve: bool,
pub is_virtual: bool,
pub is_endpoint: bool,
}
#[derive(Debug)]
@ -100,7 +102,7 @@ impl SimpleGlyph {
let mut flags: Vec<GlyphFlag> = vec![];
let mut points_processed: usize = 0;
while points_processed < num_points {
while points_processed <= num_points {
let flag: GlyphFlagData = deserialize(reader.by_ref())?;
let repetitions: u8 = if flag.repeat() { deserialize(reader.by_ref())? } else { 0 };
@ -118,28 +120,39 @@ impl SimpleGlyph {
}
fn read_coordinates<R: Read + Seek>(reader: &mut R, flags: GlyphFlags, is_y: bool) -> Result<Vec<(i32, bool)>> {
debug!("reading batch of {} coordinates at {}", if is_y { "y" } else { "x" }, reader.stream_position()?);
let mut coordinates: Vec<(i32, bool)> = vec![];
let mut current_coordinate = 0i32;
for flag in flags {
debug!("reading next coordinate");
let short_vec = if is_y { flag.y_short() } else { flag.x_short() };
let same_or_positive = if is_y { flag.y_same_or_positive() } else { flag.x_same_or_positive() };
debug!("short vec: {short_vec}, same or positive: {same_or_positive}");
if short_vec {
let sign: i32 = if same_or_positive { 1 } else { -1 };
let delta = sign * (deserialize::<_, u8>(reader.by_ref())? as i32);
debug!("delta = (u8){delta}");
current_coordinate += delta;
coordinates.push((current_coordinate, flag.on_curve()));
} else {
if same_or_positive {
debug!("coordinate is repeated");
coordinates.push((current_coordinate, flag.on_curve()));
} else {
let delta: i16 = deserialize(reader.by_ref())?;
debug!("delta = (i16){delta}");
current_coordinate += delta as i32;
coordinates.push((current_coordinate, flag.on_curve()));
}
}
debug!("current coordinate is now {current_coordinate}");
}
Ok(coordinates)
@ -158,11 +171,24 @@ impl SimpleGlyph {
let x_coords = SimpleGlyph::read_coordinates(reader, GlyphFlags::new(&flags), false)?;
let y_coords = SimpleGlyph::read_coordinates(reader, GlyphFlags::new(&flags), true)?;
if x_coords.len() != *end_points.last().unwrap() as usize + 1 {
warn!("expected {} x coordinates, got {} instead", end_points.last().unwrap() + 1, x_coords.len());
}
if y_coords.len() != *end_points.last().unwrap() as usize + 1 {
warn!("expected {} y coordinates, got {} instead", end_points.last().unwrap() + 1, y_coords.len());
}
let mut current_point: u16 = 0;
let coordinates = zip(x_coords.iter().cloned(), y_coords.iter().cloned())
.map(|(x, y)| {
GlyphPoint {
x: x.0, y: y.0, on_curve: x.1
}
let point = GlyphPoint {
x: x.0, y: y.0, on_curve: x.1, is_endpoint: end_points.contains(&current_point), is_virtual: false
};
current_point += 1;
point
});
// coordinates.for_each(|point| debug!("({}, {}, {})", point.x, point.y, if point.on_curve { "on" } else { "off" }));
@ -173,21 +199,44 @@ impl SimpleGlyph {
pub fn iter<'a>(&'a self) -> SimpleGlyphIter<'a> {
SimpleGlyphIter {
glyph: self,
iter: self.points.iter()
last_point: None,
iter: self.points.iter().peekable()
}
}
}
struct SimpleGlyphIter<'a> {
glyph: &'a SimpleGlyph,
iter: Iter<'a, GlyphPoint>
last_point: Option<GlyphPoint>,
iter: Peekable<Iter<'a, GlyphPoint>>
}
impl<'a> Iterator for SimpleGlyphIter<'a> {
type Item = &'a GlyphPoint;
type Item = GlyphPoint;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
if let Some(last_point) = self.last_point {
let Some(&next_point) = self.iter.peek() else {
return None
};
if !last_point.on_curve && !next_point.on_curve {
// Interpolate a point
let virtual_point = GlyphPoint {
x: last_point.x + (next_point.x - last_point.x) / 2,
y: last_point.y + (next_point.y - last_point.y) / 2,
is_endpoint: false,
is_virtual: true,
on_curve: true
};
self.last_point = Some(virtual_point);
return self.last_point;
}
}
self.last_point = self.iter.next().copied();
self.last_point
}
}
@ -204,7 +253,7 @@ pub struct Glyph {
}
impl Glyph {
pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a GlyphPoint> + 'a> {
pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = GlyphPoint> + 'a> {
Box::new(
match &self.data {
GlyphData::Simple(glyph) => glyph.iter(),
@ -236,14 +285,28 @@ impl GlyphTable {
let header: GlyphHeader = deserialize(self.data_source.get_ref())?;
debug!("glyph header: {header:#?}");
if header.num_countours >= 0 {
let result = if header.num_countours >= 0 {
Ok(Glyph {
data: GlyphData::Simple(SimpleGlyph::new(&header, self.data_source.by_ref())?),
header
})
} else {
todo!();
};
// sanity check
let stream_pos = self.data_source.stream_position()?;
let Some(next_offset) = location.get_offset(glyph_id + 1) else {
warn!("Failed to perform sanity check after glyph parsing");
return result;
};
let remaining_bytes = stream_pos as i64 - (self.offset + next_offset) as i64;
if remaining_bytes != 0 {
warn!("sanity check failed, there are still {remaining_bytes} bytes remaining in this glyph entry (read head: {}, table off: {}, location: [{offset}, {next_offset}])", stream_pos, self.offset);
}
result
}
}

View file

@ -11,8 +11,8 @@ pub trait Visitor {
fn write(&mut self, glyph: &Glyph) {
self.write_prefix(&glyph.header);
glyph.iter().for_each(|point| self.write_point(point));
glyph.iter().for_each(|point| self.write_point(&point));
self.write_suffix();
}

View file

@ -6,6 +6,9 @@ use super::Visitor;
pub struct SvgWriter {
first_point: bool,
last_on_curve: bool,
final_point: Option<(i32, i32)>,
final_control: Option<(i32, i32)>,
writer: BufWriter<File>
}
@ -13,6 +16,9 @@ impl SvgWriter {
pub fn new(file: File) -> Self {
SvgWriter {
first_point: true,
last_on_curve: false,
final_control: None,
final_point: None,
writer: BufWriter::new(file)
}
}
@ -23,26 +29,56 @@ impl Visitor for SvgWriter {
write!(
self.writer,
"<svg width=\"1000\" height=\"1000\" viewBox=\"{} {} {} {}\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"",
header.xmin, header.ymin,
header.xmin, -header.ymax,
header.xmax - header.xmin,
header.ymax - header.ymin
);
}
fn write_point(&mut self, point: &GlyphPoint) {
if !point.on_curve {
return
if self.final_point.is_none() && point.on_curve {
self.final_point = Some((point.x, point.y));
}
let prefix = if self.first_point { "M" } else { "L" };
if self.first_point && self.final_control.is_none() && !point.on_curve {
self.final_control = Some((point.x, point.y));
}
if self.first_point {
if !point.on_curve {
return;
}
write!(self.writer, "M{} {} ", point.x, -point.y);
self.first_point = false;
self.last_on_curve = true;
return;
}
let prefix = if point.on_curve { if self.last_on_curve { "L" } else { "" } } else { "Q" };
self.last_on_curve = point.on_curve;
write!(
self.writer,
"{prefix}{} {} ",
point.x, point.y
point.x, -point.y
);
self.first_point = false;
if point.is_endpoint {
if let Some(final_control) = self.final_control {
if let Some(final_point) = self.final_point {
write!(self.writer, "Q{} {} {} {} ", final_control.0, -final_control.1, final_point.0, -final_point.1);
} else {
write!(self.writer, "Z ");
}
} else {
write!(self.writer, "Z ");
}
self.final_point = None;
self.final_control = None;
}
self.first_point = point.is_endpoint;
}
fn write_suffix(&mut self) {