没有 window 的 GetFontUnicodeRanges
GetFontUnicodeRanges without a window
有机会在没有 window 的情况下调用 GetFontUnicodeRanges
吗?例如,它可能是不允许与桌面交互的 Windows 服务。
目前我正在使用控制台应用程序对此进行测试:
program UnicodeConsoleOutput;
{$APPTYPE CONSOLE}
uses
SysUtils,
Windows;
var
NumWritten: DWORD;
Text: WideString;
u8s: UTF8String;
procedure Add(AStart, AEnd: Word);
var
i: Word;
begin
Text := Text + WideFormat('[%x...%x]:'#13#10, [AStart, AEnd]);
for i := AStart to AEnd do
Text := Text + WideChar(i);
Text := Text + WideString(#13#10#13#10);
end;
//Actually I want to get glyph ranges for "Consolas" font
procedure GetFontRanges();
type
TRangesArray = array[0..(MaxInt div SizeOf(TWCRange)) - 1] of TWCRange;
PRangesArray = ^TRangesArray;
const
ConsoleTitle = '{A46DD332-0D57-4310-B91E-A68957C20429}';
var
GS: PGlyphSet;
GSSize: LongWord;
i: Integer;
rng: TWCRange;
hConsole: HWND;
hDev: HDC;
begin
//A dirty hack to get console window handle suggested by Microsoft
SetConsoleTitle(PChar(ConsoleTitle));
hConsole := FindWindow(nil, PChar(ConsoleTitle));
hDev := GetDC(hConsole);
try
GSSize := GetFontUnicodeRanges(hDev, nil);
GetMem(Pointer(GS), GSSize);
try
GS.cbThis := GSSize;
GS.flAccel := 0;
GS.cGlyphsSupported := 0;
GS.cRanges := 0;
if GetFontUnicodeRanges(hDev, GS) <> 0 then begin
for i := 0 to GS.cRanges - 1 do begin
rng := PRangesArray(@GS.ranges)[i];
Add(Word(rng.wcLow), Word(rng.wcLow) + rng.cGlyphs - 1);
end;
end;
finally
FreeMem(Pointer(GS), GSSize);
end;
finally
ReleaseDC(hConsole, hDev);
end;
end;
begin
try
GetFontRanges();
SetConsoleOutputCP(CP_UTF8);
u8s := UTF8Encode(Text);
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), PChar(u8s), Length(u8s),
NumWritten, nil);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.
在 Windows GDI 中,您可以创建设备上下文和 select 字体,而不需要 window 的句柄。例如,
HDC hdc = CreateDC(L"DISPLAY", NULL, NULL, NULL);
//CreateCompatibleDC(NULL) also works
HFONT hFont = CreateFont(
-20, 0, 0, 0,
FW_REGULAR,
FALSE, FALSE, FALSE, DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH || FF_DONTCARE,
L"Arial"
);
HFONT oldFont = static_cast<HFONT>(SelectObject(hdc, hFont));
请注意,GDI 文本函数使用 UTF-16 编码,并且所有这些函数都是在 Unicode 分配任何补充平面字符之前创建的。因此,采用 or return 非字符串字符列表的函数(例如 GetFontUnicodeRanges)对于当今的大部分 Unicode 来说效果不佳。 GetFontUnicodeRanges returns a pointer to a GLYPHSET, which has an array of WCRANGE 结构。它有一个 WCHAR 来表示一个 Unicode 字符。因此,GetFontUnicodeRanges 无法报告任何 Unicode 增补平面字符。在某些字体中,这可能是该字体支持的大多数字符。
在这方面,GDI不仅古老,而且过时了。对于您正在做的事情,DirectWrite 是一个更好的选择:它的所有 API 都支持所有 Unicode 字符。
您想要的 DWrite 方法是 IDWriteFontFace1::GetUnicodeRanges. Many DWrite APIs, including this, can be used without a window or even a device context. You'll probably want to obtain the IDWriteFontFace1 object by calling IDWriteFont::CreateFontFace, IDWriteFactory::CreateFontFace or IDWriteFontFaceReference::CreateFontFace,具体取决于您感兴趣的字体来源 — 可以是已安装的字体、自定义字体集、内存块或字体文件。
有机会在没有 window 的情况下调用 GetFontUnicodeRanges
吗?例如,它可能是不允许与桌面交互的 Windows 服务。
目前我正在使用控制台应用程序对此进行测试:
program UnicodeConsoleOutput;
{$APPTYPE CONSOLE}
uses
SysUtils,
Windows;
var
NumWritten: DWORD;
Text: WideString;
u8s: UTF8String;
procedure Add(AStart, AEnd: Word);
var
i: Word;
begin
Text := Text + WideFormat('[%x...%x]:'#13#10, [AStart, AEnd]);
for i := AStart to AEnd do
Text := Text + WideChar(i);
Text := Text + WideString(#13#10#13#10);
end;
//Actually I want to get glyph ranges for "Consolas" font
procedure GetFontRanges();
type
TRangesArray = array[0..(MaxInt div SizeOf(TWCRange)) - 1] of TWCRange;
PRangesArray = ^TRangesArray;
const
ConsoleTitle = '{A46DD332-0D57-4310-B91E-A68957C20429}';
var
GS: PGlyphSet;
GSSize: LongWord;
i: Integer;
rng: TWCRange;
hConsole: HWND;
hDev: HDC;
begin
//A dirty hack to get console window handle suggested by Microsoft
SetConsoleTitle(PChar(ConsoleTitle));
hConsole := FindWindow(nil, PChar(ConsoleTitle));
hDev := GetDC(hConsole);
try
GSSize := GetFontUnicodeRanges(hDev, nil);
GetMem(Pointer(GS), GSSize);
try
GS.cbThis := GSSize;
GS.flAccel := 0;
GS.cGlyphsSupported := 0;
GS.cRanges := 0;
if GetFontUnicodeRanges(hDev, GS) <> 0 then begin
for i := 0 to GS.cRanges - 1 do begin
rng := PRangesArray(@GS.ranges)[i];
Add(Word(rng.wcLow), Word(rng.wcLow) + rng.cGlyphs - 1);
end;
end;
finally
FreeMem(Pointer(GS), GSSize);
end;
finally
ReleaseDC(hConsole, hDev);
end;
end;
begin
try
GetFontRanges();
SetConsoleOutputCP(CP_UTF8);
u8s := UTF8Encode(Text);
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), PChar(u8s), Length(u8s),
NumWritten, nil);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.
在 Windows GDI 中,您可以创建设备上下文和 select 字体,而不需要 window 的句柄。例如,
HDC hdc = CreateDC(L"DISPLAY", NULL, NULL, NULL);
//CreateCompatibleDC(NULL) also works
HFONT hFont = CreateFont(
-20, 0, 0, 0,
FW_REGULAR,
FALSE, FALSE, FALSE, DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH || FF_DONTCARE,
L"Arial"
);
HFONT oldFont = static_cast<HFONT>(SelectObject(hdc, hFont));
请注意,GDI 文本函数使用 UTF-16 编码,并且所有这些函数都是在 Unicode 分配任何补充平面字符之前创建的。因此,采用 or return 非字符串字符列表的函数(例如 GetFontUnicodeRanges)对于当今的大部分 Unicode 来说效果不佳。 GetFontUnicodeRanges returns a pointer to a GLYPHSET, which has an array of WCRANGE 结构。它有一个 WCHAR 来表示一个 Unicode 字符。因此,GetFontUnicodeRanges 无法报告任何 Unicode 增补平面字符。在某些字体中,这可能是该字体支持的大多数字符。
在这方面,GDI不仅古老,而且过时了。对于您正在做的事情,DirectWrite 是一个更好的选择:它的所有 API 都支持所有 Unicode 字符。
您想要的 DWrite 方法是 IDWriteFontFace1::GetUnicodeRanges. Many DWrite APIs, including this, can be used without a window or even a device context. You'll probably want to obtain the IDWriteFontFace1 object by calling IDWriteFont::CreateFontFace, IDWriteFactory::CreateFontFace or IDWriteFontFaceReference::CreateFontFace,具体取决于您感兴趣的字体来源 — 可以是已安装的字体、自定义字体集、内存块或字体文件。