Repository: flutter
Issue: LineMetrics width give wrong value only with Material Text Widget
Labels: a: internationalization,a: typography,has reproducible steps,P2,team-engine,triaged-engine,found in release: 3.13,found in release: 3.14

### Is there an existing issue for this?

- [X] I have searched the [existing issues](https://github.com/flutter/flutter/issues)
- [X] I have read the [guide to filing a bug](https://flutter.dev/docs/resources/bug-reports)

### Steps to reproduce

```dart
final cjkText = 'CJK 中國語 にほんご 한국어';

// 1. Create TextPainter for LineMetrics
final tp = TextPainter(
        text: TextSpan(cjkText),
        textDirection: TextDirection.ltr,
        maxLines: 999,
      )..layout(minWidth: 0, maxWidth: double.infinity);

// 2. Get LineMetrics
final lines = tp.computeLineMetrics();

// 3. Compare with actual rendered text
Stack(
  children: [
    ...lines.map((line) {
      return Positioned(
        child: CustomPaint(
          painter: _LinePainter(),
          size: Size(line.width, line.height * (line.lineNumber + 1)),
        ),
      );
    }),
    Text(cjkText),
  ]
);

```

### Expected results

Same as renderd length.

### Actual results

Longer or shorter than actual.

### Code sample

### Working Sample
Below link is WEB, but IOS & Android is also presented.

https://dartpad.dev/?id=9d5cf92e2c390e0ac16e9aa7373e55ac

<details><summary>Code sample</summary>

```dart
// This code is distributed under the MIT License.
// Copyright (c) 2019 Remi Rousselet.
// You can find the original at https://github.com/rrousselGit/provider.

import 'package:flutter/material.dart';

// This is a reimplementation of the default Flutter application
// using provider + [ChangeNotifier].
void main() {
  runApp(
    // Providers are above [MyApp] instead of inside it, so that
    // tests can use [MyApp] while mocking the providers
    const MyApp(),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  TextPainter _textPainter(String text, TextStyle style,
      {double maxWidth = double.infinity}) {
    final tp = TextPainter(
      text: TextSpan(text: text, style: style),
      textDirection: TextDirection.ltr,
      maxLines: 999,
    )..layout(minWidth: 0, maxWidth: maxWidth);
    return tp;
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final textTheme = theme.textTheme;
    final style = textTheme.displayMedium;
    final underlineStyle = style!.apply(
      decoration: TextDecoration.underline,
    );
    final bgStyle = style.apply(
      backgroundColor: Colors.yellow,
    );
    const ko = '한글 사이에 공 백 이나english있으면 그리고1숫2자도 마찮가지 플 러 터 this lo사lo v랑ev해e';
    const jp = '日本語の間に空白や英語や数2字3が4混 ざ っ て いても、私はあloなloたをve愛しveています。';
    const zn = '好的，以下是你的句子，其中有空格、英文和数字，用2中23文4翻222译：「漢 字 之 間 有 空格、英文和數字，我lo愛ve你！」';
    const en =
        "asdjoaiystdfoaystdfoaystdfoasdt asodiyftaosidytf asodiyftaosid fasd asidyftaosidy aosdyt faoisdytfao";

    return Scaffold(
      appBar: AppBar(
        title: const Text(''),
      ),
      body: LayoutBuilder(
        builder: (context, constraints) {
          final tp = _textPainter(ko, underlineStyle);
          tp.layout(minWidth: 0, maxWidth: constraints.maxWidth);

          final lines = tp.computeLineMetrics();
          lines.map((x) => print(x));

          return SingleChildScrollView(
            child: Column(
              children: <Widget>[
                Text(lines.toString()),
                Text(
                  ko,
                  style: underlineStyle,
                ),
                Text(
                  ko,
                  style: bgStyle,
                ),
                const SizedBox(height: 32),
                Text('With CJK'),
                AppLinedText(
                  text: ko,
                  style: style,
                ),
                AppLinedText(
                  text: jp,
                  style: style,
                ),
                AppLinedText(
                  text: zn,
                  style: style,
                ),
                const SizedBox(height: 8),
                Text('Only Western Alphabet'),
                AppLinedText(
                  text: en,
                  style: style,
                ),
                const SizedBox(height: 32),
              ],
            ),
          );
        },
      ),
    );
  }
}

class AppLinedText extends StatelessWidget {
  final double underlineSpacing;
  final String text;
  final TextStyle style;

  final int maxLines = 999;

  const AppLinedText({
    super.key,
    this.underlineSpacing = 0,
    required this.text,
    required this.style,
  });

  TextPainter _textPainter({double maxWidth = double.infinity}) {
    final tp = TextPainter(
      text: TextSpan(text: text, style: style),
      textDirection: TextDirection.ltr,
      maxLines: maxLines,
    )..layout(minWidth: 0, maxWidth: maxWidth);
    return tp;
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(builder: (context, constraints) {
      final tp = _textPainter(maxWidth: constraints.maxWidth);
      final canvasSize = tp.size;
      final lines = tp.computeLineMetrics()
        ..sort((a, b) => a.lineNumber.compareTo(b.lineNumber));

      return SizedBox(
        width: canvasSize.width * 1.05,
        height: canvasSize.height,
        child: Stack(
          children: [
            ...lines.map(
              (x) {
                final index = x.lineNumber + 1;
                final mWidth =
                    canvasSize.width < x.width ? canvasSize.width : x.width;

                return Positioned(
                  child: CustomPaint(
                    painter: _LinePainter(),
                    size: Size(
                      mWidth,
                      (x.height +
                              (underlineSpacing < 0 ? 0 : underlineSpacing)) *
                          index,
                    ),
                  ),
                );
              },
            ),
            Text(
              text,
              style: style,
              maxLines: maxLines,
            ),
          ],
        ),
      );
    });
  }
}

class _LinePainter extends CustomPainter {
  final Color? color;

  _LinePainter({this.color});

  @override
  void paint(Canvas canvas, Size size) {
    final startPoint = Offset(0, size.height);
    final endPoint = Offset(size.width, size.height);
    final mColor = color ?? Colors.red;

    Paint paint = Paint()
      ..color = mColor
      ..strokeWidth = 8
      ..strokeCap = StrokeCap.square;

    canvas.drawLine(startPoint, endPoint, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}
```

</details>


### Screenshots or Video

- Red line is comes from LineMetrics.width
- Westen character's line width is same with actual rendered 
- CJK is wrong

<details>
<summary>Screenshots / Video demonstration</summary>

<img width="718" alt="image" src="https://github.com/flutter/flutter/assets/474775/ccbde0e0-9f49-4ec5-9b76-0ca0976932a5">

</details>


### Logs

<details><summary>Logs</summary>

```console
[Paste your logs here]
```

</details>


### Flutter Doctor output

<details><summary>Doctor output</summary>

```console
[Paste your output here]
```

</details>