Avalonia icon indicating copy to clipboard operation
Avalonia copied to clipboard

Android: App crashes with japanese text

Open marcussacana opened this issue 3 years ago • 18 comments

Describe the bug In android, when the Avalonia fail to find a possible font to render a text throws an exception in the UI thread and crash the application.

To Reproduce

  1. Create a control with japanese text, for example <TextBlock Text="の"/>
  2. Start the app in your android phone
  3. Throws this error:
System.ArgumentException: Specified family is not supported.
   at Avalonia.Media.FontFamily.GetFontFamilyIdentifier(String name) in /_/src/Avalonia.Visuals/Media/FontFamily.cs:line 131
   at Avalonia.Media.FontFamily..ctor(Uri baseUri, String name) in /_/src/Avalonia.Visuals/Media/FontFamily.cs:line 37
   at Avalonia.Media.FontFamily..ctor(String name) in /_/src/Avalonia.Visuals/Media/FontFamily.cs:line 20
   at Avalonia.Media.Typeface..ctor(String fontFamilyName, FontStyle style, FontWeight weight, FontStretch stretch) in /_/src/Avalonia.Visuals/Media/Typeface.cs:line 52
   at Avalonia.Skia.FontManagerImpl.TryMatchCharacter(Int32 codepoint, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, FontFamily fontFamily, CultureInfo culture, Typeface& fontKey) in /_/src/Skia/Avalonia.Skia/FontManagerImpl.cs:line 94
   at Avalonia.Media.FontManager.TryMatchCharacter(Int32 codepoint, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, FontFamily fontFamily, CultureInfo culture, Typeface& typeface) in /_/src/Avalonia.Visuals/Media/FontManager.cs:line 146
   at Avalonia.Media.TextFormatting.TextCharacters.CreateShapeableRun(ReadOnlySlice`1 text, TextRunProperties defaultProperties, SByte biDiLevel, TextRunProperties& previousProperties) in /_/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs:line 116
   at Avalonia.Media.TextFormatting.TextCharacters.GetShapeableCharacters(ReadOnlySlice`1 runText, SByte biDiLevel, TextRunProperties& previousProperties) in /_/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs:line 48
   at Avalonia.Media.TextFormatting.TextFormatterImpl.CoalesceLevels(IReadOnlyList`1 textCharacters, ReadOnlySlice`1 levels)+MoveNext() in /_/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs:line 324
   at Avalonia.Media.TextFormatting.TextFormatterImpl.ShapeTextRuns(List`1 textRuns, FlowDirection flowDirection, FlowDirection& resolvedFlowDirection) in /_/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs:line 178
   at Avalonia.Media.TextFormatting.TextFormatterImpl.FormatLine(ITextSource textSource, Int32 firstTextSourceIndex, Double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak) in /_/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs:line 33
   at Avalonia.Media.TextFormatting.TextLayout.CreateTextLines() in /_/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs:line 575
   at Avalonia.Media.TextFormatting.TextLayout..ctor(String text, Typeface typeface, Double fontSize, IBrush foreground, TextAlignment textAlignment, TextWrapping textWrapping, TextTrimming textTrimming, TextDecorationCollection textDecorations, FlowDirection flowDirection, Double maxWidth, Double maxHeight, Double lineHeight, Int32 maxLines, IReadOnlyList`1 textStyleOverrides) in /_/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs:line 73
   at Avalonia.Controls.TextBlock.CreateTextLayout(Size constraint, String text) in /_/src/Avalonia.Controls/TextBlock.cs:line 511
   at Avalonia.Controls.TextBlock.get_TextLayout() in /_/src/Avalonia.Controls/TextBlock.cs:line 175
   at Avalonia.Controls.TextBlock.MeasureOverride(Size availableSize) in /_/src/Avalonia.Controls/TextBlock.cs:line 553
   at Avalonia.Layout.Layoutable.MeasureCore(Size availableSize) in /_/src/Avalonia.Layout/Layoutable.cs:line 559
   at Avalonia.Layout.Layoutable.Measure(Size availableSize) in /_/src/Avalonia.Layout/Layoutable.cs:line 364
   at Avalonia.Layout.LayoutManager.Measure(ILayoutable control) in /_/src/Avalonia.Layout/LayoutManager.cs:line 291
   at Avalonia.Layout.LayoutManager.ExecuteMeasurePass() in /_/src/Avalonia.Layout/LayoutManager.cs:line 251
   at Avalonia.Layout.LayoutManager.InnerLayoutPass() in /_/src/Avalonia.Layout/LayoutManager.cs:line 233
   at Avalonia.Layout.LayoutManager.ExecuteLayoutPass() in /_/src/Avalonia.Layout/LayoutManager.cs:line 135
   at Avalonia.Threading.JobRunner.Job.Avalonia.Threading.JobRunner.IJob.Run() in /_/src/Avalonia.Base/Threading/JobRunner.cs:line 192
   at Avalonia.Threading.JobRunner.RunJobs(Nullable`1 priority) in /_/src/Avalonia.Base/Threading/JobRunner.cs:line 37
   at Avalonia.Android.AndroidThreadingInterface.<Signal>b__5_0() in /_/src/Android/Avalonia.Android/AndroidThreadingInterface.cs:line 76
   at Java.Lang.Thread.RunnableImplementor.Run()
   at Java.Lang.IRunnableInvoker.n_Run(IntPtr jnienv, IntPtr native__this)
   at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(_JniMarshal_PP_V callback, IntPtr jnienv, IntPtr klazz)
  • OS: Android 10
  • Version 0.10.999-cibuild0019213-beta

Additional context My phone doesn't use the Japanese language usually and as such it probably doesn't have a proper font for Japanese, I haven't tested it on a phone with the Japanese system/region.

marcussacana avatar Jun 13 '22 20:06 marcussacana

It also crashes with the same error if you try to display emojis, for example these ➕ ➖ 🔒

Kibnet avatar Aug 11 '22 13:08 Kibnet

Are you using the latest master from nightly build?

timunie avatar Aug 11 '22 15:08 timunie

I tried just the nuget pre-release build, but it was a long time ago, if it was fixed i'm not sure since I modified my code to remove any special character. I will test again latter.

marcussacana avatar Aug 11 '22 16:08 marcussacana

You should update anyway as android performance is muuuuuch better now.

But there are many breaking changes, so take some time to upgrade

timunie avatar Aug 11 '22 16:08 timunie

you mean the pre-release or nighty builds?

marcussacana avatar Aug 11 '22 18:08 marcussacana

No I mean the nightly builds. There is no official prerelase yet.

timunie avatar Aug 12 '22 04:08 timunie

@marcussacana Hey can know more and I want to work on it ?

samarthasthan avatar Aug 19 '22 03:08 samarthasthan

@marcussacana Hey can know more and I want to work on it ?

You're looking for a workaround? In my case since Japanese characters are that relevant and Is just something that can appears because of the user input, I just forced remove any non alphabet character from the controls in my program. But if that isn't a Option, I didn't tested, but look at this: https://stackoverflow.com/questions/62816456/how-to-include-external-font-in-avalonia-application-without-installing-it

marcussacana avatar Aug 19 '22 07:08 marcussacana

I'd suggest to ship a custom font with your app. You need a font that You need to use a font that has required glyphs in it. https://github.com/anthonyfok/fonts-wqy-microhei is a good one WenQuanYiMicroHei-01.ttf.zip

Place the ttf file it into Assets, add as AvaloniaResource and add something like

<Style Selector="TextBlock">
   <Setter Property="FontFamily" Value="avares://YourAssemblyName/Assets/Fonts#WenQuanYi Micro Hei" />
</Style>

to your App.xaml after the theme

kekekeks avatar Aug 19 '22 09:08 kekekeks

You can define your custom font as a fallback via FontManagerOptions: https://github.com/AvaloniaUI/Avalonia/pull/7089

The font manager will automatically use that. This does not rely on the platforms font manager.

Gillibald avatar Aug 19 '22 09:08 Gillibald

@marcussacana can you assign it to me ?

samarthasthan avatar Aug 19 '22 10:08 samarthasthan

I don't have permission for that.

marcussacana avatar Aug 19 '22 13:08 marcussacana

Adding https://github.com/AvaloniaUI/Avalonia/files/5703016/WenQuanYiMicroHei-01.ttf.zip as AvaloniaResource to the project and using these options during app setup should work for you:

.With(new FontManagerOptions
{  
    FontFallbacks = new[]
    {
        new FontFallback
        {
            FontFamily = new FontFamily("avares://YourAssemblyName/Assets/Fonts#WenQuanYi Micro Hei")
        }
    }
}

Gillibald avatar Aug 19 '22 13:08 Gillibald

I will try implement that in my program when I can as well, maybe, since Is a problem because of the user input, isn't like I will spend a time including fonts for all languages, lol

Should I close the issue in this case? I still think should be better the avalonia display a ? or square instead crash when had missing charcters in the font.

marcussacana avatar Aug 19 '22 13:08 marcussacana

For me a fallback char is a good idea 💡

@Gillibald do you think it's hard to implement? Maybe there could be a switch in the FontMgr. to throw or not in that case.

timunie avatar Aug 19 '22 16:08 timunie

Missing glyphs are rendered with a square by default. Are you useing a custom font manager?

This line is failing: https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Base/Media/FontFamily.cs#L131

So there is an issue with the font family name.

Gillibald avatar Aug 19 '22 20:08 Gillibald

Nop, I didn't tried change the default theme style or fonts.

marcussacana avatar Aug 20 '22 00:08 marcussacana

The crash can be reproduced with the default xplat templates with the following:

  1. New xplat project with latest templates dotnet new avalonia.xplat
  2. Verify Avalonia is on 11.0.0-preview1
  3. Run in Android Emulator and Desktop, validate app runs with Welcome to Avalonia! displayed
  4. Edit public string Greeting => "Welcome to Avalonia!"; to public string Greeting => "Welcome to Avalonia! ☕"; in MainViewModel.cs
  5. Run Desktop and validate app runs with Welcome to Avalonia! ☕ displayed
  6. Run Android and app crashes with FATAL UNHANDLED EXCEPTION: System.ArgumentException: Specified family is not supported. in the Android console logs

nixtar avatar Aug 27 '22 09:08 nixtar

Fixed

Gillibald avatar Nov 21 '22 09:11 Gillibald