Created
March 24, 2021 02:49
-
-
Save ctrlcctrlv/2b75fd25b6ac531eb2c01670f0541730 to your computer and use it in GitHub Desktop.
Nib stroker from FontForge (WIP 2)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #![feature(fn_traits)] | |
| use fontforge_sys as fontforge; | |
| use glifparser; | |
| use std::fs; | |
| use std::os::raw; | |
| use std::ops::Fn; | |
| use std::ptr; | |
| #[derive(Default, Debug, PartialEq)] | |
| struct SplinePointBitField { | |
| nonextcp: raw::c_uint, | |
| noprevcp: raw::c_uint, | |
| nextcpdef: raw::c_uint, | |
| prevcpdef: raw::c_uint, | |
| selected: raw::c_uint, | |
| nextcpselected: raw::c_uint, | |
| prevcpselected: raw::c_uint, | |
| pointtype: raw::c_uint, | |
| isintersection: raw::c_uint, | |
| flexy: raw::c_uint, | |
| flexx: raw::c_uint, | |
| roundx: raw::c_uint, | |
| roundy: raw::c_uint, | |
| dontinterpolate: raw::c_uint, | |
| ticked: raw::c_uint, | |
| watched: raw::c_uint, | |
| } | |
| #[derive(Default, Debug, PartialEq)] | |
| struct SplineBitField { | |
| islinear: raw::c_uint, | |
| isquadratic: raw::c_uint, | |
| isticked: raw::c_uint, | |
| isneeded: raw::c_uint, | |
| isunneeded: raw::c_uint, | |
| exclude: raw::c_uint, | |
| ishorvert: raw::c_uint, | |
| knowncurved: raw::c_uint, | |
| knownlinear: raw::c_uint, | |
| order2: raw::c_uint, | |
| touched: raw::c_uint, | |
| leftedge: raw::c_uint, | |
| rightedge: raw::c_uint, | |
| acceptableextrema: raw::c_uint, | |
| } | |
| impl SplinePointBitField { | |
| fn to_bitfield(self) -> (u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32) { | |
| return (self.nonextcp, self.noprevcp, self.nextcpdef, self.prevcpdef, self.selected, self.nextcpselected, self.prevcpselected, self.pointtype, self.isintersection, self.flexy, self.flexx, self.roundx, self.roundy, self.dontinterpolate, self.ticked, self.watched); | |
| } | |
| } | |
| impl SplineBitField { | |
| fn to_bitfield(self) -> (u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32) { | |
| return (self.islinear, self.isquadratic, self.isticked, self.isneeded, self.isunneeded, self.exclude, self.ishorvert, self.knowncurved, self.knownlinear, self.order2, self.touched, self.leftedge, self.rightedge, self.acceptableextrema); | |
| } | |
| } | |
| #[derive(Debug)] | |
| struct FFContour { | |
| data: Vec<Box<fontforge::SplinePoint>>, | |
| open: bool | |
| } | |
| // This function isn't close to done, don't bother reading it lol | |
| fn ffsplineset_to_outline(ss_in: fontforge::SplineSet) -> glifparser::Outline<()> { | |
| let ret = glifparser::Outline::new(); | |
| let mut splinesets; | |
| unsafe { | |
| let mut ss = ss_in; | |
| splinesets = vec![ss]; | |
| while ss.next != 0 as *mut _ { | |
| splinesets.push(*ss.next); | |
| ss = *ss.next | |
| } | |
| } | |
| println!("Splinesets: {:?}", splinesets); | |
| ret | |
| } | |
| fn make_spline(from: *mut fontforge::SplinePoint, to: *mut fontforge::SplinePoint, order2: bool) -> *mut fontforge::Spline { | |
| unsafe { | |
| let s = fontforge::SplineMake(from, to, order2 as raw::c_int); | |
| fontforge::SplineRefigure(s); | |
| s | |
| } | |
| } | |
| fn glif_to_ffsplineset<T>(glif: glifparser::Glif<T>) -> (fontforge::SplineSet, Vec<Vec<fontforge::SplinePoint>>) { | |
| let mut ffsps = vec![]; | |
| for c in glif.outline.unwrap().iter() { | |
| let mut cffsps = vec![]; | |
| for (idx, p) in c.iter().enumerate() { | |
| let bp0_1 = fontforge::BasePoint {x: p.x as f64, y: p.y as f64}; | |
| let (ax, ay) = p.handle_or_colocated(glifparser::WhichHandle::A, |f|f, |f|f); | |
| let bp0_2 = fontforge::BasePoint {x: ax as f64, y: ay as f64}; | |
| let (bx, by) = p.handle_or_colocated(glifparser::WhichHandle::B, |f|f, |f|f); | |
| let bp0_3 = fontforge::BasePoint {x: bx as f64, y: by as f64}; | |
| let mut spbf = SplinePointBitField{..Default::default()}; | |
| spbf.noprevcp = (p.a == glifparser::Handle::Colocated) as u32; | |
| spbf.nonextcp = (p.b == glifparser::Handle::Colocated) as u32; | |
| let bf = Fn::call(&fontforge::splinepoint::new_bitfield_1, spbf.to_bitfield()); | |
| let sp = fontforge::SplinePoint { | |
| me: bp0_1, | |
| prevcp: bp0_2, | |
| nextcp: bp0_3, | |
| _bitfield_align_1: [], | |
| _bitfield_1: bf, | |
| ptindex: idx as u16, | |
| // These are for TrueType fonts and don't matter to us. | |
| ttfindex: 0, nextcpindex: 0, | |
| next: 0 as *mut _, | |
| prev: 0 as *mut _, | |
| hintmask: 0 as *mut _, | |
| name: 0 as *mut _ | |
| }; | |
| cffsps.push(sp); | |
| } | |
| let cffsps_len = cffsps.len(); | |
| for idx in 0..cffsps_len { | |
| if idx == 0 { | |
| if c[0].ptype != glifparser::PointType::Move { | |
| cffsps[0].prev = make_spline(&mut cffsps[idx] as *mut _, &mut cffsps[cffsps_len-1] as *mut _, false); | |
| } else { | |
| cffsps[0].prev = 0 as *mut _; | |
| } | |
| cffsps[0].next = make_spline(&mut cffsps[idx] as *mut _, &mut cffsps[idx+1] as *mut _, false); | |
| } else if idx == cffsps_len-1 { | |
| cffsps[idx].prev = make_spline(&mut cffsps[idx] as *mut _, &mut cffsps[idx-1] as *mut _, false); | |
| if c[0].ptype != glifparser::PointType::Move { | |
| cffsps[idx].next = make_spline(&mut cffsps[idx] as *mut _, &mut cffsps[0] as *mut _, false); | |
| } else { | |
| cffsps[idx].next = 0 as *mut _; | |
| } | |
| } else { | |
| cffsps[idx].prev = make_spline(&mut cffsps[idx] as *mut _, &mut cffsps[idx-1] as *mut _, false); | |
| cffsps[idx].next = make_spline(&mut cffsps[idx] as *mut _, &mut cffsps[idx+1] as *mut _, false); | |
| } | |
| println!("{} {:?} {:?}", idx, cffsps[idx].prev, cffsps[idx].next); | |
| } | |
| ffsps.push(cffsps); | |
| } | |
| let mut ffsss = vec![]; | |
| for spl in ffsps.iter_mut() { | |
| ffsss.push(fontforge::SplineSet { | |
| first: spl.first_mut().unwrap(), | |
| last: spl.last_mut().unwrap(), | |
| next: 0 as *mut _, | |
| spiros: 0 as *mut _, | |
| spiro_cnt: 0, spiro_max: 0, ticked: 0, | |
| beziers_need_optimizer: 1, is_clip_path: 0, | |
| start_offset: 0, | |
| contour_name: 0 as *mut _ | |
| }); | |
| } | |
| for idx in 0..ffsss.len() { | |
| if idx == ffsss.len()-1 { | |
| break | |
| } else { | |
| ffsss[idx].next = Box::new(ffsss[idx+1]).as_mut() | |
| } | |
| } | |
| // We return ffsps so its valuable data doesn't go out of scope. | |
| (ffsss[0], ffsps) | |
| } | |
| #[test] | |
| fn convert_glif() { | |
| let nibglif: glifparser::Glif<()> = glifparser::read_ufo_glif(&fs::read_to_string("nib.glif").unwrap()); | |
| let (mut nibss_raw, mut _nibss_ffsps) = glif_to_ffsplineset(nibglif); | |
| let ssglif: glifparser::Glif<()> = glifparser::read_ufo_glif(&fs::read_to_string("l.glif").unwrap()); | |
| let (mut ss_raw, mut _ss_ffsps) = glif_to_ffsplineset(ssglif); | |
| let mut nibss_boxed = Box::new(nibss_raw); | |
| let mut ss_boxed = Box::new(ss_raw); | |
| let mut nibss = nibss_boxed.as_mut(); | |
| let mut ss = ss_boxed.as_mut(); | |
| let mut out_ss; | |
| unsafe { | |
| if fontforge::StrokeSetConvex(nibss, 0) == 0 { | |
| panic!("nib not convex"); | |
| } | |
| let shape = fontforge::NibIsValid(nibss); | |
| println!("Shape: {}", shape); | |
| let si = fontforge::InitializeStrokeInfo(0 as *mut _); | |
| println!("Stroking..."); | |
| let newss = fontforge::SplineSetStroke(ss, si, 0); | |
| println!("{:?}", *newss); | |
| out_ss = *newss; | |
| } | |
| let out = ffsplineset_to_outline(out_ss); | |
| println!("O: {:?}", out); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment