~raph/interp-toy

4cfe4c880ba40b18c5887781a1cce2da9f2400e9 — Raph Levien 2 years ago a65bfef
Add "merge" command

A simple command for merging in a layer from another font.

Also has some tweaks to make roundtrip more robust.
M Cargo.lock => Cargo.lock +1 -0
@@ 352,6 352,7 @@ version = "0.1.0"
dependencies = [
 "kurbo 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
 "plist_derive 0.1.0",
 "structopt 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]

M glyphstool/Cargo.lock => glyphstool/Cargo.lock +145 -0
@@ 1,6 1,14 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "arrayvec"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 9,11 17,48 @@ dependencies = [
]

[[package]]
name = "atty"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
 "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "bitflags"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "clap"
version = "2.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
 "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
 "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "glyphstool"
version = "0.1.0"
dependencies = [
 "kurbo 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
 "plist_derive 0.1.0",
 "structopt 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "heck"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]


@@ 25,6 70,11 @@ dependencies = [
]

[[package]]
name = "libc"
version = "0.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "nodrop"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 39,6 89,16 @@ dependencies = [
]

[[package]]
name = "proc-macro-error"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
 "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
 "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "proc-macro2"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 55,6 115,32 @@ dependencies = [
]

[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "structopt"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "structopt-derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "structopt-derive"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
 "proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
 "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
 "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
 "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "syn"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 65,15 151,74 @@ dependencies = [
]

[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "unicode-segmentation"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "unicode-width"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "vec_map"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "winapi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[metadata]
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba"
"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2"
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
"checksum kurbo 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2f0caeb26248a62abf92dea93aad4f8244f54668e2f1060ed9cd9fd1d5545723"
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
"checksum proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aeccfe4d5d8ea175d5f0e4a2ad0637e0f4121d63bd99d356fb1f39ab2e7c6097"
"checksum proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afdc77cc74ec70ed262262942ebb7dac3d479e9e5cfa2da1841c0806f6cdabcc"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum structopt 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe8d3289b63ef2f196d89e7701f986583c0895e764b78f052a55b9b5d34d84a"
"checksum structopt-derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f3add731f5b4fb85931d362a3c92deb1ad7113649a8d51701fb257673705f122"
"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9"
"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

M glyphstool/Cargo.toml => glyphstool/Cargo.toml +3 -0
@@ 13,3 13,6 @@ name = "glyphstool"
[dependencies]
kurbo = "0.5.1"
plist_derive = { path = "plist_derive" }

# This dep is used for the binary, not the lib, but oh well.
structopt = "0.3.2"

M glyphstool/src/font.rs => glyphstool/src/font.rs +12 -7
@@ 28,7 28,7 @@ pub struct Glyph {
    pub other_stuff: HashMap<String, Plist>,
}

#[derive(Debug, FromPlist, ToPlist)]
#[derive(Clone, Debug, FromPlist, ToPlist)]
pub struct Layer {
    pub layer_id: String,
    pub width: f64,


@@ 40,13 40,13 @@ pub struct Layer {
    pub other_stuff: HashMap<String, Plist>,
}

#[derive(Debug, FromPlist, ToPlist)]
#[derive(Clone, Debug, FromPlist, ToPlist)]
pub struct Path {
    pub closed: bool,
    pub nodes: Vec<Node>,
}

#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct Node {
    pub pt: Point,
    pub node_type: NodeType,


@@ 55,12 55,13 @@ pub struct Node {
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum NodeType {
    Line,
    LineSmooth,
    OffCurve,
    Curve,
    CurveSmooth,
}

#[derive(Debug, FromPlist, ToPlist)]
#[derive(Clone, Debug, FromPlist, ToPlist)]
pub struct Component {
    pub name: String,
    pub transform: Option<Affine>,


@@ 68,13 69,13 @@ pub struct Component {
    pub other_stuff: HashMap<String, Plist>,
}

#[derive(Debug, FromPlist, ToPlist)]
#[derive(Clone, Debug, FromPlist, ToPlist)]
pub struct Anchor {
    pub name: String,
    pub position: Point,
}

#[derive(Debug, FromPlist, ToPlist)]
#[derive(Clone, Debug, FromPlist, ToPlist)]
pub struct GuideLine {
    pub angle: Option<f64>,
    pub position: Point,


@@ 84,6 85,8 @@ pub struct GuideLine {
pub struct FontMaster {
    pub id: String,
    pub weight_value: i64,
    #[rest]
    pub other_stuff: HashMap<String, Plist>,
}

impl Font {


@@ 100,7 103,7 @@ impl Font {

impl FromPlist for Node {
    fn from_plist(plist: Plist) -> Self {
        let mut spl = plist.as_str().unwrap().split(' ');
        let mut spl = plist.as_str().unwrap().splitn(3, ' ');
        let x = spl.next().unwrap().parse().unwrap();
        let y = spl.next().unwrap().parse().unwrap();
        let pt = Point::new(x, y);


@@ 114,6 117,7 @@ impl std::str::FromStr for NodeType {
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "LINE" => Ok(NodeType::Line),
            "LINE SMOOTH" => Ok(NodeType::LineSmooth),
            "OFFCURVE" => Ok(NodeType::OffCurve),
            "CURVE" => Ok(NodeType::Curve),
            "CURVE SMOOTH" => Ok(NodeType::CurveSmooth),


@@ 126,6 130,7 @@ impl NodeType {
    fn glyphs_str(&self) -> &'static str {
        match self {
            NodeType::Line => "LINE",
            NodeType::LineSmooth => "LINE SMOOTH",
            NodeType::OffCurve => "OFFCURVE",
            NodeType::Curve => "CURVE",
            NodeType::CurveSmooth => "CURVE SMOOTH",

M glyphstool/src/lib.rs => glyphstool/src/lib.rs +1 -0
@@ 2,6 2,7 @@

mod font;
mod from_plist;
pub mod ops;
mod plist;
mod stretch;
mod to_plist;

M glyphstool/src/main.rs => glyphstool/src/main.rs +47 -1
@@ 1,13 1,58 @@
use std::env;
use std::fs;
use std::path::{Path, PathBuf};

use glyphstool::{stretch, Font, FromPlist, Plist, ToPlist};
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
enum Cmd {
    Merge(MergeCmd)
}

#[derive(StructOpt, Debug)]
struct MergeCmd {
    /// The font file to merge in.
    #[structopt(parse(from_os_str))]
    font: PathBuf,

    /// The other font file, to use as a source.
    #[structopt(parse(from_os_str))]
    other: PathBuf,

    /// The layer to merge (UUID).
    layer: String,
}

use glyphstool::{ops, stretch, Font, FromPlist, Plist, ToPlist};

fn usage() {
    eprintln!("usage: glyphstool font.glyphs");
}

fn read_font(path: &Path) -> Font {
    let contents = fs::read_to_string(path).expect("error reading font file");
    let plist = Plist::parse(&contents).expect("error parsing font file");
    FromPlist::from_plist(plist)
}

fn write_font(path: &Path, font: Font) {
    let plist = font.to_plist();
    fs::write(path, &plist.to_string());
}

fn main() {
    let cmd = Cmd::from_args();

    match cmd {
        Cmd::Merge(m) => {
            println!("merge {:?}", m);
            let mut font = read_font(&m.font);
            let other = read_font(&m.other);
            ops::merge(&mut font, &other, &m.layer);
            write_font(&m.font, font);
        }
    }
    /*
    let mut filename = None;
    for arg in env::args().skip(1) {
        if filename.is_none() {


@@ 36,4 81,5 @@ fn main() {
    stretch(&mut font, 0.5, "051EFAE4-8BBE-4FBB-A016-4335C3E52F59");
    let plist = font.to_plist();
    println!("{}", plist.to_string());
    */
}

A glyphstool/src/ops.rs => glyphstool/src/ops.rs +24 -0
@@ 0,0 1,24 @@
//! Operations for manipulating fonts.

use std::collections::HashMap;

use crate::font::Font;

pub fn merge(font: &mut Font, other: &Font, layer_id: &str) {
    let mut map = HashMap::new();
    for glyph in &other.glyphs {
        for layer in &glyph.layers {
            if layer.layer_id == layer_id {
                map.insert(glyph.glyphname.to_owned(), layer);
            }
        }
    }

    for glyph in &mut font.glyphs {
        for layer in &mut glyph.layers {
            if layer.layer_id == layer_id {
                *layer = (*map[&glyph.glyphname]).clone();
            }
        }
    }
}

M glyphstool/src/plist.rs => glyphstool/src/plist.rs +14 -2
@@ 44,16 44,28 @@ fn is_alnum_strict(b: u8) -> bool {
    is_alnum(b) && b != b'-'
}

fn is_ascii_digit(b: u8) -> bool {
    b >= b'0' && b <= b'9'
}

fn is_hex_upper(b: u8) -> bool {
    (b >= b'0' && b <= b'9') || (b >= b'A' && b <= b'F')
}

fn is_ascii_whitespace(b: u8) -> bool {
    b == b' ' || b == b'\t' || b == b'\r' || b == b'\n'
}

fn numeric_ok(s: &str) -> bool {
    let s = s.as_bytes();
    if s.is_empty() {
        return false;
    }
    if s.len() > 1 && s.as_bytes()[0] == b'0' {
        return !s.as_bytes().iter().all(|&b| b >= b'0' && b <= b'9');
    if s.iter().all(|&b| is_hex_upper(b)) && !s.iter().all(|&b| is_ascii_digit(b)) {
        return false;
    }
    if s.len() > 1 && s[0] == b'0' {
        return !s.iter().all(|&b| is_ascii_digit(b));
    }
    true
}