делает свою работу
мы хотели бы использоватьttf
шрифты как встроенные ресурсы без копирования или установки их в систему и без их записи на диск. Без проблем с утечкой памяти.
Ни одно из решений не описано в:
Как включить внешний шрифт в приложение WPF без его установки
могут использоваться в этом сценарии из-за утечки памяти WPF вокруг этого:
Утечка памяти в WPF TextBlock при использовании шрифта
Установка шрифтов из памяти и только процесс возможны в GDI черезAddFontMemResourceEx, Так как при этом устанавливается шрифт для процесса, он должен работать и для WPF, но, похоже, существуют проблемы вокругFontFamily
что мы получаем после установки шрифта черезAddFontMemResourceEx
, Например.:
var font = new FontFamily("Roboto");
Это работает в том, что это не дает никаких ошибок, но шрифт фактически не изменен, некоторые межстрочные интервалы и другие метрики изменены, но шрифт выглядит в точности какSegoe UI
по какой-то причине.
Тогда возникает вопрос: а как можно использовать шрифты, установленные сAddFontMemResourceEx
в 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);
}