在WPF中我们想使用ttf
字体作为嵌入式资源,无需将它们复制或安装到系统中,也无需实际将它们写入磁盘。没有内存泄漏问题。
没有详细说明的解决方案:
如何在 WPF 应用程序中包含外部字体而不安装它
由于 WPF 内存泄漏,在这种情况下可用:
使用 Font 时 WPF TextBlock 内存泄漏
在 GDI 中只能通过以下方式从内存和进程安装字体添加FontMemResourceEx。由于这会安装该进程的字体,因此它也应该适用于 WPF,但似乎存在问题FontFamily
我们通过安装字体后得到的AddFontMemResourceEx
. E.g.:
var font = new FontFamily("Roboto");
这是有效的,因为它不会给出任何错误,但字体实际上没有改变,一些行间距和其他指标发生了变化,但字体看起来完全像Segoe UI
因为某些原因。
那么问题是如何使用安装的字体AddFontMemResourceEx
in WPF?
PS:这里是P/Invoke代码:
const string GdiDllName = "gdi32";
[DllImport(GdiDllName, ExactSpelling= true)]
private static extern IntPtr AddFontMemResourceEx(byte[] pbFont, int cbFont, IntPtr pdv, out uint pcFonts);
public static void AddFontMemResourceEx(string fontResourceName, byte[] bytes, Action<string> log)
{
var handle = AddFontMemResourceEx(bytes, bytes.Length, IntPtr.Zero, out uint fontCount);
if (handle == IntPtr.Zero)
{
log?.Invoke($"Font install failed for '{fontResourceName}'");
}
else
{
var message = $"Font installed '{fontResourceName}' with font count '{fontCount}'";
log?.Invoke(message);
}
}
此代码成功并显示如下日志消息:
Font installed 'Roboto-Regular.ttf' with font count '1'
将嵌入资源加载为字节数组的支持代码:
public static byte[] ReadResourceByteArray(Assembly assembly, string resourceName)
{
using (var stream = assembly.GetManifestResourceStream(resourceName))
{
var bytes = new byte[stream.Length];
int read = 0;
while (read < bytes.Length)
{
read += stream.Read(bytes, read, bytes.Length - read);
}
if (read != bytes.Length)
{
throw new ArgumentException(
$"Resource '{resourceName}' has unexpected length " +
$"'{read}' expected '{bytes.Length}'");
}
return bytes;
}
}
这意味着安装嵌入字体可以像这样完成assembly
是包含嵌入字体资源的程序集EMBEDDEDFONTNAMESPACE
是嵌入资源的命名空间,例如SomeProject.Fonts
:
var resourceNames = assembly.GetManifestResourceNames();
string Prefix = "EMBEDDEDFONTNAMESPACE" + ".";
var fontFileNameToResourceName = resourceNames.Where(n => n.StartsWith(Prefix))
.ToDictionary(n => n.Replace(Prefix, string.Empty), n => n);
var fontFileNameToBytes = fontFileNameToResourceName
.ToDictionary(p => p.Key, p => ReadResourceByteArray(assembly, p.Value));
foreach (var fileNameBytes in fontFileNameToBytes)
{
AddFontMemResourceEx(fileNameBytes.Key, fileNameBytes.Value, log);
}