refine spline parsing

This commit is contained in:
lauchmelder 2025-03-03 23:48:25 +01:00
parent e56b950cb2
commit 4ba1abe786
4 changed files with 180 additions and 52 deletions

View file

@ -17,7 +17,7 @@ use log::{debug, info};
use maximum_profile::MaximumProfile; use maximum_profile::MaximumProfile;
use table_directory::TableDirectory; use table_directory::TableDirectory;
pub use glyph_data::{GlyphData, GlyphHeader, GlyphPoint, Glyph, SimpleGlyph}; pub use glyph_data::{GlyphData, GlyphHeader, GlyphPoint, Glyph, SimpleGlyph, SplineElement};
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> {
bincode::options().with_big_endian().with_fixint_encoding().deserialize_from::<R, T>(reader) bincode::options().with_big_endian().with_fixint_encoding().deserialize_from::<R, T>(reader)

View file

@ -2,7 +2,7 @@ use std::{collections::HashMap, fs::File, io::{BufReader, Read, Seek, SeekFrom},
use bincode::{ErrorKind, Result}; use bincode::{ErrorKind, Result};
use bitfield::bitfield; use bitfield::bitfield;
use log::{debug, info, warn}; use log::{debug, error, info, warn};
use serde::Deserialize; use serde::Deserialize;
use super::{deserialize, deserialize_vec, index_to_location::IndexToLocation, table_directory::{FontTable, TableDirectoryRecord}}; use super::{deserialize, deserialize_vec, index_to_location::IndexToLocation, table_directory::{FontTable, TableDirectoryRecord}};
@ -252,6 +252,108 @@ pub struct Glyph {
pub data: GlyphData pub data: GlyphData
} }
#[derive(Debug)]
pub enum SplineElement {
Line(GlyphPoint, GlyphPoint),
Bezier(GlyphPoint, GlyphPoint, GlyphPoint)
}
pub struct SplineIter<'a> {
points: Box<dyn Iterator<Item = GlyphPoint> + 'a>,
last_point: Option<GlyphPoint>,
final_control: Option<GlyphPoint>,
first_point: Option<GlyphPoint>
}
impl<'a> SplineIter<'a> {
fn handle_spline_start(&mut self) -> Result<()> {
let Some(point) = self.points.next() else {
return Err(ErrorKind::Custom("Not enough points in contous to form a spline".into()).into());
};
if point.on_curve {
self.last_point = Some(point);
self.first_point = Some(point);
return Ok(());
} else {
self.final_control = Some(point);
}
let Some(point) = self.points.next() else {
return Err(ErrorKind::Custom("Not enough points in contous to form a spline".into()).into());
};
if !point.on_curve {
return Err(ErrorKind::Custom("Iterator returned invalid point sequence".into()).into());
}
self.last_point = Some(point);
self.first_point = Some(point);
Ok(())
}
fn handle_spline_close(&mut self) -> Option<SplineElement> {
let Some(last_point) = self.last_point else {
return None;
};
if let Some(final_control) = self.final_control {
Some(SplineElement::Bezier(last_point, final_control, self.first_point.unwrap()))
} else {
Some(SplineElement::Line(last_point, self.first_point.unwrap()))
}
}
}
impl<'a> Iterator for SplineIter<'a> {
type Item = SplineElement;
fn next(&mut self) -> Option<Self::Item> {
let Some(last_point) = self.last_point else {
debug!("spline iter: last point is None");
return None;
};
if last_point.is_endpoint {
debug!("spline iter: point is an endpoint of contour. closing spline, starting new one (if possible)");
let result = self.handle_spline_close();
if let Err(err) = self.handle_spline_start() {
debug!("spline iter: no more contours");
self.last_point = None;
}
return result;
}
let Some(point) = self.points.next() else {
self.last_point = None;
debug!("spline iter: no more points, closing spline");
return self.handle_spline_close();
};
if point.on_curve {
self.last_point = Some(point);
debug!("spline iter: point is on curve");
return Some(SplineElement::Line(last_point, point));
}
debug!("spline iter: point is control point");
let Some(next_point) = self.points.next() else {
debug!("spline iter: no more data after control point, closing spline");
self.last_point = None;
return self.handle_spline_close();
};
debug!("spline iter: returning bezier");
self.last_point = Some(next_point);
Some(SplineElement::Bezier(last_point, point, next_point))
}
}
impl Glyph { impl Glyph {
pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = GlyphPoint> + 'a> { pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = GlyphPoint> + 'a> {
Box::new( Box::new(
@ -261,6 +363,19 @@ impl Glyph {
} }
) )
} }
pub fn splines<'a>(&'a self) -> Result<SplineIter<'a>> {
let mut splines = SplineIter {
points: self.iter(),
last_point: None,
final_control: None,
first_point: None
};
splines.handle_spline_start()?;
Ok(splines)
}
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -1,18 +1,22 @@
mod svg; mod svg;
use crate::font::{GlyphHeader, GlyphPoint, Glyph}; use crate::font::{Glyph, GlyphHeader, GlyphPoint, SplineElement};
use log::debug;
pub use svg::SvgWriter; pub use svg::SvgWriter;
pub trait Visitor { pub trait Visitor {
fn write_prefix(&mut self, header: &GlyphHeader); fn write_prefix(&mut self, header: &GlyphHeader);
fn write_point(&mut self, point: &GlyphPoint); fn write_point(&mut self, point: &SplineElement);
fn write_suffix(&mut self); fn write_suffix(&mut self);
fn write(&mut self, glyph: &Glyph) { fn write(&mut self, glyph: &Glyph) {
self.write_prefix(&glyph.header); self.write_prefix(&glyph.header);
glyph.iter().for_each(|point| self.write_point(&point)); glyph.splines().unwrap().for_each(|spline_element| {
debug!("{spline_element:#?}");
self.write_point(&spline_element)
});
self.write_suffix(); self.write_suffix();
} }

View file

@ -1,25 +1,48 @@
use std::{fs::File, io::{BufWriter, Write}}; use std::{fs::File, io::{BufWriter, Write}};
use crate::font::{GlyphHeader, GlyphPoint}; use crate::font::{GlyphHeader, GlyphPoint, SplineElement};
use super::Visitor; use super::Visitor;
pub struct SvgWriter { pub struct SvgWriter {
first_point: bool, first_point: bool,
last_on_curve: bool,
final_point: Option<(i32, i32)>, writer: BufWriter<File>,
final_control: Option<(i32, i32)>, points: Vec<(i32, i32)>,
writer: BufWriter<File> control_points: Vec<(i32, i32)>,
virtual_points: Vec<(i32, i32)>
} }
impl SvgWriter { impl SvgWriter {
pub fn new(file: File) -> Self { pub fn new(file: File) -> Self {
SvgWriter { SvgWriter {
first_point: true, first_point: true,
last_on_curve: false,
final_control: None, writer: BufWriter::new(file),
final_point: None, points: vec![],
writer: BufWriter::new(file) control_points: vec![],
virtual_points: vec![]
}
}
}
impl SvgWriter {
fn handle_start_point(&mut self, point: &GlyphPoint) {
if self.first_point {
write!(self.writer, "M{} {} ", point.x, -point.y);
self.first_point = false;
}
}
fn handle_end_point(&mut self, point: &GlyphPoint) {
if point.is_endpoint {
self.first_point = true;
}
}
fn write_points(writer: &mut BufWriter<File>, points: &Vec<(i32, i32)>, color: &'static str) {
for (x, y) in points {
write!(writer, "<circle cx=\"{x}\" cy=\"{y}\" r=\"10\" fill=\"{color}\" />");
} }
} }
} }
@ -35,56 +58,42 @@ impl Visitor for SvgWriter {
); );
} }
fn write_point(&mut self, point: &GlyphPoint) { fn write_point(&mut self, point: &SplineElement) {
if self.final_point.is_none() && point.on_curve { match point {
self.final_point = Some((point.x, point.y)); SplineElement::Line(start, end) => {
} self.handle_start_point(start);
write!(self.writer, "L{} {} ", end.x, -end.y);
if self.first_point && self.final_control.is_none() && !point.on_curve { self.points.push((start.x, -start.y));
self.final_control = Some((point.x, point.y)); self.handle_end_point(start);
} },
if self.first_point { SplineElement::Bezier(start, control, end) => {
if !point.on_curve { self.handle_start_point(start);
return; write!(self.writer, "Q{} {} {} {} ", control.x, -control.y, end.x, -end.y);
}
write!(self.writer, "M{} {} ", point.x, -point.y); if start.is_virtual {
self.first_point = false; self.virtual_points.push((start.x, -start.y));
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
);
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 { } else {
write!(self.writer, "Z "); self.points.push((start.x, -start.y));
} }
} else {
write!(self.writer, "Z ");
}
self.final_point = None; self.control_points.push((control.x, -control.y));
self.final_control = None; self.handle_end_point(start);
}
} }
self.first_point = point.is_endpoint;
} }
fn write_suffix(&mut self) { fn write_suffix(&mut self) {
write!( write!(
self.writer, self.writer,
"\" style=\"fill:none; stroke:black; stroke-width:3;\" /></svg>" "\" style=\"fill:none; stroke:black; stroke-width:3;\" />"
); );
SvgWriter::write_points(&mut self.writer, &self.points, "red");
SvgWriter::write_points(&mut self.writer, &self.control_points, "blue");
SvgWriter::write_points(&mut self.writer, &self.virtual_points, "green");
write!(self.writer, "</svg>");
} }
} }