capHeightShiftForTextStyle function

double capHeightShiftForTextStyle(
  1. TextStyle style, {
  2. required TextScaler textScaler,
  3. required double devicePixelRatio,
  4. 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();
  }
}