Repository: flutter
Issue: Engine Paragraph Painter Renders Whitespace-only Lines Incorrectly
Labels: engine,a: typography,has reproducible steps,P2,found in release: 3.0,found in release: 3.1,team-engine,triaged-engine

In short, the Flutter engine uses the `ParagraphStyle` to compute the line metrics of lines that contain only whitespace, even when a `TextStyle` with a non-null `fontSize` different from the `ParagraphStyle.fontSize` has been pushed to the style stack.

I would expect that when there is a style on the stack, the engine would use that style first to compute the line metrics, and then fall back on the `ParagraphStyle` if the required style values aren't present.  Updating this behavior would match the behavior of most modern text renderers.

This issue primarily affects the `RichText` and `Text.rich` widgets in Flutter, since these are the only text widgets that can modify the `TextStyle` mid-paragraph.

Here is the engine code where the problem arises: https://github.com/flutter/engine/blob/main/third_party/txt/src/txt/paragraph_txt.cc#L1132-L1142

I've included the code sample as a reproducible visual example of the engine issue. See the attached screenshot for the result of running that code sample. Note the differences in line height between the two sets of text on the whitespace-only lines. The bottom set of text matches the expected behavior, but only because we printed actual text on the "whitespace-only" lines.

## Steps to Reproduce

<!-- Please include full steps to reproduce so that we can reproduce the problem. -->

1. Execute `flutter run` on the code sample <!-- (see "Code sample" section below) -->
2. See the difference in the whitespace-only lines between the top and the bottom lines of text in the paragraph
3. The bottom run of text has the expected fontSize for the 'newline' characters, but only because we actually painted text on each line

**Expected results:** <!-- what did you want to see? -->
The paragraph painter ought to paint the whitespace-only lines with line metrics calculated from the topmost `fontSize` and typeface in the style stack, falling back on the `ParagraphStyle` if the value doesn't exist.

**Actual results:** <!-- what did you see? -->
The paragraph painter uses the `ParagraphStyle` to calculate the line metrics of whitespace-only lines.

<details>
<summary>Code sample</summary>

```dart
import 'dart:ui' as ui;

import 'package:flutter/material.dart';

void main() => runApp(DemoApp());

class DemoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SingleChildScrollView(
        child: Container(
          width: double.infinity,
          height: 1000,
          child: CustomPaint(painter: ParagraphPainter()),
        ),
      ),
    );
  }

}

class ParagraphPainter extends CustomPainter {
  final blackPaint = Paint()..color = Colors.black;

  @override
  void paint(Canvas canvas, Size size) {

    //Just use the default
    final paragraphStyle = ui.ParagraphStyle();

    final paragraphBuilder = ui.ParagraphBuilder(paragraphStyle);
    buildParagraph(paragraphBuilder, Size(size.width, size.height));

    final paragraph = paragraphBuilder.build();
    paragraph.layout(ui.ParagraphConstraints(
      width: size.width,
    ));

    //Draw a black background so we can see the text
    canvas.drawRect(
      Rect.fromLTWH(0, 0, paragraph.width, paragraph.height),
      blackPaint,
    );

    canvas.drawParagraph(paragraph, Offset.zero);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;

  void buildParagraph(ui.ParagraphBuilder builder, Size constraints) {
    final mediumStyle = ui.TextStyle(
      fontSize: 25,
    );

    final largeStyle = ui.TextStyle(
      fontSize: 50,
    );

    //Starting a new run:
    builder.addText("Before any styling\n");
    builder.pushStyle(mediumStyle);
    builder.addText("After medium text style pushed\n");
    builder.addText('\n\n');
    builder.addText("↑ 2 'empty' lines\n");
    builder.pushStyle(largeStyle);
    builder.addText("After large text style pushed\n");
    builder.addText('\n\n');
    builder.addText("↑ 2 'empty' lines\n");
    builder.pop();
    builder.addText("After large text style popped\n");
    builder.pop();
    builder.addText("After all styles popped\n");
    //End of example

    builder.addText('\n\n');
    builder.addText("Again, but with text added with every newline:");

    //Now, do the same, but with a little bit of text added to each previously blank line
    builder.addText("Before any styling\\n\n");
    builder.pushStyle(mediumStyle);
    builder.addText("After medium text style pushed\\n\n");
    builder.addText('\\n\n\\n\n');
    builder.addText("↑ 2 'empty' lines\\n\n");
    builder.pushStyle(largeStyle);
    builder.addText("After large text style pushed\\n\n");
    builder.addText('\\n\n\\n\n');
    builder.addText("↑ 2 'empty' lines\\n\n");
    builder.pop();
    builder.addText("After large text style popped\\n\n");
    builder.pop();
    builder.addText("After all styles popped");
    //End of example
  }
}
```

</details>

<details>
  <summary>Logs</summary>

<!--
      Run your application with `flutter run --verbose` and attach all the
      log output below between the lines with the backticks. If there is an
      exception, please see if the error message includes enough information
      to explain how to solve the issue. If there is a crash, please include
      the entirety of the crash log including the backtrace.
-->

```
[+1454 ms] Observatory URL on device: http://127.0.0.1:58005/-dHL07jtyt8=/
[   +7 ms] Caching compiled dill
[  +40 ms] Connecting to service protocol: http://127.0.0.1:58005/-dHL07jtyt8=/
[ +217 ms] Launching a Dart Developer Service (DDS) instance at http://127.0.0.1:0, connecting to VM service at
http://127.0.0.1:58005/-dHL07jtyt8=/.
[  +91 ms] DDS is listening at http://127.0.0.1:58008/CM_nlgW0H10=/.
[  +68 ms] Successfully connected to service protocol: http://127.0.0.1:58005/-dHL07jtyt8=/
[  +32 ms] DevFS: Creating new filesystem on the device (null)
[  +21 ms] DevFS: Created new filesystem on the device
(file:///var/folders/wm/z0n892ps46x71lsw0ht0_qt40000gn/T/com.sub6resources.flutterHtmlDemo/flutter_html_demowakU5L/flutter_html_demo/)
[   +3 ms] Updating assets
[ +131 ms] Syncing files to device macOS...
[   +2 ms] <- reset
[        ] Compiling dart to kernel with 0 updated files
[        ] Processing bundle.
[   +1 ms] <- recompile package:flutter_html_demo/html_painter.dart 3f3fec39-4357-43eb-875a-f8150a6be8a4
[        ] <- 3f3fec39-4357-43eb-875a-f8150a6be8a4
[   +3 ms] Bundle processing done.
[ +128 ms] Updating files.
[        ] DevFS: Sync finished
[   +2 ms] Syncing files to device macOS... (completed in 136ms)
[        ] Synced 0.0MB.
[   +2 ms] <- accept
[   +3 ms] Connected to _flutterView/0x7fb247054020.
[   +4 ms] Flutter run key commands.
[   +2 ms] r Hot reload. 🔥🔥🔥
[   +1 ms] R Hot restart.
[        ] h List all available interactive commands.
[        ] d Detach (terminate "flutter run" but leave application running).
[        ] c Clear the screen
[        ] q Quit (terminate the application on the device).
[        ] 💪 Running with sound null safety 💪
[   +1 ms] An Observatory debugger and profiler on macOS is available at: http://127.0.0.1:58008/CM_nlgW0H10=/
[ +537 ms] The Flutter DevTools debugger and profiler on macOS is available at:
http://127.0.0.1:9100?uri=http://127.0.0.1:58008/CM_nlgW0H10=/
[+9326 ms] Service protocol connection closed.
[        ] Lost connection to device.
[   +4 ms] DevFS: Deleting filesystem on the device
(file:///var/folders/wm/z0n892ps46x71lsw0ht0_qt40000gn/T/com.sub6resources.flutterHtmlDemo/flutter_html_demowakU5L/flutter_html_demo/)
[ +255 ms] Ignored error while cleaning up DevFS: TimeoutException after 0:00:00.250000: Future not completed
[   +5 ms] "flutter run" took 32,670ms.
[ +255 ms] ensureAnalyticsSent: 252ms
[   +1 ms] Running shutdown hooks
[        ] Shutdown hooks complete
[        ] exiting with code 0
```

<!--
     Run `flutter analyze` and attach any output of that command below.
     If there are any analysis errors, try resolving them before filing this issue.
-->

```
Analyzing paragraph_painter.dart...                                          
No issues found! (ran in 1.7s)
```

<!-- Finally, paste the output of running `flutter doctor -v` here. -->

```
[✓] Flutter (Channel stable, 3.0.5, on macOS 12.5 21G72 darwin-x64, locale en-US)
    • Flutter version 3.0.5 at /Users/matthewwhitaker/Projects/FlutterDevelopment/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision f1875d570e (5 weeks ago), 2022-07-13 11:24:16 -0700
    • Engine revision e85ea0e79c
    • Dart version 2.17.6
    • DevTools version 2.12.2

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0-rc4)
    • Android SDK at /Users/matthewwhitaker/Library/Android/sdk
    • Platform android-31, build-tools 33.0.0-rc4
    • Java binary at: /Users/matthewwhitaker/Library/Application
      Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/212.5712.43.2112.8609683/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 13.4.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • CocoaPods version 1.11.3

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2021.2)
    • Android Studio at /Users/matthewwhitaker/Library/Application
      Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/212.5712.43.2112.8609683/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)

[✓] Android Studio (version 3.6)
    • Android Studio at /Users/matthewwhitaker/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/192.6392135/Android
      Studio.app/Contents
    • Flutter plugin version 45.1.1
    • Dart plugin version 192.7761
    • Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)

[✓] IntelliJ IDEA Ultimate Edition (version 2022.1.4)
    • IntelliJ at /Users/matthewwhitaker/Applications/JetBrains Toolbox/IntelliJ IDEA Ultimate.app
    • Flutter plugin version 69.0.4
    • Dart plugin version 221.6008.17

[✓] IntelliJ IDEA Ultimate Edition (version 2022.1.4)
    • IntelliJ at /Users/matthewwhitaker/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/221.6008.13/IntelliJ IDEA.app
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart

[✓] IntelliJ IDEA Ultimate Edition (version 2022.1.3)
    • IntelliJ at /Users/matthewwhitaker/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/221.5921.22/IntelliJ IDEA.app
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart

[✓] Connected device (2 available)
    • macOS (desktop) • macos  • darwin-x64     • macOS 12.5 21G72 darwin-x64
    • Chrome (web)    • chrome • web-javascript • Google Chrome 104.0.5112.101

[✓] HTTP Host Availability
    • All required HTTP hosts are available

• No issues found!
```

</details>

Here is what the included code sample looks like upon rendering:
<!--
      Consider also attaching screenshots and/or videos to better
      illustrate the issue.

      You can upload them directly on GitHub.
      Beware that video file size is limited to 10MB.
-->
<img width="1025" alt="Screen Shot 2022-08-20 at 9 43 37 AM" src="https://user-images.githubusercontent.com/19274761/185755649-05087a50-ef6a-44f2-8e2e-bd1db4fc50cf.png">

Let me know if you have any questions! I would like to submit a pull request soon, but don't currently have the time to get my machine set back up for development on the engine.