diff --git a/src/glyph/parse.rs b/src/glyph/parse.rs index 90264529..42f8641c 100644 --- a/src/glyph/parse.rs +++ b/src/glyph/parse.rs @@ -122,6 +122,12 @@ impl<'names> GlifParser<'names> { } self.glyph.load_object_libs()?; + + let mut seen_codepoints = HashSet::new(); + let deduplicated_codepoints = + self.glyph.codepoints.iter().filter(|c| seen_codepoints.insert(**c)).cloned().collect(); + self.glyph.codepoints = deduplicated_codepoints; + Ok(self.glyph) } diff --git a/src/glyph/serialize.rs b/src/glyph/serialize.rs index e64de211..6e09b6ad 100644 --- a/src/glyph/serialize.rs +++ b/src/glyph/serialize.rs @@ -1,6 +1,9 @@ //! Writing out .glif files -use std::io::{Cursor, Write}; +use std::{ + collections::HashSet, + io::{Cursor, Write}, +}; use quick_xml::{ events::{BytesEnd, BytesStart, BytesText, Event}, @@ -59,8 +62,11 @@ impl Glyph { start.push_attribute(("format", "2")); writer.write_event(Event::Start(start)).map_err(GlifWriteError::Xml)?; + let mut seen_codepoints = HashSet::new(); for codepoint in &self.codepoints { - writer.write_event(char_to_event(*codepoint)).map_err(GlifWriteError::Xml)?; + if seen_codepoints.insert(codepoint) { + writer.write_event(char_to_event(*codepoint)).map_err(GlifWriteError::Xml)?; + } } // Skip serializing advance if both values are zero, infinite, subnormal, or NaN. diff --git a/src/glyph/tests.rs b/src/glyph/tests.rs index efa23d57..e7e1b177 100644 --- a/src/glyph/tests.rs +++ b/src/glyph/tests.rs @@ -868,3 +868,30 @@ fn has_component_with_base() { assert!(glyph.has_component_with_base("dieresis")); assert!(!glyph.has_component_with_base("Z")); } + +#[test] +fn deduplicate_unicodes() { + let data = r#" + + + + + + + +"#; + let mut glyph = parse_glyph(data.as_bytes()).unwrap(); + assert_eq!(glyph.codepoints, vec!['e', 'f', 'g']); + + glyph.codepoints = vec!['e', 'f', 'e', 'g']; + let data2 = glyph.encode_xml().unwrap(); + let data2 = std::str::from_utf8(&data2).unwrap(); + let data2_expected = r#" + + + + + +"#; + assert_eq!(data2, data2_expected); +}