AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
25.81k stars 2.23k forks source link

System.InvalidOperationException: Default font family name can't be null or empty #4427

Open elestedt opened 4 years ago

elestedt commented 4 years ago

Version: 0.10-preview2

I upgraded project IPOCS.JMRI.CONTROL, located at https://github.com/GMJS/ipocs.jmri, to preview2 (not committed and pushed yet) and made a build for linux-arm using dotnet publish IPOCS.JMRI.CONTROL -c Release -r linux-arm. Upon trying to run this on a raspberry pi I'm getting the exception:

Unhandled exception. System.InvalidOperationException: Default font family name can't be null or empty.
   at Avalonia.Media.FontManager..ctor(IFontManagerImpl platformImpl)
   at Avalonia.Media.FontManager.get_Current()
   at Avalonia.Rendering.RendererBase..ctor(Boolean useManualFpsCounting)
   at Avalonia.Rendering.DeferredRenderer..ctor(IRenderRoot root, IRenderLoop renderLoop, ISceneBuilder sceneBuilder, IDispatcher dispatcher, IDeferredRendererLock rendererLock)
   at Avalonia.X11.X11Window.CreateRenderer(IRenderRoot root)
   at Avalonia.Controls.TopLevel..ctor(ITopLevelImpl impl, IAvaloniaDependencyResolver dependencyResolver)
   at Avalonia.Controls.WindowBase..ctor(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver)
   at Avalonia.Controls.WindowBase..ctor(IWindowBaseImpl impl)
   at Avalonia.Controls.Window..ctor(IWindowImpl impl)
   at Avalonia.Controls.Window..ctor()
   at IPOCS.JMRI.CONTROL.Views.MainWindow..ctor() in <path>\ipocs.jmri\IPOCS.JMRI.CONTROL\Views\MainWindow.axaml.cs:line 9
   at IPOCS.JMRI.CONTROL.App.OnFrameworkInitializationCompleted() in <path>t\ipocs.jmri\IPOCS.JMRI.CONTROL\App.axaml.cs:line 20
   at Avalonia.Controls.AppBuilderBase`1.Setup()
   at Avalonia.Controls.AppBuilderBase`1.SetupWithLifetime(IApplicationLifetime lifetime)
   at Avalonia.ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime[T](T builder, String[] args, ShutdownMode shutdownMode)
   at IPOCS.JMRI.CONTROL.Program.Main(String[] args) in <path>\ipocs.jmri\IPOCS.JMRI.CONTROL\Program.cs:line 13
nopara73 commented 2 years ago

FTR the 0.03BTC bounty for resolving this issue is now worth twice what it was in January when it was announced 😄

zyqcome commented 2 years ago

if edit code in vscode you can add env like this in 'launch.json' file

    "configurations": [
        {
           ........
            "env": {
                "LANG": "en_US.UTF-8"
            }
        }
zhaoqinghai commented 2 years ago

export LANG=en_US.UTF-8

2lu3 commented 2 years ago

export LANG=en_US.UTF-8

This worked for me when I tried to push github using gpg in git credential manager core.

KiruyaMomochi commented 2 years ago

Is this problem also https://github.com/mono/SkiaSharp/issues/1181?

hui3215 commented 2 years ago

I also have this problem on a computer with Galaxy Kylin operating system arm architecture. I solved this problem in the following way. The solution to this problem is known through the test examples and source code provided by avalonia.

  • First step: Use fonts as resources of the Avalonia project and set them as embedded resources. AvaloniaApplication1.csproj:
<ItemGroup>
    <EmbeddedResource Include="Assets\Fonts\msyh.ttc" />
    <EmbeddedResource Include="Assets\Fonts\msyhbd.ttc" />
    <EmbeddedResource Include="Assets\Fonts\msyhl.ttc" />
  </ItemGroup>
  • Second step: Create a new class as a custom font manager.
public class CustomFontManagerImpl : IFontManagerImpl
    {
        private readonly Typeface[] _customTypefaces;
        private readonly string _defaultFamilyName;

        //Load font resources in the project, you can load multiple font resources
        private readonly Typeface _defaultTypeface =
            new Typeface("resm:AvaloniaApplication1.Assets.Fonts.msyh#微软雅黑");

        public CustomFontManagerImpl()
        {
            _customTypefaces = new[] { _defaultTypeface };
            _defaultFamilyName = _defaultTypeface.FontFamily.FamilyNames.PrimaryFamilyName;
        }

        public string GetDefaultFontFamilyName()
        {
            return _defaultFamilyName;
        }

        public IEnumerable<string> GetInstalledFontFamilyNames(bool checkForUpdates = false)
        {
            return _customTypefaces.Select(x => x.FontFamily.Name);
        }

        private readonly string[] _bcp47 = { CultureInfo.CurrentCulture.ThreeLetterISOLanguageName, CultureInfo.CurrentCulture.TwoLetterISOLanguageName };

        public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily,
            CultureInfo culture, out Typeface typeface)
        {
            foreach (var customTypeface in _customTypefaces)
            {
                if (customTypeface.GlyphTypeface.GetGlyph((uint)codepoint) == 0)
                {
                    continue;
                }

                typeface = new Typeface(customTypeface.FontFamily.Name, fontStyle, fontWeight);

                return true;
            }

            var fallback = SKFontManager.Default.MatchCharacter(fontFamily?.Name, (SKFontStyleWeight)fontWeight,
                SKFontStyleWidth.Normal, (SKFontStyleSlant)fontStyle, _bcp47, codepoint);

            typeface = new Typeface(fallback?.FamilyName ?? _defaultFamilyName, fontStyle, fontWeight);

            return true;
        }

        public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface)
        {
            SKTypeface skTypeface;

            switch (typeface.FontFamily.Name)
            {
                case FontFamily.DefaultFontFamilyName:
                case "微软雅黑":  //font family name
                    skTypeface = SKTypeface.FromFamilyName(_defaultTypeface.FontFamily.Name); break;
                default:
                    skTypeface = SKTypeface.FromFamilyName(typeface.FontFamily.Name,
                        (SKFontStyleWeight)typeface.Weight, SKFontStyleWidth.Normal, (SKFontStyleSlant)typeface.Style);
                    break;
            }

            return new GlyphTypefaceImpl(skTypeface);
        }
    }
  • Third step: Override the RegisterServices() function in the App.axaml.cs file to register a custom font management object. App.axaml.cs:
        /// <summary>
        /// override RegisterServices register custom service
        /// </summary>
        public override void RegisterServices()
        {
            AvaloniaLocator.CurrentMutable.Bind<IFontManagerImpl>().ToConstant(new CustomFontManagerImpl());
            base.RegisterServices();
        }

Through the above three steps, I have solved this problem, I hope to help you.

Hello, how i can get this file

I used this method to do it, found a magical problem I run directly is not the effect of the font does not show the same as expected, but when I debug, and wait a few seconds and then run directly font will automatically load, I feel that there is a cache in the trick, please ask you what solution is not, this is my Manager class

using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Skia;
using SkiaSharp;

namespace Test;

public class CustomFontManagerImpl : IFontManagerImpl
{
    private readonly Typeface[] _customTypefaces;
    private readonly string _defaultFamilyName;

    //Load font resources in the project, you can load multiple font resources
    private readonly Typeface _defaultTypeface =
        new Typeface("resm:Test.Assets.Fonts.msyh#微软雅黑");

    private readonly Typeface SegoeUiTypeFace =
        new Typeface("resm:Test.Assets.Fonts.segoeui#Segoe UI");

    private readonly Typeface SegUiVarTypeface =
        new Typeface("resm:Test.Assets.Fonts.SegUIVar#Segoe UI Variable Text");

    private readonly Typeface msjhTypeFace =
        new Typeface("resm:Test.Assets.Fonts.seguisym#Microsoft JhengHei UI");

    private readonly Typeface FluentAvaloniaTypeFace =
        new Typeface("resm:Test.Assets.Fonts.FluentAvalonia#Symbols");

    public CustomFontManagerImpl()
    {
        _customTypefaces = new[]
            { _defaultTypeface, SegoeUiTypeFace, SegUiVarTypeface, msjhTypeFace, FluentAvaloniaTypeFace };
        _defaultFamilyName = _defaultTypeface.FontFamily.FamilyNames.PrimaryFamilyName;
    }

    public string GetDefaultFontFamilyName()
    {
        return _defaultFamilyName;
    }

    public IEnumerable<string> GetInstalledFontFamilyNames(bool checkForUpdates = false)
    {
        return _customTypefaces.Select(x => x.FontFamily.Name);
    }

    private readonly string[] _bcp47 =
        { CultureInfo.CurrentCulture.ThreeLetterISOLanguageName, CultureInfo.CurrentCulture.TwoLetterISOLanguageName };

    public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily,
        CultureInfo culture, out Typeface typeface)
    {
        foreach (var customTypeface in _customTypefaces)
        {
            if (customTypeface.GlyphTypeface.GetGlyph((uint)codepoint) == 0)
            {
                continue;
            }

            typeface = new Typeface(customTypeface.FontFamily.Name, fontStyle, fontWeight);

            return true;
        }

        var fallback = SKFontManager.Default.MatchCharacter(fontFamily?.Name, (SKFontStyleWeight)fontWeight,
            SKFontStyleWidth.Normal, (SKFontStyleSlant)fontStyle, _bcp47, codepoint);

        typeface = new Typeface(fallback?.FamilyName ?? _defaultFamilyName, fontStyle, fontWeight);

        return true;
    }

    public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface)
    {
        SKTypeface skTypeface;

        switch (typeface.FontFamily.Name)
        {
            case "Segoe UI": //font family name
                skTypeface = SKTypeface.FromFamilyName(SegoeUiTypeFace.FontFamily.Name);
                break;
            case "微软雅黑":
                skTypeface = SKTypeface.FromFamilyName(_defaultTypeface.FontFamily.Name);
                break;
            case "Segoe UI Variable Text":
                skTypeface = SKTypeface.FromFamilyName(SegUiVarTypeface.FontFamily.Name);
                break;
            case FontFamily.DefaultFontFamilyName:
            case "Symbols":
                skTypeface = SKTypeface.FromFamilyName(FluentAvaloniaTypeFace.FontFamily.Name);
                break;
            case "Microsoft JhengHei UI":
                skTypeface = SKTypeface.FromFamilyName(msjhTypeFace.FontFamily.Name);
                break;
            default:
                skTypeface = SKTypeface.FromFamilyName(typeface.FontFamily.Name,
                    (SKFontStyleWeight)typeface.Weight, SKFontStyleWidth.Normal, (SKFontStyleSlant)typeface.Style);
                break;
        }

        if (skTypeface == null)
        {
            skTypeface = SKTypeface.FromFamilyName(_defaultTypeface.FontFamily.Name);
        }
        return new GlyphTypefaceImpl(skTypeface);
    }
}
HendrikMennen commented 2 years ago

Is this fixed in the newest previews so I can remove my CustomFontManager?

Gillibald commented 2 years ago
.With(new FontManagerOptions
{
    DefaultFamilyName = "avares://MyAssembly/MyAssets#MyCustomFont"
 }
Blazero commented 2 years ago
.With(new FontManagerOptions
{
    DefaultFamilyName = "avares://MyAssembly/MyAssets#MyCustomFont"
 }

Hello @Gillibald, where should I put this?

KisaragiEffective commented 2 years ago

Probably in your C# code, if you're one of its developer

HendrikMennen commented 2 years ago
.With(new FontManagerOptions
{
    DefaultFamilyName = "avares://MyAssembly/MyAssets#MyCustomFont"
 }

Hello @Gillibald, where should I put this?

Program.cs or App.cs, pass it to Appbuilder

Blazero commented 2 years ago
.With(new FontManagerOptions
{
    DefaultFamilyName = "avares://MyAssembly/MyAssets#MyCustomFont"
 }

Adding this to BuildAvaloniaApp() in Program.cs solved the problem...... for one moment.

My program worked perfectly when I just did this. However, after I closed the program and run dotnet run again, there is no any error, and any window.

WhiteBlackGoose commented 1 year ago
.With(new FontManagerOptions
{
    DefaultFamilyName = "avares://MyAssembly/MyAssets#MyCustomFont"
 }

This helped. I'm not sure what that magic code does - and especially given that I didn't replace "MyCustomFont" with anything - it still worked.

debian 12 sid

joezhouchenye commented 1 year ago

Adding the following environment setting to the script works for me.

env LC_ALL=C $SCRIPT_DIR/Ryujinx.Ava
env LANG=C $SCRIPT_DIR/Ryujinx.Ava
PhantomGamers commented 1 year ago

This issue is still present on 11.0.0-rc1.1

timunie commented 1 year ago

@PhantomGamers if you have a valid idea how to solve it, a contribution is welcome. Otherwise we need to wait for it until someone has the time to address it.

PhantomGamers commented 1 year ago

I am aware, I was merely confirming that the issue was still valid since it has been open for some time and there is a new major version of Avalonia (well, an RC for one at least). It was not a demand that the issue be solved immediately or anything.

nopara73 commented 1 year ago

Note there's also a 0.03 BTC bounty on properly resolving this issue, which is by now worth almost $1k https://github.com/AvaloniaUI/Avalonia/issues/4427#issuecomment-761541876

manfromarce commented 1 year ago

I've had this issue on Arch and Solus OS and installing just the DejaVu fonts (ttf-dejavu on Arch, dejavu-fonts-ttf on Solus) fixed it.

Unmeltable-Ice commented 1 year ago

Since 11.0.0 changed access level of Avalonia.Skia.GlyphTypefaceImpl, from public to internal, none of the sample codes above of implementing Avalonia.Platform.IFontManagerImpl is available now. Sadly. Changing locale works, but is not acceptable for us, which will result in wrong i18n. Additional Avalonia.Media.FontManagerOptions with Avalonia.AppBuilder will break down application startup. "System.InvalidOperationException: Could not create glyphTypeface." Any ideas? We really need some help.

Gillibald commented 1 year ago

@Unmeltable-Ice https://github.com/AvaloniaUI/Avalonia/issues/12099

Unmeltable-Ice commented 1 year ago

@Gillibald Unfortunately it didn't work directly. We had difficulties in accessing embedded font resources. However, it does give us a hint. Now we set default font family name explicitly by OS type.

Program.cs:

using Avalonia;
using Avalonia.Media;
using Avalonia.ReactiveUI;
using System;

namespace ExampleApp
{
    internal class Program
    {
        [STAThread]
        public static void Main(string[] args) => BuildAvaloniaApp()
                .StartWithClassicDesktopLifetime(args);

        public static AppBuilder BuildAvaloniaApp()
        {
            FontManagerOptions options = new();
            if (OperatingSystem.IsLinux())
            {
                options.DefaultFamilyName = "<Linux Default Font Family Name Here>";
            }
            else if (OperatingSystem.IsMacOS())
            {
                options.DefaultFamilyName = "<macOS Default Font Family Name Here>";
            }
            // No need to set default for Windows
            return AppBuilder.Configure<App>()
                        .UsePlatformDetect()
                        .LogToTrace()
                        .UseReactiveUI()
                        .With(options);
        }
    }
}

Thanks for the help. We really appreciate it.

qwe88991600 commented 1 year ago

有一个临时解决方法。您可以在 AppBuilder 中注册自定义字体管理器https://github.com/AvaloniaUI/Avalonia/blob/master/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs

字体管理器将嵌入字体定义为默认字体。

Avalonia11 can not use it

nopara73 commented 1 year ago

The winner of the Bitcoin bounty is @mihnea-radulescu: https://github.com/AvaloniaUI/Avalonia/pull/12817#issuecomment-1720353778 🚀

Gillibald commented 1 year ago

Technically the issue isn't fixed because the system's default font is still not used. The above PR just prevents the crash and defines an existing font as the default to make the application work.

The real fix is to avoid Skia entirely for font matching and instead use FontConfig directly.

BiDuang commented 1 year ago

Avalonia 11.0.4 still have this issue. Environment is Ubuntu 22.04.1 LTS Arm64.

ChihHao-Su commented 12 months ago

This also causes https://github.com/musescore/MuseScore/issues/15423

timunie commented 12 months ago

@ChihHao-Su how? I see the error log in the ticket, but not a single Avalonia file in the source? Can you point me to that failing file? Also please let us know which Avalonia version used.

jwmatthys commented 12 months ago

@timunie the MuseScore error is related to Muse Hub, which is a separate application for downloading, installing, and updating MuseScore. The Muse Hub project does not appear to be open source (at least it's not on GitHub and the project page doesn't link to the code.) https://musehub.zendesk.com/

timunie commented 12 months ago

looks like latest nightly helps here: https://github.com/BiDuang/Chief/issues/2#issuecomment-1812605106

I don't know if the font fallback is done correctly.

MarkManYUN commented 10 months ago

I also encountered this issue while using Raspberry Pi 4B, an unhandled exception. System.InvalidOperationException: Default font family name can't be null or empty.

prkdrake commented 9 months ago

Can confirm this is still an issue and causing crash on Ubuntu 24.04

Mubelotix commented 8 months ago

Issue was fixed for me

ThePlenkov commented 7 months ago

Just caught this issue in my WSL linux with git credentials manager . BTW never had - I don't know what happened. Is it possible to know which font is missing? I have installed ttf-mscorefonts-installer but didn't help

ajkfds commented 6 months ago

In a Japanese environment on Linux, I encountered the same issue. However, I found a workaround by changing the LANG environment variable from jp_JP.utf-8 to en_US.utf8.

Mubelotix commented 6 months ago

You guys should check which version you are using