flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
164.99k stars 27.19k forks source link

Emoji display problem in flutter web html render #131337

Open zjrwdl opened 1 year ago

zjrwdl commented 1 year ago

Is there an existing issue for this?

Steps to reproduce

1、flutter create emoji_sample 2、add code below: Text( '🤔Can you solve the quiz?💰 🤖#AI got puzzled! 🎁\$30 for 5 each!To win:1️⃣ FL &❤️ & RT @binary_x2⃣ Fill in the blank👇 & tag 2 friends!'), 3、flutter run -d chrome --web-renderer html

image

As shown in the picture, the emoji 1️⃣ in the red frame is not correct. 1️⃣ equals \u{0031}\u{FE0F}\u{20E3} This emoji is actually composed of two characters: the number "1" (U+0031) and a variable selector (U+FE0F), followed by a combined closed keyboard selector (U+20E3)

企业微信截图_bb9811e8-8bdc-4822-8147-830882c8c553

4、if run with --web-renderer canvaskit and set useColorEmoji: true when inital engine, the result is right.

image

List of known issues emoji in flutter web html rendering:

The Unicode encoding for 1️⃣ is: U+0031 (1) + U+FE0F (️ Variation Selector-16, VS-16). The Unicode encoding for 2⃣ is: U+0032 (2) + U+20E3 (⃣ Combining Enclosing Keycap).

@ditman

Expected results

image

Actual results

image

Code sample

Code sample ```dart Text( '🤔Can you solve the quiz?💰 🤖#AI got puzzled! 🎁\$30 for 5 each!To win:1️⃣ FL &❤️ & RT @binary_x2⃣ Fill in the blank👇 & tag 2 friends!'), ```

Screenshots or Video

Screenshots / Video demonstration [Upload media here]

Logs

Logs ```console [Paste your logs here] ```

Flutter Doctor output

Doctor output ```console [✓] Flutter (Channel stable, 3.10.2, on macOS 11.6 20G165 darwin-x64, locale zh-Hans-CN) [✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0) [✓] Xcode - develop for iOS and macOS (Xcode 13.1) [✓] Chrome - develop for the web [✓] Android Studio (version 2022.1) [✓] Connected device (2 available) ```
naveenpardeep commented 1 year ago

please add in web index.html

return engineInitializer.initializeEngine({

      useColorEmoji: true,
    });
  }).then(function (appRunner) {
    return appRunner.runApp();
zjrwdl commented 1 year ago

please add in web index.html

return engineInitializer.initializeEngine({

      useColorEmoji: true,
    });
  }).then(function (appRunner) {
    return appRunner.runApp();

useColorEmoji: true is only supported in canvaskit mode .

企业微信截图_6b516eb4-ca89-4dc0-9a1d-1aee03c4930e
darshankawar commented 1 year ago

Verified this behavior using latest stable and master version and observed that the reported behavior persists using html renderer as below, ie, the emojis are rendered properly as-is with color but overlaps:

Screenshot 2023-07-27 at 12 18 56 PM

Using canvaskit, although the emojis are not rendered as-is (color) but are not overlapped, but as below:

Screenshot 2023-07-27 at 12 18 08 PM

The reported behavior doesn't occur on desktop and mobile.

Screenshot 2023-07-27 at 12 31 54 PM

Looks like how html renders unicode chars as mentioned in original description. There may have been similar / reported issues with emojis not rendering properly (ie unicode chars) on web, but I couldn't find an exact issue if this is a known one already.

stable, master flutter doctor -v ``` [!] Flutter (Channel stable, 3.10.6, on macOS 12.2.1 21D62 darwin-x64, locale en-GB) • Flutter version 3.10.6 on channel stable at /Users/dhs/documents/fluttersdk/flutter ! Warning: `flutter` on your path resolves to /Users/dhs/Documents/Fluttersdk/flutter/bin/flutter, which is not inside your current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter. Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path. ! Warning: `dart` on your path resolves to /Users/dhs/Documents/Fluttersdk/flutter/bin/dart, which is not inside your current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter. Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path. • Upstream repository https://github.com/flutter/flutter.git • Framework revision f468f3366c (5 days ago), 2023-07-12 15:19:05 -0700 • Engine revision cdbeda788a • Dart version 3.0.6 • DevTools version 2.23.1 • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades. [!] Xcode - develop for iOS and macOS (Xcode 12.3) • Xcode at /Applications/Xcode.app/Contents/Developer ! Flutter recommends a minimum Xcode version of 13. Download the latest version or update via the Mac App Store. • CocoaPods version 1.11.2 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] VS Code (version 1.62.0) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.21.0 [✓] Connected device (5 available) • SM G975F (mobile) • RZ8M802WY0X • android-arm64 • Android 11 (API 30) • Darshan's iphone (mobile) • 21150b119064aecc249dfcfe05e259197461ce23 • ios • iOS 14.4.1 18D61 • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729 • ios • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator) • macOS (desktop) • macos • darwin-x64 • Mac OS X 10.15.4 19E2269 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 98.0.4758.80 [✓] HTTP Host Availability • All required HTTP hosts are available ! Doctor found issues in 1 category. [!] Flutter (Channel master, 3.13.0-10.0.pre.45, on macOS 12.2.1 21D62 darwin-x64, locale en-GB) • Flutter version 3.13.0-10.0.pre.45 on channel master at /Users/dhs/documents/fluttersdk/flutter ! Warning: `flutter` on your path resolves to /Users/dhs/Documents/Fluttersdk/flutter/bin/flutter, which is not inside your current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter. Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path. ! Warning: `dart` on your path resolves to /Users/dhs/Documents/Fluttersdk/flutter/bin/dart, which is not inside your current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter. Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path. • Upstream repository https://github.com/flutter/flutter.git • Framework revision 2240649358 (4 hours ago), 2023-07-27 02:30:23 +0100 • Engine revision faf1121d01 • Dart version 3.1.0 (build 3.1.0-348.0.dev) • DevTools version 2.25.0 • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades. [!] Android toolchain - develop for Android devices (Android SDK version 30.0.3) • Android SDK at /Users/dhs/Library/Android/sdk ✗ cmdline-tools component is missing Run `path/to/sdkmanager --install "cmdline-tools;latest"` See https://developer.android.com/studio/command-line for more details. ✗ Android license status unknown. Run `flutter doctor --android-licenses` to accept the SDK licenses. See https://flutter.dev/docs/get-started/install/macos#android-setup for more details. [✓] Xcode - develop for iOS and macOS (Xcode 13.2.1) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 13C100 • CocoaPods version 1.11.2 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] IntelliJ IDEA Ultimate Edition (version 2021.3.2) • IntelliJ at /Applications/IntelliJ IDEA.app • Flutter plugin version 65.1.4 • Dart plugin version 213.7228 [✓] VS Code (version 1.62.0) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.29.0 [✓] Connected device (3 available) • Darshan's iphone (mobile) • 21150b119064aecc249dfcfe05e259197461ce23 • ios • iOS 15.3.1 19D52 • macOS (desktop) • macos • darwin-x64 • macOS 12.2.1 21D62 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 109.0.5414.119 [✓] Network resources • All expected network resources are available. ! Doctor found issues in 1 category. [!] Xcode - develop for iOS and macOS (Xcode 12.3) • Xcode at /Applications/Xcode.app/Contents/Developer ! Flutter recommends a minimum Xcode version of 13. Download the latest version or update via the Mac App Store. • CocoaPods version 1.11.2 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] VS Code (version 1.62.0) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.21.0 [✓] Connected device (5 available) • SM G975F (mobile) • RZ8M802WY0X • android-arm64 • Android 11 (API 30) • Darshan's iphone (mobile) • 21150b119064aecc249dfcfe05e259197461ce23 • ios • iOS 14.4.1 18D61 • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729 • ios • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator) • macOS (desktop) • macos • darwin-x64 • Mac OS X 10.15.4 19E2269 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 98.0.4758.80 [✓] HTTP Host Availability • All required HTTP hosts are available ! Doctor found issues in 1 category. ```
Lautarourtiaga commented 1 year ago

Regarding this issue, i was trying to reproduce and found interesting insights!

I reproduced the example using "flutter run -d chrome --web-renderer html" and pasting the text code as described. But with a little tweak, i used SelectableText instead of text, that let me see the text that is trying to be displayed on html.

These are the findings:

1) As you can see i can reproduce correctly.

image

2) When accessing the text area with js console, i actually get the actual string with emojis that is written in code. This means that the value is the exact same and is not being contorted (from flutter to the UI).

Captura de pantalla 2023-07-30 a la(s) 23 20 41

3) When inspecting the text area, see that we have the background set as transparent, and the color set as transparent.

Captura de pantalla 2023-07-30 a la(s) 23 25 09

4) If we disable the background, we see that the text actually dissapears, this is because i assume that we are actually drawing that text in the background of our elements when using the html renderer, instead of just showing the text in the element value (the one in the js console).

image

5) If we disable the color attribute, we see the emojis well displayed! That's because for some reason we don't want to display the text value of the element, don't know why though.

image

Conclusion:

It seems like the emojis don't naturally display because the html renderer doesn't know how to draw them properly, while the browser actually can. Does anybody know why we don't want to let the browser render the text instead of drawing it ?

P.D: You can ignore the 2⃣, when pasting it in the dart file it didn't even get recognized at least on my system (macos ventura), but focus on the 1️⃣.

Lautarourtiaga commented 1 year ago

After investigating a bit on the engine code 1️⃣

Now i know why "why we don't want to let the browser render the text instead of drawing it" and that is because we want to keep the general interaction effects that are already coded in the flutter part.

if we display the raw text in html, that text won't have any effects / styles / animations that we declare on flutter, it will only be raw text, so we don't want that. That's probably something you guys already know but i'm new to the flutter ecosystem so sorry for the rookie mistakes 😄.

That said, i think this is an engine problem, the browser can easily understand that emoji because it has wide support in different browsers https://emojipedia.org/keycap-digit-one/. But it seems that the text that we draw in the engine can't find at least that emoji.

Another interesting bit is that the 1️⃣ emoji was introduced in Emoji 3.0, see link above for details. And i tried to display emojis from that specification and guess what they work:

image

So this issue only seems to affect those random emojis 😕

zjrwdl commented 1 year ago

After investigating a bit on the engine code 1️⃣

Now i know why "why we don't want to let the browser render the text instead of drawing it" and that is because we want to keep the general interaction effects that are already coded in the flutter part.

if we display the raw text in html, that text won't have any effects / styles / animations that we declare on flutter, it will only be raw text, so we don't want that. That's probably something you guys already know but i'm new to the flutter ecosystem so sorry for the rookie mistakes 😄.

That said, i think this is an engine problem, the browser can easily understand that emoji because it has wide support in different browsers https://emojipedia.org/keycap-digit-one/. But it seems that the text that we draw in the engine can't find at least that emoji.

Another interesting bit is that the 1️⃣ emoji was introduced in Emoji 3.0, see link above for details. And i tried to display emojis from that specification and guess what they work:

image

So this issue only seems to affect those random emojis 😕

Thank you for your detailed analysis. It is now certain that this problem comes from the engine

zjrwdl commented 1 year ago

////src/flutter/lib/web_ui/lib/src/engine/text/canvas_paragraph.dart `DomElement _createDomElement() { final DomElement rootElement = domDocument.createElement('flt-paragraph');

// 1. Set paragraph-level styles.

final DomCSSStyleDeclaration cssStyle = rootElement.style;
cssStyle
  ..position = 'absolute'
  // Prevent the browser from doing any line breaks in the paragraph. We want
  // to have full control of the paragraph layout.
  ..whiteSpace = 'pre';

// 2. Append all spans to the paragraph.

for (int i = 0; i < lines.length; i++) {
  // final ParagraphLine line = lines[i];
  // for (final LayoutFragment fragment in line.fragments) {
  //   if (fragment.isPlaceholder) {
  //     continue;
  //   }
  //
  //   final String text = fragment.getText(this);
  //   if (text.isEmpty) {
  //     continue;
  //   }
  //
  //   final DomElement spanElement = domDocument.createElement('flt-span');
  //   if (fragment.textDirection == ui.TextDirection.rtl) {
  //     spanElement.setAttribute('dir', 'rtl');
  //   }
  //   applyTextStyleToElement(element: spanElement, style: fragment.style);
  //   _positionSpanElement(spanElement, line, fragment);
  //
  //   spanElement.appendText(text);
  //   rootElement.append(spanElement);
  // }

  final ParagraphLine line = lines[i];
  String fullText = '';
  for (final LayoutFragment fragment in line.fragments) {
    if (fragment.isPlaceholder) {
      continue;
    }

    final String text = fragment.getText(this);
    if (text.isEmpty) {
      continue;
    }
    fullText += text;
  }
  if (line.fragments.isNotEmpty) {
    final LayoutFragment firstFragment = line.fragments[0];
    final DomElement spanElement = domDocument.createElement('flt-span');
    if (firstFragment.textDirection == ui.TextDirection.rtl) {
      spanElement.setAttribute('dir', 'rtl');
    }
    applyTextStyleToElement(
        element: spanElement, style: firstFragment.style);
    _positionSpanElement(spanElement, line, firstFragment);
    spanElement.appendText(fullText);
    rootElement.append(spanElement);
  }
}

return rootElement;

}`

I tried to modify the above code, and the above problem can be fixed after the modification. I think the reason for the problem is that the element is created for each character separately, but some emoji are composed of multiple characters (such as the problem emoji mentioned in issue). @ Lautarourtiaga @darshankawar @ditman

Lautarourtiaga commented 1 year ago

Good initiative! Feel free to open a PR and link it here! Good job for finding out about it and sharing!

zjrwdl commented 1 year ago

Good initiative! Feel free to open a PR and link it here! Good job for finding out about it and sharing!

https://github.com/flutter/engine/pull/44247 This is my first pr submission and I have some problems. I suggest that the official can only use this submission as a reference, and then modify it yourself. @ditman @darshankawar

ditman commented 1 year ago

Thanks for the thorough investigation here, and the PR! This is very good insight!

I think the reason for the problem is that the element is created for each character separately, but some emoji are composed of multiple characters (such as the problem emoji mentioned in issue).

That's called an "Emoji ZWJ Sequence". Does the issue reproduce with other ZWJ emojis? Here's a bunch of them: https://emojipedia.org/emoji-zwj-sequence/ (Other keycaps: https://emojipedia.org/emoji-keycap-sequence/)

flutter-triage-bot[bot] commented 1 year ago

The triaged-engine label is irrelevant if there is no team-engine label or fyi-engine label.

zjrwdl commented 1 year ago

🤔Can you solve the quiz?💰 🤖#AI got puzzled! 🎁$30 for 5 each!To win:1️⃣ FL &❤️ & RT @binary_x2⃣ Fill in the blank👇 & tag 2 friends!')

image 企业微信截图_804fbe54-026c-402d-ac74-96ed1cecfd69

I tested a few randomly, and it can be displayed normally in the current flutter version; it can also be displayed normally in my customized version.

zjrwdl commented 1 year ago

Is there any plan to fix this problem and merge it into the mainline? @ditman

VladislavYakonyuk commented 5 months ago

any updates?

ditman commented 5 months ago

Nothing to share about this for now.

Here's some news about the future of the HTML renderer:

Please, comment in that issue what's preventing you from adopting the Canvaskit renderer.

goderbauer commented 1 month ago

PR https://github.com/flutter/engine/pull/44247 was written to solve this issue but unfortunately it was not completed so the PR has been closed. If anyone would like to start from that PR and apply the review comments and drive this PR to completion, you are definitely welcome to do so!