capHeightShiftForTextStyle function
double
capHeightShiftForTextStyle(
- TextStyle style, {
- required TextScaler textScaler,
- required double devicePixelRatio,
- bool mixedCase = false,
Distance to move one line of style down so its capitals are centred in
its line box, rounded to a device pixel. Negative moves it up.
For text that a cap-centring text widget cannot draw, such as a TextField and its hint, so it lines up with an icon beside it. Measured from the loaded font, so it is right for whatever copy of the font the app has. Zero for fonts without a measured cap height.
mixedCase centres halfway between the capitals' centre and the
lowercase centre, where mixed-case text such as 'Enter web address' looks
centred. Matters most in fonts with tall lowercase, such as Tenali
Ramakrishna. Falls back to the capitals for fonts without a measured
x-height.
Implementation
double capHeightShiftForTextStyle(
TextStyle style, {
required TextScaler textScaler,
required double devicePixelRatio,
bool mixedCase = false,
}) {
final capHeightEm = rasterCapHeightEmForFontFamily(style.fontFamily);
if (capHeightEm == null) return 0;
final xHeightEm = mixedCase
? rasterXHeightEmForFontFamily(style.fontFamily)
: null;
// Centring a band of height h puts its centre at baseline - h / 2, so the
// average of the two heights centres the midpoint of the two centres.
final centredHeightEm = xHeightEm == null
? capHeightEm
: (capHeightEm + xHeightEm) / 2;
final painter = TextPainter(
text: TextSpan(text: 'H', style: style),
textDirection: TextDirection.ltr,
textScaler: textScaler,
maxLines: 1,
)..layout();
try {
final line = painter.computeLineMetrics().first;
// Glyphs are painted at the baseline rounded to a whole logical pixel.
final paintedBaseline =
(painter.computeDistanceToActualBaseline(TextBaseline.alphabetic) + 0.5)
.floorToDouble();
final shift = capHeightCentringShift(
ascent: line.unscaledAscent,
lineHeight: line.height,
baseline: paintedBaseline,
capHeight:
centredHeightEm *
textScaler.scale(style.fontSize ?? kDefaultFontSize),
);
return (shift * devicePixelRatio).roundToDouble() / devicePixelRatio;
} finally {
painter.dispose();
}
}