ここまでリソースとして埋め込まれたフォントをVCL(GDI)で使用する方法を見てきました。ところで埋め込んだフォントをSkia4DelphiのTSkLabelやISkCanvasでも使用したいときは、少し違う方法が必要になります。
Skia4Delphiで描画に使用するフォントはTSkDefaultProviders.RegisterTypeface(かIFMXFontManagerService.AddCustomFontFromFile)で登録するのですが、このときのリソースタイプは(FONTではなく)RCDATAになっている必要があります。しかしフォント以外のリソースもリソースタイプRCDATAとして登録されるため、Win32APIのEnumResourceNames関数でプログラムに含まれるRT_RCDATAのリソースを列挙し、コールバック関数でリソースを読み出したら先頭4バイトでフォントデータかどうかを判別してからWin32APIのAddFontMemResourceEx関数とTSkDefaultProviders.RegisterTypefaceに渡すようにします。
まずプロジェクトにフォントをリソースとして追加します。Delphi IDEの"メインメニュー"→"プロジェクト"→"リソースと画像"で"<プロジェクト名>のリソース"ダイアログで、フォントをリソースとして追加します。このときリソースタイプを"RCDATA"に変更しておきます。
次にプログラムのなるべく早い時点でこのフォントを読み出して登録します。
unit LoadResFontsForSkia;
{$IFNDEF SKIA}
{$MESSAGE ERROR 'Enable Skia4Delphi.'}
{$ENDIF}
interface
uses
Winapi.Windows,
System.SysUtils, System.Classes,
Vcl.Skia;
implementation
type
TFontType = (ftNotFont, ftGDIFont, ftWebFont);
function CheckFontData(Stream: TStream): TFontType;
var
Signature: DWORD;
begin
Result := TFontType.ftNotFont;
Stream.Position := 0;
try
if Stream.Read(Signature,SizeOf(Signature)) <> SizeOf(Signature) then
begin
Exit;
end;
case Signature of
$00000100, // TrueType
$4F45454F, // 'OTTO' OpenType
$66637474: // 'ttcf' TrueType Collection
begin
Result := TFontType.ftGDIFont;
end;
$46464F77, // 'wOFF' Web Open Font
$32464F77: // 'wOF2' Web Open Font 2
begin
Result := TFontType.ftWebFont;
end;
end;
finally
Stream.Position := 0;
end;
end;
function EnumResNameProc(hModule: HMODULE; lpszType: PChar; lpszName: PChar; lParam: LONG_PTR): BOOL; stdcall;
var
RS: TResourceStream;
NumFonts: DWORD;
FontType: TFontType;
begin
{ Load resource }
if Is_IntResource(lpszName) = False then
begin
{ By name }
RS := TResourceStream.Create(HInstance,String(lpszName),RT_RCDATA);
end
else
begin
{ By index }
RS := TResourceStream.CreateFromID(HInstance,NativeUInt(lpszName),RT_RCDATA);
end;
try
FontType := CheckFontData(RS);
if FontType in [TFontType.ftGDIFont] then
begin
{ Regsiter font to GDI }
if AddFontMemResourceEx(RS.Memory,RS.Size,nil,@NumFonts) = 0 then
begin
RaiseLastOSError;
end;
end;
if FontType in [TFontType.ftGDIFont, TFontType.ftWebFont] then
begin
{ Register font to Skia4Delphi }
TSkDefaultProviders.RegisterTypeface(RS);
end;
finally
RS.Free;
end;
Result := True;
end;
procedure LoadResourceFonts;
begin
if EnumResourceNames(HInstance,RT_RCDATA,@EnumResNameProc,0) = False then
begin
RaiseLastOSError;
end;
end;
initialization
LoadResourceFonts;
end.
ユニットのinitialization部で呼び出しているLoadResourceFonts関数では、Win32APIのEnumResourceNames関数でプログラムに含まれるRT_RCDATAのリソースを列挙します。コールバック関数EnumResNameProcではコンストラクタTResourceStream.Createを呼び出してフォントをストリームに読み出し、フォントデータかどうかの判定関数CheckFontDataを呼び出します。CheckFontData関数ではストリームから先頭4バイトを読み出し、TrueType(.ttf)、TrueType Collection(.ttc)、OpenType(.otf)、OpenType Collection(.otc)、WOFF(.woff)、WOFF2(.woff2)のそれぞれのシグネチャに一致するかどうかでフォントデータかどうかの判定を行います。GDIでは使用できないWOFF/WOFF2形式のフォントもSkia4Delphiでは使用できるので、WOFF/WOFF2形式のときはAddFontMemResourceEx関数に加えてTSkDefaultProviders.RegisterTypefaceも呼び出すようにしています。これで登録したフォントはSkia4DelphiのFont.Familiesにそのフォント名を指定することで使用できます。TSkLabelならTSkLabel.TextSettings.Font.Families、ISkCanvasに対する描画ならTSkTypeface.MakeFromNameの第1パラメータです。
ん?リソースタイプFONTではなくRCDATAで埋め込んだフォントデータをAddFontMemResourceEx関数に渡せる?じゃあ前回のアーティクルのようにリソースコンパイラを差し替えたりせず、RCDATAでやればいいのでは?と思うかもしれません。その通りで、今回の方法(フォントをRCDATAで埋め込む)を使えば、Delphi 12.3およびそれ以前でもビルド前イベントでresinatorを使って処理する必要はありません。しかしプログラムにフォント以外のRCDATAのリソースが埋め込まれていた場合は、それらもすべて読み出してフォントデータかどうかの判別をする必要があります。このオーバヘッドを許容できるのであれば、今回の方法でも問題ありません。