Repository: flutter
Issue: Get cap height and x-height of font, maybe on `LineMetrics`?
Labels: c: new feature,engine,c: proposal,P3,team-engine,triaged-engine

### Use case

Currently, `LineMetrics` only provides the `baseline`, `ascent`, and `descent`. (The `ascent` seems to me the same as `baseline` in most cases, i.e. it goes from the `baseline` to the `top` of the line?)

**Problem:** From the given info, one is not able to access or determine the actual letter height of capital or lowercase letters, as defined by the font itself. This makes exactly aligning text at the top of a letter (at least according to the font's definition) extremely difficult.

Two **use cases** include: **drop cap letters**, which often are aligned at both the top of the drop cap and next letter as well as the baseline; and precisely aligning text in artwork or signage.

The article, [Text Rendering: What are the building blocks of a font?](https://flutter.megathink.com/text/text-rendering) suggests that: "A **cap line** marks the upper boundary of capital letters; the **mean line**, or **median**, serves the same purpose for lowercase letters. **Cap height** and **x-height** are measured from the baseline to each of these lines, respectively." The article also suggests that, "Placeholders may also be positioned relative to the font’s ascenders, descenders, or median," but does not offer any suggestion how to do so.

I just published a package, [paginated_text](https://pub.dev/packages/paginated_text), that **partly** works around this problem, but with a bad practice that doesn't work on anything but the simplest fonts. I achieve this by using `TextPainter` to paint a capital "Z" for a given font at a large size (e.g. 3600) and render it to an `Image` using `PictureRecorder` to capture a `Canvas`, get the bytes of the image, and find the first and last non-zero pixels in the mid vertical line of the image.

```dart
  Future<double> _computeLetterHeightRatio(CapFontData fontData) async {
    if (letterHeightRatioCache.containsKey(fontData)) {
      return letterHeightRatioCache[fontData]!;
    }

    final painter = TextPainter(
      text: TextSpan(
        text: 'Z',
        style: TextStyle(
          fontFamily: fontData.fontFamily,
          fontWeight: fontData.fontWeight,
          fontStyle: fontData.fontStyle,
          fontSize: letterHeightCalcFontSize, // 3600.0
        ),
      ),
      textDirection: widget.textDirection,
    )..layout();
    final recorder = PictureRecorder();
    final canvas = Canvas(recorder);
    painter.paint(canvas, Offset.zero);
    final picture = recorder.endRecording();
    final image =
        await picture.toImage(painter.width.ceil(), painter.height.ceil());
    final bytes = await image.toByteData(format: ImageByteFormat.rawRgba);
    if (bytes == null) {
      letterHeightRatioCache[fontData] = defaultLetterHeightRatio;
      return defaultLetterHeightRatio;
    }
    final x = (image.width / 2).round();
    final line = List<bool>.generate(image.height, (y) {
      // https://github.com/marcglasberg/image_pixels/blob/master/lib/src/image_pixels.dart#L194
      final offset = 4 * (x + (y * image.width));
      // https://github.com/marcglasberg/image_pixels/blob/master/lib/src/image_pixels.dart#214
      final pixel = bytes.getUint32(offset);
      return pixel > 0;
    });
    final top = line.indexWhere((y) => y);
    final baseline = line.lastIndexWhere((y) => y);
    final letterHeight = (baseline - top).toDouble();

    final ratio = letterHeight / letterHeightCalcFontSize;
    letterHeightRatioCache[fontData] = ratio;
    return ratio;
  }
```

Then, I can use the obtained `letterHeightRatio` to compute the supposed cap height of a letter from its baseline for a given font.

This seems excessive and unnecessary, not to mention **it fails on any font that is not symmetrical**, i.e. calligraphic, gothic, handwriting, non-text, etc. fonts.


### Proposal

I think (I don't know for sure) that Flutter may have access to this data in the engine. I don't know enough about it.

Would it be possible to expose the additional font metrics from the engine to the frontend in Flutter? If they are already exposed somewhere else, would it be possible to also expose them on `LineMetrics`? Is there another way to get this info?

I am hoping that `LineMetrics` could provide something like (I am not set on the names, those are from the above article):
```dart
class LineMetrics {
  // ...
  /// Provides the height of a capital letter as measured from the baseline to the upper boundary of capital letters according to the font's definition.
  final double capHeight;
  /// Provides the height of a lowercase letter as measured from the baseline to the upper boundary of lowercase letters according to the font's definition.
  final double xHeight;
  // ...
}
```

I wish to re-emphasize that these are **not** the same as `ascent` and `unscaledAscent` (and cannot seem to be obtained therefrom), as the article clearly states: "Ascenders extend **above** the cap line or the median, depending on capitalization" (emphasis mine).

I also found open issue #133930, but it seems to be regarding getting a count of line numbers, not the actual upper/lowercase letter heights of a font.

Thank you for considering.