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
166.17k stars 27.48k forks source link

Chinese characters render garbled in iOS Simulator #6939

Closed kuloud closed 7 years ago

kuloud commented 7 years ago

Steps to Reproduce

The chinese text 中文 is garbled:

final Widget indexItem = new DrawerItem(
    icon: new Icon(Icons.home),
    child: new Row(
        children: <Widget>[
          new Flexible(child: new Text('中文')),
        ],
        ),
    );

Logs

// ignore

Flutter Doctor

[✓] Flutter (on Mac OS, channel master) • Flutter at /Users/kuloud/Dev/flutter • Framework revision 9c1a24fa72 (6 hours ago), 2016-11-18 21:22:32 • Engine revision cea5ed2b9b • Tools Dart version 1.21.0-dev.6.0

[✓] Android toolchain - develop for Android devices (Android SDK 25.0.0) • Android SDK at /usr/local/opt/android-sdk/ • Platform android-25, build-tools 25.0.0 • ANDROID_HOME = /usr/local/opt/android-sdk/ • Java(TM) SE Runtime Environment (build 1.8.0_92-b14)

[✓] iOS toolchain - develop for iOS devices (Xcode 8.1) • XCode at /Applications/Xcode.app/Contents/Developer • Xcode 8.1, Build version 8B62

[✓] IntelliJ IDEA Ultimate Edition (version 2016.2.5) • Dart plugin installed • Flutter plugin installed

[✓] Connected devices • iPhone 7 • 3DC11AAB-EF8F-4810-B104-0C756CC86323 • ios • iOS 10.1 (simulator)

eseidelGoogle commented 7 years ago

Here is a picture of new Text('中文') rendered in iPhone simulator:

screen shot 2016-11-19 at 7 42 52 am

Here is Genymotion emulating a Nexus 5X 6.0:

screen shot 2016-11-19 at 7 44 48 am

This may be iOS specific and possibly related to https://github.com/flutter/flutter/issues/4273.

FYI @chinmaygarde in case he has theories.

Hixie commented 7 years ago

Probably a font issue?

raju-bitter commented 7 years ago

I did a test with embedded fonts (based on the current documentation) for Western/English, Chinese (simplified) and Korean/Hangul. Custom fonts only get rendered for Western character set. Full project source code can be found here: https://github.com/raju-bitter/flutter-unicode-test

Cannot test on iOS right now due to https://github.com/flutter/flutter/issues/6997, but yesterday tested, and saw a slightly different behavior. When using the default font, I saw the same 6 bars for each character (Korean & Chinese) as mentioned above by Eric. But for custom fonts, I saw empty rectangles for each glyph.

raju-bitter commented 7 years ago

Did a test run on iOS, here are my observations:

The behavior is identical both for debug and release mode on iOS.

iOS / iPhone 5s

On Android, all East Asian glyphs render fine, only the custom fonts are not used.

Android / Galaxy note 5 Debug mode

Dart source code used: https://github.com/raju-bitter/flutter-unicode-test/blob/master/lib/main.dart

eseidel commented 7 years ago

HUGE thank you for the analysis @raju-bitter! @jason-simmons or @chinmaygarde are the right folks to ask here.

chinmaygarde commented 7 years ago

Thanks for the reproducible test case. Investigating.

kuloud commented 7 years ago

Thanks @raju-bitter 's help

cbracken commented 7 years ago

For the record, verified that this also fails with Japanese: Hiragana 'あ', Katakana 'ア', Kanji '亜', full-width alpha-numeric characters 'a' and '1' and punctuation '「」【】', though some symbolics such as '○' seem to render (possibly they're available in S.F. font?).

xster commented 7 years ago

Some brain dump (taking this opportunity to test a bit as well): Most written scripts work (Arabic, Hebrew, Cyrillic, Braille, as well as Asian scripts like Mongolian, Tibetan, Manchu, Thai, Tamil, Shahmukhi)

chữ Hán doesn't work since it's basically Chinese. Curiously Devanagari doesn't work either.

Suspect it's not just a fullwidth halfwidth issue since halfwidth Japanese and Korean characters like and fail too, though in slightly different ways.

@chinmaygarde previously checked and suspects it's failing somewhere in skia

xster commented 7 years ago

Some partial info. Testing with these characters: a 97 ا 1575 א 1488 ཀ 3904 中 20013 (this doesn't work)

Some control flows in Font.cpp

Rendering character 97
  Page number 0
    No node in fallback list at page number 0
  Node level 1
    Node has page
  Page has glyph for 97 with font Helvetica
Rendering character 1575
  Page number 6
    No node in fallback list at page number 6
  Node level 1
    Node doesn't have page. Falling back.
  Node level 2
    Node has page
  Node is system fallback
  Looking for system font for character 1575
    Found system font GeezaPro
  Substituted for system font Geeza Pro
Rendering character 1488
  Page number 5
    No node in fallback list at page number 5
  Node level 1
    Node has page
    Node doesn't have page. Falling back.
  Node level 2
    Node has page
  Node is system fallback
  Looking for system font for character 1488
    Found system font ArialHebrew
  Substituted for system font Arial Hebrew
Rendering character 3904
  Page number 15
    No node in fallback list at page number 15
  Node level 1
    Node doesn't have page. Falling back.
  Node level 2
    Node has page
  Node is system fallback
  Looking for system font for character 3904
    Found system font Kailasa
  Substituted for system font Kailasa
Rendering character 20013
  Page number 78
    No node in fallback list at page number 78
  Node level 1
    Node doesn't have page. Falling back.
  Node level 2
    Node has page
  Node is system fallback
  Looking for system font for character 20013
    Found system font PingFangSC-Regular
  Can't find glyph for 20013

Seems like the problem is in the creation of the FontPlatformData for the CJK fonts in FontCacheSkia

xster commented 7 years ago

Slight difference between the font files. All the other fonts are in iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Fonts/Core/ or /System/Library/Fonts/Core/. But the Chinese font is in iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Fonts/LanguageSupport/ or /System/Library/Fonts/LanguageSupport/.

Fails in Skia's SkFontHost_mac.cpp with CoreText's call to CTFontCreateWithGraphicsFont which doesn't return a CoreGraphics's CGFontRef. Not sure how to debug into it.

I think the next step is to test my google-fu on CoreText

eseidelGoogle commented 7 years ago

In case it's useful, there are other implementations of font fallback from the projects we forked from. e.g. https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/fonts/mac/FontPlatformDataMac.mm https://github.com/WebKit/webkit/blob/master/Source/WebCore/platform/graphics/ios/FontCacheIOS.mm or similar files in either repository can be helpful at times. I'm not sure the chromium repo has iOS support, but the WebKit one definitely does. Both of their font systems are likely different from ours, but of the same lineage at least.

xster commented 7 years ago

Actually may not be just font locations. Korean fails to create the font at /System/Library/Fonts/Core/AppleSDGothicNeo.ttc. Devanagari fails at /System/Library/Fonts/Core/Kohinoor.ttc Hiragana and Katakana (somewhat surprisingly) uses the same font as chinese at /System/Library/Fonts/LanguageSupport/PingFang.ttc

xster commented 7 years ago

Update: discussed offline with Skia and @chinmaygarde.

We're losing the ttc index in the data chain of unicode->CTFont->CTFontDescriptor->PlatformFallbackFont->FontFaceCreationParams->filename->CGDataProvider->CGFont->CTFont->SkTypeface between the filename and CGDataProvider for mac platforms. Instead of shoehorning that step of the existing pipeline, will try to simplify and use Skia's newer API. Remove as much of the platform specific handlings in the sky engine and use the generic APIs Skia already provides and hopefully do unicode->SkTypeface directly.

Catalog existing Blink features wrt fallback mechanisms and remove everything below FontCacheSkia::fallbackFontForCharacter and call Skia with matchFamilyStyleCharacter directly.

chinmaygarde commented 7 years ago

Sounds good to me.

eseidelGoogle commented 7 years ago

I suspect the above is fixed in the current flavor of the blink pipeline (which also uses skia, but has diverged significantly from ours): https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/fonts/mac/FontPlatformDataMac.mm

However, I also think your instinct to consider re-thinking from first principles is the correct one, since we have so many fewer constraints on our engine than Blink does. Doing so may be difficult and you might want to try and take a series of related fixes in this area as warm-ups as you slowly move us towards a simpler design (and with luck add any amount of testing along the way?). :)

xster commented 7 years ago

Yes, agreed. I don't want to spend too much time refactoring everything.

Comparing the various implementations for different platforms on different 'branches', https://chromium.googlesource.com/chromium/blink.git/+/master/Source/platform/fonts/android/FontCacheAndroid.cpp is probably the leanest and platform agnostic. Though there is still some redundancies in data wrangling going from unicode->SkTypeface->string->FontFaceCreationParams->SkTypeface. But inside that chain, there are still 2 layers of caching that I'll probably want to reproduce anyway if I wanted to reduce it to unicode->SkTypeface.

Will start by just removing FontCacheIOS, removing some parts of FontCacheSkia, updating FontCacheAndroid and making it FontCacheMobile. Will create a separate optimization issue for deleting more from FontCacheSkia, and reimplementing a single layer of cache.

xster commented 7 years ago

@eseidelGoogle, I think we're talking about some kind of pixel golden test right? Can you point me to some examples? I didn't find any in the framework repo.

eseidelGoogle commented 7 years ago

We removed all pixel tests from flutter/engine. We don't have a solution at this time. :( FYI @abarth

xster commented 7 years ago

With #3418:

screenshot-2017-02-15-00-34-10

iOS looks mostly on par with Android pre-fix (well the fix merges the fallback mechanisms between the platforms and pushes it to Skia). Doesn't seem to have any regressions and mostly responds well to font family/size/weight/various decoration changes. Both iOS and Android are still missing some extended math symbols and arrows etc but that's unrelated to this bug.

One remaining fix needed is to get serif and cursive Chinese fonts working. Will create a separate bug for it but it's lower priority. I think serious Chinese app developers will probably want to bundle their own fonts anyway.

cbracken commented 7 years ago

Nice! For Japanese, I believe the system fonts you'll want are HiraKakuProN and HiraMinProN for the gothic and mincho variants, respectively. It looks like there are -W3 and -W6 variants for the weight. Though it looks like maybe the gothic font changed from HiraKakuProN to HiraginoSans in iOS 9. http://iosfonts.com/

eseidelGoogle commented 7 years ago

So did this fix emoji too? https://github.com/flutter/flutter/issues/4273

xster commented 7 years ago

Will test emojis in the morning

xster commented 7 years ago

The emojis were the same issue. Fixed as well. Turns out the apple emoji font family "Apple Color Emoji" has a few more than Android does though composite multi-codepoint emojis like profession+skin colour type emojis don't seem to render right but it's not even possible to type or paste them in Atom or emacs so probably lower priority.

iOS Android

github-actions[bot] commented 3 years ago

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.