读取本地字体文件

效果

通过代码读取指定目录下的字体文件 (.ttf / .otf) 转换为 UE4 内部可用的 Font

image-20210812171850767

步骤

  1. 根据 FontName 在目录下寻找字体文件
  2. 生成 FontFace
  3. 生成字体对象
  4. 保存资源

根据 FontName 在目录下寻找字体文件

1
2
3
4
5
6
7
8
9
10
11
FString FontPath;
const FString FontPathTTF = FontDirectory.Path + TEXT("/") + FontNameStr + TEXT(".ttf");
const FString FontPathOTF = FontDirectory.Path + TEXT("/") + FontNameStr + TEXT(".otf");

// 判断是否存在 TTF / OTF 文件
if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*FontPathTTF)) FontPath = FontPathTTF;
else if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*FontPathOTF)) FontPath = FontPathOTF;
else
{
// 不存在名字为 FontNameStr 的字体文件
}

生成 FontFace

image-20210812171924508
1
2
3
4
5
6
7
8
9
10
11
12
13
// 读取字体数据
TArray<uint8> FontData;
if (!FFileHelper::LoadFileToArray(FontData, *FontPath))
{
// 无法解析数据
}

FontFace->FontFaceData->SetData(MoveTemp(FontData));

if (FontFace)
{
/* 执行之后的操作 */
}

生成字体对象

根据刚才生成的 FontFace 生成一个默认的 Font,就像使用拖入生成字体文件时一样:

image-20210812171939351

(即模拟此时选中 Yes 时的操作)

1
2
3
4
5
6
7
8
9
// 先生成一个空的字体对象
Font = NewObject<UFont>(FontPackage, FName(*(FontNameStr + TEXT("_FONT"))), RF_Public | RF_Standalone | RF_MarkAsRootSet | RF_Transactional);

Font->FontCacheType = EFontCacheType::Runtime;

// 用刚才生成的 FontFace 生成一个默认字体
FTypefaceEntry& DefaultTypefaceEntry = Font->CompositeFont.DefaultTypeface.Fonts[Font->CompositeFont.DefaultTypeface.Fonts.AddDefaulted()];
DefaultTypefaceEntry.Name = "Default";
DefaultTypefaceEntry.Font = FFontData(FontFace);

保存资源

首先我们要确定想要生成的 Font 所在的位置,也就是要确定 Package 的位置,也就是确定 Path:

image-20210812172807755

Package 的位置由 MountPoint 开始,这个 MountPoint 就是类似 /Game/Engine 这样的东西;

如果我们需要 在指定目录保存这些文件,比如我们不想在 /Game/Engine 的目录写入这些信息,想在自己的插件目录、其它本地目录保存,那么我们需要先注册 MountPoint:

1
2
// Register mount point to save assets
FPackageName::RegisterMountPoint("/你想的 MountPoint 名字/", 对应的路径);

需要注意的是,MountPoint 一般直接使用 /Game 或者 /Engine,如果我们的 MountPoint 是自己给定的,有可能就无法在 UMG 中搜索到:

image-20210812173227245

接着我们:

1
2
3
4
5
6
7
8
9
10
11
12
FString PackageName = MountPointName + TEXT("/fonts/") + FontNameStr;
UPackage* FontPackage = CreatePackage(nullptr, *PackageName);
FontPackage->FullyLoad();

/* 生成字体 */

FontPackage->MarkPackageDirty();
FAssetRegistryModule::AssetCreated(Font);

FString PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());

UPackage::SavePackage(FontPackage, Font, EObjectFlags::RF_Public | EObjectFlags::RF_Standalone, *PackageFileName);

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
UObject* CreateFontObject()
{
// 设置一开始默认字体,若生成失败则返回默认字体
UFont* Font = GEngine->GetSmallFont();

FString FontPath;
const FString FontPathTTF = FontDirectory.Path + TEXT("/") + FontNameStr + TEXT(".ttf");
const FString FontPathOTF = FontDirectory.Path + TEXT("/") + FontNameStr + TEXT(".otf");

if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*FontPathTTF)) FontPath = FontPathTTF;
else if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*FontPathOTF)) FontPath = FontPathOTF;
else
{
// 不存在名字为 FontNameStr 的字体文件
return Font;
}

FString PackageName = MountPointName + TEXT("/fonts/") + FontNameStr;
UPackage* FontPackage = CreatePackage(nullptr, *PackageName);
FontPackage->FullyLoad();

UFontFace* FontFace = NewObject<UFontFace>(FontPackage, FName(*FontNameStr), RF_Public | RF_Standalone | RF_MarkAsRootSet);
FontFace->SourceFilename = FontNameStr;

TArray<uint8> FontData;
if (!FFileHelper::LoadFileToArray(FontData, *FontPath))
{
// 无法解析数据
return Font;
}

FontFace->FontFaceData->SetData(MoveTemp(FontData));

if (FontFace)
{
Font = NewObject<UFont>(FontPackage, FName(*(FontNameStr + TEXT("_FONT"))), RF_Public | RF_Standalone | RF_MarkAsRootSet | RF_Transactional);

Font->FontCacheType = EFontCacheType::Runtime;

// Add a default typeface referencing the newly created font face
FTypefaceEntry& DefaultTypefaceEntry = Font->CompositeFont.DefaultTypeface.Fonts[Font->CompositeFont.DefaultTypeface.Fonts.AddDefaulted()];
DefaultTypefaceEntry.Name = "Default";
DefaultTypefaceEntry.Font = FFontData(FontFace);

FontPackage->MarkPackageDirty();
FAssetRegistryModule::AssetCreated(Font);

FString PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());

UPackage::SavePackage(FontPackage, Font, EObjectFlags::RF_Public | EObjectFlags::RF_Standalone, *PackageFileName);
}
else
{
// 不存在 FontFace,无法生成 Font
}

return Font;
}

参考源码

EditorFactories : UFontFileImportFactory:生成字体的工厂