diff --git a/src/font.rs b/src/font.rs index 8cd5e90..bfb0068 100644 --- a/src/font.rs +++ b/src/font.rs @@ -17,7 +17,7 @@ use log::{debug, info}; use maximum_profile::MaximumProfile; 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(reader: R) -> bincode::Result { bincode::options().with_big_endian().with_fixint_encoding().deserialize_from::(reader) diff --git a/src/font/glyph_data.rs b/src/font/glyph_data.rs index 233ff56..f8553a9 100644 --- a/src/font/glyph_data.rs +++ b/src/font/glyph_data.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, fs::File, io::{BufReader, Read, Seek, SeekFrom}, use bincode::{ErrorKind, Result}; use bitfield::bitfield; -use log::{debug, info, warn}; +use log::{debug, error, info, warn}; use serde::Deserialize; use super::{deserialize, deserialize_vec, index_to_location::IndexToLocation, table_directory::{FontTable, TableDirectoryRecord}}; @@ -252,6 +252,108 @@ pub struct Glyph { pub data: GlyphData } +#[derive(Debug)] +pub enum SplineElement { + Line(GlyphPoint, GlyphPoint), + Bezier(GlyphPoint, GlyphPoint, GlyphPoint) +} + +pub struct SplineIter<'a> { + points: Box + 'a>, + + last_point: Option, + final_control: Option, + first_point: Option +} + +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 { + 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 { + 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 { pub fn iter<'a>(&'a self) -> Box + 'a> { Box::new( @@ -261,6 +363,19 @@ impl Glyph { } ) } + + pub fn splines<'a>(&'a self) -> Result> { + let mut splines = SplineIter { + points: self.iter(), + last_point: None, + final_control: None, + first_point: None + }; + + splines.handle_spline_start()?; + + Ok(splines) + } } #[derive(Debug)] diff --git a/src/writer.rs b/src/writer.rs index bfde3ee..a2ef020 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -1,18 +1,22 @@ mod svg; -use crate::font::{GlyphHeader, GlyphPoint, Glyph}; +use crate::font::{Glyph, GlyphHeader, GlyphPoint, SplineElement}; +use log::debug; pub use svg::SvgWriter; pub trait Visitor { 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(&mut self, glyph: &Glyph) { 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(); } diff --git a/src/writer/svg.rs b/src/writer/svg.rs index cf9cf5f..cd6dfb0 100644 --- a/src/writer/svg.rs +++ b/src/writer/svg.rs @@ -1,25 +1,48 @@ use std::{fs::File, io::{BufWriter, Write}}; -use crate::font::{GlyphHeader, GlyphPoint}; +use crate::font::{GlyphHeader, GlyphPoint, SplineElement}; 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 + + writer: BufWriter, + points: Vec<(i32, i32)>, + control_points: Vec<(i32, i32)>, + virtual_points: Vec<(i32, i32)> } 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) + + writer: BufWriter::new(file), + points: vec![], + 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, points: &Vec<(i32, i32)>, color: &'static str) { + for (x, y) in points { + write!(writer, ""); } } } @@ -35,56 +58,42 @@ impl Visitor for SvgWriter { ); } - fn write_point(&mut self, point: &GlyphPoint) { - if self.final_point.is_none() && point.on_curve { - self.final_point = Some((point.x, point.y)); - } + fn write_point(&mut self, point: &SplineElement) { + match point { + 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.final_control = Some((point.x, point.y)); - } + self.points.push((start.x, -start.y)); + self.handle_end_point(start); + }, - if self.first_point { - if !point.on_curve { - return; - } + SplineElement::Bezier(start, control, end) => { + self.handle_start_point(start); + write!(self.writer, "Q{} {} {} {} ", control.x, -control.y, end.x, -end.y); - 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 - ); - - 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); + if start.is_virtual { + self.virtual_points.push((start.x, -start.y)); } else { - write!(self.writer, "Z "); + self.points.push((start.x, -start.y)); } - } else { - write!(self.writer, "Z "); - } - self.final_point = None; - self.final_control = None; + self.control_points.push((control.x, -control.y)); + self.handle_end_point(start); + } } - self.first_point = point.is_endpoint; } fn write_suffix(&mut self) { write!( self.writer, - "\" style=\"fill:none; stroke:black; stroke-width:3;\" />" + "\" 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, ""); } }