Skip to content

Instantly share code, notes, and snippets.

@davepagurek
Created September 25, 2025 12:10
Show Gist options
  • Select an option

  • Save davepagurek/29d3f7a9f93fe9d722792bb9a6da0393 to your computer and use it in GitHub Desktop.

Select an option

Save davepagurek/29d3f7a9f93fe9d722792bb9a6da0393 to your computer and use it in GitHub Desktop.
const verticalAlign = (font, fontSize) => {
const {
font: {
// the height of the tallest character above the baseline
ascender,
// the most negative height below the baseline that any character dips
// down to (think of characters like j)
descender,
unitsPerEm,
tables: {
os2: { sCapHeight },
},
},
} = font
let averageCharacterDescent
let averageCharacterAscent
if (sCapHeight) {
// Some fonts might contain `sCapHeight`, which is a hand-picked estimate
// of the average capital letter's height above the baseline (estimate
// because this is not an exact average or anything, the font designer
// picked it.)
//
// We'll approximate that most letters do not go below the baseline at all.
averageCharacterAscent = sCapHeight
averageCharacterDescent = 0
} else {
// If the font designer didn't provide us with an average, we can try to
// approximate it differently by just using the ascender. Probably the
// average character isn't quite as tall as the ascender, which is the max,
// but it's good enough to handle the edge case where we aren't given a
// better estimage from the font.
//
// Since we know the ascender will be an overestimate, we'll pretend that
// the average character also dips as far down as the lowest character,
// which is an overestimate in the opposite direction. Hopefully those
// cancel each other out.
averageCharacterAscent = ascender
averageCharacterDescent = descender
}
const averageCharacterHeight =
averageCharacterAscent + averageCharacterDescent
// When vertically centering text at a y value, p5 makes the y value be
// halfway through the ascender.
const defaultOffset = ascender / 2
// When we have a different estimate of average height, we want to be halfway
// through that instead
const targetOffset = averageCharacterHeight / 2
const offsetDifference = targetOffset - defaultOffset
// Convert internal font metric units to size on screen
const scale = fontSize / unitsPerEm
return scale * offsetDifference
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment