Skip to content

fix(arm64-linux): fix CJK font rendering on flutter-elinux - #15324

Merged
rustdesk merged 2 commits into
rustdesk:masterfrom
Bia951:claude/epic-einstein-sgezjv
Jun 18, 2026
Merged

rustdesk merged 2 commits into
rustdesk:masterfrom
Bia951:claude/epic-einstein-sgezjv

Conversation

@Bia503

@Bia503 Bia503 commented Jun 17, 2026 •

Copy link
Copy Markdown
Contributor

The flutter-elinux engine used for ARM64 Linux builds is compiled without --enable-fontconfig, so Flutter's text shaper cannot discover system fonts. This causes CJK characters to render as tofu boxes even when fonts such as Noto Sans CJK are installed. See flutter/flutter#139293.

Fix by loading a CJK font at startup via FontLoader (bypassing fontconfig) and propagating it through two paths so all text widgets are covered:

  1. MyTheme.applyFontFallback() — updates textTheme on both light and dark ThemeData so Material components receive the fallback through the theme.

  2. _mergeCjkFallback() in GetMaterialApp builders — wraps child widgets in DefaultTextStyle.merge so bare Text() widgets and those with inherit:true also render CJK characters correctly.

Font discovery first runs fc-list :lang=zh (the fontconfig CLI tool is available system-wide even when the engine has no fontconfig support), then falls back to a hardcoded search-path list covering Debian/Ubuntu, Fedora/ RHEL, Arch Linux, and WenQuanYi font layouts.

This is an app-level workaround. The engine-level fix is tracked at flutter/flutter#180235 (open as of 2026-06).

Fixes #10666

Summary by CodeRabbit

  • New Features
    • Added ARM64 Linux system CJK font loading during startup, including automatic font registration when available.
    • Introduced a theme-wide CJK font-family fallback applied across both light and dark themes.
    • Ensures plain Text widgets inherit the CJK fallback by integrating it into the app’s default text styling so CJK characters render more reliably.
  • Documentation
    • Added guidance that the fallback application is intended for a one-time startup call after CJK fonts load.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds ARM64 Linux CJK font support: a new font_manager.dart discovers and registers a system CJK font via FontLoader with a web stub for platform abstraction, MyTheme.applyFontFallback stamps the fallback onto both light and dark themes, and main.dart wires these together at startup and inserts _mergeCjkFallback into the widget builder chain so bare Text widgets inherit the fallback.

Changes

CJK Font Fallback for ARM64 Linux

Layer / File(s) Summary
Font discovery and registration with platform abstraction
flutter/lib/native/font_manager.dart, flutter/lib/web/font_manager.dart
Defines kLinuxCjkFontFamily constant and candidate font paths. loadSystemCJKFonts() gates on Linux + aarch64, resolves a font path via _findCjkFontPath() (using fc-list for zh, ja, ko or predefined system paths), reads font bytes, registers with FontLoader, and returns true/false. Web stub returns false unconditionally to avoid native dart:io dependencies.
MyTheme font fallback propagation
flutter/lib/common.dart
Adds MyTheme.applyFontFallback(fallbacks) that updates lightTheme and darkTheme via copyWith, applying fontFamilyFallback to both textTheme and primaryTextTheme.
Startup wiring and widget-tree integration
flutter/lib/main.dart
Imports font_manager.dart with conditional web import, calls loadSystemCJKFonts() in main() and conditionally calls MyTheme.applyFontFallback. Introduces _mergeCjkFallback(context, child) that reads Theme.of(context).textTheme.bodyMedium?.fontFamilyFallback and merges it into DefaultTextStyle. Inserts _mergeCjkFallback into both the _runApp and App.build GetMaterialApp builder chains so descendant Text widgets inherit the CJK fallback.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 On ARM64 where glyphs once fled,
A rabbit found new fonts instead.
fc-list sniffs the system's store,
CJK strokes rendered evermore.
Through theme and builder, text stands tall —
One fallback font to serve them all! 🈳

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(arm64-linux): fix CJK font rendering on flutter-elinux' directly and clearly describes the main change: implementing a fix for CJK font rendering issues on ARM64 Linux builds using flutter-elinux.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@flutter/lib/native/font_manager.dart`:
- Around line 70-75: The fc-list command in the Process.run call uses `:lang=zh`
which only searches for Chinese fonts, but this can return a Chinese-only font
lacking Japanese and Korean glyph support, breaking the CJK goal. Modify the
language filter argument in the Process.run call to probe for fonts that support
all CJK languages (Chinese, Japanese, and Korean) instead of just Chinese,
ensuring the returned font can properly render characters for all three language
scripts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0921c86a-a151-4f7d-a922-78cdbf84d4b1

📥 Commits

Reviewing files that changed from the base of the PR and between 36e812e and e9ff958b0aecdd36ba8e2f41f1da0abff1c1f724.

📒 Files selected for processing (3)
  • flutter/lib/common.dart
  • flutter/lib/main.dart
  • flutter/lib/native/font_manager.dart

Comment thread flutter/lib/native/font_manager.dart Resolved

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
flutter/lib/native/font_manager.dart (1)

40-45: 💤 Low value

Consider adding a timeout to Process.run calls.

If uname (or fc-list in _findCjkFontPath) hangs unexpectedly, the app startup will block indefinitely. Adding a timeout provides defense-in-depth for this startup path.

♻️ Suggested improvement
   try {
-    final r = await Process.run('uname', ['-m']);
+    final r = await Process.run('uname', ['-m']).timeout(
+      const Duration(seconds: 5),
+      onTimeout: () => ProcessResult(0, 1, '', 'timeout'),
+    );
     if (r.stdout.toString().trim() != 'aarch64') return false;
   } catch (_) {
     return false;
   }

Apply the same pattern to the fc-list call in _findCjkFontPath.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter/lib/native/font_manager.dart` around lines 40 - 45, The Process.run
calls for 'uname' and 'fc-list' lack timeouts, which could cause the app to hang
indefinitely during startup if these commands become unresponsive. Add a timeout
parameter to both Process.run calls: the 'uname' invocation in the current code
block and the 'fc-list' call in the _findCjkFontPath method. Wrap the timeout in
a Duration object and ensure that if the timeout expires, the catch block
gracefully returns false to allow the app to continue startup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@flutter/lib/native/font_manager.dart`:
- Around line 40-45: The Process.run calls for 'uname' and 'fc-list' lack
timeouts, which could cause the app to hang indefinitely during startup if these
commands become unresponsive. Add a timeout parameter to both Process.run calls:
the 'uname' invocation in the current code block and the 'fc-list' call in the
_findCjkFontPath method. Wrap the timeout in a Duration object and ensure that
if the timeout expires, the catch block gracefully returns false to allow the
app to continue startup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 04928a6b-f42a-4608-a2c0-5dc869091871

📥 Commits

Reviewing files that changed from the base of the PR and between fc35608a052f80da33c5442db24a1d06f3ab577e and 062289a88f22ea11ae1bb6d7ef13d84ff3cd07e3.

📒 Files selected for processing (3)
  • flutter/lib/common.dart
  • flutter/lib/main.dart
  • flutter/lib/native/font_manager.dart
🚧 Files skipped from review as they are similar to previous changes (2)
  • flutter/lib/common.dart
  • flutter/lib/main.dart

@rustdesk

rustdesk commented Jun 18, 2026 •

Copy link
Copy Markdown
Owner

Good job.

Please limit this to arm only.

@Bia503

Bia503 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

Done. Added a _cjkFontLoaded flag that stores the result of loadSystemCJKFonts().

@Bia503

Bia503 commented Jun 18, 2026 •

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 18, 2026 •

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the current state of font_manager.dart
cat flutter/lib/native/font_manager.dart 2>/dev/null || echo "File not found"
echo "---"
# Check relevant parts of main.dart
grep -n "cjk\|CJK\|arm\|aarch\|loadSystem\|_cjkFont" flutter/lib/main.dart 2>/dev/null || echo "No matches in main.dart"

Length of output: 4757



@Bia503 The ARM-only guard looks correctly implemented. Here's a quick summary of how the restriction is enforced:

  1. loadSystemCJKFonts() (font_manager.dart) has a two-level guard:

    • Returns false immediately on non-Linux platforms (!Platform.isLinux)
    • Runs uname -m and returns false unless the architecture is exactly aarch64
  2. _cjkFontLoaded (main.dart, line 41) is only ever true when loadSystemCJKFonts() succeeds, and all downstream effects — MyTheme.applyFontFallback(...) and both _mergeCjkFallback(...) call sites — are gated behind that flag.

This means the CJK workaround is completely inert on x86_64 Linux, macOS, Windows, and Android. The implementation aligns with the maintainer's feedback. Triggering a fresh review now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jun 18, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@rustdesk

rustdesk commented Jun 18, 2026 •

Copy link
Copy Markdown
Owner

The web version seems broken.

flutter build web --release

Please confirm.

@rustdesk

rustdesk commented Jun 18, 2026 •

Copy link
Copy Markdown
Owner

I asked claude, there is better way to detect arch.

  import 'dart:ffi' show Abi;


  if (Abi.current() != Abi.linuxArm64) return false;

or

 if (!Platform.version.contains('linux_arm64')) return false;

You need to ensure web version again.

@Bia503

Bia503 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

Done. I think the web version should be fixed now.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an app-level workaround for ARM64 Linux builds where the flutter-elinux engine lacks --enable-fontconfig, causing Flutter to fail to discover system fonts and render CJK glyphs as tofu. It proactively loads a system CJK font at startup and propagates it through theming and default text styling so both Material widgets and bare Text() widgets can render CJK characters.

Changes:

  • Add ARM64 Linux system CJK font discovery/loading via FontLoader, with a web stub for conditional import compatibility.
  • Apply a theme-wide fontFamilyFallback to both light/dark themes at startup after a font is loaded.
  • Wrap app builders with a DefaultTextStyle.merge fallback so non-themed/bare Text widgets inherit the CJK fallback.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
flutter/lib/web/font_manager.dart Web stub implementation for conditional import; no-op font loading.
flutter/lib/native/font_manager.dart ARM64 Linux font discovery (fc-list + fallback paths) and FontLoader registration.
flutter/lib/main.dart Calls font loader at startup; applies theme fallback and merges fallback into DefaultTextStyle via app builders.
flutter/lib/common.dart Adds MyTheme.applyFontFallback() to apply fontFamilyFallback across both themes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread flutter/lib/native/font_manager.dart Outdated
try {
final loader = FontLoader(kLinuxCjkFontFamily);
final bytes = await File(path).readAsBytes();
loader.addFont(Future.value(ByteData.view(bytes.buffer)));
Comment thread flutter/lib/native/font_manager.dart Resolved
Bia503 and others added 2 commits June 18, 2026 11:33
The flutter-elinux engine used for ARM64 Linux builds is compiled without
--enable-fontconfig, so Flutter's text shaper cannot discover system fonts.
This causes CJK characters to render as tofu boxes even when fonts such as
Noto Sans CJK are installed. See flutter/flutter#139293.

Fix by loading a CJK font at startup via FontLoader (bypassing fontconfig)
and propagating it through two paths so all text widgets are covered:

1. MyTheme.applyFontFallback() — updates textTheme on both light and dark
   ThemeData so Material components receive the fallback through the theme.

2. _mergeCjkFallback() in GetMaterialApp builders — wraps child widgets in
   DefaultTextStyle.merge so bare Text() widgets and those with inherit:true
   also render CJK characters correctly.

Font discovery queries fc-list for zh, ja, and ko separately, preferring
fonts present in all three sets (true pan-CJK fonts such as NotoSansCJK or
SourceHanSans) over Chinese-only fonts that may lack Japanese kana or Korean
hangul glyphs.  Falls back to a hardcoded search-path list covering
Debian/Ubuntu, Fedora/RHEL, Arch Linux, and WenQuanYi font layouts.

This is an app-level workaround. The engine-level fix is tracked at
flutter/flutter#180235 (open as of 2026-06).

Fixes rustdesk#10666

Signed-off-by: Bia503 <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
@Bia503

Bia503 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

Done. I've applied the first suggestion manually.

@Bia503

Bia503 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

I accidentally force-pushed and overwrote your commit 😫. I've already recovered that.

@Bia503

Bia503 commented Jun 18, 2026 •

Copy link
Copy Markdown
Contributor Author

I realized that since I recovered the commit, I'm now listed as the author. Would you mind re-pushing your original commit from your end?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment on lines +55 to +58
} catch (e) {
debugPrint('ARM64 Linux: failed to load CJK font: $e');
return false;
}
Comment on lines +79 to +81
} catch (e) {
debugPrint('ARM64 Linux: fc-list failed for lang=$lang: $e');
}
@rustdesk
rustdesk merged commit c9391fb into rustdesk:master Jun 18, 2026
7 checks passed
@Bia503
Bia503 deleted the claude/epic-einstein-sgezjv branch June 19, 2026 05:49
@rustdesk

Copy link
Copy Markdown
Owner

Since #15376, this PR will be reverted.

rustdesk added a commit that referenced this pull request Jun 22, 2026
reancool pushed a commit to flyopensource/rustdesk that referenced this pull request Sep 6, 2026
…15324)

* fix(arm64-linux): fix CJK font rendering on flutter-elinux

The flutter-elinux engine used for ARM64 Linux builds is compiled without
--enable-fontconfig, so Flutter's text shaper cannot discover system fonts.
This causes CJK characters to render as tofu boxes even when fonts such as
Noto Sans CJK are installed. See flutter/flutter#139293.

Fix by loading a CJK font at startup via FontLoader (bypassing fontconfig)
and propagating it through two paths so all text widgets are covered:

1. MyTheme.applyFontFallback() — updates textTheme on both light and dark
   ThemeData so Material components receive the fallback through the theme.

2. _mergeCjkFallback() in GetMaterialApp builders — wraps child widgets in
   DefaultTextStyle.merge so bare Text() widgets and those with inherit:true
   also render CJK characters correctly.

Font discovery queries fc-list for zh, ja, and ko separately, preferring
fonts present in all three sets (true pan-CJK fonts such as NotoSansCJK or
SourceHanSans) over Chinese-only fonts that may lack Japanese kana or Korean
hangul glyphs.  Falls back to a hardcoded search-path list covering
Debian/Ubuntu, Fedora/RHEL, Arch Linux, and WenQuanYi font layouts.

This is an app-level workaround. The engine-level fix is tracked at
flutter/flutter#180235 (open as of 2026-06).

Fixes rustdesk#10666

Signed-off-by: Bia503 <[email protected]>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <[email protected]>

---------

Signed-off-by: Bia503 <[email protected]>
Co-authored-by: RustDesk <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
reancool pushed a commit to flyopensource/rustdesk that referenced this pull request Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ubuntu ARM64: Chinese Characters Display as Squares

3 participants