Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Font in flutter page different from the one in the native side. #22966

Closed
kangwang1988 opened this issue Oct 11, 2018 · 19 comments

Comments

@kangwang1988
Copy link
Member

commented Oct 11, 2018

Steps to Reproduce

I wrote simple code below to reproduce my problem:

  Widget build(BuildContext context) {
    return new Container(color: Colors.white,child:
    Column(children: <Widget>[
      SizedBox(height: 100.0),
      Text("将",style: new TextStyle(fontWeight:FontWeight.w400,fontSize:16.0,fontFamily: ".SF UI Text"),textScaleFactor: 1.0,maxLines: 10)
    ])
  );
}

Then when I switch my iPhone's system language to Simplified Chinease and English, the flutter page is what I want as below:

1

However, When I switch the language to Japanese, the flutter page shows unexpected:
2

I checked the ios's fonts in http://iosfonts.com and think that it could be using different font when rendering.

Currently, in native side(our app is a typical hybrid scenario of native and flutter), we use codes below to set the fonts:

+ (UIFont *)fm_fontWithBold:(BOOL)isBold size:(CGFloat)size {
    NSString *regularFontName = @"PingFangSC-Regular";
    NSString *boldFontName = @"PingFangSC-Medium";
    NSString *fontName = isBold? boldFontName:regularFontName;
    UIFont *fmFont = [self fontWithName:fontName size:size];
    if (!fmFont) {
        if (isBold) {
            return [self boldSystemFontOfSize:size];
        }
        return [self systemFontOfSize:size];
    }
    
    return fmFont;
}

We want to force the flutter and native side use the safe fonts.

However, I failed to change the flutter side's behavior for rendering. It can't be done by setting fontFamily for TextStyle in flutter side.
So I think i should do it in the engine side. After some digging, I think the key point could be function below(engine/src/third_party/skia/src/ports/SkFontHost_mac.cpp):
screen shot 2018-10-12 at 1 22 10 am

Is the fallbackFont got here based on currentFont hit another font? If I want to force the flutter side use the "PingFangSC" too, what's the best approach?

[✓] Flutter (Channel xy_beta_v0.8.2, v0.8.3-pre.19, on Mac OS X 10.14 18A389, locale en-CN)
    • Flutter version 0.8.3-pre.19 at /Users/kylewong/Codes/fwn_idlefish/flutter
    • Framework revision f5da19a849 (17 hours ago), 2018-10-11 08:53:38 +0800
    • Engine revision 58a1894a1c
    • Dart version 2.1.0-dev.3.1.flutter-760a9690c2

[✓] Android toolchain - develop for Android devices (Android SDK 28.0.3)
    • Android SDK at /Users/kylewong/Library/Android/sdk
    • Android NDK at /Users/kylewong/Library/Android/sdk/ndk-bundle
    • Platform android-28, build-tools 28.0.3
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)
    • All Android licenses accepted.

[!] iOS toolchain - develop for iOS devices (Xcode 10.0)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 10.0, Build version 10A254a
    • ios-deploy 1.9.2
    ! CocoaPods out of date (1.5.0 is recommended).
        CocoaPods is used to retrieve the iOS platform side's plugin code that responds to your plugin usage on the Dart side.
        Without resolving iOS dependencies with CocoaPods, plugins will not work on iOS.
        For more info, see https://flutter.io/platform-plugins
      To upgrade:
        brew upgrade cocoapods
        pod setup

[✓] Android Studio (version 3.2)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 29.0.2
    • Dart plugin version 181.5540.11
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)

@eseidelGoogle @jason-simmons

@jason-simmons

This comment has been minimized.

Copy link
Contributor

commented Oct 11, 2018

@kangwang1988 kangwang1988 changed the title Font in flutter page different from the one in the native one. Font in flutter page different from the one in the native side. Oct 12, 2018

@zoechi zoechi added this to the Goals milestone Oct 12, 2018

@najeira

This comment has been minimized.

Copy link
Contributor

commented Oct 15, 2018

Hi, I'm Japanese.

In the screenshot when setting to Japanese, the font of the character is correct Japanese font.

@kangwang1988

This comment has been minimized.

Copy link
Member Author

commented Oct 15, 2018

@najeira
Yes, you're right.
The problem occurs as we specify "PingFangSC" in native side. However, the flutter's implementation use CoreText and its fallback font is related to the default"global cascade lis" which is related to current locale and language. With the work around below, this problem disappears.

screen shot 2018-10-16 at 12 25 06 am

@najeira

This comment has been minimized.

Copy link
Contributor

commented Oct 15, 2018

@kangwang1988 thank you for your reply.

I set "Hiragino Sans" to ThemeData.fontFamily and pass the ThemeData to MaterialApp for iOS. How is that way?

It is an excerpt from my code:

const Locale kLocale = const Locale("ja", "JP");

final String _kFontFamilly = (defaultTargetPlatform == TargetPlatform.android) 
  ? null : "Hiragino Sans";

final Typography _kTypography = new Typography(platform: defaultTargetPlatform);

final TextTheme kTextThemeWhite = _kTypography.white.copyWith(
  display4: _kTypography.white.display4.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  display3: _kTypography.white.display3.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  display2: _kTypography.white.display2.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  display1: _kTypography.white.display1.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  headline: _kTypography.white.headline.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  title: _kTypography.white.title.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  subhead: _kTypography.white.subhead.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  body2: _kTypography.white.body2.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  body1: _kTypography.white.body1.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  caption: _kTypography.white.caption.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  button: _kTypography.white.button.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
);

final TextTheme kTextThemeBlack = _kTypography.black.copyWith(
  display4: _kTypography.black.display4.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  display3: _kTypography.black.display3.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  display2: _kTypography.black.display2.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  display1: _kTypography.black.display1.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  headline: _kTypography.black.headline.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  title: _kTypography.black.title.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  subhead: _kTypography.black.subhead.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  body2: _kTypography.black.body2.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  body1: _kTypography.black.body1.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  caption: _kTypography.black.caption.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
  button: _kTypography.black.button.copyWith(fontFamily: _kFontFamilly, locale: kLocale),
);
    final themeData = new ThemeData(
      textTheme: kTextThemeBlack,
      primaryTextTheme: kTextThemeWhite,
      accentTextTheme: kTextThemeWhite,
    );
    
    return new MaterialApp(
      locale: kLocale,
      home: child,
      theme: themeData,
      navigatorObservers: navigatorObservers,
      localizationsDelegates: GlobalMaterialLocalizations.delegates,
      supportedLocales: [kLocale],
      debugShowCheckedModeBanner: false,
    );
@kangwang1988

This comment has been minimized.

Copy link
Member Author

commented Oct 15, 2018

@najeira
It doesn't work. Even I specify a _kFontFamilly in my flutter code,the result will also be unexpected, due to the font search&matching logic in skia.

@kangwang1988

This comment has been minimized.

Copy link
Member Author

commented Oct 15, 2018

@GaryQian @jason-simmons
If the "kCTFontCascadeListAttribute" used for best matching can be parameters which is set from the flutter side and applied in the skia rendering.
I think this problem will disappear.

@kangwang1988

This comment has been minimized.

Copy link
Member Author

commented Oct 16, 2018

@eseidelGoogle
I think this problem is an urgent thing. Though I've made an workaround to solve our problem, however, other guys might face the problem too.Based on flutter betav0.8.2 and the flutter_gallery example,I used some Chinease characters and change the system language to Japan(iOS8), certain characters become an empty box. This problem is serious in iOS8.

Before my modification:

img_0185

After my modification:

img_0186

When it comes to iOS9+, it might behavior unexpectedly. In my case, my iOS12 and system setting remains, however, sometimes, for the same Chinease article, the font used for rendering alters.

@eseidelGoogle

This comment has been minimized.

Copy link
Contributor

commented Oct 24, 2018

@cbracken @jason-simmons @GaryQian are the right folks to see this bug. Thank you for filing!

@Hixie

This comment has been minimized.

Copy link
Contributor

commented Oct 30, 2018

@GaryQian Have you taken a look at this one perchance?

@GaryQian

This comment has been minimized.

Copy link
Contributor

commented Oct 31, 2018

Haven't gotten to it yet. Should have some time to look at it now.

@kf6gpe

This comment has been minimized.

Copy link
Contributor

commented Nov 7, 2018

@GaryQian Any luck getting time to look at this? Thanks! Let me know if we should find someone else to help with it.

@GaryQian

This comment has been minimized.

Copy link
Contributor

commented Nov 8, 2018

Replicated on an assortment of iOS 8.1-8.4 device simulators. This problem is not present in any iOS version 9.0+.

Just finished up with a localization mini-rework, this is next on my radar.

@GaryQian

This comment has been minimized.

Copy link
Contributor

commented Nov 20, 2018

Update on the status of this, I chased the bug around and it looks like it may be skia-related. I will verify this and send it off to skia if need be.

@Hixie

This comment has been minimized.

Copy link
Contributor

commented Dec 12, 2018

@GaryQian said he'd look at this in the coming week.

@Hixie Hixie modified the milestones: Goals, January 2019 Dec 12, 2018

@GaryQian

This comment has been minimized.

Copy link
Contributor

commented Dec 14, 2018

So I would like to clear up that the original post of this issue is actually working as expected. The Chinese character is rendering using a Chinese font when the locale is Chinese, but when changed to a Japanese locale, since Chinese and Japanese Kanji share the same encoding, the character is interpreted as Japanese kanji and rendered with a Japanese font. There is no way to distinguish Chinese vs Japanese characters out of context from just the UTF-16 encoding, so the locale is used to determine how to draw them.

The later bug reported is different. Some Chinese does not exist as Japanese Kanji, and those glyphs are missing from the Japanese fonts. What should happen (it is correct in iOS9+) is that the missing glyphs should fall back to a Chinese font, but in iOS8.x, this fallback is not happening, and we are rendering tofu instead.

@GaryQian

This comment has been minimized.

Copy link
Contributor

commented Dec 14, 2018

There also exists glyphs where the same base glyph with slight variations between languages have different codepoints. For example:
http://unicode.org/cldr/utility/character.jsp?a=984C
http://unicode.org/cldr/utility/character.jsp?a=9898
Are derived from the same character, but have different codepoints due to large enough differences.

These seem to be the characters that are unable to be rendered.

Characters like 写 have different glyphs for Chinese vs Japanese, but share the same codepoint and thus are drawn with the Japanese font.

I am currently exploring/experimenting with the contents of the cascade list to determine the root cause of this bug.

@najeira

This comment has been minimized.

Copy link
Contributor

commented Dec 14, 2018

In my understanding, @kangwang1988 want to Flutter uses specified font.

If font is not specified, it is correct that Flutter selects and renders fonts according to language settings.

@zoechi zoechi added the engine label Dec 17, 2018

@GaryQian

This comment has been minimized.

Copy link
Contributor

commented Dec 17, 2018

So we did research into this topic and found that when no font is provided via a TextStyle, we delegate the font fallback to iOS's platform fallback. With iOS8, the Apple API does not provide an easy way to access/extend/modify the Cascade list, and the method that resolves fonts per-glyph is not sufficient in determining the correct font in this case. The cascade list in this case is not sufficiently populated to be able to resolve the correct glyphs.

The conclusion is that with iOS8, the platform system is not sufficient to decide to properly perform fallback.

A proposed solution to this is to add support for TextStyle.fontFamilyFallback, which would be a list of strings of the names of font families that the user would like the text to fall back with. This would offload the font fallback away from iOS onto LibTxt/minikin, which is able to better resolve a correct font if possible.

@GaryQian

This comment has been minimized.

Copy link
Contributor

commented Dec 20, 2018

flutter/engine#7241 and #25585 will expose the font fallback API that allows users to specify custom font fallback that will be handled by the engine instead if the platform.

@GaryQian GaryQian closed this Jan 2, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.