diff --git a/Cargo.toml b/Cargo.toml index 1a5b84de..7f318ed2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ exclude = [ ] [dependencies] -plist = "1.0" +plist = "1.0.1" serde = { version = "1.0", features = ["rc"] } serde_derive = "1.0" serde_repr = "0.1" diff --git a/examples/normalize.rs b/examples/normalize.rs new file mode 100644 index 00000000..32a0c806 --- /dev/null +++ b/examples/normalize.rs @@ -0,0 +1,64 @@ +//! A little tool for normalizing UFOs. +//! +//! It will scrub layer and lib data of a UFO in an opinionated way, as done +//! in the Cantarell font project, to show a real-world script. +//! +//! Call like `cargo run --release --example normalize some.ufo another.ufo`. + +fn main() { + for arg in std::env::args().skip(1) { + let mut ufo = match norad::Ufo::load(&arg) { + Ok(v) => v, + Err(e) => { + eprintln!("Loading UFO failed: {}", e); + std::process::exit(1); + } + }; + + // Prune all non-foreground layers. + ufo.layers.retain(|l| l.name == "public.default"); + + // Prune the foreground layer's lib. + let default_layer = ufo.get_default_layer_mut().unwrap(); + default_layer.info.lib.as_mut().and_then(|lib| { + Some(lib.retain(|k, &mut _| { + k.starts_with("public.") || k.starts_with("com.schriftgestaltung.layerId") + })) + }); + + // Prune all glyphs' libs. + for glyph in default_layer.iter_contents_mut() { + glyph.lib.as_mut().and_then(|lib| { + Some(lib.retain(|k, &mut _| { + (k.starts_with("public.") + || k.starts_with("com.schriftgestaltung.") + || k == "com.schriftgestaltung.componentsAlignment") + && k != "public.markColor" + })) + }); + } + + // Prune the UFO lib. + ufo.lib.as_mut().and_then(|lib| { + Some(lib.retain(|k, &mut _| { + k.starts_with("public.") + || k.starts_with("com.github.googlei18n.ufo2ft.") + || k == "com.schriftgestaltung.appVersion" + || k == "com.schriftgestaltung.fontMasterID" + || k == "com.schriftgestaltung.customParameter.GSFont.disablesLastChange" + || k == "com.schriftgestaltung.customParameter.GSFontMaster.paramArea" + || k == "com.schriftgestaltung.customParameter.GSFontMaster.paramDepth" + || k == "com.schriftgestaltung.customParameter.GSFontMaster.paramOver" + })) + }); + + ufo.meta.creator = "org.linebender.norad".to_string(); + match ufo.save(arg) { + Err(e) => { + eprintln!("Saving UFO failed: {}", e); + std::process::exit(1); + } + _ => {} + }; + } +} diff --git a/src/glyph/serialize.rs b/src/glyph/serialize.rs index afa0b640..3f460c61 100644 --- a/src/glyph/serialize.rs +++ b/src/glyph/serialize.rs @@ -71,7 +71,9 @@ impl Glyph { } if let Some(lib) = self.lib.as_ref() { - write_lib_section(lib, &mut writer)?; + if !lib.is_empty() { + write_lib_section(lib, &mut writer)?; + } } writer.write_event(Event::End(BytesEnd::borrowed(b"glyph")))?; diff --git a/src/layer.rs b/src/layer.rs index c4a52be1..3cef32b3 100644 --- a/src/layer.rs +++ b/src/layer.rs @@ -24,7 +24,7 @@ static LAYER_INFO_FILE: &str = "layerinfo.plist"; pub struct Layer { pub(crate) glyphs: BTreeMap>, contents: BTreeMap, - info: LayerInfo, + pub info: LayerInfo, } impl Layer { @@ -149,6 +149,11 @@ impl Layer { self.glyphs.values().map(Arc::clone) } + /// Iterate over the glyphs in this layer, mutably. + pub fn iter_contents_mut(&mut self) -> impl Iterator { + self.glyphs.values_mut().map(Arc::make_mut) + } + #[cfg(test)] pub fn get_path(&self, name: &str) -> Option<&Path> { self.contents.get(name).map(PathBuf::as_path)