Streamline vertical metrics and variations
Summary
Currently it's cumbersome to get vertical metrics (advance & v_origin) from Skrifa, for reasons I explain:
Whereas in https://github.com/googlefonts/fontations/pull/1530 Simon fixed the GlyphMetrics object to return correct glyph extents even in presence of variations, there is currently no API to do the same for vertical metrics, and as I'll demonstrate, it's either not possible, or inefficient, to do so using existing API.
The solution to both seems to be to expose (varied) phantom-points from read-fonts, and to wrap them in higher-level API in skrifa.
Advance height
Currently hb-fontations has this code in the get_glyph_v_advances callback:
if let Some(vert_metrics) = &data.vert_metrics {
let vert_vars = &data.vert_vars;
for i in 0..count {
let glyph = struct_at_offset(first_glyph, i, glyph_stride);
let glyph_id = GlyphId::new(glyph);
let mut advance = vert_metrics.advance(glyph_id).unwrap_or_default() as f32;
if let Some(vert_vars) = vert_vars {
let coords = data.location.coords();
if !coords.is_empty() {
advance += vert_vars
.advance_height_delta(glyph_id, coords)
.unwrap_or_default()
.to_f32();
}
}
let scaled = -(advance * data.y_mult).round() as hb_position_t;
*struct_at_offset_mut(first_advance, i, advance_stride) = scaled;
}
}
Unlike GlyphMetrics, that has variations set on it, vert_metrics here is plain vmtx table, which has no idea of variations, so variations need to be applied manually.
The code has a branch for when vert_vars (ie. VVAR table) is present, but doesn't have an else block. In the else block, one would need to get advance_height from gvar phantom-points. I see read-fonts provides phantom_point_deltas, which takes glyf and loca tables. I'll go ahead and implement that (and report).
The whole shtick probably belongs to an skrifa object akin to GlyphMetrics.
Vertical origin
The get_glyph_v_origin callback is responsible to return the Y value of the vertical origin of the glyph. The X value for the vertical origin is always set as half of the advance-width.
The hb-ot-font callbacks do this in three different ways. Pseudocode:
- If
VORGis present, use it, - If
vmtxis present (ie. we have top-side bearing), reconstruct vertical origin by adding y-max of the glyph shape and the top-side bearing and use it, - If glyph extents are available, use them to center the glyph vertically in the font's horizontal ascent/descent space and use that,
- Otherwise, synthesize something based on font's horizontal ascent/descent independent of the glyph.
The problem is in step 2, and when variations get involved. y-max of glyph shape in all implementations now, supports variations. However, variations for top-side bearing are harder to compute. If VVAR table is present AND its tsb-vars member is also non-null, then we have tsb variations and can proceed. Otherwise, that path is a deadend.
However, it happens to be the case that for glyf/gvar fonts, the top phantom-point is exactly where the v-origin is, so no need to construct it from extents & tsb. The extents code is in fact already processing all glyph variations, whereas getting the phantom-points only can be implemented faster.
Without a dedicated API for phantom points, we need to get unvaried glyph extents, unvaried tsb, and phantom-points variations. This is more work, since we don't have a GlyphMetrics object to get unvaried glyph extents.
Conclusions
Adding skrifa API (ideally, to GlyphMetrics), that can compute full, varied, phantom-points, will assist in advance-height & v-origin calculations. I suggest adding those APIs as well while at it, and mark them as working only for glyf fonts, and return None otherwise. Or go all the way and implement the full logic, which would match HarfBuzz, and FreeType. [0]
Footnotes
[0] It appears to me that FreeType does NOT support VORG table.
Note: this has some overlap with https://github.com/googlefonts/fontations/pull/1552 cc @valadaptive
cc https://github.com/harfbuzz/harfbuzz/issues/5388
Note that HarfRust also has some code dealing with these. Would be nice if some of it, the part NOT requiring glyph extents, could be unified in read-fonts perhaps.
https://github.com/harfbuzz/harfrust/blob/4db52a106c42aae8faea745ad41e369e3bd6af98/src/hb/glyph_metrics.rs#L119-L219