Skip to content

Commit

Permalink
[glyphs] Store codepoints in glyph struct
Browse files Browse the repository at this point in the history
Previously we were generating a separate cmap-like thing in the glyphs
module, which was then used to set the codepoints in the ir::Glyph. This
just keeps the codepoints in the glyphs Glyph, which will be nice
because it means that all the things that we use to determine the glyph
category will be in one place.
  • Loading branch information
cmyr committed Apr 16, 2024
1 parent 6a4248b commit 17c8289
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 47 deletions.
64 changes: 28 additions & 36 deletions glyphs-reader/src/font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ pub struct Font {
pub default_master_idx: usize,
pub glyphs: BTreeMap<SmolStr, Glyph>,
pub glyph_order: Vec<SmolStr>,
pub glyph_to_codepoints: BTreeMap<SmolStr, BTreeSet<u32>>,
// tag => (user:design) tuples
pub axis_mappings: RawUserToDesignMapping,
pub virtual_masters: Vec<BTreeMap<String, OrderedFloat<f64>>>,
Expand Down Expand Up @@ -174,6 +173,7 @@ pub struct Glyph {
pub glyphname: SmolStr,
pub export: bool,
pub layers: Vec<Layer>,
pub unicode: BTreeSet<u32>,
/// The left kerning group
pub left_kern: Option<SmolStr>,
/// The right kerning group
Expand Down Expand Up @@ -1334,21 +1334,11 @@ fn parse_glyph_order(raw_font: &RawFont) -> Vec<SmolStr> {
glyph_order
}

/// Returns a map from glyph name to codepoint(s).
fn parse_codepoints(raw_font: &mut RawFont, radix: u32) -> BTreeMap<SmolStr, BTreeSet<u32>> {
let mut name_to_cp: BTreeMap<SmolStr, BTreeSet<u32>> = BTreeMap::new();
raw_font
.glyphs
.iter()
.filter_map(|g| g.unicode.as_ref().map(|u| (&g.glyphname, u)))
.flat_map(|(g, u)| {
u.split(',')
.map(|cp| (g.clone(), i64::from_str_radix(cp, radix).unwrap() as u32))
})
.for_each(|(glyph_name, codepoint)| {
name_to_cp.entry(glyph_name).or_default().insert(codepoint);
});
name_to_cp
// glyphs2 uses hex, glyphs3 uses base10
fn parse_codepoint_str(s: &str, radix: u32) -> BTreeSet<u32> {
s.split(',')
.map(|cp| u32::from_str_radix(cp, radix).unwrap())
.collect()
}

/// <https://github.com/googlefonts/glyphsLib/blob/6f243c1f732ea1092717918d0328f3b5303ffe56/Lib/glyphsLib/builder/axes.py#L578>
Expand Down Expand Up @@ -1630,23 +1620,26 @@ impl TryFrom<RawLayer> for Layer {
}
}

impl TryFrom<RawGlyph> for Glyph {
type Error = Error;

fn try_from(from: RawGlyph) -> Result<Self, Self::Error> {
impl RawGlyph {
// we pass in the radix because it depends on the version, stored in the font struct
fn build(self, codepoint_radix: u32) -> Result<Glyph, Error> {
let mut instances = Vec::new();
for layer in from.layers {
for layer in self.layers {
if layer.is_draft() {
continue;
}
instances.push(layer.try_into()?);
}
Ok(Glyph {
glyphname: from.glyphname,
export: from.export.unwrap_or(true),
glyphname: self.glyphname,
export: self.export.unwrap_or(true),
layers: instances,
left_kern: from.kern_left,
right_kern: from.kern_right,
left_kern: self.kern_left,
right_kern: self.kern_right,
unicode: self
.unicode
.map(|s| parse_codepoint_str(&s, codepoint_radix))
.unwrap_or_default(),
})
}
}
Expand Down Expand Up @@ -1833,7 +1826,6 @@ impl TryFrom<RawFont> for Font {

let radix = if from.is_v2() { 16 } else { 10 };
let glyph_order = parse_glyph_order(&from);
let glyph_to_codepoints = parse_codepoints(&mut from, radix);

let use_typo_metrics = from.custom_parameters.bool("Use Typo Metrics");
let has_wws_names = from.custom_parameters.bool("Has WWS Names");
Expand All @@ -1850,7 +1842,7 @@ impl TryFrom<RawFont> for Font {

let mut glyphs = BTreeMap::new();
for raw_glyph in from.glyphs.into_iter() {
glyphs.insert(raw_glyph.glyphname.clone(), raw_glyph.try_into()?);
glyphs.insert(raw_glyph.glyphname.clone(), raw_glyph.build(radix)?);
}

let mut features = Vec::new();
Expand Down Expand Up @@ -1962,7 +1954,7 @@ impl TryFrom<RawFont> for Font {
default_master_idx,
glyphs,
glyph_order,
glyph_to_codepoints,
//glyph_to_codepoints,
axis_mappings,
virtual_masters,
features,
Expand Down Expand Up @@ -2351,8 +2343,8 @@ mod tests {
fn understand_v2_style_unquoted_hex_unicode() {
let font = Font::load(&glyphs2_dir().join("Unicode-UnquotedHex.glyphs")).unwrap();
assert_eq!(
&BTreeSet::from([0x1234]),
font.glyph_to_codepoints.get("name").unwrap(),
BTreeSet::from([0x1234]),
font.glyphs.get("name").unwrap().unicode,
);
assert_eq!(1, font.glyphs.len());
}
Expand All @@ -2361,8 +2353,8 @@ mod tests {
fn understand_v2_style_quoted_hex_unicode_sequence() {
let font = Font::load(&glyphs2_dir().join("Unicode-QuotedHexSequence.glyphs")).unwrap();
assert_eq!(
&BTreeSet::from([0x2044, 0x200D, 0x2215]),
font.glyph_to_codepoints.get("name").unwrap(),
BTreeSet::from([0x2044, 0x200D, 0x2215]),
font.glyphs.get("name").unwrap().unicode,
);
assert_eq!(1, font.glyphs.len());
}
Expand All @@ -2371,8 +2363,8 @@ mod tests {
fn understand_v3_style_unquoted_decimal_unicode() {
let font = Font::load(&glyphs3_dir().join("Unicode-UnquotedDec.glyphs")).unwrap();
assert_eq!(
&BTreeSet::from([182]),
font.glyph_to_codepoints.get("name").unwrap()
BTreeSet::from([182]),
font.glyphs.get("name").unwrap().unicode
);
assert_eq!(1, font.glyphs.len());
}
Expand All @@ -2381,8 +2373,8 @@ mod tests {
fn understand_v3_style_unquoted_decimal_unicode_sequence() {
let font = Font::load(&glyphs3_dir().join("Unicode-UnquotedDecSequence.glyphs")).unwrap();
assert_eq!(
&BTreeSet::from([1619, 1764]),
font.glyph_to_codepoints.get("name").unwrap(),
BTreeSet::from([1619, 1764]),
font.glyphs.get("name").unwrap().unicode,
);
assert_eq!(1, font.glyphs.len());
}
Expand Down
21 changes: 10 additions & 11 deletions glyphs2fontir/src/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ impl GlyphsIrSource {
default_master_idx: font.default_master_idx,
glyphs: Default::default(),
glyph_order: Default::default(),
glyph_to_codepoints: Default::default(),
axis_mappings: font.axis_mappings.clone(),
virtual_masters: Default::default(),
features: Default::default(),
Expand Down Expand Up @@ -123,7 +122,6 @@ impl GlyphsIrSource {
default_master_idx: font.default_master_idx,
glyphs: Default::default(),
glyph_order: Default::default(),
glyph_to_codepoints: Default::default(),
axis_mappings: Default::default(),
virtual_masters: Default::default(),
features: Default::default(),
Expand Down Expand Up @@ -837,14 +835,10 @@ impl Work<Context, WorkId, WorkError> for GlyphIrWork {
let mut ir_glyph = ir::GlyphBuilder::new(self.glyph_name.clone());
ir_glyph.emit_to_binary = glyph.export;

if let Some(codepoints) = font.glyph_to_codepoints.get(self.glyph_name.as_str()) {
codepoints.iter().for_each(|cp| {
ir_glyph.codepoints.insert(*cp);
});
}
ir_glyph.codepoints.extend(glyph.unicode.iter().copied());

// https://github.com/googlefonts/fontmake-rs/issues/285 glyphs non-spacing marks are 0-width
let zero_width = is_nonspacing_mark(&ir_glyph.codepoints, &ir_glyph.name);
let zero_width = is_nonspacing_mark(glyph);

let mut ir_anchors = AnchorBuilder::new(self.glyph_name.clone());

Expand Down Expand Up @@ -937,11 +931,16 @@ impl Work<Context, WorkId, WorkError> for GlyphIrWork {
// This will eventually need to be replaced with something that can handle
// custom GlyphData.xml files, as well as handle overrides that are part of the
// glyph source.
fn is_nonspacing_mark(codepoints: &HashSet<u32>, name: &GlyphName) -> bool {
fn is_nonspacing_mark(glyph: &glyphs_reader::Glyph) -> bool {
static GLYPH_DATA: OnceLock<GlyphData> = OnceLock::new();
let data = GLYPH_DATA.get_or_init(|| GlyphData::new(None).unwrap());
data.get_by_name(name)
.or_else(|| codepoints.iter().find_map(|cp| data.get_by_codepoint(*cp)))
data.get_by_name(&glyph.glyphname)
.or_else(|| {
glyph
.unicode
.iter()
.find_map(|cp| data.get_by_codepoint(*cp))
})
.map(|info| (info.is_nonspacing_mark()))
.unwrap_or(false)
}
Expand Down

0 comments on commit 17c8289

Please sign in to comment.