Repository: flutter
Issue: Align `TextPainter` precisely at the top
Labels: framework,a: typography,has reproducible steps,P3,found in release: 3.3,found in release: 3.7,team-framework,triaged-framework

Hey, I'm trying to align `TextPainter` precisely on a line.
I can easily align the bottom via `textPainter.computeLineMetrics().single.baseline`:
<img src="https://user-images.githubusercontent.com/1841944/205718586-7271c424-7b3b-402a-a9b9-bc79b96f0452.png">

however I can't find a reliable way to do the same for the top part (= capital letter's top) precisely:
<img src="https://user-images.githubusercontent.com/1841944/205719713-da9bbfd8-98b1-4565-b0eb-687c9a719181.png">

The diff seems linear so I can tweak it by adding a fraction of the `fontSize` until I get a straight line, but would prefer something less guessy:
<img src="https://user-images.githubusercontent.com/1841944/205720008-797d31f8-26c0-4ec3-bf7f-418ef6eafaa3.png">
produced by:
```dart
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) => const MaterialApp(home: Scaffold(body: MyStatelessWidget()));
}

class MyStatelessWidget extends StatelessWidget {
  const MyStatelessWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) => InteractiveViewer(
        minScale: 0.1,
        maxScale: 10.0,
        child: Row(children: [
          CustomPaint(
            size: MediaQuery.of(context).size,
            painter: Painter(),
          ),
        ]),
      );
}

class Painter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    var columnCount = 10;
    var minSize = 15;
    var maxSize = 200;

    const targetY = 50.0;
    canvas.drawLine(const Offset(0, targetY), Offset(size.width, targetY), Paint());

    draw(targetY, canvas, size, columnCount, minSize, maxSize, (metrics, _) {
      return -metrics.descent;
    });

    draw(targetY + 100, canvas, size, columnCount, minSize, maxSize, (metrics, fontSize) {
      var tweak = fontSize / 25;
      return -metrics.descent + tweak;
    });

    draw(targetY + 200, canvas, size, columnCount, minSize, maxSize, (metrics, fontSize) {
      return -metrics.baseline + fontSize / 1.56;
    });

    draw(targetY + 300, canvas, size, columnCount, minSize, maxSize, (_, fontSize) {
      return -fontSize / 3.8;
    });

    draw(targetY + 400, canvas, size, columnCount, minSize, maxSize, (metrics, fontSize) {
      return -metrics.height / 4.6;
    });
  }

  void draw(double targetY, Canvas canvas, Size size, int columnCount, int minSize, int maxSize, Function(LineMetrics, double) dy) {
    canvas.drawLine(Offset(0, targetY), Offset(size.width, targetY), Paint());

    var step = size.width / columnCount;
    for (var i = 1; i < columnCount - 1; ++i) {
      var fontSize = minSize + i * (maxSize / minSize);

      var textPainter = TextPainter(
        text: TextSpan(text: "X", style: GoogleFonts.alegreyaSans(fontSize: fontSize, textBaseline: TextBaseline.alphabetic)),
        textDirection: TextDirection.ltr,
        maxLines: 1,
        textAlign: TextAlign.left,
      )..layout();
      var lineMetrics = textPainter.computeLineMetrics().single;
      textPainter.paint(canvas, Offset(i * step, targetY + dy(lineMetrics, fontSize)));
    }
  }

  @override
  bool shouldRepaint(_) => true;
}
```

------

Happens for default font as well, it's just more obvious with this one.
It may be the case that the baseline is also misaligned sometimes (in Chrome at least).