diff --git a/glyphs-reader/src/font.rs b/glyphs-reader/src/font.rs index 7cd5c6c7..dc7913e3 100644 --- a/glyphs-reader/src/font.rs +++ b/glyphs-reader/src/font.rs @@ -740,6 +740,7 @@ struct RawFeature { disabled: Option, name: Option, tag: Option, + notes: Option, code: String, labels: Vec, @@ -2131,31 +2132,29 @@ impl RawFeature { self.disabled == Some(1) } + /// Some glyphs sources store stylistic set names in the 'note' field + /// + /// See the little sidebar item here: + /// + fn legacy_name_record_maybe(&self) -> Option { + let name = self.notes.as_deref()?.strip_prefix("Name:")?.trim(); + Some(format!("name 3 1 0x409 \"{name}\";")) + } + // https://github.com/googlefonts/glyphsLib/blob/24b4d340e4c82948ba121dcfe563c1450a8e69c9/Lib/glyphsLib/builder/features.py#L134 fn feature_names(&self) -> String { - if self.labels.is_empty() { - return String::new(); - } let labels = self .labels .iter() - .filter_map(|label| { - GLYPHS_TO_OPENTYPE_LANGUAGE_ID - .binary_search_by_key(&label.language, |entry| entry.0.to_owned()) - .ok() - .map(|index| { - let language_id = &GLYPHS_TO_OPENTYPE_LANGUAGE_ID[index].1; - let name = label.value.replace("\\", "\\005c").replace("\"", "\\0022"); - format!(" name 3 1 0x{:04X} \"{}\";", language_id, name) - }) - .or_else(|| { - warn!("Unknown feature label language: {}", label.language); - None - }) - }) + .filter_map(|label| label.to_fea()) + .chain(self.legacy_name_record_maybe()) .collect::>() .join("\n"); - format!("featureNames {{\n{}\n}};\n", labels) + if labels.is_empty() { + Default::default() + } else { + format!("featureNames {{\n{}\n}};\n", labels) + } } // https://github.com/googlefonts/glyphsLib/blob/24b4d340e4c82948ba121dcfe563c1450a8e69c9/Lib/glyphsLib/builder/features.py#L90 @@ -2190,6 +2189,24 @@ impl RawFeature { } } +impl RawNameValue { + fn to_fea(&self) -> Option { + match GLYPHS_TO_OPENTYPE_LANGUAGE_ID + .binary_search_by_key(&self.language.as_str(), |entry| entry.0) + { + Ok(idx) => { + let language_id = GLYPHS_TO_OPENTYPE_LANGUAGE_ID[idx].1; + let name = self.value.replace("\\", "\\005c").replace("\"", "\\0022"); + Some(format!(" name 3 1 0x{:04X} \"{}\";", language_id, name)) + } + Err(_) => { + warn!("Unknown feature label language: {}", self.language); + None + } + } + } +} + /// fn lookup_class_value(axis_tag: &str, user_class: &str) -> Option { let user_class = match user_class { @@ -3215,13 +3232,9 @@ mod tests { #[test] fn tags_make_excellent_names() { let raw = RawFeature { - name: None, tag: Some("aalt".to_string()), - automatic: None, - disabled: None, code: "blah".to_string(), - labels: vec![], - other_stuff: BTreeMap::new(), + ..Default::default() }; assert_eq!("aalt", raw.name().unwrap()); } @@ -3568,4 +3581,13 @@ mod tests { font.custom_parameters.underline_position ); } + + #[test] + fn parse_legacy_stylistic_set_name() { + let font = Font::load(&glyphs2_dir().join("FeaLegacyName.glyphs")).unwrap(); + assert_eq!(font.features.len(), 1); + assert!(font.features[0] + .content + .contains("name 3 1 0x409 \"Alternate placeholder\"")); + } } diff --git a/resources/testdata/glyphs2/FeaLegacyName.glyphs b/resources/testdata/glyphs2/FeaLegacyName.glyphs new file mode 100644 index 00000000..dc6565b1 --- /dev/null +++ b/resources/testdata/glyphs2/FeaLegacyName.glyphs @@ -0,0 +1,40 @@ +{ +.appVersion = "3148"; +familyName = "legacy stylistic set names"; +features = ( +{ +automatic = 1; +code = "sub a by a.ss01;"; +name = ss01; +notes = "Name: Alternate placeholder"; +}, +); +fontMaster = ( +{ +id = "m01"; +}, +); +glyphs = ( + +{ +glyphname = a; +layers = ( +{ +layerId = "m01"; +width = 600; +} +); +unicode = 97; +}, +{ +glyphname = a.ss01; +layers = ( +{ +layerId = "m01"; +width = 600; +} +); +}, +); +unitsPerEm = 1000; +}