9.6 KiB
AtomUI 字体系统 — 制作自定义字体包
本文档面向希望制作自己的字体包的 AtomUI 应用开发者和 库开发者,提供从零开始创建字体包的完整步骤指南。模式完全对标
AtomUI.Fonts.AlibabaSans和 Avalonia 官方的Avalonia.Fonts.Inter。
1. 概述
自定义字体包将一组字体文件(.ttf / .otf)打包为独立的 NuGet 包或项目,使其可以:
- 在多个项目间共享。
- 通过一行代码注册到
FontManager。 - 与 AtomUI 主题系统无缝集成。
- 在 XAML 中通过自定义 URI Scheme 引用。
2. 步骤一:创建项目
2.1 创建类库项目
dotnet new classlib -n MyApp.Fonts.CustomFont
2.2 配置项目文件
编辑 .csproj 文件,添加 Avalonia 依赖和字体资源嵌入配置:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<!-- 关键!将字体文件嵌入为 Avalonia 资源 -->
<ItemGroup>
<AvaloniaResource Include="Assets/*" />
</ItemGroup>
<ItemGroup>
<!-- 最小依赖:仅需要 Avalonia -->
<PackageReference Include="Avalonia" Version="11.3.*" />
<!-- 如果需要 AtomUI ThemeManagerBuilder 扩展,则添加 AtomUI.Core 依赖 -->
<!-- <ProjectReference Include="../AtomUI.Core/AtomUI.Core.csproj" /> -->
</ItemGroup>
</Project>
⚠️ 重要:
<AvaloniaResource Include="Assets/*" />是必须的。没有这个配置,字体文件不会被嵌入到程序集中。
3. 步骤二:添加字体文件
在项目根目录创建 Assets/ 文件夹,将字体文件放入:
MyApp.Fonts.CustomFont/
└── Assets/
├── CustomFont-Regular.ttf
├── CustomFont-Bold.ttf
├── CustomFont-Light.ttf
└── CustomFont-Medium.ttf
支持的格式:
- TrueType Font (
.ttf) ✅ - OpenType Font (
.otf) ✅ - Variable Fonts — 暂不支持(参见 Avalonia Issue #11092)
确认字体族内部名称: 用字体查看工具(如 macOS 的 Font Book、Windows 的字体查看器)打开字体文件,查看其 Family Name 元数据。这个名称将用于 URI 中的 # 后缀,必须精确匹配。
4. 步骤三:创建字体集合类
创建 CustomFontCollection.cs:
using Avalonia.Media.Fonts;
namespace MyApp.Fonts.CustomFont;
public sealed class CustomFontCollection : EmbeddedFontCollection
{
public CustomFontCollection() : base(
new Uri("fonts:CustomFont", UriKind.Absolute),
new Uri("avares://MyApp.Fonts.CustomFont/Assets", UriKind.Absolute))
{
}
}
参数说明:
| 参数 | 说明 | 示例 |
|---|---|---|
| 第一个 URI(自定义 Scheme) | 字体集合的标识键,用于 XAML 中引用 | fonts:CustomFont |
| 第二个 URI(资源路径) | 嵌入字体文件的 Avalonia 资源路径 | avares://MyApp.Fonts.CustomFont/Assets |
命名约定:
- URI Scheme 统一使用
fonts:前缀。 - 集合键名推荐使用字体族名称(去空格),如
fonts:SourceHanSans、fonts:Roboto。 - 资源路径中的程序集名称必须与
.csproj中的<AssemblyName>(默认为项目名)完全匹配。
5. 步骤四:创建 AppBuilder 扩展方法
创建 AppBuilderExtension.cs,用于纯 Avalonia 场景:
using Avalonia;
using MyApp.Fonts.CustomFont;
namespace MyApp.Fonts;
public static class AppBuilderExtension
{
public static AppBuilder WithCustomFont(this AppBuilder appBuilder)
{
return appBuilder.ConfigureFonts(fontManager =>
{
fontManager.AddFontCollection(new CustomFontCollection());
});
}
}
使用方式:
public static AppBuilder BuildAvaloniaApp() =>
AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithCustomFont()
.LogToTrace();
6. 步骤五:创建 ThemeManagerBuilder 扩展方法(AtomUI 集成)
如果你的字体包需要与 AtomUI 主题系统集成,创建 ThemeManagerBuilderExtensions.cs:
using AtomUI.Theme;
using Avalonia.Media;
using MyApp.Fonts.CustomFont;
public static class CustomFontThemeManagerBuilderExtensions
{
public static IThemeManagerBuilder UseCustomFont(this IThemeManagerBuilder themeManagerBuilder)
{
FontManager.Current.AddFontCollection(new CustomFontCollection());
return themeManagerBuilder;
}
}
使用方式:
app.UseAtomUI(builder =>
{
builder.UseCustomFont(); // 注册自定义字体包
builder.UseDesktopControls();
});
注意:添加此扩展方法需要项目引用
AtomUI.Core。如果字体包是纯 Avalonia 包(不依赖 AtomUI),可以省略此步骤。
7. 步骤六:添加 XAML 命名空间注册(可选)
如果希望字体包的类型能在 AXAML 中通过 atom: 或自定义前缀访问,创建 Properties/AssemblyInfo.cs:
using Avalonia.Metadata;
[assembly: XmlnsDefinition("https://atomui.net", "MyApp.Fonts.CustomFont")]
对于纯 Avalonia 场景,可以映射到 Avalonia 命名空间:
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "MyApp.Fonts.CustomFont")]
8. 步骤七:使用自定义字体
8.1 在 XAML 中直接引用
<TextBlock FontFamily="fonts:CustomFont#My Custom Font"
FontSize="16"
Text="Hello in Custom Font!" />
# 后面的名称必须与字体文件中的 Family Name 元数据一致。
8.2 设置为 AtomUI 默认字体
如果想将自定义字体设置为整个应用的默认字体(替代 Alibaba Sans),使用 WithDefaultFontFamily():
app.UseAtomUI(builder =>
{
builder.UseCustomFont(); // 注册字体包
builder.UseDesktopControls();
// 将自定义字体设置为默认字体族
builder.WithDefaultFontFamily(
"fonts:CustomFont#My Custom Font, " +
"Segoe UI, PingFang SC, Microsoft YaHei, $Default"
);
});
这会在主题加载时将 SharedTokenKind.FontFamily Token 替换为你指定的字体族,所有使用 {atom:SharedTokenResource FontFamily} 的控件都会自动使用新字体。
8.3 同时注册多个字体包
app.UseAtomUI(builder =>
{
builder.UseAlibabaSansFont(); // 内置字体
builder.UseCustomFont(); // 自定义字体
builder.UseDesktopControls();
// 选择其中一个作为默认字体
builder.WithDefaultFontFamily("fonts:CustomFont#My Custom Font, $Default");
});
9. 完整文件结构
MyApp.Fonts.CustomFont/
├── CustomFontCollection.cs # 字体集合(EmbeddedFontCollection 子类)
├── AppBuilderExtension.cs # Avalonia AppBuilder 扩展方法
├── ThemeManagerBuilderExtensions.cs # AtomUI ThemeManagerBuilder 扩展方法(可选)
├── MyApp.Fonts.CustomFont.csproj # 项目文件
├── Properties/
│ └── AssemblyInfo.cs # XAML 命名空间注册(可选)
└── Assets/
├── CustomFont-Regular.ttf
├── CustomFont-Bold.ttf
├── CustomFont-Light.ttf
└── CustomFont-Medium.ttf
10. 发布为 NuGet 包(可选)
如果需要将字体包发布为 NuGet 包供多个项目使用:
10.1 配置包元数据
在 .csproj 中添加 NuGet 包配置:
<PropertyGroup>
<PackageId>MyApp.Fonts.CustomFont</PackageId>
<Version>1.0.0</Version>
<Authors>YourName</Authors>
<Description>Custom font package for Avalonia / AtomUI applications</Description>
<PackageTags>Avalonia;AtomUI;Fonts</PackageTags>
</PropertyGroup>
10.2 打包发布
dotnet pack -c Release
dotnet nuget push bin/Release/MyApp.Fonts.CustomFont.1.0.0.nupkg -s https://api.nuget.org/v3/index.json
11. 高级:自定义字符匹配
如果你的字体包需要控制字符回退行为(例如,为特定语言的字符优先使用特定字体),可以重写 TryMatchCharacter 方法:
using System.Globalization;
using Avalonia.Media;
using Avalonia.Media.Fonts;
public sealed class CustomFontCollection : EmbeddedFontCollection
{
public CustomFontCollection() : base(
new Uri("fonts:CustomFont", UriKind.Absolute),
new Uri("avares://MyApp.Fonts.CustomFont/Assets", UriKind.Absolute))
{
}
public override bool TryMatchCharacter(
int codepoint,
FontStyle fontStyle,
FontWeight fontWeight,
FontStretch fontStretch,
CultureInfo? culture,
out Typeface typeface)
{
// 自定义字符匹配逻辑
// 例如:为 CJK 字符使用特定字体
return base.TryMatchCharacter(
codepoint, fontStyle, fontWeight, fontStretch,
culture, out typeface);
}
}
12. 检查清单
创建自定义字体包时,确认以下事项:
.csproj中包含<AvaloniaResource Include="Assets/*" />- 字体文件格式为
.ttf或.otf(不支持 Variable Fonts) EmbeddedFontCollection中的avares://URI 程序集名称正确#后的字体族名称与字体文件元数据中的 Family Name 一致- 提供了
AppBuilder扩展方法(纯 Avalonia 场景) - 提供了
IThemeManagerBuilder扩展方法(AtomUI 场景,可选) - 如果发布为 NuGet 包,字体许可证允许嵌入分发
- 在目标平台(Windows/macOS/Linux)上测试字体渲染效果