From 03b54c0609d69ef0e5f65382c7412bb28aecfa78 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Fri, 8 Nov 2019 13:04:51 -0800 Subject: [PATCH] Add "inco_fix" script This does a bunch of affine and lerp stuff to a specific version of Inconsolata-vf.glyphs, to make suitable placeholders, and so that it's possible to generate a 700 normal weight from the file. It's hacky and there was some manual fixup required (mostly declining the offer to automatically adjust glyph positions), but it's worth having the logic in a repo, as it's likely that adapting it into a more general tool in some form will be useful. --- glyphstool/src/font.rs | 7 +++ glyphstool/src/inco_fix.rs | 122 +++++++++++++++++++++++++++++++++++++ glyphstool/src/lib.rs | 2 +- glyphstool/src/main.rs | 18 +++++- src/interp_pane.rs | 2 +- 5 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 glyphstool/src/inco_fix.rs diff --git a/glyphstool/src/font.rs b/glyphstool/src/font.rs index d10a8df..c888f0e 100644 --- a/glyphstool/src/font.rs +++ b/glyphstool/src/font.rs @@ -85,6 +85,7 @@ pub struct GuideLine { pub struct FontMaster { pub id: String, pub weight_value: i64, + pub width_value: Option, #[rest] pub other_stuff: HashMap, } @@ -101,6 +102,12 @@ impl Font { } } +impl Glyph { + pub fn get_layer(&self, layer_id: &str) -> Option<&Layer> { + self.layers.iter().find(|l| l.layer_id == layer_id) + } +} + impl FromPlist for Node { fn from_plist(plist: Plist) -> Self { let mut spl = plist.as_str().unwrap().splitn(3, ' '); diff --git a/glyphstool/src/inco_fix.rs b/glyphstool/src/inco_fix.rs new file mode 100644 index 0000000..1b6986f --- /dev/null +++ b/glyphstool/src/inco_fix.rs @@ -0,0 +1,122 @@ +//! A bit of scripting to automate a bunch of Inconsolata vf work. +//! +//! Note that this is a submodule of main, rather than in the lib, as it is not +//! generally useful. But it's very likely that logic in here can be adapted into +//! a more general tool. + +use std::collections::HashMap; + +use kurbo::Affine; + +use glyphstool::{Font, Glyph, Layer, Node, Path}; + +#[derive(Default)] +struct LayerMap { + params_to_id: HashMap<(i64, i64), String>, + id_to_params: HashMap, +} + +impl LayerMap { + fn add(&mut self, wght: i64, wdth: i64, id: &str) { + self.params_to_id.insert((wght, wdth), id.to_string()); + self.id_to_params.insert(id.to_string(), (wght, wdth)); + } + + fn get_id(&self, wght: i64, wdth: i64) -> &str { + &self.params_to_id[&(wght, wdth)] + } + + fn get_params(&self, id: &str) -> Option<(i64, i64)> { + self.id_to_params.get(id).copied() + } +} + +fn affine_stretch(stretch: f64) -> Affine { + Affine::new([stretch, 0., 0., 1., 0., 0.]) +} + +fn fix_path(path0: &Path, path1: &Path, t: f64, stretch: f64) -> Path { + let a = affine_stretch(stretch); + let nodes = path0 + .nodes + .iter() + .zip(path1.nodes.iter()) + .map(|(n0, n1)| Node { + pt: a * n0.pt.lerp(n1.pt, t).round(), + node_type: n0.node_type, + }) + .collect(); + Path { + closed: path0.closed, + nodes, + } +} + +fn fix_glyph(glyph: &mut Glyph, layers: &LayerMap) { + let paths0 = glyph + .get_layer(layers.get_id(400, 100)) + .unwrap() + .paths + .clone(); + // This is actually the 700 from the master, but is stored in 900. + let paths1 = glyph + .get_layer(layers.get_id(900, 100)) + .unwrap() + .paths + .clone(); + println!("processing glyph {}", glyph.glyphname); + for layer in &mut glyph.layers { + if let Some((wght, wdth)) = layers.get_params(&layer.layer_id) { + let t = (wght as f64 - 400.0) / 300.0; + let stretch = wdth as f64 / 100.0; + println!(" touching layer {}, t = {}", layer.layer_id, t); + if let Some(ref p0) = paths0 { + let paths = p0 + .iter() + .zip(paths1.as_ref().unwrap().iter()) + .map(|(p0, p1)| fix_path(p0, p1, t, stretch)) + .collect(); + layer.paths = Some(paths); + } + layer.width = wdth as f64 * 5.0; + + // Possibly TODO: lerp the affine from the masters, rather than + // doing the processing in-place. Not clear whether it makes much + // difference. + let a = affine_stretch(stretch); + let a_inv = affine_stretch(stretch.recip()); + + if let Some(ref mut anchors) = layer.anchors { + for anchor in anchors { + anchor.position = (a * anchor.position).round(); + } + } + + if let Some(ref mut components) = layer.components { + for component in components { + if let Some(ref mut transform) = component.transform { + // TODO: round the translation component + *transform = a * *transform * a_inv; + } + } + } + } + } +} + +pub fn inco_fix(font: &mut Font) { + let mut layers = LayerMap::default(); + for master in &font.font_master { + let wght = master.weight_value; + let wdth = master.width_value.unwrap_or(100); + println!("{}: wght {}, wdth {}", master.id, wght, wdth); + layers.add(wght, wdth, &master.id); + } + let layer_400_narrow_id = layers.get_id(400, 50); + for glyph in &mut font.glyphs { + let narrow = glyph.get_layer(layer_400_narrow_id).unwrap(); + if narrow.width != 250. && !glyph.glyphname.starts_with("_corner") { + fix_glyph(glyph, &layers); + } + } +} diff --git a/glyphstool/src/lib.rs b/glyphstool/src/lib.rs index 7051534..2d050c5 100644 --- a/glyphstool/src/lib.rs +++ b/glyphstool/src/lib.rs @@ -7,7 +7,7 @@ mod plist; mod stretch; mod to_plist; -pub use font::{Font, NodeType}; +pub use font::{Font, Glyph, Layer, Node, NodeType, Path}; pub use from_plist::FromPlist; pub use plist::Plist; pub use stretch::stretch; diff --git a/glyphstool/src/main.rs b/glyphstool/src/main.rs index d5efe7c..3210e25 100644 --- a/glyphstool/src/main.rs +++ b/glyphstool/src/main.rs @@ -1,12 +1,14 @@ -use std::env; use std::fs; use std::path::{Path, PathBuf}; use structopt::StructOpt; +mod inco_fix; + #[derive(StructOpt, Debug)] enum Cmd { - Merge(MergeCmd) + Merge(MergeCmd), + IncoFix(IncoFixCmd), } #[derive(StructOpt, Debug)] @@ -23,6 +25,13 @@ struct MergeCmd { layer: String, } +#[derive(StructOpt, Debug)] +struct IncoFixCmd { + /// The font file to operate on. + #[structopt(parse(from_os_str))] + font: PathBuf, +} + use glyphstool::{ops, stretch, Font, FromPlist, Plist, ToPlist}; fn usage() { @@ -51,6 +60,11 @@ fn main() { ops::merge(&mut font, &other, &m.layer); write_font(&m.font, font); } + Cmd::IncoFix(m) => { + let mut font = read_font(&m.font); + inco_fix::inco_fix(&mut font); + write_font(&m.font, font); + } } /* let mut filename = None; diff --git a/src/interp_pane.rs b/src/interp_pane.rs index d2d9531..4d5d76d 100644 --- a/src/interp_pane.rs +++ b/src/interp_pane.rs @@ -43,7 +43,7 @@ fn reconstruct_path(pts: &[Point], structure: &[Vec]) -> BezPath { let node_type = subpath[ix]; let p = pts[j + ix]; match node_type { - NodeType::Line => bez_path.line_to(p), + NodeType::Line | NodeType::LineSmooth => bez_path.line_to(p), NodeType::OffCurve => ctrl_pts.push(p), NodeType::Curve | NodeType::CurveSmooth => { bez_path.curve_to(ctrl_pts[0], ctrl_pts[1], p); -- 2.34.2