diff --git a/glyphs-reader/src/font.rs b/glyphs-reader/src/font.rs index ff47a861..65800c67 100644 --- a/glyphs-reader/src/font.rs +++ b/glyphs-reader/src/font.rs @@ -62,6 +62,9 @@ pub struct Font { pub version_major: i32, pub version_minor: u32, pub date: Option, + + // master id => { (name or class, name or class) => adjustment } + pub kerning_ltr: BTreeMap>, } #[derive(Debug, PartialEq, Eq, Hash)] @@ -968,6 +971,13 @@ impl RawFont { Ok(()) } + fn v2_to_v3_kerning(&mut self) -> Result<(), Error> { + if let Some(kerning) = self.other_stuff.remove("kerning") { + self.other_stuff.insert("kerningLTR".to_string(), kerning); + }; + Ok(()) + } + /// `` fn v2_to_v3(&mut self) -> Result<(), Error> { self.v2_to_v3_weight()?; @@ -975,6 +985,7 @@ impl RawFont { self.v2_to_v3_metrics()?; self.v2_to_v3_names()?; self.v2_to_v3_instances()?; + self.v2_to_v3_kerning()?; Ok(()) } } @@ -1027,6 +1038,31 @@ fn parse_codepoints(raw_font: &mut RawFont, radix: u32) -> BTreeMap) -> BTreeMap> { + let mut result = BTreeMap::new(); + let Some(Plist::Dictionary(kerning)) = kerning else { + return result; + }; + for (master_id, kerning) in kerning { + let mut master_kerns = BTreeMap::new(); + if let Plist::Dictionary(kerning) = kerning { + for (kern_from, kern_tos) in kerning { + let Plist::Dictionary(kern_tos) = kern_tos else { + continue; + }; + for (kern_to, adjustment) in kern_tos { + let Some(adjustment) = adjustment.as_i64() else { + continue; + }; + master_kerns.insert((kern_from.clone(), kern_to.clone()), adjustment as i32); + } + } + } + result.insert(master_id.clone(), master_kerns); + } + result +} + /// fn default_master_idx(raw_font: &RawFont) -> usize { // Prefer an explicit origin @@ -1584,6 +1620,7 @@ impl TryFrom for Font { version_major: from.versionMajor.unwrap_or_default() as i32, version_minor: from.versionMinor.unwrap_or_default() as u32, date: from.date, + kerning_ltr: parse_kerning(from.other_stuff.get("kerningLTR")), }) } } @@ -1662,7 +1699,7 @@ mod tests { Font, FromPlist, Node, Plist, Shape, }; use std::{ - collections::{BTreeMap, BTreeSet}, + collections::{BTreeMap, BTreeSet, HashSet}, path::{Path, PathBuf}, }; @@ -1673,7 +1710,11 @@ mod tests { use kurbo::Affine; fn testdata_dir() -> PathBuf { - let dir = Path::new("../resources/testdata"); + // working dir varies CLI vs VSCode + let mut dir = Path::new("../resources/testdata"); + if !dir.is_dir() { + dir = Path::new("./resources/testdata"); + } assert!(dir.is_dir()); dir.to_path_buf() } @@ -2139,4 +2180,33 @@ mod tests { let font = Font::load(&glyphs2_dir().join("WghtVar_OS2.glyphs")).unwrap(); assert_eq!((None, None), (font.use_typo_metrics, font.has_wws_names)); } + + #[test] + fn read_simple_kerning() { + let font = Font::load(&glyphs3_dir().join("WghtVar.glyphs")).unwrap(); + assert_eq!( + HashSet::from(["m01", "E09E0C54-128D-4FEA-B209-1B70BEFE300B",]), + font.kerning_ltr + .keys() + .map(|k| k.as_str()) + .collect::>() + ); + + let actual_kerning = font + .kerning_ltr + .get("m01") + .unwrap() + .iter() + .map(|((n1, n2), value)| (n1.as_str(), n2.as_str(), *value)) + .collect::>(); + + assert_eq!( + vec![ + ("exclam", "exclam", -360), + ("exclam", "hyphen", 20), + ("hyphen", "hyphen", -150), + ], + actual_kerning, + ); + } } diff --git a/glyphs2fontir/src/source.rs b/glyphs2fontir/src/source.rs index e3e5469e..998fac0a 100644 --- a/glyphs2fontir/src/source.rs +++ b/glyphs2fontir/src/source.rs @@ -90,6 +90,7 @@ impl GlyphsIrSource { version_major: Default::default(), version_minor: Default::default(), date: None, + kerning_ltr: Default::default(), }; state.track_memory("/font_master".to_string(), &font)?; Ok(state) @@ -117,6 +118,7 @@ impl GlyphsIrSource { version_major: Default::default(), version_minor: Default::default(), date: None, + kerning_ltr: font.kerning_ltr.clone(), }; state.track_memory("/font_master".to_string(), &font)?; Ok(state) diff --git a/resources/testdata/glyphs2/WghtVar.glyphs b/resources/testdata/glyphs2/WghtVar.glyphs index 986c2ffb..1e277cd7 100644 --- a/resources/testdata/glyphs2/WghtVar.glyphs +++ b/resources/testdata/glyphs2/WghtVar.glyphs @@ -2,7 +2,7 @@ .appVersion = "3151"; DisplayStrings = ( "-", -"!" +"!!--" ); copyright = "Copy!"; customParameters = ( @@ -84,7 +84,7 @@ unicode = 0020; }, { glyphname = exclam; -lastChange = "2022-12-01 05:10:49 +0000"; +lastChange = "2023-06-05 23:22:51 +0000"; layers = ( { layerId = m01; @@ -139,7 +139,7 @@ unicode = 0021; }, { glyphname = hyphen; -lastChange = "2022-12-01 04:57:39 +0000"; +lastChange = "2023-06-05 23:23:03 +0000"; layers = ( { layerId = m01; @@ -208,6 +208,25 @@ width = 600; unicode = 003D; } ); +kerning = { +m01 = { +exclam = { +exclam = -360; +hyphen = 20; +}; +hyphen = { +hyphen = -150; +}; +}; +"E09E0C54-128D-4FEA-B209-1B70BEFE300B" = { +exclam = { +exclam = -100; +}; +hyphen = { +hyphen = -50; +}; +}; +}; unitsPerEm = 1000; versionMajor = 42; versionMinor = 42; diff --git a/resources/testdata/glyphs3/WghtVar.glyphs b/resources/testdata/glyphs3/WghtVar.glyphs index b020a6fd..22ec2e3e 100644 --- a/resources/testdata/glyphs3/WghtVar.glyphs +++ b/resources/testdata/glyphs3/WghtVar.glyphs @@ -3,7 +3,7 @@ .formatVersion = 3; DisplayStrings = ( "-", -"!" +"!!--" ); axes = ( { @@ -93,7 +93,7 @@ unicode = 32; }, { glyphname = exclam; -lastChange = "2022-12-01 05:10:49 +0000"; +lastChange = "2023-06-05 23:22:51 +0000"; layers = ( { layerId = m01; @@ -148,7 +148,7 @@ unicode = 33; }, { glyphname = hyphen; -lastChange = "2022-12-01 04:57:39 +0000"; +lastChange = "2023-06-05 23:23:03 +0000"; layers = ( { layerId = m01; @@ -218,6 +218,25 @@ width = 600; unicode = 61; } ); +kerningLTR = { +m01 = { +exclam = { +exclam = -360; +hyphen = 20; +}; +hyphen = { +hyphen = -150; +}; +}; +"E09E0C54-128D-4FEA-B209-1B70BEFE300B" = { +exclam = { +exclam = -100; +}; +hyphen = { +hyphen = -50; +}; +}; +}; metrics = ( { type = ascender;